Add for loop

Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
Marcel Müller 2026-03-11 18:09:58 +01:00
parent 7182024342
commit 42e0056374
16 changed files with 775 additions and 44 deletions

View file

@ -6,7 +6,9 @@ use thiserror::Error;
use crate::Context;
use crate::emit::Instruction;
use crate::emit::VMInstructions;
use crate::emit::VariableSlot;
use crate::input::NomoInput;
use crate::value::NomoValue;
#[derive(Debug, Error, Display)]
pub enum EvaluationError {
@ -21,10 +23,59 @@ pub enum EvaluationError {
LabelNotFound,
}
struct Scope {
stack: Vec<HashMap<String, NomoValue>>,
slots: HashMap<VariableSlot, NomoValue>,
}
impl Scope {
fn insert_into_slot(&mut self, slot: VariableSlot, value: NomoValue) {
self.slots.insert(slot, value);
}
fn get(&self, slot: &VariableSlot) -> &NomoValue {
self.slots.get(slot).expect("All slot loads must be valid")
}
fn get_mut(&mut self, slot: &VariableSlot) -> &mut NomoValue {
self.slots
.get_mut(slot)
.expect("All slot loads must be valid")
}
fn push_scope(&mut self) {
self.stack.push(Default::default());
}
fn pop_scope(&mut self) {
self.stack.pop();
}
fn insert_into_scope(&mut self, ident: &NomoInput, value: NomoValue) {
self.stack
.last_mut()
.unwrap()
.insert(ident.to_string(), value);
}
fn get_scoped(&self, name: &NomoInput) -> Option<&NomoValue> {
self.stack
.iter()
.rev()
.find_map(|scope| scope.get(name.as_str()))
}
}
#[allow(
clippy::unnecessary_to_owned,
reason = "We cannot do the suggested way as the lifetimes would not match up"
)]
pub fn execute(vm: &VMInstructions, global_context: &Context) -> Result<String, EvaluationError> {
let mut output = String::new();
let mut scopes: HashMap<crate::emit::VariableSlot, serde_json::Value> = HashMap::new();
let mut scopes = Scope {
stack: vec![global_context.values().clone()],
slots: HashMap::new(),
};
let mut ip = 0;
loop {
@ -38,21 +89,22 @@ pub fn execute(vm: &VMInstructions, global_context: &Context) -> Result<String,
Instruction::NoOp => (),
Instruction::AppendContent { content } => output.push_str(content),
Instruction::LoadFromContextToSlot { name, slot } => {
let value = global_context
.values
.get(name.as_str())
let value = scopes
.get_scoped(name)
.ok_or(EvaluationError::UnknownVariable(name.clone()))?;
scopes.insert(*slot, value.clone());
scopes.insert_into_slot(*slot, value.clone());
}
Instruction::EmitFromSlot { slot } => {
let value = scopes.get(slot).unwrap().as_str().unwrap();
let value = scopes.get(slot).as_str().unwrap();
output.push_str(value);
}
Instruction::PushScope { inherit_parent: _ } => todo!(),
Instruction::PushScope { inherit_parent: _ } => {
scopes.push_scope();
}
Instruction::Abort => return Err(EvaluationError::ExplicitAbort),
Instruction::JumpIfNotTrue { emit_slot, jump } => {
let dont_jump = scopes.get(emit_slot).unwrap().as_bool().unwrap();
let dont_jump = scopes.get(emit_slot).as_bool().unwrap();
if dont_jump {
// We are done
} else {
@ -72,6 +124,59 @@ pub fn execute(vm: &VMInstructions, global_context: &Context) -> Result<String,
ip = *new_ip;
continue;
}
Instruction::CreateIteratorFromSlotToSlot {
iterator_slot,
iterator_source_slot,
} => {
let value = scopes.get(iterator_source_slot).as_array().unwrap();
scopes.insert_into_slot(
*iterator_slot,
NomoValue::Iterator {
value: Box::new(value.to_vec().into_iter()),
},
);
}
Instruction::AdvanceIteratorOrJump {
iterator_slot,
value_slot,
jump,
} => {
let iterator = scopes.get_mut(iterator_slot).as_iterator_mut().unwrap();
if let Some(value) = iterator.next() {
scopes.insert_into_slot(*value_slot, value);
} else {
let Some(new_ip) = vm.labels.get(jump) else {
return Err(EvaluationError::LabelNotFound);
};
ip = *new_ip;
}
}
Instruction::GetIteratorEmptyOrJump {
iterator_slot,
jump,
} => {
let iterator = scopes.get(iterator_slot).as_iterator().unwrap();
let (min, _) = iterator.size_hint();
if min == 0 {
let Some(new_ip) = vm.labels.get(jump) else {
return Err(EvaluationError::LabelNotFound);
};
ip = *new_ip;
}
}
Instruction::PopScope => scopes.pop_scope(),
Instruction::LoadFromSlotToContext {
value_ident,
value_slot,
} => {
let value = scopes.get(value_slot).clone();
scopes.insert_into_scope(value_ident, value);
}
}
ip += 1;