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