From 540cc4016ef403f7d1265bb79e2075e451559c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sat, 26 Oct 2019 20:01:58 -0500 Subject: [PATCH] Expand tilde in patterns. --- src/errors.rs | 8 +- src/evaluate/evaluator.rs | 2 +- src/parser/hir.rs | 12 +- src/parser/hir/baseline_parse/tests.rs | 32 +--- src/parser/hir/syntax_shape.rs | 14 +- .../hir/syntax_shape/expression/atom.rs | 5 +- .../hir/syntax_shape/expression/pattern.rs | 47 ++---- .../hir/syntax_shape/expression/string.rs | 8 +- .../syntax_shape/expression/variable_path.rs | 2 +- src/parser/hir/tokens_iterator.rs | 2 +- src/parser/parse/parser.rs | 4 +- src/parser/parse/token_tree.rs | 148 +++++++++++------- 12 files changed, 135 insertions(+), 149 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 11628dde4b..dfad5692a1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -367,6 +367,10 @@ impl ShellError { // pub fn string(title: impl Into) -> ShellError { // ProximateShellError::String(StringError::new(title.into(), String::new())).start() // } + // + // pub(crate) fn unreachable(title: impl Into) -> ShellError { + // ShellError::untagged_runtime_error(&format!("BUG: Unreachable: {}", title.into())) + // } pub(crate) fn unimplemented(title: impl Into) -> ShellError { ShellError::untagged_runtime_error(&format!("Unimplemented: {}", title.into())) @@ -375,10 +379,6 @@ impl ShellError { pub(crate) fn unexpected(title: impl Into) -> ShellError { ShellError::untagged_runtime_error(&format!("Unexpected: {}", title.into())) } - - pub(crate) fn unreachable(title: impl Into) -> ShellError { - ShellError::untagged_runtime_error(&format!("BUG: Unreachable: {}", title.into())) - } } #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 75eb2f4667..f1caa21f1d 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -148,7 +148,7 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged int.into(), hir::Literal::Size(int, unit) => unit.compute(int), hir::Literal::String(tag) => Value::string(tag.slice(source)), - hir::Literal::GlobPattern => Value::pattern(literal.tag().slice(source)), + hir::Literal::GlobPattern(pattern) => Value::pattern(pattern), hir::Literal::Bare => Value::string(literal.tag().slice(source)), }; diff --git a/src/parser/hir.rs b/src/parser/hir.rs index ac6423943d..7108b0f7f9 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -227,8 +227,8 @@ impl Expression { RawExpression::Literal(Literal::Bare).spanned(span) } - pub(crate) fn pattern(span: impl Into) -> Expression { - RawExpression::Literal(Literal::GlobPattern).spanned(span.into()) + pub(crate) fn pattern(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Literal(Literal::GlobPattern(inner.into())).spanned(outer.into()) } pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { @@ -297,7 +297,7 @@ pub enum Literal { Number(Number), Size(Number, Unit), String(Span), - GlobPattern, + GlobPattern(String), Bare, } @@ -315,7 +315,7 @@ impl std::fmt::Display for Tagged<&Literal> { Literal::Number(number) => write!(f, "{}", number), Literal::Size(number, unit) => write!(f, "{}{}", number, unit.as_str()), Literal::String(_) => write!(f, "String{{ {}..{} }}", span.start(), span.end()), - Literal::GlobPattern => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()), + Literal::GlobPattern(_) => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()), Literal::Bare => write!(f, "Bare{{ {}..{} }}", span.start(), span.end()), } } @@ -327,7 +327,7 @@ impl ToDebug for Spanned<&Literal> { Literal::Number(number) => write!(f, "{:?}", number), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), Literal::String(tag) => write!(f, "{}", tag.slice(source)), - Literal::GlobPattern => write!(f, "{}", self.span.slice(source)), + Literal::GlobPattern(_) => write!(f, "{}", self.span.slice(source)), Literal::Bare => write!(f, "{}", self.span.slice(source)), } } @@ -340,7 +340,7 @@ impl Literal { Literal::Size(..) => "size", Literal::String(..) => "string", Literal::Bare => "string", - Literal::GlobPattern => "pattern", + Literal::GlobPattern(_) => "pattern", } } } diff --git a/src/parser/hir/baseline_parse/tests.rs b/src/parser/hir/baseline_parse/tests.rs index d3b9248496..ddd4af4930 100644 --- a/src/parser/hir/baseline_parse/tests.rs +++ b/src/parser/hir/baseline_parse/tests.rs @@ -6,7 +6,7 @@ use crate::parser::hir::syntax_shape::*; use crate::parser::hir::TokensIterator; use crate::parser::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b}; use crate::parser::TokenNode; -use crate::{Span, SpannedItem, Tag, Tagged, Text}; +use crate::{Span, SpannedItem, Tag, Text}; use pretty_assertions::assert_eq; use std::fmt::Debug; @@ -63,7 +63,7 @@ fn test_parse_command() { vec![b::bare("ls"), b::sp(), b::pattern("*.txt")], |tokens| { let bare = tokens[0].expect_bare(); - let pat = tokens[2].span(); + let pattern = tokens[2].expect_pattern(); ClassifiedCommand::Internal(InternalCommand::new( "ls".to_string(), @@ -73,7 +73,7 @@ fn test_parse_command() { }, hir::Call { head: Box::new(hir::RawExpression::Command(bare).spanned(bare)), - positional: Some(vec![hir::Expression::pattern(pat)]), + positional: Some(vec![hir::Expression::pattern("*.txt", pattern)]), named: None, }, )) @@ -84,41 +84,19 @@ fn test_parse_command() { // ) }, ); - - parse_tokens( - VariablePathShape, - vec![ - b::var("cpu"), - b::op("."), - b::bare("amount"), - b::op("."), - b::string("max ghz"), - ], - |tokens| { - let (outer_var, inner_var) = tokens[0].expect_var(); - let amount = tokens[2].expect_bare(); - let (outer_max_ghz, _) = tokens[4].expect_string(); - - hir::Expression::path( - hir::Expression::variable(inner_var, outer_var), - vec!["amount".spanned(amount), "max ghz".spanned(outer_max_ghz)], - outer_var.until(outer_max_ghz), - ) - }, - ); } fn parse_tokens( shape: impl ExpandSyntax, tokens: Vec, - expected: impl FnOnce(Tagged<&[TokenNode]>) -> T, + expected: impl FnOnce(&[TokenNode]) -> T, ) { let tokens = b::token_list(tokens); let (tokens, source) = b::build(tokens); ExpandContext::with_empty(&Text::from(source), |context| { let tokens = tokens.expect_list(); - let mut iterator = TokensIterator::all(tokens.item, *context.span()); + let mut iterator = TokensIterator::all(tokens, *context.span()); let expr = expand_syntax(&shape, &mut iterator, &context); diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index 2169467e43..a38a77500b 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -497,18 +497,18 @@ pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>( token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { - trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); + trace!(target: "nu::expand_expression", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); - let result = shape.expand_syntax(token_nodes, context); + let result = shape.expand_expr(token_nodes, context); match result { Err(err) => { - trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source)); + trace!(target: "nu::expand_expression", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source)); Err(err) } Ok(result) => { - trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source)); + trace!(target: "nu::expand_expression", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source)); Ok(result) } } @@ -719,11 +719,7 @@ impl TestSyntax for BareShape { let peeked = token_nodes.peek_any(); match peeked.node { - Some(TokenNode::Token(token)) => match token.item { - RawToken::Bare => Some(peeked), - _ => None, - }, - + Some(token) if token.is_bare() => Some(peeked), _ => None, } } diff --git a/src/parser/hir/syntax_shape/expression/atom.rs b/src/parser/hir/syntax_shape/expression/atom.rs index bb1b8065ec..888d9430e6 100644 --- a/src/parser/hir/syntax_shape/expression/atom.rs +++ b/src/parser/hir/syntax_shape/expression/atom.rs @@ -142,7 +142,10 @@ impl<'tokens> SpannedAtomicToken<'tokens> { Expression::external_command(*command, self.span) } AtomicToken::ExternalWord { text } => Expression::string(*text, self.span), - AtomicToken::GlobPattern { pattern } => Expression::pattern(*pattern), + AtomicToken::GlobPattern { pattern } => Expression::pattern( + expand_file_path(pattern.slice(context.source), context).to_string_lossy(), + self.span, + ), AtomicToken::Word { text } => Expression::string(*text, *text), AtomicToken::SquareDelimited { .. } => unimplemented!("into_hir"), AtomicToken::ParenDelimited { .. } => unimplemented!("into_hir"), diff --git a/src/parser/hir/syntax_shape/expression/pattern.rs b/src/parser/hir/syntax_shape/expression/pattern.rs index eab0b6e5bb..ed3bd610cd 100644 --- a/src/parser/hir/syntax_shape/expression/pattern.rs +++ b/src/parser/hir/syntax_shape/expression/pattern.rs @@ -1,7 +1,6 @@ use crate::parser::hir::syntax_shape::{ - expand_atom, expand_bare, expand_syntax, expression::expand_file_path, parse_single_node, - AtomicToken, ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, FallibleColorSyntax, - FlatShape, + expand_atom, expand_bare, expression::expand_file_path, AtomicToken, ExpandContext, + ExpandExpression, ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, }; use crate::parser::{hir, hir::TokensIterator, Operator, RawToken, TokenNode}; use crate::prelude::*; @@ -72,43 +71,17 @@ impl ExpandExpression for PatternShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - let pattern = expand_syntax(&BarePatternShape, token_nodes, context); + let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::new())?; - match pattern { - Ok(tag) => { - return Ok(hir::Expression::pattern(tag)); + match atom.item { + AtomicToken::Word { text: body } + | AtomicToken::String { body } + | AtomicToken::GlobPattern { pattern: body } => { + let path = expand_file_path(body.slice(context.source), context); + return Ok(hir::Expression::pattern(path.to_string_lossy(), atom.span)); } - Err(_) => {} + _ => return atom.into_hir(context, "pattern"), } - - parse_single_node(token_nodes, "Pattern", |token, token_tag, _| { - Ok(match token { - RawToken::GlobPattern => { - return Err(ShellError::unreachable( - "glob pattern after glob already returned", - )) - } - RawToken::Operator(..) => { - return Err(ShellError::unreachable("dot after glob already returned")) - } - RawToken::Bare => { - return Err(ShellError::unreachable("bare after glob already returned")) - } - - RawToken::Variable(tag) if tag.slice(context.source) == "it" => { - hir::Expression::it_variable(tag, token_tag) - } - RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), - RawToken::Number(_) => hir::Expression::bare(token_tag), - - RawToken::String(tag) => hir::Expression::file_path( - expand_file_path(tag.slice(context.source), context), - token_tag, - ), - }) - }) } } diff --git a/src/parser/hir/syntax_shape/expression/string.rs b/src/parser/hir/syntax_shape/expression/string.rs index 116ed8fd0d..46015376e8 100644 --- a/src/parser/hir/syntax_shape/expression/string.rs +++ b/src/parser/hir/syntax_shape/expression/string.rs @@ -3,7 +3,7 @@ use crate::parser::hir::syntax_shape::{ ExpansionRule, FallibleColorSyntax, FlatShape, TestSyntax, }; use crate::parser::hir::tokens_iterator::Peeked; -use crate::parser::{hir, hir::TokensIterator, RawToken, TokenNode}; +use crate::parser::{hir, hir::TokensIterator, RawToken}; use crate::prelude::*; #[derive(Debug, Copy, Clone)] @@ -118,11 +118,7 @@ impl TestSyntax for StringShape { let peeked = token_nodes.peek_any(); match peeked.node { - Some(TokenNode::Token(token)) => match token.item { - RawToken::String(_) => Some(peeked), - _ => None, - }, - + Some(token) if token.is_string() => Some(peeked), _ => None, } } diff --git a/src/parser/hir/syntax_shape/expression/variable_path.rs b/src/parser/hir/syntax_shape/expression/variable_path.rs index e983630348..5ed615a9e8 100644 --- a/src/parser/hir/syntax_shape/expression/variable_path.rs +++ b/src/parser/hir/syntax_shape/expression/variable_path.rs @@ -821,7 +821,7 @@ impl ExpandSyntax for MemberShape { if let Some(peeked) = string { let node = peeked.not_eof("column")?.commit(); - let (outer, inner) = node.expect_string(); + let (outer, inner) = node.as_string().unwrap(); return Ok(Member::String(outer, inner)); } diff --git a/src/parser/hir/tokens_iterator.rs b/src/parser/hir/tokens_iterator.rs index b3069247c9..8e2f4a8f88 100644 --- a/src/parser/hir/tokens_iterator.rs +++ b/src/parser/hir/tokens_iterator.rs @@ -566,7 +566,7 @@ impl<'content> TokensIterator<'content> { impl<'content> Iterator for TokensIterator<'content> { type Item = &'content TokenNode; - fn next(&mut self) -> Option<&'content TokenNode> { + fn next(&mut self) -> Option { next(self, self.state.skip_ws) } } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index f7fce7c814..0dd1bc8566 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -666,7 +666,7 @@ fn is_glob_specific_char(c: char) -> bool { } fn is_start_glob_char(c: char) -> bool { - is_start_bare_char(c) || is_glob_specific_char(c) + is_start_bare_char(c) || is_glob_specific_char(c) || c == '.' } fn is_glob_char(c: char) -> bool { @@ -1147,7 +1147,7 @@ mod tests { fn test_patterns() { equal_tokens! { - "cp ../formats/*" -> b::pipeline(vec![vec![b::bare("cp"), b::sp(), b::op("."), b::op("."), b::pattern("/formats/*")]]) + "cp ../formats/*" -> b::pipeline(vec![vec![b::bare("cp"), b::sp(), b::pattern("../formats/*")]]) } equal_tokens! { diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index cb335e925e..0d00dcff0d 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -155,6 +155,26 @@ impl TokenNode { } } + pub fn is_string(&self) -> bool { + match self { + TokenNode::Token(Spanned { + item: RawToken::String(_), + .. + }) => true, + _ => false, + } + } + + pub fn as_string(&self) -> Option<(Span, Span)> { + match self { + TokenNode::Token(Spanned { + item: RawToken::String(inner_span), + span: outer_span, + }) => Some((*outer_span, *inner_span)), + _ => None, + } + } + pub fn is_pattern(&self) -> bool { match self { TokenNode::Token(Spanned { @@ -200,16 +220,6 @@ impl TokenNode { } } - pub fn expect_external(&self) -> Span { - match self { - TokenNode::Token(Spanned { - item: RawToken::ExternalCommand(span), - .. - }) => *span, - _ => panic!("Only call expect_external if you checked is_external first"), - } - } - pub(crate) fn as_flag(&self, value: &str, source: &Text) -> Option> { match self { TokenNode::Flag( @@ -224,7 +234,7 @@ impl TokenNode { pub fn as_pipeline(&self) -> Result { match self { TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()), - _ => Err(ShellError::unimplemented("unimplemented")), + _ => Err(ShellError::type_error("pipeline", self.tagged_type_name())), } } @@ -234,49 +244,6 @@ impl TokenNode { _ => false, } } - - pub fn expect_string(&self) -> (Span, Span) { - match self { - TokenNode::Token(Spanned { - item: RawToken::String(inner_span), - span: outer_span, - }) => (*outer_span, *inner_span), - other => panic!("Expected string, found {:?}", other), - } - } -} - -#[cfg(test)] -impl TokenNode { - pub fn expect_list(&self) -> Tagged<&[TokenNode]> { - match self { - TokenNode::Nodes(Spanned { item, span }) => (&item[..]).tagged(Tag { - span: *span, - anchor: None, - }), - other => panic!("Expected list, found {:?}", other), - } - } - - pub fn expect_var(&self) -> (Span, Span) { - match self { - TokenNode::Token(Spanned { - item: RawToken::Variable(inner_span), - span: outer_span, - }) => (*outer_span, *inner_span), - other => panic!("Expected var, found {:?}", other), - } - } - - pub fn expect_bare(&self) -> Span { - match self { - TokenNode::Token(Spanned { - item: RawToken::Bare, - span, - }) => *span, - other => panic!("Expected bare, found {:?}", other), - } - } } #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] @@ -328,3 +295,76 @@ pub struct PathNode { head: Box, tail: Vec, } + +#[cfg(test)] +impl TokenNode { + pub fn expect_external(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::ExternalCommand(span), + .. + }) => *span, + other => panic!( + "Only call expect_external if you checked is_external first, found {:?}", + other + ), + } + } + + pub fn expect_string(&self) -> (Span, Span) { + match self { + TokenNode::Token(Spanned { + item: RawToken::String(inner_span), + span: outer_span, + }) => (*outer_span, *inner_span), + other => panic!("Expected string, found {:?}", other), + } + } + + pub fn expect_list(&self) -> &[TokenNode] { + match self { + TokenNode::Nodes(token_nodes) => &token_nodes[..], + other => panic!("Expected list, found {:?}", other), + } + } + + pub fn expect_pattern(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::GlobPattern, + span: outer_span, + }) => *outer_span, + other => panic!("Expected pattern, found {:?}", other), + } + } + + pub fn expect_var(&self) -> (Span, Span) { + match self { + TokenNode::Token(Spanned { + item: RawToken::Variable(inner_span), + span: outer_span, + }) => (*outer_span, *inner_span), + other => panic!("Expected var, found {:?}", other), + } + } + + pub fn expect_dot(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::Operator(Operator::Dot), + span, + }) => *span, + other => panic!("Expected dot, found {:?}", other), + } + } + + pub fn expect_bare(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::Bare, + span, + }) => *span, + other => panic!("Expected bare, found {:?}", other), + } + } +}