mirror of
https://github.com/danog/PHP-Parser.git
synced 2024-11-26 20:04:48 +01:00
[PHP 7.4] Add support for arrow functions (#602)
Per RFC https://wiki.php.net/rfc/arrow_functions_v2.
This commit is contained in:
parent
78d9985d11
commit
f3b19c19ef
@ -27,7 +27,7 @@ reserved_non_modifiers:
|
||||
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
|
||||
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
|
||||
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
|
||||
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
|
||||
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
|
||||
;
|
||||
|
||||
semi_reserved:
|
||||
|
@ -27,7 +27,7 @@ reserved_non_modifiers:
|
||||
| T_FINALLY | T_THROW | T_USE | T_INSTEADOF | T_GLOBAL | T_VAR | T_UNSET | T_ISSET | T_EMPTY | T_CONTINUE | T_GOTO
|
||||
| T_FUNCTION | T_CONST | T_RETURN | T_PRINT | T_YIELD | T_LIST | T_SWITCH | T_ENDSWITCH | T_CASE | T_DEFAULT
|
||||
| T_BREAK | T_ARRAY | T_CALLABLE | T_EXTENDS | T_IMPLEMENTS | T_NAMESPACE | T_TRAIT | T_INTERFACE | T_CLASS
|
||||
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER
|
||||
| T_CLASS_C | T_TRAIT_C | T_FUNC_C | T_METHOD_C | T_LINE | T_FILE | T_DIR | T_NS_C | T_HALT_COMPILER | T_FN
|
||||
;
|
||||
|
||||
semi_reserved:
|
||||
@ -729,6 +729,12 @@ expr:
|
||||
| T_YIELD expr { $$ = Expr\Yield_[$2, null]; }
|
||||
| T_YIELD expr T_DOUBLE_ARROW expr { $$ = Expr\Yield_[$4, $2]; }
|
||||
| T_YIELD_FROM expr { $$ = Expr\YieldFrom[$2]; }
|
||||
|
||||
| T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
|
||||
{ $$ = Expr\ArrowFunction[['static' => false, 'byRef' => $2, 'params' => $4, 'returnType' => $6, 'expr' => $8]]; }
|
||||
| T_STATIC T_FN optional_ref '(' parameter_list ')' optional_return_type T_DOUBLE_ARROW expr
|
||||
{ $$ = Expr\ArrowFunction[['static' => true, 'byRef' => $3, 'params' => $5, 'returnType' => $7, 'expr' => $9]]; }
|
||||
|
||||
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars optional_return_type
|
||||
block_or_error
|
||||
{ $$ = Expr\Closure[['static' => false, 'byRef' => $2, 'params' => $4, 'uses' => $6, 'returnType' => $7, 'stmts' => $8]]; }
|
||||
|
@ -64,6 +64,7 @@
|
||||
%token T_CONTINUE
|
||||
%token T_GOTO
|
||||
%token T_FUNCTION
|
||||
%token T_FN
|
||||
%token T_CONST
|
||||
%token T_RETURN
|
||||
%token T_TRY
|
||||
|
@ -4,9 +4,12 @@ namespace PhpParser\Lexer;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Parser;
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\TokenEmulatorInterface;
|
||||
|
||||
class Emulative extends \PhpParser\Lexer
|
||||
class Emulative extends Lexer
|
||||
{
|
||||
const PHP_7_3 = '7.3.0dev';
|
||||
const PHP_7_4 = '7.4.0dev';
|
||||
@ -17,13 +20,12 @@ class Emulative extends \PhpParser\Lexer
|
||||
(?<indentation>\h*)\2(?![a-zA-Z_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
|
||||
REGEX;
|
||||
|
||||
const T_COALESCE_EQUAL = 1007;
|
||||
|
||||
/**
|
||||
* @var mixed[] Patches used to reverse changes introduced in the code
|
||||
*/
|
||||
/** @var mixed[] Patches used to reverse changes introduced in the code */
|
||||
private $patches = [];
|
||||
|
||||
/** @var TokenEmulatorInterface[] */
|
||||
private $tokenEmulators = [];
|
||||
|
||||
/**
|
||||
* @param mixed[] $options
|
||||
*/
|
||||
@ -31,8 +33,14 @@ REGEX;
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
// prepare token emulators
|
||||
$this->tokenEmulators[] = new FnTokenEmulator();
|
||||
$this->tokenEmulators[] = new CoaleseEqualTokenEmulator();
|
||||
|
||||
// add emulated tokens here
|
||||
$this->tokenMap[self::T_COALESCE_EQUAL] = Parser\Tokens::T_COALESCE_EQUAL;
|
||||
foreach ($this->tokenEmulators as $emulativeToken) {
|
||||
$this->tokenMap[$emulativeToken->getTokenId()] = $emulativeToken->getParserTokenId();
|
||||
}
|
||||
}
|
||||
|
||||
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
|
||||
@ -50,8 +58,13 @@ REGEX;
|
||||
$preparedCode = $this->processHeredocNowdoc($code);
|
||||
parent::startLexing($preparedCode, $collector);
|
||||
|
||||
// 2. emulation of ??= token
|
||||
$this->processCoaleseEqual($code);
|
||||
// add token emulation
|
||||
foreach ($this->tokenEmulators as $emulativeToken) {
|
||||
if ($emulativeToken->isEmulationNeeded($code)) {
|
||||
$this->tokens = $emulativeToken->emulate($code, $this->tokens);
|
||||
}
|
||||
}
|
||||
|
||||
$this->fixupTokens();
|
||||
|
||||
$errors = $collector->getErrors();
|
||||
@ -63,41 +76,6 @@ REGEX;
|
||||
}
|
||||
}
|
||||
|
||||
private function isCoalesceEqualEmulationNeeded(string $code): bool
|
||||
{
|
||||
// skip version where this works without emulation
|
||||
if (version_compare(\PHP_VERSION, self::PHP_7_4, '>=')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos($code, '??=') !== false;
|
||||
}
|
||||
|
||||
private function processCoaleseEqual(string $code)
|
||||
{
|
||||
if ($this->isCoalesceEqualEmulationNeeded($code) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to manually iterate and manage a count because we'll change
|
||||
// the tokens array on the way
|
||||
$line = 1;
|
||||
for ($i = 0, $c = count($this->tokens); $i < $c; ++$i) {
|
||||
if (isset($this->tokens[$i + 1])) {
|
||||
if ($this->tokens[$i][0] === T_COALESCE && $this->tokens[$i + 1] === '=') {
|
||||
array_splice($this->tokens, $i, 2, [
|
||||
[self::T_COALESCE_EQUAL, '??=', $line]
|
||||
]);
|
||||
$c--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (\is_array($this->tokens[$i])) {
|
||||
$line += substr_count($this->tokens[$i][1], "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isHeredocNowdocEmulationNeeded(string $code): bool
|
||||
{
|
||||
// skip version where this works without emulation
|
||||
@ -155,15 +133,13 @@ REGEX;
|
||||
|
||||
private function isEmulationNeeded(string $code): bool
|
||||
{
|
||||
if ($this->isHeredocNowdocEmulationNeeded($code)) {
|
||||
return true;
|
||||
foreach ($this->tokenEmulators as $emulativeToken) {
|
||||
if ($emulativeToken->isEmulationNeeded($code)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->isCoalesceEqualEmulationNeeded($code)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->isHeredocNowdocEmulationNeeded($code);
|
||||
}
|
||||
|
||||
private function fixupTokens()
|
||||
|
@ -0,0 +1,54 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
final class CoaleseEqualTokenEmulator implements TokenEmulatorInterface
|
||||
{
|
||||
const T_COALESCE_EQUAL = 1007;
|
||||
|
||||
public function getTokenId(): int
|
||||
{
|
||||
return self::T_COALESCE_EQUAL;
|
||||
}
|
||||
|
||||
public function getParserTokenId(): int
|
||||
{
|
||||
return Tokens::T_COALESCE_EQUAL;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code) : bool
|
||||
{
|
||||
// skip version where this is supported
|
||||
if (version_compare(\PHP_VERSION, Emulative::PHP_7_4, '>=')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos($code, '??=') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
// We need to manually iterate and manage a count because we'll change
|
||||
// the tokens array on the way
|
||||
$line = 1;
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
if (isset($tokens[$i + 1])) {
|
||||
if ($tokens[$i][0] === T_COALESCE && $tokens[$i + 1] === '=') {
|
||||
array_splice($tokens, $i, 2, [
|
||||
[self::T_COALESCE_EQUAL, '??=', $line]
|
||||
]);
|
||||
$c--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (\is_array($tokens[$i])) {
|
||||
$line += substr_count($tokens[$i][1], "\n");
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
}
|
66
lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php
Normal file
66
lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
final class FnTokenEmulator implements TokenEmulatorInterface
|
||||
{
|
||||
const T_FN = 1008;
|
||||
|
||||
public function getTokenId(): int
|
||||
{
|
||||
return self::T_FN;
|
||||
}
|
||||
|
||||
public function getParserTokenId(): int
|
||||
{
|
||||
return Tokens::T_FN;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code) : bool
|
||||
{
|
||||
// skip version where this is supported
|
||||
if (version_compare(\PHP_VERSION, Emulative::PHP_7_4, '>=')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strpos($code, 'fn') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
// We need to manually iterate and manage a count because we'll change
|
||||
// the tokens array on the way
|
||||
foreach ($tokens as $i => $token) {
|
||||
if ($token[0] === T_STRING && $token[1] === 'fn') {
|
||||
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $i);
|
||||
if ($previousNonSpaceToken !== null && $previousNonSpaceToken[0] === T_OBJECT_OPERATOR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokens[$i][0] = self::T_FN;
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $tokens
|
||||
* @return mixed[]|null
|
||||
*/
|
||||
private function getPreviousNonSpaceToken(array $tokens, int $start)
|
||||
{
|
||||
for ($i = $start - 1; $i >= 0; --$i) {
|
||||
if ($tokens[$i][0] === T_WHITESPACE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $tokens[$i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
17
lib/PhpParser/Lexer/TokenEmulator/TokenEmulatorInterface.php
Normal file
17
lib/PhpParser/Lexer/TokenEmulator/TokenEmulatorInterface.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
interface TokenEmulatorInterface
|
||||
{
|
||||
public function getTokenId(): int;
|
||||
|
||||
public function getParserTokenId(): int;
|
||||
|
||||
public function isEmulationNeeded(string $code): bool;
|
||||
|
||||
/**
|
||||
* @return array Modified Tokens
|
||||
*/
|
||||
public function emulate(string $code, array $tokens): array;
|
||||
}
|
71
lib/PhpParser/Node/Expr/ArrowFunction.php
Normal file
71
lib/PhpParser/Node/Expr/ArrowFunction.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Node\Expr;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
|
||||
class ArrowFunction extends Expr implements FunctionLike
|
||||
{
|
||||
/** @var bool */
|
||||
public $static;
|
||||
|
||||
/** @var bool */
|
||||
public $byRef;
|
||||
|
||||
/** @var Node\Param[] */
|
||||
public $params = [];
|
||||
|
||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType */
|
||||
public $returnType;
|
||||
|
||||
/** @var Expr */
|
||||
public $expr;
|
||||
|
||||
/**
|
||||
* @param array $subNodes Array of the following optional subnodes:
|
||||
* 'static' => false : Whether the closure is static
|
||||
* 'byRef' => false : Whether to return by reference
|
||||
* 'params' => array() : Parameters
|
||||
* 'returnType' => null : Return type
|
||||
* 'expr' => Expr : Expression body
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(array $subNodes = [], array $attributes = []) {
|
||||
parent::__construct($attributes);
|
||||
$this->static = $subNodes['static'] ?? false;
|
||||
$this->byRef = $subNodes['byRef'] ?? false;
|
||||
$this->params = $subNodes['params'] ?? [];
|
||||
$returnType = $subNodes['returnType'] ?? null;
|
||||
$this->returnType = \is_string($returnType) ? new Node\Identifier($returnType) : $returnType;
|
||||
$this->expr = $subNodes['expr'] ?? null;
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['static', 'byRef', 'params', 'returnType', 'expr'];
|
||||
}
|
||||
|
||||
public function returnsByRef() : bool {
|
||||
return $this->byRef;
|
||||
}
|
||||
|
||||
public function getParams() : array {
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
public function getReturnType() {
|
||||
return $this->returnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node\Stmt\Return_[]
|
||||
*/
|
||||
public function getStmts() : array {
|
||||
return [new Node\Stmt\Return_($this->expr)];
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
return 'Expr_ArrowFunction';
|
||||
}
|
||||
}
|
@ -6,9 +6,7 @@ use PhpParser\NodeAbstract;
|
||||
|
||||
class Name extends NodeAbstract
|
||||
{
|
||||
/**
|
||||
* @var string[] Parts of the name
|
||||
*/
|
||||
/** @var string[] Parts of the name */
|
||||
public $parts;
|
||||
|
||||
private static $specialClassNames = [
|
||||
@ -237,7 +235,7 @@ class Name extends NodeAbstract
|
||||
'Expected string, array of parts or Name instance'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function getType() : string {
|
||||
return 'Name';
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6,140 +6,142 @@ namespace PhpParser\Parser;
|
||||
final class Tokens
|
||||
{
|
||||
const YYERRTOK = 256;
|
||||
const T_INCLUDE = 257;
|
||||
const T_INCLUDE_ONCE = 258;
|
||||
const T_EVAL = 259;
|
||||
const T_REQUIRE = 260;
|
||||
const T_REQUIRE_ONCE = 261;
|
||||
const T_LOGICAL_OR = 262;
|
||||
const T_LOGICAL_XOR = 263;
|
||||
const T_LOGICAL_AND = 264;
|
||||
const T_PRINT = 265;
|
||||
const T_YIELD = 266;
|
||||
const T_DOUBLE_ARROW = 267;
|
||||
const T_YIELD_FROM = 268;
|
||||
const T_PLUS_EQUAL = 269;
|
||||
const T_MINUS_EQUAL = 270;
|
||||
const T_MUL_EQUAL = 271;
|
||||
const T_DIV_EQUAL = 272;
|
||||
const T_CONCAT_EQUAL = 273;
|
||||
const T_MOD_EQUAL = 274;
|
||||
const T_AND_EQUAL = 275;
|
||||
const T_OR_EQUAL = 276;
|
||||
const T_XOR_EQUAL = 277;
|
||||
const T_SL_EQUAL = 278;
|
||||
const T_SR_EQUAL = 279;
|
||||
const T_POW_EQUAL = 280;
|
||||
const T_COALESCE_EQUAL = 281;
|
||||
const T_COALESCE = 282;
|
||||
const T_BOOLEAN_OR = 283;
|
||||
const T_BOOLEAN_AND = 284;
|
||||
const T_IS_EQUAL = 285;
|
||||
const T_IS_NOT_EQUAL = 286;
|
||||
const T_IS_IDENTICAL = 287;
|
||||
const T_IS_NOT_IDENTICAL = 288;
|
||||
const T_SPACESHIP = 289;
|
||||
const T_IS_SMALLER_OR_EQUAL = 290;
|
||||
const T_IS_GREATER_OR_EQUAL = 291;
|
||||
const T_SL = 292;
|
||||
const T_SR = 293;
|
||||
const T_INSTANCEOF = 294;
|
||||
const T_INC = 295;
|
||||
const T_DEC = 296;
|
||||
const T_INT_CAST = 297;
|
||||
const T_DOUBLE_CAST = 298;
|
||||
const T_STRING_CAST = 299;
|
||||
const T_ARRAY_CAST = 300;
|
||||
const T_OBJECT_CAST = 301;
|
||||
const T_BOOL_CAST = 302;
|
||||
const T_UNSET_CAST = 303;
|
||||
const T_POW = 304;
|
||||
const T_NEW = 305;
|
||||
const T_CLONE = 306;
|
||||
const T_EXIT = 307;
|
||||
const T_IF = 308;
|
||||
const T_ELSEIF = 309;
|
||||
const T_ELSE = 310;
|
||||
const T_ENDIF = 311;
|
||||
const T_LNUMBER = 312;
|
||||
const T_DNUMBER = 313;
|
||||
const T_STRING = 314;
|
||||
const T_STRING_VARNAME = 315;
|
||||
const T_VARIABLE = 316;
|
||||
const T_NUM_STRING = 317;
|
||||
const T_INLINE_HTML = 318;
|
||||
const T_CHARACTER = 319;
|
||||
const T_BAD_CHARACTER = 320;
|
||||
const T_ENCAPSED_AND_WHITESPACE = 321;
|
||||
const T_CONSTANT_ENCAPSED_STRING = 322;
|
||||
const T_ECHO = 323;
|
||||
const T_DO = 324;
|
||||
const T_WHILE = 325;
|
||||
const T_ENDWHILE = 326;
|
||||
const T_FOR = 327;
|
||||
const T_ENDFOR = 328;
|
||||
const T_FOREACH = 329;
|
||||
const T_ENDFOREACH = 330;
|
||||
const T_DECLARE = 331;
|
||||
const T_ENDDECLARE = 332;
|
||||
const T_AS = 333;
|
||||
const T_SWITCH = 334;
|
||||
const T_ENDSWITCH = 335;
|
||||
const T_CASE = 336;
|
||||
const T_DEFAULT = 337;
|
||||
const T_BREAK = 338;
|
||||
const T_CONTINUE = 339;
|
||||
const T_GOTO = 340;
|
||||
const T_FUNCTION = 341;
|
||||
const T_CONST = 342;
|
||||
const T_RETURN = 343;
|
||||
const T_TRY = 344;
|
||||
const T_CATCH = 345;
|
||||
const T_FINALLY = 346;
|
||||
const T_THROW = 347;
|
||||
const T_USE = 348;
|
||||
const T_INSTEADOF = 349;
|
||||
const T_GLOBAL = 350;
|
||||
const T_STATIC = 351;
|
||||
const T_ABSTRACT = 352;
|
||||
const T_FINAL = 353;
|
||||
const T_PRIVATE = 354;
|
||||
const T_PROTECTED = 355;
|
||||
const T_PUBLIC = 356;
|
||||
const T_VAR = 357;
|
||||
const T_UNSET = 358;
|
||||
const T_ISSET = 359;
|
||||
const T_EMPTY = 360;
|
||||
const T_HALT_COMPILER = 361;
|
||||
const T_CLASS = 362;
|
||||
const T_TRAIT = 363;
|
||||
const T_INTERFACE = 364;
|
||||
const T_EXTENDS = 365;
|
||||
const T_IMPLEMENTS = 366;
|
||||
const T_OBJECT_OPERATOR = 367;
|
||||
const T_LIST = 368;
|
||||
const T_ARRAY = 369;
|
||||
const T_CALLABLE = 370;
|
||||
const T_CLASS_C = 371;
|
||||
const T_TRAIT_C = 372;
|
||||
const T_METHOD_C = 373;
|
||||
const T_FUNC_C = 374;
|
||||
const T_LINE = 375;
|
||||
const T_FILE = 376;
|
||||
const T_COMMENT = 377;
|
||||
const T_DOC_COMMENT = 378;
|
||||
const T_OPEN_TAG = 379;
|
||||
const T_OPEN_TAG_WITH_ECHO = 380;
|
||||
const T_CLOSE_TAG = 381;
|
||||
const T_WHITESPACE = 382;
|
||||
const T_START_HEREDOC = 383;
|
||||
const T_END_HEREDOC = 384;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 385;
|
||||
const T_CURLY_OPEN = 386;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 387;
|
||||
const T_NAMESPACE = 388;
|
||||
const T_NS_C = 389;
|
||||
const T_DIR = 390;
|
||||
const T_NS_SEPARATOR = 391;
|
||||
const T_ELLIPSIS = 392;
|
||||
const T_ARROW_FUNCTION = 257;
|
||||
const T_INCLUDE = 258;
|
||||
const T_INCLUDE_ONCE = 259;
|
||||
const T_EVAL = 260;
|
||||
const T_REQUIRE = 261;
|
||||
const T_REQUIRE_ONCE = 262;
|
||||
const T_LOGICAL_OR = 263;
|
||||
const T_LOGICAL_XOR = 264;
|
||||
const T_LOGICAL_AND = 265;
|
||||
const T_PRINT = 266;
|
||||
const T_YIELD = 267;
|
||||
const T_DOUBLE_ARROW = 268;
|
||||
const T_YIELD_FROM = 269;
|
||||
const T_PLUS_EQUAL = 270;
|
||||
const T_MINUS_EQUAL = 271;
|
||||
const T_MUL_EQUAL = 272;
|
||||
const T_DIV_EQUAL = 273;
|
||||
const T_CONCAT_EQUAL = 274;
|
||||
const T_MOD_EQUAL = 275;
|
||||
const T_AND_EQUAL = 276;
|
||||
const T_OR_EQUAL = 277;
|
||||
const T_XOR_EQUAL = 278;
|
||||
const T_SL_EQUAL = 279;
|
||||
const T_SR_EQUAL = 280;
|
||||
const T_POW_EQUAL = 281;
|
||||
const T_COALESCE_EQUAL = 282;
|
||||
const T_COALESCE = 283;
|
||||
const T_BOOLEAN_OR = 284;
|
||||
const T_BOOLEAN_AND = 285;
|
||||
const T_IS_EQUAL = 286;
|
||||
const T_IS_NOT_EQUAL = 287;
|
||||
const T_IS_IDENTICAL = 288;
|
||||
const T_IS_NOT_IDENTICAL = 289;
|
||||
const T_SPACESHIP = 290;
|
||||
const T_IS_SMALLER_OR_EQUAL = 291;
|
||||
const T_IS_GREATER_OR_EQUAL = 292;
|
||||
const T_SL = 293;
|
||||
const T_SR = 294;
|
||||
const T_INSTANCEOF = 295;
|
||||
const T_INC = 296;
|
||||
const T_DEC = 297;
|
||||
const T_INT_CAST = 298;
|
||||
const T_DOUBLE_CAST = 299;
|
||||
const T_STRING_CAST = 300;
|
||||
const T_ARRAY_CAST = 301;
|
||||
const T_OBJECT_CAST = 302;
|
||||
const T_BOOL_CAST = 303;
|
||||
const T_UNSET_CAST = 304;
|
||||
const T_POW = 305;
|
||||
const T_NEW = 306;
|
||||
const T_CLONE = 307;
|
||||
const T_EXIT = 308;
|
||||
const T_IF = 309;
|
||||
const T_ELSEIF = 310;
|
||||
const T_ELSE = 311;
|
||||
const T_ENDIF = 312;
|
||||
const T_LNUMBER = 313;
|
||||
const T_DNUMBER = 314;
|
||||
const T_STRING = 315;
|
||||
const T_STRING_VARNAME = 316;
|
||||
const T_VARIABLE = 317;
|
||||
const T_NUM_STRING = 318;
|
||||
const T_INLINE_HTML = 319;
|
||||
const T_CHARACTER = 320;
|
||||
const T_BAD_CHARACTER = 321;
|
||||
const T_ENCAPSED_AND_WHITESPACE = 322;
|
||||
const T_CONSTANT_ENCAPSED_STRING = 323;
|
||||
const T_ECHO = 324;
|
||||
const T_DO = 325;
|
||||
const T_WHILE = 326;
|
||||
const T_ENDWHILE = 327;
|
||||
const T_FOR = 328;
|
||||
const T_ENDFOR = 329;
|
||||
const T_FOREACH = 330;
|
||||
const T_ENDFOREACH = 331;
|
||||
const T_DECLARE = 332;
|
||||
const T_ENDDECLARE = 333;
|
||||
const T_AS = 334;
|
||||
const T_SWITCH = 335;
|
||||
const T_ENDSWITCH = 336;
|
||||
const T_CASE = 337;
|
||||
const T_DEFAULT = 338;
|
||||
const T_BREAK = 339;
|
||||
const T_CONTINUE = 340;
|
||||
const T_GOTO = 341;
|
||||
const T_FUNCTION = 342;
|
||||
const T_FN = 343;
|
||||
const T_CONST = 344;
|
||||
const T_RETURN = 345;
|
||||
const T_TRY = 346;
|
||||
const T_CATCH = 347;
|
||||
const T_FINALLY = 348;
|
||||
const T_THROW = 349;
|
||||
const T_USE = 350;
|
||||
const T_INSTEADOF = 351;
|
||||
const T_GLOBAL = 352;
|
||||
const T_STATIC = 353;
|
||||
const T_ABSTRACT = 354;
|
||||
const T_FINAL = 355;
|
||||
const T_PRIVATE = 356;
|
||||
const T_PROTECTED = 357;
|
||||
const T_PUBLIC = 358;
|
||||
const T_VAR = 359;
|
||||
const T_UNSET = 360;
|
||||
const T_ISSET = 361;
|
||||
const T_EMPTY = 362;
|
||||
const T_HALT_COMPILER = 363;
|
||||
const T_CLASS = 364;
|
||||
const T_TRAIT = 365;
|
||||
const T_INTERFACE = 366;
|
||||
const T_EXTENDS = 367;
|
||||
const T_IMPLEMENTS = 368;
|
||||
const T_OBJECT_OPERATOR = 369;
|
||||
const T_LIST = 370;
|
||||
const T_ARRAY = 371;
|
||||
const T_CALLABLE = 372;
|
||||
const T_CLASS_C = 373;
|
||||
const T_TRAIT_C = 374;
|
||||
const T_METHOD_C = 375;
|
||||
const T_FUNC_C = 376;
|
||||
const T_LINE = 377;
|
||||
const T_FILE = 378;
|
||||
const T_COMMENT = 379;
|
||||
const T_DOC_COMMENT = 380;
|
||||
const T_OPEN_TAG = 381;
|
||||
const T_OPEN_TAG_WITH_ECHO = 382;
|
||||
const T_CLOSE_TAG = 383;
|
||||
const T_WHITESPACE = 384;
|
||||
const T_START_HEREDOC = 385;
|
||||
const T_END_HEREDOC = 386;
|
||||
const T_DOLLAR_OPEN_CURLY_BRACES = 387;
|
||||
const T_CURLY_OPEN = 388;
|
||||
const T_PAAMAYIM_NEKUDOTAYIM = 389;
|
||||
const T_NAMESPACE = 390;
|
||||
const T_NS_C = 391;
|
||||
const T_DIR = 392;
|
||||
const T_NS_SEPARATOR = 393;
|
||||
const T_ELLIPSIS = 394;
|
||||
}
|
||||
|
@ -582,6 +582,15 @@ class Standard extends PrettyPrinterAbstract
|
||||
. ' {' . $this->pStmts($node->stmts) . $this->nl . '}';
|
||||
}
|
||||
|
||||
protected function pExpr_ArrowFunction(Expr\ArrowFunction $node) {
|
||||
return ($node->static ? 'static ' : '')
|
||||
. 'fn' . ($node->byRef ? '&' : '')
|
||||
. '(' . $this->pCommaSeparated($node->params) . ')'
|
||||
. (null !== $node->returnType ? ': ' . $this->p($node->returnType) : '')
|
||||
. ' => '
|
||||
. $this->p($node->expr);
|
||||
}
|
||||
|
||||
protected function pExpr_ClosureUse(Expr\ClosureUse $node) {
|
||||
return ($node->byRef ? '&' : '') . $this->p($node->var);
|
||||
}
|
||||
|
@ -1195,6 +1195,7 @@ abstract class PrettyPrinterAbstract
|
||||
$this->removalMap = [
|
||||
'Expr_ArrayDimFetch->dim' => $stripBoth,
|
||||
'Expr_ArrayItem->key' => $stripDoubleArrow,
|
||||
'Expr_ArrowFunction->returnType' => $stripColon,
|
||||
'Expr_Closure->returnType' => $stripColon,
|
||||
'Expr_Exit->expr' => $stripBoth,
|
||||
'Expr_Ternary->if' => $stripBoth,
|
||||
@ -1231,6 +1232,7 @@ abstract class PrettyPrinterAbstract
|
||||
$this->insertionMap = [
|
||||
'Expr_ArrayDimFetch->dim' => ['[', false, null, null],
|
||||
'Expr_ArrayItem->key' => [null, false, null, ' => '],
|
||||
'Expr_ArrowFunction->returnType' => [')', false, ' : ', null],
|
||||
'Expr_Closure->returnType' => [')', false, ' : ', null],
|
||||
'Expr_Ternary->if' => ['?', false, ' ', ' '],
|
||||
'Expr_Yield->key' => [\T_YIELD, false, null, ' => '],
|
||||
@ -1274,6 +1276,7 @@ abstract class PrettyPrinterAbstract
|
||||
|
||||
// comma-separated lists
|
||||
'Expr_Array->items' => ', ',
|
||||
'Expr_ArrowFunction->params' => ', ',
|
||||
'Expr_Closure->params' => ', ',
|
||||
'Expr_Closure->uses' => ', ',
|
||||
'Expr_FuncCall->args' => ', ',
|
||||
|
@ -26,7 +26,7 @@ class EmulativeTest extends LexerTest
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testNoReplaceKeywordsAfterObjectOperator($keyword) {
|
||||
public function testNoReplaceKeywordsAfterObjectOperator(string $keyword) {
|
||||
$lexer = $this->getLexer();
|
||||
$lexer->startLexing('<?php ->' . $keyword);
|
||||
|
||||
@ -35,8 +35,23 @@ class EmulativeTest extends LexerTest
|
||||
$this->assertSame(0, $lexer->getNextToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestReplaceKeywords
|
||||
*/
|
||||
public function testNoReplaceKeywordsAfterObjectOperatorWithSpaces(string $keyword) {
|
||||
$lexer = $this->getLexer();
|
||||
$lexer->startLexing('<?php -> ' . $keyword);
|
||||
|
||||
$this->assertSame(Tokens::T_OBJECT_OPERATOR, $lexer->getNextToken());
|
||||
$this->assertSame(Tokens::T_STRING, $lexer->getNextToken());
|
||||
$this->assertSame(0, $lexer->getNextToken());
|
||||
}
|
||||
|
||||
public function provideTestReplaceKeywords() {
|
||||
return [
|
||||
// PHP 7.4
|
||||
['fn', Tokens::T_FN],
|
||||
|
||||
// PHP 5.5
|
||||
['finally', Tokens::T_FINALLY],
|
||||
['yield', Tokens::T_YIELD],
|
||||
@ -88,7 +103,7 @@ class EmulativeTest extends LexerTest
|
||||
*/
|
||||
public function testErrorAfterEmulation($code) {
|
||||
$errorHandler = new ErrorHandler\Collecting;
|
||||
$lexer = $this->getLexer([]);
|
||||
$lexer = $this->getLexer();
|
||||
$lexer->startLexing('<?php ' . $code . "\0", $errorHandler);
|
||||
|
||||
$errors = $errorHandler->getErrors();
|
||||
|
@ -212,8 +212,8 @@ interface A extends C, D {
|
||||
public function a(A $a) : A;
|
||||
}
|
||||
|
||||
function fn(A $a) : A {}
|
||||
function fn2(array $a) : array {}
|
||||
function f(A $a) : A {}
|
||||
function f2(array $a) : array {}
|
||||
function(A $a) : A {};
|
||||
|
||||
function fn3(?A $a) : ?A {}
|
||||
@ -249,10 +249,10 @@ interface A extends \NS\C, \NS\D
|
||||
{
|
||||
public function a(\NS\A $a) : \NS\A;
|
||||
}
|
||||
function fn(\NS\A $a) : \NS\A
|
||||
function f(\NS\A $a) : \NS\A
|
||||
{
|
||||
}
|
||||
function fn2(array $a) : array
|
||||
function f2(array $a) : array
|
||||
{
|
||||
}
|
||||
function (\NS\A $a) : \NS\A {
|
||||
|
97
test/code/formatPreservation/arrow_function.test
Normal file
97
test/code/formatPreservation/arrow_function.test
Normal file
@ -0,0 +1,97 @@
|
||||
Arrow function
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
=>
|
||||
$a;
|
||||
-----
|
||||
$stmts[0]->expr->expr = new Expr\Variable('b');
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
=>
|
||||
$b;
|
||||
-----
|
||||
<?php
|
||||
fn(
|
||||
$a
|
||||
) => $a;
|
||||
-----
|
||||
$stmts[0]->expr->params[] = new Node\Param(new Expr\Variable('b'));
|
||||
-----
|
||||
<?php
|
||||
fn(
|
||||
$a, $b
|
||||
) => $a;
|
||||
-----
|
||||
<?php
|
||||
fn(
|
||||
$a
|
||||
)
|
||||
=>
|
||||
$a;
|
||||
-----
|
||||
// TODO: Format preserving currently not supported
|
||||
$stmts[0]->expr->params = [];
|
||||
-----
|
||||
<?php
|
||||
fn() => $a;
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
: int
|
||||
=> $a;
|
||||
-----
|
||||
$stmts[0]->expr->returnType = new Node\Identifier('bool');
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
: bool
|
||||
=> $a;
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
: int
|
||||
=> $a;
|
||||
-----
|
||||
$stmts[0]->expr->returnType = null;
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
=> $a;
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
: int
|
||||
=> $a;
|
||||
|
||||
static fn($a)
|
||||
: int
|
||||
=> $a;
|
||||
-----
|
||||
// TODO: Format preserving currently not supported
|
||||
$stmts[0]->expr->static = true;
|
||||
$stmts[1]->expr->static = false;
|
||||
-----
|
||||
<?php
|
||||
static fn($a): int => $a;
|
||||
|
||||
fn($a): int => $a;
|
||||
-----
|
||||
<?php
|
||||
fn($a)
|
||||
: int
|
||||
=> $a;
|
||||
|
||||
fn&($a)
|
||||
: int
|
||||
=> $a;
|
||||
-----
|
||||
// TODO: Format preserving currently not supported
|
||||
$stmts[0]->expr->byRef = true;
|
||||
$stmts[1]->expr->byRef = false;
|
||||
-----
|
||||
<?php
|
||||
fn&($a): int => $a;
|
||||
|
||||
fn($a): int => $a;
|
145
test/code/parser/expr/arrow_function.test
Normal file
145
test/code/parser/expr/arrow_function.test
Normal file
@ -0,0 +1,145 @@
|
||||
Arrow Functions
|
||||
-----
|
||||
<?php
|
||||
fn(bool $a) => $a;
|
||||
fn($x = 42) => $x;
|
||||
static fn(&$x) => $x;
|
||||
fn&($x) => $x;
|
||||
fn($x, ...$rest) => $rest;
|
||||
fn(): int => $x;
|
||||
-----
|
||||
!!php7
|
||||
array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
type: Identifier(
|
||||
name: bool
|
||||
)
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
expr: Expr_Variable(
|
||||
name: a
|
||||
)
|
||||
)
|
||||
)
|
||||
1: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
default: Scalar_LNumber(
|
||||
value: 42
|
||||
)
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
expr: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
)
|
||||
)
|
||||
2: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
static: true
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
type: null
|
||||
byRef: true
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
expr: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
)
|
||||
)
|
||||
3: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
static: false
|
||||
byRef: true
|
||||
params: array(
|
||||
0: Param(
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
expr: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
)
|
||||
)
|
||||
4: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
0: Param(
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
default: null
|
||||
)
|
||||
1: Param(
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: true
|
||||
var: Expr_Variable(
|
||||
name: rest
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
expr: Expr_Variable(
|
||||
name: rest
|
||||
)
|
||||
)
|
||||
)
|
||||
5: Stmt_Expression(
|
||||
expr: Expr_ArrowFunction(
|
||||
static: false
|
||||
byRef: false
|
||||
params: array(
|
||||
)
|
||||
returnType: Identifier(
|
||||
name: int
|
||||
)
|
||||
expr: Expr_Variable(
|
||||
name: x
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
18
test/code/prettyPrinter/expr/arrow_function.test
Normal file
18
test/code/prettyPrinter/expr/arrow_function.test
Normal file
@ -0,0 +1,18 @@
|
||||
Arrow function
|
||||
-----
|
||||
<?php
|
||||
|
||||
fn($a) => $a;
|
||||
fn($x = 42) => $x;
|
||||
fn(&$x) => $x;
|
||||
fn&($x) => $x;
|
||||
static fn($x, ...$rest) => $rest;
|
||||
fn(): int => $x;
|
||||
-----
|
||||
!!php7
|
||||
fn($a) => $a;
|
||||
fn($x = 42) => $x;
|
||||
fn(&$x) => $x;
|
||||
fn&($x) => $x;
|
||||
static fn($x, ...$rest) => $rest;
|
||||
fn(): int => $x;
|
@ -1,4 +1,4 @@
|
||||
wget -q https://github.com/php/php-src/archive/php-7.3.0RC1.tar.gz
|
||||
wget -q https://github.com/php/php-src/archive/PHP-7.4.tar.gz
|
||||
mkdir -p ./data/php-src
|
||||
tar -xzf ./php-7.3.0RC1.tar.gz -C ./data/php-src --strip-components=1
|
||||
tar -xzf ./PHP-7.4.tar.gz -C ./data/php-src --strip-components=1
|
||||
php -n test_old/run.php --verbose --no-progress PHP7 ./data/php-src
|
||||
|
Loading…
Reference in New Issue
Block a user