aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared/src/parser.rs
diff options
context:
space:
mode:
authorLibravatar Christian Vallentin <mail@vallentin.dev>2020-12-01 10:04:14 +0100
committerLibravatar GitHub <noreply@github.com>2020-12-01 10:04:14 +0100
commit266c606e39402a7c0cd357cdda8881e8f2ea417f (patch)
treee8e6f9930ea22f4c701eb678cb85c706f362ba93 /askama_shared/src/parser.rs
parentf4065b09b91f5d00efa5644915cdd83bfb7d393a (diff)
downloadaskama-266c606e39402a7c0cd357cdda8881e8f2ea417f.tar.gz
askama-266c606e39402a7c0cd357cdda8881e8f2ea417f.tar.bz2
askama-266c606e39402a7c0cd357cdda8881e8f2ea417f.zip
Fixed parsing precedence and associativity (#391)
Diffstat (limited to 'askama_shared/src/parser.rs')
-rw-r--r--askama_shared/src/parser.rs206
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]>>>;