Add parsing for conditionals (cont.)

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-08 15:06:29 +01:00
parent 974086a877
commit 8afc2d1bde
29 changed files with 994 additions and 746 deletions

View file

@ -24,11 +24,24 @@ pub struct VariableSlot {
#[derive(Debug)]
pub enum Instruction {
AppendContent { content: NomoInput },
LoadFromContextToSlot { name: NomoInput, slot: VariableSlot },
EmitFromSlot { slot: VariableSlot },
PushScope { inherit_parent: bool },
AppendContent {
content: NomoInput,
},
LoadFromContextToSlot {
name: NomoInput,
slot: VariableSlot,
},
EmitFromSlot {
slot: VariableSlot,
},
PushScope {
inherit_parent: bool,
},
Abort,
JumpIfNotTrue {
emit_slot: VariableSlot,
jump: isize,
},
}
pub fn emit_machine(input: crate::ast::TemplateAst<'_>) -> Vec<Instruction> {
@ -55,53 +68,110 @@ fn emit_ast_expr(
});
}
TemplateAstExpr::Interpolation {
prev_whitespace_content: prev_whitespace,
wants_output,
prev_whitespace_content,
expression,
post_whitespace_content: post_whitespace,
post_whitespace_content,
} => {
if let Some(ws) = prev_whitespace {
if let Some(ws) = prev_whitespace_content {
eval.push(Instruction::AppendContent {
content: ws.source().clone(),
});
}
let emit_slot = machine.reserve_slot();
emit_expr(machine, eval, emit_slot, expression);
emit_expr_load(machine, eval, emit_slot, expression);
eval.push(Instruction::EmitFromSlot { slot: emit_slot });
if let Some(ws) = post_whitespace {
if let Some(ws) = post_whitespace_content {
eval.push(Instruction::AppendContent {
content: ws.source().clone(),
});
}
}
TemplateAstExpr::Invalid { .. } | TemplateAstExpr::VariableAccess { .. } => {
eval.push(Instruction::Abort)
TemplateAstExpr::ConditionalChain { chain } => {
let mut chain = chain.iter();
let Some(TemplateAstExpr::IfConditional {
if_block: expression,
content,
end_block,
}) = chain.next()
else {
unreachable!("First element in conditional chain should be an IfConditional");
};
let TemplateAstExpr::Block {
prev_whitespace_content,
expression,
post_whitespace_content,
} = expression.as_ref()
else {
unreachable!("The end of an IfConditional must be a Block");
};
if let Some(ws) = prev_whitespace_content {
eval.push(Instruction::AppendContent {
content: ws.source().clone(),
});
}
let emit_slot = machine.reserve_slot();
emit_expr_load(machine, eval, emit_slot, expression);
let index = eval.len();
eval.push(Instruction::JumpIfNotTrue {
emit_slot,
jump: isize::MAX,
});
if let Some(ws) = post_whitespace_content {
eval.push(Instruction::AppendContent {
content: ws.source().clone(),
});
}
for ast in content {
emit_ast_expr(machine, eval, ast);
}
let TemplateAstExpr::Block {
prev_whitespace_content,
post_whitespace_content,
..
} = end_block.as_ref()
else {
unreachable!("The end of an IfConditional must be a Block");
};
if let Some(ws) = prev_whitespace_content {
eval.push(Instruction::AppendContent {
content: ws.source().clone(),
});
}
let jump = eval.len() - index - 1;
eval[index] = Instruction::JumpIfNotTrue {
emit_slot,
jump: jump as isize,
};
if let Some(ws) = post_whitespace_content {
eval.push(Instruction::AppendContent {
content: ws.source().clone(),
});
}
}
TemplateAstExpr::ConditionalChain { chain } => todo!(),
TemplateAstExpr::ElseConditional { expression } => todo!(),
TemplateAstExpr::Action {
prev_whitespace_content,
expression,
post_whitespace_content,
} => todo!(),
TemplateAstExpr::EndBlock => todo!(),
TemplateAstExpr::Block {
prev_whitespace_content,
expression,
post_whitespace_content,
} => todo!(),
TemplateAstExpr::IfConditional {
expression,
content,
end_block,
} => todo!(),
TemplateAstExpr::Block { .. }
| TemplateAstExpr::EndBlock
| TemplateAstExpr::IfConditional { .. }
| TemplateAstExpr::ElseConditional { .. }
| TemplateAstExpr::Invalid { .. }
| TemplateAstExpr::VariableAccess { .. } => eval.push(Instruction::Abort),
}
}
fn emit_expr(
machine: &mut EmitMachine,
fn emit_expr_load(
_machine: &mut EmitMachine,
eval: &mut Vec<Instruction>,
emit_slot: VariableSlot,
expression: &TemplateAstExpr<'_>,
@ -117,24 +187,11 @@ fn emit_expr(
TemplateAstExpr::StaticContent { .. } | TemplateAstExpr::Interpolation { .. } => {
unreachable!("Invalid AST here")
}
TemplateAstExpr::ConditionalChain { chain } => todo!(),
TemplateAstExpr::ElseConditional { expression } => todo!(),
TemplateAstExpr::Action {
prev_whitespace_content,
expression,
post_whitespace_content,
} => todo!(),
TemplateAstExpr::ConditionalChain { .. } => todo!(),
TemplateAstExpr::ElseConditional { .. } => todo!(),
TemplateAstExpr::EndBlock => todo!(),
TemplateAstExpr::Block {
prev_whitespace_content,
expression,
post_whitespace_content,
} => todo!(),
TemplateAstExpr::IfConditional {
expression,
content,
end_block,
} => todo!(),
TemplateAstExpr::Block { .. } => todo!(),
TemplateAstExpr::IfConditional { .. } => todo!(),
}
}
@ -155,13 +212,13 @@ mod tests {
insta::assert_debug_snapshot!(emit, @r#"
[
AppendContent {
content: "Hello",
content: "Hello" (0..5),
},
AppendContent {
content: " ",
content: " " (5..6),
},
LoadFromContextToSlot {
name: "world",
name: "world" (10..15),
slot: VariableSlot {
index: 0,
},