mirror of
https://github.com/php/php-src.git
synced 2026-04-28 18:53:33 +02:00
Add support for yielding keys
Keys are yielded using the
yield $key => $value
syntax. Currently this is implemented as a statement only and not as an
expression, because conflicts arise considering nesting and use in arrays:
yield yield $a => $b;
// could be either
yield (yield $a) => $b;
// or
yield (yield $a => $b);
Once I find some way to resolve these conflicts this should be available
as an expression too.
Also the key yielding code is rather copy-and-past-y for the value yielding
code, so that should be factored out.
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
--TEST--
|
||||
Generators can also yield keys
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
function *reverse(array $array) {
|
||||
end($array);
|
||||
while (null !== $key = key($array)) {
|
||||
yield $key => current($array);
|
||||
prev($array);
|
||||
}
|
||||
}
|
||||
|
||||
$array = [
|
||||
'foo' => 'bar',
|
||||
'bar' => 'foo',
|
||||
];
|
||||
|
||||
foreach (reverse($array) as $key => $value) {
|
||||
echo $key, ' => ', $value, "\n";
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
bar => foo
|
||||
foo => bar
|
||||
+9
-3
@@ -2662,7 +2662,7 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */
|
||||
void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
zend_op *opline;
|
||||
|
||||
@@ -2674,8 +2674,14 @@ void zend_do_yield(znode *result, const znode *expr TSRMLS_DC) /* {{{ */
|
||||
|
||||
opline->opcode = ZEND_YIELD;
|
||||
|
||||
if (expr) {
|
||||
SET_NODE(opline->op1, expr);
|
||||
if (value) {
|
||||
SET_NODE(opline->op1, value);
|
||||
} else {
|
||||
SET_UNUSED(opline->op1);
|
||||
}
|
||||
|
||||
if (key) {
|
||||
SET_NODE(opline->op2, key);
|
||||
} else {
|
||||
SET_UNUSED(opline->op2);
|
||||
}
|
||||
|
||||
+1
-1
@@ -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 *result, const znode *expr TSRMLS_DC);
|
||||
void zend_do_yield(znode *result, const znode *value, const znode *key TSRMLS_DC);
|
||||
void zend_do_suspend_if_generator(TSRMLS_D);
|
||||
void zend_do_handle_exception(TSRMLS_D);
|
||||
|
||||
|
||||
@@ -92,6 +92,10 @@ void zend_generator_close(zend_generator *generator, zend_bool finished_executio
|
||||
generator->value = NULL;
|
||||
}
|
||||
|
||||
if (generator->key) {
|
||||
zval_ptr_dtor(&generator->key);
|
||||
generator->key = NULL;
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@@ -269,6 +273,10 @@ ZEND_METHOD(Generator, key)
|
||||
generator = (zend_generator *) zend_object_store_get_object(object TSRMLS_CC);
|
||||
|
||||
zend_generator_ensure_initialized(object, generator TSRMLS_CC);
|
||||
|
||||
if (generator->key) {
|
||||
RETURN_ZVAL(generator->key, 1, 0);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ typedef struct _zend_generator {
|
||||
zend_execute_data *execute_data;
|
||||
/* Current value */
|
||||
zval *value;
|
||||
/* Current key */
|
||||
zval *key;
|
||||
/* Variable to put sent value into */
|
||||
temp_variable *send_target;
|
||||
} zend_generator;
|
||||
|
||||
@@ -299,6 +299,7 @@ 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 T_DOUBLE_ARROW expr ';' { zend_do_yield(&$$, &$4, &$2 TSRMLS_CC); }
|
||||
| T_GLOBAL global_var_list ';'
|
||||
| T_STATIC static_var_list ';'
|
||||
| T_ECHO echo_expr_list ';'
|
||||
@@ -801,8 +802,8 @@ 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 { zend_do_yield(&$$, NULL TSRMLS_CC); }
|
||||
| T_YIELD expr { zend_do_yield(&$$, &$2 TSRMLS_CC); }
|
||||
| T_YIELD { zend_do_yield(&$$, NULL, NULL TSRMLS_CC); }
|
||||
| T_YIELD expr { zend_do_yield(&$$, &$2, NULL 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; }
|
||||
|
||||
+39
-2
@@ -5258,7 +5258,7 @@ ZEND_VM_HANDLER(159, ZEND_SUSPEND_AND_RETURN_GENERATOR, ANY, ANY)
|
||||
ZEND_VM_RETURN();
|
||||
}
|
||||
|
||||
ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY)
|
||||
ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, CONST|TMP|VAR|CV|UNUSED)
|
||||
{
|
||||
USE_OPLINE
|
||||
|
||||
@@ -5270,6 +5270,11 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY)
|
||||
zval_ptr_dtor(&generator->value);
|
||||
}
|
||||
|
||||
/* Destroy the previously yielded key */
|
||||
if (generator->key) {
|
||||
zval_ptr_dtor(&generator->key);
|
||||
}
|
||||
|
||||
/* Set the new yielded value */
|
||||
if (OP1_TYPE != IS_UNUSED) {
|
||||
zend_free_op free_op1;
|
||||
@@ -5297,11 +5302,43 @@ ZEND_VM_HANDLER(160, ZEND_YIELD, CONST|TMP|VAR|CV|UNUSED, ANY)
|
||||
|
||||
FREE_OP1_IF_VAR();
|
||||
} else {
|
||||
/* If no value way specified yield null */
|
||||
/* If no value was specified yield null */
|
||||
Z_ADDREF(EG(uninitialized_zval));
|
||||
generator->value = &EG(uninitialized_zval);
|
||||
}
|
||||
|
||||
/* Set the new yielded key */
|
||||
if (OP2_TYPE != IS_UNUSED) {
|
||||
zend_free_op free_op2;
|
||||
zval *key = GET_OP2_ZVAL_PTR(BP_VAR_R);
|
||||
|
||||
/* Consts, temporary variables and references need copying */
|
||||
if (OP2_TYPE == IS_CONST || OP2_TYPE == IS_TMP_VAR
|
||||
|| (PZVAL_IS_REF(key) && Z_REFCOUNT_P(key) > 0)
|
||||
) {
|
||||
zval *copy;
|
||||
|
||||
ALLOC_ZVAL(copy);
|
||||
INIT_PZVAL_COPY(copy, key);
|
||||
|
||||
/* Temporary variables don't need ctor copying */
|
||||
if (!IS_OP1_TMP_FREE()) {
|
||||
zval_copy_ctor(copy);
|
||||
}
|
||||
|
||||
generator->key = copy;
|
||||
} else {
|
||||
Z_ADDREF_P(key);
|
||||
generator->key = key;
|
||||
}
|
||||
|
||||
FREE_OP2_IF_VAR();
|
||||
} else {
|
||||
/* Setting the key to NULL signals that the auto-increment key
|
||||
* generation should be used */
|
||||
generator->key = NULL;
|
||||
}
|
||||
|
||||
/* If a value is sent it should go into the result var */
|
||||
generator->send_target = &EX_T(opline->result.var);
|
||||
|
||||
|
||||
+2360
-311
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user