AST-based compiler: Stage 1

This commit is contained in:
Nikita Popov 2014-06-07 13:06:53 +02:00
parent 98918fe65b
commit 258a733ed2
10 changed files with 1262 additions and 163 deletions

View file

@ -9,8 +9,8 @@ echo "I am alive";
--EXPECTF--
Notice: Uninitialized string offset: 0 in %sbug39304.php on line %d
Notice: Uninitialized string offset: 1 in %sbug39304.php on line %d
Notice: Uninitialized string offset: 0 in %sbug39304.php on line %d
Notice: Uninitialized string offset: 1 in %sbug39304.php on line %d
I am alive

View file

@ -11,8 +11,8 @@ Bug #39304 (Segmentation fault with list unpacking of string offset)
--EXPECTF--
Notice: Uninitialized string offset: 0 in %sbug39304_2_4.php on line %d
Notice: Uninitialized string offset: 1 in %sbug39304_2_4.php on line %d
Notice: Uninitialized string offset: 0 in %sbug39304_2_4.php on line %d
Notice: Uninitialized string offset: 1 in %sbug39304_2_4.php on line %d
string(0) ""
string(0) ""

View file

@ -9,7 +9,7 @@ foreach (array(array(1,2), array(3,4)) as list($a, )) {
$array = [['a', 'b'], 'c', 'd'];
foreach($array as list(list(), $a)) {
foreach($array as list(, $a)) {
var_dump($a);
}

View file

@ -3,33 +3,33 @@ Testing list() with several variables
--FILE--
<?php
$a = "foo";
$str = "foo";
list($a, $b, $c) = $a;
list($a, $b, $c) = $str;
var_dump($a, $b, $c);
print "----\n";
$a = 1;
$int = 1;
list($a, $b, $c) = $a;
list($a, $b, $c) = $int;
var_dump($a, $b, $c);
print "----\n";
$a = new stdClass;
$obj = new stdClass;
list($a, $b, $c) = $a;
list($a, $b, $c) = $obj;
var_dump($a, $b, $c);
print "----\n";
$a = array(1, 2, 3);
$arr = array(1, 2, 3);
list($a, $b, $c) = $a;
list($a, $b, $c) = $arr;
var_dump($a, $b, $c);

View file

@ -7,8 +7,6 @@ list($a, list($b, list(list($d)))) = array();
?>
--EXPECTF--
Notice: Undefined offset: 1 in %s on line %d
Notice: Undefined offset: 1 in %s on line %d
Notice: Undefined offset: 0 in %s on line %d
Notice: Undefined offset: 1 in %s on line %d

View file

@ -32,6 +32,15 @@ ZEND_API zend_ast *zend_ast_create_constant(zval *zv)
return (zend_ast *) ast;
}
ZEND_API zend_ast *zend_ast_create_znode(znode *node)
{
zend_ast_znode *ast = emalloc(sizeof(zend_ast_znode));
ast->kind = ZEND_AST_ZNODE;
ast->children = 0;
ast->node = *node;
return (zend_ast *) ast;
}
ZEND_API zend_ast* zend_ast_create_unary(uint kind, zend_ast *op0)
{
zend_ast *ast = emalloc(sizeof(zend_ast));
@ -62,7 +71,7 @@ ZEND_API zend_ast* zend_ast_create_ternary(uint kind, zend_ast *op0, zend_ast *o
return ast;
}
ZEND_API zend_ast* zend_ast_create_dynamic(uint kind)
ZEND_API zend_ast *zend_ast_create_dynamic(uint kind)
{
/* use 4 children as default */
zend_ast *ast = emalloc(sizeof(zend_ast) + sizeof(zend_ast *) * 3);

View file

@ -26,12 +26,35 @@
typedef enum _zend_ast_kind {
/* first 256 kinds are reserved for opcodes */
ZEND_CONST = 256,
ZEND_CONST = 256, /* TODO.AST: Split in constant lookup and literal zval */
ZEND_BOOL_AND,
ZEND_BOOL_OR,
ZEND_SELECT,
ZEND_UNARY_PLUS,
ZEND_UNARY_MINUS,
ZEND_AST_ZNODE,
ZEND_AST_VAR,
ZEND_AST_DIM,
ZEND_AST_PROP,
ZEND_AST_STATIC_PROP,
ZEND_AST_CALL,
ZEND_AST_METHOD_CALL,
ZEND_AST_STATIC_CALL,
ZEND_AST_ASSIGN,
ZEND_AST_ASSIGN_REF,
ZEND_AST_LIST,
ZEND_AST_GLOBAL,
ZEND_AST_UNSET,
ZEND_AST_PARAMS,
ZEND_AST_UNPACK,
ZEND_AST_NAME,
ZEND_AST_NAME_FQ,
} zend_ast_kind;
struct _zend_ast {
@ -66,4 +89,28 @@ ZEND_API void zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *s
ZEND_API zend_ast *zend_ast_copy(zend_ast *ast);
ZEND_API void zend_ast_destroy(zend_ast *ast);
static inline zend_ast *zend_ast_create_var(zval *name) {
return zend_ast_create_unary(ZEND_AST_VAR, zend_ast_create_constant(name));
}
/* Temporary, for porting */
#define AST_COMPILE(res, ast) do { \
zend_ast *_ast = (ast); \
zend_compile_expr((res), _ast TSRMLS_CC); \
zend_ast_destroy(_ast); \
} while (0)
#define AST_COMPILE_VAR(res, ast, type) do { \
zend_ast *_ast = (ast); \
zend_compile_var((res), _ast, type TSRMLS_CC); \
zend_ast_destroy(_ast); \
} while (0)
#define AST_COMPILE_STMT(ast) do { \
zend_ast *_ast = (ast); \
zend_compile_stmt(_ast TSRMLS_CC); \
zend_ast_destroy(_ast); \
} while (0)
#define AST_ZNODE(znode) zend_ast_create_znode((znode))
#define AST_ZVAL(znode) zend_ast_create_constant(&(znode)->u.constant)
#endif

File diff suppressed because it is too large Load diff

View file

@ -87,6 +87,22 @@ typedef struct _znode { /* used only during compilation */
zend_uint EA; /* extended attributes */
} znode;
/* Temporarily defined here, to avoid header ordering issues */
typedef struct _zend_ast_znode {
unsigned short kind;
unsigned short children;
znode node;
} zend_ast_znode;
ZEND_API zend_ast *zend_ast_create_znode(znode *node);
static inline znode *zend_ast_get_znode(zend_ast *ast) {
return &((zend_ast_znode *) ast)->node;
}
void zend_compile_stmt(zend_ast *ast TSRMLS_DC);
void zend_compile_expr(znode *node, zend_ast *ast TSRMLS_DC);
void zend_compile_var(znode *node, zend_ast *ast, int type TSRMLS_DC);
typedef struct _zend_execute_data zend_execute_data;
#define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data TSRMLS_DC
@ -759,9 +775,13 @@ int zend_add_literal(zend_op_array *op_array, zval *zv TSRMLS_DC);
#define BP_VAR_W 1
#define BP_VAR_RW 2
#define BP_VAR_IS 3
#define BP_VAR_NA 4 /* if not applicable */
#define BP_VAR_FUNC_ARG 5
#define BP_VAR_UNSET 6
#define BP_VAR_FUNC_ARG 4
#define BP_VAR_UNSET 5
#define BP_VAR_REF 6 /* right-hand side of by-ref assignment */
/* Bottom 3 bits are the type, top bits are arg num for BP_VAR_FUNC_ARG */
#define BP_VAR_SHIFT 3
#define BP_VAR_MASK 7
#define ZEND_INTERNAL_FUNCTION 1

View file

@ -389,7 +389,8 @@ unset_variables:
;
unset_variable:
variable { zend_do_end_variable_parse(&$1, BP_VAR_UNSET, 0 TSRMLS_CC); zend_do_unset(&$1 TSRMLS_CC); }
variable
{ $$.u.ast = zend_ast_create_unary(ZEND_AST_UNSET, $1.u.ast); AST_COMPILE_STMT($$.u.ast); }
;
function_declaration_statement:
@ -469,9 +470,9 @@ foreach_optional_arg:
;
foreach_variable:
variable { zend_check_writable_variable(&$1); $$ = $1; }
| '&' variable { zend_check_writable_variable(&$2); $$ = $2; $$.EA |= ZEND_PARSED_REFERENCE_VARIABLE; }
| T_LIST '(' { zend_do_list_init(TSRMLS_C); } assignment_list ')' { $$ = $1; $$.EA = ZEND_PARSED_LIST_EXPR; }
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; }
;
for_statement:
@ -596,13 +597,37 @@ function_call_parameter:
| T_ELLIPSIS expr { zend_do_unpack_params(&$2 TSRMLS_CC); }
;
function_call_parameter_list_ast:
'(' ')' { $$.u.ast = zend_ast_create_dynamic(ZEND_AST_PARAMS); }
| '(' non_empty_function_call_parameter_list_ast ')' { $$.u.ast = $2.u.ast; }
| '(' yield_expr ')'
{ $$.u.ast = zend_ast_create_dynamic(ZEND_AST_PARAMS);
zend_ast_dynamic_add(&$$.u.ast, AST_ZNODE(&$2)); }
;
non_empty_function_call_parameter_list_ast:
function_call_parameter_ast
{ $$.u.ast = zend_ast_create_dynamic(ZEND_AST_PARAMS);
zend_ast_dynamic_add(&$$.u.ast, $1.u.ast); }
| non_empty_function_call_parameter_list_ast ',' function_call_parameter_ast
{ zend_ast_dynamic_add(&$1.u.ast, $3.u.ast); $$.u.ast = $1.u.ast; }
;
function_call_parameter_ast:
expr_without_variable { $$.u.ast = AST_ZNODE(&$1); }
| variable { $$.u.ast = $1.u.ast; }
| '&' w_variable { /* ERROR */ ZEND_ASSERT(0); }
| T_ELLIPSIS expr { $$.u.ast = zend_ast_create_unary(ZEND_AST_UNPACK, AST_ZNODE(&$2)); }
;
global_var_list:
global_var_list ',' global_var
| global_var
;
global_var:
simple_variable { zend_do_fetch_global_variable(&$1, NULL, ZEND_FETCH_GLOBAL_LOCK TSRMLS_CC); }
simple_variable
{ $$.u.ast = zend_ast_create_unary(ZEND_AST_GLOBAL, $1.u.ast); AST_COMPILE_STMT($$.u.ast); }
;
@ -749,23 +774,53 @@ new_expr:
;
expr_without_variable:
T_LIST '(' { zend_do_list_init(TSRMLS_C); } assignment_list ')' '=' expr { zend_do_list_end(&$$, &$7 TSRMLS_CC); }
| variable '=' expr { zend_check_writable_variable(&$1); zend_do_assign(&$$, &$1, &$3 TSRMLS_CC); }
| variable '=' '&' variable { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$4, BP_VAR_W, 1 TSRMLS_CC); zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); zend_do_assign_ref(&$$, &$1, &$4 TSRMLS_CC); }
T_LIST '(' assignment_list ')' '=' expr
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_ASSIGN, $3.u.ast, AST_ZNODE(&$6));
AST_COMPILE(&$$, $$.u.ast); }
| variable '=' expr
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_ASSIGN, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $$.u.ast); }
| variable '=' '&' variable
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_ASSIGN_REF, $1.u.ast, $4.u.ast);
AST_COMPILE(&$$, $$.u.ast); }
| variable '=' '&' T_NEW class_name_reference { zend_error(E_DEPRECATED, "Assigning the return value of new by reference is deprecated"); zend_check_writable_variable(&$1); zend_do_extended_fcall_begin(TSRMLS_C); zend_do_begin_new_object(&$4, &$5 TSRMLS_CC); } ctor_arguments { zend_do_end_new_object(&$3, &$4 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); $3.EA = ZEND_PARSED_NEW; zend_do_assign_ref(&$$, &$1, &$3 TSRMLS_CC); }
| T_CLONE expr { zend_do_clone(&$$, &$2 TSRMLS_CC); }
| variable T_PLUS_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_ADD, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_MINUS_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SUB, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_MUL_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_MUL, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_POW_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_POW, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_DIV_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_DIV, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_CONCAT_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_CONCAT, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_MOD_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_MOD, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_AND_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_AND, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_OR_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_OR, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_XOR_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_BW_XOR, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_SL_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SL, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_SR_EQUAL expr { zend_check_writable_variable(&$1); zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); zend_do_binary_assign_op(ZEND_ASSIGN_SR, &$$, &$1, &$3 TSRMLS_CC); }
| variable T_PLUS_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_ADD, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_MINUS_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_SUB, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_MUL_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_MUL, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_POW_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_POW, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_DIV_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_DIV, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_CONCAT_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_CONCAT, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_MOD_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_MOD, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_AND_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_BW_AND, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_OR_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_BW_OR, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_XOR_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_BW_XOR, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_SL_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_SL, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| variable T_SR_EQUAL expr
{ $2.u.ast = zend_ast_create_binary(ZEND_ASSIGN_SR, $1.u.ast, AST_ZNODE(&$3));
AST_COMPILE(&$$, $2.u.ast); }
| rw_variable T_INC { zend_do_post_incdec(&$$, &$1, ZEND_POST_INC TSRMLS_CC); }
| T_INC rw_variable { zend_do_pre_incdec(&$$, &$2, ZEND_PRE_INC TSRMLS_CC); }
| rw_variable T_DEC { zend_do_post_incdec(&$$, &$1, ZEND_POST_DEC TSRMLS_CC); }
@ -853,18 +908,25 @@ lexical_var_list:
;
function_call:
namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$1, 1 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, 0, $2.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name { $1.op_type = IS_CONST; ZVAL_EMPTY_STRING(&$1.u.constant); zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC); $$.u.op.opline_num = zend_do_begin_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, 0, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| T_NS_SEPARATOR namespace_name { $$.u.op.opline_num = zend_do_begin_function_call(&$2, 0 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$2, &$$, 0, $3.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C); }
| class_name T_PAAMAYIM_NEKUDOTAYIM member_name { $$.u.op.opline_num = zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call($4.u.op.opline_num?NULL:&$3, &$$, $4.u.op.opline_num, $4.u.op.opline_num TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name { zend_do_begin_class_member_function_call(&$1, &$3 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(NULL, &$$, 1, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
| callable_expr { zend_do_begin_dynamic_function_call(&$1, 0 TSRMLS_CC); }
function_call_parameter_list { zend_do_end_function_call(&$1, &$$, 0, 1 TSRMLS_CC); zend_do_extended_fcall_end(TSRMLS_C);}
namespace_name function_call_parameter_list_ast
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_CALL,
zend_ast_create_unary(ZEND_AST_NAME, AST_ZVAL(&$1)), $2.u.ast); }
| T_NAMESPACE T_NS_SEPARATOR namespace_name function_call_parameter_list_ast
{ ZVAL_EMPTY_STRING(&$1.u.constant);
zend_do_build_namespace_name(&$1, &$1, &$3 TSRMLS_CC);
$$.u.ast = zend_ast_create_binary(ZEND_AST_CALL,
zend_ast_create_unary(ZEND_AST_NAME_FQ, AST_ZVAL(&$1)), $4.u.ast); }
| T_NS_SEPARATOR namespace_name function_call_parameter_list_ast
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_CALL,
zend_ast_create_unary(ZEND_AST_NAME_FQ, AST_ZVAL(&$2)), $3.u.ast); }
| class_name T_PAAMAYIM_NEKUDOTAYIM member_name function_call_parameter_list_ast
{ $$.u.ast = zend_ast_create_ternary(ZEND_AST_STATIC_CALL,
AST_ZVAL(&$1), $3.u.ast, $4.u.ast); }
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM member_name function_call_parameter_list_ast
{ $$.u.ast = zend_ast_create_ternary(ZEND_AST_STATIC_CALL,
AST_ZNODE(&$1), $3.u.ast, $4.u.ast); }
| callable_expr function_call_parameter_list_ast
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_CALL, $1.u.ast, $2.u.ast); }
;
class_name:
@ -884,7 +946,7 @@ fully_qualified_class_name:
class_name_reference:
class_name { zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
| new_variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
| new_variable { AST_COMPILE_VAR(&$1, $1.u.ast, BP_VAR_R); zend_do_fetch_class(&$$, &$1 TSRMLS_CC); }
;
exit_expr:
@ -1032,112 +1094,110 @@ parenthesis_expr:
r_variable:
variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; }
variable { zend_compile_var(&$$, $1.u.ast, BP_VAR_R TSRMLS_CC); zend_ast_destroy($1.u.ast); }
;
w_variable:
variable { zend_do_end_variable_parse(&$1, BP_VAR_W, 0 TSRMLS_CC); $$ = $1;
variable { zend_compile_var(&$$, $1.u.ast, BP_VAR_W TSRMLS_CC); zend_ast_destroy($1.u.ast);
zend_check_writable_variable(&$1); }
;
rw_variable:
variable { zend_do_end_variable_parse(&$1, BP_VAR_RW, 0 TSRMLS_CC); $$ = $1;
zend_check_writable_variable(&$1); }
variable { zend_compile_var(&$$, $1.u.ast, BP_VAR_RW TSRMLS_CC); zend_ast_destroy($1.u.ast);
zend_check_writable_variable(&$$); }
;
variable_class_name:
dereferencable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; }
dereferencable { zend_compile_expr(&$$, $1.u.ast TSRMLS_CC); zend_ast_destroy($1.u.ast); }
;
dereferencable:
variable { $$ = $1; }
| '(' expr ')' { $$ = $2; zend_do_begin_variable_parse(TSRMLS_C); $$.EA = 0; }
| dereferencable_scalar { $$ = $1; zend_do_begin_variable_parse(TSRMLS_C); $$.EA = 0; }
variable { $$.u.ast = $1.u.ast; }
| '(' expr ')' { $$.u.ast = AST_ZNODE(&$2); }
| dereferencable_scalar { $$.u.ast = AST_ZNODE(&$1); }
;
callable_expr:
callable_variable { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); $$ = $1; }
| '(' expr ')' { $$ = $2; }
| dereferencable_scalar { $$ = $1; }
callable_variable { $$.u.ast = $1.u.ast; }
| '(' expr ')' { $$.u.ast = AST_ZNODE(&$2); }
| dereferencable_scalar { $$.u.ast = AST_ZNODE(&$1); }
;
callable_variable:
simple_variable
{ zend_do_begin_variable_parse(TSRMLS_C);
fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC);
$$.EA = ZEND_PARSED_VARIABLE; }
{ $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $1.u.ast); }
| dereferencable '[' dim_offset ']'
{ fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); $$.EA = ZEND_PARSED_VARIABLE; }
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, $3.u.ast); }
| dereferencable '{' expr '}'
{ fetch_string_offset(&$$, &$1, &$3 TSRMLS_CC); $$.EA = ZEND_PARSED_VARIABLE; }
| dereferencable T_OBJECT_OPERATOR member_name
{ zend_do_fetch_property(&$$, &$1, &$3 TSRMLS_CC);
zend_do_begin_method_call(&$$ TSRMLS_CC); }
function_call_parameter_list
{ zend_do_end_function_call(&$4, &$$, 1, 1 TSRMLS_CC);
zend_do_extended_fcall_end(TSRMLS_C);
$$.EA = ZEND_PARSED_METHOD_CALL; }
| function_call
{ zend_do_begin_variable_parse(TSRMLS_C);
$$ = $1; $$.EA = ZEND_PARSED_FUNCTION_CALL; }
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, AST_ZNODE(&$3)); }
| dereferencable T_OBJECT_OPERATOR member_name function_call_parameter_list_ast
{ $$.u.ast = zend_ast_create_ternary(ZEND_AST_METHOD_CALL, $1.u.ast, $3.u.ast, $4.u.ast); }
| function_call { $$.u.ast = $1.u.ast; }
;
variable:
callable_variable { $$ = $1; }
| static_member { $$ = $1; $$.EA = ZEND_PARSED_STATIC_MEMBER; }
callable_variable
{ $$.u.ast = $1.u.ast; }
| static_member
{ $$.u.ast = $1.u.ast; }
| dereferencable T_OBJECT_OPERATOR member_name
{ zend_do_fetch_property(&$$, &$1, &$3 TSRMLS_CC); $$.EA = ZEND_PARSED_MEMBER; }
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_PROP, $1.u.ast, $3.u.ast); }
;
simple_variable:
T_VARIABLE { $$ = $1; }
| '$' '{' expr '}' { $$ = $3; }
| '$' simple_variable { zend_do_indirect_reference(&$$, &$2 TSRMLS_CC); }
T_VARIABLE
{ $$.u.ast = AST_ZVAL(&$1); }
| '$' '{' expr '}'
{ $$.u.ast = zend_ast_create_znode(&$3); }
| '$' simple_variable
{ $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $2.u.ast); }
;
static_member:
class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable { zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); }
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable { zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); }
class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, AST_ZVAL(&$1), $3.u.ast); }
| variable_class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, AST_ZNODE(&$1), $3.u.ast); }
;
new_variable:
simple_variable
{ zend_do_begin_variable_parse(TSRMLS_C);
fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); }
| new_variable '[' dim_offset ']' { fetch_array_dim(&$$, &$1, &$3 TSRMLS_CC); }
| new_variable '{' expr '}' { fetch_string_offset(&$$, &$1, &$3 TSRMLS_CC); }
{ $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $1.u.ast); }
| new_variable '[' dim_offset ']'
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, $3.u.ast); }
| new_variable '{' expr '}'
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM, $1.u.ast, AST_ZNODE(&$3)); }
| new_variable T_OBJECT_OPERATOR member_name
{ zend_do_fetch_property(&$$, &$1, &$3 TSRMLS_CC); }
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_PROP, $1.u.ast, $3.u.ast); }
| class_name T_PAAMAYIM_NEKUDOTAYIM simple_variable
{ zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); }
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, AST_ZVAL(&$1), $3.u.ast); }
| new_variable T_PAAMAYIM_NEKUDOTAYIM simple_variable
{ zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC);
zend_do_fetch_static_member(&$$, &$1, &$3 TSRMLS_CC); }
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_STATIC_PROP, $1.u.ast, $3.u.ast); }
;
dim_offset:
/* empty */ { $$.op_type = IS_UNUSED; }
| expr { $$ = $1; }
/* empty */ { $$.u.ast = NULL; }
| expr { $$.u.ast = AST_ZNODE(&$1); }
;
member_name:
T_STRING { $$ = $1; }
| '{' expr '}' { $$ = $2; }
| simple_variable { fetch_simple_variable_ex(&$$, &$1, 0, ZEND_FETCH_R TSRMLS_CC); }
T_STRING { $$.u.ast = AST_ZVAL(&$1); }
| '{' expr '}' { $$.u.ast = AST_ZNODE(&$2); }
| simple_variable { $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, $1.u.ast); }
;
assignment_list:
assignment_list ',' assignment_list_element
{ zend_ast_dynamic_add(&$1.u.ast, $3.u.ast); $$.u.ast = $1.u.ast; }
| assignment_list_element
{ $$.u.ast = zend_ast_create_dynamic(ZEND_AST_LIST);
zend_ast_dynamic_add(&$$.u.ast, $1.u.ast); }
;
assignment_list_element:
variable { zend_do_add_list_element(&$1 TSRMLS_CC); }
| T_LIST '(' { zend_do_new_list_begin(TSRMLS_C); } assignment_list ')' { zend_do_new_list_end(TSRMLS_C); }
| /* empty */ { zend_do_add_list_element(NULL TSRMLS_CC); }
variable { $$.u.ast = $1.u.ast; }
| T_LIST '(' assignment_list ')' { $$.u.ast = $3.u.ast; }
| /* empty */ { $$.u.ast = NULL; }
;
@ -1158,34 +1218,42 @@ non_empty_array_pair_list:
;
encaps_list:
encaps_list encaps_var { zend_do_end_variable_parse(&$2, BP_VAR_R, 0 TSRMLS_CC); zend_do_add_variable(&$$, &$1, &$2 TSRMLS_CC); }
encaps_list encaps_var
{ AST_COMPILE(&$2, $2.u.ast); zend_do_add_variable(&$$, &$1, &$2 TSRMLS_CC); }
| encaps_list T_ENCAPSED_AND_WHITESPACE { zend_do_add_string(&$$, &$1, &$2 TSRMLS_CC); }
| encaps_var { zend_do_end_variable_parse(&$1, BP_VAR_R, 0 TSRMLS_CC); zend_do_add_variable(&$$, NULL, &$1 TSRMLS_CC); }
| T_ENCAPSED_AND_WHITESPACE encaps_var { zend_do_add_string(&$$, NULL, &$1 TSRMLS_CC); zend_do_end_variable_parse(&$2, BP_VAR_R, 0 TSRMLS_CC); zend_do_add_variable(&$$, &$$, &$2 TSRMLS_CC); }
| encaps_var { AST_COMPILE(&$1, $1.u.ast); zend_do_add_variable(&$$, NULL, &$1 TSRMLS_CC); }
| T_ENCAPSED_AND_WHITESPACE encaps_var
{ zend_do_add_string(&$$, NULL, &$1 TSRMLS_CC);
AST_COMPILE(&$2, $2.u.ast); zend_do_add_variable(&$$, &$$, &$2 TSRMLS_CC); }
;
encaps_var:
T_VARIABLE { zend_do_begin_variable_parse(TSRMLS_C); fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); }
| T_VARIABLE '[' { zend_do_begin_variable_parse(TSRMLS_C); } encaps_var_offset ']' { fetch_array_begin(&$$, &$1, &$4 TSRMLS_CC); }
| T_VARIABLE T_OBJECT_OPERATOR T_STRING { zend_do_begin_variable_parse(TSRMLS_C); fetch_simple_variable(&$2, &$1, 1 TSRMLS_CC); zend_do_fetch_property(&$$, &$2, &$3 TSRMLS_CC); }
| T_DOLLAR_OPEN_CURLY_BRACES expr '}' { zend_do_begin_variable_parse(TSRMLS_C); fetch_simple_variable(&$$, &$2, 1 TSRMLS_CC); }
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}' { zend_do_begin_variable_parse(TSRMLS_C); fetch_array_begin(&$$, &$2, &$4 TSRMLS_CC); }
| T_CURLY_OPEN variable '}' { $$ = $2; }
T_VARIABLE
{ $$.u.ast = zend_ast_create_var(&$1.u.constant); }
| T_VARIABLE '[' encaps_var_offset ']'
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM,
zend_ast_create_var(&$1.u.constant), $3.u.ast); }
| T_VARIABLE T_OBJECT_OPERATOR T_STRING
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_PROP,
zend_ast_create_var(&$1.u.constant), AST_ZNODE(&$3)); }
| T_DOLLAR_OPEN_CURLY_BRACES expr '}'
{ $$.u.ast = zend_ast_create_unary(ZEND_AST_VAR, AST_ZNODE(&$2)); }
| T_DOLLAR_OPEN_CURLY_BRACES T_STRING_VARNAME '[' expr ']' '}'
{ $$.u.ast = zend_ast_create_binary(ZEND_AST_DIM,
zend_ast_create_var(&$2.u.constant), AST_ZNODE(&$4)); }
| T_CURLY_OPEN variable '}' { $$.u.ast = $2.u.ast; }
;
encaps_var_offset:
T_STRING { $$ = $1; }
| T_NUM_STRING { $$ = $1; }
| T_VARIABLE { fetch_simple_variable(&$$, &$1, 1 TSRMLS_CC); }
T_STRING { $$.u.ast = AST_ZVAL(&$1); }
| T_NUM_STRING { $$.u.ast = AST_ZVAL(&$1); }
| T_VARIABLE { $$.u.ast = zend_ast_create_var(&$1.u.constant); }
;
internal_functions_in_yacc:
T_ISSET '(' isset_variables ')' { $$ = $3; }
| T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
| T_EMPTY '(' variable ')' { zend_do_isset_or_isempty(ZEND_ISEMPTY, &$$, &$3 TSRMLS_CC); }
| T_EMPTY '(' expr_without_variable ')' { zend_do_unary_op(ZEND_BOOL_NOT, &$$, &$3 TSRMLS_CC); }
| T_INCLUDE expr { zend_do_include_or_eval(ZEND_INCLUDE, &$$, &$2 TSRMLS_CC); }
| T_INCLUDE_ONCE expr { zend_do_include_or_eval(ZEND_INCLUDE_ONCE, &$$, &$2 TSRMLS_CC); }
@ -1200,7 +1268,7 @@ isset_variables:
;
isset_variable:
variable { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); }
variable { zend_do_isset_or_isempty(ZEND_ISSET, &$$, &$1 TSRMLS_CC); }
| expr_without_variable { zend_error_noreturn(E_COMPILE_ERROR, "Cannot use isset() on the result of an expression (you can use \"null !== expression\" instead)"); }
;