fix rust macro arguments

This commit is contained in:
Juan Aguilar Santillana 2018-12-12 22:30:57 +01:00 committed by Dirkjan Ochtman
parent 65b4f6aae7
commit 39e08325c0
4 changed files with 121 additions and 10 deletions

View File

@ -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

View File

@ -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, "||");

View File

@ -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!
)
}}

View File

@ -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());
}