diff options
Diffstat (limited to '')
-rw-r--r-- | askama_shared/src/parser.rs | 247 |
1 files changed, 203 insertions, 44 deletions
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index 9344c07..a40e967 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -1,7 +1,7 @@ use nom::branch::alt; use nom::bytes::complete::{escaped, is_not, tag, take_until}; use nom::character::complete::{anychar, char, digit1}; -use nom::combinator::{complete, map, opt}; +use nom::combinator::{complete, map, opt, value}; use nom::error::ParseError; use nom::multi::{many0, many1, separated_list0, separated_list1}; use nom::sequence::{delimited, pair, tuple}; @@ -19,7 +19,7 @@ pub enum Node<'a> { LetDecl(WS, Target<'a>), Let(WS, Target<'a>, Expr<'a>), Cond(Vec<(WS, Option<Expr<'a>>, Vec<Node<'a>>)>, WS), - Match(WS, Expr<'a>, Option<&'a str>, Vec<When<'a>>, WS), + Match(WS, Expr<'a>, Vec<When<'a>>, WS), Loop(WS, Target<'a>, Expr<'a>, Vec<Node<'a>>, WS), Extends(Expr<'a>), BlockDef(WS, &'a str, Vec<Node<'a>>, WS), @@ -312,10 +312,12 @@ fn expr_var_call(i: &[u8]) -> IResult<&[u8], Expr> { } fn path(i: &[u8]) -> IResult<&[u8], Vec<&str>> { + let root = opt(value("", ws(tag("::")))); let tail = separated_list1(ws(tag("::")), identifier); - let (i, (start, _, rest)) = tuple((identifier, ws(tag("::")), tail))(i)?; - - let mut path = vec![start]; + let (i, (root, start, _, rest)) = tuple((root, identifier, ws(tag("::")), tail))(i)?; + let mut path = Vec::new(); + path.extend(root); + path.push(start); path.extend(rest); Ok((i, path)) } @@ -573,36 +575,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)) + }), + )) } } } @@ -773,8 +771,8 @@ fn block_match<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a> arms.push(arm); } - let inter = match inter { - Some(Node::Lit(lws, val, rws)) => { + match inter { + Some(Node::Lit(_, val, rws)) => { assert!( val.is_empty(), "only whitespace allowed between match and first when, found {}", @@ -785,18 +783,16 @@ fn block_match<'a>(i: &'a [u8], s: &'a Syntax<'a>) -> IResult<&'a [u8], Node<'a> "only whitespace allowed between match and first when, found {}", rws ); - Some(lws) } - None => None, + None => {} _ => panic!("only literals allowed between match and first when"), - }; + } Ok(( i, Node::Match( WS(pws1.is_some(), nws1.is_some()), expr, - inter, arms, WS(pws2.is_some(), nws2.is_some()), ), @@ -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,17 +1126,36 @@ 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")], ), )], ); } #[test] + fn test_parse_root_path() { + let syntax = Syntax::default(); + assert_eq!( + super::parse("{{ std::string::String::new() }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + Expr::PathCall(vec!["std", "string", "String", "new"], vec![]), + )], + ); + assert_eq!( + super::parse("{{ ::std::string::String::new() }}", &syntax).unwrap(), + vec![Node::Expr( + WS(false, false), + Expr::PathCall(vec!["", "std", "string", "String", "new"], vec![]), + )], + ); + } + + #[test] fn change_delimiters_parse_filter() { let syntax = Syntax { expr_start: "{~", @@ -1152,6 +1165,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]>>>; |