Skip to content

Commit

Permalink
Merge pull request #82 from xp-framework/feature/scope_chaining
Browse files Browse the repository at this point in the history
Allow chaining scope resolution operator `::`
  • Loading branch information
thekid authored Mar 28, 2020
2 parents 420cd51 + 69ae2c9 commit fd0ad6e
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 6 deletions.
17 changes: 15 additions & 2 deletions src/main/php/lang/ast/emit/PHP.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -717,8 +717,21 @@ protected function emitInvoke($result, $invoke) {
}

protected function emitScope($result, $scope) {
$result->out->write($scope->type.'::');
$this->emitOne($result, $scope->member);
if ($scope->type instanceof Variable) {
$this->emitOne($result, $scope->type);
$result->out->write('::');
$this->emitOne($result, $scope->member);
} else if ($scope->type instanceof Node) {
$t= $result->temp();
$result->out->write('('.$t.'=');
$this->emitOne($result, $scope->type);
$result->out->write(')?'.$t.'::');
$this->emitOne($result, $scope->member);
$result->out->write(':null');
} else {
$result->out->write($scope->type.'::');
$this->emitOne($result, $scope->member);
}
}

protected function emitInstance($result, $instance) {
Expand Down
9 changes: 8 additions & 1 deletion src/main/php/lang/ast/syntax/PHP.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ public function __construct() {
});

$this->infix('::', 100, function($parse, $token, $left) {
$scope= $parse->scope->resolve($left->expression);
$scope= $left instanceof Literal ? $parse->scope->resolve($left->expression) : $left;

if ('variable' === $parse->token->kind) {
$expr= new Variable($parse->token->value, $parse->token->line);
Expand All @@ -150,6 +150,13 @@ public function __construct() {
}

$parse->forward();
if ('(' === $parse->token->value) {
$parse->expecting('(', 'invoke expression');
$arguments= $this->expressions($parse);
$parse->expecting(')', 'invoke expression');
$expr= new InvokeExpression($expr, $arguments, $token->line);
}

return new ScopeExpression($scope, $expr, $token->line);
});

Expand Down
62 changes: 62 additions & 0 deletions src/test/php/lang/ast/unittest/emit/MembersTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,48 @@ public function run() {
Assert::equals('Test', $r);
}

#[@test]
public function dynamic_class_property() {
$r= $this->run('class <T> {
private static $MEMBER= "Test";
public function run() {
$class= self::class;
return $class::$MEMBER;
}
}');

Assert::equals('Test', $r);
}

#[@test]
public function dynamic_class_method() {
$r= $this->run('class <T> {
private static function member() { return "Test"; }
public function run() {
$class= self::class;
return $class::member();
}
}');

Assert::equals('Test', $r);
}

#[@test]
public function dynamic_class_constant() {
$r= $this->run('class <T> {
private const MEMBER = "Test";
public function run() {
$class= self::class;
return $class::MEMBER;
}
}');

Assert::equals('Test', $r);
}

#[@test]
public function instance_property() {
$r= $this->run('class <T> {
Expand Down Expand Up @@ -122,4 +164,24 @@ public function run() {

Assert::null($r);
}

#[@test]
public function chaining_sccope_operators() {
$r= $this->run('class <T> {
private const TYPE = self::class;
private const NAME = "Test";
private static $name = "Test";
private static function name() { return "Test"; }
public function run() {
$name= "name";
return [self::TYPE::NAME, self::TYPE::$name, self::TYPE::name(), self::TYPE::$name()];
}
}');

Assert::equals(['Test', 'Test', 'Test', 'Test'], $r);
}
}
6 changes: 3 additions & 3 deletions src/test/php/lang/ast/unittest/parse/MembersTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -188,9 +188,9 @@ public function instance_method_invocation() {
#[@test]
public function static_method_invocation() {
$this->assertParsed(
[new InvokeExpression(
new ScopeExpression('\\A', new Literal('member', self::LINE), self::LINE),
[new Literal('1', self::LINE)],
[new ScopeExpression(
'\\A',
new InvokeExpression(new Literal('member', self::LINE), [new Literal('1', self::LINE)], self::LINE),
self::LINE
)],
'A::member(1);'
Expand Down

0 comments on commit fd0ad6e

Please sign in to comment.