Skip to content

Commit 236e67c

Browse files
authored
Merge pull request #161 from xp-framework/feature/dynamic-class-constants
Support dynamic class constant fetch
2 parents 2635de6 + effac73 commit 236e67c

16 files changed

+244
-56
lines changed

ChangeLog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ XP Compiler ChangeLog
33

44
## ?.?.? / ????-??-??
55

6+
## 8.10.0 / 2023-04-08
7+
8+
* Merged PR #161: Support dynamic class constant fetch, the first PHP
9+
8.3 feature: https://wiki.php.net/rfc/dynamic_class_constant_fetch
10+
(@thekid)
11+
612
## 8.9.0 / 2023-02-19
713

814
* Merged PR #159: Implement command line option to set file extension

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"keywords": ["module", "xp"],
88
"require" : {
99
"xp-framework/core": "^11.0 | ^10.0",
10-
"xp-framework/ast": "^9.2",
10+
"xp-framework/ast": "^10.0",
1111
"php" : ">=7.0.0"
1212
},
1313
"require-dev" : {

src/main/php/lang/ast/emit/ArbitrayNewExpressions.class.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ protected function emitNew($result, $new) {
1414
if (!($new->type instanceof IsExpression)) return parent::emitNew($result, $new);
1515

1616
// Emit supported `new $var`, rewrite unsupported `new ($expr)`
17-
if ($new->type->expression instanceof Variable) {
18-
$result->out->write('new $'.$new->type->expression->name.'(');
17+
if ($new->type->expression instanceof Variable && $new->type->expression->const) {
18+
$result->out->write('new $'.$new->type->expression->pointer.'(');
1919
$this->emitArguments($result, $new->arguments);
2020
$result->out->write(')');
2121
} else {

src/main/php/lang/ast/emit/CallablesAsClosures.class.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php namespace lang\ast\emit;
22

33
use lang\ast\Node;
4-
use lang\ast\nodes\{InstanceExpression, ScopeExpression, Literal};
4+
use lang\ast\nodes\{Expression, InstanceExpression, ScopeExpression, Literal};
55

66
/**
77
* Rewrites callable expressions to `Callable::fromClosure()`
@@ -36,6 +36,10 @@ private function emitQuoted($result, $node) {
3636
$result->out->write(',');
3737
$this->emitQuoted($result, $node->member);
3838
$result->out->write(']');
39+
} else if ($node instanceof Expression) {
40+
41+
// Rewrite T::{<f>} => [T::class, <f>]
42+
$this->emitOne($result, $node->inline);
3943
} else {
4044

4145
// Emit other expressions as-is

src/main/php/lang/ast/emit/NullsafeAsTernaries.class.php

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@ protected function emitNullsafeInstance($result, $instance) {
1212
$result->out->write('null===('.$t.'=');
1313
$this->emitOne($result, $instance->expression);
1414
$result->out->write(')?null:'.$t.'->');
15-
16-
if ('literal' === $instance->member->kind) {
17-
$result->out->write($instance->member->expression);
18-
} else {
19-
$result->out->write('{');
20-
$this->emitOne($result, $instance->member);
21-
$result->out->write('}');
22-
}
15+
$this->emitOne($result, $instance->member);
2316
}
2417
}

src/main/php/lang/ast/emit/PHP.class.php

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
BinaryExpression,
88
Block,
99
Comment,
10+
Expression,
1011
InstanceExpression,
1112
Literal,
1213
Property,
@@ -104,8 +105,8 @@ protected function propertyType($type) {
104105
protected function enclose($result, $node, $signature, $static, $emit) {
105106
$capture= [];
106107
foreach ($result->codegen->search($node, 'variable') as $var) {
107-
if (isset($result->locals[$var->name])) {
108-
$capture[$var->name]= true;
108+
if (isset($result->locals[$var->pointer])) {
109+
$capture[$var->pointer]= true;
109110
}
110111
}
111112
unset($capture['this']);
@@ -228,7 +229,18 @@ protected function emitStatic($result, $static) {
228229
}
229230

230231
protected function emitVariable($result, $variable) {
231-
$result->out->write('$'.$variable->name);
232+
if ($variable->const) {
233+
$result->out->write('$'.$variable->pointer);
234+
} else {
235+
$result->out->write('$');
236+
$this->emitOne($result, $variable->pointer);
237+
}
238+
}
239+
240+
protected function emitExpression($result, $expression) {
241+
$result->out->write('{');
242+
$this->emitOne($result, $expression->inline);
243+
$result->out->write('}');
232244
}
233245

234246
protected function emitCast($result, $cast) {
@@ -674,10 +686,10 @@ protected function emitOffset($result, $offset) {
674686
}
675687

676688
protected function emitAssign($result, $target) {
677-
if ('variable' === $target->kind) {
678-
$result->out->write('$'.$target->name);
679-
$result->locals[$target->name]= true;
680-
} else if ('array' === $target->kind) {
689+
if ($target instanceof Variable && $target->const) {
690+
$result->out->write('$'.$target->pointer);
691+
$result->locals[$target->pointer]= true;
692+
} else if ($target instanceof ArrayLiteral) {
681693
$result->out->write('list(');
682694
foreach ($target->values as $pair) {
683695
if ($pair[0]) {
@@ -813,7 +825,8 @@ protected function emitForeach($result, $foreach) {
813825
$result->out->write(' => ');
814826
}
815827

816-
if ('array' === $foreach->value->kind) {
828+
// Support empty elements: `foreach (<expr> as [$a, , $b])`
829+
if ($foreach->value instanceof ArrayLiteral) {
817830
$result->out->write('[');
818831
foreach ($foreach->value->values as $pair) {
819832
if ($pair[0]) {
@@ -960,6 +973,13 @@ protected function emitNewClass($result, $new) {
960973
}
961974

962975
protected function emitCallable($result, $callable) {
976+
977+
// Disambiguate the following:
978+
//
979+
// - `T::{$func}`, a dynamic class constant
980+
// - `T::{$func}(...)`, a dynamic first-class callable
981+
$callable->expression->line= -1;
982+
963983
$this->emitOne($result, $callable->expression);
964984
$result->out->write('(...)');
965985
}
@@ -983,25 +1003,29 @@ protected function emitInvoke($result, $invoke) {
9831003
}
9841004

9851005
protected function emitScope($result, $scope) {
1006+
1007+
// $x::<expr> vs. e.g. invoke()::<expr> vs. T::<expr>
9861008
if ($scope->type instanceof Variable) {
9871009
$this->emitOne($result, $scope->type);
9881010
$result->out->write('::');
989-
$this->emitOne($result, $scope->member);
9901011
} else if ($scope->type instanceof Node) {
9911012
$t= $result->temp();
992-
$result->out->write('('.$t.'=');
1013+
$result->out->write('(null==='.$t.'=');
9931014
$this->emitOne($result, $scope->type);
994-
$result->out->write(')?'.$t.'::');
995-
$this->emitOne($result, $scope->member);
996-
$result->out->write(':null');
997-
} else if (
1015+
$result->out->write(")?null:{$t}::");
1016+
} else {
1017+
$result->out->write("{$scope->type}::");
1018+
}
1019+
1020+
// Rewrite T::member to T::$member for XP enums
1021+
if (
9981022
$scope->member instanceof Literal &&
1023+
is_string($scope->type) &&
9991024
'class' !== $scope->member->expression &&
10001025
$result->lookup($scope->type)->rewriteEnumCase($scope->member->expression)
10011026
) {
1002-
$result->out->write($scope->type.'::$'.$scope->member->expression);
1027+
$result->out->write('$'.$scope->member->expression);
10031028
} else {
1004-
$result->out->write($scope->type.'::');
10051029
$this->emitOne($result, $scope->member);
10061030
}
10071031
}
@@ -1016,26 +1040,13 @@ protected function emitInstance($result, $instance) {
10161040
$result->out->write('->');
10171041
}
10181042

1019-
if ('literal' === $instance->member->kind) {
1020-
$result->out->write($instance->member->expression);
1021-
} else {
1022-
$result->out->write('{');
1023-
$this->emitOne($result, $instance->member);
1024-
$result->out->write('}');
1025-
}
1043+
$this->emitOne($result, $instance->member);
10261044
}
10271045

10281046
protected function emitNullsafeInstance($result, $instance) {
10291047
$this->emitOne($result, $instance->expression);
10301048
$result->out->write('?->');
1031-
1032-
if ('literal' === $instance->member->kind) {
1033-
$result->out->write($instance->member->expression);
1034-
} else {
1035-
$result->out->write('{');
1036-
$this->emitOne($result, $instance->member);
1037-
$result->out->write('}');
1038-
}
1049+
$this->emitOne($result, $instance->member);
10391050
}
10401051

10411052
protected function emitUnpack($result, $unpack) {

src/main/php/lang/ast/emit/PHP70.class.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php namespace lang\ast\emit;
22

33
use lang\ast\Node;
4-
use lang\ast\nodes\{InstanceExpression, ScopeExpression, Literal, Variable};
4+
use lang\ast\nodes\{Expression, InstanceExpression, ScopeExpression, Literal, Variable};
55
use lang\ast\types\{IsUnion, IsIntersection, IsFunction, IsArray, IsMap, IsNullable, IsValue, IsLiteral, IsGeneric};
66

77
/**
@@ -74,6 +74,9 @@ protected function emitCallable($result, $callable) {
7474
$this->emitOne($result, $callable->expression->expression);
7575
if ($callable->expression->member instanceof Literal) {
7676
$result->out->write(',"'.trim($callable->expression->member, '"\'').'"');
77+
} else if ($callable->expression->member instanceof Expression) {
78+
$result->out->write(',');
79+
$this->emitOne($result, $callable->expression->member->inline);
7780
} else {
7881
$result->out->write(',');
7982
$this->emitOne($result, $callable->expression->member);
@@ -90,6 +93,9 @@ protected function emitCallable($result, $callable) {
9093
}
9194
if ($callable->expression->member instanceof Literal) {
9295
$result->out->write(',"'.trim($callable->expression->member, '"\'').'"');
96+
} else if ($callable->expression->member instanceof Expression) {
97+
$result->out->write(',');
98+
$this->emitOne($result, $callable->expression->member->inline);
9399
} else {
94100
$result->out->write(',');
95101
$this->emitOne($result, $callable->expression->member);

src/main/php/lang/ast/emit/PHP80.class.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* @see https://wiki.php.net/rfc#php_80
1010
*/
1111
class PHP80 extends PHP {
12-
use RewriteBlockLambdaExpressions, RewriteExplicitOctals, RewriteEnums;
12+
use RewriteBlockLambdaExpressions, RewriteDynamicClassConstants, RewriteExplicitOctals, RewriteEnums;
1313
use ReadonlyClasses, ReadonlyProperties, CallablesAsClosures, ArrayUnpackUsingMerge;
1414

1515
/** Sets up type => literal mappings */

src/main/php/lang/ast/emit/PHP81.class.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* @see https://wiki.php.net/rfc#php_81
1111
*/
1212
class PHP81 extends PHP {
13-
use RewriteBlockLambdaExpressions, ReadonlyClasses;
13+
use RewriteBlockLambdaExpressions, RewriteDynamicClassConstants, ReadonlyClasses;
1414

1515
/** Sets up type => literal mappings */
1616
public function __construct() {

src/main/php/lang/ast/emit/PHP82.class.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
* @see https://wiki.php.net/rfc#php_82
1111
*/
1212
class PHP82 extends PHP {
13-
use RewriteBlockLambdaExpressions, ReadonlyClasses;
13+
use RewriteBlockLambdaExpressions, RewriteDynamicClassConstants, ReadonlyClasses;
1414

1515
/** Sets up type => literal mappings */
1616
public function __construct() {

0 commit comments

Comments
 (0)