From beac224f5ba864db03473f1a319ff313e6baa0ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 15 Mar 2026 12:11:30 +0100 Subject: [PATCH] Add lexing of '?' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- src/lexer/mod.rs | 25 +++++++++++++++++++++++++ src/parser/mod.rs | 8 +++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/lexer/mod.rs b/src/lexer/mod.rs index a35e096..6fb66a2 100644 --- a/src/lexer/mod.rs +++ b/src/lexer/mod.rs @@ -238,6 +238,7 @@ pub enum TokenOperator { GreaterOrEqual, Lesser, LesserOrEqual, + QuestionMark, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -567,6 +568,7 @@ fn parse_operator<'input>(input: &mut Input<'input>) -> PResult<'input, Template "=".value(TokenOperator::Equal), cut_err(fail), )), + '?' => empty.value(TokenOperator::QuestionMark), _ => fail, }, ) @@ -604,6 +606,7 @@ fn ident_terminator_check<'input>(input: &mut Input<'input>) -> PResult<'input, fn ident_terminator<'input>(input: &mut Input<'input>) -> PResult<'input, ()> { alt(( eof.void(), + parse_operator.void(), one_of(('{', '}')).void(), one_of(('(', ',', ')')).void(), one_of((' ', '\t', '\r', '\n')).void(), @@ -905,4 +908,26 @@ mod tests { ) "#); } + + #[test] + fn parse_question_mark() { + let input = "{{= foo? }}"; + let output = parse(input.into()); + + insta::assert_debug_snapshot!(output, @r#" + Ok( + ParsedTemplate { + tokens: [ + [LeftDelim]"{{" (0..2), + [WantsOutput]"=" (2..3), + [Whitespace]" " (3..4), + [Ident]"foo" (4..7), + [Operator(QuestionMark)]"?" (7..8), + [Whitespace]" " (8..9), + [RightDelim]"}}" (9..11), + ], + }, + ) + "#); + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index ed4456f..8c12ffb 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7,6 +7,7 @@ use winnow::combinator::cut_err; use winnow::combinator::delimited; use winnow::combinator::dispatch; use winnow::combinator::expression; +use winnow::combinator::fail; use winnow::combinator::not; use winnow::combinator::opt; use winnow::combinator::peek; @@ -685,8 +686,9 @@ fn parse_expression<'input>( op: TokenOperator::$val, lhs: Box::new(lhs), rhs: Box::new(rhs) - })) - ),* + })), + )* + _ => fail } }; } @@ -737,6 +739,7 @@ mod tests { use winnow::combinator::fail; use winnow::stream::TokenSlice; + use crate::lexer::TokenKind; use crate::parser::AstError; use crate::parser::AstFailure; use crate::parser::TemplateAst; @@ -744,7 +747,6 @@ mod tests { use crate::parser::parse; use crate::parser::parse_block; use crate::parser::parse_end; - use crate::lexer::TokenKind; fn panic_pretty<'a>( input: &'_ str,