Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions crates/zapcode-core/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,16 +239,43 @@ impl Vm {
locals.push(args.get(i).cloned().unwrap_or(Value::Undefined));
}
ParamPattern::Rest(_) => {
let rest: Vec<Value> = args[i..].to_vec();
let rest: Vec<Value> = args.get(i..).map(|s| s.to_vec()).unwrap_or_default();
locals.push(Value::Array(rest));
}
ParamPattern::DefaultValue { .. } => {
let val = args.get(i).cloned().unwrap_or(Value::Undefined);
// Keep Undefined so the compiler-emitted default init can fire
locals.push(val);
}
_ => {
locals.push(args.get(i).cloned().unwrap_or(Value::Undefined));
// Destructuring params bind one local per name — matching the
// per-name locals declared in `compile_function_def`. Push a
// value for each *named* slot, in declaration order.
ParamPattern::ObjectDestructure(fields) => {
let arg = args.get(i).cloned().unwrap_or(Value::Undefined);
for field in fields {
let val = match &arg {
Value::Object(map) => map
.get(field.key.as_str())
.cloned()
.unwrap_or(Value::Undefined),
_ => Value::Undefined,
};
locals.push(val);
}
}
ParamPattern::ArrayDestructure(elems) => {
let arg = args.get(i).cloned().unwrap_or(Value::Undefined);
for (j, elem) in elems.iter().enumerate() {
// Only `Some(Ident)` slots declare a local (holes and
// unsupported nested patterns declare nothing) — stay aligned.
if let Some(ParamPattern::Ident(_)) = elem {
let val = match &arg {
Value::Array(a) => a.get(j).cloned().unwrap_or(Value::Undefined),
_ => Value::Undefined,
};
locals.push(val);
}
}
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions crates/zapcode-core/tests/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,24 @@ fn test_rest_params() {
// Rest params create an array — need array indexing to work
assert_eq!(result, Value::Int(15));
}

// ── destructuring parameters (regression: bound whole arg, not elements) ─────

#[test]
fn test_array_destructure_param() {
let r = eval_ts("const f = ([a, b]) => a + b; f([10, 20])").unwrap();
assert_eq!(r, Value::Int(30));
}

#[test]
fn test_object_destructure_param() {
let r = eval_ts("const f = ({ x, y }) => x * y; f({ x: 6, y: 7 })").unwrap();
assert_eq!(r, Value::Int(42));
}

#[test]
fn test_entries_map_destructure() {
let r =
eval_ts("Object.entries({ a: 1, b: 2 }).map(([k, v]) => `${k}${v}`).join(',')").unwrap();
assert_eq!(r, Value::String("a1,b2".into()));
}