1 | use std::collections::HashSet; |
2 | use std::str; |
3 | |
4 | use nom::branch::alt; |
5 | use nom::bytes::complete::{tag, take_till}; |
6 | use nom::character::complete::{char, digit1}; |
7 | use nom::combinator::{consumed, cut, fail, map, not, opt, peek, recognize, value}; |
8 | use nom::error::ErrorKind; |
9 | use nom::error_position; |
10 | use nom::multi::{fold_many0, many0, separated_list0, separated_list1}; |
11 | use nom::sequence::{pair, preceded, terminated, tuple}; |
12 | |
13 | use 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 | |
18 | macro_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)] |
36 | pub 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 | |
71 | impl<'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 | |
402 | fn 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 | |
414 | fn 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)] |
430 | pub struct Filter<'a> { |
431 | pub name: &'a str, |
432 | pub arguments: Vec<WithSpan<'a, Expr<'a>>>, |
433 | } |
434 | |
435 | enum 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 | |
444 | impl<'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 | |