Add first working pipeline of parse -> ast -> instr -> render
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
f5050e369e
commit
1ea15f0e49
5 changed files with 234 additions and 6 deletions
139
src/emit/mod.rs
Normal file
139
src/emit/mod.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use crate::ast::TemplateAstExpr;
|
||||
|
||||
pub struct EmitMachine {
|
||||
current_index: usize,
|
||||
}
|
||||
|
||||
impl EmitMachine {
|
||||
fn reserve_slot(&mut self) -> VariableSlot {
|
||||
VariableSlot {
|
||||
index: {
|
||||
let val = self.current_index;
|
||||
self.current_index += 1;
|
||||
val
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct VariableSlot {
|
||||
index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Instruction {
|
||||
AppendContent { content: String },
|
||||
LoadFromContextToSlot { name: String, slot: VariableSlot },
|
||||
EmitFromSlot { slot: VariableSlot },
|
||||
PushScope { inherit_parent: bool },
|
||||
Abort,
|
||||
}
|
||||
|
||||
pub fn emit_machine(input: crate::ast::TemplateAst<'_>) -> Vec<Instruction> {
|
||||
let mut eval = vec![];
|
||||
|
||||
let mut machine = EmitMachine { current_index: 0 };
|
||||
|
||||
for ast in input.root() {
|
||||
emit_ast_expr(&mut machine, &mut eval, ast);
|
||||
}
|
||||
|
||||
eval
|
||||
}
|
||||
|
||||
fn emit_ast_expr(machine: &mut EmitMachine, eval: &mut Vec<Instruction>, ast: &TemplateAstExpr<'_>) {
|
||||
match ast {
|
||||
TemplateAstExpr::StaticContent(template_token) => {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: template_token.source().to_string(),
|
||||
});
|
||||
}
|
||||
TemplateAstExpr::Interpolation {
|
||||
prev_whitespace,
|
||||
wants_output,
|
||||
expression,
|
||||
post_whitespace,
|
||||
} => {
|
||||
if let Some(ws) = prev_whitespace {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: ws.source().to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let emit_slot = machine.reserve_slot();
|
||||
emit_expr(machine, eval, emit_slot, expression);
|
||||
|
||||
if wants_output.is_some() {
|
||||
eval.push(Instruction::EmitFromSlot { slot: emit_slot });
|
||||
}
|
||||
|
||||
if let Some(ws) = post_whitespace {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: ws.source().to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
TemplateAstExpr::Invalid { .. } | TemplateAstExpr::VariableAccess { .. } => {
|
||||
eval.push(Instruction::Abort)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_expr(
|
||||
machine: &mut EmitMachine,
|
||||
eval: &mut Vec<Instruction>,
|
||||
emit_slot: VariableSlot,
|
||||
expression: &TemplateAstExpr<'_>,
|
||||
) {
|
||||
match expression {
|
||||
TemplateAstExpr::VariableAccess(template_token) => {
|
||||
eval.push(Instruction::LoadFromContextToSlot {
|
||||
name: template_token.source().to_string(),
|
||||
slot: emit_slot,
|
||||
});
|
||||
}
|
||||
TemplateAstExpr::Invalid { .. } => eval.push(Instruction::Abort),
|
||||
TemplateAstExpr::StaticContent { .. } | TemplateAstExpr::Interpolation { .. } => {
|
||||
unreachable!("Invalid AST here")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::emit::emit_machine;
|
||||
|
||||
#[test]
|
||||
fn check_simple_variable_interpolation() {
|
||||
let input = "Hello {{= world }}";
|
||||
|
||||
let parsed = crate::parser::parse(input).unwrap();
|
||||
|
||||
let ast = crate::ast::parse(parsed.tokens()).unwrap();
|
||||
|
||||
let emit = emit_machine(ast);
|
||||
|
||||
insta::assert_debug_snapshot!(emit, @r#"
|
||||
[
|
||||
AppendContent {
|
||||
content: "Hello",
|
||||
},
|
||||
AppendContent {
|
||||
content: " ",
|
||||
},
|
||||
LoadFromContextToSlot {
|
||||
name: "world",
|
||||
slot: VariableSlot {
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
EmitFromSlot {
|
||||
slot: VariableSlot {
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
]
|
||||
"#);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue