]> granicus.if.org Git - php/commitdiff
Port foreach
authorNikita Popov <nikic@php.net>
Fri, 11 Jul 2014 10:16:21 +0000 (12:16 +0200)
committerNikita Popov <nikic@php.net>
Fri, 11 Jul 2014 10:16:21 +0000 (12:16 +0200)
Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y

index 2f07cf3b426e19ce1718249ddb472f1cda766bf1..3a956717ab6e9f7ac0f75363e526dcc10c623b7f 100644 (file)
@@ -80,6 +80,8 @@ enum _zend_ast_kind {
        ZEND_AST_WHILE,
        ZEND_AST_DO_WHILE,
        ZEND_AST_FOR,
+       ZEND_AST_FOREACH,
+       ZEND_AST_REF,
        ZEND_AST_IF,
        ZEND_AST_IF_ELEM,
 };
index 0556707ca8e516a70b1d3a9a48355cb971666595..8de27b4430b75a709648522b3e9e7cacdc8c2786 100644 (file)
@@ -7164,6 +7164,118 @@ void zend_compile_for(zend_ast *ast TSRMLS_DC) {
        do_end_loop(opnum_loop, 0 TSRMLS_CC);
 }
 
+void zend_compile_foreach(zend_ast *ast TSRMLS_DC) {
+       zend_ast *expr_ast = ast->child[0];
+       zend_ast *value_ast = ast->child[1];
+       zend_ast *key_ast = ast->child[2];
+       zend_ast *stmt_ast = ast->child[3];
+       zend_bool by_ref = value_ast->kind == ZEND_AST_REF;
+       zend_bool is_variable = zend_is_variable(expr_ast) && !zend_is_call(expr_ast)
+               && zend_can_write_to_variable(expr_ast);
+
+       znode expr_node, reset_node, value_node, key_node, dummy_node;
+       zend_op *opline;
+       zend_uint opnum_reset, opnum_fetch;
+       zend_op foreach_stack_opline;
+
+       if (key_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) {
+                       zend_error_noreturn(E_COMPILE_ERROR, "Cannot use list as key element");
+               }
+       }
+
+       if (by_ref) {
+               value_ast = value_ast->child[0];
+       }
+
+       if (by_ref && is_variable) {
+               zend_compile_var(&expr_node, expr_ast, BP_VAR_W TSRMLS_CC);
+       } else {
+               zend_compile_expr(&expr_node, expr_ast TSRMLS_CC);
+       }
+
+       opnum_reset = get_next_op_number(CG(active_op_array));
+       opline = emit_op(&reset_node, ZEND_FE_RESET, &expr_node, NULL TSRMLS_CC);
+       if (by_ref && is_variable) {
+               opline->extended_value = ZEND_FE_RESET_VARIABLE; // ???
+       }
+
+       SET_NODE(foreach_stack_opline.result, &reset_node);
+       zend_stack_push(&CG(foreach_copy_stack), &foreach_stack_opline);
+
+       opnum_fetch = get_next_op_number(CG(active_op_array));
+       opline = emit_op(&value_node, ZEND_FE_FETCH, &reset_node, NULL TSRMLS_CC);
+       if (by_ref) {
+               opline->extended_value |= ZEND_FE_FETCH_BYREF;
+       }
+       if (key_ast) {
+               opline->extended_value |= ZEND_FE_FETCH_WITH_KEY;
+       }
+
+       opline = emit_op(NULL, ZEND_OP_DATA, NULL, NULL TSRMLS_CC);
+
+       /* Allocate enough space to keep HashPointer on VM stack */
+       opline->op1_type = IS_TMP_VAR;
+       opline->op1.var = get_temporary_variable(CG(active_op_array));
+       if (sizeof(HashPointer) > sizeof(zval)) {
+               /* Make sure 1 zval is enough for HashPointer (2 must be enough) */
+               get_temporary_variable(CG(active_op_array));
+       }
+
+       if (key_ast) {
+               opline->result_type = IS_TMP_VAR;
+               opline->result.opline_num = get_temporary_variable(CG(active_op_array));
+               GET_NODE(&key_node, opline->result);
+       }
+
+       if (value_ast->attr == ZEND_AST_LIST) {
+               zend_compile_list_assign(&dummy_node, value_ast, &value_node TSRMLS_CC);
+               zend_do_free(&dummy_node TSRMLS_CC);
+       } else if (by_ref) {
+               zend_compile_assign_ref_common(NULL, value_ast, &value_node TSRMLS_CC);
+       } else {
+               zend_ast *znode_ast = zend_ast_create_znode(&value_node);
+               zend_ast *assign_ast = zend_ast_create_binary(ZEND_AST_ASSIGN, value_ast, znode_ast);
+               zend_compile_expr(&dummy_node, assign_ast TSRMLS_CC);
+               zend_do_free(&dummy_node TSRMLS_CC);
+               efree(znode_ast);
+               efree(assign_ast);
+       }
+
+       if (key_ast) {
+               zend_ast *znode_ast = zend_ast_create_znode(&key_node);
+               zend_ast *assign_ast = zend_ast_create_binary(ZEND_AST_ASSIGN, key_ast, znode_ast);
+               zend_compile_expr(&dummy_node, assign_ast TSRMLS_CC);
+               zend_do_free(&dummy_node TSRMLS_CC);
+               efree(znode_ast);
+               efree(assign_ast);
+       }
+
+       do_begin_loop(TSRMLS_C);
+
+       zend_compile_stmt(stmt_ast TSRMLS_CC);
+
+       opline = emit_op(NULL, ZEND_JMP, NULL, NULL TSRMLS_CC);
+       opline->op1.opline_num = opnum_fetch;
+
+       opline = &CG(active_op_array)->opcodes[opnum_reset];
+       opline->op2.opline_num = get_next_op_number(CG(active_op_array));
+
+       opline = &CG(active_op_array)->opcodes[opnum_fetch];
+       opline->op2.opline_num = get_next_op_number(CG(active_op_array));
+
+       do_end_loop(opnum_fetch, 1 TSRMLS_CC);
+
+       {
+               zend_op *container_ptr = zend_stack_top(&CG(foreach_copy_stack));
+               generate_free_foreach_copy(container_ptr TSRMLS_CC);
+               zend_stack_del_top(&CG(foreach_copy_stack));
+       }
+}
+
 void zend_compile_if(zend_ast *ast TSRMLS_DC) {
        zend_uint i;
        zend_uint *jmp_opnums = safe_emalloc(sizeof(zend_uint), ast->children - 1, 0);
@@ -7998,6 +8110,9 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC) {
                case ZEND_AST_FOR:
                        zend_compile_for(ast TSRMLS_CC);
                        break;
+               case ZEND_AST_FOREACH:
+                       zend_compile_foreach(ast TSRMLS_CC);
+                       break;
                case ZEND_AST_IF:
                        zend_compile_if(ast TSRMLS_CC);
                        break;
index c83b2510d9183fbe2241034b39c5314b5777cb7f..eeead1e4145c3625a97a9eba6775936ac70e2e9a 100644 (file)
@@ -349,14 +349,17 @@ unticked_statement:
        |       T_INLINE_HTML { $$.u.ast = zend_ast_create_unary(ZEND_ECHO, AST_ZVAL(&$1)); }
        |       expr ';' { $$.u.ast = $1.u.ast; }
        |       T_UNSET '(' unset_variables ')' ';' { $$.u.ast = $3.u.ast; }
-       |       T_FOREACH '(' variable T_AS
-               { zend_do_foreach_begin(&$1, &$2, &$3, &$4, 1 TSRMLS_CC); }
-               foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
-               foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); AN($$); }
-       |       T_FOREACH '(' expr_without_variable T_AS
+       |       T_FOREACH '(' expr T_AS foreach_variable ')' foreach_statement
+                       { $$.u.ast = zend_ast_create(4, ZEND_AST_FOREACH,
+                             $3.u.ast, $5.u.ast, NULL, $7.u.ast); }
+       |       T_FOREACH '(' expr T_AS foreach_variable T_DOUBLE_ARROW foreach_variable ')'
+               foreach_statement
+                       { $$.u.ast = zend_ast_create(4, ZEND_AST_FOREACH,
+                             $3.u.ast, $7.u.ast, $5.u.ast, $9.u.ast); }
+       /*|     T_FOREACH '(' expr_without_variable T_AS
                { AC($3); zend_do_foreach_begin(&$1, &$2, &$3, &$4, 0 TSRMLS_CC); }
                foreach_variable foreach_optional_arg ')' { zend_do_foreach_cont(&$1, &$2, &$4, &$6, &$7 TSRMLS_CC); }
-               foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); AN($$); }
+               foreach_statement { zend_do_foreach_end(&$1, &$4 TSRMLS_CC); AN($$); }*/
        |       T_DECLARE { $1.u.op.opline_num = get_next_op_number(CG(active_op_array)); zend_do_declare_begin(TSRMLS_C); } '(' declare_list ')' declare_statement { zend_do_declare_end(&$1 TSRMLS_CC); AN($$); }
        |       ';'     /* empty statement */ { AN($$); }
        |       T_TRY { zend_do_try(&$1 TSRMLS_CC); } '{' inner_statement_list '}' { AS($4); }
@@ -473,15 +476,10 @@ interface_list:
        |       interface_list ',' fully_qualified_class_name { zend_do_implements_interface(&$3 TSRMLS_CC); }
 ;
 
-foreach_optional_arg:
-               /* empty */                                             { $$.op_type = IS_UNUSED; }
-       |       T_DOUBLE_ARROW foreach_variable { $$ = $2; }
-;
-
 foreach_variable:
-               variable                        { $$.u.ast = $1.u.ast; $$.EA = 0; }
-       |       '&' variable            { $$.u.ast = $2.u.ast; $$.EA = ZEND_PARSED_REFERENCE_VARIABLE; }
-       |       T_LIST '(' assignment_list ')' { $$.u.ast = $3.u.ast; $$.EA = ZEND_PARSED_LIST_EXPR; }
+               variable                        { $$.u.ast = $1.u.ast; }
+       |       '&' variable            { $$.u.ast = zend_ast_create_unary(ZEND_AST_REF, $2.u.ast); }
+       |       T_LIST '(' assignment_list ')' { $$.u.ast = $3.u.ast; }
 ;
 
 for_statement:
@@ -491,8 +489,8 @@ for_statement:
 
 
 foreach_statement:
-               statement { AS($1); }
-       |       ':' inner_statement_list T_ENDFOREACH ';' { AS($2); }
+               statement { $$.u.ast = $1.u.ast; }
+       |       ':' inner_statement_list T_ENDFOREACH ';' { $$.u.ast = $2.u.ast; }
 ;