diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4351042..1b25884 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -654,17 +654,33 @@ fn parse_operator<'input>(input: &mut Input<'input>) -> Result( input: &mut Input<'input>, ) -> Result, 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); + } } diff --git a/src/ast/snapshots/nomo__ast__tests__check_logical_expression.snap b/src/ast/snapshots/nomo__ast__tests__check_logical_expression.snap new file mode 100644 index 0000000..e9caca2 --- /dev/null +++ b/src/ast/snapshots/nomo__ast__tests__check_logical_expression.snap @@ -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, + }, + ], +} diff --git a/src/parser/mod.rs b/src/parser/mod.rs index af65cf7..37e2757 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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;