Skip to content

Commit 0b4de96

Browse files
jiripudilondrejmirtes
authored andcommitted
ConstExprParser: support numeric literal separator
1 parent 308c57c commit 0b4de96

File tree

6 files changed

+52
-13
lines changed

6 files changed

+52
-13
lines changed

doc/grammars/type.abnf

+8-8
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,18 @@ ConstantExpr
8585
/ ConstantFetch *ByteHorizontalWs
8686

8787
ConstantFloat
88-
= ["-"] 1*ByteDecDigit "." *ByteDecDigit [ConstantFloatExp]
89-
/ ["-"] 1*ByteDecDigit ConstantFloatExp
90-
/ ["-"] "." 1*ByteDecDigit [ConstantFloatExp]
88+
= ["-"] 1*ByteDecDigit *("_" 1*ByteDecDigit) "." [1*ByteDecDigit *("_" 1*ByteDecDigit)] [ConstantFloatExp]
89+
/ ["-"] 1*ByteDecDigit *("_" 1*ByteDecDigit) ConstantFloatExp
90+
/ ["-"] "." 1*ByteDecDigit *("_" 1*ByteDecDigit) [ConstantFloatExp]
9191

9292
ConstantFloatExp
93-
= "e" ["-"] 1*ByteDecDigit
93+
= "e" ["-"] 1*ByteDecDigit *("_" 1*ByteDecDigit)
9494

9595
ConstantInt
96-
= ["-"] "0b" 1*ByteBinDigit
97-
/ ["-"] "0o" 1*ByteOctDigit
98-
/ ["-"] "0x" 1*ByteHexDigit
99-
/ ["-"] 1*ByteDecDigit
96+
= ["-"] "0b" 1*ByteBinDigit *("_" 1*ByteBinDigit)
97+
/ ["-"] "0o" 1*ByteOctDigit *("_" 1*ByteOctDigit)
98+
/ ["-"] "0x" 1*ByteHexDigit *("_" 1*ByteHexDigit)
99+
/ ["-"] 1*ByteDecDigit *("_" 1*ByteDecDigit)
100100

101101
ConstantTrue
102102
= "true"

src/Lexer/Lexer.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,8 @@ private function generateRegexp(): string
160160
self::TOKEN_PHPDOC_TAG => '@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+',
161161
self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/)\\x20?+)?',
162162

163-
self::TOKEN_FLOAT => '(?:-?[0-9]++\\.[0-9]*+(?:e-?[0-9]++)?)|(?:-?[0-9]*+\\.[0-9]++(?:e-?[0-9]++)?)|(?:-?[0-9]++e-?[0-9]++)',
164-
self::TOKEN_INTEGER => '-?(?:(?:0b[0-1]++)|(?:0o[0-7]++)|(?:0x[0-9a-f]++)|(?:[0-9]++))',
163+
self::TOKEN_FLOAT => '(?:-?[0-9]++(_[0-9]++)*\\.[0-9]*(_[0-9]++)*+(?:e-?[0-9]++(_[0-9]++)*)?)|(?:-?[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e-?[0-9]++(_[0-9]++)*)?)|(?:-?[0-9]++(_[0-9]++)*e-?[0-9]++(_[0-9]++)*)',
164+
self::TOKEN_INTEGER => '-?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))',
165165
self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'',
166166
self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"',
167167

src/Parser/ConstExprParser.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use PHPStan\PhpDocParser\Ast;
66
use PHPStan\PhpDocParser\Lexer\Lexer;
7+
use function str_replace;
78
use function strtolower;
89
use function substr;
910

@@ -47,7 +48,7 @@ public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\Con
4748

4849
return $this->enrichWithAttributes(
4950
$tokens,
50-
new Ast\ConstExpr\ConstExprFloatNode($value),
51+
new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)),
5152
$startLine,
5253
$startIndex
5354
);
@@ -59,7 +60,7 @@ public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\Con
5960

6061
return $this->enrichWithAttributes(
6162
$tokens,
62-
new Ast\ConstExpr\ConstExprIntegerNode($value),
63+
new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)),
6364
$startLine,
6465
$startIndex
6566
);

src/Parser/TypeParser.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use PHPStan\PhpDocParser\Ast;
77
use PHPStan\PhpDocParser\Lexer\Lexer;
88
use function in_array;
9+
use function str_replace;
910
use function strpos;
1011
use function trim;
1112

@@ -710,7 +711,7 @@ private function parseArrayShapeKey(TokenIterator $tokens)
710711
$startLine = $tokens->currentTokenLine();
711712

712713
if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) {
713-
$key = new Ast\ConstExpr\ConstExprIntegerNode($tokens->currentTokenValue());
714+
$key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue()));
714715
$tokens->next();
715716

716717
} elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) {

tests/PHPStan/Parser/ConstExprParserTest.php

+20
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,21 @@ public function provideIntegerNodeParseData(): Iterator
176176
'-0X7Fb4',
177177
new ConstExprIntegerNode('-0X7Fb4'),
178178
];
179+
180+
yield [
181+
'123_456',
182+
new ConstExprIntegerNode('123456'),
183+
];
184+
185+
yield [
186+
'0b01_01_01',
187+
new ConstExprIntegerNode('0b010101'),
188+
];
189+
190+
yield [
191+
'-0X7_Fb_4',
192+
new ConstExprIntegerNode('-0X7Fb4'),
193+
];
179194
}
180195

181196

@@ -240,6 +255,11 @@ public function provideFloatNodeParseData(): Iterator
240255
'-12.3e-4',
241256
new ConstExprFloatNode('-12.3e-4'),
242257
];
258+
259+
yield [
260+
'-1_2.3_4e5_6',
261+
new ConstExprFloatNode('-12.34e56'),
262+
];
243263
}
244264

245265

tests/PHPStan/Parser/TypeParserTest.php

+17
Original file line numberDiff line numberDiff line change
@@ -1035,10 +1035,27 @@ public function provideParseData(): array
10351035
'123',
10361036
new ConstTypeNode(new ConstExprIntegerNode('123')),
10371037
],
1038+
[
1039+
'123_456',
1040+
new ConstTypeNode(new ConstExprIntegerNode('123456')),
1041+
],
1042+
[
1043+
'_123',
1044+
new IdentifierTypeNode('_123'),
1045+
],
1046+
[
1047+
'123_',
1048+
new ConstTypeNode(new ConstExprIntegerNode('123')),
1049+
Lexer::TOKEN_IDENTIFIER,
1050+
],
10381051
[
10391052
'123.2',
10401053
new ConstTypeNode(new ConstExprFloatNode('123.2')),
10411054
],
1055+
[
1056+
'123_456.789_012',
1057+
new ConstTypeNode(new ConstExprFloatNode('123456.789012')),
1058+
],
10421059
[
10431060
'"bar"',
10441061
new ConstTypeNode(new QuoteAwareConstExprStringNode('bar', QuoteAwareConstExprStringNode::DOUBLE_QUOTED)),

0 commit comments

Comments
 (0)