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

index 7f885ab029c1f9a43d7b4a2bfe93a89cce4b83eb..3c22fda3493d224bf83ea1b7daa9e2490757618b 100644 (file)
@@ -101,6 +101,7 @@ enum _zend_ast_kind {
 
        ZEND_AST_FUNC_DECL,
 
+       ZEND_AST_CLOSURE,
        ZEND_AST_CLOSURE_USES,
 };
 
index 7daa61880318ffdd177bb8215a68d5d2000e9154..2e8e88b00aa249cb578e48a3a8aacf1a7fbd85f8 100644 (file)
@@ -7004,14 +7004,9 @@ void zend_compile_closure_uses(zend_ast *ast TSRMLS_DC) {
        }
 }
 
-void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) {
-       zend_ast *name_ast = ast->child[0];
-       zend_ast *params_ast = ast->child[1];
-       zend_ast *stmt_ast = ast->child[2];
-       zend_bool returns_ref = ast->attr;
-       zend_string *name = Z_STR_P(zend_ast_get_zval(name_ast));
-
-       zend_op_array *orig_op_array = CG(active_op_array);
+static zend_op *zend_begin_func_decl(
+       zend_string *name, zend_bool returns_ref, zend_uint start_lineno TSRMLS_DC
+) {
        zend_op_array *op_array = emalloc(sizeof(zend_op_array));
        zend_string *lcname;
        zend_op *opline;
@@ -7027,7 +7022,7 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) {
                op_array->function_name = STR_COPY(name);
        }
 
-       op_array->line_start = ast->lineno;
+       op_array->line_start = start_lineno;
        if (returns_ref) {
                op_array->fn_flags |= ZEND_ACC_RETURN_REFERENCE;
        }
@@ -7072,10 +7067,8 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) {
        zend_init_compiler_context(TSRMLS_C);
 
        if (CG(compiler_options) & ZEND_COMPILE_EXTENDED_INFO) {
-               zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
-
-               opline = emit_op(NULL, ZEND_EXT_NOP, NULL, NULL TSRMLS_CC);
-               opline->lineno = ast->lineno;
+               zend_op *opline_ext = emit_op(NULL, ZEND_EXT_NOP, NULL, NULL TSRMLS_CC);
+               opline_ext->lineno = start_lineno;
        }
 
        {
@@ -7098,9 +7091,10 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) {
                zend_stack_push(&CG(foreach_copy_stack), (void *) &dummy_opline);
        }
 
-       zend_compile_params(params_ast TSRMLS_CC);
-       zend_compile_stmt(stmt_ast TSRMLS_CC);
+       return opline;
+}
 
+static void zend_end_func_decl(TSRMLS_D) {
        zend_do_extended_info(TSRMLS_C);
        zend_do_return(NULL, 0 TSRMLS_CC);
 
@@ -7111,13 +7105,57 @@ void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) {
        // TODO.AST end line
        // TODO.AST doc comment
 
-       CG(active_op_array) = orig_op_array;
-
        /* Pop the switch and foreach separators */
        zend_stack_del_top(&CG(switch_cond_stack));
        zend_stack_del_top(&CG(foreach_copy_stack));
 }
 
+void zend_compile_func_decl(zend_ast *ast TSRMLS_DC) {
+       zend_ast *name_ast = ast->child[0];
+       zend_ast *params_ast = ast->child[1];
+       zend_ast *stmt_ast = ast->child[2];
+       zend_bool returns_ref = ast->attr;
+       zend_string *name = Z_STR_P(zend_ast_get_zval(name_ast));
+
+       zend_op_array *orig_op_array = CG(active_op_array);
+       zend_begin_func_decl(name, returns_ref, ast->lineno TSRMLS_CC);
+
+       zend_compile_params(params_ast TSRMLS_CC);
+       zend_compile_stmt(stmt_ast TSRMLS_CC);
+
+       zend_end_func_decl(TSRMLS_C);
+       CG(active_op_array) = orig_op_array;
+}
+
+void zend_compile_closure(znode *result, zend_ast *ast TSRMLS_DC) {
+       zend_ast *params_ast = ast->child[0];
+       zend_ast *uses_ast = ast->child[1];
+       zend_ast *stmt_ast = ast->child[2];
+       zend_bool returns_ref = ast->attr;
+       zend_string *name = STR_INIT("{closure}", sizeof("{closure}") - 1, 0);
+
+       zend_op_array *orig_op_array = CG(active_op_array);
+       zend_op *opline = zend_begin_func_decl(name, returns_ref, ast->lineno TSRMLS_CC);
+
+       result->op_type = IS_TMP_VAR;
+       result->u.op.var = get_temporary_variable(orig_op_array);
+
+       opline->opcode = ZEND_DECLARE_LAMBDA_FUNCTION;
+       zend_del_literal(orig_op_array, opline->op2.constant);
+       SET_UNUSED(opline->op2);
+       SET_NODE(opline->result, result);
+       CG(active_op_array)->fn_flags |= ZEND_ACC_CLOSURE;
+
+       zend_compile_closure_uses(uses_ast TSRMLS_CC);
+       zend_compile_params(params_ast TSRMLS_CC);
+       zend_compile_stmt(stmt_ast TSRMLS_CC);
+
+       zend_end_func_decl(TSRMLS_C);
+       CG(active_op_array) = orig_op_array;
+
+       STR_RELEASE(name);
+}
+
 void zend_compile_binary_op(znode *result, zend_ast *ast TSRMLS_DC) {
        zend_ast *left_ast = ast->child[0];
        zend_ast *right_ast = ast->child[1];
@@ -8083,6 +8121,9 @@ void zend_compile_expr(znode *result, zend_ast *ast TSRMLS_DC) {
                case ZEND_AST_MAGIC_CONST:
                        zend_compile_magic_const(result, ast TSRMLS_CC);
                        return;
+               case ZEND_AST_CLOSURE:
+                       zend_compile_closure(result, ast TSRMLS_CC);
+                       return;
                default:
                        ZEND_ASSERT(0 /* not supported */);
        }
index 50263eed8acdcfab20442d61f1f1193ff5b9a0db..7fdf5eb8e625778ae0ef3904ffe062fb8c86b7c8 100644 (file)
@@ -869,9 +869,9 @@ expr_without_variable:
        |       T_YIELD expr { $$.u.ast = zend_ast_create_binary(ZEND_YIELD, $2.u.ast, NULL); }
        |       T_YIELD expr T_DOUBLE_ARROW expr
                        { $$.u.ast = zend_ast_create_binary(ZEND_YIELD, $4.u.ast, $2.u.ast); }
-       |       function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); }
-               '(' parameter_list ')' { zend_compile_params($5.u.ast TSRMLS_CC); zend_ast_destroy($5.u.ast); } lexical_vars { zend_compile_closure_uses($8.u.ast TSRMLS_CC); if ($8.u.ast) zend_ast_destroy($8.u.ast); }
-               '{' inner_statement_list '}' { AS($11); zend_do_end_function_declaration(&$1 TSRMLS_CC); $$.u.ast = AST_ZNODE(&$3); }
+       |       function is_reference '(' parameter_list ')' lexical_vars '{' inner_statement_list '}'
+                       { $$.u.ast = zend_ast_create_ex(3, ZEND_AST_CLOSURE,
+                             $2.op_type, $4.u.ast, $6.u.ast, $8.u.ast); }
        |       T_STATIC function is_reference { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, 1 TSRMLS_CC); }
                '(' parameter_list ')' { zend_compile_params($6.u.ast TSRMLS_CC); zend_ast_destroy($6.u.ast); } lexical_vars { zend_compile_closure_uses($9.u.ast TSRMLS_CC); if ($9.u.ast) zend_ast_destroy($9.u.ast); }
                '{' inner_statement_list '}' { AS($12); zend_do_end_function_declaration(&$2 TSRMLS_CC); $$.u.ast = AST_ZNODE(&$4); }