Skip to content

Commit 694b153

Browse files
authored
Merge pull request #228 from TysonAndre/fix-unary-lhs-binary
For #19: Fix an edge case parsing `=` and `instanceof`
2 parents 90dc39f + d996815 commit 694b153

13 files changed

+350
-2
lines changed

src/Parser.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,8 +1686,35 @@ private function parseBinaryExpressionOrHigher($precedence, $parentNode) {
16861686
//
16871687
// After we finish building the BinaryExpression, we rebuild the UnaryExpression so that it includes
16881688
// the original operator, and the newly constructed exponentiation-expression as the operand.
1689-
$shouldOperatorTakePrecedenceOverUnary =
1690-
$token->kind === TokenKind::AsteriskAsteriskToken && $leftOperand instanceof UnaryExpression;
1689+
$shouldOperatorTakePrecedenceOverUnary = false;
1690+
switch ($token->kind) {
1691+
case TokenKind::AsteriskAsteriskToken:
1692+
$shouldOperatorTakePrecedenceOverUnary = $leftOperand instanceof UnaryExpression;
1693+
break;
1694+
case TokenKind::EqualsToken:
1695+
case TokenKind::AsteriskAsteriskEqualsToken:
1696+
case TokenKind::AsteriskEqualsToken:
1697+
case TokenKind::SlashEqualsToken:
1698+
case TokenKind::PercentEqualsToken:
1699+
case TokenKind::PlusEqualsToken:
1700+
case TokenKind::MinusEqualsToken:
1701+
case TokenKind::DotEqualsToken:
1702+
case TokenKind::LessThanLessThanEqualsToken:
1703+
case TokenKind::GreaterThanGreaterThanEqualsToken:
1704+
case TokenKind::AmpersandEqualsToken:
1705+
case TokenKind::CaretEqualsToken:
1706+
case TokenKind::BarEqualsToken:
1707+
case TokenKind::InstanceOfKeyword:
1708+
// Workarounds for https://github.com/Microsoft/tolerant-php-parser/issues/19#issue-201714377
1709+
// Parse `!$a = $b` as `!($a = $b)` - PHP constrains the Left Hand Side of an assignment to a variable. A unary operator (`@`, `!`, etc.) is not a variable.
1710+
// Instanceof has similar constraints for the LHS.
1711+
// So does `!$a += $b`
1712+
// TODO: Any other operators?
1713+
if ($leftOperand instanceof UnaryOpExpression) {
1714+
$shouldOperatorTakePrecedenceOverUnary = true;
1715+
}
1716+
break;
1717+
}
16911718

16921719
if ($shouldOperatorTakePrecedenceOverUnary) {
16931720
$unaryExpression = $leftOperand;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
echo ~$b = $c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
"EchoExpression": {
18+
"echoKeyword": {
19+
"kind": "EchoKeyword",
20+
"textLength": 4
21+
},
22+
"expressions": {
23+
"ExpressionList": {
24+
"children": [
25+
{
26+
"UnaryOpExpression": {
27+
"operator": {
28+
"kind": "TildeToken",
29+
"textLength": 1
30+
},
31+
"operand": {
32+
"AssignmentExpression": {
33+
"leftOperand": {
34+
"Variable": {
35+
"dollar": null,
36+
"name": {
37+
"kind": "VariableName",
38+
"textLength": 2
39+
}
40+
}
41+
},
42+
"operator": {
43+
"kind": "EqualsToken",
44+
"textLength": 1
45+
},
46+
"byRef": null,
47+
"rightOperand": {
48+
"Variable": {
49+
"dollar": null,
50+
"name": {
51+
"kind": "VariableName",
52+
"textLength": 2
53+
}
54+
}
55+
}
56+
}
57+
}
58+
}
59+
}
60+
]
61+
}
62+
}
63+
}
64+
},
65+
"semicolon": {
66+
"kind": "SemicolonToken",
67+
"textLength": 1
68+
}
69+
}
70+
}
71+
],
72+
"endOfFileToken": {
73+
"kind": "EndOfFileToken",
74+
"textLength": 0
75+
}
76+
}
77+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
echo !$b += $c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
"EchoExpression": {
18+
"echoKeyword": {
19+
"kind": "EchoKeyword",
20+
"textLength": 4
21+
},
22+
"expressions": {
23+
"ExpressionList": {
24+
"children": [
25+
{
26+
"UnaryOpExpression": {
27+
"operator": {
28+
"kind": "ExclamationToken",
29+
"textLength": 1
30+
},
31+
"operand": {
32+
"BinaryExpression": {
33+
"leftOperand": {
34+
"Variable": {
35+
"dollar": null,
36+
"name": {
37+
"kind": "VariableName",
38+
"textLength": 2
39+
}
40+
}
41+
},
42+
"operator": {
43+
"kind": "PlusEqualsToken",
44+
"textLength": 2
45+
},
46+
"rightOperand": {
47+
"Variable": {
48+
"dollar": null,
49+
"name": {
50+
"kind": "VariableName",
51+
"textLength": 2
52+
}
53+
}
54+
}
55+
}
56+
}
57+
}
58+
}
59+
]
60+
}
61+
}
62+
}
63+
},
64+
"semicolon": {
65+
"kind": "SemicolonToken",
66+
"textLength": 1
67+
}
68+
}
69+
}
70+
],
71+
"endOfFileToken": {
72+
"kind": "EndOfFileToken",
73+
"textLength": 0
74+
}
75+
}
76+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
3+
echo !$b instanceof $c;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
"EchoExpression": {
18+
"echoKeyword": {
19+
"kind": "EchoKeyword",
20+
"textLength": 4
21+
},
22+
"expressions": {
23+
"ExpressionList": {
24+
"children": [
25+
{
26+
"UnaryOpExpression": {
27+
"operator": {
28+
"kind": "ExclamationToken",
29+
"textLength": 1
30+
},
31+
"operand": {
32+
"BinaryExpression": {
33+
"leftOperand": {
34+
"Variable": {
35+
"dollar": null,
36+
"name": {
37+
"kind": "VariableName",
38+
"textLength": 2
39+
}
40+
}
41+
},
42+
"operator": {
43+
"kind": "InstanceOfKeyword",
44+
"textLength": 10
45+
},
46+
"rightOperand": {
47+
"Variable": {
48+
"dollar": null,
49+
"name": {
50+
"kind": "VariableName",
51+
"textLength": 2
52+
}
53+
}
54+
}
55+
}
56+
}
57+
}
58+
}
59+
]
60+
}
61+
}
62+
}
63+
},
64+
"semicolon": {
65+
"kind": "SemicolonToken",
66+
"textLength": 1
67+
}
68+
}
69+
}
70+
],
71+
"endOfFileToken": {
72+
"kind": "EndOfFileToken",
73+
"textLength": 0
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)