]> granicus.if.org Git - php/commitdiff
Port switch
authorNikita Popov <nikic@php.net>
Fri, 11 Jul 2014 13:31:47 +0000 (15:31 +0200)
committerNikita Popov <nikic@php.net>
Fri, 11 Jul 2014 13:31:47 +0000 (15:31 +0200)
Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y

index 3a956717ab6e9f7ac0f75363e526dcc10c623b7f..9869e28a7c27285050f4fa3d8f7c0c716b0c471a 100644 (file)
@@ -84,6 +84,9 @@ enum _zend_ast_kind {
        ZEND_AST_REF,
        ZEND_AST_IF,
        ZEND_AST_IF_ELEM,
+       ZEND_AST_SWITCH,
+       ZEND_AST_SWITCH_LIST,
+       ZEND_AST_SWITCH_CASE,
 };
 
 typedef unsigned short zend_ast_kind;
index 8de27b4430b75a709648522b3e9e7cacdc8c2786..aab3f7ebd865d2383f2e9f3c6c17962ea83e01e0 100644 (file)
@@ -7316,6 +7316,89 @@ void zend_compile_if(zend_ast *ast TSRMLS_DC) {
        efree(jmp_opnums);
 }
 
+void zend_compile_switch(zend_ast *ast TSRMLS_DC) {
+       zend_ast *expr_ast = ast->child[0];
+       zend_ast *cases_ast = ast->child[1];
+
+       zend_uint i;
+       zend_bool has_default_case = 0;
+       zend_switch_entry switch_entry;
+
+       znode expr_node, case_node;
+       zend_op *opline;
+       zend_uint *jmpnz_opnums = safe_emalloc(sizeof(zend_uint), cases_ast->children, 0);
+       zend_uint opnum_default_jmp;
+
+       zend_compile_expr(&expr_node, expr_ast TSRMLS_CC);
+
+       switch_entry.cond = expr_node;
+       switch_entry.default_case = -1;
+       switch_entry.control_var = -1;
+       zend_stack_push(&CG(switch_cond_stack), (void *) &switch_entry);
+
+       do_begin_loop(TSRMLS_C);
+
+       case_node.op_type = IS_TMP_VAR;
+       case_node.u.op.var = get_temporary_variable(CG(active_op_array));
+
+       for (i = 0; i < cases_ast->children; ++i) {
+               zend_ast *case_ast = cases_ast->child[i];
+               zend_ast *cond_ast = case_ast->child[0];
+               znode cond_node;
+
+               if (!cond_ast) {
+                       has_default_case = 1;
+                       continue;
+               }
+
+               zend_compile_expr(&cond_node, cond_ast TSRMLS_CC);
+
+               opline = emit_op(NULL, ZEND_CASE, &expr_node, &cond_node TSRMLS_CC);
+               SET_NODE(opline->result, &case_node);
+               if (opline->op1_type == IS_CONST) {
+                       zval_copy_ctor(&CONSTANT(opline->op1.constant));
+               }
+
+               jmpnz_opnums[i] = get_next_op_number(CG(active_op_array));
+               emit_op(NULL, ZEND_JMPNZ, &case_node, NULL TSRMLS_CC);
+       }
+
+       opnum_default_jmp = get_next_op_number(CG(active_op_array));
+       emit_op(NULL, ZEND_JMP, NULL, NULL TSRMLS_CC);
+
+       for (i = 0; i < cases_ast->children; ++i) {
+               zend_ast *case_ast = cases_ast->child[i];
+               zend_ast *cond_ast = case_ast->child[0];
+               zend_ast *stmt_ast = case_ast->child[1];
+
+               if (cond_ast) {
+                       opline = &CG(active_op_array)->opcodes[jmpnz_opnums[i]];
+                       opline->op2.opline_num = get_next_op_number(CG(active_op_array));
+               } else {
+                       opline = &CG(active_op_array)->opcodes[opnum_default_jmp];
+                       opline->op1.opline_num = get_next_op_number(CG(active_op_array));
+               }
+
+               zend_compile_stmt(stmt_ast TSRMLS_CC);
+       }
+
+       if (!has_default_case) {
+               opline = &CG(active_op_array)->opcodes[opnum_default_jmp];
+               opline->op1.opline_num = get_next_op_number(CG(active_op_array));
+       }
+
+       do_end_loop(get_next_op_number(CG(active_op_array)), 1 TSRMLS_CC);
+
+       if (expr_node.op_type == IS_VAR || expr_node.op_type == IS_TMP_VAR) {
+               emit_op(NULL, expr_node.op_type == IS_TMP_VAR ? ZEND_FREE : ZEND_SWITCH_FREE,
+                       &expr_node, NULL TSRMLS_CC);
+       } else if (expr_node.op_type == IS_CONST) {
+               zval_dtor(&expr_node.u.constant);
+       }
+
+       zend_stack_del_top(&CG(switch_cond_stack));
+}
+
 void zend_compile_stmt_list(zend_ast *ast TSRMLS_DC) {
        zend_uint i;
        for (i = 0; i < ast->children; ++i) {
@@ -8116,6 +8199,9 @@ void zend_compile_stmt(zend_ast *ast TSRMLS_DC) {
                case ZEND_AST_IF:
                        zend_compile_if(ast TSRMLS_CC);
                        break;
+               case ZEND_AST_SWITCH:
+                       zend_compile_switch(ast TSRMLS_CC);
+                       break;
                default:
                {
                        znode result;
index eeead1e4145c3625a97a9eba6775936ac70e2e9a..c832129a7d980e5c156be74a24d51f907cdfc055 100644 (file)
@@ -334,7 +334,9 @@ unticked_statement:
                        { $$.u.ast = zend_ast_create_binary(ZEND_AST_DO_WHILE, $2.u.ast, $4.u.ast); }
        |       T_FOR '(' for_expr ';' for_expr ';' for_expr ')' for_statement
                        { $$.u.ast = zend_ast_create(4, ZEND_AST_FOR, $3.u.ast, $5.u.ast, $7.u.ast, $9.u.ast); }
-       |       T_SWITCH parenthesis_expr       { AC($2); zend_do_switch_cond(&$2 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$4 TSRMLS_CC); AN($$); }
+       |       T_SWITCH parenthesis_expr switch_case_list
+                       { $$.u.ast = zend_ast_create_binary(ZEND_AST_SWITCH, $2.u.ast, $3.u.ast); }
+       /*|     T_SWITCH parenthesis_expr       { AC($2); zend_do_switch_cond(&$2 TSRMLS_CC); } switch_case_list { zend_do_switch_end(&$4 TSRMLS_CC); AN($$); }*/
        |       T_BREAK ';'                     { $$.u.ast = zend_ast_create_unary(ZEND_BRK, NULL); }
        |       T_BREAK expr ';'        { $$.u.ast = zend_ast_create_unary(ZEND_BRK, $2.u.ast); }
        |       T_CONTINUE ';'          { $$.u.ast = zend_ast_create_unary(ZEND_CONT, NULL); }
@@ -356,10 +358,6 @@ unticked_statement:
                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($$); }*/
        |       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); }
@@ -507,17 +505,22 @@ declare_list:
 
 
 switch_case_list:
-               '{' case_list '}'                                       { $$ = $2; }
-       |       '{' ';' case_list '}'                           { $$ = $3; }
-       |       ':' case_list T_ENDSWITCH ';'           { $$ = $2; }
-       |       ':' ';' case_list T_ENDSWITCH ';'       { $$ = $3; }
+               '{' case_list '}'                                       { $$.u.ast = $2.u.ast; }
+       |       '{' ';' case_list '}'                           { $$.u.ast = $3.u.ast; }
+       |       ':' case_list T_ENDSWITCH ';'           { $$.u.ast = $2.u.ast; }
+       |       ':' ';' case_list T_ENDSWITCH ';'       { $$.u.ast = $3.u.ast; }
 ;
 
 
 case_list:
-               /* empty */     { $$.op_type = IS_UNUSED; }
-       |       case_list T_CASE expr case_separator { AC($3); zend_do_extended_info(TSRMLS_C);  zend_do_case_before_statement(&$1, &$2, &$3 TSRMLS_CC); } inner_statement_list { AS($6); zend_do_case_after_statement(&$$, &$2 TSRMLS_CC); $$.op_type = IS_CONST; }
-       |       case_list T_DEFAULT case_separator { zend_do_extended_info(TSRMLS_C);  zend_do_default_before_statement(&$1, &$2 TSRMLS_CC); } inner_statement_list { AS($5); zend_do_case_after_statement(&$$, &$2 TSRMLS_CC); $$.op_type = IS_CONST; }
+               /* empty */ { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_SWITCH_LIST); }
+       |       case_list T_CASE expr case_separator inner_statement_list
+                       { $$.u.ast = zend_ast_dynamic_add($1.u.ast,
+                             zend_ast_create_binary(ZEND_AST_SWITCH_CASE, $3.u.ast, $5.u.ast)); }
+       |       case_list T_DEFAULT case_separator inner_statement_list
+                       { $$.u.ast = zend_ast_dynamic_add($1.u.ast,
+                             zend_ast_create_binary(ZEND_AST_SWITCH_CASE, NULL, $4.u.ast)); }
+       /*|     case_list T_CASE expr case_separator { AC($3); zend_do_extended_info(TSRMLS_C);  zend_do_case_before_statement(&$1, &$2, &$3 TSRMLS_CC); } inner_statement_list { AS($6); zend_do_case_after_statement(&$$, &$2 TSRMLS_CC); $$.op_type = IS_CONST; }*/
 ;