Repository: djc/askama
Pull request #843 (2023-07-21 13:19:05)

Fix parsing calls
This change:

* adds a cut when the leading `(` was encountered, and
* fixed the interaction between call expressions and boolean OR.

djc: This will need to be rebased now, hope it's not too painful!
Kijewski: No problem at all. :)

Patch:
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 5438379..b4ba2bb 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -32,9 +32,9 @@ jobs:
     strategy:
       matrix:
         package: [
-          askama, askama_derive, testing,
-          askama_actix, askama_axum, askama_escape, askama_gotham, askama_hyper,
-          askama_mendes, askama_rocket, askama_tide, askama_warp,
+          askama, askama_actix, askama_axum, askama_derive, askama_escape, askama_gotham,
+          askama_hyper, askama_mendes, askama_parser, askama_rocket, askama_tide, askama_warp,
+          testing,
         ]
     runs-on: ubuntu-latest
     steps:
diff --git a/askama_parser/src/expr.rs b/askama_parser/src/expr.rs
index ef5c5e2..9610c05 100644
--- a/askama_parser/src/expr.rs
+++ b/askama_parser/src/expr.rs
@@ -5,8 +5,8 @@ use nom::bytes::complete::{tag, take_till};
 use nom::character::complete::char;
 use nom::combinator::{cut, map, not, opt, peek, recognize};
 use nom::error::ErrorKind;
-use nom::multi::{fold_many0, many0, separated_list0, separated_list1};
-use nom::sequence::{delimited, pair, preceded, terminated, tuple};
+use nom::multi::{fold_many0, many0, separated_list0};
+use nom::sequence::{pair, preceded, terminated, tuple};
 use nom::{error_position, IResult};
 
 use super::{bool_lit, char_lit, identifier, not_ws, num_lit, path, str_lit, ws};
@@ -68,10 +68,12 @@ pub enum Expr<'a> {
 
 impl<'a> Expr<'a> {
     pub(super) fn arguments(i: &'a str) -> IResult<&'a str, Vec<Self>> {
-        delimited(
+        preceded(
             ws(char('(')),
-            separated_list0(char(','), ws(Self::parse)),
-            ws(char(')')),
+            cut(terminated(
+                separated_list0(char(','), ws(Self::parse)),
+                char(')'),
+            )),
         )(i)
     }
 
@@ -175,10 +177,12 @@ impl<'a> Expr<'a> {
     }
 
     fn array(i: &'a str) -> IResult<&'a str, Self> {
-        delimited(
+        preceded(
             ws(char('[')),
-            map(separated_list1(ws(char(',')), Self::parse), Self::Array),
-            ws(char(']')),
+            cut(terminated(
+                map(separated_list0(char(','), ws(Self::parse)), Self::Array),
+                char(']'),
+            )),
         )(i)
     }
 


Test patch:
diff --git a/askama_parser/src/tests.rs b/askama_parser/src/tests.rs
index 5b3b66e..61aa279 100644
--- a/askama_parser/src/tests.rs
+++ b/askama_parser/src/tests.rs
@@ -493,9 +493,34 @@ fn test_odd_calls() {
         Ast::from_str("{{ -a(b) }}", &syntax).unwrap().nodes,
         vec![Node::Expr(
             Ws(None, None),
-            Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")])),),
+            Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")]))),
         )],
     );
+    assert_eq!(
+        Ast::from_str("{{ a(b)|c }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Filter("c", vec![Call(Box::new(Var("a")), vec![Var("b")])]),
+        )]
+    );
+    assert_eq!(
+        Ast::from_str("{{ a(b)| c }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Filter("c", vec![Call(Box::new(Var("a")), vec![Var("b")])]),
+        )]
+    );
+    assert_eq!(
+        Ast::from_str("{{ a(b) |c }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            BinOp(
+                "|",
+                Box::new(Call(Box::new(Var("a")), vec![Var("b")])),
+                Box::new(Var("c"))
+            ),
+        )]
+    );
 }
 
 #[test]
@@ -736,3 +761,86 @@ fn test_missing_space_after_kw() {
         "unable to parse template:\n\n\"{%leta=b%}\""
     ));
 }
+
+#[test]
+fn test_parse_array() {
+    let syntax = Syntax::default();
+    assert_eq!(
+        Ast::from_str("{{ [] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(Ws(None, None), Expr::Array(vec![]))],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [1] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Array(vec![Expr::NumLit("1")])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [ 1] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Array(vec![Expr::NumLit("1")])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [1 ] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Array(vec![Expr::NumLit("1")])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [1,2] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Array(vec![Expr::NumLit("1"), Expr::NumLit("2")])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [1 ,2] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Array(vec![Expr::NumLit("1"), Expr::NumLit("2")])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [1, 2] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Array(vec![Expr::NumLit("1"), Expr::NumLit("2")])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [1,2 ] }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Array(vec![Expr::NumLit("1"), Expr::NumLit("2")])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ []|foo }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Filter("foo", vec![Expr::Array(vec![])])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ []| foo }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::Filter("foo", vec![Expr::Array(vec![])])
+        )],
+    );
+    assert_eq!(
+        Ast::from_str("{{ [] |foo }}", &syntax).unwrap().nodes,
+        vec![Node::Expr(
+            Ws(None, None),
+            Expr::BinOp(
+                "|",
+                Box::new(Expr::Array(vec![])),
+                Box::new(Expr::Var("foo"))
+            ),
+        )],
+    );
+}
diff --git a/testing/tests/ui/loop_cycle_empty.rs b/testing/tests/ui/loop_cycle_empty.rs
index 4aa6010..30d54fa 100644
--- a/testing/tests/ui/loop_cycle_empty.rs
+++ b/testing/tests/ui/loop_cycle_empty.rs
@@ -1,6 +1,3 @@
-// Nb. this test fails because currently an empty array "[]" is always a syntax error in askama,
-// but even if this changes, this test should keep failing, but possibly with another error message
-
 use askama::Template;
 
 #[derive(Template)]
diff --git a/testing/tests/ui/loop_cycle_empty.stderr b/testing/tests/ui/loop_cycle_empty.stderr
index 13beb9a..dd4f0dd 100644
--- a/testing/tests/ui/loop_cycle_empty.stderr
+++ b/testing/tests/ui/loop_cycle_empty.stderr
@@ -1,8 +1,7 @@
-error: problems parsing template source at row 1, column 34 near:
-       "([]) }}{{ v }},{% endfor %}"
- --> tests/ui/loop_cycle_empty.rs:6:10
+error: loop.cycle(…) cannot use an empty array
+ --> tests/ui/loop_cycle_empty.rs:3:10
   |
-6 | #[derive(Template)]
+3 | #[derive(Template)]
   |          ^^^^^^^^
   |
   = note: this error originates in the derive macro `Template` (in Nightly builds, run with -Z macro-backtrace for more info)