Last active
May 22, 2023 19:04
-
-
Save thekid/dc12c4c4f4cf3f971b7dbbf4a5cd83b4 to your computer and use it in GitHub Desktop.
Reusable property hooks
This file contains 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.class.php b/src/main/php/lang/ast/syntax/PHP.class.php | |
index 7561184..1e84854 100755 | |
--- a/src/main/php/lang/ast/syntax/PHP.class.php | |
+++ b/src/main/php/lang/ast/syntax/PHP.class.php | |
@@ -1337,6 +1337,13 @@ class PHP extends Language { | |
$body[$lookup]->hooks['get']= new Hook([], 'get', $expr, false, null, $line, $holder); | |
return; | |
+ } else if ('as' === $parse->token->value) { | |
+ $parse->forward(); | |
+ $expr= $this->expression($parse, 0); | |
+ $parse->expecting(';', 'property hook'); | |
+ | |
+ $body[$lookup]->hooks['as']= new Hook([], 'as', $expr, false, null, $line, $holder); | |
+ return; | |
} else if ('{' === $parse->token->value) { | |
$parse->forward(); | |
This file contains 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/emit/PropertyHooks.class.php b/src/main/php/lang/ast/emit/PropertyHooks.class.php | |
index bd29452..ab063a0 100755 | |
--- a/src/main/php/lang/ast/emit/PropertyHooks.class.php | |
+++ b/src/main/php/lang/ast/emit/PropertyHooks.class.php | |
@@ -3,18 +3,23 @@ | |
use lang\ast\Code; | |
use lang\ast\nodes\{ | |
Assignment, | |
+ ArrayLiteral, | |
Block, | |
+ CastExpression, | |
InstanceExpression, | |
InvokeExpression, | |
Literal, | |
Method, | |
OffsetExpression, | |
Parameter, | |
+ Property, | |
ReturnStatement, | |
ScopeExpression, | |
Signature, | |
+ UnaryExpression, | |
Variable | |
}; | |
+use lang\ast\types\IsLiteral; | |
/** | |
* Property hooks | |
@@ -160,6 +165,42 @@ trait PropertyHooks { | |
// except inside interfaces, which cannot contain properties. | |
if ('interface' === $scope->type->kind) return; | |
+ // Rewrite hook delegates without declaring methods | |
+ if ($hook= $property->hooks['as'] ?? null) { | |
+ | |
+ // Emit helper property if the expression is not already a property reference | |
+ if ( | |
+ $hook->expression instanceof InstanceExpression && | |
+ $hook->expression->expression instanceof Variable && 'this' === $hook->expression->expression->pointer && | |
+ $hook->expression->member instanceof Literal | |
+ ) { | |
+ $expression= $hook->expression; | |
+ } else { | |
+ $member= '__'.$property->name.'_delegate'; | |
+ | |
+ $this->emitOne($result, new Property(['private'], $member, null, $hook->expression)); | |
+ $expression= new InstanceExpression(new Variable('this'), new Literal($member)); | |
+ } | |
+ | |
+ // Property: name, value reference and type (which may be null) | |
+ $reference= new CastExpression(new IsLiteral('object'), new ArrayLiteral([ | |
+ [new Literal("'name'"), $literal], | |
+ [new Literal("'value'"), new UnaryExpression('prefix', $virtual, '&')], | |
+ [new Literal("'type'"), $property->type ? new Literal("'{$property->type->literal()}'") : new Literal('null')], | |
+ ])); | |
+ $get= new Block([ | |
+ new Assignment(new Variable('r'), $hook->byref ? '=&' : '=', new InvokeExpression( | |
+ new InstanceExpression($expression, new Literal('get')), | |
+ [new Variable('this'), $reference] | |
+ )), | |
+ new ReturnStatement(new Variable('r')) | |
+ ]); | |
+ $set= new InvokeExpression( | |
+ new InstanceExpression($expression, new Literal('set')), | |
+ [new Variable('this'), $reference, new Variable('value')] | |
+ ); | |
+ } | |
+ | |
$scope->virtual[$property->name]= [ | |
$get ?? new ReturnStatement($virtual), | |
$set ?? new Assignment($virtual, '=', new Variable('value')) |
This file contains 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 | |
use util\cmd\Console; | |
class ByLazy { | |
public function __construct(private callable $init) { } | |
public function get($self, $property) { | |
return ($property->value??= [($this->init)()])[0]; | |
} | |
public function set($self, $property, $value) { | |
$property->value= [$value]; | |
} | |
} | |
class Environment { | |
public string $home as new ByLazy(function() { | |
Console::writeLine('Getting environment variable'); | |
return getenv('HOME'); | |
}); | |
} | |
class PropertyDelegates { | |
public static function main($args) { | |
$env= new Environment(); | |
$args && $env->home= realpath($args[0]); | |
Console::writeLine($env->home); // Gets and prints env var unless initialized above | |
Console::writeLine($env->home); // Prints cached copy | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Invoking without arguments, lazy-fetches $HOME
Invoking with argument, initializes via setter and never fetches from environment: