Skip to content

Commit

Permalink
Add syntactic support for `A<T>::class
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed Aug 2, 2024
1 parent 8dbdcdd commit 0468d7c
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 2 deletions.
36 changes: 35 additions & 1 deletion src/main/php/lang/ast/syntax/PHP.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public function __construct() {
$expr= new InvokeExpression($expr, $arguments, $token->line);
}

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

$this->infix('(', 100, function($parse, $token, $left) {
Expand Down Expand Up @@ -482,6 +482,40 @@ public function __construct() {
});

$this->prefix('(name)', 0, function($parse, $token) {
static $types= ['(' => 1, ')' => 1, ',' => 1, '?' => 1, ':' => 1, '|' => 1, '&' => 1];

// Disambiguate `Class<T>` from `const < expr` by looking ahead
if ('<' === $parse->token->value) {
$generic= true;
$level= 1;
$skipped= [$parse->token];
while ($level > 0) {
$parse->forward();
$skipped[]= $parse->token;

if ('<' === $parse->token->symbol->id) {
$level++;
} else if ('<?' === $parse->token->symbol->id) {
$level++;
} else if ('>' === $parse->token->symbol->id) {
$level--;
} else if ('>>' === $parse->token->symbol->id) {
$level-= 2;
} else if ('name' !== $parse->token->kind && !isset($types[$parse->token->value])) {
$generic= false;
break;
}
}

$parse->queue= $parse->queue ? array_merge($skipped, $parse->queue) : $skipped;
if ($generic) {
$parse->token= $token;
return $this->type($parse, false);
}

$parse->forward();
}

return new Literal($token->value, $token->line);
});

Expand Down
10 changes: 9 additions & 1 deletion src/test/php/lang/ast/unittest/parse/MembersTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
Variable,
Parameter
};
use lang\ast\types\{IsFunction, IsLiteral, IsNullable, IsUnion, IsValue};
use lang\ast\types\{IsFunction, IsLiteral, IsNullable, IsUnion, IsValue, IsGeneric};
use test\{Assert, Test, Values};

class MembersTest extends ParseTest {
Expand Down Expand Up @@ -297,6 +297,14 @@ public function class_resolution() {
);
}

#[Test]
public function generic_class_resolution() {
$this->assertParsed(
[new ScopeExpression(new IsGeneric(new IsValue('\\A'), [new IsValue('\\T')]), new Literal('class', self::LINE), self::LINE)],
'A<T>::class;'
);
}

#[Test]
public function instance_method_invocation() {
$this->assertParsed(
Expand Down
13 changes: 13 additions & 0 deletions src/test/php/lang/ast/unittest/parse/OperatorTest.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,4 +332,17 @@ public function multiple_semicolons() {
';; $a= 1 ;;; $b= 2;'
);
}

#[Test]
public function const_less_than_const() {
$this->assertParsed(
[new BinaryExpression(
new Literal('a', self::LINE),
'<',
new Literal('b', self::LINE),
self::LINE
)],
'a < b;'
);
}
}

0 comments on commit 0468d7c

Please sign in to comment.