Add parsing of if/end

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-06 20:53:57 +01:00
parent 10a33dc935
commit d691fb9198

View file

@ -13,6 +13,7 @@ use winnow::ascii::multispace1;
use winnow::combinator::alt;
use winnow::combinator::cut_err;
use winnow::combinator::eof;
use winnow::combinator::not;
use winnow::combinator::opt;
use winnow::combinator::peek;
use winnow::combinator::repeat_till;
@ -204,6 +205,14 @@ pub enum TokenKind {
Ident,
Whitespace,
Invalid,
ConditionalIf,
End,
Literal(TokenLiteral),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TokenLiteral {
Bool(bool),
}
impl PartialEq<TokenKind> for TemplateToken {
@ -242,52 +251,35 @@ pub struct TemplateToken {
source: TempleInput,
}
macro_rules! impl_token_kind_builders {
($($name:ident => $kind:expr),+ $(,)?) => {
$(
fn $name(source: TempleInput) -> Self {
TemplateToken {
kind: $kind,
source,
}
}
)+
};
}
impl TemplateToken {
fn content(source: TempleInput) -> Self {
TemplateToken {
kind: TokenKind::Content,
source,
}
impl_token_kind_builders! {
content => TokenKind::Content,
left_delim => TokenKind::LeftDelim,
right_delim => TokenKind::RightDelim,
wants_output => TokenKind::WantsOutput,
ident => TokenKind::Ident,
whitespace => TokenKind::Whitespace,
invalid => TokenKind::Invalid,
conditional_if => TokenKind::ConditionalIf,
end => TokenKind::End,
}
fn left_delim(source: TempleInput) -> Self {
pub fn literal(literal: TokenLiteral, source: TempleInput) -> Self {
TemplateToken {
kind: TokenKind::LeftDelim,
source,
}
}
fn right_delim(source: TempleInput) -> Self {
TemplateToken {
kind: TokenKind::RightDelim,
source,
}
}
fn wants_output(source: TempleInput) -> Self {
TemplateToken {
kind: TokenKind::WantsOutput,
source,
}
}
fn ident(source: TempleInput) -> Self {
TemplateToken {
kind: TokenKind::Ident,
source,
}
}
fn whitespace(source: TempleInput) -> Self {
TemplateToken {
kind: TokenKind::Whitespace,
source,
}
}
fn invalid(source: TempleInput) -> Self {
TemplateToken {
kind: TokenKind::Invalid,
kind: TokenKind::Literal(literal),
source,
}
}
@ -364,11 +356,43 @@ fn parse_interpolate<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<T
fn parse_interpolate_token<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
trace(
"parse_interpolate_token",
alt((parse_ident, parse_whitespace)),
alt((
parse_ident,
parse_literal,
parse_condition,
parse_end,
parse_whitespace,
)),
)
.parse_next(input)
}
fn parse_literal<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
trace(
"parse_condition",
alt((parse_boolean,))
.with_taken()
.map(|(lit, span)| TemplateToken::literal(lit, span)),
)
.parse_next(input)
}
fn parse_boolean<'input>(input: &mut Input<'input>) -> PResult<'input, TokenLiteral> {
alt((
"true".value(TokenLiteral::Bool(true)),
"false".value(TokenLiteral::Bool(false)),
))
.parse_next(input)
}
fn parse_condition<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
trace("parse_condition", "if".map(TemplateToken::conditional_if)).parse_next(input)
}
fn parse_end<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
trace("parse_condition", "end".map(TemplateToken::end)).parse_next(input)
}
fn parse_whitespace<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
trace(
"parse_whitespace",
@ -396,6 +420,10 @@ fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateTok
}
fn ident<'input>(input: &mut Input<'input>) -> PResult<'input, TempleInput> {
peek(not(parse_literal))
.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)
}
@ -517,4 +545,83 @@ mod tests {
insta::assert_snapshot!(error.to_report());
}
#[test]
fn parse_simple_condition() {
let input = "{{ if true }} Hello! {{ end }}";
let output = parse(input.into());
insta::assert_debug_snapshot!(output, @r#"
Ok(
ParsedTemplate {
tokens: [
TemplateToken {
kind: LeftDelim,
source: "{{",
},
TemplateToken {
kind: Whitespace,
source: " ",
},
TemplateToken {
kind: Ident,
source: "if",
},
TemplateToken {
kind: Whitespace,
source: " ",
},
TemplateToken {
kind: Literal(
Bool(
true,
),
),
source: "true",
},
TemplateToken {
kind: Whitespace,
source: " ",
},
TemplateToken {
kind: RightDelim,
source: "}}",
},
TemplateToken {
kind: Whitespace,
source: " ",
},
TemplateToken {
kind: Content,
source: "Hello!",
},
TemplateToken {
kind: Whitespace,
source: " ",
},
TemplateToken {
kind: LeftDelim,
source: "{{",
},
TemplateToken {
kind: Whitespace,
source: " ",
},
TemplateToken {
kind: Ident,
source: "end",
},
TemplateToken {
kind: Whitespace,
source: " ",
},
TemplateToken {
kind: RightDelim,
source: "}}",
},
],
},
)
"#);
}
}