askama_parser/
node.rs

1use std::borrow::Cow;
2use std::str;
3
4use nom::branch::alt;
5use nom::bytes::complete::{tag, take_until};
6use nom::character::complete::char;
7use nom::combinator::{
8    complete, consumed, cut, eof, map, map_res, not, opt, peek, recognize, value,
9};
10use nom::error::{Error, ErrorKind};
11use nom::error_position;
12use nom::multi::{fold_many0, many0, many1, separated_list0, separated_list1};
13use nom::sequence::{delimited, pair, preceded, terminated, tuple};
14
15use crate::{ErrorContext, ParseResult};
16
17use super::{
18    bool_lit, char_lit, identifier, is_ws, keyword, num_lit, path_or_identifier, skip_till,
19    str_lit, ws, Expr, PathOrIdentifier, State,
20};
21
22#[derive(Debug, PartialEq)]
23pub enum Node<'a> {
24    Lit(Lit<'a>),
25    Comment(Comment<'a>),
26    Expr(Ws, Expr<'a>),
27    Call(Call<'a>),
28    Let(Let<'a>),
29    If(If<'a>),
30    Match(Match<'a>),
31    Loop(Box<Loop<'a>>),
32    Extends(Extends<'a>),
33    BlockDef(BlockDef<'a>),
34    Include(Include<'a>),
35    Import(Import<'a>),
36    Macro(Macro<'a>),
37    Raw(Raw<'a>),
38    Break(Ws),
39    Continue(Ws),
40}
41
42impl<'a> Node<'a> {
43    pub(super) fn many(i: &'a str, s: &State<'_>) -> ParseResult<'a, Vec<Self>> {
44        complete(many0(alt((
45            map(|i| Lit::parse(i, s), Self::Lit),
46            map(|i| Comment::parse(i, s), Self::Comment),
47            |i| Self::expr(i, s),
48            |i| Self::parse(i, s),
49        ))))(i)
50    }
51
52    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
53        let mut p = delimited(
54            |i| s.tag_block_start(i),
55            alt((
56                map(|i| Call::parse(i, s), Self::Call),
57                map(|i| Let::parse(i, s), Self::Let),
58                map(|i| If::parse(i, s), Self::If),
59                map(|i| Loop::parse(i, s), |l| Self::Loop(Box::new(l))),
60                map(|i| Match::parse(i, s), Self::Match),
61                map(Extends::parse, Self::Extends),
62                map(Include::parse, Self::Include),
63                map(Import::parse, Self::Import),
64                map(|i| BlockDef::parse(i, s), Self::BlockDef),
65                map(|i| Macro::parse(i, s), Self::Macro),
66                map(|i| Raw::parse(i, s), Self::Raw),
67                |i| Self::r#break(i, s),
68                |i| Self::r#continue(i, s),
69            )),
70            cut(|i| s.tag_block_end(i)),
71        );
72
73        s.nest(i)?;
74        let result = p(i);
75        s.leave();
76
77        result
78    }
79
80    fn r#break(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
81        let mut p = tuple((
82            opt(Whitespace::parse),
83            ws(keyword("break")),
84            opt(Whitespace::parse),
85        ));
86        let (j, (pws, _, nws)) = p(i)?;
87        if !s.is_in_loop() {
88            return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag)));
89        }
90        Ok((j, Self::Break(Ws(pws, nws))))
91    }
92
93    fn r#continue(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
94        let mut p = tuple((
95            opt(Whitespace::parse),
96            ws(keyword("continue")),
97            opt(Whitespace::parse),
98        ));
99        let (j, (pws, _, nws)) = p(i)?;
100        if !s.is_in_loop() {
101            return Err(nom::Err::Failure(error_position!(i, ErrorKind::Tag)));
102        }
103        Ok((j, Self::Continue(Ws(pws, nws))))
104    }
105
106    fn expr(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
107        let mut p = tuple((
108            |i| s.tag_expr_start(i),
109            cut(tuple((
110                opt(Whitespace::parse),
111                ws(|i| Expr::parse(i, s.level.get())),
112                opt(Whitespace::parse),
113                |i| s.tag_expr_end(i),
114            ))),
115        ));
116        let (i, (_, (pws, expr, nws, _))) = p(i)?;
117        Ok((i, Self::Expr(Ws(pws, nws), expr)))
118    }
119}
120
121#[derive(Clone, Debug, PartialEq)]
122pub enum Target<'a> {
123    Name(&'a str),
124    Tuple(Vec<&'a str>, Vec<Target<'a>>),
125    Struct(Vec<&'a str>, Vec<(&'a str, Target<'a>)>),
126    NumLit(&'a str),
127    StrLit(&'a str),
128    CharLit(&'a str),
129    BoolLit(&'a str),
130    Path(Vec<&'a str>),
131    OrChain(Vec<Target<'a>>),
132}
133
134impl<'a> Target<'a> {
135    /// Parses multiple targets with `or` separating them
136    pub(super) fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
137        map(
138            separated_list1(ws(tag("or")), |i| {
139                s.nest(i)?;
140                let ret = Self::parse_one(i, s)?;
141                s.leave();
142                Ok(ret)
143            }),
144            |mut opts| match opts.len() {
145                1 => opts.pop().unwrap(),
146                _ => Self::OrChain(opts),
147            },
148        )(i)
149    }
150
151    /// Parses a single target without an `or`, unless it is wrapped in parentheses.
152    fn parse_one(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
153        let mut opt_opening_paren = map(opt(ws(char('('))), |o| o.is_some());
154        let mut opt_closing_paren = map(opt(ws(char(')'))), |o| o.is_some());
155        let mut opt_opening_brace = map(opt(ws(char('{'))), |o| o.is_some());
156
157        let (i, lit) = opt(Self::lit)(i)?;
158        if let Some(lit) = lit {
159            return Ok((i, lit));
160        }
161
162        // match tuples and unused parentheses
163        let (i, target_is_tuple) = opt_opening_paren(i)?;
164        if target_is_tuple {
165            let (i, is_empty_tuple) = opt_closing_paren(i)?;
166            if is_empty_tuple {
167                return Ok((i, Self::Tuple(Vec::new(), Vec::new())));
168            }
169
170            let (i, first_target) = Self::parse(i, s)?;
171            let (i, is_unused_paren) = opt_closing_paren(i)?;
172            if is_unused_paren {
173                return Ok((i, first_target));
174            }
175
176            let mut targets = vec![first_target];
177            let (i, _) = cut(tuple((
178                fold_many0(
179                    preceded(ws(char(',')), |i| Self::parse(i, s)),
180                    || (),
181                    |_, target| {
182                        targets.push(target);
183                    },
184                ),
185                opt(ws(char(','))),
186                ws(cut(char(')'))),
187            )))(i)?;
188            return Ok((i, Self::Tuple(Vec::new(), targets)));
189        }
190
191        let path = |i| {
192            map_res(path_or_identifier, |v| match v {
193                PathOrIdentifier::Path(v) => Ok(v),
194                PathOrIdentifier::Identifier(v) => Err(v),
195            })(i)
196        };
197
198        // match structs
199        let (i, path) = opt(path)(i)?;
200        if let Some(path) = path {
201            let i_before_matching_with = i;
202            let (i, _) = opt(ws(keyword("with")))(i)?;
203
204            let (i, is_unnamed_struct) = opt_opening_paren(i)?;
205            if is_unnamed_struct {
206                let (i, targets) = alt((
207                    map(char(')'), |_| Vec::new()),
208                    terminated(
209                        cut(separated_list1(ws(char(',')), |i| Self::parse(i, s))),
210                        pair(opt(ws(char(','))), ws(cut(char(')')))),
211                    ),
212                ))(i)?;
213                return Ok((i, Self::Tuple(path, targets)));
214            }
215
216            let (i, is_named_struct) = opt_opening_brace(i)?;
217            if is_named_struct {
218                let (i, targets) = alt((
219                    map(char('}'), |_| Vec::new()),
220                    terminated(
221                        cut(separated_list1(ws(char(',')), |i| Self::named(i, s))),
222                        pair(opt(ws(char(','))), ws(cut(char('}')))),
223                    ),
224                ))(i)?;
225                return Ok((i, Self::Struct(path, targets)));
226            }
227
228            return Ok((i_before_matching_with, Self::Path(path)));
229        }
230
231        // neither literal nor struct nor path
232        let (new_i, name) = identifier(i)?;
233        Ok((new_i, Self::verify_name(i, name)?))
234    }
235
236    fn lit(i: &'a str) -> ParseResult<'a, Self> {
237        alt((
238            map(str_lit, Self::StrLit),
239            map(char_lit, Self::CharLit),
240            map(num_lit, Self::NumLit),
241            map(bool_lit, Self::BoolLit),
242        ))(i)
243    }
244
245    fn named(init_i: &'a str, s: &State<'_>) -> ParseResult<'a, (&'a str, Self)> {
246        let (i, (src, target)) = pair(
247            identifier,
248            opt(preceded(ws(char(':')), |i| Self::parse(i, s))),
249        )(init_i)?;
250
251        let target = match target {
252            Some(target) => target,
253            None => Self::verify_name(init_i, src)?,
254        };
255
256        Ok((i, (src, target)))
257    }
258
259    fn verify_name(input: &'a str, name: &'a str) -> Result<Self, nom::Err<ErrorContext<'a>>> {
260        match name {
261            "self" | "writer" => Err(nom::Err::Failure(ErrorContext {
262                input,
263                message: Some(Cow::Owned(format!("Cannot use `{name}` as a name"))),
264            })),
265            _ => Ok(Self::Name(name)),
266        }
267    }
268}
269
270#[derive(Debug, PartialEq)]
271pub struct When<'a> {
272    pub ws: Ws,
273    pub target: Target<'a>,
274    pub nodes: Vec<Node<'a>>,
275}
276
277impl<'a> When<'a> {
278    fn r#match(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
279        let mut p = tuple((
280            |i| s.tag_block_start(i),
281            opt(Whitespace::parse),
282            ws(keyword("else")),
283            cut(tuple((
284                opt(Whitespace::parse),
285                |i| s.tag_block_end(i),
286                cut(|i| Node::many(i, s)),
287            ))),
288        ));
289        let (i, (_, pws, _, (nws, _, nodes))) = p(i)?;
290        Ok((
291            i,
292            Self {
293                ws: Ws(pws, nws),
294                target: Target::Name("_"),
295                nodes,
296            },
297        ))
298    }
299
300    #[allow(clippy::self_named_constructors)]
301    fn when(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
302        let mut p = tuple((
303            |i| s.tag_block_start(i),
304            opt(Whitespace::parse),
305            ws(keyword("when")),
306            cut(tuple((
307                ws(|i| Target::parse(i, s)),
308                opt(Whitespace::parse),
309                |i| s.tag_block_end(i),
310                cut(|i| Node::many(i, s)),
311            ))),
312        ));
313        let (i, (_, pws, _, (target, nws, _, nodes))) = p(i)?;
314        Ok((
315            i,
316            Self {
317                ws: Ws(pws, nws),
318                target,
319                nodes,
320            },
321        ))
322    }
323}
324
325#[derive(Debug, PartialEq)]
326pub struct Cond<'a> {
327    pub ws: Ws,
328    pub cond: Option<CondTest<'a>>,
329    pub nodes: Vec<Node<'a>>,
330}
331
332impl<'a> Cond<'a> {
333    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
334        let mut p = tuple((
335            |i| s.tag_block_start(i),
336            opt(Whitespace::parse),
337            ws(alt((keyword("else"), |i| {
338                let _ = keyword("elif")(i)?;
339                Err(nom::Err::Failure(ErrorContext {
340                    input: i,
341                    message: Some(Cow::Borrowed(
342                        "unknown `elif` keyword; did you mean `else if`?",
343                    )),
344                }))
345            }))),
346            cut(tuple((
347                opt(|i| CondTest::parse(i, s)),
348                opt(Whitespace::parse),
349                |i| s.tag_block_end(i),
350                cut(|i| Node::many(i, s)),
351            ))),
352        ));
353        let (i, (_, pws, _, (cond, nws, _, nodes))) = p(i)?;
354        Ok((
355            i,
356            Self {
357                ws: Ws(pws, nws),
358                cond,
359                nodes,
360            },
361        ))
362    }
363}
364
365#[derive(Debug, PartialEq)]
366pub struct CondTest<'a> {
367    pub target: Option<Target<'a>>,
368    pub expr: Expr<'a>,
369}
370
371impl<'a> CondTest<'a> {
372    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
373        let mut p = preceded(
374            ws(keyword("if")),
375            cut(tuple((
376                opt(delimited(
377                    ws(alt((keyword("let"), keyword("set")))),
378                    ws(|i| Target::parse(i, s)),
379                    ws(char('=')),
380                )),
381                ws(|i| Expr::parse(i, s.level.get())),
382            ))),
383        );
384        let (i, (target, expr)) = p(i)?;
385        Ok((i, Self { target, expr }))
386    }
387}
388
389#[derive(Clone, Copy, Debug, PartialEq)]
390pub enum Whitespace {
391    Preserve,
392    Suppress,
393    Minimize,
394}
395
396impl Whitespace {
397    fn parse(i: &str) -> ParseResult<'_, Self> {
398        alt((
399            value(Self::Preserve, char('+')),
400            value(Self::Suppress, char('-')),
401            value(Self::Minimize, char('~')),
402        ))(i)
403    }
404}
405
406#[derive(Debug, PartialEq)]
407pub struct Loop<'a> {
408    pub ws1: Ws,
409    pub var: Target<'a>,
410    pub iter: Expr<'a>,
411    pub cond: Option<Expr<'a>>,
412    pub body: Vec<Node<'a>>,
413    pub ws2: Ws,
414    pub else_nodes: Vec<Node<'a>>,
415    pub ws3: Ws,
416}
417
418impl<'a> Loop<'a> {
419    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
420        fn content<'a>(i: &'a str, s: &State<'_>) -> ParseResult<'a, Vec<Node<'a>>> {
421            s.enter_loop();
422            let result = Node::many(i, s);
423            s.leave_loop();
424            result
425        }
426
427        let if_cond = preceded(
428            ws(keyword("if")),
429            cut(ws(|i| Expr::parse(i, s.level.get()))),
430        );
431
432        let else_block = |i| {
433            let mut p = preceded(
434                ws(keyword("else")),
435                cut(tuple((
436                    opt(Whitespace::parse),
437                    delimited(
438                        |i| s.tag_block_end(i),
439                        |i| Node::many(i, s),
440                        |i| s.tag_block_start(i),
441                    ),
442                    opt(Whitespace::parse),
443                ))),
444            );
445            let (i, (pws, nodes, nws)) = p(i)?;
446            Ok((i, (pws, nodes, nws)))
447        };
448
449        let mut p = tuple((
450            opt(Whitespace::parse),
451            ws(keyword("for")),
452            cut(tuple((
453                ws(|i| Target::parse(i, s)),
454                ws(keyword("in")),
455                cut(tuple((
456                    ws(|i| Expr::parse(i, s.level.get())),
457                    opt(if_cond),
458                    opt(Whitespace::parse),
459                    |i| s.tag_block_end(i),
460                    cut(tuple((
461                        |i| content(i, s),
462                        cut(tuple((
463                            |i| s.tag_block_start(i),
464                            opt(Whitespace::parse),
465                            opt(else_block),
466                            ws(keyword("endfor")),
467                            opt(Whitespace::parse),
468                        ))),
469                    ))),
470                ))),
471            ))),
472        ));
473        let (i, (pws1, _, (var, _, (iter, cond, nws1, _, (body, (_, pws2, else_block, _, nws2)))))) =
474            p(i)?;
475        let (nws3, else_block, pws3) = else_block.unwrap_or_default();
476        Ok((
477            i,
478            Self {
479                ws1: Ws(pws1, nws1),
480                var,
481                iter,
482                cond,
483                body,
484                ws2: Ws(pws2, nws3),
485                else_nodes: else_block,
486                ws3: Ws(pws3, nws2),
487            },
488        ))
489    }
490}
491
492#[derive(Debug, PartialEq)]
493pub struct Macro<'a> {
494    pub ws1: Ws,
495    pub name: &'a str,
496    pub args: Vec<&'a str>,
497    pub nodes: Vec<Node<'a>>,
498    pub ws2: Ws,
499}
500
501impl<'a> Macro<'a> {
502    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
503        fn parameters(i: &str) -> ParseResult<'_, Vec<&str>> {
504            delimited(
505                ws(char('(')),
506                separated_list0(char(','), ws(identifier)),
507                tuple((opt(ws(char(','))), char(')'))),
508            )(i)
509        }
510
511        let mut start = tuple((
512            opt(Whitespace::parse),
513            ws(keyword("macro")),
514            cut(tuple((
515                ws(identifier),
516                opt(ws(parameters)),
517                opt(Whitespace::parse),
518                |i| s.tag_block_end(i),
519            ))),
520        ));
521        let (i, (pws1, _, (name, params, nws1, _))) = start(i)?;
522
523        let mut end = cut(tuple((
524            |i| Node::many(i, s),
525            cut(tuple((
526                |i| s.tag_block_start(i),
527                opt(Whitespace::parse),
528                ws(keyword("endmacro")),
529                cut(preceded(
530                    opt(|before| {
531                        let (after, end_name) = ws(identifier)(before)?;
532                        check_end_name(before, after, name, end_name, "macro")
533                    }),
534                    opt(Whitespace::parse),
535                )),
536            ))),
537        )));
538        let (i, (contents, (_, pws2, _, nws2))) = end(i)?;
539
540        if name == "super" {
541            // TODO: yield a a better error message here
542            return Err(ErrorContext::from_err(nom::Err::Failure(Error::new(
543                i,
544                ErrorKind::Fail,
545            ))));
546        }
547
548        Ok((
549            i,
550            Self {
551                ws1: Ws(pws1, nws1),
552                name,
553                args: params.unwrap_or_default(),
554                nodes: contents,
555                ws2: Ws(pws2, nws2),
556            },
557        ))
558    }
559}
560
561#[derive(Debug, PartialEq)]
562pub struct Import<'a> {
563    pub ws: Ws,
564    pub path: &'a str,
565    pub scope: &'a str,
566}
567
568impl<'a> Import<'a> {
569    fn parse(i: &'a str) -> ParseResult<'a, Self> {
570        let mut p = tuple((
571            opt(Whitespace::parse),
572            ws(keyword("import")),
573            cut(tuple((
574                ws(str_lit),
575                ws(keyword("as")),
576                cut(pair(ws(identifier), opt(Whitespace::parse))),
577            ))),
578        ));
579        let (i, (pws, _, (path, _, (scope, nws)))) = p(i)?;
580        Ok((
581            i,
582            Self {
583                ws: Ws(pws, nws),
584                path,
585                scope,
586            },
587        ))
588    }
589}
590
591#[derive(Debug, PartialEq)]
592pub struct Call<'a> {
593    pub ws: Ws,
594    pub scope: Option<&'a str>,
595    pub name: &'a str,
596    pub args: Vec<Expr<'a>>,
597}
598
599impl<'a> Call<'a> {
600    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
601        let mut p = tuple((
602            opt(Whitespace::parse),
603            ws(keyword("call")),
604            cut(tuple((
605                opt(tuple((ws(identifier), ws(tag("::"))))),
606                ws(identifier),
607                opt(ws(|nested| Expr::arguments(nested, s.level.get(), true))),
608                opt(Whitespace::parse),
609            ))),
610        ));
611        let (i, (pws, _, (scope, name, args, nws))) = p(i)?;
612        let scope = scope.map(|(scope, _)| scope);
613        let args = args.unwrap_or_default();
614        Ok((
615            i,
616            Self {
617                ws: Ws(pws, nws),
618                scope,
619                name,
620                args,
621            },
622        ))
623    }
624}
625
626#[derive(Debug, PartialEq)]
627pub struct Match<'a> {
628    pub ws1: Ws,
629    pub expr: Expr<'a>,
630    pub arms: Vec<When<'a>>,
631    pub ws2: Ws,
632}
633
634impl<'a> Match<'a> {
635    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
636        let mut p = tuple((
637            opt(Whitespace::parse),
638            ws(keyword("match")),
639            cut(tuple((
640                ws(|i| Expr::parse(i, s.level.get())),
641                opt(Whitespace::parse),
642                |i| s.tag_block_end(i),
643                cut(tuple((
644                    ws(many0(ws(value((), |i| Comment::parse(i, s))))),
645                    many1(|i| When::when(i, s)),
646                    cut(tuple((
647                        opt(|i| When::r#match(i, s)),
648                        cut(tuple((
649                            ws(|i| s.tag_block_start(i)),
650                            opt(Whitespace::parse),
651                            ws(keyword("endmatch")),
652                            opt(Whitespace::parse),
653                        ))),
654                    ))),
655                ))),
656            ))),
657        ));
658        let (i, (pws1, _, (expr, nws1, _, (_, arms, (else_arm, (_, pws2, _, nws2)))))) = p(i)?;
659
660        let mut arms = arms;
661        if let Some(arm) = else_arm {
662            arms.push(arm);
663        }
664
665        Ok((
666            i,
667            Self {
668                ws1: Ws(pws1, nws1),
669                expr,
670                arms,
671                ws2: Ws(pws2, nws2),
672            },
673        ))
674    }
675}
676
677#[derive(Debug, PartialEq)]
678pub struct BlockDef<'a> {
679    pub ws1: Ws,
680    pub name: &'a str,
681    pub nodes: Vec<Node<'a>>,
682    pub ws2: Ws,
683}
684
685impl<'a> BlockDef<'a> {
686    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
687        let mut start = tuple((
688            opt(Whitespace::parse),
689            ws(keyword("block")),
690            cut(tuple((ws(identifier), opt(Whitespace::parse), |i| {
691                s.tag_block_end(i)
692            }))),
693        ));
694        let (i, (pws1, _, (name, nws1, _))) = start(i)?;
695
696        let mut end = cut(tuple((
697            |i| Node::many(i, s),
698            cut(tuple((
699                |i| s.tag_block_start(i),
700                opt(Whitespace::parse),
701                ws(keyword("endblock")),
702                cut(tuple((
703                    opt(|before| {
704                        let (after, end_name) = ws(identifier)(before)?;
705                        check_end_name(before, after, name, end_name, "block")
706                    }),
707                    opt(Whitespace::parse),
708                ))),
709            ))),
710        )));
711        let (i, (nodes, (_, pws2, _, (_, nws2)))) = end(i)?;
712
713        Ok((
714            i,
715            BlockDef {
716                ws1: Ws(pws1, nws1),
717                name,
718                nodes,
719                ws2: Ws(pws2, nws2),
720            },
721        ))
722    }
723}
724
725fn check_end_name<'a>(
726    before: &'a str,
727    after: &'a str,
728    name: &'a str,
729    end_name: &'a str,
730    kind: &str,
731) -> ParseResult<'a> {
732    if name == end_name {
733        return Ok((after, end_name));
734    }
735    let message = if name.is_empty() && !end_name.is_empty() {
736        format!("unexpected name `{end_name}` in `end{kind}` tag for unnamed `{kind}`")
737    } else {
738        format!("expected name `{name}` in `end{kind}` tag, found `{end_name}`")
739    };
740    Err(nom::Err::Failure(ErrorContext {
741        input: before,
742        message: Some(Cow::Owned(message)),
743    }))
744}
745
746#[derive(Debug, PartialEq)]
747pub struct Lit<'a> {
748    pub lws: &'a str,
749    pub val: &'a str,
750    pub rws: &'a str,
751}
752
753impl<'a> Lit<'a> {
754    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
755        let p_start = alt((
756            tag(s.syntax.block_start),
757            tag(s.syntax.comment_start),
758            tag(s.syntax.expr_start),
759        ));
760
761        let (i, _) = not(eof)(i)?;
762        let (i, content) = opt(recognize(skip_till(p_start)))(i)?;
763        let (i, content) = match content {
764            Some("") => {
765                // {block,comment,expr}_start follows immediately.
766                return Err(nom::Err::Error(error_position!(i, ErrorKind::TakeUntil)));
767            }
768            Some(content) => (i, content),
769            None => ("", i), // there is no {block,comment,expr}_start: take everything
770        };
771        Ok((i, Self::split_ws_parts(content)))
772    }
773
774    pub(crate) fn split_ws_parts(s: &'a str) -> Self {
775        let trimmed_start = s.trim_start_matches(is_ws);
776        let len_start = s.len() - trimmed_start.len();
777        let trimmed = trimmed_start.trim_end_matches(is_ws);
778        Self {
779            lws: &s[..len_start],
780            val: trimmed,
781            rws: &trimmed_start[trimmed.len()..],
782        }
783    }
784}
785
786#[derive(Debug, PartialEq)]
787pub struct Raw<'a> {
788    pub ws1: Ws,
789    pub lit: Lit<'a>,
790    pub ws2: Ws,
791}
792
793impl<'a> Raw<'a> {
794    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
795        let endraw = tuple((
796            |i| s.tag_block_start(i),
797            opt(Whitespace::parse),
798            ws(keyword("endraw")),
799            opt(Whitespace::parse),
800            peek(|i| s.tag_block_end(i)),
801        ));
802
803        let mut p = tuple((
804            opt(Whitespace::parse),
805            ws(keyword("raw")),
806            cut(tuple((
807                opt(Whitespace::parse),
808                |i| s.tag_block_end(i),
809                consumed(skip_till(endraw)),
810            ))),
811        ));
812
813        let (_, (pws1, _, (nws1, _, (contents, (i, (_, pws2, _, nws2, _)))))) = p(i)?;
814        let lit = Lit::split_ws_parts(contents);
815        let ws1 = Ws(pws1, nws1);
816        let ws2 = Ws(pws2, nws2);
817        Ok((i, Self { ws1, lit, ws2 }))
818    }
819}
820
821#[derive(Debug, PartialEq)]
822pub struct Let<'a> {
823    pub ws: Ws,
824    pub var: Target<'a>,
825    pub val: Option<Expr<'a>>,
826}
827
828impl<'a> Let<'a> {
829    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
830        let mut p = tuple((
831            opt(Whitespace::parse),
832            ws(alt((keyword("let"), keyword("set")))),
833            cut(tuple((
834                ws(|i| Target::parse(i, s)),
835                opt(preceded(
836                    ws(char('=')),
837                    ws(|i| Expr::parse(i, s.level.get())),
838                )),
839                opt(Whitespace::parse),
840            ))),
841        ));
842        let (i, (pws, _, (var, val, nws))) = p(i)?;
843
844        Ok((
845            i,
846            Let {
847                ws: Ws(pws, nws),
848                var,
849                val,
850            },
851        ))
852    }
853}
854
855#[derive(Debug, PartialEq)]
856pub struct If<'a> {
857    pub ws: Ws,
858    pub branches: Vec<Cond<'a>>,
859}
860
861impl<'a> If<'a> {
862    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
863        let mut p = tuple((
864            opt(Whitespace::parse),
865            |i| CondTest::parse(i, s),
866            cut(tuple((
867                opt(Whitespace::parse),
868                |i| s.tag_block_end(i),
869                cut(tuple((
870                    |i| Node::many(i, s),
871                    many0(|i| Cond::parse(i, s)),
872                    cut(tuple((
873                        |i| s.tag_block_start(i),
874                        opt(Whitespace::parse),
875                        ws(keyword("endif")),
876                        opt(Whitespace::parse),
877                    ))),
878                ))),
879            ))),
880        ));
881
882        let (i, (pws1, cond, (nws1, _, (nodes, elifs, (_, pws2, _, nws2))))) = p(i)?;
883        let mut branches = vec![Cond {
884            ws: Ws(pws1, nws1),
885            cond: Some(cond),
886            nodes,
887        }];
888        branches.extend(elifs);
889
890        Ok((
891            i,
892            Self {
893                ws: Ws(pws2, nws2),
894                branches,
895            },
896        ))
897    }
898}
899
900#[derive(Debug, PartialEq)]
901pub struct Include<'a> {
902    pub ws: Ws,
903    pub path: &'a str,
904}
905
906impl<'a> Include<'a> {
907    fn parse(i: &'a str) -> ParseResult<'a, Self> {
908        let mut p = tuple((
909            opt(Whitespace::parse),
910            ws(keyword("include")),
911            cut(pair(ws(str_lit), opt(Whitespace::parse))),
912        ));
913        let (i, (pws, _, (path, nws))) = p(i)?;
914        Ok((
915            i,
916            Self {
917                ws: Ws(pws, nws),
918                path,
919            },
920        ))
921    }
922}
923
924#[derive(Debug, PartialEq)]
925pub struct Extends<'a> {
926    pub path: &'a str,
927}
928
929impl<'a> Extends<'a> {
930    fn parse(i: &'a str) -> ParseResult<'a, Self> {
931        let (i, path) = preceded(ws(keyword("extends")), cut(ws(str_lit)))(i)?;
932        Ok((i, Self { path }))
933    }
934}
935
936#[derive(Debug, PartialEq)]
937pub struct Comment<'a> {
938    pub ws: Ws,
939    pub content: &'a str,
940}
941
942impl<'a> Comment<'a> {
943    fn parse(i: &'a str, s: &State<'_>) -> ParseResult<'a, Self> {
944        fn body<'a>(mut i: &'a str, s: &State<'_>) -> ParseResult<'a> {
945            let mut level = 0;
946            loop {
947                let (end, tail) = take_until(s.syntax.comment_end)(i)?;
948                match take_until::<_, _, ErrorContext<'_>>(s.syntax.comment_start)(i) {
949                    Ok((start, _)) if start.as_ptr() < end.as_ptr() => {
950                        level += 1;
951                        i = &start[2..];
952                    }
953                    _ if level > 0 => {
954                        level -= 1;
955                        i = &end[2..];
956                    }
957                    _ => return Ok((end, tail)),
958                }
959            }
960        }
961
962        let mut p = tuple((
963            |i| s.tag_comment_start(i),
964            cut(tuple((
965                opt(Whitespace::parse),
966                |i| body(i, s),
967                |i| s.tag_comment_end(i),
968            ))),
969        ));
970        let (i, (content, (pws, tail, _))) = p(i)?;
971        let nws = if tail.ends_with('-') {
972            Some(Whitespace::Suppress)
973        } else if tail.ends_with('+') {
974            Some(Whitespace::Preserve)
975        } else if tail.ends_with('~') {
976            Some(Whitespace::Minimize)
977        } else {
978            None
979        };
980        Ok((
981            i,
982            Self {
983                ws: Ws(pws, nws),
984                content,
985            },
986        ))
987    }
988}
989
990/// First field is "minus/plus sign was used on the left part of the item".
991///
992/// Second field is "minus/plus sign was used on the right part of the item".
993#[derive(Clone, Copy, Debug, PartialEq)]
994pub struct Ws(pub Option<Whitespace>, pub Option<Whitespace>);