Split up if chains more

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-08 21:22:57 +01:00
parent 383f543119
commit c9314a3d9b
4 changed files with 90 additions and 21 deletions

View file

@ -228,10 +228,12 @@ pub enum TemplateAstExpr<'input> {
}, },
IfConditional { IfConditional {
if_block: Box<TemplateAstExpr<'input>>, if_block: Box<TemplateAstExpr<'input>>,
},
ConditionalContent {
content: Vec<TemplateAstExpr<'input>>, content: Vec<TemplateAstExpr<'input>>,
}, },
ElseConditional { ElseConditional {
expression: Vec<TemplateAstExpr<'input>>, expression: Option<Box<TemplateAstExpr<'input>>>,
}, },
Invalid(&'input [TemplateToken]), Invalid(&'input [TemplateToken]),
EndBlock, EndBlock,
@ -314,21 +316,45 @@ fn parse_conditional_chain<'input>(
input: &mut Input<'input>, input: &mut Input<'input>,
) -> Result<TemplateAstExpr<'input>, AstError> { ) -> Result<TemplateAstExpr<'input>, AstError> {
trace("conditional_chain", |input: &mut Input<'input>| { trace("conditional_chain", |input: &mut Input<'input>| {
let if_block = parse_conditional.map(Box::new).parse_next(input)?; let if_block = parse_conditional_if.map(Box::new).parse_next(input)?;
let mut chain = vec![]; let mut chain = vec![];
let (content, end_block): (Vec<_>, _) = chain.push(TemplateAstExpr::IfConditional { if_block });
repeat_till(0.., parse_ast, parse_end).parse_next(input)?;
loop {
let (content, end_block): (Vec<_>, _) = repeat_till(
0..,
parse_ast,
trace(
"conditional_chain_else/end",
alt((parse_end, parse_conditional_else)),
),
)
.parse_next(input)?;
chain.push(TemplateAstExpr::ConditionalContent { content });
let is_end = if let TemplateAstExpr::Block { ref expression, .. } = end_block
&& let TemplateAstExpr::EndBlock = &**expression
{
true
} else {
false
};
chain.push(TemplateAstExpr::IfConditional { if_block, content });
chain.push(end_block); chain.push(end_block);
if dbg!(is_end) {
break;
}
}
Ok(TemplateAstExpr::ConditionalChain { chain }) Ok(TemplateAstExpr::ConditionalChain { chain })
}) })
.parse_next(input) .parse_next(input)
} }
fn parse_conditional<'input>( fn parse_conditional_if<'input>(
input: &mut Input<'input>, input: &mut Input<'input>,
) -> Result<TemplateAstExpr<'input>, AstError> { ) -> Result<TemplateAstExpr<'input>, AstError> {
trace( trace(
@ -344,6 +370,27 @@ fn parse_conditional<'input>(
.parse_next(input) .parse_next(input)
} }
fn parse_conditional_else<'input>(
input: &mut Input<'input>,
) -> Result<TemplateAstExpr<'input>, AstError> {
trace(
"conditional_else",
parse_block(
preceded(
surrounded(ws, TokenKind::ConditionalElse),
opt(preceded(
TokenKind::ConditionalIf,
cut_err(surrounded(ws, parse_value_expression)).map(Box::new),
)),
)
.map(|else_expr| TemplateAstExpr::ElseConditional {
expression: else_expr,
}),
),
)
.parse_next(input)
}
fn parse_end<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> { fn parse_end<'input>(input: &mut Input<'input>) -> Result<TemplateAstExpr<'input>, AstError> {
trace( trace(
"end", "end",
@ -440,6 +487,18 @@ mod tests {
use crate::ast::parse_end; use crate::ast::parse_end;
use crate::parser::TokenKind; use crate::parser::TokenKind;
fn panic_pretty<'a>(
input: &'_ str,
tokens: Result<TemplateAst<'a>, AstFailure>,
) -> TemplateAst<'a> {
match tokens {
Ok(ast) => ast,
Err(failure) => {
panic!("{}", failure.to_report(input));
}
}
}
#[test] #[test]
fn check_only_content() { fn check_only_content() {
let input = "Hello World"; let input = "Hello World";
@ -493,7 +552,7 @@ mod tests {
let parsed = crate::parser::parse(input.into()).unwrap(); let parsed = crate::parser::parse(input.into()).unwrap();
let ast = parse(parsed.tokens()).unwrap(); let ast = panic_pretty(input, parse(parsed.tokens()));
insta::assert_debug_snapshot!(ast, @r#" insta::assert_debug_snapshot!(ast, @r#"
TemplateAst { TemplateAst {
@ -510,6 +569,8 @@ mod tests {
[Whitespace]" " (12..13), [Whitespace]" " (12..13),
), ),
}, },
},
ConditionalContent {
content: [ content: [
StaticContent( StaticContent(
[Content]"Hiii" (13..17), [Content]"Hiii" (13..17),
@ -530,18 +591,6 @@ mod tests {
"#); "#);
} }
fn panic_pretty<'a>(
input: &'_ str,
tokens: Result<TemplateAst<'a>, AstFailure>,
) -> TemplateAst<'a> {
match tokens {
Ok(ast) => ast,
Err(failure) => {
panic!("{}", failure.to_report(input));
}
}
}
#[test] #[test]
fn check_invalid_action() { fn check_invalid_action() {
let input = r#"{{ value }} let input = r#"{{ value }}
@ -624,4 +673,13 @@ mod tests {
) )
"#); "#);
} }
#[test]
fn check_empty_output() {
let input = "{{ if foo }}{{ end }}";
let parsed = crate::parser::parse(input.into()).unwrap();
panic_pretty(input, parse(parsed.tokens()));
}
} }

View file

@ -16,6 +16,8 @@ TemplateAst {
[Whitespace]"\n " (12..25), [Whitespace]"\n " (12..25),
), ),
}, },
},
ConditionalContent {
content: [ content: [
ConditionalChain { ConditionalChain {
chain: [ chain: [
@ -29,6 +31,8 @@ TemplateAst {
[Whitespace]"\n " (37..54), [Whitespace]"\n " (37..54),
), ),
}, },
},
ConditionalContent {
content: [ content: [
StaticContent( StaticContent(
[Content]"Hiii" (54..58), [Content]"Hiii" (54..58),

View file

@ -92,7 +92,6 @@ fn emit_ast_expr(
let mut chain = chain.iter(); let mut chain = chain.iter();
let Some(TemplateAstExpr::IfConditional { let Some(TemplateAstExpr::IfConditional {
if_block: expression, if_block: expression,
content,
}) = chain.next() }) = chain.next()
else { else {
unreachable!("First element in conditional chain should be an IfConditional"); unreachable!("First element in conditional chain should be an IfConditional");
@ -107,6 +106,10 @@ fn emit_ast_expr(
unreachable!("The end of an IfConditional must be a Block"); unreachable!("The end of an IfConditional must be a Block");
}; };
let Some(TemplateAstExpr::ConditionalContent { content }) = chain.next() else {
unreachable!("The end of an IfConditional must be a Block");
};
if let Some(ws) = prev_whitespace_content { if let Some(ws) = prev_whitespace_content {
eval.push(Instruction::AppendContent { eval.push(Instruction::AppendContent {
content: ws.source().clone(), content: ws.source().clone(),
@ -163,6 +166,7 @@ fn emit_ast_expr(
TemplateAstExpr::Block { .. } TemplateAstExpr::Block { .. }
| TemplateAstExpr::EndBlock | TemplateAstExpr::EndBlock
| TemplateAstExpr::IfConditional { .. } | TemplateAstExpr::IfConditional { .. }
| TemplateAstExpr::ConditionalContent { .. }
| TemplateAstExpr::ElseConditional { .. } | TemplateAstExpr::ElseConditional { .. }
| TemplateAstExpr::Invalid { .. } | TemplateAstExpr::Invalid { .. }
| TemplateAstExpr::VariableAccess { .. } => eval.push(Instruction::Abort), | TemplateAstExpr::VariableAccess { .. } => eval.push(Instruction::Abort),
@ -191,6 +195,7 @@ fn emit_expr_load(
TemplateAstExpr::EndBlock => todo!(), TemplateAstExpr::EndBlock => todo!(),
TemplateAstExpr::Block { .. } => todo!(), TemplateAstExpr::Block { .. } => todo!(),
TemplateAstExpr::IfConditional { .. } => todo!(), TemplateAstExpr::IfConditional { .. } => todo!(),
TemplateAstExpr::ConditionalContent { .. } => todo!(),
} }
} }

View file

@ -17,6 +17,8 @@ TemplateAst {
[Whitespace]"\n " (13..18), [Whitespace]"\n " (13..18),
), ),
}, },
},
ConditionalContent {
content: [ content: [
StaticContent( StaticContent(
[Content]"Hello World!" (18..30), [Content]"Hello World!" (18..30),