implement Select & SelectDefault

This commit is contained in:
2026-03-30 18:23:21 +08:00
parent 6567ed4058
commit e82369695c
2 changed files with 172 additions and 2 deletions
+170 -2
View File
@@ -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<StringId, String> =
self.arena.mutate_root(|_, root| {
let v = root.pop_stack();
if let Some(sid) = v.as_inline::<StringId>() {
Ok(sid)
} else if let Some(ns) = v.as_gc::<NixString>() {
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::<AttrSet<'_>>() 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<StringId, String> =
self.arena.mutate_root(|_, root| {
let v = root.pop_stack();
if let Some(sid) = v.as_inline::<StringId>() {
Ok(sid)
} else if let Some(ns) = v.as_gc::<NixString>() {
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::<AttrSet<'_>>() {
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;