From 520c5d7d5f09c0b205e6e1c471483730380c5e33 Mon Sep 17 00:00:00 2001 From: Nathan Lapel Date: Sun, 15 Mar 2020 11:46:31 +0100 Subject: Support function calls --- askama_shared/src/generator.rs | 39 +++++++++++++++++++++++++ askama_shared/src/parser.rs | 66 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 9 deletions(-) (limited to 'askama_shared') diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 59df687..fb8619e 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -909,7 +909,9 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { Expr::StrLit(s) => self.visit_str_lit(buf, s), Expr::CharLit(s) => self.visit_char_lit(buf, s), Expr::Var(s) => self.visit_var(buf, s), + Expr::VarCall(var, ref args) => self.visit_var_call(buf, var, args), Expr::Path(ref path) => self.visit_path(buf, path), + Expr::PathCall(ref path, ref args) => self.visit_path_call(buf, path, args), Expr::Array(ref elements) => self.visit_array(buf, elements), Expr::Attr(ref obj, name) => self.visit_attr(buf, obj, name), Expr::Index(ref obj, ref key) => self.visit_index(buf, obj, key), @@ -1160,6 +1162,24 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { DisplayWrap::Unwrapped } + fn visit_path_call(&mut self, buf: &mut Buffer, path: &[&str], args: &[Expr]) -> DisplayWrap { + for (i, part) in path.iter().enumerate() { + if i > 0 { + buf.write("::"); + } + buf.write(part); + } + buf.write("("); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + buf.write(","); + } + self.visit_expr(buf, arg); + } + buf.write(")"); + DisplayWrap::Unwrapped + } + fn visit_var(&mut self, buf: &mut Buffer, s: &str) -> DisplayWrap { if self.locals.contains(s) || s == "self" { buf.write(s); @@ -1170,6 +1190,25 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { DisplayWrap::Unwrapped } + fn visit_var_call(&mut self, buf: &mut Buffer, s: &str, args: &[Expr]) -> DisplayWrap { + buf.write("("); + if self.locals.contains(s) || s == "self" { + buf.write(s); + } else { + buf.write("self."); + buf.write(s); + } + buf.write(")("); + for (i, arg) in args.iter().enumerate() { + if i > 0 { + buf.write(","); + } + self.visit_expr(buf, arg); + } + buf.write(")"); + DisplayWrap::Unwrapped + } + fn visit_bool_lit(&mut self, buf: &mut Buffer, s: &str) -> DisplayWrap { buf.write(s); DisplayWrap::Unwrapped diff --git a/askama_shared/src/parser.rs b/askama_shared/src/parser.rs index 9c3358e..b6404c5 100644 --- a/askama_shared/src/parser.rs +++ b/askama_shared/src/parser.rs @@ -10,14 +10,16 @@ use std::str; use crate::Syntax; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Expr<'a> { BoolLit(&'a str), NumLit(&'a str), StrLit(&'a str), CharLit(&'a str), Var(&'a str), + VarCall(&'a str, Vec>), Path(Vec<&'a str>), + PathCall(Vec<&'a str>, Vec>), Array(Vec>), Attr(Box>, &'a str), Index(Box>, Box>), @@ -30,7 +32,7 @@ pub enum Expr<'a> { RustMacro(&'a str, &'a str), } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MatchVariant<'a> { Path(Vec<&'a str>), Name(&'a str), @@ -39,7 +41,7 @@ pub enum MatchVariant<'a> { CharLit(&'a str), } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MatchParameter<'a> { Name(&'a str), NumLit(&'a str), @@ -47,16 +49,16 @@ pub enum MatchParameter<'a> { CharLit(&'a str), } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Target<'a> { Name(&'a str), Tuple(Vec<&'a str>), } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct WS(pub bool, pub bool); -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct Macro<'a> { pub ws1: WS, pub args: Vec<&'a str>, @@ -64,7 +66,7 @@ pub struct Macro<'a> { pub ws2: WS, } -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum Node<'a> { Lit(&'a str, &'a str, &'a str), Comment(WS), @@ -84,6 +86,7 @@ pub enum Node<'a> { } pub type Cond<'a> = (WS, Option>, Vec>); + pub type When<'a> = ( WS, Option>, @@ -91,7 +94,7 @@ pub type When<'a> = ( Vec>, ); -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MatchParameters<'a> { Simple(Vec>), Named(Vec<(&'a str, Option>)>), @@ -301,15 +304,30 @@ fn expr_var(i: &[u8]) -> IResult<&[u8], Expr> { map(identifier, |s| Expr::Var(s))(i) } -fn expr_path(i: &[u8]) -> IResult<&[u8], Expr> { +fn expr_var_call(i: &[u8]) -> IResult<&[u8], Expr> { + let (i, (s, args)) = tuple((identifier, arguments))(i)?; + Ok((i, Expr::VarCall(s, args))) +} + +fn path(i: &[u8]) -> IResult<&[u8], Vec<&str>> { let tail = separated_nonempty_list(tag("::"), identifier); let (i, (start, _, rest)) = tuple((identifier, tag("::"), tail))(i)?; let mut path = vec![start]; path.extend(rest); + Ok((i, path)) +} + +fn expr_path(i: &[u8]) -> IResult<&[u8], Expr> { + let (i, path) = path(i)?; Ok((i, Expr::Path(path))) } +fn expr_path_call(i: &[u8]) -> IResult<&[u8], Expr> { + let (i, (path, args)) = tuple((path, arguments))(i)?; + Ok((i, Expr::PathCall(path, args))) +} + fn variant_path(i: &[u8]) -> IResult<&[u8], MatchVariant> { map(separated_nonempty_list(tag("::"), identifier), |path| { MatchVariant::Path(path) @@ -437,9 +455,11 @@ fn expr_single(i: &[u8]) -> IResult<&[u8], 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) @@ -1078,6 +1098,34 @@ mod tests { super::parse("{{ strvar|e }}", &Syntax::default()); } + #[test] + fn test_parse_var_call() { + assert_eq!( + super::parse("{{ function(\"123\", 3) }}", &Syntax::default()), + vec![super::Node::Expr( + super::WS(false, false), + super::Expr::VarCall( + "function", + vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")] + ), + )], + ); + } + + #[test] + fn test_parse_path_call() { + assert_eq!( + super::parse("{{ self::function(\"123\", 3) }}", &Syntax::default()), + vec![super::Node::Expr( + super::WS(false, false), + super::Expr::PathCall( + vec!["self", "function"], + vec![super::Expr::StrLit("123"), super::Expr::NumLit("3")], + ), + )], + ); + } + #[test] fn change_delimiters_parse_filter() { let syntax = Syntax { -- cgit