Add support for destructuring def

This commit introduces the `DefDestructure` bound kind and modifies the
binder, analyzer, type checker, and VM to support destructuring in `def`
statements. This allows for pattern matching on the right-hand side of a
`def` to bind multiple variables.

The parser has been updated to accept patterns in `def` statements. The
binder now handles `UntypedKind::Def` with a `target` pattern, rather
than a simple `name`. This enables destructuring.

The `Gemini.md` documentation has been updated to include a new rule for
incremental development.
This commit is contained in:
Michael Schimmel
2026-02-24 08:40:45 +01:00
parent eeb6621280
commit 252b725677
13 changed files with 290 additions and 117 deletions
+25 -9
View File
@@ -281,6 +281,18 @@ impl VM {
globals[idx] = val.clone();
Ok(val)
}
BoundKind::DefDestructure { pattern, value } => {
let val = self.eval_internal(obs, value)?;
let mut offset = 0;
// Destructuring works on tuples/vectors, or single values wrapped in a slice
if let Some(vals) = val.as_slice() {
self.unpack(pattern, vals, &mut offset)?;
} else {
self.unpack(pattern, &[val.clone()], &mut offset)?;
}
Ok(val)
}
BoundKind::Get { addr, .. } => self.get_value(*addr),
BoundKind::Set { addr, value } => {
let val = self.eval_internal(obs, value)?;
@@ -622,9 +634,7 @@ impl VM {
fn flatten_value(val: Value, into: &mut Vec<Value>) {
if let Some(values) = val.as_slice() {
for item in values.iter() {
Self::flatten_value(item.clone(), into);
}
into.extend_from_slice(values);
} else {
into.push(val);
}
@@ -654,12 +664,7 @@ impl VM {
into: &mut Vec<Value>,
) -> Result<(), String> {
for e in elements {
match &e.kind {
BoundKind::Tuple { elements: sub } => {
self.eval_and_flatten_internal(obs, sub, into)?
}
_ => into.push(self.eval_internal(obs, e)?),
}
into.push(self.eval_internal(obs, e)?);
}
Ok(())
}
@@ -685,6 +690,17 @@ impl VM {
}
Ok(())
}
BoundKind::DefGlobal { global_index, .. } => {
let val = values.get(*offset).cloned().unwrap_or(Value::Void);
*offset += 1;
let idx = *global_index as usize;
let mut globals = self.globals.borrow_mut();
if idx >= globals.len() {
globals.resize(idx + 1, Value::Void);
}
globals[idx] = val;
Ok(())
}
BoundKind::Tuple { elements } => {
if let Some(sub_values) = values.get(*offset).and_then(|v| v.as_slice()) {
*offset += 1;