Skip to content

Commit 19debdc

Browse files
committed
Support parsing nested calls (#175)
For #175 Using tolerant-php-parser-to-php-ast, the following types of statements have been tested, and the transformed ASTs were identical to nikic/php-ast's AST nodes: ```php <?php foo(1234)['index'](true, false); (new MyClass())(); $result = foo(1234)(5678); $obj->call('prop')('value'); ``` Also, callExpression8.php is valid PHP code. There shouldn't have been any diagnostics whatsoever. I'm not very familiar with this repo, so I might have missed potential bugs.
1 parent 42fe00b commit 19debdc

12 files changed

+340
-70
lines changed

src/Parser.php

+29-22
Original file line numberDiff line numberDiff line change
@@ -2333,36 +2333,43 @@ private function parsePostfixExpressionRest($expression, $allowUpdateExpression
23332333
return $this->parsePostfixExpressionRest($expression);
23342334
}
23352335

2336-
while (true) {
2337-
$tokenKind = $this->getCurrentToken()->kind;
2336+
$tokenKind = $this->getCurrentToken()->kind;
23382337

2339-
if ($tokenKind === TokenKind::OpenBraceToken ||
2340-
$tokenKind === TokenKind::OpenBracketToken) {
2341-
$expression = $this->parseSubscriptExpression($expression);
2342-
return $this->parsePostfixExpressionRest($expression);
2343-
}
2338+
if ($tokenKind === TokenKind::OpenBraceToken ||
2339+
$tokenKind === TokenKind::OpenBracketToken) {
2340+
$expression = $this->parseSubscriptExpression($expression);
2341+
return $this->parsePostfixExpressionRest($expression);
2342+
}
23442343

2345-
if ($expression instanceof ArrayCreationExpression) {
2346-
// Remaining postfix expressions are invalid, so abort
2347-
return $expression;
2348-
}
2344+
if ($expression instanceof ArrayCreationExpression) {
2345+
// Remaining postfix expressions are invalid, so abort
2346+
return $expression;
2347+
}
23492348

2350-
if ($tokenKind === TokenKind::ArrowToken) {
2351-
$expression = $this->parseMemberAccessExpression($expression);
2352-
return $this->parsePostfixExpressionRest($expression);
2353-
}
2349+
if ($tokenKind === TokenKind::ArrowToken) {
2350+
$expression = $this->parseMemberAccessExpression($expression);
2351+
return $this->parsePostfixExpressionRest($expression);
2352+
}
23542353

2355-
if ($tokenKind === TokenKind::OpenParenToken && !$this->isParsingObjectCreationExpression) {
2356-
$expression = $this->parseCallExpressionRest($expression);
2354+
if ($tokenKind === TokenKind::OpenParenToken && !$this->isParsingObjectCreationExpression) {
2355+
$expression = $this->parseCallExpressionRest($expression);
23572356

2358-
return $this->checkToken(TokenKind::OpenParenToken)
2359-
? $expression // $a()() should get parsed as CallExpr-ParenExpr, so do not recurse
2360-
: $this->parsePostfixExpressionRest($expression);
2357+
if (!$this->checkToken(TokenKind::OpenParenToken)) {
2358+
return $this->parsePostfixExpressionRest($expression);
2359+
}
2360+
if (
2361+
$expression instanceof ParenthesizedExpression ||
2362+
$expression instanceof CallExpression ||
2363+
$expression instanceof SubscriptExpression) {
2364+
// Continue parsing the remaining brackets for expressions
2365+
// such as `(new Foo())()`, `foo()()`, `foo()['index']()`
2366+
return $this->parsePostfixExpressionRest($expression);
23612367
}
2362-
2363-
// Reached the end of the postfix-expression, so return
23642368
return $expression;
23652369
}
2370+
2371+
// Reached the end of the postfix-expression, so return
2372+
return $expression;
23662373
}
23672374

23682375
private function parseMemberName($parentNode) {
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
a()();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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+
"CallExpression": {
20+
"callableExpression": {
21+
"QualifiedName": {
22+
"globalSpecifier": null,
23+
"relativeSpecifier": null,
24+
"nameParts": [
25+
{
26+
"kind": "Name",
27+
"textLength": 1
28+
}
29+
]
30+
}
31+
},
32+
"openParen": {
33+
"kind": "OpenParenToken",
34+
"textLength": 1
35+
},
36+
"argumentExpressionList": null,
37+
"closeParen": {
38+
"kind": "CloseParenToken",
39+
"textLength": 1
40+
}
41+
}
42+
},
43+
"openParen": {
44+
"kind": "OpenParenToken",
45+
"textLength": 1
46+
},
47+
"argumentExpressionList": null,
48+
"closeParen": {
49+
"kind": "CloseParenToken",
50+
"textLength": 1
51+
}
52+
}
53+
},
54+
"semicolon": {
55+
"kind": "SemicolonToken",
56+
"textLength": 1
57+
}
58+
}
59+
}
60+
],
61+
"endOfFileToken": {
62+
"kind": "EndOfFileToken",
63+
"textLength": 0
64+
}
65+
}
66+
}
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
(new MyClass())();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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+
"ParenthesizedExpression": {
20+
"openParen": {
21+
"kind": "OpenParenToken",
22+
"textLength": 1
23+
},
24+
"expression": {
25+
"ObjectCreationExpression": {
26+
"newKeword": {
27+
"kind": "NewKeyword",
28+
"textLength": 3
29+
},
30+
"classTypeDesignator": {
31+
"QualifiedName": {
32+
"globalSpecifier": null,
33+
"relativeSpecifier": null,
34+
"nameParts": [
35+
{
36+
"kind": "Name",
37+
"textLength": 7
38+
}
39+
]
40+
}
41+
},
42+
"openParen": {
43+
"kind": "OpenParenToken",
44+
"textLength": 1
45+
},
46+
"argumentExpressionList": null,
47+
"closeParen": {
48+
"kind": "CloseParenToken",
49+
"textLength": 1
50+
},
51+
"classBaseClause": null,
52+
"classInterfaceClause": null,
53+
"classMembers": null
54+
}
55+
},
56+
"closeParen": {
57+
"kind": "CloseParenToken",
58+
"textLength": 1
59+
}
60+
}
61+
},
62+
"openParen": {
63+
"kind": "OpenParenToken",
64+
"textLength": 1
65+
},
66+
"argumentExpressionList": null,
67+
"closeParen": {
68+
"kind": "CloseParenToken",
69+
"textLength": 1
70+
}
71+
}
72+
},
73+
"semicolon": {
74+
"kind": "SemicolonToken",
75+
"textLength": 1
76+
}
77+
}
78+
}
79+
],
80+
"endOfFileToken": {
81+
"kind": "EndOfFileToken",
82+
"textLength": 0
83+
}
84+
}
85+
}
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
a()['myOffset'](1234);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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+
"SubscriptExpression": {
20+
"postfixExpression": {
21+
"CallExpression": {
22+
"callableExpression": {
23+
"QualifiedName": {
24+
"globalSpecifier": null,
25+
"relativeSpecifier": null,
26+
"nameParts": [
27+
{
28+
"kind": "Name",
29+
"textLength": 1
30+
}
31+
]
32+
}
33+
},
34+
"openParen": {
35+
"kind": "OpenParenToken",
36+
"textLength": 1
37+
},
38+
"argumentExpressionList": null,
39+
"closeParen": {
40+
"kind": "CloseParenToken",
41+
"textLength": 1
42+
}
43+
}
44+
},
45+
"openBracketOrBrace": {
46+
"kind": "OpenBracketToken",
47+
"textLength": 1
48+
},
49+
"accessExpression": {
50+
"StringLiteral": {
51+
"startQuote": null,
52+
"children": {
53+
"kind": "StringLiteralToken",
54+
"textLength": 10
55+
},
56+
"endQuote": null
57+
}
58+
},
59+
"closeBracketOrBrace": {
60+
"kind": "CloseBracketToken",
61+
"textLength": 1
62+
}
63+
}
64+
},
65+
"openParen": {
66+
"kind": "OpenParenToken",
67+
"textLength": 1
68+
},
69+
"argumentExpressionList": {
70+
"ArgumentExpressionList": {
71+
"children": [
72+
{
73+
"ArgumentExpression": {
74+
"byRefToken": null,
75+
"dotDotDotToken": null,
76+
"expression": {
77+
"NumericLiteral": {
78+
"children": {
79+
"kind": "IntegerLiteralToken",
80+
"textLength": 4
81+
}
82+
}
83+
}
84+
}
85+
}
86+
]
87+
}
88+
},
89+
"closeParen": {
90+
"kind": "CloseParenToken",
91+
"textLength": 1
92+
}
93+
}
94+
},
95+
"semicolon": {
96+
"kind": "SemicolonToken",
97+
"textLength": 1
98+
}
99+
}
100+
}
101+
],
102+
"endOfFileToken": {
103+
"kind": "EndOfFileToken",
104+
"textLength": 0
105+
}
106+
}
107+
}
+1-8
Original file line numberDiff line numberDiff line change
@@ -1,8 +1 @@
1-
[
2-
{
3-
"kind": 0,
4-
"message": "';' expected.",
5-
"start": 14,
6-
"length": 0
7-
}
8-
]
1+
[]

0 commit comments

Comments
 (0)