Add for loop
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
7182024342
commit
42e0056374
16 changed files with 775 additions and 44 deletions
185
src/emit/mod.rs
185
src/emit/mod.rs
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::ast::TemplateAstExpr;
|
||||
use crate::input::NomoInput;
|
||||
|
||||
pub struct EmitMachine {
|
||||
current_index: usize,
|
||||
labels: HashMap<LabelSlot, usize>,
|
||||
labels: BTreeMap<LabelSlot, usize>,
|
||||
}
|
||||
|
||||
impl EmitMachine {
|
||||
|
|
@ -36,12 +36,12 @@ impl EmitMachine {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct VariableSlot {
|
||||
index: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct LabelSlot {
|
||||
index: usize,
|
||||
}
|
||||
|
|
@ -70,11 +70,29 @@ pub enum Instruction {
|
|||
jump: LabelSlot,
|
||||
},
|
||||
NoOp,
|
||||
CreateIteratorFromSlotToSlot {
|
||||
iterator_slot: VariableSlot,
|
||||
iterator_source_slot: VariableSlot,
|
||||
},
|
||||
AdvanceIteratorOrJump {
|
||||
iterator_slot: VariableSlot,
|
||||
value_slot: VariableSlot,
|
||||
jump: LabelSlot,
|
||||
},
|
||||
GetIteratorEmptyOrJump {
|
||||
iterator_slot: VariableSlot,
|
||||
jump: LabelSlot,
|
||||
},
|
||||
PopScope,
|
||||
LoadFromSlotToContext {
|
||||
value_ident: NomoInput,
|
||||
value_slot: VariableSlot,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VMInstructions {
|
||||
pub labels: HashMap<LabelSlot, usize>,
|
||||
pub labels: BTreeMap<LabelSlot, usize>,
|
||||
pub instructions: Vec<Instruction>,
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +101,7 @@ pub fn emit_machine(input: crate::ast::TemplateAst<'_>) -> VMInstructions {
|
|||
|
||||
let mut machine = EmitMachine {
|
||||
current_index: 0,
|
||||
labels: HashMap::new(),
|
||||
labels: BTreeMap::new(),
|
||||
};
|
||||
|
||||
for ast in input.root() {
|
||||
|
|
@ -221,13 +239,166 @@ fn emit_ast_expr(
|
|||
eval.push(Instruction::NoOp);
|
||||
}
|
||||
}
|
||||
TemplateAstExpr::ForChain {
|
||||
for_block,
|
||||
content,
|
||||
else_block,
|
||||
else_content,
|
||||
end_block,
|
||||
} => {
|
||||
let post_for_whitespace_content;
|
||||
let label_to_else_or_empty_index = machine.reserve_label();
|
||||
let label_to_end_index = machine.reserve_label();
|
||||
let label_start_loop = machine.reserve_label();
|
||||
if let TemplateAstExpr::Block {
|
||||
prev_whitespace_content,
|
||||
expression,
|
||||
post_whitespace_content,
|
||||
} = &**for_block
|
||||
&& let TemplateAstExpr::For {
|
||||
value_ident,
|
||||
value_expression,
|
||||
} = &**expression
|
||||
{
|
||||
if let Some(ws) = prev_whitespace_content {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: ws.source().clone(),
|
||||
});
|
||||
}
|
||||
post_for_whitespace_content = post_whitespace_content;
|
||||
|
||||
eval.push(Instruction::PushScope {
|
||||
inherit_parent: true,
|
||||
});
|
||||
|
||||
let value_slot = machine.reserve_slot();
|
||||
let iterator_source_slot = machine.reserve_slot();
|
||||
let iterator_slot = machine.reserve_slot();
|
||||
|
||||
emit_expr_load(machine, eval, iterator_source_slot, value_expression);
|
||||
eval.push(Instruction::CreateIteratorFromSlotToSlot {
|
||||
iterator_source_slot,
|
||||
iterator_slot,
|
||||
});
|
||||
|
||||
eval.push(Instruction::GetIteratorEmptyOrJump {
|
||||
iterator_slot,
|
||||
jump: label_to_else_or_empty_index,
|
||||
});
|
||||
|
||||
machine.assign_label(label_start_loop, eval.len());
|
||||
eval.push(Instruction::AdvanceIteratorOrJump {
|
||||
iterator_slot,
|
||||
value_slot,
|
||||
jump: label_to_end_index,
|
||||
});
|
||||
|
||||
eval.push(Instruction::LoadFromSlotToContext {
|
||||
value_slot,
|
||||
value_ident: value_ident.source(),
|
||||
});
|
||||
} else {
|
||||
panic!("For block should be a for block");
|
||||
};
|
||||
|
||||
if let Some(ws) = post_for_whitespace_content {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: ws.source().clone(),
|
||||
});
|
||||
}
|
||||
|
||||
for content in content {
|
||||
emit_ast_expr(machine, eval, content);
|
||||
}
|
||||
|
||||
let end_of_content_jump = eval.len();
|
||||
eval.push(Instruction::Jump {
|
||||
jump: label_start_loop,
|
||||
});
|
||||
|
||||
let has_else = else_block.is_some();
|
||||
|
||||
if let Some(TemplateAstExpr::Block {
|
||||
prev_whitespace_content,
|
||||
expression,
|
||||
post_whitespace_content,
|
||||
}) = else_block.as_deref()
|
||||
&& let TemplateAstExpr::ForElse = &**expression
|
||||
{
|
||||
if let Some(ws) = prev_whitespace_content {
|
||||
eval.insert(
|
||||
end_of_content_jump.saturating_sub(1),
|
||||
Instruction::AppendContent {
|
||||
content: ws.source().clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
machine.assign_label(label_to_else_or_empty_index, eval.len());
|
||||
|
||||
if let Some(ws) = post_whitespace_content {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: ws.source().clone(),
|
||||
});
|
||||
}
|
||||
|
||||
for content in else_content
|
||||
.as_ref()
|
||||
.expect("If there is a for block, there should be for content (even if empty)")
|
||||
{
|
||||
emit_ast_expr(machine, eval, content);
|
||||
}
|
||||
}
|
||||
|
||||
let post_end_whitespace_content;
|
||||
|
||||
if let TemplateAstExpr::Block {
|
||||
prev_whitespace_content,
|
||||
expression,
|
||||
post_whitespace_content,
|
||||
} = &**end_block
|
||||
&& let TemplateAstExpr::EndBlock = &**expression
|
||||
{
|
||||
post_end_whitespace_content = post_whitespace_content;
|
||||
|
||||
if let Some(ws) = prev_whitespace_content {
|
||||
if has_else {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: ws.source().clone(),
|
||||
});
|
||||
} else {
|
||||
eval.insert(
|
||||
end_of_content_jump.saturating_sub(1),
|
||||
Instruction::AppendContent {
|
||||
content: ws.source().clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if !has_else {
|
||||
machine.assign_label(label_to_else_or_empty_index, eval.len());
|
||||
}
|
||||
|
||||
machine.assign_label(label_to_end_index, eval.len());
|
||||
|
||||
eval.push(Instruction::PopScope);
|
||||
|
||||
if let Some(ws) = post_end_whitespace_content {
|
||||
eval.push(Instruction::AppendContent {
|
||||
content: ws.source().clone(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
panic!("End block should be an endblock");
|
||||
}
|
||||
}
|
||||
|
||||
TemplateAstExpr::Block { .. }
|
||||
| TemplateAstExpr::EndBlock
|
||||
| TemplateAstExpr::IfConditional { .. }
|
||||
| TemplateAstExpr::ConditionalContent { .. }
|
||||
| TemplateAstExpr::ElseConditional { .. }
|
||||
| TemplateAstExpr::ForChain { .. }
|
||||
| TemplateAstExpr::For { .. }
|
||||
| TemplateAstExpr::ForElse
|
||||
| TemplateAstExpr::Invalid { .. }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue