aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--askama_derive/src/generator.rs10
-rw-r--r--askama_derive/src/parser.rs63
-rw-r--r--testing/templates/rust-macro-args.html26
-rw-r--r--testing/tests/rust_macro.rs32
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());
+}