Skip to content

Commit 0c47b3f

Browse files
committed
feat: add caller pattern and adjust syntax to closer match jinja
Signed-off-by: Michael Pollind <[email protected]>
1 parent f0cc0cb commit 0c47b3f

40 files changed

+352
-251
lines changed

askama_derive/src/generator.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::path::Path;
99
use std::str;
1010
use std::sync::Arc;
1111

12-
use parser::node::{Macro, Whitespace};
12+
use parser::node::{Call, Macro, Whitespace};
1313
use parser::{
1414
CharLit, Expr, FloatKind, IntKind, MAX_RUST_KEYWORD_LEN, Num, RUST_KEYWORDS, StrLit, WithSpan,
1515
};
@@ -86,8 +86,8 @@ struct Generator<'a, 'h> {
8686
is_in_filter_block: usize,
8787
/// Set of called macros we are currently in. Used to prevent (indirect) recursions.
8888
seen_macros: Vec<(&'a Macro<'a>, Option<FileInfo<'a>>)>,
89-
/// Set of called macros we are currently associated with a call.
90-
seen_call_macro: Vec<&'a Macro<'a>>,
89+
/// Set of callers to forward into the macro.
90+
seen_callers: Vec<&'a Call<'a>>,
9191
}
9292

9393
impl<'a, 'h> Generator<'a, 'h> {
@@ -113,7 +113,7 @@ impl<'a, 'h> Generator<'a, 'h> {
113113
},
114114
is_in_filter_block,
115115
seen_macros: Vec::new(),
116-
seen_call_macro: Vec::new(),
116+
seen_callers: Vec::new(),
117117
}
118118
}
119119

askama_derive/src/generator/node.rs

Lines changed: 98 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::borrow::Cow;
22
use std::collections::hash_map::{Entry, HashMap};
3-
use std::fmt::{self, Write};
3+
use std::fmt::{self, Debug, Write};
44
use std::mem;
55

66
use parser::node::{
@@ -96,7 +96,7 @@ impl<'a> Generator<'a, '_> {
9696
self.write_comment(comment);
9797
}
9898
Node::Expr(ws, ref val) => {
99-
self.write_expr(ws, val);
99+
size_hint += self.write_expr(ctx, buf, ws, val)?;
100100
}
101101
Node::Let(ref l) => {
102102
self.write_let(ctx, buf, l)?;
@@ -600,18 +600,10 @@ impl<'a> Generator<'a, '_> {
600600
scope,
601601
name,
602602
ref args,
603-
ref block,
603+
..
604604
} = **call;
605-
if name == "super" {
606-
return self.write_block(ctx, buf, None, ws, call.span());
607-
}
608605

609-
let (def, own_ctx) = if name == "caller" {
610-
let def = self.seen_call_macro.last().ok_or_else(|| {
611-
ctx.generate_error(format_args!("block is not defined for caller"), call.span())
612-
})?;
613-
(*def, ctx)
614-
} else if let Some(s) = scope {
606+
let (def, own_ctx) = if let Some(s) = scope {
615607
let path = ctx.imports.get(s).ok_or_else(|| {
616608
ctx.generate_error(format_args!("no import found for scope {s:?}"), call.span())
617609
})?;
@@ -645,9 +637,7 @@ impl<'a> Generator<'a, '_> {
645637
} else {
646638
self.seen_macros.push((def, ctx.file_info_of(call.span())));
647639
}
648-
if let Some(macr) = block {
649-
self.seen_call_macro.push(macr);
650-
}
640+
self.seen_callers.push(call);
651641
self.flush_ws(ws); // Cannot handle_ws() here: whitespace from macro definition comes first
652642
let size_hint = self.push_locals(|this| {
653643
macro_call_ensure_arg_count(call, def, ctx)?;
@@ -771,9 +761,7 @@ impl<'a> Generator<'a, '_> {
771761
})?;
772762
self.prepare_ws(ws);
773763
self.seen_macros.pop();
774-
if let Some(_macr) = block {
775-
self.seen_call_macro.pop();
776-
}
764+
self.seen_callers.pop();
777765
Ok(size_hint)
778766
}
779767

@@ -1103,17 +1091,108 @@ impl<'a> Generator<'a, '_> {
11031091
Ok(size_hint)
11041092
}
11051093

1106-
fn write_expr(&mut self, ws: Ws, s: &'a WithSpan<'a, Expr<'a>>) {
1094+
fn write_expr(
1095+
&mut self,
1096+
ctx: &Context<'a>,
1097+
buf: &mut Buffer,
1098+
ws: Ws,
1099+
s: &'a WithSpan<'a, Expr<'a>>,
1100+
) -> Result<usize, CompileError> {
1101+
if let Expr::Call {
1102+
ref path,
1103+
args: expr_args,
1104+
..
1105+
} = &**s
1106+
{
1107+
if ***path == Expr::Var("super") {
1108+
return self.write_block(ctx, buf, None, ws, s.span());
1109+
} else if ***path == Expr::Var("caller") {
1110+
let def = self.seen_callers.last().ok_or_else(|| {
1111+
ctx.generate_error(format_args!("block is not defined for caller"), s.span())
1112+
})?;
1113+
//self.flush_ws(ws); // Cannot handle_ws() here: whitespace from macro definition comes first
1114+
let size_hint = self.push_locals(|this| {
1115+
this.write_buf_writable(ctx, buf)?;
1116+
buf.write('{');
1117+
this.prepare_ws(def.ws1);
1118+
let mut value = Buffer::new();
1119+
for (index, arg) in def.caller_args.iter().enumerate() {
1120+
match expr_args.get(index) {
1121+
Some(expr) => {
1122+
match &**expr {
1123+
// If `expr` is already a form of variable then
1124+
// don't reintroduce a new variable. This is
1125+
// to avoid moving non-copyable values.
1126+
Expr::Var(name) if *name != "self" => {
1127+
let var = this.locals.resolve_or_self(name);
1128+
this.locals
1129+
.insert(Cow::Borrowed(arg), LocalMeta::with_ref(var));
1130+
}
1131+
Expr::Attr(obj, attr) => {
1132+
let mut attr_buf = Buffer::new();
1133+
this.visit_attr(ctx, &mut attr_buf, obj, attr)?;
1134+
1135+
let attr = attr_buf.into_string();
1136+
let var = this.locals.resolve(&attr).unwrap_or(attr);
1137+
this.locals
1138+
.insert(Cow::Borrowed(arg), LocalMeta::with_ref(var));
1139+
}
1140+
// Everything else still needs to become variables,
1141+
// to avoid having the same logic be executed
1142+
// multiple times, e.g. in the case of macro
1143+
// parameters being used multiple times.
1144+
_ => {
1145+
value.clear();
1146+
let (before, after) = if !is_copyable(expr) {
1147+
("&(", ")")
1148+
} else {
1149+
("", "")
1150+
};
1151+
value.write(this.visit_expr_root(ctx, expr)?);
1152+
// We need to normalize the arg to write it, thus we need to add it to
1153+
// locals in the normalized manner
1154+
let normalized_arg = normalize_identifier(arg);
1155+
buf.write(format_args!(
1156+
"let {} = {before}{value}{after};",
1157+
normalized_arg
1158+
));
1159+
this.locals
1160+
.insert_with_default(Cow::Borrowed(normalized_arg));
1161+
}
1162+
}
1163+
}
1164+
None => {
1165+
return Err(ctx.generate_error(
1166+
format_args!("missing `{arg}` argument"),
1167+
s.span(),
1168+
));
1169+
}
1170+
}
1171+
}
1172+
1173+
let mut size_hint = this.handle(ctx, &def.nodes, buf, AstLevel::Nested)?;
1174+
this.flush_ws(def.ws2);
1175+
size_hint += this.write_buf_writable(ctx, buf)?;
1176+
buf.write('}');
1177+
Ok(size_hint)
1178+
})?;
1179+
self.prepare_ws(ws);
1180+
return Ok(size_hint);
1181+
}
1182+
}
1183+
11071184
self.handle_ws(ws);
11081185
let items = if let Expr::Concat(exprs) = &**s {
11091186
exprs
11101187
} else {
11111188
std::slice::from_ref(s)
11121189
};
1190+
11131191
for s in items {
11141192
self.buf_writable
11151193
.push(compile_time_escape(s, self.input.escaper).unwrap_or(Writable::Expr(s)));
11161194
}
1195+
Ok(0)
11171196
}
11181197

11191198
// Write expression buffer and empty

askama_parser/src/node.rs

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -878,10 +878,13 @@ impl<'a> Import<'a> {
878878
#[derive(Debug, PartialEq)]
879879
pub struct Call<'a> {
880880
pub ws: Ws,
881+
pub caller_args: Vec<&'a str>,
881882
pub scope: Option<&'a str>,
882883
pub name: &'a str,
883884
pub args: Vec<WithSpan<'a, Expr<'a>>>,
884-
pub block: Option<Macro<'a>>,
885+
pub ws1: Ws,
886+
pub nodes: Vec<Node<'a>>,
887+
pub ws2: Ws,
885888
}
886889

887890
impl<'a> Call<'a> {
@@ -918,51 +921,40 @@ impl<'a> Call<'a> {
918921
ws(identifier),
919922
opt(ws(|nested: &mut _| Expr::arguments(nested, s.level, true))),
920923
opt(Whitespace::parse),
924+
|i: &mut _| s.tag_block_end(i),
921925
),
922926
),
923927
);
924-
let (pws, _, call_args, (scope, name, args, nws)) = p.parse_next(i)?;
928+
let (pws, _, call_args, (scope, name, args, nws, _)) = p.parse_next(i)?;
925929
let scope = scope.map(|(scope, _)| scope);
926930
let args = args.unwrap_or_default();
931+
let mut end = cut_node(
932+
Some("call"),
933+
(
934+
|i: &mut _| Node::many(i, s),
935+
cut_node(
936+
Some("call"),
937+
(
938+
|i: &mut _| check_block_start(i, start_s, s, "call", "endcall"),
939+
opt(Whitespace::parse),
940+
end_node("call", "endcall"),
941+
opt(Whitespace::parse),
942+
),
943+
),
944+
),
945+
);
946+
let (nodes, (_, pws2, _, nws2)) = end.parse_next(i)?;
927947

928948
Ok(WithSpan::new(
929949
Self {
930950
ws: Ws(pws, nws),
951+
caller_args: call_args.unwrap_or_default(),
931952
scope,
932953
name,
933954
args,
934-
block: match call_args {
935-
Some(args) => {
936-
let mut end = cut_node(
937-
Some("call"),
938-
(
939-
|i: &mut _| s.tag_block_end(i),
940-
|i: &mut _| Node::many(i, s),
941-
cut_node(
942-
Some("call"),
943-
(
944-
|i: &mut _| {
945-
check_block_start(i, start_s, s, "call", "endcall")
946-
},
947-
opt(Whitespace::parse),
948-
end_node("call", "endcall"),
949-
opt(Whitespace::parse),
950-
),
951-
),
952-
),
953-
);
954-
955-
let (_, nodes, (_, pws2, _, nws2)) = end.parse_next(i)?;
956-
Some(Macro {
957-
ws1: Ws(pws, pws2),
958-
name: "", // this macro is not resolved wtih a name
959-
args: args.iter().map(|v| (*v, None)).collect(),
960-
nodes,
961-
ws2: Ws(pws2, nws2),
962-
})
963-
}
964-
None => None,
965-
},
955+
ws1: Ws(pws, pws2),
956+
nodes,
957+
ws2: Ws(pws2, nws2),
966958
},
967959
start_s,
968960
))

0 commit comments

Comments
 (0)