Skip to content

Instantly share code, notes, and snippets.

@thekid
Created December 28, 2025 10:15
Show Gist options
  • Select an option

  • Save thekid/5f85c8ec6e14644b06cb9b100644529e to your computer and use it in GitHub Desktop.

Select an option

Save thekid/5f85c8ec6e14644b06cb9b100644529e to your computer and use it in GitHub Desktop.
Is operator with binding rest
diff --git a/src/main/php/lang/ast/syntax/php/IsOperator.class.php b/src/main/php/lang/ast/syntax/php/IsOperator.class.php
index 7e34fc1..99c40b3 100755
--- a/src/main/php/lang/ast/syntax/php/IsOperator.class.php
+++ b/src/main/php/lang/ast/syntax/php/IsOperator.class.php
@@ -14,6 +14,7 @@ use lang\ast\nodes\{
MatchExpression,
OffsetExpression,
ScopeExpression,
+ UnpackExpression,
Variable
};
use lang\ast\syntax\Extension;
@@ -80,9 +81,17 @@ class IsOperator implements Extension {
$r= new IsArrayStructure();
$parse->forward();
if (']' !== $parse->token->value) do {
+
+ // Bind rest of array via `... $rest` vs. discarding it
if ('...' === $parse->token->value) {
- $r->rest= true;
$parse->forward();
+ if ('variable' === $parse->token->kind) {
+ $parse->forward();
+ $r->rest= new Variable($parse->token->value);
+ $parse->forward();
+ } else {
+ $r->rest= true;
+ }
break;
}
@@ -251,14 +260,34 @@ class IsOperator implements Extension {
new Literal((string)sizeof($pattern->patterns))
)
);
+ $matched= new ArrayLiteral([]);
+ $null= new Literal('null');
foreach ($pattern->patterns as $key => $p) {
$offset= new Literal((string)$key);
+ $matched->values[]= [$offset, $null];
$compound= new BinaryExpression($compound, '&&', new BinaryExpression(
new InvokeExpression(new Literal('array_key_exists'), [$offset, $use]),
'&&',
$match($codegen, new OffsetExpression($use, $offset), $p),
));
}
+
+ // array_diff_key() removes entries for keys in the second argument,
+ // unpacking re-keys lists but keeps map key-value pairs intact.
+ if ($pattern->rest instanceof Variable) {
+ $compound= new BinaryExpression($compound, '&&', new Braced(new BinaryExpression(
+ new Braced(new Assignment($pattern->rest, '=', $pattern->patterns
+ ? new ArrayLiteral([[
+ null,
+ new UnpackExpression(new InvokeExpression(new Literal('array_diff_key'), [$use, $matched]))
+ ]])
+ : $use
+ )),
+ '||',
+ new Literal('true')
+ )));
+ }
+
return $compound;
}
diff --git a/src/test/php/lang/ast/syntax/php/unittest/VariableBindingTest.class.php b/src/test/php/lang/ast/syntax/php/unittest/VariableBindingTest.class.php
index 3c02f5e..8ec2077 100755
--- a/src/test/php/lang/ast/syntax/php/unittest/VariableBindingTest.class.php
+++ b/src/test/php/lang/ast/syntax/php/unittest/VariableBindingTest.class.php
@@ -31,6 +31,13 @@ class VariableBindingTest extends EmittingTest {
yield ['[1, [1]] is [1, $rest] ? $rest : null', [1]];
yield ['[1, [2], "three"] is [1, mixed, $rest] ? $rest : null', 'three'];
+ yield ['[] is [...$rest] ? $rest : null', []];
+ yield ['[1] is [int, ...$rest] ? $rest : null', []];
+ yield ['[1, 2, 3] is [...$rest] ? $rest : null', [1, 2, 3]];
+ yield ['[1, 2, 3] is [1, ...$rest] ? $rest : null', [2, 3]];
+ yield ['[1, [1]] is [int, ...$rest] ? $rest : null', [[1]]];
+ yield ['["one" => 1] is ["one" => 1, ...$rest] ? $rest : null', []];
+ yield ['["one" => 1, "two" => 2] is ["one" => 1, ...$rest] ? $rest : null', ['two' => 2]];
yield ['["one" => 1, "two" => 2] is ["one" => 1, "two" => $t] ? $t : null', 2];
yield ['["one" => 0, "two" => 2] is ["one" => 1, "two" => $t] ? $t : null', null];
@@ -57,12 +64,13 @@ class VariableBindingTest extends EmittingTest {
}', explode(' ', $input)));
}
- #[Test, Values([['go north', 'north'], ['go south', 'south'], ['go home', null]])]
+ #[Test, Values([['go north', 'north'], ['go south', 'south'], ['go home', null], ['drop it now', ['it', 'now']]])]
public function bind_with_subpattern($input, $expected) {
Assert::equals($expected, $this->run('class %T {
public function run($command) {
return match ($command) is {
["go", $direction & ("north"|"south"|"east"|"west")] => $direction,
+ ["drop", ...$objects] => $objects,
default => null,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment