Skip to content

Compiler support for destructuring assignment (let [a, b, c] = expr) #2496

@Jeckerson

Description

@Jeckerson

Summary

The parser (php-zephir-parser) now supports destructuring assignment syntax:

let [a, b, c] = arr;
let [a, , c] = arr;    // skip slots

See: zephir-lang/php-zephir-parser#186 (closes zephir-lang/php-zephir-parser#18)

The compiler needs a corresponding handler to translate this AST node into C code.

AST Shape

The parser emits the following AST node inside a let statement's assignments array:

{
  "assign-type": "destructure",
  "operator": "assign",
  "variables": [
    { "type": "variable", "value": "a", "file": "...", "line": 1, "char": 1 },
    { "type": "variable", "value": "b", "file": "...", "line": 1, "char": 1 },
    { "type": "variable", "value": "c", "file": "...", "line": 1, "char": 1 }
  ],
  "expr": { "type": "variable", "value": "arr", ... },
  "file": "...",
  "line": 1,
  "char": 1
}
  • variables is a flat array of variable name objects (type "variable", with a "value" key holding the name string)
  • Skipped slots (let [a, , c] = arr;) have null entries in the variables array
  • expr is the right-hand side expression (any valid xx_assign_expr)

Required Compiler Changes

1. src/Statements/LetStatement.php

Add 'destructure' to the match expression (line ~45) so the symbol table lookup doesn't crash:

$symbolVariable = match ($assignment['assign-type']) {
    // ...existing cases...
    'destructure' => null,  // handled specially below
    // ...
};

The destructure case needs special handling: iterate $assignment['variables'], skip null entries, and for each variable name call $compilationContext->symbolTable->getVariableForWrite(...).

2. src/Statements/Let/AssignmentFactory.php

Add 'destructure' to the ASSIGNMENT_MAP:

private const ASSIGNMENT_MAP = [
    // ...existing entries...
    'destructure' => Destructure::class,
];

And add a case to the switch in process():

case 'destructure':
    $handler->assign($assignment, $resolvedExpr, $compilationContext);
    break;

3. New src/Statements/Let/Destructure.php

Create a new handler class that generates C code equivalent to indexed array fetches:

let [a, b, c] = arr;

Should generate C equivalent to:

zephir_array_fetch_long(&a, arr, 0, PH_NOISY_CC);
zephir_array_fetch_long(&b, arr, 1, PH_NOISY_CC);
zephir_array_fetch_long(&c, arr, 2, PH_NOISY_CC);

For skipped slots (null in the variables array), simply skip that index.

The handler should:

  1. Compile the RHS expression once into a temp variable
  2. Iterate the variables array with index
  3. For each non-null entry, emit a zephir_array_fetch_long() call using the index
  4. Handle type coercion based on the target variable's declared type

4. src/Passes/LocalContextPass.php and src/Passes/SkipVariantInit.php

These passes inspect assign-type for optimization. Add 'destructure' handling so the passes don't skip or mishandle destructured variables.

Out of Scope

  • Nested destructuring (let [[a, b], c] = arr;) — not supported by the parser
  • Keyed/object destructuring (let {key1, key2} = obj;) — not supported by the parser
  • These can be added incrementally in follow-up PRs

Related

Metadata

Metadata

Assignees

Labels

nfrNew Feature Request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions