Skip to content

Commit 2c63e8a

Browse files
committed
Support php 8.0's nullsafe operator
See https://wiki.php.net/rfc/nullsafe_operator and the corresponding github implementation PR.
1 parent 9bb043e commit 2c63e8a

12 files changed

+233
-5
lines changed

src/Parser.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,7 +1199,7 @@ private function parseTemplateStringExpression($parentNode) {
11991199
$token = $this->getCurrentToken();
12001200
if ($token->kind === TokenKind::OpenBracketToken) {
12011201
return $this->parseTemplateStringSubscriptExpression($var);
1202-
} else if ($token->kind === TokenKind::ArrowToken) {
1202+
} else if ($token->kind === TokenKind::ArrowToken || $token->kind === TokenKind::QuestionArrowToken) {
12031203
return $this->parseTemplateStringMemberAccessExpression($var);
12041204
} else {
12051205
return $var;
@@ -1248,7 +1248,7 @@ private function parseTemplateStringMemberAccessExpression($expression) : Member
12481248
$expression->parent = $memberAccessExpression;
12491249

12501250
$memberAccessExpression->dereferencableExpression = $expression;
1251-
$memberAccessExpression->arrowToken = $this->eat1(TokenKind::ArrowToken);
1251+
$memberAccessExpression->arrowToken = $this->eat(TokenKind::ArrowToken, TokenKind::QuestionArrowToken);
12521252
$memberAccessExpression->memberName = $this->eat1(TokenKind::Name);
12531253

12541254
return $memberAccessExpression;
@@ -2711,7 +2711,7 @@ private function parsePostfixExpressionRest($expression, $allowUpdateExpression
27112711
return $expression;
27122712
}
27132713

2714-
if ($tokenKind === TokenKind::ArrowToken) {
2714+
if ($tokenKind === TokenKind::ArrowToken || $tokenKind === TokenKind::QuestionArrowToken) {
27152715
$expression = $this->parseMemberAccessExpression($expression);
27162716
return $this->parsePostfixExpressionRest($expression);
27172717
}
@@ -2836,7 +2836,7 @@ private function parseMemberAccessExpression($expression):MemberAccessExpression
28362836
$expression->parent = $memberAccessExpression;
28372837

28382838
$memberAccessExpression->dereferencableExpression = $expression;
2839-
$memberAccessExpression->arrowToken = $this->eat1(TokenKind::ArrowToken);
2839+
$memberAccessExpression->arrowToken = $this->eat(TokenKind::ArrowToken, TokenKind::QuestionArrowToken);
28402840
$memberAccessExpression->memberName = $this->parseMemberName($memberAccessExpression);
28412841

28422842
return $memberAccessExpression;

src/PhpTokenizer.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
// The replacement value is arbitrary - it just has to be different from other values of token constants.
1111
define(__NAMESPACE__ . '\T_COALESCE_EQUAL', defined('T_COALESCE_EQUAL') ? constant('T_COALESCE_EQUAL') : 'T_COALESCE_EQUAL');
1212
define(__NAMESPACE__ . '\T_FN', defined('T_FN') ? constant('T_FN') : 'T_FN');
13-
// If this predaates PHP 8.0, T_MATCH is unavailable. The replacement value is arbitrary - it just has to be different from other values of token constants.
13+
// If this predates PHP 8.0, T_MATCH is unavailable. The replacement value is arbitrary - it just has to be different from other values of token constants.
1414
define(__NAMESPACE__ . '\T_MATCH', defined('T_MATCH') ? constant('T_MATCH') : 'T_MATCH');
15+
define(__NAMESPACE__ . '\T_NULLSAFE_OBJECT_OPERATOR', defined('T_NULLSAFE_OBJECT_OPERATOR') ? constant('T_NULLSAFE_OBJECT_OPERATOR') : 'T_MATCH');
1516

1617
/**
1718
* Tokenizes content using PHP's built-in `token_get_all`, and converts to "lightweight" Token representation.
@@ -307,6 +308,7 @@ protected static function tokenGetAll(string $content, $parseContext): array
307308
"}" => TokenKind::CloseBraceToken,
308309
"." => TokenKind::DotToken,
309310
T_OBJECT_OPERATOR => TokenKind::ArrowToken,
311+
T_NULLSAFE_OBJECT_OPERATOR => TokenKind::QuestionArrowToken,
310312
T_INC => TokenKind::PlusPlusToken,
311313
T_DEC => TokenKind::MinusMinusToken,
312314
T_POW => TokenKind::AsteriskAsteriskToken,

src/TokenKind.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ class TokenKind {
149149
const BacktickToken = 260;
150150
const QuestionToken = 261;
151151
const QuestionQuestionEqualsToken = 262;
152+
const QuestionArrowToken = 263;
152153

153154
const DecimalLiteralToken = 301;
154155
const OctalLiteralToken = 302;

src/TokenStringMaps.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ class TokenStringMaps {
160160
"^=" => TokenKind::CaretEqualsToken,
161161
"|=" => TokenKind::BarEqualsToken,
162162
"," => TokenKind::CommaToken,
163+
"?->" => TokenKind::QuestionArrowToken,
163164
"??" => TokenKind::QuestionQuestionToken,
164165
"??=" => TokenKind::QuestionQuestionEqualsToken,
165166
"<=>" => TokenKind::LessThanEqualsGreaterThanToken,
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
$x = $a?->b;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"SourceFileNode": {
3+
"statementList": [
4+
{
5+
"InlineHtml": {
6+
"scriptSectionEndTag": null,
7+
"text": null,
8+
"scriptSectionStartTag": {
9+
"kind": "ScriptSectionStartTag",
10+
"textLength": 6
11+
}
12+
}
13+
},
14+
{
15+
"ExpressionStatement": {
16+
"expression": {
17+
"AssignmentExpression": {
18+
"leftOperand": {
19+
"Variable": {
20+
"dollar": null,
21+
"name": {
22+
"kind": "VariableName",
23+
"textLength": 2
24+
}
25+
}
26+
},
27+
"operator": {
28+
"kind": "EqualsToken",
29+
"textLength": 1
30+
},
31+
"byRef": null,
32+
"rightOperand": {
33+
"MemberAccessExpression": {
34+
"dereferencableExpression": {
35+
"Variable": {
36+
"dollar": null,
37+
"name": {
38+
"kind": "VariableName",
39+
"textLength": 2
40+
}
41+
}
42+
},
43+
"arrowToken": {
44+
"kind": "QuestionArrowToken",
45+
"textLength": 3
46+
},
47+
"memberName": {
48+
"kind": "Name",
49+
"textLength": 1
50+
}
51+
}
52+
}
53+
}
54+
},
55+
"semicolon": {
56+
"kind": "SemicolonToken",
57+
"textLength": 1
58+
}
59+
}
60+
}
61+
],
62+
"endOfFileToken": {
63+
"kind": "EndOfFileToken",
64+
"textLength": 0
65+
}
66+
}
67+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
$a?->foo(1);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"SourceFileNode": {
3+
"statementList": [
4+
{
5+
"InlineHtml": {
6+
"scriptSectionEndTag": null,
7+
"text": null,
8+
"scriptSectionStartTag": {
9+
"kind": "ScriptSectionStartTag",
10+
"textLength": 6
11+
}
12+
}
13+
},
14+
{
15+
"ExpressionStatement": {
16+
"expression": {
17+
"CallExpression": {
18+
"callableExpression": {
19+
"MemberAccessExpression": {
20+
"dereferencableExpression": {
21+
"Variable": {
22+
"dollar": null,
23+
"name": {
24+
"kind": "VariableName",
25+
"textLength": 2
26+
}
27+
}
28+
},
29+
"arrowToken": {
30+
"kind": "QuestionArrowToken",
31+
"textLength": 3
32+
},
33+
"memberName": {
34+
"kind": "Name",
35+
"textLength": 3
36+
}
37+
}
38+
},
39+
"openParen": {
40+
"kind": "OpenParenToken",
41+
"textLength": 1
42+
},
43+
"argumentExpressionList": {
44+
"ArgumentExpressionList": {
45+
"children": [
46+
{
47+
"ArgumentExpression": {
48+
"byRefToken": null,
49+
"dotDotDotToken": null,
50+
"expression": {
51+
"NumericLiteral": {
52+
"children": {
53+
"kind": "IntegerLiteralToken",
54+
"textLength": 1
55+
}
56+
}
57+
}
58+
}
59+
}
60+
]
61+
}
62+
},
63+
"closeParen": {
64+
"kind": "CloseParenToken",
65+
"textLength": 1
66+
}
67+
}
68+
},
69+
"semicolon": {
70+
"kind": "SemicolonToken",
71+
"textLength": 1
72+
}
73+
}
74+
}
75+
],
76+
"endOfFileToken": {
77+
"kind": "EndOfFileToken",
78+
"textLength": 0
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)