start adding row expr parsing

This commit is contained in:
JT 2021-07-08 10:55:46 +12:00
parent bf1a23afcf
commit e540f0ad26
4 changed files with 95 additions and 9 deletions

View file

@ -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());

View file

@ -11,39 +11,56 @@ use crate::{
pub enum SyntaxShape {
/// A specific match to a word or symbol
Literal(Vec<u8>),
/// 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<Block>),
List(Vec<Expression>),
Table(Vec<Expression>, Vec<Vec<Expression>>),
Literal(Vec<u8>),
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<ParseError>) {
self.parse_math_expression(spans)
}
pub fn parse_table_expression(&mut self, span: Span) -> (Expression, Option<ParseError>) {
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)),
),
}
}

View file

@ -190,8 +190,8 @@ impl ParserWorkingSet {
}
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
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<VarId> {
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);
}
}

View file

@ -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<Flag> {
for flag in &self.named {