Add operator precedence and refactor expressions

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-12 17:53:11 +01:00
parent 437584c844
commit 722e61cc85
3 changed files with 99 additions and 10 deletions

View file

@ -654,17 +654,33 @@ fn parse_operator<'input>(input: &mut Input<'input>) -> Result<TokenOperator, As
fn parse_expression<'input>(
input: &mut Input<'input>,
) -> 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(
"expression",
expression(surrounded(ws, parse_operand)).infix(dispatch! { surrounded(ws, parse_operator);
TokenOperator::Plus => Left(5, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Plus, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
TokenOperator::Minus => Left(5, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Minus, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
TokenOperator::Times => Left(7, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Times, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
TokenOperator::Divide => Left(7, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Divide, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
TokenOperator::And => Left(7, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::And, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
TokenOperator::Or => Left(5, |_, lhs, rhs| Ok(TemplateAstExpr::Operation { op: TokenOperator::Or, lhs: Box::new(lhs), rhs: Box::new(rhs) })),
_ => winnow::combinator::todo
}),
expression(surrounded(ws, parse_operand)).infix(
infix! { surrounded(ws, parse_operator) => [
left Plus => 18,
left Minus => 18,
left Times => 20,
left Divide => 20,
left And => 10,
left Or => 7,
left Equal => 12,
left NotEqual => 12,
left Greater => 15,
left GreaterOrEqual => 15,
left Lesser => 15,
left LesserOrEqual => 15,
] },
),
)
.parse_next(input)
}
@ -981,4 +997,15 @@ mod tests {
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);
}
}

View file

@ -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,
},
],
}

View file

@ -33,7 +33,6 @@ use winnow::stream::Location;
use winnow::stream::Recoverable;
use winnow::stream::Stream;
use winnow::token::any;
use winnow::token::literal;
use winnow::token::one_of;
use winnow::token::rest;
use winnow::token::take_until;