diff --git a/fix/src/runtime.rs b/fix/src/runtime.rs index 0cc0bb4..847c1ac 100644 --- a/fix/src/runtime.rs +++ b/fix/src/runtime.rs @@ -47,6 +47,7 @@ pub struct Runtime { fuel: usize, error_contexts: Stack<8192, ErrorFrame>, arena: Arena]>, + force_mode: ForceMode, } impl Runtime { @@ -81,6 +82,7 @@ impl Runtime { fuel: Self::DEFAULT_FUEL_AMOUNT, error_contexts: Stack::new(), arena, + force_mode: ForceMode::default(), }) } diff --git a/fix/src/runtime/vm.rs b/fix/src/runtime/vm.rs index 9c7f57b..24d65e1 100644 --- a/fix/src/runtime/vm.rs +++ b/fix/src/runtime/vm.rs @@ -578,13 +578,181 @@ impl Runtime { let n = self.read_u16() as usize; let _span_id = self.read_u32(); let keys = self.read_attr_keys(n); - todo!("implement Select (force + lookup)"); + + // Move dynamic key values from stack to temp_stack + let dyn_count = keys + .iter() + .filter(|k| matches!(k, SelectKeyData::Dynamic)) + .count(); + let temp_base = self.arena.mutate_root(|_, root| { + let base = root.temp_stack.len(); + for _ in 0..dyn_count { + let v = root.pop_stack(); + root.temp_stack.push(v); + } + base + }); + + // Force the expr (now TOS) + self.force_tos(); + + for (i, key) in keys.iter().enumerate() { + let key_sid = match key { + &SelectKeyData::Static(sid) => sid, + SelectKeyData::Dynamic => { + self.arena.mutate_root(|_, root| { + let v = + root.temp_stack.pop().expect("missing dynamic key"); + root.push_stack(v); + }); + self.force_tos(); + let key_data: std::result::Result = + self.arena.mutate_root(|_, root| { + let v = root.pop_stack(); + if let Some(sid) = v.as_inline::() { + Ok(sid) + } else if let Some(ns) = v.as_gc::() { + Err(ns.as_str().to_owned()) + } else { + panic!("dynamic select key must be a string") + } + }); + match key_data { + Ok(sid) => sid, + Err(s) => StringId(self.strings.get_or_intern(&s)), + } + } + }; + + let result = self.arena.mutate_root(|_, root| { + let val = root.pop_stack(); + let Some(attrset) = val.as_gc::>() else { + return Err(vm_err( + "value is not a set while a set was expected", + )); + }; + match attrset.lookup(key_sid) { + Some(v) => { + root.push_stack(v); + Ok(true) + } + None => Ok(false), + } + }); + + match result { + Err(e) => { + self.arena + .mutate_root(|_, root| root.temp_stack.truncate(temp_base)); + return Runtime::handle_vm_error(self, e); + } + Ok(false) => { + self.arena + .mutate_root(|_, root| root.temp_stack.truncate(temp_base)); + let name = + self.strings.resolve(key_sid.0).unwrap_or("«unknown»"); + return Runtime::handle_vm_error( + self, + vm_err(format!("attribute '{name}' missing")), + ); + } + Ok(true) => { + if i < n - 1 { + self.force_tos(); + } + } + } + } + + self.arena + .mutate_root(|_, root| root.temp_stack.truncate(temp_base)); } SelectDefault => { let n = self.read_u16() as usize; let _span_id = self.read_u32(); let keys = self.read_attr_keys(n); - todo!("implement SelectDefault (force + lookup with default)"); + + let dyn_count = keys + .iter() + .filter(|k| matches!(k, SelectKeyData::Dynamic)) + .count(); + let temp_base = self.arena.mutate_root(|_, root| { + let base = root.temp_stack.len(); + // Default value is on top of the stack (pushed last by codegen) + let default_val = root.pop_stack(); + root.temp_stack.push(default_val); + // Then dynamic keys + for _ in 0..dyn_count { + let v = root.pop_stack(); + root.temp_stack.push(v); + } + base + }); + + // Force the expr (now TOS) + self.force_tos(); + + let mut use_default = false; + for (i, key) in keys.iter().enumerate() { + let key_sid = match key { + &SelectKeyData::Static(sid) => sid, + SelectKeyData::Dynamic => { + self.arena.mutate_root(|_, root| { + let v = + root.temp_stack.pop().expect("missing dynamic key"); + root.push_stack(v); + }); + self.force_tos(); + let key_data: std::result::Result = + self.arena.mutate_root(|_, root| { + let v = root.pop_stack(); + if let Some(sid) = v.as_inline::() { + Ok(sid) + } else if let Some(ns) = v.as_gc::() { + Err(ns.as_str().to_owned()) + } else { + panic!("dynamic select key must be a string") + } + }); + match key_data { + Ok(sid) => sid, + Err(s) => StringId(self.strings.get_or_intern(&s)), + } + } + }; + + let found = self.arena.mutate_root(|_, root| { + let val = root.pop_stack(); + if let Some(attrset) = val.as_gc::>() { + if let Some(v) = attrset.lookup(key_sid) { + root.push_stack(v); + return true; + } + } + // Not a set or key missing → use default + false + }); + + if !found { + use_default = true; + break; + } + + if i < n - 1 { + self.force_tos(); + } + } + + if use_default { + self.arena.mutate_root(|_, root| { + let default_val = root.temp_stack[temp_base]; + root.temp_stack.truncate(temp_base); + root.push_stack(default_val); + }); + } else { + self.arena + .mutate_root(|_, root| root.temp_stack.truncate(temp_base)); + } } HasAttr => { let n = self.read_u16() as usize;