Commit:    3600914ced52eb4f6db10410ba887c8e2a2acfe1
Author:    Nikita Popov <ni...@php.net>         Tue, 29 May 2012 17:34:33 +0200
Parents:   b770b221e0b3036708deb9e22dacf296402787f0
Branches:  master

Link:       
http://git.php.net/?p=php-src.git;a=commitdiff;h=3600914ced52eb4f6db10410ba887c8e2a2acfe1

Log:
Add support for $generator->send()

Yield now is an expression and the return value is the value passed to
$generator->send(). By default (i.e. if ->next() is called) the value is
NULL.

Unlike in Python ->send() can be run without priming the generator with a
->next() call first.

Changed paths:
  A  Zend/tests/generators/generator_send.phpt
  M  Zend/zend_compile.c
  M  Zend/zend_compile.h
  M  Zend/zend_generators.c
  M  Zend/zend_generators.h
  M  Zend/zend_language_parser.y
  M  Zend/zend_vm_def.h
  M  Zend/zend_vm_execute.h

diff --git a/Zend/tests/generators/generator_send.phpt 
b/Zend/tests/generators/generator_send.phpt
new file mode 100644
index 0000000..11ac37f
--- /dev/null
+++ b/Zend/tests/generators/generator_send.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Values can be sent back to the generator
+--FILE--
+<?php
+
+function *gen() {
+    var_dump(yield "yield foo");
+    var_dump(yield "yield bar");
+}
+
+$gen = gen();
+var_dump($gen->current());
+$gen->send("send bar");
+var_dump($gen->current());
+$gen->send("send foo");
+
+?>
+--EXPECT--
+string(9) "yield foo"
+string(8) "send bar"
+string(9) "yield bar"
+string(8) "send foo"
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 30fecf1..37e4901 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -2662,7 +2662,7 @@ void zend_do_return(znode *expr, int do_end_vparse 
TSRMLS_DC) /* {{{ */
 }
 /* }}} */
 
-void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */
+void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */
 {
        zend_op *opline;
 
@@ -2675,6 +2675,10 @@ void zend_do_yield(znode *expr TSRMLS_DC) /* {{{ */
        opline->opcode = ZEND_YIELD;
        SET_NODE(opline->op1, expr);
        SET_UNUSED(opline->op2);
+
+       opline->result_type = IS_VAR;
+       opline->result.var = get_temporary_variable(CG(active_op_array));
+       GET_NODE(result, opline->result);
 }
 /* }}} */
 
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 5365f96..953a9f1 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -490,7 +490,7 @@ void zend_do_build_full_name(znode *result, znode *prefix, 
znode *name, int is_c
 int zend_do_begin_class_member_function_call(znode *class_name, znode 
*method_name TSRMLS_DC);
 void zend_do_end_function_call(znode *function_name, znode *result, const 
znode *argument_list, int is_method, int is_dynamic_fcall TSRMLS_DC);
 void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC);
-void zend_do_yield(znode *expr TSRMLS_DC);
+void zend_do_yield(znode *result, const znode *expr TSRMLS_DC);
 void zend_do_suspend_if_generator(TSRMLS_D);
 void zend_do_handle_exception(TSRMLS_D);
 
diff --git a/Zend/zend_generators.c b/Zend/zend_generators.c
index e4b0704..01c9aa3 100644
--- a/Zend/zend_generators.c
+++ b/Zend/zend_generators.c
@@ -292,15 +292,47 @@ ZEND_METHOD(Generator, next)
 }
 /* }}} */
 
+/* {{{ proto void Generator::send()
+ * Sends a value to the generator */
+ZEND_METHOD(Generator, send)
+{
+       zval *object, *value;
+       zend_generator *generator;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == 
FAILURE) {
+               return;
+       }
+
+       object = getThis();
+       generator = (zend_generator *) zend_object_store_get_object(object 
TSRMLS_CC);
+
+       zend_generator_ensure_initialized(object, generator TSRMLS_CC); 
+
+       /* The sent value was initialized to NULL, so dtor that */
+       zval_ptr_dtor(generator->send_target->var.ptr_ptr);
+
+       /* Set new sent value */
+       Z_ADDREF_P(value);
+       generator->send_target->var.ptr = value;
+       generator->send_target->var.ptr_ptr = &value;
+
+       zend_generator_resume(object, generator TSRMLS_CC);
+}
+
 ZEND_BEGIN_ARG_INFO(arginfo_generator_void, 0)
 ZEND_END_ARG_INFO()
 
+ZEND_BEGIN_ARG_INFO_EX(arginfo_generator_send, 0, 0, 1)
+       ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
 static const zend_function_entry generator_functions[] = {
        ZEND_ME(Generator, rewind,  arginfo_generator_void, ZEND_ACC_PUBLIC)
        ZEND_ME(Generator, valid,   arginfo_generator_void, ZEND_ACC_PUBLIC)
        ZEND_ME(Generator, current, arginfo_generator_void, ZEND_ACC_PUBLIC)
        ZEND_ME(Generator, key,     arginfo_generator_void, ZEND_ACC_PUBLIC)
        ZEND_ME(Generator, next,    arginfo_generator_void, ZEND_ACC_PUBLIC)
+       ZEND_ME(Generator, send,    arginfo_generator_send, ZEND_ACC_PUBLIC)
        ZEND_FE_END
 };
 
diff --git a/Zend/zend_generators.h b/Zend/zend_generators.h
index 192e9e7..c9b43b3 100644
--- a/Zend/zend_generators.h
+++ b/Zend/zend_generators.h
@@ -30,6 +30,8 @@ typedef struct _zend_generator {
        zend_execute_data *execute_data;
        /* Current value */
        zval *value;
+       /* Variable to put sent value into */
+       temp_variable *send_target;
 } zend_generator;
 
 extern ZEND_API zend_class_entry *zend_ce_generator;
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 2045a5e..ea8ac41 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -68,6 +68,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
 %token T_LOGICAL_AND  "and (T_LOGICAL_AND)"
 %right T_PRINT
 %token T_PRINT        "print (T_PRINT)"
+%right T_YIELD
+%token T_YIELD        "yield (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
 %token T_PLUS_EQUAL   "+= (T_PLUS_EQUAL)"
 %token T_MINUS_EQUAL  "-= (T_MINUS_EQUAL)"
@@ -158,7 +160,6 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
 %token T_FUNCTION   "function (T_FUNCTION)"
 %token T_CONST      "const (T_CONST)"
 %token T_RETURN     "return (T_RETURN)"
-%token T_YIELD      "yield (T_YIELD)"
 %token T_TRY        "try (T_TRY)"
 %token T_CATCH      "catch (T_CATCH)"
 %token T_THROW      "throw (T_THROW)"
@@ -298,7 +299,6 @@ unticked_statement:
        |       T_RETURN ';'                                            { 
zend_do_return(NULL, 0 TSRMLS_CC); }
        |       T_RETURN expr_without_variable ';'      { zend_do_return(&$2, 0 
TSRMLS_CC); }
        |       T_RETURN variable ';'                           { 
zend_do_return(&$2, 1 TSRMLS_CC); }
-       |       T_YIELD expr ';'                                        { 
zend_do_yield(&$2 TSRMLS_CC); }
        |       T_GLOBAL global_var_list ';'
        |       T_STATIC static_var_list ';'
        |       T_ECHO echo_expr_list ';'
@@ -801,6 +801,7 @@ expr_without_variable:
        |       combined_scalar { $$ = $1; }
        |       '`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 
TSRMLS_CC); }
        |       T_PRINT expr  { zend_do_print(&$$, &$2 TSRMLS_CC); }
+       |       T_YIELD expr { zend_do_yield(&$$, &$2 TSRMLS_CC); }
        |       function is_generator is_reference { 
zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, $3.op_type, 0 
TSRMLS_CC); }
                '(' parameter_list ')' lexical_vars { 
zend_do_suspend_if_generator(TSRMLS_C); }
                '{' inner_statement_list '}' { 
zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; }
diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h
index 47d5ec4..8c810cd 100644
--- a/Zend/zend_vm_def.h
+++ b/Zend/zend_vm_def.h
@@ -5260,6 +5260,8 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, 
ANY, ANY)
 
 ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY)
 {
+       USE_OPLINE
+
        /* The generator object is stored in return_value_ptr_ptr */
        zend_generator *generator = (zend_generator *) 
zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
 
@@ -5268,8 +5270,8 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY)
                zval_ptr_dtor(&generator->value);
        }
 
+       /* Set the new yielded value */
        {
-               USE_OPLINE
                zend_free_op free_op1;
                zval *value = GET_OP1_ZVAL_PTR(BP_VAR_R);
 
@@ -5296,6 +5298,13 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV, ANY)
                FREE_OP1_IF_VAR();
        }
 
+       /* If a value is sent it should go into the result var */
+       generator->send_target = &EX_T(opline->result.var);
+
+       /* Initialize the sent value to NULL */
+       Z_ADDREF(EG(uninitialized_zval));
+       AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
+
        /* The GOTO VM uses a local opline variable. We need to set the opline
         * variable in execute_data so we don't resume at an old position. */
        SAVE_OPLINE();
diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h
index bd22768..fa07733 100644
--- a/Zend/zend_vm_execute.h
+++ b/Zend/zend_vm_execute.h
@@ -3036,6 +3036,8 @@ static int ZEND_FASTCALL  
ZEND_QM_ASSIGN_VAR_SPEC_CONST_HANDLER(ZEND_OPCODE_HAND
 
 static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
+       USE_OPLINE
+
        /* The generator object is stored in return_value_ptr_ptr */
        zend_generator *generator = (zend_generator *) 
zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
 
@@ -3044,8 +3046,8 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS
                zval_ptr_dtor(&generator->value);
        }
 
+       /* Set the new yielded value */
        {
-               USE_OPLINE
 
                zval *value = opline->op1.zv;
 
@@ -3071,6 +3073,13 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_CONST_HANDLER(ZEND_OPCODE_HANDLER_ARGS
 
        }
 
+       /* If a value is sent it should go into the result var */
+       generator->send_target = &EX_T(opline->result.var);
+
+       /* Initialize the sent value to NULL */
+       Z_ADDREF(EG(uninitialized_zval));
+       AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
+
        /* The GOTO VM uses a local opline variable. We need to set the opline
         * variable in execute_data so we don't resume at an old position. */
        SAVE_OPLINE();
@@ -7677,6 +7686,8 @@ static int ZEND_FASTCALL  
ZEND_INSTANCEOF_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_A
 
 static int ZEND_FASTCALL  ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
+       USE_OPLINE
+
        /* The generator object is stored in return_value_ptr_ptr */
        zend_generator *generator = (zend_generator *) 
zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
 
@@ -7685,8 +7696,8 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                zval_ptr_dtor(&generator->value);
        }
 
+       /* Set the new yielded value */
        {
-               USE_OPLINE
                zend_free_op free_op1;
                zval *value = _get_zval_ptr_tmp(opline->op1.var, EX_Ts(), 
&free_op1 TSRMLS_CC);
 
@@ -7712,6 +7723,13 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_TMP_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 
        }
 
+       /* If a value is sent it should go into the result var */
+       generator->send_target = &EX_T(opline->result.var);
+
+       /* Initialize the sent value to NULL */
+       Z_ADDREF(EG(uninitialized_zval));
+       AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
+
        /* The GOTO VM uses a local opline variable. We need to set the opline
         * variable in execute_data so we don't resume at an old position. */
        SAVE_OPLINE();
@@ -12395,6 +12413,8 @@ static int ZEND_FASTCALL  
ZEND_INSTANCEOF_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_A
 
 static int ZEND_FASTCALL  ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
+       USE_OPLINE
+
        /* The generator object is stored in return_value_ptr_ptr */
        zend_generator *generator = (zend_generator *) 
zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
 
@@ -12403,8 +12423,8 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                zval_ptr_dtor(&generator->value);
        }
 
+       /* Set the new yielded value */
        {
-               USE_OPLINE
                zend_free_op free_op1;
                zval *value = _get_zval_ptr_var(opline->op1.var, EX_Ts(), 
&free_op1 TSRMLS_CC);
 
@@ -12431,6 +12451,13 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                if (free_op1.var) {zval_ptr_dtor(&free_op1.var);};
        }
 
+       /* If a value is sent it should go into the result var */
+       generator->send_target = &EX_T(opline->result.var);
+
+       /* Initialize the sent value to NULL */
+       Z_ADDREF(EG(uninitialized_zval));
+       AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
+
        /* The GOTO VM uses a local opline variable. We need to set the opline
         * variable in execute_data so we don't resume at an old position. */
        SAVE_OPLINE();
@@ -28285,6 +28312,8 @@ static int ZEND_FASTCALL  
ZEND_INSTANCEOF_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_AR
 
 static int ZEND_FASTCALL  ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 {
+       USE_OPLINE
+
        /* The generator object is stored in return_value_ptr_ptr */
        zend_generator *generator = (zend_generator *) 
zend_object_store_get_object(*EG(return_value_ptr_ptr) TSRMLS_CC);
 
@@ -28293,8 +28322,8 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
                zval_ptr_dtor(&generator->value);
        }
 
+       /* Set the new yielded value */
        {
-               USE_OPLINE
 
                zval *value = _get_zval_ptr_cv_BP_VAR_R(EX_CVs(), 
opline->op1.var TSRMLS_CC);
 
@@ -28320,6 +28349,13 @@ static int ZEND_FASTCALL  
ZEND_YIELD_SPEC_CV_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
 
        }
 
+       /* If a value is sent it should go into the result var */
+       generator->send_target = &EX_T(opline->result.var);
+
+       /* Initialize the sent value to NULL */
+       Z_ADDREF(EG(uninitialized_zval));
+       AI_SET_PTR(&EX_T(opline->result.var), &EG(uninitialized_zval));
+
        /* The GOTO VM uses a local opline variable. We need to set the opline
         * variable in execute_data so we don't resume at an old position. */
        SAVE_OPLINE();
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to