1use std::str::{self, FromStr};
2
3use winnow::combinator::{
4 alt, cut_err, delimited, eof, fail, not, opt, peek, preceded, repeat, separated,
5 separated_pair, terminated,
6};
7use winnow::error::ErrMode;
8use winnow::stream::{Location, Stream};
9use winnow::token::{any, literal, rest, take, take_until};
10use winnow::{ModalParser, Parser};
11
12use crate::expr::BinOp;
13use crate::{
14 ErrorContext, Expr, Filter, HashSet, InputStream, ParseErr, ParseResult, Span, Target,
15 TyGenerics, WithSpan, block_end, block_start, cut_error, deny_any_rust_token, expr_end,
16 expr_start, filter, identifier, is_rust_keyword, keyword, skip_ws0, str_lit_without_prefix, ws,
17};
18
19#[derive(Debug, PartialEq)]
20pub enum Node<'a> {
21 Lit(WithSpan<Lit<'a>>),
22 Comment(WithSpan<Comment<'a>>),
23 Expr(Ws, WithSpan<Box<Expr<'a>>>),
24 Call(WithSpan<Call<'a>>),
25 Let(WithSpan<Let<'a>>),
26 Compound(WithSpan<Compound<'a>>),
28 Declare(WithSpan<Declare<'a>>),
29 If(WithSpan<If<'a>>),
30 Match(WithSpan<Match<'a>>),
31 Loop(WithSpan<Loop<'a>>),
32 Extends(WithSpan<Extends<'a>>),
33 BlockDef(WithSpan<BlockDef<'a>>),
34 Include(WithSpan<Include<'a>>),
35 Import(WithSpan<Import<'a>>),
36 Macro(WithSpan<Macro<'a>>),
37 Raw(WithSpan<Raw<'a>>),
38 Break(WithSpan<Ws>),
39 Continue(WithSpan<Ws>),
40 FilterBlock(WithSpan<FilterBlock<'a>>),
41}
42
43impl<'a: 'l, 'l> Node<'a> {
44 pub(super) fn parse_template(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec<Box<Self>>> {
45 let mut nodes = vec![];
46 let mut allow_extends = true;
47 while let Some(node) = parse_with_unexpected_fallback(
48 opt(move |i: &mut _| Self::one(i, allow_extends)),
49 unexpected_tag,
50 )
51 .parse_next(i)?
52 {
53 if allow_extends {
54 match &*node {
55 Node::Comment(_) => {}
57 Node::Lit(lit) if lit.val.is_empty() => {}
59 _ => allow_extends = false,
61 }
62 }
63 nodes.push(node);
64 }
65
66 if !i.is_empty() {
67 opt(unexpected_tag).parse_next(i)?;
68 return cut_error!(
69 "cannot parse entire template\n\
70 you should never encounter this error\n\
71 please report this error to <https://github.com/askama-rs/askama/issues>",
72 *i,
73 );
74 }
75 Ok(nodes)
76 }
77
78 fn many(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec<Box<Self>>> {
79 repeat(0.., |i: &mut _| Self::one(i, false)).parse_next(i)
80 }
81
82 fn one(i: &mut InputStream<'a, 'l>, allow_extends: bool) -> ParseResult<'a, Box<Self>> {
83 let node = alt((Lit::parse, Comment::parse, Self::expr, Self::parse)).parse_next(i)?;
84 if !allow_extends && let Node::Extends(node) = &*node {
85 return cut_error!("`extends` block must come first in a template", node.span());
86 }
87 Ok(node)
88 }
89
90 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Self>> {
91 let start = i.checkpoint();
92 let (span, tag) = (
93 block_start.span(),
94 peek(preceded((opt(Whitespace::parse), skip_ws0), identifier)),
95 )
96 .parse_next(i)?;
97
98 let func = match tag {
99 "block" => BlockDef::parse,
100 "break" => Self::r#break,
101 "call" => Call::parse,
102 "continue" => Self::r#continue,
103 "decl" | "declare" => Declare::parse,
104 "extends" => Extends::parse,
105 "filter" => FilterBlock::parse,
106 "for" => Loop::parse,
107 "if" => If::parse,
108 "import" => Import::parse,
109 "include" => Include::parse,
110 "let" | "set" => Let::parse,
111 "macro" => Macro::parse,
112 "match" => Match::parse,
113 "mut" => Compound::parse,
114 "raw" => Raw::parse,
115 _ => {
116 i.reset(&start);
117 return fail.parse_next(i);
118 }
119 };
120
121 let _level_guard = i.state.level.nest(i)?;
122 let node = func(i)?;
123 let closed =
124 cut_node(None, alt((ws(eof).value(false), block_end.value(true)))).parse_next(i)?;
125 match closed {
126 true => Ok(node),
127 false => {
128 Err(
129 ErrorContext::unclosed("block", i.state.syntax.block_end, Span::new(span))
130 .cut(),
131 )
132 }
133 }
134 }
135
136 fn r#break(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
137 let mut p = (
138 opt(Whitespace::parse),
139 ws(keyword("break").span()),
140 opt(Whitespace::parse),
141 );
142
143 let (pws, span, nws) = p.parse_next(i)?;
144 if !i.state.is_in_loop() {
145 return cut_error!("you can only `break` inside a `for` loop", span);
146 }
147 Ok(Box::new(Self::Break(WithSpan::new(Ws(pws, nws), span))))
148 }
149
150 fn r#continue(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
151 let mut p = (
152 opt(Whitespace::parse),
153 ws(keyword("continue").span()),
154 opt(Whitespace::parse),
155 );
156
157 let (pws, span, nws) = p.parse_next(i)?;
158 if !i.state.is_in_loop() {
159 return cut_error!("you can only `continue` inside a `for` loop", span);
160 }
161 Ok(Box::new(Self::Continue(WithSpan::new(Ws(pws, nws), span))))
162 }
163
164 fn expr(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Self>> {
165 let mut p = (
166 expr_start.span(),
167 cut_node(
168 None,
169 (
170 opt(Whitespace::parse),
171 ws(|i: &mut _| Expr::parse(i, false)),
172 ),
173 ),
174 cut_node(
175 None,
176 (
177 opt(Whitespace::parse),
178 alt((
179 expr_end.value(true),
180 ws(eof).value(false),
181 deny_any_rust_token.value(false),
182 )),
183 ),
184 ),
185 );
186
187 let (start, (pws, expr), (nws, closed)) = p.parse_next(i)?;
188 if closed {
189 Ok(Box::new(Self::Expr(Ws(pws, nws), expr)))
190 } else {
191 Err(ErrorContext::unclosed("expression", i.state.syntax.expr_end, start).cut())
192 }
193 }
194
195 #[must_use]
196 pub fn span(&self) -> Span {
197 match self {
198 Self::Lit(span) => span.span,
199 Self::Comment(span) => span.span,
200 Self::Expr(_, span) => span.span,
201 Self::Call(span) => span.span,
202 Self::Let(span) => span.span,
203 Self::Compound(span) => span.span,
204 Self::Declare(span) => span.span,
205 Self::If(span) => span.span,
206 Self::Match(span) => span.span,
207 Self::Loop(span) => span.span,
208 Self::Extends(span) => span.span,
209 Self::BlockDef(span) => span.span,
210 Self::Include(span) => span.span,
211 Self::Import(span) => span.span,
212 Self::Macro(span) => span.span,
213 Self::Raw(span) => span.span,
214 Self::Break(span) => span.span,
215 Self::Continue(span) => span.span,
216 Self::FilterBlock(span) => span.span,
217 }
218 }
219}
220
221#[inline]
222fn parse_with_unexpected_fallback<'a: 'l, 'l, O>(
223 mut parser: impl ModalParser<InputStream<'a, 'l>, O, ErrorContext>,
224 mut unexpected_parser: impl FnMut(&mut InputStream<'a, 'l>) -> ParseResult<'a, ()>,
225) -> impl ModalParser<InputStream<'a, 'l>, O, ErrorContext> {
226 #[cold]
227 #[inline(never)]
228 fn try_assign_fallback_error<'a: 'l, 'l>(
229 i: &mut InputStream<'a, 'l>,
230 unexpected_parser: &mut dyn FnMut(&mut InputStream<'a, 'l>) -> ParseResult<'a, ()>,
231 err: &mut ErrMode<ErrorContext>,
232 ) {
233 let (ErrMode::Backtrack(err_ctx) | ErrMode::Cut(err_ctx)) = &err else {
234 return;
235 };
236 if err_ctx.message.is_some() {
237 return;
238 }
239
240 let checkpoint = i.checkpoint();
241 i.input.reset_to_start();
242 if take::<_, _, ()>(err_ctx.span.start).parse_next(i).is_ok()
243 && let Err(better_err) = opt(unexpected_parser).parse_next(i)
244 && let ErrMode::Backtrack(better_ctx) | ErrMode::Cut(better_ctx) = &better_err
245 && better_ctx.message.is_some()
246 {
247 *err = better_err;
248 }
249 i.reset(&checkpoint);
250 }
251
252 move |i: &mut InputStream<'a, 'l>| {
253 let mut result = parser.parse_next(i);
254 if let Err(err) = &mut result {
255 try_assign_fallback_error(i, &mut unexpected_parser, err);
256 }
257 result
258 }
259}
260
261#[inline]
262fn cut_node<'a: 'l, 'l, O>(
263 kind: Option<&'static str>,
264 inner: impl ModalParser<InputStream<'a, 'l>, O, ErrorContext>,
265) -> impl ModalParser<InputStream<'a, 'l>, O, ErrorContext> {
266 parse_with_unexpected_fallback(cut_err(inner), move |i: &mut _| unexpected_raw_tag(kind, i))
267}
268
269fn unexpected_tag<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> {
270 (block_start, opt(Whitespace::parse), |i: &mut _| {
271 unexpected_raw_tag(None, i)
272 })
273 .void()
274 .parse_next(i)
275}
276
277fn unexpected_raw_tag<'a: 'l, 'l>(
278 kind: Option<&'static str>,
279 i: &mut InputStream<'a, 'l>,
280) -> ParseResult<'a, ()> {
281 let Ok((tag, span)) = peek(ws(identifier.with_span())).parse_next(i) else {
282 let (c, span) = peek(ws(any.with_span())).parse_next(i)?;
283 return cut_error!(format!("unexpected character `{c}`"), span);
284 };
285 cut_error!(
286 match tag {
287 "end" | "elif" | "else" | "when" => match kind {
288 Some(kind) => {
289 format!("node `{tag}` was not expected in the current context: `{kind}` block")
290 }
291 None => format!("node `{tag}` was not expected in the current context"),
292 },
293 tag if tag.starts_with("end") => format!("unexpected closing tag `{tag}`"),
294 tag => format!("unknown node `{tag}`"),
295 },
296 span,
297 )
298}
299
300#[derive(Debug, PartialEq)]
301pub struct When<'a> {
302 pub ws: Ws,
303 pub target: Vec<Target<'a>>,
304 pub nodes: Vec<Box<Node<'a>>>,
305}
306
307impl<'a: 'l, 'l> When<'a> {
308 fn r#else(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan<Self>> {
309 let mut p = (
310 block_start,
311 opt(Whitespace::parse),
312 ws(keyword("else").span()),
313 cut_node(
314 Some("match-else"),
315 (
316 opt(Whitespace::parse),
317 block_end,
318 cut_node(Some("match-else"), Node::many),
319 ),
320 ),
321 );
322
323 let (_, pws, span, (nws, _, nodes)) = p.parse_next(i)?;
324 let span = Span::new(span);
325 let inner = Self {
326 ws: Ws(pws, nws),
327 target: vec![Target::Placeholder(WithSpan::new((), span))],
328 nodes,
329 };
330 Ok(WithSpan::new(inner, span))
331 }
332
333 #[allow(clippy::self_named_constructors)]
334 fn when(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan<Self>> {
335 let mut p = (
336 block_start,
337 opt(Whitespace::parse),
338 ws(keyword("when").span()),
339 cut_node(
340 Some("match-when"),
341 (
342 separated(1.., ws(Target::parse), '|'),
343 opt(Whitespace::parse),
344 block_end,
345 cut_node(Some("match-when"), Node::many),
346 opt(Self::endwhen),
347 ),
348 ),
349 );
350 let (_, pws, span, (target, nws, _, mut nodes, endwhen)) = p.parse_next(i)?;
351 if let Some(endwhen) = endwhen {
352 nodes.push(endwhen);
353 }
354 Ok(WithSpan::new(
355 Self {
356 ws: Ws(pws, nws),
357 target,
358 nodes,
359 },
360 span,
361 ))
362 }
363
364 fn endwhen(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
365 let mut p = ws(terminated(
366 (
367 block_start,
368 opt(Whitespace::parse),
369 ws(keyword("endwhen").span()),
370 ),
371 cut_node(
372 Some("match-endwhen"),
373 (
374 opt(Whitespace::parse),
375 block_end,
376 repeat(0.., ws(Comment::parse).void()).map(|()| ()),
377 ),
378 ),
379 ));
380 let (_, pws, span) = p.parse_next(i)?;
381 Ok(Box::new(Node::Comment(WithSpan::new(
382 Comment {
383 ws: Ws(pws, Some(Whitespace::Suppress)),
384 content: "",
385 },
386 span,
387 ))))
388 }
389}
390
391#[derive(Debug, PartialEq)]
392pub struct Cond<'a> {
393 pub ws: Ws,
394 pub cond: Option<WithSpan<CondTest<'a>>>,
395 pub nodes: Vec<Box<Node<'a>>>,
396}
397
398impl<'a: 'l, 'l> Cond<'a> {
399 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan<Self>> {
400 let alt_else = (ws(keyword("else").span()), opt(CondTest::parse));
401 let alt_elif = |i: &mut _| {
402 let mut p = (
403 ws(keyword("elif").span()),
404 cut_node(Some("if-elif"), CondTest::parse_cond),
405 );
406 let (span, cond) = p.parse_next(i)?;
407 Ok((span.clone(), Some(WithSpan::new(cond, span))))
408 };
409
410 let (_, pws, (span, cond), nws, _, nodes) = (
411 block_start,
412 opt(Whitespace::parse),
413 alt((alt_else, alt_elif)),
414 opt(Whitespace::parse),
415 cut_node(Some("if"), block_end),
416 cut_node(Some("if"), Node::many),
417 )
418 .parse_next(i)?;
419
420 Ok(WithSpan::new(
421 Self {
422 ws: Ws(pws, nws),
423 cond,
424 nodes,
425 },
426 span,
427 ))
428 }
429}
430
431#[derive(Debug, PartialEq, Clone)]
432pub struct CondTest<'a> {
433 pub target: Option<Target<'a>>,
434 pub expr: WithSpan<Box<Expr<'a>>>,
435 pub contains_bool_lit_or_is_defined: bool,
436}
437
438impl<'a: 'l, 'l> CondTest<'a> {
439 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, WithSpan<Self>> {
440 let mut p = (
441 ws(keyword("if").span()),
442 cut_node(Some("if"), Self::parse_cond),
443 );
444 let (span, cond) = p.parse_next(i)?;
445 Ok(WithSpan::new(cond, span))
446 }
447
448 fn parse_cond(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> {
449 let (target, expr) = (
450 opt(delimited(
451 ws(alt((keyword("let"), keyword("set")))),
452 ws(Target::parse),
453 ws('='),
454 )),
455 ws(|i: &mut InputStream<'a, 'l>| {
456 let start_checkpoint = i.checkpoint();
457 let start_offset = i.current_token_start();
458
459 let mut expr = Expr::parse(i, false)?;
460 if let Expr::BinOp(v) = &mut *expr.inner
461 && matches!(*v.rhs.inner, Expr::Var("set" | "let"))
462 {
463 let _level_guard = i.state.level.nest(i)?;
464
465 i.reset(&start_checkpoint);
466 i.next_slice(v.rhs.span.start - start_offset);
467
468 let (new_right, span) = Self::parse_cond.with_span().parse_next(i)?;
469 *v.rhs.inner = Expr::LetCond(WithSpan::new(new_right, span));
470 }
471 Ok(expr)
472 }),
473 )
474 .parse_next(i)?;
475 let contains_bool_lit_or_is_defined = expr.contains_bool_lit_or_is_defined();
476 Ok(Self {
477 target,
478 expr,
479 contains_bool_lit_or_is_defined,
480 })
481 }
482}
483
484#[derive(Clone, Copy, Default, PartialEq, Eq, Debug, Hash)]
485#[cfg_attr(feature = "config", derive(serde_derive::Deserialize))]
486#[cfg_attr(feature = "config", serde(field_identifier, rename_all = "lowercase"))]
487pub enum Whitespace {
488 #[default]
489 Preserve,
490 Suppress,
491 Minimize,
492}
493
494impl Whitespace {
495 fn parse<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Self> {
496 any.verify_map(Self::parse_char).parse_next(i)
497 }
498
499 fn parse_char(c: char) -> Option<Self> {
500 if c.is_ascii() {
501 Self::parse_byte(c as u8)
502 } else {
503 None
504 }
505 }
506
507 fn parse_byte(b: u8) -> Option<Whitespace> {
508 match b {
509 b'+' => Some(Self::Preserve),
510 b'-' => Some(Self::Suppress),
511 b'~' => Some(Self::Minimize),
512 _ => None,
513 }
514 }
515}
516
517impl FromStr for Whitespace {
518 type Err = String;
519
520 fn from_str(s: &str) -> Result<Self, Self::Err> {
521 match s {
522 "+" | "preserve" => Ok(Whitespace::Preserve),
523 "-" | "suppress" => Ok(Whitespace::Suppress),
524 "~" | "minimize" => Ok(Whitespace::Minimize),
525 s => Err(format!("invalid value for `whitespace`: {s:?}")),
526 }
527 }
528}
529
530fn check_block_start<'a: 'l, 'l>(
531 i: &mut InputStream<'a, 'l>,
532 start: Span,
533 node: &str,
534 expected: &str,
535) -> ParseResult<'a, ()> {
536 if i.is_empty() {
537 return cut_error!(
538 format!("expected `{expected}` to terminate `{node}` node, found nothing"),
539 start,
540 );
541 }
542 i.state.syntax.block_start.void().parse_next(i)
543}
544
545#[derive(Debug, PartialEq)]
546pub struct Loop<'a> {
547 pub ws1: Ws,
548 pub var: Target<'a>,
549 pub iter: WithSpan<Box<Expr<'a>>>,
550 pub cond: Option<WithSpan<Box<Expr<'a>>>>,
551 pub body: Vec<Box<Node<'a>>>,
552 pub ws2: Ws,
553 pub else_nodes: Vec<Box<Node<'a>>>,
554 pub ws3: Ws,
555}
556
557impl<'a: 'l, 'l> Loop<'a> {
558 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
559 fn content<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Vec<Box<Node<'a>>>> {
560 i.state.enter_loop();
561 let result = Node::many(i);
562 i.state.leave_loop();
563 result
564 }
565
566 let (pws1, span) = (opt(Whitespace::parse), ws(keyword("for").span())).parse_next(i)?;
567 let span = Span::new(span);
568
569 let if_cond = preceded(
570 ws(keyword("if")),
571 cut_node(Some("for-if"), ws(|i: &mut _| Expr::parse(i, true))),
572 );
573
574 let else_block = |i: &mut InputStream<'a, 'l>| {
575 let mut p = preceded(
576 ws(keyword("else")),
577 cut_node(
578 Some("for-else"),
579 (
580 opt(Whitespace::parse),
581 delimited(block_end, Node::many, block_start),
582 opt(Whitespace::parse),
583 ),
584 ),
585 );
586 let (pws, nodes, nws) = p.parse_next(i)?;
587 Ok((pws, nodes, nws))
588 };
589
590 let body_and_end = |i: &mut _| {
591 let (body, (_, pws, else_block, _, nws)) = cut_node(
592 Some("for"),
593 (
594 content,
595 cut_node(
596 Some("for"),
597 (
598 |i: &mut _| check_block_start(i, span, "for", "endfor"),
599 opt(Whitespace::parse),
600 opt(else_block),
601 end_node("for", "endfor"),
602 opt(Whitespace::parse),
603 ),
604 ),
605 ),
606 )
607 .parse_next(i)?;
608 Ok((body, pws, else_block, nws))
609 };
610
611 let mut p = cut_node(
612 Some("for"),
613 (
614 ws(Target::parse),
615 ws(keyword("in")),
616 cut_node(
617 Some("for"),
618 (
619 ws(|i: &mut _| Expr::parse(i, true)),
620 opt(if_cond),
621 opt(Whitespace::parse),
622 block_end,
623 body_and_end,
624 ),
625 ),
626 ),
627 );
628 let (var, _, (iter, cond, nws1, _, (body, pws2, else_block, nws2))) = p.parse_next(i)?;
629 let (nws3, else_nodes, pws3) = else_block.unwrap_or_default();
630 Ok(Box::new(Node::Loop(WithSpan::new(
631 Self {
632 ws1: Ws(pws1, nws1),
633 var,
634 iter,
635 cond,
636 body,
637 ws2: Ws(pws2, nws3),
638 else_nodes,
639 ws3: Ws(pws3, nws2),
640 },
641 span,
642 ))))
643 }
644}
645
646#[derive(Debug, PartialEq)]
647pub struct Macro<'a> {
648 pub ws1: Ws,
649 pub name: WithSpan<&'a str>,
650 pub args: Vec<MacroArg<'a>>,
651 pub nodes: Vec<Box<Node<'a>>>,
652 pub ws2: Ws,
653}
654
655#[derive(Debug, PartialEq)]
656pub struct MacroArg<'a> {
657 pub name: WithSpan<&'a str>,
658 pub ty: Option<WithSpan<TyGenerics<'a>>>,
659 pub default: Option<WithSpan<Box<Expr<'a>>>>,
660}
661
662fn check_duplicated_name<'a>(
663 names: &mut HashSet<&'a str>,
664 arg_name: &WithSpan<&'a str>,
665) -> Result<(), ParseErr<'a>> {
666 if !names.insert(arg_name.inner) {
667 return cut_error!(
668 format!("duplicated argument `{}`", arg_name.escape_debug()),
669 arg_name.span
670 );
671 }
672 Ok(())
673}
674
675impl<'a: 'l, 'l> Macro<'a> {
676 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
677 let (pws1, keyword_span, (name, name_span)) = (
678 opt(Whitespace::parse),
679 ws(keyword("macro").span()),
680 cut_node(Some("macro"), ws(identifier.with_span())),
681 )
682 .parse_next(i)?;
683 if is_rust_keyword(name) {
684 return cut_error!(
685 format!("`{}` is not a valid name for a macro", name.escape_debug()),
686 name_span,
687 );
688 }
689 let keyword_span = Span::new(keyword_span);
690
691 let macro_arg = |i: &mut _| {
692 let mut p = (
693 ws(identifier.with_span()),
694 opt(preceded(':', ws(|i: &mut _| TyGenerics::parse(i)))),
695 opt(preceded('=', ws(|i: &mut _| Expr::parse(i, false)))),
696 );
697 let ((name, name_span), ty, default) = p.parse_next(i)?;
698 Ok(MacroArg {
699 name: WithSpan::new(name, name_span),
700 ty,
701 default,
702 })
703 };
704 let mut args = opt((
705 '('.span(),
706 opt(terminated(separated(1.., macro_arg, ','), opt(','))),
707 ws(opt(')')),
708 ));
709 let parameters = |i: &mut _| match args.parse_next(i)? {
710 Some((_, args, Some(_))) => Ok(args),
711 Some((span, _, None)) => {
712 cut_error!("expected `)` to close macro argument list", span)
713 }
714 None => Ok(None::<Vec<MacroArg<'_>>>),
715 };
716
717 let (params, nws1, _) = cut_node(
718 Some("macro"),
719 (parameters, opt(Whitespace::parse), block_end),
720 )
721 .parse_next(i)?;
722
723 if let Some(ref params) = params {
724 let mut names = HashSet::default();
725 let mut iter = params.iter();
726 for arg in iter.by_ref() {
727 check_duplicated_name(&mut names, &arg.name)?;
728 if arg.default.is_none() {
729 continue;
730 }
731
732 for new_arg in iter.by_ref() {
733 check_duplicated_name(&mut names, &new_arg.name)?;
734 if new_arg.default.is_some() {
735 continue;
736 }
737
738 return cut_error!(
739 format!(
740 "all arguments following `{}` should have a default value, \
741 `{}` doesn't have a default value",
742 arg.name.escape_debug(),
743 new_arg.name.escape_debug(),
744 ),
745 new_arg.name.span,
746 );
747 }
748 break;
749 }
750 }
751
752 let mut end = cut_node(
753 Some("macro"),
754 (
755 Node::many,
756 cut_node(
757 Some("macro"),
758 (
759 |i: &mut _| check_block_start(i, keyword_span, "macro", "endmacro"),
760 opt(Whitespace::parse),
761 end_node("macro", "endmacro"),
762 check_end_name(name, "macro"),
763 ),
764 ),
765 ),
766 );
767 let (contents, (_, pws2, _, nws2)) = end.parse_next(i)?;
768
769 Ok(Box::new(Node::Macro(WithSpan::new(
770 Self {
771 ws1: Ws(pws1, nws1),
772 name: WithSpan::new(name, name_span),
773 args: params.unwrap_or_default(),
774 nodes: contents,
775 ws2: Ws(pws2, nws2),
776 },
777 keyword_span,
778 ))))
779 }
780}
781
782#[derive(Debug, PartialEq)]
783pub struct FilterBlock<'a> {
784 pub ws1: Ws,
785 pub filters: Filter<'a>,
786 pub nodes: Vec<Box<Node<'a>>>,
787 pub ws2: Ws,
788}
789
790impl<'a: 'l, 'l> FilterBlock<'a> {
791 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
792 let (pws1, span) = (opt(Whitespace::parse), ws(keyword("filter").span())).parse_next(i)?;
793 let span = Span::new(span);
794
795 fn filters<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Filter<'a>> {
796 let mut filter = opt(ws(filter.with_span()));
797
798 let (mut res, span) = Filter::parse.with_span().parse_next(i)?;
799 res.arguments
800 .insert(0, WithSpan::new(Box::new(Expr::FilterSource), span));
801
802 let mut level_guard = i.state.level.guard();
803 let mut i_before = *i;
804 while let Some((mut filter, span)) = filter.parse_next(i)? {
805 level_guard.nest(&i_before)?;
806 filter
807 .arguments
808 .insert(0, WithSpan::new(Box::new(Expr::Filter(res)), span));
809 res = filter;
810 i_before = *i;
811 }
812 Ok(res)
813 }
814
815 let mut p = (
816 cut_node(
817 Some("filter"),
818 (ws(filters), opt(Whitespace::parse), block_end),
819 ),
820 cut_node(Some("filter"), Node::many),
821 cut_node(
822 Some("filter"),
823 (
824 |i: &mut _| check_block_start(i, span, "filter", "endfilter"),
825 opt(Whitespace::parse),
826 end_node("filter", "endfilter"),
827 opt(Whitespace::parse),
828 ),
829 ),
830 );
831 let ((filters, nws1, _), nodes, (_, pws2, _, nws2)) = p.parse_next(i)?;
832
833 Ok(Box::new(Node::FilterBlock(WithSpan::new(
834 Self {
835 ws1: Ws(pws1, nws1),
836 filters,
837 nodes,
838 ws2: Ws(pws2, nws2),
839 },
840 span,
841 ))))
842 }
843}
844
845#[derive(Debug, PartialEq)]
846pub struct Import<'a> {
847 pub ws: Ws,
848 pub path: &'a str,
849 pub scope: &'a str,
850}
851
852impl<'a: 'l, 'l> Import<'a> {
853 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
854 let mut p = (
855 opt(Whitespace::parse),
856 ws(keyword("import").span()),
857 cut_node(
858 Some("import"),
859 (
860 ws(str_lit_without_prefix),
861 ws(keyword("as")),
862 cut_node(Some("import"), (ws(identifier), opt(Whitespace::parse))),
863 ),
864 ),
865 );
866 let (pws, span, (path, _, (scope, nws))) = p.parse_next(i)?;
867 Ok(Box::new(Node::Import(WithSpan::new(
868 Self {
869 ws: Ws(pws, nws),
870 path,
871 scope,
872 },
873 span,
874 ))))
875 }
876}
877
878#[derive(Debug, PartialEq)]
879pub struct Call<'a> {
880 pub ws1: Ws,
881 pub caller_args: Vec<&'a str>,
882 pub scope: Option<WithSpan<&'a str>>,
883 pub name: WithSpan<&'a str>,
884 pub args: Option<Vec<WithSpan<Box<Expr<'a>>>>>,
885 pub nodes: Vec<Box<Node<'a>>>,
886 pub ws2: Ws,
887}
888
889impl<'a: 'l, 'l> Call<'a> {
890 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
891 let (pws, span) = (opt(Whitespace::parse), ws(keyword("call").span())).parse_next(i)?;
892 let keyword_span = Span::new(span);
893
894 let parameters = |i: &mut _| -> ParseResult<'_, Option<Vec<&str>>> {
895 let mut p = opt((
896 '('.span(),
897 opt(terminated(separated(0.., ws(identifier), ','), opt(','))),
898 ws(opt(')')),
899 ));
900 match p.parse_next(i)? {
901 Some((_, args, Some(_))) => Ok(args),
902 Some((span, _, None)) => {
903 cut_error!("expected `)` to close call argument list", span)
904 }
905 None => Ok(None),
906 }
907 };
908 let mut p = (
909 parameters,
910 cut_node(
911 Some("call"),
912 (
913 opt(|i: &mut _| {
914 let (scope, span) =
915 terminated(ws(identifier.with_span()), ws("::")).parse_next(i)?;
916 Ok(WithSpan::new(scope, span))
917 }),
918 ws(identifier.with_span()),
919 opt(ws(Expr::arguments)),
920 opt(Whitespace::parse),
921 block_end,
922 ),
923 ),
924 );
925
926 let (call_args, (scope, (name, name_span), args, nws, _)) = p.parse_next(i)?;
927 let mut end = cut_node(
928 Some("call"),
929 (
930 Node::many,
931 cut_node(
932 Some("call"),
933 (
934 |i: &mut _| check_block_start(i, keyword_span, "call", "endcall"),
935 opt(Whitespace::parse),
936 end_node("call", "endcall"),
937 opt(Whitespace::parse),
938 ),
939 ),
940 ),
941 );
942 let (nodes, (_, pws2, _, nws2)) = end.parse_next(i)?;
943
944 Ok(Box::new(Node::Call(WithSpan::new(
945 Self {
946 ws1: Ws(pws, nws),
947 caller_args: call_args.unwrap_or_default(),
948 scope,
949 name: WithSpan::new(name, name_span),
950 args: args.map(|args| args.deconstruct().0),
951 nodes,
952 ws2: Ws(pws2, nws2),
953 },
954 keyword_span,
955 ))))
956 }
957}
958
959#[derive(Debug, PartialEq)]
960pub struct Match<'a> {
961 pub ws1: Ws,
962 pub expr: WithSpan<Box<Expr<'a>>>,
963 pub arms: Vec<WithSpan<When<'a>>>,
964 pub ws2: Ws,
965}
966
967impl<'a: 'l, 'l> Match<'a> {
968 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
969 let (pws1, span) = (opt(Whitespace::parse), ws(keyword("match").span())).parse_next(i)?;
970 let span = Span::new(span);
971 let mut p = cut_node(
972 Some("match"),
973 (
974 ws(|i: &mut _| Expr::parse(i, false)),
975 opt(Whitespace::parse),
976 block_end,
977 cut_node(
978 Some("match"),
979 (
980 ws(repeat(0.., ws(Comment::parse))).map(|()| ()),
981 repeat(0.., When::when).map(|v: Vec<_>| v),
982 ),
983 ),
984 cut_node(Some("match"), opt(When::r#else)),
985 cut_node(
986 Some("match"),
987 (
988 ws(|i: &mut _| check_block_start(i, span, "match", "endmatch")),
989 opt(Whitespace::parse),
990 end_node("match", "endmatch"),
991 opt(Whitespace::parse),
992 ),
993 ),
994 ),
995 );
996 let (expr, nws1, _, (_, mut arms), else_arm, (_, pws2, _, nws2)) = p.parse_next(i)?;
997
998 if let Some(arm) = else_arm {
999 arms.push(arm);
1000 }
1001 if arms.is_empty() {
1002 return cut_error!(
1003 "`match` nodes must contain at least one `when` node and/or an `else` case",
1004 span,
1005 );
1006 }
1007
1008 Ok(Box::new(Node::Match(WithSpan::new(
1009 Self {
1010 ws1: Ws(pws1, nws1),
1011 expr,
1012 arms,
1013 ws2: Ws(pws2, nws2),
1014 },
1015 span,
1016 ))))
1017 }
1018}
1019
1020#[derive(Debug, PartialEq)]
1021pub struct BlockDef<'a> {
1022 pub ws1: Ws,
1023 pub name: WithSpan<&'a str>,
1024 pub nodes: Vec<Box<Node<'a>>>,
1025 pub ws2: Ws,
1026}
1027
1028impl<'a: 'l, 'l> BlockDef<'a> {
1029 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1030 let mut start = (
1031 opt(Whitespace::parse),
1032 ws(keyword("block").span()),
1033 cut_node(
1034 Some("block"),
1035 (
1036 ws(identifier.with_span()),
1037 opt(Whitespace::parse),
1038 block_end,
1039 ),
1040 ),
1041 );
1042 let (pws1, keyword_span, ((name, name_span), nws1, _)) = start.parse_next(i)?;
1043 let keyword_span = Span::new(keyword_span);
1044
1045 let mut end = cut_node(
1046 Some("block"),
1047 (
1048 Node::many,
1049 cut_node(
1050 Some("block"),
1051 (
1052 |i: &mut _| check_block_start(i, keyword_span, "block", "endblock"),
1053 opt(Whitespace::parse),
1054 end_node("block", "endblock"),
1055 check_end_name(name, "block"),
1056 ),
1057 ),
1058 ),
1059 );
1060 let (nodes, (_, pws2, _, nws2)) = end.parse_next(i)?;
1061
1062 Ok(Box::new(Node::BlockDef(WithSpan::new(
1063 BlockDef {
1064 ws1: Ws(pws1, nws1),
1065 name: WithSpan::new(name, name_span),
1066 nodes,
1067 ws2: Ws(pws2, nws2),
1068 },
1069 keyword_span,
1070 ))))
1071 }
1072}
1073
1074fn check_end_name<'a: 'l, 'l>(
1075 name: &'a str,
1076 kind: &'static str,
1077) -> impl ModalParser<InputStream<'a, 'l>, Option<Whitespace>, ErrorContext> {
1078 let name = move |i: &mut InputStream<'a, 'l>| {
1079 let Some((end_name, span)) = ws(opt(identifier.with_span())).parse_next(i)? else {
1080 return Ok(());
1081 };
1082 if name == end_name {
1083 return Ok(());
1084 }
1085
1086 cut_error!(
1087 if name.is_empty() && !end_name.is_empty() {
1088 format!("unexpected name `{end_name}` in `end{kind}` tag for unnamed `{kind}`")
1089 } else {
1090 format!("expected name `{name}` in `end{kind}` tag, found `{end_name}`")
1091 },
1092 span,
1093 )
1094 };
1095 cut_node(Some(kind), preceded(name, opt(Whitespace::parse)))
1096}
1097
1098#[derive(Debug, PartialEq)]
1099pub struct Lit<'a> {
1100 pub lws: WithSpan<&'a str>,
1101 pub val: WithSpan<&'a str>,
1102 pub rws: WithSpan<&'a str>,
1103}
1104
1105impl<'a: 'l, 'l> Lit<'a> {
1106 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1107 let content = take_until(
1108 ..,
1109 (
1110 i.state.syntax.block_start,
1111 i.state.syntax.comment_start,
1112 i.state.syntax.expr_start,
1113 ),
1114 );
1115 let (content, span) = preceded(not(eof), alt((content, rest)))
1116 .verify(|s: &str| !s.is_empty())
1117 .with_span()
1118 .parse_next(i)?;
1119 Ok(Box::new(Node::Lit(Self::split_ws_parts(WithSpan::new(
1120 content, span,
1121 )))))
1122 }
1123
1124 pub(crate) fn split_ws_parts(s: WithSpan<&'a str>) -> WithSpan<Self> {
1125 let content = if let Some(mid) = s.find(|c: char| !c.is_ascii_whitespace()) {
1126 let (lws, val) = s.split_at(mid);
1127 let mid = val.trim_ascii_end().len();
1128 if val.len() != mid {
1129 let (val, rws) = val.split_at(mid);
1130 Self { lws, val, rws }
1131 } else {
1132 Self {
1133 lws,
1134 val,
1135 rws: val.end(),
1136 }
1137 }
1138 } else {
1139 let end = s.end();
1140 Self {
1141 lws: s,
1142 val: end,
1143 rws: end,
1144 }
1145 };
1146 WithSpan::new(content, s.span)
1147 }
1148}
1149
1150#[derive(Debug, PartialEq)]
1151pub struct Raw<'a> {
1152 pub ws1: Ws,
1153 pub lit: WithSpan<Lit<'a>>,
1154 pub ws2: Ws,
1155}
1156
1157impl<'a: 'l, 'l> Raw<'a> {
1158 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1159 fn endraw<'a: 'l, 'l>(
1160 i: &mut InputStream<'a, 'l>,
1161 ) -> ParseResult<'a, (Ws, WithSpan<Lit<'a>>)> {
1162 let start_i = ***i;
1163 let start_idx = i.current_token_start();
1164 loop {
1165 let span = terminated(take_until(.., "endraw").span(), "endraw").parse_next(i)?;
1170 let inner = start_i[..span.end - start_idx].trim_ascii_end();
1171
1172 let mut inner_chars = inner.chars();
1173 let (inner, pws) = if let Some(c) = inner_chars.next_back()
1174 && let Some(pws) = Whitespace::parse_char(c)
1175 {
1176 (inner_chars.as_str(), Some(pws))
1177 } else {
1178 (inner, None)
1179 };
1180
1181 let Some(inner) = inner.strip_suffix(i.state.syntax.block_start) else {
1182 continue;
1183 };
1184 let span = start_idx..start_idx + inner.len();
1185
1186 skip_ws0(i)?;
1188 let i_before_nws = i.checkpoint();
1189 let nws = opt(Whitespace::parse).parse_next(i)?;
1190 if opt(peek(block_end)).parse_next(i)?.is_none() {
1191 i.reset(&i_before_nws); continue;
1193 }
1194
1195 return Ok((
1196 Ws(pws, nws),
1197 Lit::split_ws_parts(WithSpan::new(inner, span)),
1198 ));
1199 }
1200 }
1201
1202 let mut p = (
1203 opt(Whitespace::parse),
1204 ws(keyword("raw").span()),
1205 cut_node(
1206 Some("raw"),
1207 separated_pair(opt(Whitespace::parse), block_end, endraw),
1208 ),
1209 );
1210 let (pws, span, (nws, (ws2, lit))) = p.parse_next(i)?;
1211 let ws1 = Ws(pws, nws);
1212 Ok(Box::new(Node::Raw(WithSpan::new(
1213 Self { ws1, lit, ws2 },
1214 span,
1215 ))))
1216 }
1217}
1218
1219#[derive(Debug, PartialEq)]
1220pub struct Declare<'a> {
1221 pub ws: Ws,
1222 pub var_name: WithSpan<&'a str>,
1223 pub is_mutable: bool,
1224}
1225
1226impl<'a: 'l, 'l> Declare<'a> {
1227 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1228 let mut p = (
1229 opt(Whitespace::parse),
1230 ws(alt((keyword("decl"), keyword("declare"))).span()),
1231 ws(opt(keyword("mut").span())),
1232 ws(identifier.with_span()),
1233 opt(Whitespace::parse),
1234 );
1235 let (pws, span, is_mut, (var_name, var_name_span), nws) = p.parse_next(i)?;
1236
1237 Ok(Box::new(Node::Declare(WithSpan::new(
1238 Declare {
1239 ws: Ws(pws, nws),
1240 var_name: WithSpan::new(var_name, var_name_span),
1241 is_mutable: is_mut.is_some(),
1242 },
1243 span,
1244 ))))
1245 }
1246}
1247
1248#[derive(Debug, PartialEq)]
1249pub enum LetValueOrBlock<'a> {
1250 Value(WithSpan<Box<Expr<'a>>>),
1251 Block { nodes: Vec<Box<Node<'a>>>, ws: Ws },
1252}
1253
1254#[derive(Debug, PartialEq)]
1255pub struct Let<'a> {
1256 pub ws: Ws,
1257 pub var: Target<'a>,
1258 pub val: LetValueOrBlock<'a>,
1259 pub is_mutable: bool,
1260}
1261
1262impl<'a: 'l, 'l> Let<'a> {
1263 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1264 let (pws, (tag, span), is_mut) = (
1265 opt(Whitespace::parse),
1266 ws(alt((keyword("let"), keyword("set"))).with_span()),
1267 ws(opt(keyword("mut").span())),
1268 )
1269 .parse_next(i)?;
1270
1271 let no_compound = |i: &mut _| {
1272 if let Some((op, span)) = opt(compound_assignment_op.with_span()).parse_next(i)? {
1273 cut_error!(
1274 format!(
1275 "the compound assignment `{op}` cannot be used with {s} {tag} {e}`, \
1276 try `{s} mut {e}` instead",
1277 s = i.state.syntax.block_start.escape_debug(),
1278 e = i.state.syntax.block_end.escape_debug(),
1279 ),
1280 span,
1281 )
1282 } else {
1283 Ok(None)
1284 }
1285 };
1286
1287 let ((var, var_span), val, nws) = cut_node(
1288 Some("let"),
1289 (
1290 ws(Target::parse.with_span()),
1291 alt((
1292 preceded(
1293 ws('='),
1294 cut_node(Some("let"), ws(|i: &mut _| Expr::parse(i, false).map(Some))),
1295 ),
1296 no_compound,
1297 )),
1298 opt(Whitespace::parse),
1299 ),
1300 )
1301 .parse_next(i)?;
1302
1303 if val.is_none()
1304 && let Some(kind) = match &var {
1305 Target::Name(_) => None,
1306 Target::Tuple(..) => Some("a tuple"),
1307 Target::Array(..) => Some("an array"),
1308 Target::Struct(..) => Some("a struct"),
1309 Target::NumLit(..)
1310 | Target::StrLit(..)
1311 | Target::CharLit(..)
1312 | Target::BoolLit(..) => Some("a literal"),
1313 Target::Path(..) => Some("a path or enum variant"),
1314 Target::OrChain(..) | Target::Placeholder(..) | Target::Rest(..) => {
1315 Some("a pattern")
1316 }
1317 }
1318 {
1319 return cut_error!(
1320 format!(
1321 "when you forward-define a variable, you cannot use {kind} in place of \
1322 a variable name"
1323 ),
1324 var_span,
1325 );
1326 }
1327
1328 if let Some(mut_span) = &is_mut
1329 && !matches!(var, Target::Name(_))
1330 {
1331 return cut_error!(
1332 "you can only use the `mut` keyword with a variable name",
1333 mut_span.clone(),
1334 );
1335 }
1336
1337 if let Some(val) = val {
1338 return Ok(Box::new(Node::Let(WithSpan::new(
1339 Let {
1340 ws: Ws(pws, nws),
1341 var,
1342 val: LetValueOrBlock::Value(val),
1343 is_mutable: is_mut.is_some(),
1344 },
1345 span,
1346 ))));
1347 }
1348
1349 if block_end.parse_next(i).is_err() {
1351 return Err(
1352 ErrorContext::unclosed("block", i.state.syntax.block_end, Span::new(span)).cut(),
1353 );
1354 }
1355
1356 let (keyword, end_keyword) = if tag == "let" {
1357 ("let", "endlet")
1358 } else {
1359 ("set", "endset")
1360 };
1361
1362 let keyword_span = Span::new(span.clone());
1363 let mut end = cut_node(
1364 Some(keyword),
1365 (
1366 Node::many,
1367 cut_node(
1368 Some(keyword),
1369 (
1370 |i: &mut _| check_block_start(i, keyword_span, keyword, end_keyword),
1371 opt(Whitespace::parse),
1372 end_node(keyword, end_keyword),
1373 opt(Whitespace::parse),
1374 ),
1375 ),
1376 ),
1377 );
1378 let (nodes, (_, pws2, _, nws2)) = end.parse_next(i)?;
1379
1380 Ok(Box::new(Node::Let(WithSpan::new(
1381 Let {
1382 ws: Ws(pws, nws),
1383 var,
1384 val: LetValueOrBlock::Block {
1385 nodes,
1386 ws: Ws(pws2, nws2),
1387 },
1388 is_mutable: is_mut.is_some(),
1389 },
1390 span,
1391 ))))
1392 }
1393}
1394
1395#[derive(Debug, PartialEq)]
1396pub struct Compound<'a> {
1397 pub op: WithSpan<BinOp<'a>>,
1398 pub ws: Ws,
1399}
1400
1401impl<'a: 'l, 'l> Compound<'a> {
1402 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1403 let (pws, span, (lhs, rhs, nws)) = (
1404 opt(Whitespace::parse),
1405 ws(keyword("mut").span()),
1406 cut_node(
1407 Some("mut"),
1408 (
1409 |i: &mut _| Expr::parse(i, false),
1410 opt((
1411 ws(alt(("=", compound_assignment_op))),
1412 cut_node(Some("mut"), ws(|i: &mut _| Expr::parse(i, false))),
1413 )),
1414 opt(Whitespace::parse),
1415 ),
1416 ),
1417 )
1418 .parse_next(i)?;
1419
1420 let Some((op, rhs)) = rhs else {
1421 return cut_error!(
1422 format!(
1423 "`{s} mut {e}` expects a (compound) assignment, did you mean to \
1424 forward declare a mutable variable with `{s} let mut {e}`?",
1425 s = i.state.syntax.block_start.escape_debug(),
1426 e = i.state.syntax.block_end.escape_debug(),
1427 ),
1428 span,
1429 );
1430 };
1431
1432 Ok(Box::new(Node::Compound(WithSpan::new(
1433 Compound {
1434 ws: Ws(pws, nws),
1435 op: WithSpan::new(BinOp { op, lhs, rhs }, span.clone()),
1436 },
1437 span,
1438 ))))
1439 }
1440}
1441
1442fn compound_assignment_op<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a> {
1444 const TWO: &[[u8; 2]] = &[
1445 *b"+=", *b"-=", *b"*=", *b"/=", *b"%=", *b"&=", *b"|=", *b"^=",
1446 ];
1447
1448 alt((
1449 take(2usize).verify(|s: &str| {
1450 if let Ok(s) = s.as_bytes().try_into() {
1451 TWO.contains(&s)
1452 } else {
1453 false
1454 }
1455 }),
1456 take(3usize).verify(|s| matches!(s, "<<=" | ">>=")),
1457 ))
1458 .parse_next(i)
1459}
1460
1461#[derive(Debug, PartialEq)]
1462pub struct If<'a> {
1463 pub ws: Ws,
1464 pub branches: Vec<WithSpan<Cond<'a>>>,
1465}
1466
1467impl<'a: 'l, 'l> If<'a> {
1468 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1469 let (pws1, cond) = (opt(Whitespace::parse), CondTest::parse).parse_next(i)?;
1470 let cond_span = cond.span;
1471 let end_if = cut_node(
1472 Some("if"),
1473 (
1474 |i: &mut _| check_block_start(i, cond_span, "if", "endif"),
1475 opt(Whitespace::parse),
1476 end_node("if", "endif"),
1477 opt(Whitespace::parse),
1478 ),
1479 );
1480 let mut p = cut_node(
1481 Some("if"),
1482 (
1483 opt(Whitespace::parse),
1484 block_end,
1485 cut_node(
1486 Some("if"),
1487 (
1488 Node::many,
1489 repeat(0.., Cond::parse).map(|v: Vec<_>| v),
1490 end_if,
1491 ),
1492 ),
1493 ),
1494 );
1495
1496 let (nws1, _, (nodes, elifs, (_, pws2, _, nws2))) = p.parse_next(i)?;
1497 let mut branches = vec![WithSpan::new(
1498 Cond {
1499 ws: Ws(pws1, nws1),
1500 cond: Some(cond),
1501 nodes,
1502 },
1503 cond_span,
1504 )];
1505 branches.extend(elifs);
1506
1507 Ok(Box::new(Node::If(WithSpan::new(
1508 Self {
1509 ws: Ws(pws2, nws2),
1510 branches,
1511 },
1512 cond_span,
1513 ))))
1514 }
1515}
1516
1517#[derive(Debug, PartialEq)]
1518pub struct Include<'a> {
1519 pub ws: Ws,
1520 pub path: &'a str,
1521}
1522
1523impl<'a: 'l, 'l> Include<'a> {
1524 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1525 let p = (
1526 opt(Whitespace::parse),
1527 ws(keyword("include")),
1528 cut_node(
1529 Some("include"),
1530 (ws(str_lit_without_prefix), opt(Whitespace::parse)),
1531 ),
1532 );
1533 let ((pws, _, (path, nws)), span) = p.with_span().parse_next(i)?;
1534 Ok(Box::new(Node::Include(WithSpan::new(
1535 Self {
1536 ws: Ws(pws, nws),
1537 path,
1538 },
1539 span,
1540 ))))
1541 }
1542}
1543
1544#[derive(Debug, PartialEq)]
1545pub struct Extends<'a> {
1546 pub path: &'a str,
1547}
1548
1549impl<'a: 'l, 'l> Extends<'a> {
1550 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1551 let p = preceded(
1552 (opt(Whitespace::parse), ws(keyword("extends"))),
1553 cut_node(
1554 Some("extends"),
1555 terminated(ws(str_lit_without_prefix), opt(Whitespace::parse)),
1556 ),
1557 );
1558 let (path, span) = p.with_span().parse_next(i)?;
1559 Ok(Box::new(Node::Extends(WithSpan::new(Self { path }, span))))
1560 }
1561}
1562
1563#[derive(Debug, PartialEq)]
1564pub struct Comment<'a> {
1565 pub ws: Ws,
1566 pub content: &'a str,
1567}
1568
1569impl<'a: 'l, 'l> Comment<'a> {
1570 fn parse(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, Box<Node<'a>>> {
1571 fn content<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a, ()> {
1572 let mut depth = 0usize;
1573 loop {
1574 take_until(
1575 ..,
1576 (i.state.syntax.comment_start, i.state.syntax.comment_end),
1577 )
1578 .parse_next(i)?;
1579 let is_open = opt(i.state.syntax.comment_start).parse_next(i)?.is_some();
1580 if is_open {
1581 depth += 1;
1583 } else if let Some(new_depth) = depth.checked_sub(1) {
1584 literal(i.state.syntax.comment_end).parse_next(i)?;
1585 depth = new_depth;
1586 } else {
1587 return Ok(());
1588 }
1589 }
1590 }
1591
1592 fn comment<'a: 'l, 'l>(i: &mut InputStream<'a, 'l>) -> ParseResult<'a> {
1593 let start = i.state.syntax.comment_start.span().parse_next(i)?;
1594 let mut content = opt(terminated(content.take(), i.state.syntax.comment_end));
1595 let Some(content) = content.parse_next(i)? else {
1596 return Err(
1597 ErrorContext::unclosed("comment", i.state.syntax.comment_end, start).cut(),
1598 );
1599 };
1600 Ok(content)
1601 }
1602
1603 let (content, span) = comment.with_span().parse_next(i)?;
1604 let ws = match *content.as_bytes() {
1605 [b'-' | b'+' | b'~'] => {
1606 return cut_error!(
1607 format!(
1608 "ambiguous whitespace stripping\n\
1609 use `{}{content} {content}{}` to apply the same whitespace stripping on \
1610 both sides",
1611 i.state.syntax.comment_start, i.state.syntax.comment_end,
1612 ),
1613 span,
1614 );
1615 }
1616 [pws, .., nws] => Ws(Whitespace::parse_byte(pws), Whitespace::parse_byte(nws)),
1617 _ => Ws(None, None),
1618 };
1619 Ok(Box::new(Node::Comment(WithSpan::new(
1620 Self { ws, content },
1621 span,
1622 ))))
1623 }
1624}
1625
1626#[derive(Clone, Copy, Debug, PartialEq)]
1630pub struct Ws(pub Option<Whitespace>, pub Option<Whitespace>);
1631
1632fn end_node<'a: 'l, 'g: 'a, 'l>(
1633 node: &'g str,
1634 expected: &'g str,
1635) -> impl ModalParser<InputStream<'a, 'l>, &'a str, ErrorContext> + 'g {
1636 move |i: &mut InputStream<'a, 'l>| {
1637 let start = i.checkpoint();
1638 let (actual, span) = ws(identifier.with_span()).parse_next(i)?;
1639 if actual == expected {
1640 Ok(actual)
1641 } else if actual.starts_with("end") {
1642 i.reset(&start);
1643 cut_error!(
1644 format!("expected `{expected}` to terminate `{node}` node, found `{actual}`"),
1645 span,
1646 )
1647 } else {
1648 i.reset(&start);
1649 fail.parse_next(i)
1650 }
1651 }
1652}