This commit is contained in:
JT 2021-07-02 18:44:37 +12:00
parent 4ef65f0983
commit ba2e3d94eb
3 changed files with 210 additions and 3 deletions

View file

@ -10,7 +10,8 @@ fn main() -> std::io::Result<()> {
//let file = std::fs::read(&path)?; //let file = std::fs::read(&path)?;
//let (output, err) = working_set.parse_file(&path, &file); //let (output, err) = working_set.parse_file(&path, &file);
let (output, err) = working_set.parse_source(path.as_bytes()); let (output, err) = working_set.parse_source(path.as_bytes());
println!("{:?} {:?}", output, err); println!("{:#?}", output);
println!("error: {:?}", err);
Ok(()) Ok(())
} else { } else {

View file

@ -15,4 +15,5 @@ pub enum ParseError {
ShortFlagBatchCantTakeArg(Span), ShortFlagBatchCantTakeArg(Span),
MissingPositional(String, Span), MissingPositional(String, Span),
MissingRequiredFlag(String, Span), MissingRequiredFlag(String, Span),
IncompleteMathExpression(Span),
} }

View file

@ -10,7 +10,7 @@ use crate::{
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum SyntaxShape { pub enum SyntaxShape {
/// A specific match to a word or symbol /// A specific match to a word or symbol
Word(Vec<u8>), Literal(Vec<u8>),
/// Any syntactic form is allowed /// Any syntactic form is allowed
Any, Any,
/// Strings and string-like bare words are allowed /// Strings and string-like bare words are allowed
@ -44,6 +44,30 @@ pub enum SyntaxShape {
RowCondition, RowCondition,
/// A general math expression, eg `1 + 2` /// A general math expression, eg `1 + 2`
MathExpression, MathExpression,
/// A general expression, eg `1 + 2` or `foo --bar`
Expression,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Operator {
Equal,
NotEqual,
LessThan,
GreaterThan,
LessThanOrEqual,
GreaterThanOrEqual,
Contains,
NotContains,
Plus,
Minus,
Multiply,
Divide,
In,
NotIn,
Modulo,
And,
Or,
Pow,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -75,6 +99,8 @@ pub enum Expr {
Int(i64), Int(i64),
Var(VarId), Var(VarId),
Call(Call), Call(Call),
Operator(Operator),
BinaryOp(Box<Expression>, Box<Expression>, Box<Expression>), //lhs, op, rhs
Garbage, Garbage,
} }
@ -92,6 +118,32 @@ impl Expression {
ty: Type::Unknown, ty: Type::Unknown,
} }
} }
pub fn precedence(&self) -> usize {
match &self.expr {
Expr::Operator(operator) => {
// Higher precedence binds tighter
match operator {
Operator::Pow => 100,
Operator::Multiply | Operator::Divide | Operator::Modulo => 95,
Operator::Plus | Operator::Minus => 90,
Operator::NotContains
| Operator::Contains
| Operator::LessThan
| Operator::LessThanOrEqual
| Operator::GreaterThan
| Operator::GreaterThanOrEqual
| Operator::Equal
| Operator::NotEqual
| Operator::In
| Operator::NotIn => 80,
Operator::And => 50,
Operator::Or => 40, // TODO: should we have And and Or be different precedence?
}
}
_ => 0,
}
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -497,6 +549,27 @@ impl ParserWorkingSet {
) )
} }
} }
SyntaxShape::Any => {
let shapes = vec![
SyntaxShape::Int,
SyntaxShape::Number,
SyntaxShape::Range,
SyntaxShape::Filesize,
SyntaxShape::Duration,
SyntaxShape::Block,
SyntaxShape::Table,
SyntaxShape::String,
];
for shape in shapes.iter() {
if let (s, None) = self.parse_arg(span, shape.clone()) {
return (s, None);
}
}
(
garbage(span),
Some(ParseError::Mismatch("any shape".into(), span)),
)
}
_ => ( _ => (
garbage(span), garbage(span),
Some(ParseError::Mismatch("number".into(), span)), Some(ParseError::Mismatch("number".into(), span)),
@ -504,8 +577,140 @@ impl ParserWorkingSet {
} }
} }
pub fn parse_operator(&mut self, span: Span) -> (Expression, Option<ParseError>) {
let contents = self.get_span_contents(span);
let operator = match contents {
b"==" => Operator::Equal,
b"!=" => Operator::NotEqual,
b"<" => Operator::LessThan,
b"<=" => Operator::LessThanOrEqual,
b">" => Operator::GreaterThan,
b">=" => Operator::GreaterThanOrEqual,
b"=~" => Operator::Contains,
b"!~" => Operator::NotContains,
b"+" => Operator::Plus,
b"-" => Operator::Minus,
b"*" => Operator::Multiply,
b"/" => Operator::Divide,
b"in" => Operator::In,
b"not-in" => Operator::NotIn,
b"mod" => Operator::Modulo,
b"&&" => Operator::And,
b"||" => Operator::Or,
b"**" => Operator::Pow,
_ => {
return (
garbage(span),
Some(ParseError::Mismatch("operator".into(), span)),
);
}
};
(
Expression {
expr: Expr::Operator(operator),
ty: Type::Unknown,
span,
},
None,
)
}
pub fn parse_math_expression(&mut self, spans: &[Span]) -> (Expression, Option<ParseError>) { pub fn parse_math_expression(&mut self, spans: &[Span]) -> (Expression, Option<ParseError>) {
self.parse_arg(spans[0], SyntaxShape::Number) // As the expr_stack grows, we increase the required precedence to grow larger
// If, at any time, the operator we're looking at is the same or lower precedence
// of what is in the expression stack, we collapse the expression stack.
//
// This leads to an expression stack that grows under increasing precedence and collapses
// under decreasing/sustained precedence
//
// The end result is a stack that we can fold into binary operations as right associations
// safely.
let mut expr_stack: Vec<Expression> = vec![];
let mut idx = 0;
let mut last_prec = 1000000;
let mut error = None;
let (lhs, err) = self.parse_arg(spans[0], SyntaxShape::Any);
error = error.or(err);
idx += 1;
expr_stack.push(lhs);
while idx < spans.len() {
let (op, err) = self.parse_operator(spans[idx]);
error = error.or(err);
let op_prec = op.precedence();
idx += 1;
if idx == spans.len() {
// Handle broken math expr `1 +` etc
error = error.or(Some(ParseError::IncompleteMathExpression(spans[idx - 1])));
break;
}
let (rhs, err) = self.parse_arg(spans[idx], SyntaxShape::Any);
error = error.or(err);
if op_prec <= last_prec {
while expr_stack.len() > 1 {
// Collapse the right associated operations first
// so that we can get back to a stack with a lower precedence
let rhs = expr_stack
.pop()
.expect("internal error: expression stack empty");
let op = expr_stack
.pop()
.expect("internal error: expression stack empty");
let lhs = expr_stack
.pop()
.expect("internal error: expression stack empty");
let op_span = span(&[lhs.span, rhs.span]);
expr_stack.push(Expression {
expr: Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
span: op_span,
ty: Type::Unknown,
});
}
}
expr_stack.push(op);
expr_stack.push(rhs);
last_prec = op_prec;
idx += 1;
}
while expr_stack.len() != 1 {
let rhs = expr_stack
.pop()
.expect("internal error: expression stack empty");
let op = expr_stack
.pop()
.expect("internal error: expression stack empty");
let lhs = expr_stack
.pop()
.expect("internal error: expression stack empty");
let binary_op_span = span(&[lhs.span, rhs.span]);
expr_stack.push(Expression {
expr: Expr::BinaryOp(Box::new(lhs), Box::new(op), Box::new(rhs)),
ty: Type::Unknown,
span: binary_op_span,
});
}
let output = expr_stack
.pop()
.expect("internal error: expression stack empty");
(output, error)
} }
pub fn parse_expression(&mut self, spans: &[Span]) -> (Expression, Option<ParseError>) { pub fn parse_expression(&mut self, spans: &[Span]) -> (Expression, Option<ParseError>) {