diff options
-rw-r--r-- | askama_derive/src/generator.rs | 10 | ||||
-rw-r--r-- | askama_derive/src/parser.rs | 63 | ||||
-rw-r--r-- | testing/templates/rust-macro-args.html | 26 | ||||
-rw-r--r-- | testing/tests/rust_macro.rs | 32 |
4 files changed, 121 insertions, 10 deletions
diff --git a/askama_derive/src/generator.rs b/askama_derive/src/generator.rs index 047e975..95a7d17 100644 --- a/askama_derive/src/generator.rs +++ b/askama_derive/src/generator.rs @@ -1,6 +1,8 @@ use super::{get_template_source, Context, Heritage}; use crate::input::TemplateInput; -use crate::parser::{Cond, Expr, MatchParameter, MatchParameters, MatchVariant, Node, Target, When, WS}; +use crate::parser::{ + Cond, Expr, MatchParameter, MatchParameters, MatchVariant, Node, Target, When, WS, +}; use askama_shared::filters; use proc_macro2::Span; @@ -773,14 +775,14 @@ impl<'a> Generator<'a> { Expr::MethodCall(ref obj, method, ref args) => { self.visit_method_call(buf, obj, method, args) } - Expr::RustMacro(name, ref args) => self.visit_rust_macro(buf, name, args), + Expr::RustMacro(name, args) => self.visit_rust_macro(buf, name, args), } } - fn visit_rust_macro(&mut self, buf: &mut Buffer, name: &str, args: &[Expr]) -> DisplayWrap { + fn visit_rust_macro(&mut self, buf: &mut Buffer, name: &str, args: &str) -> DisplayWrap { buf.write(name); buf.write("!("); - self._visit_args(buf, args); + buf.write(args); buf.write(")"); DisplayWrap::Unwrapped diff --git a/askama_derive/src/parser.rs b/askama_derive/src/parser.rs index 7968ac1..81935f6 100644 --- a/askama_derive/src/parser.rs +++ b/askama_derive/src/parser.rs @@ -21,7 +21,7 @@ pub enum 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>>), - RustMacro(&'a str, Vec<Expr<'a>>), + RustMacro(&'a str, &'a str), } #[derive(Debug)] @@ -302,6 +302,60 @@ named!(arguments<Input, Vec<Expr>>, do_parse!( (args.unwrap_or_default()) )); +named!(macro_arguments<Input, &str>, + delimited!(char!('('), nested_parenthesis, char!(')')) +); + +fn nested_parenthesis(i: Input) -> Result<(Input, &str), nom::Err<Input>> { + let mut nested = 0; + let mut last = 0; + let mut in_str = false; + let mut escaped = false; + + for (i, b) in i.iter().enumerate() { + if !(*b == b'(' || *b == b')') || !in_str { + match *b { + b'(' => { + nested += 1 + }, + b')' => { + if nested == 0 { + last = i; + break; + } + nested -= 1; + }, + b'"' => { + if in_str { + if !escaped { + in_str = false; + } + } else { + in_str = true; + } + }, + b'\\' => { + escaped = !escaped; + }, + _ => (), + } + } + + if escaped && *b != b'\\' { + escaped = false; + } + } + + if nested == 0 { + Ok((Input(&i[last..]), str::from_utf8(&i[..last]).unwrap())) + } else { + Err(nom::Err::Error(error_position!( + i, + nom::ErrorKind::Custom(0) + ))) + } +} + named!(parameters<Input, Vec<&str>>, do_parse!( tag!("(") >> vals: opt!(do_parse!( @@ -477,11 +531,10 @@ named!(expr_unary<Input, Expr>, do_parse!( named!(rust_macro<Input, Expr>, do_parse!( mname: identifier >> tag!("!") >> - args: arguments >> + args: macro_arguments >> (Expr::RustMacro(mname, args)) )); - macro_rules! expr_prec_layer { ( $name:ident, $inner:ident, $( $op:expr ),* ) => { named!($name<Input, Expr>, do_parse!( @@ -503,9 +556,7 @@ expr_prec_layer!(expr_shifts, expr_addsub, ">>", "<<"); expr_prec_layer!(expr_band, expr_shifts, "&"); expr_prec_layer!(expr_bxor, expr_band, "^"); expr_prec_layer!(expr_bor, expr_bxor, "|"); -expr_prec_layer!(expr_compare, expr_bor, - "==", "!=", ">=", ">", "<=", "<" -); +expr_prec_layer!(expr_compare, expr_bor, "==", "!=", ">=", ">", "<=", "<"); expr_prec_layer!(expr_and, expr_compare, "&&"); expr_prec_layer!(expr_or, expr_and, "||"); diff --git a/testing/templates/rust-macro-args.html b/testing/templates/rust-macro-args.html new file mode 100644 index 0000000..b009a73 --- /dev/null +++ b/testing/templates/rust-macro-args.html @@ -0,0 +1,26 @@ +{{ + call_a_or_b_on_tail!( + (a: compute_len, b: zero), + the recursive part that skips over all these + tokens doesn't much care whether we will call a + or call b: only the terminal rules care. + ) +}} +{{ + call_a_or_b_on_tail!( + (a: compute_len, b: zero), + and now, to justify the existence of two paths + we will also call a: its input should somehow + be self-referential, so let's make it return + some ninety one! + ) +}} +{{ + call_a_or_b_on_tail!( + (a: compute_len, b: zero), + and now, to justify the existence of two paths + we will also call a: its input should somehow + be self-referential, so let's make it return + some ninety "(\"()"nine! + ) +}} diff --git a/testing/tests/rust_macro.rs b/testing/tests/rust_macro.rs index 8a114f9..b92bbeb 100644 --- a/testing/tests/rust_macro.rs +++ b/testing/tests/rust_macro.rs @@ -15,3 +15,35 @@ fn main() { let template = RustMacrosTemplate {}; assert_eq!("Hello, world!", template.render().unwrap()); } + +macro_rules! call_a_or_b_on_tail { + ((a: $a:expr, b: $b:expr), call a: $($tail:tt)*) => { + $a(stringify!($($tail)*)) + }; + + ((a: $a:expr, b: $b:expr), call b: $($tail:tt)*) => { + $b(stringify!($($tail)*)) + }; + + ($ab:tt, $_skip:tt $($tail:tt)*) => { + call_a_or_b_on_tail!($ab, $($tail)*) + }; +} + +fn compute_len(s: &str) -> usize { + s.len() +} + +fn zero(_s: &str) -> usize { + 0 +} + +#[derive(Template)] +#[template(path = "rust-macro-args.html")] +struct RustMacrosArgTemplate {} + +#[test] +fn args() { + let template = RustMacrosArgTemplate {}; + assert_eq!("0\n91\n99", template.render().unwrap()); +} |