Created
December 27, 2025 16:52
-
-
Save thekid/12b2eae6cce613b9f75928f93f246370 to your computer and use it in GitHub Desktop.
Is operator with binding defaults using =
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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..1013b29 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, | |
| + TernaryExpression, | |
| Variable | |
| }; | |
| use lang\ast\syntax\Extension; | |
| @@ -70,6 +71,11 @@ class IsOperator implements Extension { | |
| $parse->forward(); | |
| $r->restriction= $pattern($parse, $types); | |
| } | |
| + | |
| + if ('=' === $parse->token->value) { | |
| + $parse->forward(); | |
| + return new IsOptional($r, $types->expression($parse, 0)); | |
| + } | |
| return $r; | |
| } else if ('>' === $parse->token->value || '>=' === $parse->token->value || '<' === $parse->token->value || '<=' === $parse->token->value) { | |
| $operator= $parse->token->value; | |
| @@ -220,11 +226,11 @@ class IsOperator implements Extension { | |
| new BinaryExpression($use, $pattern->operator, $pattern->value) | |
| ); | |
| } else if ($pattern instanceof IsNullable) { | |
| - return new BinaryExpression( | |
| + return new Braced(new BinaryExpression( | |
| new BinaryExpression(new Literal('null'), '===', $init), | |
| '||', | |
| $match($codegen, $use, $pattern->element) | |
| - ); | |
| + )); | |
| } else if ($pattern instanceof IsCompound) { | |
| $compound= $match($codegen, $init, $pattern->patterns[0]); | |
| for ($i= 1, $s= sizeof($pattern->patterns), $op= $pattern->operator.$pattern->operator; $i < $s; $i++) { | |
| @@ -242,22 +248,39 @@ class IsOperator implements Extension { | |
| } | |
| 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)) | |
| + ); | |
| + $compound= new BinaryExpression(new InvokeExpression(new Literal('is_array'), [$init]), '&&', $size); | |
| + $optional= 0; | |
| foreach ($pattern->patterns as $key => $p) { | |
| $offset= new Literal((string)$key); | |
| - $compound= new BinaryExpression($compound, '&&', new BinaryExpression( | |
| - new InvokeExpression(new Literal('array_key_exists'), [$offset, $use]), | |
| - '&&', | |
| - $match($codegen, new OffsetExpression($use, $offset), $p), | |
| - )); | |
| + $exists= new InvokeExpression(new Literal('array_key_exists'), [$offset, $use]); | |
| + | |
| + if ($p instanceof IsOptional) { | |
| + $test= new Braced(new TernaryExpression( | |
| + $exists, | |
| + $match($codegen, new OffsetExpression($use, $offset), $p->binding), | |
| + $match($codegen, $p->default, $p->binding) | |
| + )); | |
| + $optional++; | |
| + } else { | |
| + $test= new BinaryExpression( | |
| + $exists, | |
| + '&&', | |
| + $match($codegen, new OffsetExpression($use, $offset), $p), | |
| + ); | |
| + } | |
| + | |
| + $compound= new BinaryExpression($compound, '&&', $test); | |
| + } | |
| + | |
| + // Optional implies rest | |
| + if ($optional) { | |
| + $size->operator= '>='; | |
| + $size->right->expression= (string)($size->right->expression - $optional); | |
| } | |
| 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..c675450 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 | |
| @@ -115,4 +115,13 @@ class VariableBindingTest extends EmittingTest { | |
| } | |
| }')); | |
| } | |
| + | |
| + #[Test, Values([[['key' => 1], null], [[], ['matched' => null]], [['key' => 'value'], ['matched' => 'value']]])] | |
| + public function with_default($input, $expected) { | |
| + Assert::equals($expected, $this->run('class %T { | |
| + public function run($input) { | |
| + return $input is ["key" => $expr & ?string = null] ? ["matched" => $expr] : null; | |
| + } | |
| + }', $input)); | |
| + } | |
| } | |
| \ No newline at end of file |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php namespace lang\ast\syntax\php; | |
| use lang\ast\Type; | |
| use util\Objects; | |
| class IsOptional extends Type { | |
| public $binding, $default; | |
| /** | |
| * Creates a an optional binding "type" | |
| * | |
| * @param lang.ast.syntax.php.IsBinding $binding | |
| * @param lang.ast.Node $default | |
| */ | |
| public function __construct($binding, $default) { | |
| $this->binding= $binding; | |
| $this->default= $default; | |
| } | |
| /** @return string */ | |
| public function toString() { | |
| return nameof($this).'('.$this->binding->toString().' ?? '.Objects::stringOf($this->default).')'; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment