From 751595e72e575c0052c5cbf3086c22565261a088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Fri, 29 Oct 2021 23:50:28 +0300 Subject: [PATCH] Add multi-word name calling support --- crates/nu-parser/src/parser.rs | 214 ++++++++++++--------------------- 1 file changed, 75 insertions(+), 139 deletions(-) diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index deec9834a8..1046149e5f 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -11,7 +11,7 @@ use nu_protocol::{ Operator, PathMember, Pipeline, RangeInclusion, RangeOperator, Statement, }, engine::StateWorkingSet, - span, DeclId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, + span, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, }; use crate::parse_keywords::{ @@ -610,68 +610,21 @@ pub fn parse_internal_call( (Box::new(call), span(spans), error) } -pub fn parse_command_name( - working_set: &mut StateWorkingSet, - spans: &[Span], -) -> (Option, Option) { - if spans.len() == 0 { - ( - None, - Some(ParseError::UnknownState( - "Encountered command with zero spans".into(), - span(spans), - )), - ) - } else if spans.len() == 1 { - let bytes = working_set.get_span_contents(spans[0]); - (working_set.find_decl(bytes), None) - } else { - // Find the longest group of words that could form a command - let mut longest_name = working_set.get_span_contents(spans[0]).to_vec(); - let mut indices = vec![0]; - for span in spans[1..].iter() { - let bytes = working_set.get_span_contents(*span); - if is_math_expression_byte(bytes[0]) { - break; - } - indices.push(longest_name.len()); - longest_name.push(b' '); - longest_name.extend_from_slice(bytes); - } - - // Now, try if it matches a command and if not, peel off the last word and try again - let mut decl_id = working_set.find_decl(&longest_name); - let mut err = None; - while decl_id.is_none() { - let split_idx = if let Some(i) = indices.pop() { - i - } else { - decl_id = None; - err = Some(ParseError::UnknownState( - "Command has no words".into(), - span(spans), - )); - break; - }; - - if split_idx == 0 { - // This is the first word, we reached the end - break; - } - - decl_id = working_set.find_decl(&longest_name[..split_idx]); - } - - (decl_id, err) - } -} - pub fn parse_call( working_set: &mut StateWorkingSet, spans: &[Span], expand_aliases: bool, ) -> (Expression, Option) { - // assume spans.len() > 0? + if spans.is_empty() { + return ( + garbage(Span::unknown()), + Some(ParseError::UnknownState( + "Encountered command with zero spans".into(), + span(spans), + )), + ); + } + let mut pos = 0; let mut shorthand = vec![]; @@ -689,105 +642,82 @@ pub fn parse_call( if pos == spans.len() { return ( - Expression::garbage(span(spans)), + garbage(span(spans)), Some(ParseError::UnknownCommand(spans[0])), ); } - parse_command_name(working_set, &spans[pos..]); - let name = working_set.get_span_contents(spans[pos]); - let cmd_start = pos; + let mut name_spans = vec![]; - if expand_aliases { - if let Some(expansion) = working_set.find_alias(name) { - let orig_span = spans[pos]; - //let mut spans = spans.to_vec(); - let mut new_spans: Vec = vec![]; - new_spans.extend(&spans[0..pos]); - new_spans.extend(expansion); - if spans.len() > pos { - new_spans.extend(&spans[(pos + 1)..]); - } + for word_span in spans[cmd_start..].iter() { + // Find the longest group of words that could form a command + let bytes = working_set.get_span_contents(*word_span); - let (result, err) = parse_expression(working_set, &new_spans, false); + if is_math_expression_byte(bytes[0]) { + break; + } - let expression = match result { - Expression { - expr: Expr::Call(mut call), - span, - ty, - custom_completion: None, - } => { - call.head = orig_span; + name_spans.push(*word_span); + + let name = working_set.get_span_contents(span(&name_spans)); + + if expand_aliases { + // If the word is an alias, expand it and re-parse the expression + if let Some(expansion) = working_set.find_alias(name) { + let orig_span = spans[pos]; + let mut new_spans: Vec = vec![]; + new_spans.extend(&spans[0..pos]); + new_spans.extend(expansion); + if spans.len() > pos { + new_spans.extend(&spans[(pos + 1)..]); + } + + let (result, err) = parse_expression(working_set, &new_spans, false); + + let expression = match result { Expression { - expr: Expr::Call(call), + expr: Expr::Call(mut call), span, ty, custom_completion: None, - } - } - x => x, - }; - - return (expression, err); - } - } - - pos += 1; - - if let Some(mut decl_id) = working_set.find_decl(name) { - let mut name = name.to_vec(); - while pos < spans.len() { - // look to see if it's a subcommand - let mut new_name = name.to_vec(); - new_name.push(b' '); - new_name.extend(working_set.get_span_contents(spans[pos])); - - if expand_aliases { - if let Some(expansion) = working_set.find_alias(&new_name) { - let orig_span = span(&spans[cmd_start..pos + 1]); - //let mut spans = spans.to_vec(); - let mut new_spans: Vec = vec![]; - new_spans.extend(&spans[0..cmd_start]); - new_spans.extend(expansion); - if spans.len() > pos { - new_spans.extend(&spans[(pos + 1)..]); - } - - let (result, err) = parse_expression(working_set, &new_spans, false); - - let expression = match result { + } => { + call.head = orig_span; Expression { - expr: Expr::Call(mut call), + expr: Expr::Call(call), span, ty, custom_completion: None, - } => { - call.head = orig_span; - Expression { - expr: Expr::Call(call), - span, - ty, - custom_completion: None, - } } - x => x, - }; + } + x => x, + }; - return (expression, err); - } + return (expression, err); } - - if let Some(did) = working_set.find_decl(&new_name) { - decl_id = did; - } else { - break; - } - name = new_name; - pos += 1; } + pos += 1; + } + + let name = working_set.get_span_contents(span(&name_spans)); + let mut maybe_decl_id = working_set.find_decl(name); + + while maybe_decl_id.is_none() { + // Find the longest command match + if name_spans.len() <= 1 { + // Keep the first word even if it does not match -- could be external command + break; + } + + name_spans.pop(); + pos -= 1; + + let name = working_set.get_span_contents(span(&name_spans)); + maybe_decl_id = working_set.find_decl(name); + } + + if let Some(decl_id) = maybe_decl_id { // Before the internal parsing we check if there is no let or alias declarations // that are missing their name, e.g.: let = 1 or alias = 2 if spans.len() > 1 { @@ -795,7 +725,7 @@ pub fn parse_call( if test_equal == [b'='] { return ( - garbage(Span::new(0, 0)), + garbage(Span::unknown()), Some(ParseError::UnknownState( "Incomplete statement".into(), span(spans), @@ -805,8 +735,12 @@ pub fn parse_call( } // parse internal command - let (call, _, err) = - parse_internal_call(working_set, span(&spans[0..pos]), &spans[pos..], decl_id); + let (call, _, err) = parse_internal_call( + working_set, + span(&spans[cmd_start..pos]), + &spans[pos..], + decl_id, + ); ( Expression { expr: Expr::Call(call), @@ -825,6 +759,8 @@ pub fn parse_call( return (range_expr, range_err); } } + + // Otherwise, try external command parse_external_call(working_set, spans) } }