mirror of
https://github.com/nikic/PHP-Parser.git
synced 2025-07-25 16:21:23 +02:00
Add support for yield expressions (PHP 5.5)
This adds a new Yield expression type, with subnodes key and value.
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
Version 0.9.3-dev
|
||||
-----------------
|
||||
|
||||
* [PHP 5.5] Add support for `yield` expressions. This adds a new `Yield` expression type, with subnodes `key` and
|
||||
`value`.
|
||||
|
||||
* [PHP 5.5] Add support for `finally`. This adds a new `finallyStmts` subnode to the `TryCatch` node. If there is no
|
||||
finally clause it will be `null`.
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
%left T_LOGICAL_XOR
|
||||
%left T_LOGICAL_AND
|
||||
%right T_PRINT
|
||||
%right T_YIELD
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL
|
||||
%left '?' ':'
|
||||
%left T_BOOLEAN_OR
|
||||
@@ -170,20 +171,22 @@ inner_statement:
|
||||
|
||||
statement:
|
||||
'{' inner_statement_list '}' { $$ = $2; }
|
||||
| T_IF '(' expr ')' statement elseif_list else_single { $$ = Stmt_If[$3, [stmts: toArray($5), elseifs: $6, else: $7]]; }
|
||||
| T_IF '(' expr ')' ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
|
||||
{ $$ = Stmt_If[$3, [stmts: $6, elseifs: $7, else: $8]]; }
|
||||
| T_WHILE '(' expr ')' while_statement { $$ = Stmt_While[$3, $5]; }
|
||||
| T_DO statement T_WHILE '(' expr ')' ';' { $$ = Stmt_Do [$5, toArray($2)]; }
|
||||
| T_IF parentheses_expr statement elseif_list else_single
|
||||
{ $$ = Stmt_If[$2, [stmts: toArray($3), elseifs: $4, else: $5]]; }
|
||||
| T_IF parentheses_expr ':' inner_statement_list new_elseif_list new_else_single T_ENDIF ';'
|
||||
{ $$ = Stmt_If[$2, [stmts: $4, elseifs: $5, else: $6]]; }
|
||||
| T_WHILE parentheses_expr while_statement { $$ = Stmt_While[$2, $3]; }
|
||||
| T_DO statement T_WHILE parentheses_expr ';' { $$ = Stmt_Do [$4, toArray($2)]; }
|
||||
| T_FOR '(' for_expr ';' for_expr ';' for_expr ')' for_statement
|
||||
{ $$ = Stmt_For[[init: $3, cond: $5, loop: $7, stmts: $9]]; }
|
||||
| T_SWITCH '(' expr ')' switch_case_list { $$ = Stmt_Switch[$3, $5]; }
|
||||
| T_SWITCH parentheses_expr switch_case_list { $$ = Stmt_Switch[$2, $3]; }
|
||||
| T_BREAK ';' { $$ = Stmt_Break[null]; }
|
||||
| T_BREAK expr ';' { $$ = Stmt_Break[$2]; }
|
||||
| T_CONTINUE ';' { $$ = Stmt_Continue[null]; }
|
||||
| T_CONTINUE expr ';' { $$ = Stmt_Continue[$2]; }
|
||||
| T_RETURN ';' { $$ = Stmt_Return[null]; }
|
||||
| T_RETURN expr ';' { $$ = Stmt_Return[$2]; }
|
||||
| yield_expr ';' { $$ = $1; }
|
||||
| T_GLOBAL global_var_list ';' { $$ = Stmt_Global[$2]; }
|
||||
| T_STATIC static_var_list ';' { $$ = Stmt_Static[$2]; }
|
||||
| T_ECHO expr_list ';' { $$ = Stmt_Echo[$2]; }
|
||||
@@ -325,7 +328,7 @@ elseif_list:
|
||||
;
|
||||
|
||||
elseif:
|
||||
T_ELSEIF '(' expr ')' statement { $$ = Stmt_ElseIf[$3, toArray($5)]; }
|
||||
T_ELSEIF parentheses_expr statement { $$ = Stmt_ElseIf[$2, toArray($3)]; }
|
||||
;
|
||||
|
||||
new_elseif_list:
|
||||
@@ -334,7 +337,7 @@ new_elseif_list:
|
||||
;
|
||||
|
||||
new_elseif:
|
||||
T_ELSEIF '(' expr ')' ':' inner_statement_list { $$ = Stmt_ElseIf[$3, $6]; }
|
||||
T_ELSEIF parentheses_expr ':' inner_statement_list { $$ = Stmt_ElseIf[$2, $4]; }
|
||||
;
|
||||
|
||||
else_single:
|
||||
@@ -378,8 +381,9 @@ optional_class_type:
|
||||
;
|
||||
|
||||
argument_list:
|
||||
non_empty_argument_list { $$ = $1; }
|
||||
| /* empty */ { $$ = array(); }
|
||||
'(' ')' { $$ = array(); }
|
||||
| '(' non_empty_argument_list ')' { $$ = $2; }
|
||||
| '(' yield_expr ')' { $$ = array(Arg[$2, false]); }
|
||||
;
|
||||
|
||||
non_empty_argument_list:
|
||||
@@ -556,7 +560,7 @@ expr:
|
||||
| expr '>' expr { $$ = Expr_Greater [$1, $3]; }
|
||||
| expr T_IS_GREATER_OR_EQUAL expr { $$ = Expr_GreaterOrEqual[$1, $3]; }
|
||||
| expr T_INSTANCEOF class_name_reference { $$ = Expr_Instanceof [$1, $3]; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| parentheses_expr { $$ = $1; }
|
||||
/* we need a separate '(' new_expr ')' rule to avoid problems caused by a s/r conflict */
|
||||
| '(' new_expr ')' { $$ = $2; }
|
||||
| expr '?' expr ':' expr { $$ = Expr_Ternary[$1, $3, $5]; }
|
||||
@@ -565,7 +569,7 @@ expr:
|
||||
| T_EMPTY '(' variable ')' { $$ = Expr_Empty[$3]; }
|
||||
| T_INCLUDE expr { $$ = Expr_Include[$2, Expr_Include::TYPE_INCLUDE]; }
|
||||
| T_INCLUDE_ONCE expr { $$ = Expr_Include[$2, Expr_Include::TYPE_INCLUDE_ONCE]; }
|
||||
| T_EVAL '(' expr ')' { $$ = Expr_Eval[$3]; }
|
||||
| T_EVAL parentheses_expr { $$ = Expr_Eval[$2]; }
|
||||
| T_REQUIRE expr { $$ = Expr_Include[$2, Expr_Include::TYPE_REQUIRE]; }
|
||||
| T_REQUIRE_ONCE expr { $$ = Expr_Include[$2, Expr_Include::TYPE_REQUIRE_ONCE]; }
|
||||
| T_INT_CAST expr { $$ = Expr_Cast_Int [$2]; }
|
||||
@@ -582,12 +586,23 @@ expr:
|
||||
| '[' array_pair_list ']' { $$ = Expr_Array[$2]; }
|
||||
| '`' backticks_expr '`' { $$ = Expr_ShellExec[$2]; }
|
||||
| T_PRINT expr { $$ = Expr_Print[$2]; }
|
||||
| T_YIELD { $$ = Expr_Yield[null, null]; }
|
||||
| T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars '{' inner_statement_list '}'
|
||||
{ $$ = Expr_Closure[[static: false, byRef: $2, params: $4, uses: $6, stmts: $8]]; }
|
||||
| T_STATIC T_FUNCTION optional_ref '(' parameter_list ')' lexical_vars '{' inner_statement_list '}'
|
||||
{ $$ = Expr_Closure[[static: true, byRef: $3, params: $5, uses: $7, stmts: $9]]; }
|
||||
;
|
||||
|
||||
parentheses_expr:
|
||||
'(' expr ')' { $$ = $2; }
|
||||
| '(' yield_expr ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
yield_expr:
|
||||
T_YIELD expr { $$ = Expr_Yield[$2, null]; }
|
||||
| T_YIELD expr T_DOUBLE_ARROW expr { $$ = Expr_Yield[$2, $4]; }
|
||||
;
|
||||
|
||||
new_expr:
|
||||
T_NEW class_name_reference ctor_arguments { $$ = Expr_New[$2, $3]; }
|
||||
;
|
||||
@@ -611,28 +626,28 @@ lexical_var:
|
||||
;
|
||||
|
||||
function_call:
|
||||
name '(' argument_list ')' { $$ = Expr_FuncCall[$1, $3]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM T_STRING '(' argument_list ')'
|
||||
{ $$ = Expr_StaticCall[$1, $3, $5]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' '(' argument_list ')'
|
||||
{ $$ = Expr_StaticCall[$1, $4, $7]; }
|
||||
| static_property '(' argument_list ')' {
|
||||
name argument_list { $$ = Expr_FuncCall[$1, $2]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM T_STRING argument_list
|
||||
{ $$ = Expr_StaticCall[$1, $3, $4]; }
|
||||
| class_name_or_var T_PAAMAYIM_NEKUDOTAYIM '{' expr '}' argument_list
|
||||
{ $$ = Expr_StaticCall[$1, $4, $6]; }
|
||||
| static_property argument_list {
|
||||
if ($1 instanceof PHPParser_Node_Expr_StaticPropertyFetch) {
|
||||
$$ = Expr_StaticCall[$1->class, Expr_Variable[$1->name], $3];
|
||||
$$ = Expr_StaticCall[$1->class, Expr_Variable[$1->name], $2];
|
||||
} elseif ($1 instanceof PHPParser_Node_Expr_ArrayDimFetch) {
|
||||
$tmp = $1;
|
||||
while ($tmp->var instanceof PHPParser_Node_Expr_ArrayDimFetch) {
|
||||
$tmp = $tmp->var;
|
||||
}
|
||||
|
||||
$$ = Expr_StaticCall[$tmp->var->class, $1, $3];
|
||||
$$ = Expr_StaticCall[$tmp->var->class, $1, $2];
|
||||
$tmp->var = Expr_Variable[$tmp->var->name];
|
||||
} else {
|
||||
throw new Exception;
|
||||
}
|
||||
}
|
||||
| variable_without_objects '(' argument_list ')'
|
||||
{ $$ = Expr_FuncCall[$1, $3]; }
|
||||
| variable_without_objects argument_list
|
||||
{ $$ = Expr_FuncCall[$1, $2]; }
|
||||
| function_call '[' dim_offset ']' { $$ = Expr_ArrayDimFetch[$1, $3]; }
|
||||
/* alternative array syntax missing intentionally */
|
||||
;
|
||||
@@ -675,7 +690,7 @@ object_access_for_dcnr:
|
||||
exit_expr:
|
||||
/* empty */ { $$ = null; }
|
||||
| '(' ')' { $$ = null; }
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
| parentheses_expr { $$ = $1; }
|
||||
;
|
||||
|
||||
backticks_expr:
|
||||
@@ -686,7 +701,7 @@ backticks_expr:
|
||||
|
||||
ctor_arguments:
|
||||
/* empty */ { $$ = array(); }
|
||||
| '(' argument_list ')' { $$ = $2; }
|
||||
| argument_list { $$ = $1; }
|
||||
;
|
||||
|
||||
common_scalar:
|
||||
@@ -762,9 +777,9 @@ new_expr_array_deref:
|
||||
object_access:
|
||||
variable_or_new_expr T_OBJECT_OPERATOR object_property
|
||||
{ $$ = Expr_PropertyFetch[$1, $3]; }
|
||||
| variable_or_new_expr T_OBJECT_OPERATOR object_property '(' argument_list ')'
|
||||
{ $$ = Expr_MethodCall[$1, $3, $5]; }
|
||||
| object_access '(' argument_list ')' { $$ = Expr_FuncCall[$1, $3]; }
|
||||
| variable_or_new_expr T_OBJECT_OPERATOR object_property argument_list
|
||||
{ $$ = Expr_MethodCall[$1, $3, $4]; }
|
||||
| object_access argument_list { $$ = Expr_FuncCall[$1, $2]; }
|
||||
| object_access '[' dim_offset ']' { $$ = Expr_ArrayDimFetch[$1, $3]; }
|
||||
| object_access '{' expr '}' { $$ = Expr_ArrayDimFetch[$1, $3]; }
|
||||
;
|
||||
|
@@ -14,6 +14,7 @@ class PHPParser_Lexer_Emulative extends PHPParser_Lexer
|
||||
$newKeywordsPerVersion = array(
|
||||
'5.5.0-dev' => array(
|
||||
'finally' => PHPParser_Parser::T_FINALLY,
|
||||
'yield' => PHPParser_Parser::T_YIELD,
|
||||
),
|
||||
'5.4.0-dev' => array(
|
||||
'callable' => PHPParser_Parser::T_CALLABLE,
|
||||
|
25
lib/PHPParser/Node/Expr/Yield.php
Normal file
25
lib/PHPParser/Node/Expr/Yield.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @property null|PHPParser_Node_Expr $value Value expression
|
||||
* @property null|PHPParser_Node_Expr $key Key expression
|
||||
*/
|
||||
class PHPParser_Node_Expr_Yield extends PHPParser_Node_Expr
|
||||
{
|
||||
/**
|
||||
* Constructs a yield expression node.
|
||||
*
|
||||
* @param null|PHPParser_Node_Expr $value ´ Value expression
|
||||
* @param null|PHPParser_Node_Expr $key Key expression
|
||||
* @param array $attributes Additional attributes
|
||||
*/
|
||||
public function __construct(PHPParser_Node_Expr $value = null, PHPParser_Node_Expr $key = null, array $attributes = array()) {
|
||||
parent::__construct(
|
||||
array(
|
||||
'key' => $key,
|
||||
'value' => $value,
|
||||
),
|
||||
$attributes
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -32,10 +32,17 @@ class PHPParser_Tests_Lexer_EmulativeTest extends PHPUnit_Framework_TestCase
|
||||
|
||||
public function provideTestReplaceKeywords() {
|
||||
return array(
|
||||
// PHP 5.5
|
||||
array('finally', PHPParser_Parser::T_FINALLY),
|
||||
array('yield', PHPParser_Parser::T_YIELD),
|
||||
|
||||
// PHP 5.4
|
||||
array('callable', PHPParser_Parser::T_CALLABLE),
|
||||
array('insteadof', PHPParser_Parser::T_INSTEADOF),
|
||||
array('trait', PHPParser_Parser::T_TRAIT),
|
||||
array('__TRAIT__', PHPParser_Parser::T_TRAIT_C),
|
||||
|
||||
// PHP 5.3
|
||||
array('__DIR__', PHPParser_Parser::T_DIR),
|
||||
array('goto', PHPParser_Parser::T_GOTO),
|
||||
array('namespace', PHPParser_Parser::T_NAMESPACE),
|
||||
|
227
test/code/parser/stmt/function/generator.test
Normal file
227
test/code/parser/stmt/function/generator.test
Normal file
@@ -0,0 +1,227 @@
|
||||
Generators (yield expression
|
||||
-----
|
||||
<?php
|
||||
|
||||
function gen() {
|
||||
// statements
|
||||
yield;
|
||||
yield $value;
|
||||
yield $key => $value;
|
||||
|
||||
// expressions
|
||||
$data = yield;
|
||||
$data = (yield $value);
|
||||
$data = (yield $key => $value);
|
||||
|
||||
// yield in language constructs with their own parentheses
|
||||
if (yield $foo); elseif (yield $foo);
|
||||
if (yield $foo): elseif (yield $foo): endif;
|
||||
while (yield $foo);
|
||||
do {} while (yield $foo);
|
||||
switch (yield $foo) {}
|
||||
die(yield $foo);
|
||||
|
||||
// yield in function calls
|
||||
func(yield $foo);
|
||||
$foo->func(yield $foo);
|
||||
new Foo(yield $foo);
|
||||
}
|
||||
-----
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
byRef: false
|
||||
params: array(
|
||||
)
|
||||
stmts: array(
|
||||
0: Expr_Yield(
|
||||
key: null
|
||||
value: null
|
||||
)
|
||||
1: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: value
|
||||
)
|
||||
)
|
||||
2: Expr_Yield(
|
||||
key: Expr_Variable(
|
||||
name: value
|
||||
)
|
||||
value: Expr_Variable(
|
||||
name: key
|
||||
)
|
||||
)
|
||||
3: Expr_Assign(
|
||||
var: Expr_Variable(
|
||||
name: data
|
||||
)
|
||||
expr: Expr_Yield(
|
||||
key: null
|
||||
value: null
|
||||
)
|
||||
)
|
||||
4: Expr_Assign(
|
||||
var: Expr_Variable(
|
||||
name: data
|
||||
)
|
||||
expr: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: value
|
||||
)
|
||||
)
|
||||
)
|
||||
5: Expr_Assign(
|
||||
var: Expr_Variable(
|
||||
name: data
|
||||
)
|
||||
expr: Expr_Yield(
|
||||
key: Expr_Variable(
|
||||
name: value
|
||||
)
|
||||
value: Expr_Variable(
|
||||
name: key
|
||||
)
|
||||
)
|
||||
)
|
||||
6: Stmt_If(
|
||||
stmts: array(
|
||||
)
|
||||
elseifs: array(
|
||||
0: Stmt_ElseIf(
|
||||
cond: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
else: null
|
||||
cond: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
)
|
||||
7: Stmt_If(
|
||||
stmts: array(
|
||||
)
|
||||
elseifs: array(
|
||||
0: Stmt_ElseIf(
|
||||
cond: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
else: null
|
||||
cond: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
)
|
||||
8: Stmt_While(
|
||||
cond: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
9: Stmt_Do(
|
||||
cond: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
10: Stmt_Switch(
|
||||
cond: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
cases: array(
|
||||
)
|
||||
)
|
||||
11: Expr_Exit(
|
||||
expr: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
)
|
||||
12: Expr_FuncCall(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: func
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
0: Arg(
|
||||
value: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
byRef: false
|
||||
)
|
||||
)
|
||||
)
|
||||
13: Expr_MethodCall(
|
||||
var: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
name: func
|
||||
args: array(
|
||||
0: Arg(
|
||||
value: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
byRef: false
|
||||
)
|
||||
)
|
||||
)
|
||||
14: Expr_New(
|
||||
class: Name(
|
||||
parts: array(
|
||||
0: Foo
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
0: Arg(
|
||||
value: Expr_Yield(
|
||||
key: null
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
)
|
||||
byRef: false
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
name: gen
|
||||
)
|
||||
)
|
Reference in New Issue
Block a user