Skip to content

Commit

Permalink
Add support for yield expressions (PHP 5.5)
Browse files Browse the repository at this point in the history
This adds a new Yield expression type, with subnodes key and value.
  • Loading branch information
nikic committed Sep 7, 2012
1 parent ae3774f commit 417a8bb
Show file tree
Hide file tree
Showing 7 changed files with 1,328 additions and 1,008 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -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`.

Expand Down
69 changes: 42 additions & 27 deletions grammar/zend_language_parser.phpy
Expand Up @@ -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
Expand Down Expand Up @@ -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]; }
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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]; }
Expand All @@ -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]; }
Expand All @@ -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]; }
;
Expand All @@ -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 */
;
Expand Down Expand Up @@ -675,7 +690,7 @@ object_access_for_dcnr:
exit_expr:
/* empty */ { $$ = null; }
| '(' ')' { $$ = null; }
| '(' expr ')' { $$ = $2; }
| parentheses_expr { $$ = $1; }
;

backticks_expr:
Expand All @@ -686,7 +701,7 @@ backticks_expr:

ctor_arguments:
/* empty */ { $$ = array(); }
| '(' argument_list ')' { $$ = $2; }
| argument_list { $$ = $1; }
;

common_scalar:
Expand Down Expand Up @@ -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]; }
;
Expand Down
1 change: 1 addition & 0 deletions lib/PHPParser/Lexer/Emulative.php
Expand Up @@ -14,6 +14,7 @@ public function __construct() {
$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,
Expand Down
25 changes: 25 additions & 0 deletions lib/PHPParser/Node/Expr/Yield.php
@@ -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
);
}
}

0 comments on commit 417a8bb

Please sign in to comment.