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 187c2ab..9165d13 100755 --- a/src/main/php/lang/ast/syntax/php/IsOperator.class.php +++ b/src/main/php/lang/ast/syntax/php/IsOperator.class.php @@ -284,31 +284,37 @@ public function setup($language, $emitter) { } return $compound; } else if ($pattern instanceof IsArrayStructure) { - $compound= new BinaryExpression( - new InvokeExpression(new Literal('is_array'), [$init]), - '&&', - new BinaryExpression( - new InvokeExpression(new Literal('sizeof'), [$use]), - $pattern->rest ? '>=' : '===', - new Literal((string)sizeof($pattern->patterns)) - ) + $size= new BinaryExpression( + new InvokeExpression(new Literal('sizeof'), [$use]), + $pattern->rest ? '>=' : '===', + new Literal((string)sizeof($pattern->patterns)) ); + $arrays= new BinaryExpression(new InvokeExpression(new Literal('is_array'), [$init]), '&&', $size); + $objects= new BinaryExpression(new InstanceOfExpression($init, new IsValue('ArrayAccess')), '&&', $size); + $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( + + $apply= $match($codegen, new OffsetExpression($use, $offset), $p); + $arrays= new BinaryExpression($arrays, '&&', new BinaryExpression( new InvokeExpression(new Literal('array_key_exists'), [$offset, $use]), '&&', - $match($codegen, new OffsetExpression($use, $offset), $p), + $apply, + )); + $objects= new BinaryExpression($objects, '&&', new BinaryExpression( + new InvokeExpression(new InstanceExpression($use, new Literal('offsetExists')), [$offset]), + '&&', + $apply, )); } // 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( + $arrays= new BinaryExpression($arrays, '&&', new Braced(new BinaryExpression( new Braced(new Assignment($pattern->rest, '=', $pattern->patterns ? new ArrayLiteral([[ null, @@ -319,9 +325,23 @@ public function setup($language, $emitter) { '||', new Literal('true') ))); + $objects= new BinaryExpression($objects, '&&', new Braced(new BinaryExpression( + new Braced(new Assignment($pattern->rest, '=', $pattern->patterns + ? new ArrayLiteral([[ + null, + new UnpackExpression(new InvokeExpression(new Literal('array_diff_key'), [ + new ArrayLiteral([[null, new UnpackExpression($use)]]), + $matched + ])) + ]]) + : new ArrayLiteral([[null, new UnpackExpression($use)]]) + )), + '||', + new Literal('true') + ))); } - return $compound; + return new Braced(new BinaryExpression($arrays, '||', $objects)); } // Should be unreachable diff --git a/src/test/php/lang/ast/syntax/php/unittest/ArrayAccessTest.class.php b/src/test/php/lang/ast/syntax/php/unittest/ArrayAccessTest.class.php new file mode 100755 index 0000000..c30031c --- /dev/null +++ b/src/test/php/lang/ast/syntax/php/unittest/ArrayAccessTest.class.php @@ -0,0 +1,41 @@ +run('class %T implements ArrayAccess, IteratorAggregate, Countable { + + public function count(): int { return 3; } + + public function offsetExists($i): bool { return true; } + + public function offsetGet($i): mixed { return $i + 1; } + + public function offsetSet($i, $value): void { /* NOOP */ } + + public function offsetUnset($i): void { /* NOOP */ } + + public function getIterator(): Traversable { yield from [1, 2, 3]; } + + public function run() { + return '.$expr.'; + } + }')); + } +} \ No newline at end of file