From e540f0ad26c1b62cfc0d849998a9a60eae725e9c Mon Sep 17 00:00:00 2001 From: JT Date: Thu, 8 Jul 2021 10:55:46 +1200 Subject: [PATCH] start adding row expr parsing --- src/main.rs | 15 +++++++++ src/parser.rs | 77 ++++++++++++++++++++++++++++++++++++++++++--- src/parser_state.rs | 8 ++--- src/signature.rs | 4 +++ 4 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index b963a769ba..c7b2b8e6ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,21 @@ fn main() -> std::io::Result<()> { let sig = Signature::build("foo").named("--jazz", SyntaxShape::Int, "jazz!!", Some('j')); working_set.add_decl((b"foo").to_vec(), sig); + let sig = + Signature::build("where").required("cond", SyntaxShape::RowCondition, "condition"); + working_set.add_decl((b"where").to_vec(), sig); + + let sig = Signature::build("if") + .required("cond", SyntaxShape::RowCondition, "condition") + .required("then_block", SyntaxShape::Block, "then block") + .required( + "else", + SyntaxShape::Literal(b"else".to_vec()), + "else keyword", + ) + .required("else_block", SyntaxShape::Block, "else block"); + working_set.add_decl((b"if").to_vec(), sig); + //let file = std::fs::read(&path)?; //let (output, err) = working_set.parse_file(&path, file); let (output, err) = working_set.parse_source(path.as_bytes()); diff --git a/src/parser.rs b/src/parser.rs index 3bbf0b5bbf..636b911692 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -11,39 +11,56 @@ use crate::{ pub enum SyntaxShape { /// A specific match to a word or symbol Literal(Vec), + /// Any syntactic form is allowed Any, + /// Strings and string-like bare words are allowed String, + /// A dotted path to navigate the table ColumnPath, + /// A dotted path to navigate the table (including variable) FullColumnPath, + /// Only a numeric (integer or decimal) value is allowed Number, + /// A range is allowed (eg, `1..3`) Range, + /// Only an integer value is allowed Int, + /// A filepath is allowed FilePath, + /// A glob pattern is allowed, eg `foo*` GlobPattern, + /// A block is allowed, eg `{start this thing}` Block, + /// A table is allowed, eg `[first second]` Table, + /// A filesize value is allowed, eg `10kb` Filesize, + /// A duration value is allowed, eg `19day` Duration, + /// An operator Operator, + /// A math expression which expands shorthand forms on the lefthand side, eg `foo > 1` /// The shorthand allows us to more easily reach columns inside of the row being passed in RowCondition, + /// A general math expression, eg `1 + 2` MathExpression, + /// A general expression, eg `1 + 2` or `foo --bar` Expression, } @@ -105,6 +122,7 @@ pub enum Expr { Block(Box), List(Vec), Table(Vec, Vec>), + Literal(Vec), String(String), // FIXME: improve this in the future? Garbage, } @@ -290,6 +308,7 @@ impl ParserWorkingSet { if let Some(decl_id) = self.find_decl(name) { let mut call = Call::new(); + call.decl_id = decl_id; let sig = self .get_decl(decl_id) @@ -402,10 +421,35 @@ impl ParserWorkingSet { } } } else if let Some(positional) = sig.get_positional(positional_idx) { - let (arg, err) = self.parse_arg(arg_span, positional.shape); - error = error.or(err); + match positional.shape { + SyntaxShape::RowCondition => { + let remainder = sig.num_positionals() - positional_idx; - call.positional.push(arg); + if spans.len() < remainder { + error = error.or_else(|| { + Some(ParseError::MissingPositional( + "required args".into(), + arg_span, + )) + }); + } else { + let (arg, err) = self.parse_row_condition( + &spans[arg_offset..(spans.len() - remainder + 1)], + ); + error = error.or(err); + call.positional.push(arg); + + arg_offset = spans.len() - remainder; + } + } + _ => { + let (arg, err) = self.parse_arg(arg_span, positional.shape); + error = error.or(err); + + call.positional.push(arg); + } + } + positional_idx += 1; } else { error = error.or(Some(ParseError::ExtraPositional(arg_span))) } @@ -584,6 +628,10 @@ impl ParserWorkingSet { } } + pub fn parse_row_condition(&mut self, spans: &[Span]) -> (Expression, Option) { + self.parse_math_expression(spans) + } + pub fn parse_table_expression(&mut self, span: Span) -> (Expression, Option) { let bytes = self.get_span_contents(span); let mut error = None; @@ -786,7 +834,26 @@ impl ParserWorkingSet { } else { ( garbage(span), - Some(ParseError::Mismatch("number".into(), span)), + Some(ParseError::Mismatch("int".into(), span)), + ) + } + } + SyntaxShape::Literal(literal) => { + if bytes == literal { + ( + Expression { + expr: Expr::Literal(literal), + span, + }, + None, + ) + } else { + ( + garbage(span), + Some(ParseError::Mismatch( + format!("keyword '{}'", String::from_utf8_lossy(&literal)), + span, + )), ) } } @@ -815,7 +882,7 @@ impl ParserWorkingSet { } _ => ( garbage(span), - Some(ParseError::Mismatch("number".into(), span)), + Some(ParseError::Mismatch("incomplete parser".into(), span)), ), } } diff --git a/src/parser_state.rs b/src/parser_state.rs index e37260736e..76451ab233 100644 --- a/src/parser_state.rs +++ b/src/parser_state.rs @@ -190,8 +190,8 @@ impl ParserWorkingSet { } pub fn find_decl(&self, name: &[u8]) -> Option { - for scope in self.scope.iter().rev().enumerate() { - if let Some(decl_id) = scope.1.decls.get(name) { + for scope in self.scope.iter().rev() { + if let Some(decl_id) = scope.decls.get(name) { return Some(*decl_id); } } @@ -209,8 +209,8 @@ impl ParserWorkingSet { } pub fn find_variable(&self, name: &[u8]) -> Option { - for scope in self.scope.iter().rev().enumerate() { - if let Some(var_id) = scope.1.vars.get(name) { + for scope in self.scope.iter().rev() { + if let Some(var_id) = scope.vars.get(name) { return Some(*var_id); } } diff --git a/src/signature.rs b/src/signature.rs index c003eba8ad..f2a0874652 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -190,6 +190,10 @@ impl Signature { } } + pub fn num_positionals(&self) -> usize { + self.required_positional.len() + self.optional_positional.len() + } + /// Find the matching long flag pub fn get_long_flag(&self, name: &str) -> Option { for flag in &self.named {