Skip to content

Instantly share code, notes, and snippets.

@thekid
Last active May 22, 2023 19:04
Show Gist options
  • Save thekid/dc12c4c4f4cf3f971b7dbbf4a5cd83b4 to your computer and use it in GitHub Desktop.
Save thekid/dc12c4c4f4cf3f971b7dbbf4a5cd83b4 to your computer and use it in GitHub Desktop.
Reusable property hooks
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();
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'))
<?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
}
}
@thekid
Copy link
Author

thekid commented May 19, 2023

Invoking without arguments, lazy-fetches $HOME

$ xp Lazy
Getting environment variable
C:\Tools\Cygwin\home\timmf
C:\Tools\Cygwin\home\timmf

Invoking with argument, initializes via setter and never fetches from environment:

$ xp Lazy .
C:\Tools\Cygwin\home\timmf\devel\xp\compiler
C:\Tools\Cygwin\home\timmf\devel\xp\compiler

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment