From 018ba3cd2c7bcaf7ca3080307927f28cd25e293b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Wed, 11 Mar 2026 10:34:17 +0100 Subject: [PATCH] Add for loop parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/parser/mod.rs | 85 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 16 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a9171f0..de0ba97 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -209,6 +209,8 @@ pub enum TokenKind { Invalid, ConditionalIf, ConditionalElse, + For, + In, End, Literal(TokenLiteral), } @@ -295,6 +297,8 @@ impl TemplateToken { invalid => TokenKind::Invalid, conditional_if => TokenKind::ConditionalIf, conditional_else => TokenKind::ConditionalElse, + keyword_for => TokenKind::For, + keyword_in => TokenKind::In, end => TokenKind::End, } @@ -381,14 +385,7 @@ fn parse_interpolate<'input>(input: &mut Input<'input>) -> PResult<'input, Vec(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { trace( "parse_block_token", - alt(( - parse_ident, - terminated(parse_literal, ident_terminator_check), - terminated(parse_condition_if, ident_terminator_check), - terminated(parse_condition_else, ident_terminator_check), - terminated(parse_end, ident_terminator_check), - parse_whitespace, - )), + alt((parse_ident, parse_keyword, parse_whitespace)), ) .parse_next(input) } @@ -431,6 +428,26 @@ fn parse_end<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken trace("parse_end", "end".map(TemplateToken::end)).parse_next(input) } +fn parse_for<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { + trace("parse_for", "for".map(TemplateToken::keyword_for)).parse_next(input) +} + +fn parse_in<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { + trace("parse_in", "in".map(TemplateToken::keyword_in)).parse_next(input) +} + +fn parse_keyword<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { + alt(( + terminated(parse_literal, ident_terminator_check), + terminated(parse_condition_if, ident_terminator_check), + terminated(parse_condition_else, ident_terminator_check), + terminated(parse_for, ident_terminator_check), + terminated(parse_in, ident_terminator_check), + terminated(parse_end, ident_terminator_check), + )) + .parse_next(input) +} + fn parse_whitespace<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { trace( "parse_whitespace", @@ -458,14 +475,9 @@ fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateTok } fn ident<'input>(input: &mut Input<'input>) -> PResult<'input, NomoInput> { - peek(not(alt(( - parse_literal, - parse_condition_if, - parse_condition_else, - parse_end, - )))) - .context(ParseError::ctx().msg("Expected an ident, but found a literal instead")) - .parse_next(input)?; + peek(not(parse_keyword)) + .context(ParseError::ctx().msg("Expected an ident, but found a literal instead")) + .parse_next(input)?; take_while(1.., |c: char| c.is_alphanumeric() || "_".contains(c)).parse_next(input) } @@ -627,4 +639,45 @@ mod tests { ) "#); } + + #[test] + fn parse_for_loop() { + let input = "{{ for value in array }} Hi: {{= value }} {{ end }}"; + let output = parse(input.into()); + + insta::assert_debug_snapshot!(output, @r#" + Ok( + ParsedTemplate { + tokens: [ + [LeftDelim]"{{" (0..2), + [Whitespace]" " (2..3), + [For]"for" (3..6), + [Whitespace]" " (6..7), + [Ident]"value" (7..12), + [Whitespace]" " (12..13), + [In]"in" (13..15), + [Whitespace]" " (15..16), + [Ident]"array" (16..21), + [Whitespace]" " (21..22), + [RightDelim]"}}" (22..24), + [Whitespace]" " (24..25), + [Content]"Hi:" (25..28), + [Whitespace]" " (28..29), + [LeftDelim]"{{" (29..31), + [WantsOutput]"=" (31..32), + [Whitespace]" " (32..33), + [Ident]"value" (33..38), + [Whitespace]" " (38..39), + [RightDelim]"}}" (39..41), + [Whitespace]" " (41..42), + [LeftDelim]"{{" (42..44), + [Whitespace]" " (44..45), + [End]"end" (45..48), + [Whitespace]" " (48..49), + [RightDelim]"}}" (49..51), + ], + }, + ) + "#); + } }