diff options
author | Christian Vallentin <mail@vallentin.dev> | 2020-12-01 10:04:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-01 10:04:14 +0100 |
commit | 266c606e39402a7c0cd357cdda8881e8f2ea417f (patch) | |
tree | e8e6f9930ea22f4c701eb678cb85c706f362ba93 /askama_shared | |
parent | f4065b09b91f5d00efa5644915cdd83bfb7d393a (diff) | |
download | askama-266c606e39402a7c0cd357cdda8881e8f2ea417f.tar.gz askama-266c606e39402a7c0cd357cdda8881e8f2ea417f.tar.bz2 askama-266c606e39402a7c0cd357cdda8881e8f2ea417f.zip |
Fixed parsing precedence and associativity (#391)
Diffstat (limited to 'askama_shared')
-rw-r--r-- | askama_shared/src/parser.rs | 206 |
1 files changed, 173 insertions, 33 deletions
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index 9344c07..7224d02 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -573,36 +573,32 @@ fn expr_rust_macro(i: &[u8]) -> IResult<&[u8], Expr> { macro_rules! expr_prec_layer { ( $name:ident, $inner:ident, $op:expr ) => { fn $name(i: &[u8]) -> IResult<&[u8], Expr> { - let (i, (left, op_and_right)) = tuple(( + let (i, left) = $inner(i)?; + let (i, right) = many0(pair( + ws(tag($op)), $inner, - opt(pair( - ws(tag($op)), - expr_any, - )) ))(i)?; - Ok((i, match op_and_right { - Some((op, right)) => Expr::BinOp( - str::from_utf8(op).unwrap(), Box::new(left), Box::new(right) - ), - None => left, - })) + Ok(( + i, + right.into_iter().fold(left, |left, (op, right)| { + Expr::BinOp(str::from_utf8(op).unwrap(), Box::new(left), Box::new(right)) + }), + )) } }; ( $name:ident, $inner:ident, $( $op:expr ),+ ) => { fn $name(i: &[u8]) -> IResult<&[u8], Expr> { - let (i, (left, op_and_right)) = tuple(( + let (i, left) = $inner(i)?; + let (i, right) = many0(pair( + ws(alt(($( tag($op) ),*,))), $inner, - opt(pair( - ws(alt(($( tag($op) ),*,))), - expr_any - )) ))(i)?; - Ok((i, match op_and_right { - Some((op, right)) => Expr::BinOp( - str::from_utf8(op).unwrap(), Box::new(left), Box::new(right) - ), - None => left, - })) + Ok(( + i, + right.into_iter().fold(left, |left, (op, right)| { + Expr::BinOp(str::from_utf8(op).unwrap(), Box::new(left), Box::new(right)) + }), + )) } } } @@ -1078,12 +1074,13 @@ pub fn parse<'a>(src: &'a str, syntax: &'a Syntax<'a>) -> Result<Vec<Node<'a>>, #[cfg(test)] mod tests { + use super::{Expr, Node, WS}; use crate::Syntax; fn check_ws_split(s: &str, res: &(&str, &str, &str)) { let node = super::split_ws_parts(s.as_bytes()); match node { - super::Node::Lit(lws, s, rws) => { + Node::Lit(lws, s, rws) => { assert_eq!(lws, res.0); assert_eq!(s, res.1); assert_eq!(rws, res.2); @@ -1118,12 +1115,9 @@ mod tests { fn test_parse_var_call() { assert_eq!( super::parse("{{ function(\"123\", 3) }}", &Syntax::default()).unwrap(), - vec![super::Node::Expr( - super::WS(false, false), - super::Expr::VarCall( - "function", - vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")] - ), + vec![Node::Expr( + WS(false, false), + Expr::VarCall("function", vec![Expr::StrLit("123"), Expr::NumLit("3")]), )], ); } @@ -1132,11 +1126,11 @@ mod tests { fn test_parse_path_call() { assert_eq!( super::parse("{{ self::function(\"123\", 3) }}", &Syntax::default()).unwrap(), - vec![super::Node::Expr( - super::WS(false, false), - super::Expr::PathCall( + vec![Node::Expr( + WS(false, false), + Expr::PathCall( vec!["self", "function"], - vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")], + vec![Expr::StrLit("123"), Expr::NumLit("3")], ), )], ); @@ -1152,6 +1146,152 @@ mod tests { super::parse("{~ strvar|e ~}", &syntax).unwrap(); } + + #[test] + fn test_precedence() { + use Expr::*; + let syntax = Syntax::default(); + assert_eq!( + super::parse("{{ a + b == c }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "==", + BinOp("+", Var("a").into(), Var("b").into()).into(), + Var("c").into(), + ) + )], + ); + assert_eq!( + super::parse("{{ a + b * c - d / e }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "-", + BinOp( + "+", + Var("a").into(), + BinOp("*", Var("b").into(), Var("c").into()).into(), + ) + .into(), + BinOp("/", Var("d").into(), Var("e").into()).into(), + ) + )], + ); + assert_eq!( + super::parse("{{ a * (b + c) / -d }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "/", + BinOp( + "*", + Var("a").into(), + Group(BinOp("+", Var("b").into(), Var("c").into()).into()).into() + ) + .into(), + Unary("-", Var("d").into()).into() + ) + )], + ); + assert_eq!( + super::parse("{{ a || b && c || d && e }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "||", + BinOp( + "||", + Var("a").into(), + BinOp("&&", Var("b").into(), Var("c").into()).into(), + ) + .into(), + BinOp("&&", Var("d").into(), Var("e").into()).into(), + ) + )], + ); + } + + #[test] + fn test_associativity() { + use Expr::*; + let syntax = Syntax::default(); + assert_eq!( + super::parse("{{ a + b + c }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "+", + BinOp("+", Var("a").into(), Var("b").into()).into(), + Var("c").into() + ) + )], + ); + assert_eq!( + super::parse("{{ a * b * c }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "*", + BinOp("*", Var("a").into(), Var("b").into()).into(), + Var("c").into() + ) + )], + ); + assert_eq!( + super::parse("{{ a && b && c }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "&&", + BinOp("&&", Var("a").into(), Var("b").into()).into(), + Var("c").into() + ) + )], + ); + assert_eq!( + super::parse("{{ a + b - c + d }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "+", + BinOp( + "-", + BinOp("+", Var("a").into(), Var("b").into()).into(), + Var("c").into() + ) + .into(), + Var("d").into() + ) + )], + ); + assert_eq!( + super::parse("{{ a == b != c > d > e == f }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + BinOp( + "==", + BinOp( + ">", + BinOp( + ">", + BinOp( + "!=", + BinOp("==", Var("a").into(), Var("b").into()).into(), + Var("c").into() + ) + .into(), + Var("d").into() + ) + .into(), + Var("e").into() + ) + .into(), + Var("f").into() + ) + )], + ); + } } type ParserError<'a, T> = Result<(&'a [u8], T), nom::Err<nom::error::Error<&'a [u8]>>>; |