aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared/src/parser.rs
diff options
context:
space:
mode:
authorLibravatar René Kijewski <Kijewski@users.noreply.github.com>2022-01-27 10:33:13 +0100
committerLibravatar GitHub <noreply@github.com>2022-01-27 10:33:13 +0100
commitcb351fe6b1dda644a4ec023dc850cdfe83732503 (patch)
tree1c872f05b81ecada0010462380edfc0fde1dfde4 /askama_shared/src/parser.rs
parentbb7c60ece5dcfea9d31f79f445f80fdd4e0bf3ca (diff)
downloadaskama-cb351fe6b1dda644a4ec023dc850cdfe83732503.tar.gz
askama-cb351fe6b1dda644a4ec023dc850cdfe83732503.tar.bz2
askama-cb351fe6b1dda644a4ec023dc850cdfe83732503.zip
Unify handling of calls (#614)
Instead of having `Expr::VarCall`, `Expr::PathCall` and `Expr::MethodCall`, this PR unifies the handling of calls by removing the former three variants, and introducing `Expr::Call`.
Diffstat (limited to 'askama_shared/src/parser.rs')
-rw-r--r--askama_shared/src/parser.rs192
1 files changed, 126 insertions, 66 deletions
diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs
index f5685b9..f429628 100644
--- a/askama_shared/src/parser.rs
+++ b/askama_shared/src/parser.rs
@@ -52,9 +52,7 @@ pub enum Expr<'a> {
StrLit(&'a str),
CharLit(&'a str),
Var(&'a str),
- VarCall(&'a str, Vec<Expr<'a>>),
Path(Vec<&'a str>),
- PathCall(Vec<&'a str>, Vec<Expr<'a>>),
Array(Vec<Expr<'a>>),
Attr(Box<Expr<'a>>, &'a str),
Index(Box<Expr<'a>>, Box<Expr<'a>>),
@@ -63,7 +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>>),
- MethodCall(Box<Expr<'a>>, &'a str, Vec<Expr<'a>>),
+ Call(Box<Expr<'a>>, Vec<Expr<'a>>),
RustMacro(&'a str, &'a str),
}
@@ -86,7 +84,7 @@ impl Expr<'_> {
// The result of a call likely doesn't need to be borrowed,
// as in that case the call is more likely to return a
// reference in the first place then.
- VarCall(..) | Path(..) | PathCall(..) | MethodCall(..) => true,
+ Call(..) | Path(..) => true,
// If the `expr` is within a `Unary` or `BinOp` then
// an assumption can be made that the operand is copy.
// If not, then the value is moved and adding `.clone()`
@@ -296,11 +294,6 @@ fn expr_var(i: &str) -> IResult<&str, Expr<'_>> {
map(identifier, Expr::Var)(i)
}
-fn expr_var_call(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (s, args)) = tuple((ws(identifier), arguments))(i)?;
- Ok((i, Expr::VarCall(s, args)))
-}
-
fn path(i: &str) -> IResult<&str, Vec<&str>> {
let root = opt(value("", ws(tag("::"))));
let tail = separated_list1(ws(tag("::")), identifier);
@@ -335,11 +328,6 @@ fn expr_path(i: &str) -> IResult<&str, Expr<'_>> {
Ok((i, Expr::Path(path)))
}
-fn expr_path_call(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (path, args)) = tuple((ws(path), arguments))(i)?;
- Ok((i, Expr::PathCall(path, args)))
-}
-
fn named_target(i: &str) -> IResult<&str, (&str, Target<'_>)> {
let (i, (src, target)) = pair(identifier, opt(preceded(ws(char(':')), target)))(i)?;
Ok((i, (src, target.unwrap_or(Target::Name(src)))))
@@ -510,52 +498,39 @@ fn expr_single(i: &str) -> IResult<&str, Expr<'_>> {
expr_num_lit,
expr_str_lit,
expr_char_lit,
- expr_path_call,
expr_path,
expr_rust_macro,
expr_array_lit,
- expr_var_call,
expr_var,
expr_group,
))(i)
}
-fn attr(i: &str) -> IResult<&str, (&str, Option<Vec<Expr<'_>>>)> {
- let (i, (_, attr, args)) = tuple((
- ws(char('.')),
- alt((num_lit, identifier)),
- ws(opt(arguments)),
- ))(i)?;
- Ok((i, (attr, args)))
+enum Suffix<'a> {
+ Attr(&'a str),
+ Index(Expr<'a>),
+ Call(Vec<Expr<'a>>),
}
-fn expr_attr(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (obj, attrs)) = tuple((expr_single, many0(attr)))(i)?;
-
- let mut res = obj;
- for (aname, args) in attrs {
- res = if let Some(args) = args {
- Expr::MethodCall(Box::new(res), aname, args)
- } else {
- Expr::Attr(Box::new(res), aname)
- };
- }
-
- Ok((i, res))
+fn expr_attr(i: &str) -> IResult<&str, Suffix<'_>> {
+ map(
+ preceded(
+ ws(pair(char('.'), not(char('.')))),
+ cut(alt((num_lit, identifier))),
+ ),
+ Suffix::Attr,
+ )(i)
}
-fn expr_index(i: &str) -> IResult<&str, Expr<'_>> {
- let key = opt(tuple((ws(char('[')), expr_any, ws(char(']')))));
- let (i, (obj, key)) = tuple((expr_attr, key))(i)?;
- let key = key.map(|(_, key, _)| key);
+fn expr_index(i: &str) -> IResult<&str, Suffix<'_>> {
+ map(
+ preceded(ws(char('[')), cut(terminated(expr_any, ws(char(']'))))),
+ Suffix::Index,
+ )(i)
+}
- Ok((
- i,
- match key {
- Some(key) => Expr::Index(Box::new(obj), Box::new(key)),
- None => obj,
- },
- ))
+fn expr_call(i: &str) -> IResult<&str, Suffix<'_>> {
+ map(arguments, Suffix::Call)(i)
}
fn filter(i: &str) -> IResult<&str, (&str, Option<Vec<Expr<'_>>>)> {
@@ -564,7 +539,7 @@ fn filter(i: &str) -> IResult<&str, (&str, Option<Vec<Expr<'_>>>)> {
}
fn expr_filtered(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (obj, filters)) = tuple((expr_unary, many0(filter)))(i)?;
+ let (i, (obj, filters)) = tuple((expr_prefix, many0(filter)))(i)?;
let mut res = obj;
for (fname, args) in filters {
@@ -581,15 +556,27 @@ fn expr_filtered(i: &str) -> IResult<&str, Expr<'_>> {
Ok((i, res))
}
-fn expr_unary(i: &str) -> IResult<&str, Expr<'_>> {
- let (i, (op, expr)) = tuple((opt(alt((ws(tag("!")), ws(tag("-"))))), expr_index))(i)?;
- Ok((
- i,
- match op {
- Some(op) => Expr::Unary(op, Box::new(expr)),
- None => expr,
- },
- ))
+fn expr_prefix(i: &str) -> IResult<&str, Expr<'_>> {
+ let (i, (ops, mut expr)) = pair(many0(ws(alt((tag("!"), tag("-"))))), expr_suffix)(i)?;
+ for op in ops.iter().rev() {
+ expr = Expr::Unary(op, Box::new(expr));
+ }
+ Ok((i, expr))
+}
+
+fn expr_suffix(i: &str) -> IResult<&str, Expr<'_>> {
+ let (mut i, mut expr) = expr_single(i)?;
+ loop {
+ let (j, suffix) = opt(alt((expr_attr, expr_index, expr_call)))(i)?;
+ i = j;
+ match suffix {
+ Some(Suffix::Attr(attr)) => expr = Expr::Attr(expr.into(), attr),
+ Some(Suffix::Index(index)) => expr = Expr::Index(expr.into(), index.into()),
+ Some(Suffix::Call(args)) => expr = Expr::Call(expr.into(), args),
+ None => break,
+ }
+ }
+ Ok((i, expr))
}
fn expr_rust_macro(i: &str) -> IResult<&str, Expr<'_>> {
@@ -1348,7 +1335,10 @@ mod tests {
super::parse("{{ Some(123) }}", &s).unwrap(),
vec![Node::Expr(
Ws(false, false),
- Expr::PathCall(vec!["Some"], vec![Expr::NumLit("123")],),
+ Expr::Call(
+ Box::new(Expr::Path(vec!["Some"])),
+ vec![Expr::NumLit("123")]
+ ),
)],
);
@@ -1356,14 +1346,14 @@ mod tests {
super::parse("{{ Ok(123) }}", &s).unwrap(),
vec![Node::Expr(
Ws(false, false),
- Expr::PathCall(vec!["Ok"], vec![Expr::NumLit("123")],),
+ Expr::Call(Box::new(Expr::Path(vec!["Ok"])), vec![Expr::NumLit("123")]),
)],
);
assert_eq!(
super::parse("{{ Err(123) }}", &s).unwrap(),
vec![Node::Expr(
Ws(false, false),
- Expr::PathCall(vec!["Err"], vec![Expr::NumLit("123")],),
+ Expr::Call(Box::new(Expr::Path(vec!["Err"])), vec![Expr::NumLit("123")]),
)],
);
}
@@ -1374,7 +1364,10 @@ mod tests {
super::parse("{{ function(\"123\", 3) }}", &Syntax::default()).unwrap(),
vec![Node::Expr(
Ws(false, false),
- Expr::VarCall("function", vec![Expr::StrLit("123"), Expr::NumLit("3")]),
+ Expr::Call(
+ Box::new(Expr::Var("function")),
+ vec![Expr::StrLit("123"), Expr::NumLit("3")]
+ ),
)],
);
}
@@ -1394,7 +1387,10 @@ mod tests {
super::parse("{{ Option::Some(123) }}", &s).unwrap(),
vec![Node::Expr(
Ws(false, false),
- Expr::PathCall(vec!["Option", "Some"], vec![Expr::NumLit("123")],),
+ Expr::Call(
+ Box::new(Expr::Path(vec!["Option", "Some"])),
+ vec![Expr::NumLit("123")],
+ ),
)],
);
@@ -1402,8 +1398,8 @@ mod tests {
super::parse("{{ self::function(\"123\", 3) }}", &s).unwrap(),
vec![Node::Expr(
Ws(false, false),
- Expr::PathCall(
- vec!["self", "function"],
+ Expr::Call(
+ Box::new(Expr::Path(vec!["self", "function"])),
vec![Expr::StrLit("123"), Expr::NumLit("3")],
),
)],
@@ -1417,14 +1413,20 @@ mod tests {
super::parse("{{ std::string::String::new() }}", &syntax).unwrap(),
vec![Node::Expr(
Ws(false, false),
- Expr::PathCall(vec!["std", "string", "String", "new"], vec![]),
+ Expr::Call(
+ Box::new(Expr::Path(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![]),
+ Expr::Call(
+ Box::new(Expr::Path(vec!["", "std", "string", "String", "new"])),
+ vec![]
+ ),
)],
);
}
@@ -1587,6 +1589,64 @@ mod tests {
}
#[test]
+ fn test_odd_calls() {
+ use Expr::*;
+ let syntax = Syntax::default();
+ assert_eq!(
+ super::parse("{{ a[b](c) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Call(
+ Box::new(Index(Box::new(Var("a")), Box::new(Var("b")))),
+ vec![Var("c")],
+ ),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (a + b)(c) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Call(
+ Box::new(Group(Box::new(BinOp(
+ "+",
+ Box::new(Var("a")),
+ Box::new(Var("b"))
+ )))),
+ vec![Var("c")],
+ ),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ a + b(c) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ BinOp(
+ "+",
+ Box::new(Var("a")),
+ Box::new(Call(Box::new(Var("b")), vec![Var("c")])),
+ ),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ (-a)(b) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Call(
+ Box::new(Group(Box::new(Unary("-", Box::new(Var("a")))))),
+ vec![Var("b")],
+ ),
+ )],
+ );
+ assert_eq!(
+ super::parse("{{ -a(b) }}", &syntax).unwrap(),
+ vec![Node::Expr(
+ Ws(false, false),
+ Unary("-", Box::new(Call(Box::new(Var("a")), vec![Var("b")])),),
+ )],
+ );
+ }
+
+ #[test]
fn test_parse_comments() {
let s = &Syntax::default();