Add support for first-class callables

I'm somewhat unsure about the AST structure here.
VariadicPlaceholder is not a general expression. Maybe Arg->expr
should be Expr|VariadicPlaceholder? Or possibly the call arguments
should be an array of Arg|VariadicPlaceholder?
This commit is contained in:
Nikita Popov 2021-09-03 17:18:40 +02:00
parent d2c645f163
commit 13549aa794
8 changed files with 1008 additions and 819 deletions

View File

@ -5,6 +5,9 @@ Version 4.12.1-dev
* [PHP 8.1] Added support for intersection types using a new `IntersectionType` node.
* [PHP 8.1] Added support for explicit octal literals.
* [PHP 8.1] Added support for first-class callables. These are represented using a call whose first
`Arg->expr` is an `Expr\VariadicPlaceholder`. The representation is intended to be
forward-compatible with partial function application, just like the PHP feature itself.
Version 4.12.0 (2021-07-21)
---------------------------

View File

@ -619,6 +619,12 @@ optional_return_type:
argument_list:
'(' ')' { $$ = array(); }
| '(' non_empty_argument_list optional_comma ')' { $$ = $2; }
| '(' variadic_placeholder ')' { init($2); }
;
variadic_placeholder:
T_ELLIPSIS
{ $$ = Node\Arg[Node\VariadicPlaceholder[], false, false]; }
;
non_empty_argument_list:

View File

@ -2,6 +2,7 @@
namespace PhpParser\Node;
use PhpParser\Node\Expr\VariadicPlaceholder;
use PhpParser\NodeAbstract;
class Arg extends NodeAbstract

View File

@ -0,0 +1,27 @@
<?php declare(strict_types=1);
namespace PhpParser\Node\Expr;
use PhpParser\Node\Expr;
/**
* Represents the "..." in "foo(...)" of the first-class callable syntax.
*/
class VariadicPlaceholder extends Expr {
/**
* Create a variadic argument placeholder (first-class callable syntax).
*
* @param array $attributes Additional attributes
*/
public function __construct(array $attributes = []) {
$this->attributes = $attributes;
}
public function getType(): string {
return 'Expr_VariadicPlaceholder';
}
public function getSubNodeNames(): array {
return [];
}
}

File diff suppressed because it is too large Load Diff

View File

@ -692,6 +692,10 @@ class Standard extends PrettyPrinterAbstract
}
}
protected function pExpr_VariadicPlaceholder(Expr\VariadicPlaceholder $node) {
return '...';
}
// Declarations
protected function pStmt_Namespace(Stmt\Namespace_ $node) {

View File

@ -0,0 +1,131 @@
First-class callables
-----
<?php
foo(...);
$this->foo(...);
A::foo(...);
// These are invalid, but accepted on the parser level.
new Foo(...);
#[Foo(...)]
function foo() {}
-----
!!php7
array(
0: Stmt_Expression(
expr: Expr_FuncCall(
name: Name(
parts: array(
0: foo
)
)
args: array(
0: Arg(
name: null
value: Expr_VariadicPlaceholder(
)
byRef: false
unpack: false
)
)
)
)
1: Stmt_Expression(
expr: Expr_MethodCall(
var: Expr_Variable(
name: this
)
name: Identifier(
name: foo
)
args: array(
0: Arg(
name: null
value: Expr_VariadicPlaceholder(
)
byRef: false
unpack: false
)
)
)
)
2: Stmt_Expression(
expr: Expr_StaticCall(
class: Name(
parts: array(
0: A
)
)
name: Identifier(
name: foo
)
args: array(
0: Arg(
name: null
value: Expr_VariadicPlaceholder(
)
byRef: false
unpack: false
)
)
)
)
3: Stmt_Expression(
expr: Expr_New(
class: Name(
parts: array(
0: Foo
)
)
args: array(
0: Arg(
name: null
value: Expr_VariadicPlaceholder(
)
byRef: false
unpack: false
)
)
comments: array(
0: // These are invalid, but accepted on the parser level.
)
)
comments: array(
0: // These are invalid, but accepted on the parser level.
)
)
4: Stmt_Function(
attrGroups: array(
0: AttributeGroup(
attrs: array(
0: Attribute(
name: Name(
parts: array(
0: Foo
)
)
args: array(
0: Arg(
name: null
value: Expr_VariadicPlaceholder(
)
byRef: false
unpack: false
)
)
)
)
)
)
byRef: false
name: Identifier(
name: foo
)
params: array(
)
returnType: null
stmts: array(
)
)
)

View File

@ -0,0 +1,11 @@
First-class callables
-----
<?php
foo(...);
$this->foo(...);
A::foo(...);
-----
!!php7
foo(...);
$this->foo(...);
A::foo(...);