aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared
diff options
context:
space:
mode:
authorLibravatar René Kijewski <kijewski@library.vetmed.fu-berlin.de>2022-01-14 15:32:53 +0100
committerLibravatar Dirkjan Ochtman <dirkjan@ochtman.nl>2022-01-28 11:21:06 +0100
commitda0b6ead0e75082bfffa24f8529d1f83961ba45c (patch)
tree58bbdb1de244e99c77b378a9a9d4eaf4dd873474 /askama_shared
parent85ad2e6ba3a205e9b648431318aac0e75c027a82 (diff)
downloadaskama-da0b6ead0e75082bfffa24f8529d1f83961ba45c.tar.gz
askama-da0b6ead0e75082bfffa24f8529d1f83961ba45c.tar.bz2
askama-da0b6ead0e75082bfffa24f8529d1f83961ba45c.zip
Parse tuple expressions
Askama understands how to destructure tuples in let and match statements, but it does not understand how to build a tuple. This PR fixes this shortcoming.
Diffstat (limited to 'askama_shared')
-rw-r--r--askama_shared/src/generator.rs18
-rw-r--r--askama_shared/src/parser.rs148
2 files changed, 163 insertions, 3 deletions
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index de2fdcb..7080b87 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -1061,6 +1061,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
Expr::Call(ref obj, ref args) => self.visit_call(buf, obj, args)?,
Expr::RustMacro(name, args) => self.visit_rust_macro(buf, name, args),
Expr::Try(ref expr) => self.visit_try(buf, expr.as_ref())?,
+ Expr::Tuple(ref exprs) => self.visit_tuple(buf, exprs)?,
})
}
@@ -1403,6 +1404,23 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
Ok(DisplayWrap::Unwrapped)
}
+ fn visit_tuple(
+ &mut self,
+ buf: &mut Buffer,
+ exprs: &[Expr<'_>],
+ ) -> Result<DisplayWrap, CompileError> {
+ buf.write("(");
+ for (index, expr) in exprs.iter().enumerate() {
+ if index > 0 {
+ buf.write(" ");
+ }
+ self.visit_expr(buf, expr)?;
+ buf.write(",");
+ }
+ buf.write(")");
+ Ok(DisplayWrap::Unwrapped)
+ }
+
fn visit_array(
&mut self,
buf: &mut Buffer,
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs
index 63cea0c..085d73b 100644
--- a/askama_shared/src/parser.rs
+++ b/askama_shared/src/parser.rs
@@ -61,6 +61,7 @@ pub enum Expr<'a> {
BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
Range(&'a str, Option<Box<Expr<'a>>>, Option<Box<Expr<'a>>>),
Group(Box<Expr<'a>>),
+ Tuple(Vec<Expr<'a>>),
Call(Box<Expr<'a>>, Vec<Expr<'a>>),
RustMacro(&'a str, &'a str),
Try(Box<Expr<'a>>),
@@ -488,9 +489,31 @@ fn parameters(i: &str) -> IResult<&str, Vec<&str>> {
}
fn expr_group(i: &str) -> IResult<&str, Expr<'_>> {
- map(delimited(ws(char('(')), expr_any, ws(char(')'))), |s| {
- Expr::Group(Box::new(s))
- })(i)
+ let (i, expr) = preceded(ws(char('(')), opt(expr_any))(i)?;
+ let expr = match expr {
+ Some(expr) => expr,
+ None => {
+ let (i, _) = char(')')(i)?;
+ return Ok((i, Expr::Tuple(vec![])));
+ }
+ };
+
+ let (i, comma) = ws(opt(peek(char(','))))(i)?;
+ if comma.is_none() {
+ let (i, _) = char(')')(i)?;
+ return Ok((i, Expr::Group(Box::new(expr))));
+ }
+
+ let mut exprs = vec![expr];
+ let (i, _) = fold_many0(
+ preceded(char(','), ws(expr_any)),
+ || (),
+ |_, expr| {
+ exprs.push(expr);
+ },
+ )(i)?;
+ let (i, _) = pair(ws(opt(char(','))), char(')'))(i)?;
+ Ok((i, Expr::Tuple(exprs)))
}
fn expr_single(i: &str) -> IResult<&str, Expr<'_>> {
@@ -1687,4 +1710,123 @@ mod tests {
vec![Node::Comment(Ws(false, false))],
);
}
+
+ #[test]
+ fn test_parse_tuple() {
+ use super::Expr::*;
+ let syntax = Syntax::default();
+ assert_eq!(
+ super::parse("{{ () }}", &syntax).unwrap(),
+ vec![Node::Expr(Ws(false, false), Tuple(vec![]),)],
+ );
+ assert_eq!(
+ super::parse("{{ (1) }}", &syntax).unwrap(),
+ vec![Node::Expr(Ws(false, false), Group(Box::new(NumLit("1"))),)],
+ );
+ assert_eq!(
+ super::parse("{{ (1,) }}", &syntax).unwrap(),
+ vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
+ );
+ assert_eq!(
+ super::parse("{{ (1, ) }}", &syntax).unwrap(),
+ vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
+ );
+ assert_eq!(
+ super::parse("{{ (1 ,) }}", &syntax).unwrap(),
+ vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
+ );
+ assert_eq!(
+ super::parse("{{ (1 , ) }}", &syntax).unwrap(),
+ vec![Node::Expr(Ws(false, false), Tuple(vec![NumLit("1")]),)],
+ );
+ assert_eq!(
+ super::parse("{{ (1, 2) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Tuple(vec![NumLit("1"), NumLit("2")]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1, 2,) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Tuple(vec![NumLit("1"), NumLit("2")]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1, 2, 3) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Tuple(vec![NumLit("1"), NumLit("2"), NumLit("3")]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ ()|abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Filter("abs", vec![Tuple(vec![])]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ () | abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ BinOp("|", Box::new(Tuple(vec![])), Box::new(Var("abs"))),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1)|abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Filter("abs", vec![Group(Box::new(NumLit("1")))]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1) | abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ BinOp(
+ "|",
+ Box::new(Group(Box::new(NumLit("1")))),
+ Box::new(Var("abs"))
+ ),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1,)|abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Filter("abs", vec![Tuple(vec![NumLit("1")])]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1,) | abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ BinOp(
+ "|",
+ Box::new(Tuple(vec![NumLit("1")])),
+ Box::new(Var("abs"))
+ ),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1, 2)|abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Filter("abs", vec![Tuple(vec![NumLit("1"), NumLit("2")])]),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (1, 2) | abs }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ BinOp(
+ "|",
+ Box::new(Tuple(vec![NumLit("1"), NumLit("2")])),
+ Box::new(Var("abs"))
+ ),
+ )],
+ );
+ }
}