Skip to content

Commit

Permalink
Support underscores in number literals
Browse files Browse the repository at this point in the history
```
{{ 1000 == 1_000 ? 'yes' : 'no' }}
# now: syntax error
# this PR: "yes"
```

> As of PHP 7.4.0, integer literals may contain underscores (_) between digits, for better readability of literals. These underscores are removed by PHP's scanner.
https://www.php.net/manual/en/language.types.integer.php

This PR replicates this behaviour, using the regexp to match the literals and then remove the "_".

I'm targeting Twig4 but maybe 3.x would be ok?
I cannot think of a real case that
- does not trigger an error currently
- would work differently after this PR
  • Loading branch information
smnandre committed Nov 30, 2024
1 parent 3eed037 commit 621a8cd
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 4 deletions.
8 changes: 4 additions & 4 deletions src/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Lexer
public const STATE_INTERPOLATION = 4;

public const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
public const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A';
public const REGEX_NUMBER = '/[0-9]+(?:_[0-9]+)*(?:\.[0-9]+(?:_[0-9]+)*)?([Ee][\+\-][0-9]+)?/A';
public const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
public const REGEX_DQ_STRING_DELIM = '/"/A';
public const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
Expand Down Expand Up @@ -346,9 +346,9 @@ private function lexExpression(): void
}
// numbers
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) {
$number = (float) $match[0]; // floats
if (ctype_digit($match[0]) && $number <= \PHP_INT_MAX) {
$number = (int) $match[0]; // integers lower than the maximum
$number = (float) str_replace('_', '', $match[0]); // floats
if (ctype_digit(str_replace('_', '', $match[0])) && $number <= \PHP_INT_MAX) {
$number = (int) $number; // integers lower than the maximum
}
$this->pushToken(Token::NUMBER_TYPE, $number);
$this->moveCursor($match[0]);
Expand Down
20 changes: 20 additions & 0 deletions tests/Fixtures/expressions/underscored_numbers.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
Twig compile numbers literals with underscores correctly
--TEMPLATE--
{{ 0_0 is same as 0 ? 'ok' : 'ko' }}
{{ 1_23 is same as 123 ? 'ok' : 'ko' }}
{{ 12_3 is same as 123 ? 'ok' : 'ko' }}
{{ 1_2_3 is same as 123 ? 'ok' : 'ko' }}
{{ -1_2 is same as -12 ? 'ok' : 'ko' }}
{{ 1_2.3_4 is same as 12.34 ? 'ok' : 'ko' }}
{{ -1_2.3_4 is same as -12.34 ? 'ok' : 'ko' }}
--DATA--
return []
--EXPECT--
ok
ok
ok
ok
ok
ok
ok
8 changes: 8 additions & 0 deletions tests/Fixtures/expressions/underscored_numbers_error.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--TEST--
Twig does not allow to use 2 underscored between digits in numbers
--TEMPLATE--
{{ 1__2 }}
--DATA--
return []
--EXCEPTION--
Twig\Error\SyntaxError: Unexpected token "name" of value "__2" ("end of print statement" expected) in "index.twig" at line 2.

0 comments on commit 621a8cd

Please sign in to comment.