Skip to content

Commit 1b5d767

Browse files
authored
Support match expression v2 rfc (nikic#161)
The naming is based on `switch` statements. Note that even if statement blocks were supported after a future RFC those would probably be considered block **expressions**, hence the `expr` name for `AST_MATCH_ARM`. * Regenerate ast_stub.php * Add missing files to package.xml
1 parent d0545ee commit 1b5d767

File tree

11 files changed

+125
-15
lines changed

11 files changed

+125
-15
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,8 @@ AST_INSTANCEOF: expr, class
405405
AST_ISSET: var
406406
AST_LABEL: name
407407
AST_MAGIC_CONST:
408+
AST_MATCH: cond, stmts // php 8.0+ match
409+
AST_MATCH_ARM: cond, expr // php 8.0+ match
408410
AST_METHOD: name, docComment, params, stmts, returnType, attributes
409411
uses // prior to version 60
410412
AST_METHOD_CALL: expr, method, args
@@ -457,6 +459,7 @@ AST_ENCAPS_LIST // interpolated string: "foo$bar"
457459
AST_EXPR_LIST
458460
AST_IF
459461
AST_LIST
462+
AST_MATCH_ARM_LIST // php 8.0+ match
460463
AST_NAME_LIST
461464
AST_PARAM_LIST
462465
AST_PROP_DECL

ast.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ static const ast_flag_info flag_info[] = {
287287
{ ZEND_AST_PROP_DECL, 1, modifier_flags },
288288
{ ZEND_AST_PROP_GROUP, 1, modifier_flags },
289289
{ ZEND_AST_CLASS_CONST_DECL, 1, visibility_flags },
290+
{ ZEND_AST_CLASS_CONST_GROUP, 1, visibility_flags },
290291
{ ZEND_AST_TRAIT_ALIAS, 1, modifier_flags },
291292
{ ZEND_AST_DIM, 1, dim_flags },
292293
{ ZEND_AST_CONDITIONAL, 1, conditional_flags },
@@ -355,6 +356,7 @@ static inline zend_bool ast_kind_uses_attr(zend_ast_kind kind) {
355356
|| kind == ZEND_AST_PROP_GROUP
356357
|| kind == ZEND_AST_GROUP_USE || kind == ZEND_AST_USE_ELEM
357358
|| kind == AST_NAME || kind == AST_CLOSURE_VAR || kind == ZEND_AST_CLASS_CONST_DECL
359+
|| kind == ZEND_AST_CLASS_CONST_GROUP
358360
|| kind == ZEND_AST_ARRAY || kind == ZEND_AST_DIM || kind == ZEND_AST_CONDITIONAL;
359361
}
360362

@@ -829,6 +831,7 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
829831
// ast->child is [AST_CLASS_CONST_DECL, optional attributes_list]
830832
if (state->version < 80) {
831833
// Keep class constants as a list of numerically indexed values in php 8
834+
ast->child[0]->attr = ast->attr;
832835
ast_to_zval(zv, ast->child[0], state);
833836
return;
834837
}
@@ -975,9 +978,10 @@ static void ast_to_zval(zval *zv, zend_ast *ast, ast_state_info_t *state) {
975978
zval attributes_zval;
976979
ZVAL_COPY_VALUE(&const_decl_zval, zv);
977980
ZVAL_NULL(&attributes_zval);
981+
ast_update_property_long(zv, AST_STR(str_flags), 0, AST_CACHE_SLOT_FLAGS);
978982
// For version 80, create an AST_CLASS_CONST_GROUP wrapping the created AST_CLASS_CONST_DECL
979983
ast_create_virtual_node_ex(
980-
zv, ZEND_AST_CLASS_CONST_GROUP, 0, zend_ast_get_lineno(ast), state, 2, &const_decl_zval, &attributes_zval);
984+
zv, ZEND_AST_CLASS_CONST_GROUP, ast->attr, zend_ast_get_lineno(ast), state, 2, &const_decl_zval, &attributes_zval);
981985
}
982986
#endif
983987
}

ast_data.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const zend_ast_kind ast_kinds[] = {
2020
ZEND_AST_USE,
2121
ZEND_AST_TYPE_UNION,
2222
ZEND_AST_ATTRIBUTE_LIST,
23+
ZEND_AST_MATCH_ARM_LIST,
2324
AST_NAME,
2425
AST_CLOSURE_VAR,
2526
AST_NULLABLE_TYPE,
@@ -91,6 +92,8 @@ const zend_ast_kind ast_kinds[] = {
9192
ZEND_AST_TRAIT_ALIAS,
9293
ZEND_AST_GROUP_USE,
9394
ZEND_AST_ATTRIBUTE,
95+
ZEND_AST_MATCH,
96+
ZEND_AST_MATCH_ARM,
9497
ZEND_AST_METHOD_CALL,
9598
ZEND_AST_STATIC_CALL,
9699
ZEND_AST_CONDITIONAL,
@@ -124,6 +127,7 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
124127
case ZEND_AST_USE: return "AST_USE";
125128
case ZEND_AST_TYPE_UNION: return "AST_TYPE_UNION";
126129
case ZEND_AST_ATTRIBUTE_LIST: return "AST_ATTRIBUTE_LIST";
130+
case ZEND_AST_MATCH_ARM_LIST: return "AST_MATCH_ARM_LIST";
127131
case AST_NAME: return "AST_NAME";
128132
case AST_CLOSURE_VAR: return "AST_CLOSURE_VAR";
129133
case AST_NULLABLE_TYPE: return "AST_NULLABLE_TYPE";
@@ -195,6 +199,8 @@ const char *ast_kind_to_name(zend_ast_kind kind) {
195199
case ZEND_AST_TRAIT_ALIAS: return "AST_TRAIT_ALIAS";
196200
case ZEND_AST_GROUP_USE: return "AST_GROUP_USE";
197201
case ZEND_AST_ATTRIBUTE: return "AST_ATTRIBUTE";
202+
case ZEND_AST_MATCH: return "AST_MATCH";
203+
case ZEND_AST_MATCH_ARM: return "AST_MATCH_ARM";
198204
case ZEND_AST_METHOD_CALL: return "AST_METHOD_CALL";
199205
case ZEND_AST_STATIC_CALL: return "AST_STATIC_CALL";
200206
case ZEND_AST_CONDITIONAL: return "AST_CONDITIONAL";
@@ -613,6 +619,18 @@ zend_string *ast_kind_child_name(zend_ast_kind kind, uint32_t child) {
613619
case 1: return AST_STR(str_args);
614620
}
615621
return NULL;
622+
case ZEND_AST_MATCH:
623+
switch (child) {
624+
case 0: return AST_STR(str_cond);
625+
case 1: return AST_STR(str_stmts);
626+
}
627+
return NULL;
628+
case ZEND_AST_MATCH_ARM:
629+
switch (child) {
630+
case 0: return AST_STR(str_cond);
631+
case 1: return AST_STR(str_expr);
632+
}
633+
return NULL;
616634
case ZEND_AST_METHOD_CALL:
617635
switch (child) {
618636
case 0: return AST_STR(str_expr);
@@ -698,6 +716,7 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
698716
REGISTER_NS_LONG_CONSTANT("ast", "AST_USE", ZEND_AST_USE, CONST_CS | CONST_PERSISTENT);
699717
REGISTER_NS_LONG_CONSTANT("ast", "AST_TYPE_UNION", ZEND_AST_TYPE_UNION, CONST_CS | CONST_PERSISTENT);
700718
REGISTER_NS_LONG_CONSTANT("ast", "AST_ATTRIBUTE_LIST", ZEND_AST_ATTRIBUTE_LIST, CONST_CS | CONST_PERSISTENT);
719+
REGISTER_NS_LONG_CONSTANT("ast", "AST_MATCH_ARM_LIST", ZEND_AST_MATCH_ARM_LIST, CONST_CS | CONST_PERSISTENT);
701720
REGISTER_NS_LONG_CONSTANT("ast", "AST_NAME", AST_NAME, CONST_CS | CONST_PERSISTENT);
702721
REGISTER_NS_LONG_CONSTANT("ast", "AST_CLOSURE_VAR", AST_CLOSURE_VAR, CONST_CS | CONST_PERSISTENT);
703722
REGISTER_NS_LONG_CONSTANT("ast", "AST_NULLABLE_TYPE", AST_NULLABLE_TYPE, CONST_CS | CONST_PERSISTENT);
@@ -769,6 +788,8 @@ void ast_register_kind_constants(INIT_FUNC_ARGS) {
769788
REGISTER_NS_LONG_CONSTANT("ast", "AST_TRAIT_ALIAS", ZEND_AST_TRAIT_ALIAS, CONST_CS | CONST_PERSISTENT);
770789
REGISTER_NS_LONG_CONSTANT("ast", "AST_GROUP_USE", ZEND_AST_GROUP_USE, CONST_CS | CONST_PERSISTENT);
771790
REGISTER_NS_LONG_CONSTANT("ast", "AST_ATTRIBUTE", ZEND_AST_ATTRIBUTE, CONST_CS | CONST_PERSISTENT);
791+
REGISTER_NS_LONG_CONSTANT("ast", "AST_MATCH", ZEND_AST_MATCH, CONST_CS | CONST_PERSISTENT);
792+
REGISTER_NS_LONG_CONSTANT("ast", "AST_MATCH_ARM", ZEND_AST_MATCH_ARM, CONST_CS | CONST_PERSISTENT);
772793
REGISTER_NS_LONG_CONSTANT("ast", "AST_METHOD_CALL", ZEND_AST_METHOD_CALL, CONST_CS | CONST_PERSISTENT);
773794
REGISTER_NS_LONG_CONSTANT("ast", "AST_STATIC_CALL", ZEND_AST_STATIC_CALL, CONST_CS | CONST_PERSISTENT);
774795
REGISTER_NS_LONG_CONSTANT("ast", "AST_CONDITIONAL", ZEND_AST_CONDITIONAL, CONST_CS | CONST_PERSISTENT);

ast_stub.php

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
const AST_TRAIT_ADAPTATIONS = 142;
2626
const AST_USE = 143;
2727
const AST_TYPE_UNION = 144;
28+
const AST_ATTRIBUTE_LIST = 145;
29+
const AST_MATCH_ARM_LIST = 146;
2830
const AST_NAME = 2048;
2931
const AST_CLOSURE_VAR = 2049;
3032
const AST_NULLABLE_TYPE = 2050;
@@ -64,6 +66,7 @@
6466
const AST_BREAK = 286;
6567
const AST_CONTINUE = 287;
6668
const AST_CLASS_NAME = 276;
69+
const AST_CLASS_CONST_GROUP = 545;
6770
const AST_DIM = 512;
6871
const AST_PROP = 513;
6972
const AST_STATIC_PROP = 514;
@@ -94,14 +97,17 @@
9497
const AST_USE_ELEM = 542;
9598
const AST_TRAIT_ALIAS = 543;
9699
const AST_GROUP_USE = 544;
100+
const AST_ATTRIBUTE = 546;
101+
const AST_MATCH = 547;
102+
const AST_MATCH_ARM = 548;
97103
const AST_METHOD_CALL = 768;
98104
const AST_STATIC_CALL = 769;
99105
const AST_CONDITIONAL = 770;
100106
const AST_TRY = 771;
101107
const AST_CATCH = 772;
102-
const AST_PARAM = 1280;
103108
const AST_FOR = 1024;
104109
const AST_FOREACH = 1025;
110+
const AST_PARAM = 1280;
105111
// END AST KIND CONSTANTS
106112

107113
// AST FLAG CONSTANTS
@@ -178,14 +184,14 @@
178184
const USE_NORMAL = 1;
179185
const USE_FUNCTION = 2;
180186
const USE_CONST = 4;
181-
const MAGIC_LINE = 373;
182-
const MAGIC_FILE = 374;
183-
const MAGIC_DIR = 375;
184-
const MAGIC_NAMESPACE = 392;
185-
const MAGIC_FUNCTION = 379;
186-
const MAGIC_METHOD = 378;
187-
const MAGIC_CLASS = 376;
188-
const MAGIC_TRAIT = 377;
187+
const MAGIC_LINE = 372;
188+
const MAGIC_FILE = 373;
189+
const MAGIC_DIR = 374;
190+
const MAGIC_NAMESPACE = 379;
191+
const MAGIC_FUNCTION = 378;
192+
const MAGIC_METHOD = 377;
193+
const MAGIC_CLASS = 375;
194+
const MAGIC_TRAIT = 376;
189195
const ARRAY_SYNTAX_LIST = 1;
190196
const ARRAY_SYNTAX_LONG = 2;
191197
const ARRAY_SYNTAX_SHORT = 3;

package.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,12 @@
5050
<file name="array_destructuring.phpt" role="test" />
5151
<file name="assign_ops.phpt" role="test" />
5252
<file name="ast_dump_with_linenos.phpt" role="test" />
53+
<file name="attributes_01.phpt" role="test" />
54+
<file name="attributes_02.phpt" role="test" />
5355
<file name="binary_ops.phpt" role="test" />
5456
<file name="by_ref_destructuring.phpt" role="test" />
5557
<file name="class_consts.phpt" role="test" />
58+
<file name="class_consts_80.phpt" role="test" />
5659
<file name="class_on_objects.phpt" role="test" />
5760
<file name="class_name_version_50.phpt" role="test" />
5861
<file name="class_name_version_70.phpt" role="test" />
@@ -73,7 +76,9 @@
7376
<file name="get_supported_versions.phpt" role="test" />
7477
<file name="invalid_file.php" role="test" />
7578
<file name="magic_constants.phpt" role="test" />
79+
<file name="match_expression.phpt" role="test" />
7680
<file name="metadata.phpt" role="test" />
81+
<file name="mixed_type.phpt" role="test" />
7782
<file name="multi_catch.phpt" role="test" />
7883
<file name="multiple_final_modifiers.phpt" role="test" />
7984
<file name="named_children.phpt" role="test" />

php_ast.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ extern ast_str_globals str_globals;
6161
/* NOTE: For list nodes, the first set bit is 0x80 */
6262
# define ZEND_AST_TYPE_UNION ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 2)
6363
# define ZEND_AST_ATTRIBUTE_LIST ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 3)
64-
// NOTE: The first hex digit is the number of child nodes a given kind has
64+
# define ZEND_AST_MATCH_ARM_LIST ((1 << (ZEND_AST_IS_LIST_SHIFT + 1)) - 4)
6565
# define ZEND_AST_CLASS_CONST_GROUP 0x2fe
6666
# define ZEND_AST_ATTRIBUTE 0x2fd
67+
# define ZEND_AST_MATCH 0x2fc
68+
# define ZEND_AST_MATCH_ARM 0x2fb
69+
// NOTE: The first hex digit is the number of child nodes a given kind has
6770
#endif
6871

6972
/* Pretend it still exists */

scripts/generate_ast_data.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@
130130
'ZEND_AST_TRAIT_ALIAS' => ['method', 'alias'],
131131
'ZEND_AST_GROUP_USE' => ['prefix', 'uses'],
132132
'ZEND_AST_ATTRIBUTE' => ['class', 'args'],
133+
'ZEND_AST_MATCH' => ['cond', 'stmts'],
134+
'ZEND_AST_MATCH_ARM' => ['cond', 'expr'],
133135

134136
/* 3 child nodes */
135137
'ZEND_AST_METHOD_CALL' => ['expr', 'method', 'args'],
@@ -167,6 +169,7 @@
167169
'ZEND_AST_USE',
168170
'ZEND_AST_TYPE_UNION',
169171
'ZEND_AST_ATTRIBUTE_LIST',
172+
'ZEND_AST_MATCH_ARM_LIST',
170173
];
171174

172175
$data = [];

tests/attributes_02.phpt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,9 @@ AST_STMT_LIST
101101
flags: NAME_NOT_FQ (%d)
102102
name: "true"
103103
1: AST_CLASS_CONST_GROUP
104+
flags: MODIFIER_PUBLIC (%d)
104105
const: AST_CLASS_CONST_DECL
105-
flags: MODIFIER_PUBLIC (%d)
106+
flags: 0
106107
0: AST_CONST_ELEM
107108
name: "CONST_WITH_ATTRIBUTE"
108109
value: 123

tests/class_consts_80.phpt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,18 @@ AST_STMT_LIST
3333
implements: null
3434
stmts: AST_STMT_LIST
3535
0: AST_CLASS_CONST_GROUP
36+
flags: MODIFIER_PUBLIC (%d)
3637
const: AST_CLASS_CONST_DECL
37-
flags: MODIFIER_PUBLIC (%d)
38+
flags: 0
3839
0: AST_CONST_ELEM
3940
name: "A"
4041
value: 1
4142
docComment: "/** Doc A */"
4243
attributes: null
4344
1: AST_CLASS_CONST_GROUP
45+
flags: MODIFIER_PROTECTED (%d)
4446
const: AST_CLASS_CONST_DECL
45-
flags: MODIFIER_PROTECTED (%d)
47+
flags: 0
4648
0: AST_CONST_ELEM
4749
name: "E"
4850
value: 5

tests/match_expression.phpt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
--TEST--
2+
Match expression in PHP 8.0
3+
--SKIPIF--
4+
<?php if (PHP_VERSION_ID < 80000) die('skip PHP >= 8.0 only'); ?>
5+
--FILE--
6+
<?php
7+
8+
require __DIR__ . '/../util.php';
9+
10+
$code = <<<'PHP'
11+
<?php
12+
$x = match($y) { 2 => 3, default => 5 };
13+
14+
match(1) {};
15+
match(my_const) {
16+
1, \other_const => $x,
17+
default, => 123,
18+
};
19+
PHP;
20+
21+
$node = ast\parse_code($code, $version=70);
22+
echo ast_dump($node), "\n";
23+
--EXPECTF--
24+
AST_STMT_LIST
25+
0: AST_ASSIGN
26+
var: AST_VAR
27+
name: "x"
28+
expr: AST_MATCH
29+
cond: AST_VAR
30+
name: "y"
31+
stmts: AST_MATCH_ARM_LIST
32+
0: AST_MATCH_ARM
33+
cond: AST_EXPR_LIST
34+
0: 2
35+
expr: 3
36+
1: AST_MATCH_ARM
37+
cond: null
38+
expr: 5
39+
1: AST_MATCH
40+
cond: 1
41+
stmts: AST_MATCH_ARM_LIST
42+
2: AST_MATCH
43+
cond: AST_CONST
44+
name: AST_NAME
45+
flags: NAME_NOT_FQ (%d)
46+
name: "my_const"
47+
stmts: AST_MATCH_ARM_LIST
48+
0: AST_MATCH_ARM
49+
cond: AST_EXPR_LIST
50+
0: 1
51+
1: AST_CONST
52+
name: AST_NAME
53+
flags: NAME_FQ (%d)
54+
name: "other_const"
55+
expr: AST_VAR
56+
name: "x"
57+
1: AST_MATCH_ARM
58+
cond: null
59+
expr: 123

0 commit comments

Comments
 (0)