2019-06-22 01:36:57 +00:00
use derive_new ::new ;
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
use getset ::Getters ;
2020-10-15 22:25:17 +00:00
use nu_source ::{
b , span_for_spanned_list , DebugDocBuilder , HasFallibleSpan , PrettyDebug , Span , Spanned ,
SpannedItem ,
} ;
Extract core stuff into own crates
This commit extracts five new crates:
- nu-source, which contains the core source-code handling logic in Nu,
including Text, Span, and also the pretty.rs-based debug logic
- nu-parser, which is the parser and expander logic
- nu-protocol, which is the bulk of the types and basic conveniences
used by plugins
- nu-errors, which contains ShellError, ParseError and error handling
conveniences
- nu-textview, which is the textview plugin extracted into a crate
One of the major consequences of this refactor is that it's no longer
possible to `impl X for Spanned<Y>` outside of the `nu-source` crate, so
a lot of types became more concrete (Value became a concrete type
instead of Spanned<Value>, for example).
This also turned a number of inherent methods in the main nu crate into
plain functions (impl Value {} became a bunch of functions in the
`value` namespace in `crate::data::value`).
2019-11-26 02:30:48 +00:00
use num_bigint ::BigInt ;
2019-08-02 19:15:07 +00:00
use serde ::{ Deserialize , Serialize } ;
2019-06-22 01:36:57 +00:00
2020-10-15 22:25:17 +00:00
use crate ::hir ::{ Expression , Literal , Member , SpannedExpression } ;
use nu_errors ::ParseError ;
2020-01-14 07:38:56 +00:00
/// A PathMember that has yet to be spanned so that it can be used in later processing
2019-11-04 15:47:03 +00:00
#[ derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize) ]
2019-11-21 14:33:14 +00:00
pub enum UnspannedPathMember {
2019-11-04 15:47:03 +00:00
String ( String ) ,
Int ( BigInt ) ,
}
2019-11-21 14:33:14 +00:00
impl UnspannedPathMember {
2020-01-14 07:38:56 +00:00
/// Add the span information and get a full PathMember
2019-11-21 14:33:14 +00:00
pub fn into_path_member ( self , span : impl Into < Span > ) -> PathMember {
PathMember {
unspanned : self ,
span : span . into ( ) ,
}
}
}
2020-01-14 07:38:56 +00:00
/// A basic piece of a ColumnPath, which describes the steps to take through a table to arrive a cell, row, or inner table
2019-11-21 14:33:14 +00:00
#[ derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize) ]
pub struct PathMember {
pub unspanned : UnspannedPathMember ,
pub span : Span ,
}
2019-11-04 15:47:03 +00:00
impl PrettyDebug for & PathMember {
2020-01-14 07:38:56 +00:00
/// Gets the PathMember ready to be pretty-printed
2019-11-21 14:33:14 +00:00
fn pretty ( & self ) -> DebugDocBuilder {
match & self . unspanned {
UnspannedPathMember ::String ( string ) = > b ::primitive ( format! ( " {:?} " , string ) ) ,
UnspannedPathMember ::Int ( int ) = > b ::primitive ( format! ( " {} " , int ) ) ,
2019-11-04 15:47:03 +00:00
}
}
}
2020-01-14 07:38:56 +00:00
/// The fundamental path primitive to descrive how to navigate through a table to get to a sub-item. A path member can be either a word or a number. Words/strings are taken to mean
/// a column name, and numbers are the row number. Taken together they describe which column or row to narrow to in order to get data.
///
/// Rows must follow column names, they can't come first. eg) `foo.1` is valid where `1.foo` is not.
2019-11-04 15:47:03 +00:00
#[ derive(
Debug , Hash , Serialize , Deserialize , Ord , PartialOrd , Eq , PartialEq , Getters , Clone , new ,
) ]
pub struct ColumnPath {
#[ get = " pub " ]
members : Vec < PathMember > ,
}
impl ColumnPath {
2020-01-14 07:38:56 +00:00
/// Iterate over the members of the column path
2019-11-04 15:47:03 +00:00
pub fn iter ( & self ) -> impl Iterator < Item = & PathMember > {
self . members . iter ( )
}
2020-01-14 07:38:56 +00:00
/// Returns the last member and a slice of the remaining members
2020-01-02 05:24:41 +00:00
pub fn split_last ( & self ) -> Option < ( & PathMember , & [ PathMember ] ) > {
self . members . split_last ( )
2019-11-04 15:47:03 +00:00
}
2020-07-06 15:27:01 +00:00
/// Returns the last member
pub fn last ( & self ) -> Option < & PathMember > {
self . iter ( ) . last ( )
}
2020-10-15 22:25:17 +00:00
pub fn build ( text : & Spanned < String > ) -> ColumnPath {
if let (
SpannedExpression {
expr : Expression ::Literal ( Literal ::ColumnPath ( path ) ) ,
span : _ ,
} ,
_ ,
) = parse ( & text )
{
ColumnPath {
members : path . iter ( ) . map ( | member | member . to_path_member ( ) ) . collect ( ) ,
}
} else {
ColumnPath { members : vec ! [ ] }
}
}
2019-11-04 15:47:03 +00:00
}
impl PrettyDebug for ColumnPath {
2020-01-14 07:38:56 +00:00
/// Gets the ColumnPath ready to be pretty-printed
2019-11-21 14:33:14 +00:00
fn pretty ( & self ) -> DebugDocBuilder {
let members : Vec < DebugDocBuilder > =
self . members . iter ( ) . map ( | member | member . pretty ( ) ) . collect ( ) ;
2019-11-04 15:47:03 +00:00
b ::delimit (
" ( " ,
b ::description ( " path " ) + b ::equals ( ) + b ::intersperse ( members , b ::space ( ) ) ,
" ) " ,
)
. nest ( )
}
}
impl HasFallibleSpan for ColumnPath {
2020-01-14 07:38:56 +00:00
/// Creates a span that will cover the column path, if possible
2019-11-04 15:47:03 +00:00
fn maybe_span ( & self ) -> Option < Span > {
2019-12-31 07:36:08 +00:00
if self . members . is_empty ( ) {
2019-11-04 15:47:03 +00:00
None
} else {
Some ( span_for_spanned_list ( self . members . iter ( ) . map ( | m | m . span ) ) )
}
}
}
impl PathMember {
2020-01-14 07:38:56 +00:00
/// Create a string path member
2019-11-04 15:47:03 +00:00
pub fn string ( string : impl Into < String > , span : impl Into < Span > ) -> PathMember {
2019-11-21 14:33:14 +00:00
UnspannedPathMember ::String ( string . into ( ) ) . into_path_member ( span )
2019-11-04 15:47:03 +00:00
}
2020-01-14 07:38:56 +00:00
/// Create a numeric path member
2019-11-04 15:47:03 +00:00
pub fn int ( int : impl Into < BigInt > , span : impl Into < Span > ) -> PathMember {
2019-11-21 14:33:14 +00:00
UnspannedPathMember ::Int ( int . into ( ) ) . into_path_member ( span )
2019-11-04 15:47:03 +00:00
}
2020-07-06 15:27:01 +00:00
pub fn as_string ( & self ) -> String {
match & self . unspanned {
UnspannedPathMember ::String ( string ) = > string . clone ( ) ,
UnspannedPathMember ::Int ( int ) = > format! ( " {} " , int ) ,
}
}
2019-11-04 15:47:03 +00:00
}
2020-10-15 22:25:17 +00:00
fn parse ( raw_column_path : & Spanned < String > ) -> ( SpannedExpression , Option < ParseError > ) {
let mut delimiter = '.' ;
let mut inside_delimiter = false ;
let mut output = vec! [ ] ;
let mut current_part = String ::new ( ) ;
let mut start_index = 0 ;
let mut last_index = 0 ;
for ( idx , c ) in raw_column_path . item . char_indices ( ) {
last_index = idx ;
if inside_delimiter {
if c = = delimiter {
inside_delimiter = false ;
}
} else if c = = '\'' | | c = = '"' | | c = = '`' {
inside_delimiter = true ;
delimiter = c ;
} else if c = = '.' {
let part_span = Span ::new (
raw_column_path . span . start ( ) + start_index ,
raw_column_path . span . start ( ) + idx ,
) ;
if let Ok ( row_number ) = current_part . parse ::< u64 > ( ) {
output . push ( Member ::Int ( BigInt ::from ( row_number ) , part_span ) ) ;
} else {
let trimmed = trim_quotes ( & current_part ) ;
output . push ( Member ::Bare ( trimmed . clone ( ) . spanned ( part_span ) ) ) ;
}
current_part . clear ( ) ;
// Note: I believe this is safe because of the delimiter we're using, but if we get fancy with
// unicode we'll need to change this
start_index = idx + '.' . len_utf8 ( ) ;
continue ;
}
current_part . push ( c ) ;
}
if ! current_part . is_empty ( ) {
let part_span = Span ::new (
raw_column_path . span . start ( ) + start_index ,
raw_column_path . span . start ( ) + last_index + 1 ,
) ;
if let Ok ( row_number ) = current_part . parse ::< u64 > ( ) {
output . push ( Member ::Int ( BigInt ::from ( row_number ) , part_span ) ) ;
} else {
let current_part = trim_quotes ( & current_part ) ;
output . push ( Member ::Bare ( current_part . spanned ( part_span ) ) ) ;
}
}
(
SpannedExpression ::new ( Expression ::simple_column_path ( output ) , raw_column_path . span ) ,
None ,
)
}
fn trim_quotes ( input : & str ) -> String {
let mut chars = input . chars ( ) ;
match ( chars . next ( ) , chars . next_back ( ) ) {
( Some ( '\'' ) , Some ( '\'' ) ) = > chars . collect ( ) ,
( Some ( '"' ) , Some ( '"' ) ) = > chars . collect ( ) ,
( Some ( '`' ) , Some ( '`' ) ) = > chars . collect ( ) ,
_ = > input . to_string ( ) ,
}
}