askama_parser/
expr.rs

1use std::borrow::Cow;
2use std::collections::HashSet;
3use std::str;
4
5use nom::branch::alt;
6use nom::bytes::complete::{tag, take_till};
7use nom::character::complete::char;
8use nom::combinator::{cut, map, not, opt, peek, recognize};
9use nom::error::ErrorKind;
10use nom::error_position;
11use nom::multi::{fold_many0, many0, separated_list0};
12use nom::sequence::{pair, preceded, terminated, tuple};
13
14use super::{
15    char_lit, identifier, not_ws, num_lit, path_or_identifier, str_lit, ws, Level, PathOrIdentifier,
16};
17use crate::{ErrorContext, ParseResult};
18
19macro_rules! expr_prec_layer {
20    ( $name:ident, $inner:ident, $op:expr ) => {
21        fn $name(i: &'a str, level: Level) -> ParseResult<'a, Self> {
22            let (_, level) = level.nest(i)?;
23            let (i, left) = Self::$inner(i, level)?;
24            let (i, right) = many0(pair(
25                ws(tag($op)),
26                |i| Self::$inner(i, level),
27            ))(i)?;
28            Ok((
29                i,
30                right.into_iter().fold(left, |left, (op, right)| {
31                    Self::BinOp(op, Box::new(left), Box::new(right))
32                }),
33            ))
34        }
35    };
36    ( $name:ident, $inner:ident, $( $op:expr ),+ ) => {
37        fn $name(i: &'a str, level: Level) -> ParseResult<'a, Self> {
38            let (_, level) = level.nest(i)?;
39            let (i, left) = Self::$inner(i, level)?;
40            let (i, right) = many0(pair(
41                ws(alt(($( tag($op) ),+,))),
42                |i| Self::$inner(i, level),
43            ))(i)?;
44            Ok((
45                i,
46                right.into_iter().fold(left, |left, (op, right)| {
47                    Self::BinOp(op, Box::new(left), Box::new(right))
48                }),
49            ))
50        }
51    }
52}
53
54#[derive(Clone, Debug, PartialEq)]
55pub enum Expr<'a> {
56    BoolLit(&'a str),
57    NumLit(&'a str),
58    StrLit(&'a str),
59    CharLit(&'a str),
60    Var(&'a str),
61    Path(Vec<&'a str>),
62    Array(Vec<Expr<'a>>),
63    Attr(Box<Expr<'a>>, &'a str),
64    Index(Box<Expr<'a>>, Box<Expr<'a>>),
65    Filter(&'a str, Vec<Expr<'a>>),
66    NamedArgument(&'a str, Box<Expr<'a>>),
67    Unary(&'a str, Box<Expr<'a>>),
68    BinOp(&'a str, Box<Expr<'a>>, Box<Expr<'a>>),
69    Range(&'a str, Option<Box<Expr<'a>>>, Option<Box<Expr<'a>>>),
70    Group(Box<Expr<'a>>),
71    Tuple(Vec<Expr<'a>>),
72    Call(Box<Expr<'a>>, Vec<Expr<'a>>),
73    RustMacro(Vec<&'a str>, &'a str),
74    Try(Box<Expr<'a>>),
75}
76
77impl<'a> Expr<'a> {
78    pub(super) fn arguments(
79        i: &'a str,
80        level: Level,
81        is_template_macro: bool,
82    ) -> ParseResult<'a, Vec<Self>> {
83        let (_, level) = level.nest(i)?;
84        let mut named_arguments = HashSet::new();
85        let start = i;
86
87        preceded(
88            ws(char('(')),
89            cut(terminated(
90                separated_list0(
91                    char(','),
92                    ws(move |i| {
93                        // Needed to prevent borrowing it twice between this closure and the one
94                        // calling `Self::named_arguments`.
95                        let named_arguments = &mut named_arguments;
96                        let has_named_arguments = !named_arguments.is_empty();
97
98                        let (i, expr) = alt((
99                            move |i| {
100                                Self::named_argument(
101                                    i,
102                                    level,
103                                    named_arguments,
104                                    start,
105                                    is_template_macro,
106                                )
107                            },
108                            move |i| Self::parse(i, level),
109                        ))(i)?;
110                        if has_named_arguments && !matches!(expr, Self::NamedArgument(_, _)) {
111                            Err(nom::Err::Failure(ErrorContext {
112                                input: start,
113                                message: Some(Cow::Borrowed(
114                                    "named arguments must always be passed last",
115                                )),
116                            }))
117                        } else {
118                            Ok((i, expr))
119                        }
120                    }),
121                ),
122                tuple((opt(ws(char(','))), char(')'))),
123            )),
124        )(i)
125    }
126
127    fn named_argument(
128        i: &'a str,
129        level: Level,
130        named_arguments: &mut HashSet<&'a str>,
131        start: &'a str,
132        is_template_macro: bool,
133    ) -> ParseResult<'a, Self> {
134        if !is_template_macro {
135            // If this is not a template macro, we don't want to parse named arguments so
136            // we instead return an error which will allow to continue the parsing.
137            return Err(nom::Err::Error(error_position!(i, ErrorKind::Alt)));
138        }
139
140        let (_, level) = level.nest(i)?;
141        let (i, (argument, _, value)) =
142            tuple((identifier, ws(char('=')), move |i| Self::parse(i, level)))(i)?;
143        if named_arguments.insert(argument) {
144            Ok((i, Self::NamedArgument(argument, Box::new(value))))
145        } else {
146            Err(nom::Err::Failure(ErrorContext {
147                input: start,
148                message: Some(Cow::Owned(format!(
149                    "named argument `{argument}` was passed more than once"
150                ))),
151            }))
152        }
153    }
154
155    pub(super) fn parse(i: &'a str, level: Level) -> ParseResult<'a, Self> {
156        let (_, level) = level.nest(i)?;
157        let range_right = move |i| {
158            pair(
159                ws(alt((tag("..="), tag("..")))),
160                opt(move |i| Self::or(i, level)),
161            )(i)
162        };
163        alt((
164            map(range_right, |(op, right)| {
165                Self::Range(op, None, right.map(Box::new))
166            }),
167            map(
168                pair(move |i| Self::or(i, level), opt(range_right)),
169                |(left, right)| match right {
170                    Some((op, right)) => Self::Range(op, Some(Box::new(left)), right.map(Box::new)),
171                    None => left,
172                },
173            ),
174        ))(i)
175    }
176
177    expr_prec_layer!(or, and, "||");
178    expr_prec_layer!(and, compare, "&&");
179    expr_prec_layer!(compare, bor, "==", "!=", ">=", ">", "<=", "<");
180    expr_prec_layer!(bor, bxor, "|");
181    expr_prec_layer!(bxor, band, "^");
182    expr_prec_layer!(band, shifts, "&");
183    expr_prec_layer!(shifts, addsub, ">>", "<<");
184    expr_prec_layer!(addsub, muldivmod, "+", "-");
185    expr_prec_layer!(muldivmod, filtered, "*", "/", "%");
186
187    fn filtered(i: &'a str, level: Level) -> ParseResult<'a, Self> {
188        let (_, level) = level.nest(i)?;
189        #[allow(clippy::type_complexity)]
190        fn filter(i: &str, level: Level) -> ParseResult<'_, (&str, Option<Vec<Expr<'_>>>)> {
191            let (i, (_, fname, args)) = tuple((
192                char('|'),
193                ws(identifier),
194                opt(|i| Expr::arguments(i, level, false)),
195            ))(i)?;
196            Ok((i, (fname, args)))
197        }
198
199        let (i, (obj, filters)) =
200            tuple((|i| Self::prefix(i, level), many0(|i| filter(i, level))))(i)?;
201
202        let mut res = obj;
203        for (fname, args) in filters {
204            res = Self::Filter(fname, {
205                let mut args = match args {
206                    Some(inner) => inner,
207                    None => Vec::new(),
208                };
209                args.insert(0, res);
210                args
211            });
212        }
213
214        Ok((i, res))
215    }
216
217    fn prefix(i: &'a str, mut level: Level) -> ParseResult<'a, Self> {
218        let (_, nested) = level.nest(i)?;
219        let (i, (ops, mut expr)) = pair(many0(ws(alt((tag("!"), tag("-"))))), |i| {
220            Suffix::parse(i, nested)
221        })(i)?;
222
223        for op in ops.iter().rev() {
224            // This is a rare place where we create recursion in the parsed AST
225            // without recursing the parser call stack. However, this can lead
226            // to stack overflows in drop glue when the AST is very deep.
227            level = level.nest(i)?.1;
228            expr = Self::Unary(op, Box::new(expr));
229        }
230
231        Ok((i, expr))
232    }
233
234    fn single(i: &'a str, level: Level) -> ParseResult<'a, Self> {
235        let (_, level) = level.nest(i)?;
236        alt((
237            Self::num,
238            Self::str,
239            Self::char,
240            Self::path_var_bool,
241            move |i| Self::array(i, level),
242            move |i| Self::group(i, level),
243        ))(i)
244    }
245
246    fn group(i: &'a str, level: Level) -> ParseResult<'a, Self> {
247        let (_, level) = level.nest(i)?;
248        let (i, expr) = preceded(ws(char('(')), opt(|i| Self::parse(i, level)))(i)?;
249        let expr = match expr {
250            Some(expr) => expr,
251            None => {
252                let (i, _) = char(')')(i)?;
253                return Ok((i, Self::Tuple(vec![])));
254            }
255        };
256
257        let (i, comma) = ws(opt(peek(char(','))))(i)?;
258        if comma.is_none() {
259            let (i, _) = char(')')(i)?;
260            return Ok((i, Self::Group(Box::new(expr))));
261        }
262
263        let mut exprs = vec![expr];
264        let (i, _) = fold_many0(
265            preceded(char(','), ws(|i| Self::parse(i, level))),
266            || (),
267            |_, expr| {
268                exprs.push(expr);
269            },
270        )(i)?;
271        let (i, _) = pair(ws(opt(char(','))), char(')'))(i)?;
272        Ok((i, Self::Tuple(exprs)))
273    }
274
275    fn array(i: &'a str, level: Level) -> ParseResult<'a, Self> {
276        let (_, level) = level.nest(i)?;
277        preceded(
278            ws(char('[')),
279            cut(terminated(
280                map(
281                    separated_list0(char(','), ws(move |i| Self::parse(i, level))),
282                    Self::Array,
283                ),
284                char(']'),
285            )),
286        )(i)
287    }
288
289    fn path_var_bool(i: &'a str) -> ParseResult<'a, Self> {
290        map(path_or_identifier, |v| match v {
291            PathOrIdentifier::Path(v) => Self::Path(v),
292            PathOrIdentifier::Identifier(v @ "true") => Self::BoolLit(v),
293            PathOrIdentifier::Identifier(v @ "false") => Self::BoolLit(v),
294            PathOrIdentifier::Identifier(v) => Self::Var(v),
295        })(i)
296    }
297
298    fn str(i: &'a str) -> ParseResult<'a, Self> {
299        map(str_lit, Self::StrLit)(i)
300    }
301
302    fn num(i: &'a str) -> ParseResult<'a, Self> {
303        map(num_lit, Self::NumLit)(i)
304    }
305
306    fn char(i: &'a str) -> ParseResult<'a, Self> {
307        map(char_lit, Self::CharLit)(i)
308    }
309}
310
311enum Suffix<'a> {
312    Attr(&'a str),
313    Index(Expr<'a>),
314    Call(Vec<Expr<'a>>),
315    // The value is the arguments of the macro call.
316    MacroCall(&'a str),
317    Try,
318}
319
320impl<'a> Suffix<'a> {
321    fn parse(i: &'a str, level: Level) -> ParseResult<'a, Expr<'a>> {
322        let (_, level) = level.nest(i)?;
323        let (mut i, mut expr) = Expr::single(i, level)?;
324        loop {
325            let (j, suffix) = opt(alt((
326                Self::attr,
327                |i| Self::index(i, level),
328                |i| Self::call(i, level),
329                Self::r#try,
330                Self::r#macro,
331            )))(i)?;
332
333            match suffix {
334                Some(Self::Attr(attr)) => expr = Expr::Attr(expr.into(), attr),
335                Some(Self::Index(index)) => expr = Expr::Index(expr.into(), index.into()),
336                Some(Self::Call(args)) => expr = Expr::Call(expr.into(), args),
337                Some(Self::Try) => expr = Expr::Try(expr.into()),
338                Some(Self::MacroCall(args)) => match expr {
339                    Expr::Path(path) => expr = Expr::RustMacro(path, args),
340                    Expr::Var(name) => expr = Expr::RustMacro(vec![name], args),
341                    _ => return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))),
342                },
343                None => break,
344            }
345
346            i = j;
347        }
348        Ok((i, expr))
349    }
350
351    fn r#macro(i: &'a str) -> ParseResult<'a, Self> {
352        fn nested_parenthesis(input: &str) -> ParseResult<'_, ()> {
353            let mut nested = 0;
354            let mut last = 0;
355            let mut in_str = false;
356            let mut escaped = false;
357
358            for (i, c) in input.char_indices() {
359                if !(c == '(' || c == ')') || !in_str {
360                    match c {
361                        '(' => nested += 1,
362                        ')' => {
363                            if nested == 0 {
364                                last = i;
365                                break;
366                            }
367                            nested -= 1;
368                        }
369                        '"' => {
370                            if in_str {
371                                if !escaped {
372                                    in_str = false;
373                                }
374                            } else {
375                                in_str = true;
376                            }
377                        }
378                        '\\' => {
379                            escaped = !escaped;
380                        }
381                        _ => (),
382                    }
383                }
384
385                if escaped && c != '\\' {
386                    escaped = false;
387                }
388            }
389
390            if nested == 0 {
391                Ok((&input[last..], ()))
392            } else {
393                Err(nom::Err::Error(error_position!(
394                    input,
395                    ErrorKind::SeparatedNonEmptyList
396                )))
397            }
398        }
399
400        preceded(
401            pair(ws(char('!')), char('(')),
402            cut(terminated(
403                map(recognize(nested_parenthesis), Self::MacroCall),
404                char(')'),
405            )),
406        )(i)
407    }
408
409    fn attr(i: &'a str) -> ParseResult<'a, Self> {
410        map(
411            preceded(
412                ws(pair(char('.'), not(char('.')))),
413                cut(alt((num_lit, identifier))),
414            ),
415            Self::Attr,
416        )(i)
417    }
418
419    fn index(i: &'a str, level: Level) -> ParseResult<'a, Self> {
420        let (_, level) = level.nest(i)?;
421        map(
422            preceded(
423                ws(char('[')),
424                cut(terminated(ws(move |i| Expr::parse(i, level)), char(']'))),
425            ),
426            Self::Index,
427        )(i)
428    }
429
430    fn call(i: &'a str, level: Level) -> ParseResult<'a, Self> {
431        let (_, level) = level.nest(i)?;
432        map(move |i| Expr::arguments(i, level, false), Self::Call)(i)
433    }
434
435    fn r#try(i: &'a str) -> ParseResult<'a, Self> {
436        map(preceded(take_till(not_ws), char('?')), |_| Self::Try)(i)
437    }
438}