1use std::collections::HashSet;
2use std::str;
3
4use nom::branch::alt;
5use nom::bytes::complete::{tag, take_till};
6use nom::character::complete::{char, digit1};
7use nom::combinator::{consumed, cut, fail, map, not, opt, peek, recognize, value};
8use nom::error::ErrorKind;
9use nom::error_position;
10use nom::multi::{fold_many0, many0, separated_list0, separated_list1};
11use nom::sequence::{pair, preceded, terminated, tuple};
12
13use crate::{
14 CharLit, ErrorContext, Level, Num, ParseResult, PathOrIdentifier, StrLit, WithSpan, char_lit,
15 filter, identifier, keyword, not_ws, num_lit, path_or_identifier, str_lit, ws,
16};
17
18macro_rules! expr_prec_layer {
19 ( $name:ident, $inner:ident, $op:expr ) => {
20 fn $name(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
21 let (_, level) = level.nest(i)?;
22 let start = i;
23 let (i, left) = Self::$inner(i, level)?;
24 let (i, right) = many0(pair(ws($op), |i| Self::$inner(i, level)))(i)?;
25 Ok((
26 i,
27 right.into_iter().fold(left, |left, (op, right)| {
28 WithSpan::new(Self::BinOp(op, Box::new(left), Box::new(right)), start)
29 }),
30 ))
31 }
32 };
33}
34
35#[derive(Clone, Debug, PartialEq)]
36pub enum Expr<'a> {
37 BoolLit(bool),
38 NumLit(&'a str, Num<'a>),
39 StrLit(StrLit<'a>),
40 CharLit(CharLit<'a>),
41 Var(&'a str),
42 Path(Vec<&'a str>),
43 Array(Vec<WithSpan<'a, Expr<'a>>>),
44 Attr(Box<WithSpan<'a, Expr<'a>>>, &'a str),
45 Index(Box<WithSpan<'a, Expr<'a>>>, Box<WithSpan<'a, Expr<'a>>>),
46 Filter(Filter<'a>),
47 As(Box<WithSpan<'a, Expr<'a>>>, &'a str),
48 NamedArgument(&'a str, Box<WithSpan<'a, Expr<'a>>>),
49 Unary(&'a str, Box<WithSpan<'a, Expr<'a>>>),
50 BinOp(
51 &'a str,
52 Box<WithSpan<'a, Expr<'a>>>,
53 Box<WithSpan<'a, Expr<'a>>>,
54 ),
55 Range(
56 &'a str,
57 Option<Box<WithSpan<'a, Expr<'a>>>>,
58 Option<Box<WithSpan<'a, Expr<'a>>>>,
59 ),
60 Group(Box<WithSpan<'a, Expr<'a>>>),
61 Tuple(Vec<WithSpan<'a, Expr<'a>>>),
62 Call(Box<WithSpan<'a, Expr<'a>>>, Vec<WithSpan<'a, Expr<'a>>>),
63 RustMacro(Vec<&'a str>, &'a str),
64 Try(Box<WithSpan<'a, Expr<'a>>>),
65 /// This variant should never be used directly. It is created when generating filter blocks.
66 FilterSource,
67 IsDefined(&'a str),
68 IsNotDefined(&'a str),
69}
70
71impl<'a> Expr<'a> {
72 pub(super) fn arguments(
73 i: &'a str,
74 level: Level,
75 is_template_macro: bool,
76 ) -> ParseResult<'a, Vec<WithSpan<'a, Self>>> {
77 let (_, level) = level.nest(i)?;
78 let mut named_arguments = HashSet::new();
79 let start = i;
80
81 preceded(
82 ws(char('(')),
83 cut(terminated(
84 separated_list0(
85 char(','),
86 ws(move |i| {
87 // Needed to prevent borrowing it twice between this closure and the one
88 // calling `Self::named_arguments`.
89 let named_arguments = &mut named_arguments;
90 let has_named_arguments = !named_arguments.is_empty();
91
92 let (i, expr) = alt((
93 move |i| {
94 Self::named_argument(
95 i,
96 level,
97 named_arguments,
98 start,
99 is_template_macro,
100 )
101 },
102 move |i| Self::parse(i, level),
103 ))(i)?;
104 if has_named_arguments && !matches!(*expr, Self::NamedArgument(_, _)) {
105 Err(nom::Err::Failure(ErrorContext::new(
106 "named arguments must always be passed last",
107 start,
108 )))
109 } else {
110 Ok((i, expr))
111 }
112 }),
113 ),
114 tuple((opt(ws(char(','))), char(')'))),
115 )),
116 )(i)
117 }
118
119 fn named_argument(
120 i: &'a str,
121 level: Level,
122 named_arguments: &mut HashSet<&'a str>,
123 start: &'a str,
124 is_template_macro: bool,
125 ) -> ParseResult<'a, WithSpan<'a, Self>> {
126 if !is_template_macro {
127 // If this is not a template macro, we don't want to parse named arguments so
128 // we instead return an error which will allow to continue the parsing.
129 return fail(i);
130 }
131
132 let (_, level) = level.nest(i)?;
133 let (i, (argument, _, value)) =
134 tuple((identifier, ws(char('=')), move |i| Self::parse(i, level)))(i)?;
135 if named_arguments.insert(argument) {
136 Ok((
137 i,
138 WithSpan::new(Self::NamedArgument(argument, Box::new(value)), start),
139 ))
140 } else {
141 Err(nom::Err::Failure(ErrorContext::new(
142 format!("named argument `{argument}` was passed more than once"),
143 start,
144 )))
145 }
146 }
147
148 pub(super) fn parse(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
149 let (_, level) = level.nest(i)?;
150 let start = i;
151 let range_right = move |i| {
152 pair(
153 ws(alt((tag("..="), tag("..")))),
154 opt(move |i| Self::or(i, level)),
155 )(i)
156 };
157 alt((
158 map(range_right, |(op, right)| {
159 WithSpan::new(Self::Range(op, None, right.map(Box::new)), start)
160 }),
161 map(
162 pair(move |i| Self::or(i, level), opt(range_right)),
163 |(left, right)| match right {
164 Some((op, right)) => WithSpan::new(
165 Self::Range(op, Some(Box::new(left)), right.map(Box::new)),
166 start,
167 ),
168 None => left,
169 },
170 ),
171 ))(i)
172 }
173
174 expr_prec_layer!(or, and, tag("||"));
175 expr_prec_layer!(and, compare, tag("&&"));
176 expr_prec_layer!(
177 compare,
178 bor,
179 alt((
180 tag("=="),
181 tag("!="),
182 tag(">="),
183 tag(">"),
184 tag("<="),
185 tag("<"),
186 ))
187 );
188 expr_prec_layer!(bor, bxor, value("|", tag("bitor")));
189 expr_prec_layer!(bxor, band, token_xor);
190 expr_prec_layer!(band, shifts, token_bitand);
191 expr_prec_layer!(shifts, addsub, alt((tag(">>"), tag("<<"))));
192 expr_prec_layer!(addsub, muldivmod, alt((tag("+"), tag("-"))));
193 expr_prec_layer!(muldivmod, is_as, alt((tag("*"), tag("/"), tag("%"))));
194
195 fn is_as(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
196 let start = i;
197 let (before_keyword, lhs) = Self::filtered(i, level)?;
198 let (j, rhs) = opt(ws(identifier))(before_keyword)?;
199 let i = match rhs {
200 Some("is") => j,
201 Some("as") => {
202 let (i, target) = opt(identifier)(j)?;
203 let target = target.unwrap_or_default();
204 if crate::PRIMITIVE_TYPES.contains(&target) {
205 return Ok((i, WithSpan::new(Self::As(Box::new(lhs), target), start)));
206 } else if target.is_empty() {
207 return Err(nom::Err::Failure(ErrorContext::new(
208 "`as` operator expects the name of a primitive type on its right-hand side",
209 before_keyword.trim_start(),
210 )));
211 } else {
212 return Err(nom::Err::Failure(ErrorContext::new(
213 format!(
214 "`as` operator expects the name of a primitive type on its right-hand \
215 side, found `{target}`"
216 ),
217 before_keyword.trim_start(),
218 )));
219 }
220 }
221 _ => return Ok((before_keyword, lhs)),
222 };
223
224 let (i, rhs) = opt(terminated(opt(keyword("not")), ws(keyword("defined"))))(i)?;
225 let ctor = match rhs {
226 None => {
227 return Err(nom::Err::Failure(ErrorContext::new(
228 "expected `defined` or `not defined` after `is`",
229 // We use `start` to show the whole `var is` thing instead of the current token.
230 start,
231 )));
232 }
233 Some(None) => Self::IsDefined,
234 Some(Some(_)) => Self::IsNotDefined,
235 };
236 let var_name = match *lhs {
237 Self::Var(var_name) => var_name,
238 Self::Attr(_, _) => {
239 return Err(nom::Err::Failure(ErrorContext::new(
240 "`is defined` operator can only be used on variables, not on their fields",
241 start,
242 )));
243 }
244 _ => {
245 return Err(nom::Err::Failure(ErrorContext::new(
246 "`is defined` operator can only be used on variables",
247 start,
248 )));
249 }
250 };
251 Ok((i, WithSpan::new(ctor(var_name), start)))
252 }
253
254 fn filtered(i: &'a str, mut level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
255 let start = i;
256 let (mut i, mut res) = Self::prefix(i, level)?;
257 while let (j, Some((name, args))) = opt(|i| filter(i, &mut level))(i)? {
258 i = j;
259
260 let mut arguments = args.unwrap_or_else(|| Vec::with_capacity(1));
261 arguments.insert(0, res);
262
263 res = WithSpan::new(Self::Filter(Filter { name, arguments }), start);
264 }
265 Ok((i, res))
266 }
267
268 fn prefix(i: &'a str, mut level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
269 let (_, nested) = level.nest(i)?;
270 let start = i;
271 let (i, (ops, mut expr)) = pair(
272 many0(ws(alt((tag("!"), tag("-"), tag("*"), tag("&"))))),
273 |i| Suffix::parse(i, nested),
274 )(i)?;
275
276 for op in ops.iter().rev() {
277 // This is a rare place where we create recursion in the parsed AST
278 // without recursing the parser call stack. However, this can lead
279 // to stack overflows in drop glue when the AST is very deep.
280 level = level.nest(i)?.1;
281 expr = WithSpan::new(Self::Unary(op, Box::new(expr)), start);
282 }
283
284 Ok((i, expr))
285 }
286
287 fn single(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
288 let (_, level) = level.nest(i)?;
289 alt((
290 Self::num,
291 Self::str,
292 Self::char,
293 Self::path_var_bool,
294 move |i| Self::array(i, level),
295 move |i| Self::group(i, level),
296 ))(i)
297 }
298
299 fn group(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
300 let (_, level) = level.nest(i)?;
301 let start = i;
302 let (i, expr) = preceded(ws(char('(')), opt(|i| Self::parse(i, level)))(i)?;
303 let Some(expr) = expr else {
304 let (i, _) = char(')')(i)?;
305 return Ok((i, WithSpan::new(Self::Tuple(vec![]), start)));
306 };
307
308 let (i, comma) = ws(opt(peek(char(','))))(i)?;
309 if comma.is_none() {
310 let (i, _) = char(')')(i)?;
311 return Ok((i, WithSpan::new(Self::Group(Box::new(expr)), start)));
312 }
313
314 let mut exprs = vec![expr];
315 let (i, ()) = fold_many0(
316 preceded(char(','), ws(|i| Self::parse(i, level))),
317 || (),
318 |(), expr| {
319 exprs.push(expr);
320 },
321 )(i)?;
322 let (i, _) = pair(ws(opt(char(','))), char(')'))(i)?;
323 Ok((i, WithSpan::new(Self::Tuple(exprs), start)))
324 }
325
326 fn array(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Self>> {
327 let start = i;
328 let (i, level) = level.nest(i)?;
329 let (i, array) = preceded(
330 ws(char('[')),
331 cut(terminated(
332 opt(terminated(
333 separated_list1(char(','), ws(move |i| Self::parse(i, level))),
334 ws(opt(char(','))),
335 )),
336 char(']'),
337 )),
338 )(i)?;
339 Ok((
340 i,
341 WithSpan::new(Self::Array(array.unwrap_or_default()), start),
342 ))
343 }
344
345 fn path_var_bool(i: &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
346 let start = i;
347 map(path_or_identifier, |v| match v {
348 PathOrIdentifier::Path(v) => Self::Path(v),
349 PathOrIdentifier::Identifier("true") => Self::BoolLit(true),
350 PathOrIdentifier::Identifier("false") => Self::BoolLit(false),
351 PathOrIdentifier::Identifier(v) => Self::Var(v),
352 })(i)
353 .map(|(i, expr)| (i, WithSpan::new(expr, start)))
354 }
355
356 fn str(i: &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
357 let start = i;
358 map(str_lit, |i| WithSpan::new(Self::StrLit(i), start))(i)
359 }
360
361 fn num(i: &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
362 let start = i;
363 let (i, (full, num)) = consumed(num_lit)(i)?;
364 Ok((i, WithSpan::new(Expr::NumLit(full, num), start)))
365 }
366
367 fn char(i: &'a str) -> ParseResult<'a, WithSpan<'a, Self>> {
368 let start = i;
369 map(char_lit, |i| WithSpan::new(Self::CharLit(i), start))(i)
370 }
371
372 #[must_use]
373 pub fn contains_bool_lit_or_is_defined(&self) -> bool {
374 match self {
375 Self::BoolLit(_) | Self::IsDefined(_) | Self::IsNotDefined(_) => true,
376 Self::Unary(_, expr) | Self::Group(expr) => expr.contains_bool_lit_or_is_defined(),
377 Self::BinOp("&&" | "||", left, right) => {
378 left.contains_bool_lit_or_is_defined() || right.contains_bool_lit_or_is_defined()
379 }
380 Self::NumLit(_, _)
381 | Self::StrLit(_)
382 | Self::CharLit(_)
383 | Self::Var(_)
384 | Self::FilterSource
385 | Self::RustMacro(_, _)
386 | Self::As(_, _)
387 | Self::Call(_, _)
388 | Self::Range(_, _, _)
389 | Self::Try(_)
390 | Self::NamedArgument(_, _)
391 | Self::Filter(_)
392 | Self::Attr(_, _)
393 | Self::Index(_, _)
394 | Self::Tuple(_)
395 | Self::Array(_)
396 | Self::BinOp(_, _, _)
397 | Self::Path(_) => false,
398 }
399 }
400}
401
402fn token_xor(i: &str) -> ParseResult<'_> {
403 let (i: &str, good: bool) = alt((value(val:true, parser:keyword("xor")), value(val:false, parser:char('^'))))(i)?;
404 if good {
405 Ok((i, "^"))
406 } else {
407 Err(nom::Err::Failure(ErrorContext::new(
408 message:"the binary XOR operator is called `xor` in rinja",
409 input:i,
410 )))
411 }
412}
413
414fn token_bitand(i: &str) -> ParseResult<'_> {
415 let (i: &str, good: bool) = alt((
416 value(val:true, parser:keyword("bitand")),
417 value(val:false, parser:pair(first:char('&'), second:not(parser:char('&')))),
418 ))(i)?;
419 if good {
420 Ok((i, "&"))
421 } else {
422 Err(nom::Err::Failure(ErrorContext::new(
423 message:"the binary AND operator is called `bitand` in rinja",
424 input:i,
425 )))
426 }
427}
428
429#[derive(Clone, Debug, PartialEq)]
430pub struct Filter<'a> {
431 pub name: &'a str,
432 pub arguments: Vec<WithSpan<'a, Expr<'a>>>,
433}
434
435enum Suffix<'a> {
436 Attr(&'a str),
437 Index(WithSpan<'a, Expr<'a>>),
438 Call(Vec<WithSpan<'a, Expr<'a>>>),
439 // The value is the arguments of the macro call.
440 MacroCall(&'a str),
441 Try,
442}
443
444impl<'a> Suffix<'a> {
445 fn parse(i: &'a str, level: Level) -> ParseResult<'a, WithSpan<'a, Expr<'a>>> {
446 let (_, level) = level.nest(i)?;
447 let (mut i, mut expr) = Expr::single(i, level)?;
448 loop {
449 let (j, suffix) = opt(alt((
450 Self::attr,
451 |i| Self::index(i, level),
452 |i| Self::call(i, level),
453 Self::r#try,
454 Self::r#macro,
455 )))(i)?;
456
457 match suffix {
458 Some(Self::Attr(attr)) => expr = WithSpan::new(Expr::Attr(expr.into(), attr), i),
459 Some(Self::Index(index)) => {
460 expr = WithSpan::new(Expr::Index(expr.into(), index.into()), i);
461 }
462 Some(Self::Call(args)) => expr = WithSpan::new(Expr::Call(expr.into(), args), i),
463 Some(Self::Try) => expr = WithSpan::new(Expr::Try(expr.into()), i),
464 Some(Self::MacroCall(args)) => match expr.inner {
465 Expr::Path(path) => expr = WithSpan::new(Expr::RustMacro(path, args), i),
466 Expr::Var(name) => expr = WithSpan::new(Expr::RustMacro(vec![name], args), i),
467 _ => return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag))),
468 },
469 None => break,
470 }
471
472 i = j;
473 }
474 Ok((i, expr))
475 }
476
477 fn r#macro(i: &'a str) -> ParseResult<'a, Self> {
478 fn nested_parenthesis(input: &str) -> ParseResult<'_, ()> {
479 let mut nested = 0;
480 let mut last = 0;
481 let mut in_str = false;
482 let mut escaped = false;
483
484 for (i, c) in input.char_indices() {
485 if !(c == '(' || c == ')') || !in_str {
486 match c {
487 '(' => nested += 1,
488 ')' => {
489 if nested == 0 {
490 last = i;
491 break;
492 }
493 nested -= 1;
494 }
495 '"' => {
496 if in_str {
497 if !escaped {
498 in_str = false;
499 }
500 } else {
501 in_str = true;
502 }
503 }
504 '\\' => {
505 escaped = !escaped;
506 }
507 _ => (),
508 }
509 }
510
511 if escaped && c != '\\' {
512 escaped = false;
513 }
514 }
515
516 if nested == 0 {
517 Ok((&input[last..], ()))
518 } else {
519 fail(input)
520 }
521 }
522
523 preceded(
524 pair(ws(char('!')), char('(')),
525 cut(terminated(
526 map(recognize(nested_parenthesis), Self::MacroCall),
527 char(')'),
528 )),
529 )(i)
530 }
531
532 fn attr(i: &'a str) -> ParseResult<'a, Self> {
533 map(
534 preceded(
535 ws(pair(char('.'), not(char('.')))),
536 cut(alt((digit1, identifier))),
537 ),
538 Self::Attr,
539 )(i)
540 }
541
542 fn index(i: &'a str, level: Level) -> ParseResult<'a, Self> {
543 let (_, level) = level.nest(i)?;
544 map(
545 preceded(
546 ws(char('[')),
547 cut(terminated(ws(move |i| Expr::parse(i, level)), char(']'))),
548 ),
549 Self::Index,
550 )(i)
551 }
552
553 fn call(i: &'a str, level: Level) -> ParseResult<'a, Self> {
554 let (_, level) = level.nest(i)?;
555 map(move |i| Expr::arguments(i, level, false), Self::Call)(i)
556 }
557
558 fn r#try(i: &'a str) -> ParseResult<'a, Self> {
559 map(preceded(take_till(not_ws), char('?')), |_| Self::Try)(i)
560 }
561}
562