Skip to content

Commit 6f9946e

Browse files
committed
Handle parenthesized breakout level
1 parent 099d0b1 commit 6f9946e

19 files changed

+318
-39
lines changed

src/DiagnosticsProvider.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,45 @@ public static function checkDiagnostics($node) {
8484
}
8585
}
8686
}
87+
else if ($node instanceof Node\Statement\BreakOrContinueStatement) {
88+
if ($node->breakoutLevel === null) {
89+
return null;
90+
}
91+
92+
$breakoutLevel = $node->breakoutLevel;
93+
while ($breakoutLevel instanceof Node\Expression\ParenthesizedExpression) {
94+
$breakoutLevel = $breakoutLevel->expression;
95+
}
96+
97+
if (
98+
$breakoutLevel instanceof Node\Expression\NumericLiteral
99+
&& \in_array($breakoutLevel->children->kind, [
100+
TokenKind::BinaryLiteralToken,
101+
TokenKind::DecimalLiteralToken,
102+
TokenKind::HexadecimalLiteralToken,
103+
TokenKind::OctalLiteralToken,
104+
TokenKind::IntegerLiteralToken
105+
])
106+
&& \intval($breakoutLevel->getText()) > 0
107+
) {
108+
return null;
109+
}
110+
111+
if ($breakoutLevel instanceof Token) {
112+
$start = $breakoutLevel->getStartPosition();
113+
}
114+
else {
115+
$start = $breakoutLevel->getStart();
116+
}
117+
$end = $breakoutLevel->getEndPosition();
118+
119+
return new Diagnostic(
120+
DiagnosticKind::Error,
121+
"Expected positive integer literal.",
122+
$start,
123+
$end - $start
124+
);
125+
}
87126
}
88127
return null;
89128
}

src/Node/Statement/BreakOrContinueStatement.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
class BreakOrContinueStatement extends StatementNode {
1313
/** @var Token */
1414
public $breakOrContinueKeyword;
15-
/** @var Token|null */
15+
/** @var Expression|null */
1616
public $breakoutLevel;
1717
/** @var Token */
1818
public $semicolon;

src/Parser.php

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1865,20 +1865,10 @@ private function parseBreakOrContinueStatement($parentNode) {
18651865
$continueStatement->parent = $parentNode;
18661866
$continueStatement->breakOrContinueKeyword = $this->eat(TokenKind::ContinueKeyword, TokenKind::BreakKeyword);
18671867

1868-
// TODO this level of granularity is unnecessary - integer-literal should be sufficient
1869-
$continueStatement->breakoutLevel =
1870-
$this->eatOptional(
1871-
TokenKind::BinaryLiteralToken,
1872-
TokenKind::DecimalLiteralToken,
1873-
TokenKind::InvalidHexadecimalLiteral,
1874-
TokenKind::InvalidBinaryLiteral,
1875-
TokenKind::FloatingLiteralToken,
1876-
TokenKind::HexadecimalLiteralToken,
1877-
TokenKind::OctalLiteralToken,
1878-
TokenKind::InvalidOctalLiteralToken,
1879-
// TODO the parser should be permissive of floating literals, but rule validation should produce error
1880-
TokenKind::IntegerLiteralToken
1881-
);
1868+
if ($this->isExpressionStart($this->getCurrentToken())) {
1869+
$continueStatement->breakoutLevel = $this->parseExpression($continueStatement);
1870+
}
1871+
18821872
$continueStatement->semicolon = $this->eatSemicolonOrAbortStatement();
18831873

18841874
return $continueStatement;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
for (;;) {
4+
break (1);
5+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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+
"ForStatement": {
16+
"for": {
17+
"kind": "ForKeyword",
18+
"textLength": 3
19+
},
20+
"openParen": {
21+
"kind": "OpenParenToken",
22+
"textLength": 1
23+
},
24+
"forInitializer": null,
25+
"exprGroupSemicolon1": {
26+
"kind": "SemicolonToken",
27+
"textLength": 1
28+
},
29+
"forControl": null,
30+
"exprGroupSemicolon2": {
31+
"kind": "SemicolonToken",
32+
"textLength": 1
33+
},
34+
"forEndOfLoop": null,
35+
"closeParen": {
36+
"kind": "CloseParenToken",
37+
"textLength": 1
38+
},
39+
"colon": null,
40+
"statements": {
41+
"CompoundStatementNode": {
42+
"openBrace": {
43+
"kind": "OpenBraceToken",
44+
"textLength": 1
45+
},
46+
"statements": [
47+
{
48+
"BreakOrContinueStatement": {
49+
"breakOrContinueKeyword": {
50+
"kind": "BreakKeyword",
51+
"textLength": 5
52+
},
53+
"breakoutLevel": {
54+
"ParenthesizedExpression": {
55+
"openParen": {
56+
"kind": "OpenParenToken",
57+
"textLength": 1
58+
},
59+
"expression": {
60+
"NumericLiteral": {
61+
"children": {
62+
"kind": "IntegerLiteralToken",
63+
"textLength": 1
64+
}
65+
}
66+
},
67+
"closeParen": {
68+
"kind": "CloseParenToken",
69+
"textLength": 1
70+
}
71+
}
72+
},
73+
"semicolon": {
74+
"kind": "SemicolonToken",
75+
"textLength": 1
76+
}
77+
}
78+
}
79+
],
80+
"closeBrace": {
81+
"kind": "CloseBraceToken",
82+
"textLength": 1
83+
}
84+
}
85+
},
86+
"endFor": null,
87+
"endForSemicolon": null
88+
}
89+
}
90+
],
91+
"endOfFileToken": {
92+
"kind": "EndOfFileToken",
93+
"textLength": 0
94+
}
95+
}
96+
}

tests/cases/parser/breakStatement3.php.tree

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@
117117
"textLength": 5
118118
},
119119
"breakoutLevel": {
120-
"kind": "IntegerLiteralToken",
121-
"textLength": 4
120+
"NumericLiteral": {
121+
"children": {
122+
"kind": "IntegerLiteralToken",
123+
"textLength": 4
124+
}
125+
}
122126
},
123127
"semicolon": {
124128
"kind": "SemicolonToken",

tests/cases/parser/breakStatement4.php.tree

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,12 @@
117117
"textLength": 5
118118
},
119119
"breakoutLevel": {
120-
"kind": "IntegerLiteralToken",
121-
"textLength": 2
120+
"NumericLiteral": {
121+
"children": {
122+
"kind": "IntegerLiteralToken",
123+
"textLength": 2
124+
}
125+
}
122126
},
123127
"semicolon": {
124128
"kind": "SemicolonToken",

tests/cases/parser/breakStatement5.php.tree

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,12 @@
8484
"textLength": 5
8585
},
8686
"breakoutLevel": {
87-
"kind": "FloatingLiteralToken",
88-
"textLength": 3
87+
"NumericLiteral": {
88+
"children": {
89+
"kind": "FloatingLiteralToken",
90+
"textLength": 3
91+
}
92+
}
8993
},
9094
"semicolon": {
9195
"kind": "SemicolonToken",

tests/cases/parser/breakStatement6.php.tree

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,12 @@
8484
"textLength": 5
8585
},
8686
"breakoutLevel": {
87-
"kind": "IntegerLiteralToken",
88-
"textLength": 1
87+
"NumericLiteral": {
88+
"children": {
89+
"kind": "IntegerLiteralToken",
90+
"textLength": 1
91+
}
92+
}
8993
},
9094
"semicolon": {
9195
"kind": "SemicolonToken",

tests/cases/parser/breakStatement8.php.tree

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,12 @@
5151
"textLength": 5
5252
},
5353
"breakoutLevel": {
54-
"kind": "IntegerLiteralToken",
55-
"textLength": 3
54+
"NumericLiteral": {
55+
"children": {
56+
"kind": "IntegerLiteralToken",
57+
"textLength": 3
58+
}
59+
}
5660
},
5761
"semicolon": {
5862
"kind": "SemicolonToken",

0 commit comments

Comments
 (0)