diff --git a/src/commands/config.rs b/src/commands/config.rs index 47b073e0b..954cc08ef 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -3,7 +3,8 @@ use crate::prelude::*; use crate::errors::ShellError; use crate::object::config; use crate::object::Value; -use crate::parser::registry::{CommandConfig, NamedType, NamedValue}; +use crate::parser::hir::SyntaxType; +use crate::parser::registry::{CommandConfig, NamedType}; use indexmap::IndexMap; use log::trace; use std::iter::FromIterator; @@ -20,14 +21,11 @@ impl Command for Config { fn config(&self) -> CommandConfig { let mut named: IndexMap = IndexMap::new(); - named.insert("set".to_string(), NamedType::Optional(NamedValue::Single)); - named.insert("get".to_string(), NamedType::Optional(NamedValue::Single)); + named.insert("set".to_string(), NamedType::Optional(SyntaxType::Any)); + named.insert("get".to_string(), NamedType::Optional(SyntaxType::Any)); named.insert("clear".to_string(), NamedType::Switch); - named.insert( - "remove".to_string(), - NamedType::Optional(NamedValue::Single), - ); + named.insert("remove".to_string(), NamedType::Optional(SyntaxType::Any)); CommandConfig { name: self.name().to_string(), diff --git a/src/commands/macros.rs b/src/commands/macros.rs index c7dd74de7..97818df54 100644 --- a/src/commands/macros.rs +++ b/src/commands/macros.rs @@ -1,3 +1,10 @@ +#[doc(hidden)] +macro_rules! named_type { + ($name:ident) => { + $crate::parser::registry::NamedType::$($name)* + } +} + #[macro_export] macro_rules! command { ( @@ -11,7 +18,7 @@ macro_rules! command { rest_positional: $rest_positional:tt, named: { $( - ($named_param:tt : $named_type:tt) + ($named_param:tt : $named_type:ty : $named_kind:tt) )* } } @@ -35,7 +42,7 @@ macro_rules! command { Ok(output.boxed().to_output_stream()) } - let tuple = ( $($extract),*, ); + let tuple = ( $($extract ,)* ); command( $args, tuple ) } @@ -58,7 +65,7 @@ macro_rules! command { let mut named: indexmap::IndexMap = indexmap::IndexMap::new(); $( - named.insert(stringify!($named_param).to_string(), NamedType::$named_type); + named.insert(stringify!($named_param).to_string(), $crate::parser::registry::NamedType::$named_kind); )* named @@ -72,7 +79,7 @@ macro_rules! command { ( Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } - Rest { , -- $param_name:ident : Switch $($rest:tt)* } + Rest { -- $param_name:ident : Switch , $($rest:tt)* } CommandConfig { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], @@ -100,7 +107,7 @@ macro_rules! command { rest_positional: $rest_positional, named: { $($config_named)* - ($param_name : Switch) + ($param_name : Switch : Switch) } } @@ -109,11 +116,11 @@ macro_rules! command { } Extract { - ($($extract)* { + $($extract)* { use std::convert::TryInto; $args.get(stringify!($param_name)).clone().try_into()? - }) + } } ); }; @@ -122,7 +129,7 @@ macro_rules! command { ( Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } - Rest { , -- $param_name:ident : $param_kind:tt $($rest:tt)* } + Rest { -- $param_name:ident : $param_kind:ty , $($rest:tt)* } CommandConfig { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], @@ -159,11 +166,11 @@ macro_rules! command { } Extract { - ($($extract)* { + $($extract)* { use std::convert::TryInto; $args.get(stringify!($param_name)).clone().try_into()? - }) + } } ); }; @@ -172,7 +179,7 @@ macro_rules! command { ( Named { $export:tt $args:ident $body:block } Positional { $($positional_count:tt)* } - Rest { , -- $param_name:ident ? : $param_kind:tt $($rest:tt)* } + Rest { -- $param_name:ident ? : $param_kind:ty , $($rest:tt)* } CommandConfig { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], @@ -209,11 +216,11 @@ macro_rules! command { } Extract { - ($($extract)* { + $($extract)* { use std::convert::TryInto; $args.get(stringify!($param_name)).clone().try_into()? - }) + } } ); }; @@ -222,7 +229,7 @@ macro_rules! command { ( Named { $export:ident $args:ident $body:block } Positional { $($positional_count:tt)* } - Rest { , $param_name:ident : Block $($rest:tt)* } + Rest { $param_name:ident : Block , $($rest:tt)* } CommandConfig { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], @@ -277,7 +284,7 @@ macro_rules! command { ( Named { $export:ident $args:ident $body:block } Positional { $($positional_count:tt)* } - Rest { , $param_name:ident : $param_kind:tt $($rest:tt)* } + Rest { $param_name:ident : $param_kind:ty , $($rest:tt)* } CommandConfig { name: $config_name:tt, mandatory_positional: vec![ $($mandatory_positional:tt)* ], @@ -304,7 +311,7 @@ macro_rules! command { CommandConfig { name: $config_name, mandatory_positional: vec![ $($mandatory_positional)* $crate::parser::registry::PositionalType::mandatory( - stringify!($param_name) + stringify!($param_name), <$param_kind>::syntax_type() ), ], optional_positional: vec![ $($optional_positional)* ], rest_positional: $rest_positional, @@ -321,14 +328,13 @@ macro_rules! command { $($extract:tt)* { use $crate::object::types::ExtractType; let value = $args.expect_nth($($positional_count)*)?; - // let value = $param_kind.check(value)?; - $param_kind::extract(value)? + <$param_kind>::extract(&value)? } } ); }; - ($export:ident as $config_name:tt ( $args:ident $($command_rest:tt)* ) $body:block) => { + ($export:ident as $config_name:tt ( $args:ident , $($command_rest:tt)* ) $body:block) => { command!( Named { $export $args $body } Positional { 0 } diff --git a/src/commands/open.rs b/src/commands/open.rs index f4a024628..f04f9aae8 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; command! { - Open as open(args, --raw: Switch) { + Open as open(args, path: Spanned, --raw: Switch,) { let span = args.name_span; let cwd = args @@ -21,16 +21,19 @@ command! { let full_path = PathBuf::from(cwd); - let (file_extension, contents, contents_span) = match &args.expect_nth(0)?.item { - Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?, - _ => { - return Err(ShellError::labeled_error( - "Expected string value for filename", - "expected filename", - args.expect_nth(0)?.span, - )); - } - }; + let path_str = path.to_str().ok_or(ShellError::type_error("Path", "invalid path".spanned(path.span)))?; + + let (file_extension, contents, contents_span) = fetch(&full_path, path_str, path.span)?; + // let (file_extension, contents, contents_span) = match &args.expect_nth(0)?.item { + // Value::Primitive(Primitive::String(s)) => fetch(&full_path, s, args.expect_nth(0)?.span)?, + // _ => { + // return Err(ShellError::labeled_error( + // "Expected string value for filename", + // "expected filename", + // args.expect_nth(0)?.span, + // )); + // } + // }; let mut stream = VecDeque::new(); diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 335ceee17..6603bd0d4 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -17,6 +17,7 @@ pub fn value_to_json_value(v: &Value) -> serde_json::Value { } Value::Primitive(Primitive::Nothing) => serde_json::Value::Null, Value::Primitive(Primitive::String(s)) => serde_json::Value::String(s.clone()), + Value::Primitive(Primitive::Path(s)) => serde_json::Value::String(s.display().to_string()), Value::Filesystem => serde_json::Value::Null, Value::List(l) => { diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 87cf71ed2..7dc990b64 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -13,6 +13,7 @@ pub fn value_to_toml_value(v: &Value) -> toml::Value { Value::Primitive(Primitive::Int(i)) => toml::Value::Integer(*i), Value::Primitive(Primitive::Nothing) => toml::Value::String("".to_string()), Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()), + Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()), Value::Filesystem => toml::Value::String("".to_string()), Value::List(l) => toml::Value::Array(l.iter().map(|x| value_to_toml_value(x)).collect()), diff --git a/src/commands/where_.rs b/src/commands/where_.rs index d8f68c2b6..0b7b818c8 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -5,7 +5,7 @@ use futures::future::ready; use log::trace; command! { - Where as where(args, condition: Block) { + Where as where(args, condition: Block,) { let input: InputStream = trace_stream!(target: "nu::trace_stream::where", "where input" = args.input); input.values.filter_map(move |item| { diff --git a/src/object/base.rs b/src/object/base.rs index 8b8a5a24d..b6674ef3d 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -11,6 +11,7 @@ use derive_new::new; use ordered_float::OrderedFloat; use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer}; use std::fmt; +use std::path::PathBuf; use std::time::SystemTime; #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, new, Serialize, Deserialize)] @@ -40,6 +41,7 @@ pub enum Primitive { String(String), Boolean(bool), Date(DateTime), + Path(PathBuf), EndOfStream, } @@ -51,6 +53,7 @@ impl Primitive { match self { Nothing => "nothing", EndOfStream => "end-of-stream", + Path(_) => "path", Int(_) => "int", Float(_) => "float", Bytes(_) => "bytes", @@ -68,6 +71,7 @@ impl Primitive { Nothing => write!(f, "Nothing"), EndOfStream => write!(f, "EndOfStream"), Int(int) => write!(f, "{}", int), + Path(path) => write!(f, "{}", path.display()), Float(float) => write!(f, "{:?}", float), Bytes(bytes) => write!(f, "{}", bytes), String(string) => write!(f, "{:?}", string), @@ -80,6 +84,7 @@ impl Primitive { match self { Primitive::Nothing => format!("{}", Color::Black.bold().paint("-")), Primitive::EndOfStream => format!("{}", Color::Black.bold().paint("-")), + Primitive::Path(p) => format!("{}", p.display()), Primitive::Bytes(b) => { let byte = byte_unit::Byte::from_bytes(*b as u128); diff --git a/src/object/types.rs b/src/object/types.rs index 287cb4ef9..abc4b7e8f 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -3,19 +3,34 @@ use crate::parser::hir; use crate::prelude::*; use derive_new::new; use serde_derive::Deserialize; +use std::path::PathBuf; pub trait Type: std::fmt::Debug + Send { type Extractor: ExtractType; fn name(&self) -> &'static str; - fn coerce(&self) -> Option { - None - } } pub trait ExtractType: Sized { fn extract(value: &Spanned) -> Result; fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError>; + fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::Any + } +} + +impl ExtractType for Spanned { + fn extract(value: &Spanned) -> Result, ShellError> { + Ok(T::extract(value)?.spanned(value.span)) + } + + fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + T::check(value) + } + + fn syntax_type() -> hir::SyntaxType { + T::syntax_type() + } } #[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)] @@ -39,6 +54,35 @@ impl ExtractType for Spanned { } } +#[derive(Debug)] +pub struct FilePath; + +impl ExtractType for std::path::PathBuf { + fn syntax_type() -> hir::SyntaxType { + hir::SyntaxType::Path + } + + fn extract(value: &'a Spanned) -> Result { + match &value { + Spanned { + item: Value::Primitive(Primitive::String(p)), + span, + } => Ok(PathBuf::from(p)), + other => Err(ShellError::type_error("Path", other.spanned_type_name())), + } + } + + fn check(value: &'value Spanned) -> Result<&'value Spanned, ShellError> { + match &value { + v @ Spanned { + item: Value::Primitive(Primitive::Path(_)), + .. + } => Ok(v), + other => Err(ShellError::type_error("Path", other.spanned_type_name())), + } + } +} + #[derive(Debug, Deserialize, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, new)] pub struct Integer; diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 888a8a732..7b0db7508 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -8,8 +8,8 @@ use crate::parser::{Span, Spanned, Unit}; use derive_new::new; use getset::Getters; -crate use baseline_parse::baseline_parse_single_token; -crate use baseline_parse_tokens::{baseline_parse_next_expr, ExpressionKindHint, TokensIterator}; +crate use baseline_parse::{baseline_parse_single_token, baseline_parse_token_as_string}; +crate use baseline_parse_tokens::{baseline_parse_next_expr, SyntaxType, TokensIterator}; crate use binary::Binary; crate use named::NamedArguments; crate use path::Path; diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 2661fb772..b4dc38458 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -13,3 +13,16 @@ pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Express RawToken::Bare => hir::Expression::bare(token.span), } } + +pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expression { + match *token.item() { + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span) + } + RawToken::Variable(span) => hir::Expression::variable(span, token.span), + RawToken::Integer(_) => hir::Expression::bare(token.span), + RawToken::Size(int, unit) => hir::Expression::bare(token.span), + RawToken::Bare => hir::Expression::bare(token.span), + RawToken::String(span) => hir::Expression::string(span, token.span), + } +} diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index 12c604e70..8b8dd026d 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -1,11 +1,14 @@ use crate::errors::ShellError; use crate::parser::registry::CommandRegistry; use crate::parser::{ - hir, hir::baseline_parse_single_token, DelimitedNode, Delimiter, PathNode, RawToken, Span, - Spanned, TokenNode, + hir, + hir::{baseline_parse_single_token, baseline_parse_token_as_string}, + DelimitedNode, Delimiter, PathNode, RawToken, Span, Spanned, TokenNode, }; use crate::{SpannedItem, Text}; use derive_new::new; +use log::trace; +use serde_derive::{Deserialize, Serialize}; pub fn baseline_parse_tokens( token_nodes: &mut TokensIterator<'_>, @@ -19,7 +22,7 @@ pub fn baseline_parse_tokens( break; } - let expr = baseline_parse_next_expr(token_nodes, registry, source, None)?; + let expr = baseline_parse_next_expr(token_nodes, registry, source, SyntaxType::Any)?; exprs.push(expr); } @@ -27,10 +30,12 @@ pub fn baseline_parse_tokens( } #[allow(unused)] -#[derive(Debug)] -pub enum ExpressionKindHint { +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum SyntaxType { + Any, Literal, Variable, + Path, Binary, Block, Boolean, @@ -40,13 +45,31 @@ pub fn baseline_parse_next_expr( tokens: &mut TokensIterator, registry: &dyn CommandRegistry, source: &Text, - coerce_hint: Option, + syntax_type: SyntaxType, ) -> Result { - let first = match tokens.next() { - None => return Err(ShellError::string("Expected token, found none")), - Some(token) => baseline_parse_semantic_token(token, registry, source)?, + let next = tokens + .next() + .ok_or_else(|| ShellError::string("Expected token, found none"))?; + + trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next); + + match (syntax_type, next) { + (SyntaxType::Path, TokenNode::Token(token)) => { + return Ok(baseline_parse_token_as_string(token, source)) + } + + (SyntaxType::Path, token) => { + return Err(ShellError::type_error( + "Path", + token.type_name().spanned(token.span()), + )) + } + + _ => {} }; + let first = baseline_parse_semantic_token(next, registry, source)?; + let possible_op = tokens.peek(); let op = match possible_op { @@ -69,8 +92,8 @@ pub fn baseline_parse_next_expr( // We definitely have a binary expression here -- let's see if we should coerce it into a block - match coerce_hint { - None => { + match syntax_type { + SyntaxType::Any => { let span = (first.span.start, second.span.end); let binary = hir::Binary::new(first, op, second); let binary = hir::RawExpression::Binary(Box::new(binary)); @@ -79,74 +102,75 @@ pub fn baseline_parse_next_expr( Ok(binary) } - Some(hint) => match hint { - ExpressionKindHint::Block => { - let span = (first.span.start, second.span.end); + SyntaxType::Block => { + let span = (first.span.start, second.span.end); - let path: Spanned = match first { + let path: Spanned = match first { + Spanned { + item: hir::RawExpression::Literal(hir::Literal::Bare), + span, + } => { + let string = Spanned::from_item(span.slice(source).to_string(), span); + let path = hir::Path::new( + Spanned::from_item( + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), + (0, 0), + ), + vec![string], + ); + let path = hir::RawExpression::Path(Box::new(path)); Spanned { - item: hir::RawExpression::Literal(hir::Literal::Bare), + item: path, + span: first.span, + } + } + Spanned { + item: hir::RawExpression::Literal(hir::Literal::String(inner)), + span, + } => { + let string = Spanned::from_item(inner.slice(source).to_string(), span); + let path = hir::Path::new( + Spanned::from_item( + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), + (0, 0), + ), + vec![string], + ); + let path = hir::RawExpression::Path(Box::new(path)); + Spanned { + item: path, + span: first.span, + } + } + Spanned { + item: hir::RawExpression::Variable(..), + .. + } => first, + Spanned { span, item } => { + return Err(ShellError::labeled_error( + "The first part of an un-braced block must be a column name", + item.type_name(), span, - } => { - let string = Spanned::from_item(span.slice(source).to_string(), span); - let path = hir::Path::new( - Spanned::from_item( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), - (0, 0), - ), - vec![string], - ); - let path = hir::RawExpression::Path(Box::new(path)); - Spanned { - item: path, - span: first.span, - } - } - Spanned { - item: hir::RawExpression::Literal(hir::Literal::String(inner)), - span, - } => { - let string = Spanned::from_item(inner.slice(source).to_string(), span); - let path = hir::Path::new( - Spanned::from_item( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), - (0, 0), - ), - vec![string], - ); - let path = hir::RawExpression::Path(Box::new(path)); - Spanned { - item: path, - span: first.span, - } - } - Spanned { - item: hir::RawExpression::Variable(..), - .. - } => first, - Spanned { span, item } => { - return Err(ShellError::labeled_error( - "The first part of an un-braced block must be a column name", - item.type_name(), - span, - )) - } - }; + )) + } + }; - let binary = hir::Binary::new(path, op, second); - let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = Spanned::from_item(binary, span); + let binary = hir::Binary::new(path, op, second); + let binary = hir::RawExpression::Binary(Box::new(binary)); + let binary = Spanned::from_item(binary, span); - let block = hir::RawExpression::Block(vec![binary]); - let block = Spanned::from_item(block, span); + let block = hir::RawExpression::Block(vec![binary]); + let block = Spanned::from_item(block, span); - Ok(block) - } + Ok(block) + } - other => unimplemented!("coerce hint {:?}", other), - }, + other => Err(ShellError::unimplemented(format!( + "coerce hint {:?}", + other + ))), } } @@ -161,8 +185,10 @@ pub fn baseline_parse_semantic_token( TokenNode::Delimited(delimited) => baseline_parse_delimited(delimited, registry, source), TokenNode::Pipeline(_pipeline) => unimplemented!(), TokenNode::Operator(_op) => unreachable!(), - TokenNode::Flag(_flag) => Err(ShellError::unimplemented("passing flags is not supported yet.")), - TokenNode::Identifier(_span) => unreachable!(), + TokenNode::Flag(_flag) => Err(ShellError::unimplemented( + "passing flags is not supported yet.", + )), + TokenNode::Member(_span) => unreachable!(), TokenNode::Whitespace(_span) => unreachable!(), TokenNode::Error(error) => Err(*error.item.clone()), TokenNode::Path(path) => baseline_parse_path(path, registry, source), @@ -210,7 +236,7 @@ pub fn baseline_parse_path( } }, - TokenNode::Identifier(span) => span.slice(source), + TokenNode::Member(span) => span.slice(source), // TODO: Make this impossible other => unreachable!("{:?}", other), diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 795b980ee..0ba9e4a2f 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -151,7 +151,7 @@ pub fn var(input: NomSpan) -> IResult { trace_step(input, "var", move |input| { let start = input.offset; let (input, _) = tag("$")(input)?; - let (input, bare) = identifier(input)?; + let (input, bare) = member(input)?; let end = input.offset; Ok(( @@ -161,14 +161,7 @@ pub fn var(input: NomSpan) -> IResult { }) } -// let start = input.offset; -// let (input, _) = take_while1(is_start_bare_char)(input)?; -// let (input, _) = take_while(is_bare_char)(input)?; -// let end = input.offset; - -// Ok((input, TokenTreeBuilder::spanned_bare((start, end)))) - -pub fn identifier(input: NomSpan) -> IResult { +pub fn member(input: NomSpan) -> IResult { trace_step(input, "identifier", move |input| { let start = input.offset; let (input, _) = take_while1(is_id_start)(input)?; @@ -176,7 +169,7 @@ pub fn identifier(input: NomSpan) -> IResult { let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_ident((start, end)))) + Ok((input, TokenTreeBuilder::spanned_member((start, end)))) }) } @@ -412,7 +405,7 @@ pub fn path(input: NomSpan) -> IResult { let left = input.offset; let (input, head) = node1(input)?; let (input, _) = tag(".")(input)?; - let (input, tail) = separated_list(tag("."), alt((identifier, string)))(input)?; + let (input, tail) = separated_list(tag("."), alt((member, string)))(input)?; let right = input.offset; Ok(( @@ -802,14 +795,14 @@ mod tests { let _ = pretty_env_logger::try_init(); assert_eq!( apply(node, "node", "$it.print"), - build_token(b::path(b::var("it"), vec![b::ident("print")])) + build_token(b::path(b::var("it"), vec![b::member("print")])) ); assert_eq!( apply(node, "node", "$head.part1.part2"), build_token(b::path( b::var("head"), - vec![b::ident("part1"), b::ident("part2")] + vec![b::member("part1"), b::member("part2")] )) ); @@ -817,7 +810,7 @@ mod tests { apply(node, "node", "( hello ).world"), build_token(b::path( b::parens(vec![b::sp(), b::bare("hello"), b::sp()]), - vec![b::ident("world")] + vec![b::member("world")] )) ); @@ -843,7 +836,7 @@ mod tests { b::sp(), b::path( b::var("it"), - vec![b::ident("is"), b::string("great news"), b::ident("right")] + vec![b::member("is"), b::string("great news"), b::member("right")] ), b::sp(), b::bare("yep"), @@ -972,7 +965,7 @@ mod tests { vec![ b::sp(), b::braced(vec![ - b::path(b::var("it"), vec![b::ident("size")]), + b::path(b::var("it"), vec![b::member("size")]), b::sp(), b::op(">"), b::sp(), diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index b187ef64b..573b04c5a 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -15,7 +15,7 @@ pub enum TokenNode { Pipeline(Spanned), Operator(Spanned), Flag(Spanned), - Identifier(Span), + Member(Span), Whitespace(Span), #[allow(unused)] Error(Spanned>), @@ -92,13 +92,29 @@ impl TokenNode { TokenNode::Pipeline(s) => s.span, TokenNode::Operator(s) => s.span, TokenNode::Flag(s) => s.span, - TokenNode::Identifier(s) => *s, + TokenNode::Member(s) => *s, TokenNode::Whitespace(s) => *s, TokenNode::Error(s) => s.span, TokenNode::Path(s) => s.span, } } + pub fn type_name(&self) -> String { + match self { + TokenNode::Token(t) => t.type_name(), + TokenNode::Call(s) => "command", + TokenNode::Delimited(d) => d.type_name(), + TokenNode::Pipeline(s) => "pipeline", + TokenNode::Operator(s) => "operator", + TokenNode::Flag(s) => "flag", + TokenNode::Member(s) => "member", + TokenNode::Whitespace(s) => "whitespace", + TokenNode::Error(s) => "error", + TokenNode::Path(s) => "path", + } + .to_string() + } + pub fn debug(&'a self, source: &'a Text) -> DebugTokenNode<'a> { DebugTokenNode { node: self, source } } @@ -147,6 +163,16 @@ pub struct DelimitedNode { children: Vec, } +impl DelimitedNode { + pub fn type_name(&self) -> &'static str { + match self.delimiter { + Delimiter::Brace => "braced expression", + Delimiter::Paren => "parenthesized expression", + Delimiter::Square => "array literal or index operator", + } + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, FromStr)] pub enum Delimiter { Paren, diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index e423c8afa..d6a993aa4 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -266,17 +266,17 @@ impl TokenTreeBuilder { )) } - pub fn ident(input: impl Into) -> CurriedToken { + pub fn member(input: impl Into) -> CurriedToken { let input = input.into(); Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::spanned_ident((start, end)) + TokenTreeBuilder::spanned_member((start, end)) }) } - pub fn spanned_ident(span: impl Into) -> TokenNode { - TokenNode::Identifier(span.into()) + pub fn spanned_member(span: impl Into) -> TokenNode { + TokenNode::Member(span.into()) } pub fn call(head: CurriedToken, input: Vec) -> CurriedCall { diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index fc63500b4..16a9c8816 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -86,7 +86,7 @@ fn parse_command_tail( named.insert_switch(name, flag); } - NamedType::Mandatory(kind) => { + NamedType::Mandatory(syntax_type) => { match extract_mandatory(config, name, tail, source, command_span) { Err(err) => return Err(err), // produce a correct diagnostic Ok((pos, flag)) => { @@ -100,19 +100,15 @@ fn parse_command_tail( )); } - let expr = hir::baseline_parse_next_expr( - tail, - registry, - source, - kind.to_coerce_hint(), - )?; + let expr = + hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?; tail.restart(); named.insert_mandatory(name, expr); } } } - NamedType::Optional(kind) => match extract_optional(name, tail, source) { + NamedType::Optional(syntax_type) => match extract_optional(name, tail, source) { Err(err) => return Err(err), // produce a correct diagnostic Ok(Some((pos, flag))) => { tail.move_to(pos); @@ -125,12 +121,7 @@ fn parse_command_tail( )); } - let expr = hir::baseline_parse_next_expr( - tail, - registry, - source, - kind.to_coerce_hint(), - )?; + let expr = hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?; tail.restart(); named.insert_optional(name, Some(expr)); @@ -169,7 +160,7 @@ fn parse_command_tail( } } - let result = hir::baseline_parse_next_expr(tail, registry, source, arg.to_coerce_hint())?; + let result = hir::baseline_parse_next_expr(tail, registry, source, arg.syntax_type())?; positional.push(result); } diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 2bdb06c23..0d54f0429 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -1,5 +1,5 @@ use crate::evaluate::{evaluate_baseline_expr, Scope}; -use crate::parser::{hir, hir::ExpressionKindHint, parse_command, CallNode, Spanned}; +use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode, Spanned}; use crate::prelude::*; use derive_new::new; use getset::Getters; @@ -12,55 +12,34 @@ use std::fmt; #[derive(Debug, Serialize, Deserialize)] pub enum NamedType { Switch, - Mandatory(NamedValue), - Optional(NamedValue), -} - -#[derive(Debug, Serialize, Deserialize)] -pub enum NamedValue { - Single, - - #[allow(unused)] - Block, -} - -impl NamedValue { - crate fn to_coerce_hint(&self) -> Option { - match self { - NamedValue::Single => None, - NamedValue::Block => Some(ExpressionKindHint::Block), - } - } + Mandatory(SyntaxType), + Optional(SyntaxType), } #[allow(unused)] #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PositionalType { - Mandatory(String, PositionalValue), - Optional(String, PositionalValue), -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum PositionalValue { - Value, - Block, + Mandatory(String, SyntaxType), + Optional(String, SyntaxType), } impl PositionalType { - pub fn mandatory(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), PositionalValue::Value) + pub fn mandatory(name: &str, ty: SyntaxType) -> PositionalType { + PositionalType::Mandatory(name.to_string(), ty) + } + + pub fn mandatory_any(name: &str) -> PositionalType { + PositionalType::Mandatory(name.to_string(), SyntaxType::Any) } pub fn mandatory_block(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), PositionalValue::Block) + PositionalType::Mandatory(name.to_string(), SyntaxType::Block) } - crate fn to_coerce_hint(&self) -> Option { + crate fn to_coerce_hint(&self) -> Option { match self { - PositionalType::Mandatory(_, PositionalValue::Block) - | PositionalType::Optional(_, PositionalValue::Block) => { - Some(ExpressionKindHint::Block) - } + PositionalType::Mandatory(_, SyntaxType::Block) + | PositionalType::Optional(_, SyntaxType::Block) => Some(SyntaxType::Block), _ => None, } } @@ -71,6 +50,13 @@ impl PositionalType { PositionalType::Optional(s, _) => s, } } + + crate fn syntax_type(&self) -> SyntaxType { + match *self { + PositionalType::Mandatory(_, t) => t, + PositionalType::Optional(_, t) => t, + } + } } #[derive(Debug, Getters, Serialize, Deserialize)] diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index be2a013df..0e41d585b 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -17,7 +17,7 @@ impl Plugin for Inc { fn config(&mut self) -> Result { Ok(CommandConfig { name: "inc".to_string(), - positional: vec![PositionalType::mandatory("Increment")], + positional: vec![PositionalType::mandatory_any("Increment")], is_filter: true, is_sink: false, named: IndexMap::new(), diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 020e313dc..809b8c065 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -101,7 +101,7 @@ fn paint_token_node(token_node: &TokenNode, line: &str) -> String { TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.span().slice(line)), TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.span().slice(line)), TokenNode::Flag(..) => Color::Black.bold().paint(token_node.span().slice(line)), - TokenNode::Identifier(..) => Color::Yellow.bold().paint(token_node.span().slice(line)), + TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.span().slice(line)), TokenNode::Path(..) => Color::Green.bold().paint(token_node.span().slice(line)), TokenNode::Error(..) => Color::Red.bold().paint(token_node.span().slice(line)), TokenNode::Delimited(..) => Color::White.paint(token_node.span().slice(line)), @@ -139,7 +139,12 @@ fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> Str styled.push_str(&Color::White.normal().paint(ws.slice(line))); } - styled.push_str(&Color::Cyan.bold().paint(pipeline_element.call().head().span().slice(line)).to_string()); + styled.push_str( + &Color::Cyan + .bold() + .paint(pipeline_element.call().head().span().slice(line)) + .to_string(), + ); if let Some(children) = pipeline_element.call().children() { for child in children {