From 4fc533340b8a8056e216b65418581b97b6471496 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= Date: Thu, 28 Oct 2021 00:52:59 +0300 Subject: [PATCH] Add function that searches for multi-word commands It doesn't do anything right now. --- crates/nu-parser/src/parse_keywords.rs | 1 + crates/nu-parser/src/parser.rs | 87 ++++++++++++++++++++++++-- 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index be6366bc7..3ebf61bbd 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -511,6 +511,7 @@ pub fn parse_use( working_set.get_block(block_id).exports.clone(), ) } else { + //TODO: Fix this // It could be a file let module_filename = String::from_utf8_lossy(&import_pattern.head).to_string(); let module_path = Path::new(&module_filename); diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 17c9d76ac..deec9834a 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, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, + span, DeclId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId, }; use crate::parse_keywords::{ @@ -39,6 +39,26 @@ fn is_identifier_byte(b: u8) -> bool { b != b'.' && b != b'[' && b != b'(' && b != b'{' } +fn is_math_expression_byte(b: u8) -> bool { + b == b'0' + || b == b'1' + || b == b'2' + || b == b'3' + || b == b'4' + || b == b'5' + || b == b'6' + || b == b'7' + || b == b'8' + || b == b'9' + || b == b'(' + || b == b'{' + || b == b'[' + || b == b'$' + || b == b'"' + || b == b'\'' + || b == b'-' +} + fn is_identifier(bytes: &[u8]) -> bool { bytes.iter().all(|x| is_identifier_byte(*x)) } @@ -590,6 +610,62 @@ 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], @@ -618,6 +694,7 @@ pub fn parse_call( ); } + parse_command_name(working_set, &spans[pos..]); let name = working_set.get_span_contents(spans[pos]); let cmd_start = pos; @@ -2931,10 +3008,10 @@ pub fn parse_expression( ) -> (Expression, Option) { let bytes = working_set.get_span_contents(spans[0]); - match bytes[0] { - b'0' | b'1' | b'2' | b'3' | b'4' | b'5' | b'6' | b'7' | b'8' | b'9' | b'(' | b'{' - | b'[' | b'$' | b'"' | b'\'' | b'-' => parse_math_expression(working_set, spans, None), - _ => parse_call(working_set, spans, expand_aliases), + if is_math_expression_byte(bytes[0]) { + parse_math_expression(working_set, spans, None) + } else { + parse_call(working_set, spans, expand_aliases) } }