Add operator precedence and refactor expressions
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
437584c844
commit
722e61cc85
3 changed files with 99 additions and 10 deletions
|
|
@ -654,17 +654,33 @@ fn parse_operator<'input>(input: &mut Input<'input>) -> Result<TokenOperator, As
|
||||||
fn parse_expression<'input>(
|
fn parse_expression<'input>(
|
||||||
input: &mut Input<'input>,
|
input: &mut Input<'input>,
|
||||||
) -> Result<TemplateAstExpr<'input>, AstError> {
|
) -> Result<TemplateAstExpr<'input>, AstError> {
|
||||||
|
macro_rules! infix {
|
||||||
|
($parser:expr => [ $($side:ident $val:tt => $prec:expr),* $(,)? ]) => {
|
||||||
|
dispatch! { surrounded(ws, parse_operator);
|
||||||
|
$(
|
||||||
|
TokenOperator::$val => Left($prec, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::$val, lhs: Box::new(lhs), rhs: Box::new(rhs) }))
|
||||||
|
),*
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
trace(
|
trace(
|
||||||
"expression",
|
"expression",
|
||||||
expression(surrounded(ws, parse_operand)).infix(dispatch! { surrounded(ws, parse_operator);
|
expression(surrounded(ws, parse_operand)).infix(
|
||||||
TokenOperator::Plus => Left(5, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Plus, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
|
infix! { surrounded(ws, parse_operator) => [
|
||||||
TokenOperator::Minus => Left(5, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Minus, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
|
left Plus => 18,
|
||||||
TokenOperator::Times => Left(7, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Times, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
|
left Minus => 18,
|
||||||
TokenOperator::Divide => Left(7, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Divide, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
|
left Times => 20,
|
||||||
TokenOperator::And => Left(7, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::And, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
|
left Divide => 20,
|
||||||
TokenOperator::Or => Left(5, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Or, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
|
left And => 10,
|
||||||
_ => winnow::combinator::todo
|
left Or => 7,
|
||||||
}),
|
left Equal => 12,
|
||||||
|
left NotEqual => 12,
|
||||||
|
left Greater => 15,
|
||||||
|
left GreaterOrEqual => 15,
|
||||||
|
left Lesser => 15,
|
||||||
|
left LesserOrEqual => 15,
|
||||||
|
] },
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.parse_next(input)
|
.parse_next(input)
|
||||||
}
|
}
|
||||||
|
|
@ -981,4 +997,15 @@ mod tests {
|
||||||
|
|
||||||
insta::assert_debug_snapshot!(ast);
|
insta::assert_debug_snapshot!(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn check_logical_expression() {
|
||||||
|
let input = "{{= true && false || 3 >= 2 && 5 == 2 }}";
|
||||||
|
|
||||||
|
let parsed = crate::parser::parse(input.into()).unwrap();
|
||||||
|
|
||||||
|
let ast = panic_pretty(input, parse(parsed.tokens()));
|
||||||
|
|
||||||
|
insta::assert_debug_snapshot!(ast);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,63 @@
|
||||||
|
---
|
||||||
|
source: src/ast/mod.rs
|
||||||
|
expression: ast
|
||||||
|
---
|
||||||
|
TemplateAst {
|
||||||
|
root: [
|
||||||
|
Interpolation {
|
||||||
|
prev_whitespace_content: None,
|
||||||
|
expression: Operation {
|
||||||
|
op: Or,
|
||||||
|
lhs: Operation {
|
||||||
|
op: And,
|
||||||
|
lhs: Literal {
|
||||||
|
source: [Literal(Bool(true))]"true" (4..8),
|
||||||
|
value: Bool {
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rhs: Literal {
|
||||||
|
source: [Literal(Bool(false))]"false" (12..17),
|
||||||
|
value: Bool {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rhs: Operation {
|
||||||
|
op: And,
|
||||||
|
lhs: Operation {
|
||||||
|
op: GreaterOrEqual,
|
||||||
|
lhs: Literal {
|
||||||
|
source: [Literal(Integer(3))]"3" (21..22),
|
||||||
|
value: Integer {
|
||||||
|
value: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rhs: Literal {
|
||||||
|
source: [Literal(Integer(2))]"2" (26..27),
|
||||||
|
value: Integer {
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rhs: Operation {
|
||||||
|
op: Equal,
|
||||||
|
lhs: Literal {
|
||||||
|
source: [Literal(Integer(5))]"5" (31..32),
|
||||||
|
value: Integer {
|
||||||
|
value: 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rhs: Literal {
|
||||||
|
source: [Literal(Integer(2))]"2" (36..37),
|
||||||
|
value: Integer {
|
||||||
|
value: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
post_whitespace_content: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
@ -33,7 +33,6 @@ use winnow::stream::Location;
|
||||||
use winnow::stream::Recoverable;
|
use winnow::stream::Recoverable;
|
||||||
use winnow::stream::Stream;
|
use winnow::stream::Stream;
|
||||||
use winnow::token::any;
|
use winnow::token::any;
|
||||||
use winnow::token::literal;
|
|
||||||
use winnow::token::one_of;
|
use winnow::token::one_of;
|
||||||
use winnow::token::rest;
|
use winnow::token::rest;
|
||||||
use winnow::token::take_until;
|
use winnow::token::take_until;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue