Add parsing of operations

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-12 10:04:15 +01:00
parent 474324726a
commit 09ae91c393

View file

@ -12,7 +12,10 @@ use winnow::ascii::multispace0;
use winnow::ascii::multispace1; use winnow::ascii::multispace1;
use winnow::combinator::alt; use winnow::combinator::alt;
use winnow::combinator::cut_err; use winnow::combinator::cut_err;
use winnow::combinator::dispatch;
use winnow::combinator::empty;
use winnow::combinator::eof; use winnow::combinator::eof;
use winnow::combinator::fail;
use winnow::combinator::not; use winnow::combinator::not;
use winnow::combinator::opt; use winnow::combinator::opt;
use winnow::combinator::peek; use winnow::combinator::peek;
@ -213,6 +216,17 @@ pub enum TokenKind {
In, In,
End, End,
Literal(TokenLiteral), Literal(TokenLiteral),
Operator(TokenOperator),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TokenOperator {
Plus,
Minus,
Times,
Divide,
And,
Or,
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
@ -309,6 +323,13 @@ impl TemplateToken {
} }
} }
pub fn operator(operator: TokenOperator, source: NomoInput) -> Self {
TemplateToken {
kind: TokenKind::Operator(operator),
source,
}
}
pub fn kind(&self) -> TokenKind { pub fn kind(&self) -> TokenKind {
self.kind self.kind
} }
@ -385,7 +406,7 @@ fn parse_interpolate<'input>(input: &mut Input<'input>) -> PResult<'input, Vec<T
fn parse_block_token<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> { fn parse_block_token<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
trace( trace(
"parse_block_token", "parse_block_token",
alt((parse_ident, parse_keyword, parse_whitespace)), alt((parse_ident, parse_keyword, parse_whitespace, parse_operator)),
) )
.parse_next(input) .parse_next(input)
} }
@ -474,6 +495,31 @@ fn parse_ident<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateTok
.parse_next(input) .parse_next(input)
} }
fn parse_operator<'input>(input: &mut Input<'input>) -> PResult<'input, TemplateToken> {
let (operator, source) = trace(
"operator",
dispatch! {any;
'+' => empty.value(TokenOperator::Plus),
'-' => empty.value(TokenOperator::Minus),
'*' => empty.value(TokenOperator::Times),
'/' => empty.value(TokenOperator::Divide),
'&' => alt((
"&".value(TokenOperator::And),
cut_err(fail),
)),
'|' => alt((
"|".value(TokenOperator::Or),
cut_err(fail),
)),
_ => fail,
},
)
.with_taken()
.parse_next(input)?;
Ok(TemplateToken::operator(operator, source))
}
fn ident<'input>(input: &mut Input<'input>) -> PResult<'input, NomoInput> { fn ident<'input>(input: &mut Input<'input>) -> PResult<'input, NomoInput> {
peek(not(parse_keyword)) peek(not(parse_keyword))
.context(ParseError::ctx().msg("Expected an ident, but found a literal instead")) .context(ParseError::ctx().msg("Expected an ident, but found a literal instead"))
@ -680,4 +726,61 @@ mod tests {
) )
"#); "#);
} }
#[test]
fn parse_operations() {
let input = "{{= 5 * 4 + 3 / 2 - 1 }}{{ if foo && bar || baz }}{{ end }}";
let output = parse(input.into());
insta::assert_debug_snapshot!(output, @r#"
Ok(
ParsedTemplate {
tokens: [
[LeftDelim]"{{" (0..2),
[WantsOutput]"=" (2..3),
[Whitespace]" " (3..4),
[Ident]"5" (4..5),
[Whitespace]" " (5..6),
[Operator(Times)]"*" (6..7),
[Whitespace]" " (7..8),
[Ident]"4" (8..9),
[Whitespace]" " (9..10),
[Operator(Plus)]"+" (10..11),
[Whitespace]" " (11..12),
[Ident]"3" (12..13),
[Whitespace]" " (13..14),
[Operator(Divide)]"/" (14..15),
[Whitespace]" " (15..16),
[Ident]"2" (16..17),
[Whitespace]" " (17..18),
[Operator(Minus)]"-" (18..19),
[Whitespace]" " (19..20),
[Ident]"1" (20..21),
[Whitespace]" " (21..22),
[RightDelim]"}}" (22..24),
[LeftDelim]"{{" (24..26),
[Whitespace]" " (26..27),
[ConditionalIf]"if" (27..29),
[Whitespace]" " (29..30),
[Ident]"foo" (30..33),
[Whitespace]" " (33..34),
[Operator(And)]"&&" (34..36),
[Whitespace]" " (36..37),
[Ident]"bar" (37..40),
[Whitespace]" " (40..41),
[Operator(Or)]"||" (41..43),
[Whitespace]" " (43..44),
[Ident]"baz" (44..47),
[Whitespace]" " (47..48),
[RightDelim]"}}" (48..50),
[LeftDelim]"{{" (50..52),
[Whitespace]" " (52..53),
[End]"end" (53..56),
[Whitespace]" " (56..57),
[RightDelim]"}}" (57..59),
],
},
)
"#);
}
} }