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:
nikic
2012-09-07 19:24:44 +02:00
parent ae3774f0f2
commit 417a8bb07e
7 changed files with 1951 additions and 1631 deletions

View File

@@ -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`.

View File

@@ -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]; }
;

View File

@@ -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,

View 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

View File

@@ -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),

View 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
)
)