Skip to content

Implement ES6-style array decomposition. #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 59 additions & 24 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2462,11 +2462,12 @@ static void zend_separate_if_call_and_write(znode *node, zend_ast *ast, uint32_t
void zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type);
void zend_compile_assign(znode *result, zend_ast *ast);
static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_node);
static void zend_compile_assign_ref(znode *result, zend_ast *ast);

static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
{
znode dummy_node;
if (var_ast->kind == ZEND_AST_LIST) {
if (var_ast->kind == ZEND_AST_ARRAY) {
zend_compile_list_assign(&dummy_node, var_ast, value_node);
} else {
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN, var_ast,
Expand All @@ -2477,6 +2478,14 @@ static inline void zend_emit_assign_znode(zend_ast *var_ast, znode *value_node)
}
/* }}} */

static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
{
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN_REF, var_ast,
zend_ast_create_znode(value_node));
zend_compile_assign_ref(NULL, assign_ast);
}
/* }}} */

static zend_op *zend_delayed_compile_dim(znode *result, zend_ast *ast, uint32_t type) /* {{{ */
{
zend_ast *var_ast = ast->child[0];
Expand Down Expand Up @@ -2618,9 +2627,13 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n
zend_bool has_elems = 0;

for (i = 0; i < list->children; ++i) {
zend_ast *var_ast = list->child[i];
zend_ast *pair_ast = list->child[i];
zend_ast *var_ast = pair_ast->child[0];
znode fetch_result, dim_node;

if (pair_ast->child[1] != NULL) {
zend_error_noreturn(E_COMPILE_ERROR, "Keys are not allowed in array decomposition");
}
if (var_ast == NULL) {
continue;
}
Expand All @@ -2633,8 +2646,22 @@ static void zend_compile_list_assign(znode *result, zend_ast *ast, znode *expr_n
Z_TRY_ADDREF(expr_node->u.constant);
}

zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
zend_emit_assign_znode(var_ast, &fetch_result);
if (pair_ast->attr) {
zend_error_noreturn(E_COMPILE_ERROR, "References are not allowed in array decomposition");
} else {
switch (var_ast->kind) {
case ZEND_AST_VAR:
case ZEND_AST_STATIC_PROP:
case ZEND_AST_DIM:
case ZEND_AST_PROP:
case ZEND_AST_ARRAY:
zend_emit_op(&fetch_result, ZEND_FETCH_LIST, expr_node, &dim_node);
zend_emit_assign_znode(var_ast, &fetch_result);
break;
default:
zend_error_noreturn(E_COMPILE_ERROR, "Cannot assign to an expression");
}
}
}

if (!has_elems) {
Expand Down Expand Up @@ -2694,7 +2721,7 @@ zend_bool zend_list_has_assign_to(zend_ast *list_ast, zend_string *name) /* {{{
}

/* Recursively check nested list()s */
if (var_ast->kind == ZEND_AST_LIST && zend_list_has_assign_to(var_ast, name)) {
if (var_ast->kind == ZEND_AST_ARRAY && zend_list_has_assign_to(var_ast, name)) {
return 1;
}

Expand Down Expand Up @@ -2774,7 +2801,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */

zend_emit_op_data(&expr_node);
return;
case ZEND_AST_LIST:
case ZEND_AST_ARRAY:
if (zend_list_has_assign_to_self(var_ast, expr_ast)) {
/* list($a, $b) = $a should evaluate the right $a first */
zend_compile_simple_var_no_cv(&expr_node, expr_ast, BP_VAR_R, 0);
Expand Down Expand Up @@ -2802,29 +2829,26 @@ void zend_compile_assign_ref(znode *result, zend_ast *ast) /* {{{ */
}
zend_ensure_writable_variable(target_ast);

zend_compile_var(&target_node, target_ast, BP_VAR_W);
zend_compile_var(&source_node, source_ast, BP_VAR_REF);

if (source_node.op_type != IS_VAR && zend_is_call(source_ast)) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use result of built-in function in write context");
}

opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node);
if (!result) {
opline->result_type |= EXT_TYPE_UNUSED;
if (target_ast->kind != ZEND_AST_ARRAY) {
zend_compile_var(&target_node, target_ast, BP_VAR_W);

opline = zend_emit_op(result, ZEND_ASSIGN_REF, &target_node, &source_node);
if (!result) {
opline->result_type |= EXT_TYPE_UNUSED;
}
} else {
zend_compile_list_assign(&target_node, target_ast, &source_node);
}

if (zend_is_call(source_ast)) {
opline->extended_value = ZEND_RETURNS_FUNCTION;
}
}
/* }}} */

static inline void zend_emit_assign_ref_znode(zend_ast *var_ast, znode *value_node) /* {{{ */
{
zend_ast *assign_ast = zend_ast_create(ZEND_AST_ASSIGN_REF, var_ast,
zend_ast_create_znode(value_node));
zend_compile_assign_ref(NULL, assign_ast);
}
/* }}} */

Expand Down Expand Up @@ -4089,7 +4113,7 @@ void zend_compile_foreach(zend_ast *ast) /* {{{ */
if (key_ast->kind == ZEND_AST_REF) {
zend_error_noreturn(E_COMPILE_ERROR, "Key element cannot be a reference");
}
if (key_ast->kind == ZEND_AST_LIST) {
if (key_ast->kind == ZEND_AST_ARRAY) {
zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
}
}
Expand Down Expand Up @@ -6034,7 +6058,13 @@ static zend_bool zend_try_ct_eval_array(zval *result, zend_ast *ast) /* {{{ */
for (i = 0; i < list->children; ++i) {
zend_ast *elem_ast = list->child[i];
zend_bool by_ref = elem_ast->attr;
zend_eval_const_expr(&elem_ast->child[0]);
if (!elem_ast->child[0]) {
zval null_value;
ZVAL_NULL(&null_value);
elem_ast->child[0] = zend_ast_create_zval(&null_value);
} else {
zend_eval_const_expr(&elem_ast->child[0]);
}
zend_eval_const_expr(&elem_ast->child[1]);

if (by_ref || elem_ast->child[0]->kind != ZEND_AST_ZVAL
Expand Down Expand Up @@ -6661,11 +6691,16 @@ void zend_compile_array(znode *result, zend_ast *ast) /* {{{ */
key_node_ptr = &key_node;
}

if (by_ref) {
zend_ensure_writable_variable(value_ast);
zend_compile_var(&value_node, value_ast, BP_VAR_W);
if (value_ast) {
if (by_ref) {
zend_ensure_writable_variable(value_ast);
zend_compile_var(&value_node, value_ast, BP_VAR_W);
} else {
zend_compile_expr(&value_node, value_ast);
}
} else {
zend_compile_expr(&value_node, value_ast);
value_node.op_type = IS_CONST;
ZVAL_NULL(&value_node.u.constant);
}

if (i == 0) {
Expand Down
67 changes: 43 additions & 24 deletions Zend/zend_language_parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -233,24 +233,24 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%type <ast> unprefixed_use_declarations const_decl inner_statement
%type <ast> expr optional_expr while_statement for_statement foreach_variable
%type <ast> foreach_statement declare_statement finally_statement unset_variable variable
%type <ast> extends_from parameter optional_type argument expr_without_variable global_var
%type <ast> extends_from parameter optional_type argument expr_without_variable expr_assignment global_var
%type <ast> static_var class_statement trait_adaptation trait_precedence trait_alias
%type <ast> absolute_trait_method_reference trait_method_reference property echo_expr
%type <ast> new_expr anonymous_class class_name class_name_reference simple_variable
%type <ast> internal_functions_in_yacc
%type <ast> exit_expr scalar backticks_expr lexical_var function_call member_name property_name
%type <ast> variable_class_name dereferencable_scalar constant dereferencable
%type <ast> callable_expr callable_variable static_member new_variable
%type <ast> assignment_list_element array_pair encaps_var encaps_var_offset isset_variables
%type <ast> array_pair omittable_array_pair encaps_var encaps_var_offset isset_variables
%type <ast> top_statement_list use_declarations const_list inner_statement_list if_stmt
%type <ast> alt_if_stmt for_exprs switch_case_list global_var_list static_var_list
%type <ast> echo_expr_list unset_variables catch_list parameter_list class_statement_list
%type <ast> implements_list case_list if_stmt_without_else
%type <ast> non_empty_parameter_list argument_list non_empty_argument_list property_list
%type <ast> class_const_list class_const_decl name_list trait_adaptations method_body non_empty_for_exprs
%type <ast> ctor_arguments alt_if_stmt_without_else trait_adaptation_list lexical_vars
%type <ast> lexical_var_list encaps_list array_pair_list non_empty_array_pair_list
%type <ast> assignment_list isset_variable type return_type
%type <ast> lexical_var_list encaps_list array array_pair_list non_empty_array_pair_list omittable_array_pair_list non_empty_omittable_array_pair_list
%type <ast> isset_variable type return_type
%type <ast> identifier

%type <num> returns_ref function is_reference is_variadic variable_modifiers
Expand Down Expand Up @@ -538,7 +538,7 @@ implements_list:
foreach_variable:
variable { $$ = $1; }
| '&' variable { $$ = zend_ast_create(ZEND_AST_REF, $2); }
| T_LIST '(' assignment_list ')' { $$ = $3; }
| array { $$ = $1; }
;

for_statement:
Expand Down Expand Up @@ -852,14 +852,17 @@ new_expr:
{ $$ = $2; }
;

expr_without_variable:
T_LIST '(' assignment_list ')' '=' expr
{ $$ = zend_ast_create(ZEND_AST_ASSIGN, $3, $6); }
expr_assignment:
array '=' expr
{ $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); }
| variable '=' expr
{ $$ = zend_ast_create(ZEND_AST_ASSIGN, $1, $3); }
| variable '=' '&' variable
{ $$ = zend_ast_create(ZEND_AST_ASSIGN_REF, $1, $4); }
| T_CLONE expr { $$ = zend_ast_create(ZEND_AST_CLONE, $2); }
;

expr_without_variable:
T_CLONE expr { $$ = zend_ast_create(ZEND_AST_CLONE, $2); }
| variable T_PLUS_EQUAL expr
{ $$ = zend_ast_create_assign_op(ZEND_ASSIGN_ADD, $1, $3); }
| variable T_MINUS_EQUAL expr
Expand Down Expand Up @@ -969,6 +972,7 @@ expr_without_variable:
{ $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $3 | ZEND_ACC_STATIC, $2, $9,
zend_string_init("{closure}", sizeof("{closure}") - 1, 0),
$5, $7, $11, $8); }
| expr_assignment
;

function:
Expand Down Expand Up @@ -1041,9 +1045,14 @@ ctor_arguments:
;


dereferencable_scalar:
array:
T_ARRAY '(' array_pair_list ')' { $$ = $3; }
| T_LIST '(' omittable_array_pair_list ')' { $$ = $3; }
| '[' array_pair_list ']' { $$ = $2; }
;

dereferencable_scalar:
array { $$ = $1; }
| T_CONSTANT_ENCAPSED_STRING { $$ = $1; }
;

Expand Down Expand Up @@ -1169,20 +1178,6 @@ property_name:
| simple_variable { $$ = zend_ast_create(ZEND_AST_VAR, $1); }
;

assignment_list:
assignment_list ',' assignment_list_element
{ $$ = zend_ast_list_add($1, $3); }
| assignment_list_element
{ $$ = zend_ast_create_list(1, ZEND_AST_LIST, $1); }
;

assignment_list_element:
variable { $$ = $1; }
| T_LIST '(' assignment_list ')' { $$ = $3; }
| /* empty */ { $$ = NULL; }
;


array_pair_list:
/* empty */ { $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); }
| non_empty_array_pair_list possible_comma { $$ = $1; }
Expand All @@ -1195,6 +1190,23 @@ non_empty_array_pair_list:
{ $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, $1); }
;

omittable_array_pair_list:
/* empty */
{ $$ = zend_ast_create_list(0, ZEND_AST_ARRAY); }
| non_empty_omittable_array_pair_list
{ $$ = $1; }
| ',' omittable_array_pair
{ $$ = zend_ast_create_list(2, ZEND_AST_ARRAY,
zend_ast_create(ZEND_AST_ARRAY_ELEM, NULL, NULL), $2); }
;

non_empty_omittable_array_pair_list:
non_empty_omittable_array_pair_list ',' omittable_array_pair
{ $$ = zend_ast_list_add($1, $3); }
| array_pair
{ $$ = zend_ast_create_list(1, ZEND_AST_ARRAY, $1); }
;

array_pair:
expr T_DOUBLE_ARROW expr
{ $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, $3, $1); }
Expand All @@ -1205,6 +1217,13 @@ array_pair:
{ $$ = zend_ast_create_ex(ZEND_AST_ARRAY_ELEM, 1, $2, NULL); }
;

omittable_array_pair:
array_pair
{ $$ = $1; }
| /* empty */
{ $$ = zend_ast_create(ZEND_AST_ARRAY_ELEM, NULL, NULL); }
;

encaps_list:
encaps_list encaps_var
{ $$ = zend_ast_list_add($1, $2); }
Expand Down