Skip to content

Commit c174250

Browse files
committed
Implement and pattern
1 parent 479825b commit c174250

File tree

6 files changed

+65
-7
lines changed

6 files changed

+65
-7
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
And pattern
3+
--FILE--
4+
<?php
5+
6+
var_dump(1 is (int&1));
7+
var_dump(2 is (int&(1|2)));
8+
var_dump(3 is (float&1));
9+
var_dump(4 is (int&float));
10+
var_dump([] is ([]&[...]));
11+
var_dump('foo' is (string&'bar'));
12+
13+
?>
14+
--EXPECT--
15+
bool(true)
16+
bool(true)
17+
bool(false)
18+
bool(false)
19+
bool(true)
20+
bool(false)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
Array pattern with mixed implicit and explicit keys
3+
--FILE--
4+
<?php
5+
6+
var_dump([] is ['foo', 1 => 'bar']);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Must not mix implicit and explicit array keys in array pattern in %s on line %d

Zend/zend_ast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ enum _zend_ast_kind {
156156
ZEND_AST_NAMED_ARG,
157157
ZEND_AST_IS,
158158
ZEND_AST_OR_PATTERN,
159+
ZEND_AST_AND_PATTERN,
159160
ZEND_AST_OBJECT_PATTERN,
160161
ZEND_AST_OBJECT_PATTERN_ELEMENT,
161162
ZEND_AST_RANGE_PATTERN,

Zend/zend_compile.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6155,11 +6155,13 @@ static void zend_compile_array_pattern(zend_ast **ast_ptr)
61556155
{
61566156
zend_ast *array_pattern = *ast_ptr;
61576157
zend_ast_list *element_list = zend_ast_get_list(array_pattern->child[0]);
6158+
bool has_implicit = false, has_explicit = false;
61586159

61596160
for (uint32_t i = 0; i < element_list->children; i++) {
61606161
zend_ast *element = element_list->child[i];
61616162
zend_ast *key_ast = element->child[0];
61626163
if (key_ast) {
6164+
has_explicit = true;
61636165
zval *key = zend_ast_get_zval(key_ast);
61646166
if (Z_TYPE_P(key) == IS_STRING) {
61656167
zend_ulong index;
@@ -6168,8 +6170,13 @@ static void zend_compile_array_pattern(zend_ast **ast_ptr)
61686170
ZVAL_LONG(key, index);
61696171
}
61706172
}
6173+
} else {
6174+
has_implicit = true;
61716175
}
61726176
}
6177+
if (has_implicit && has_explicit) {
6178+
zend_throw_exception(zend_ce_compile_error, "Must not mix implicit and explicit array keys in array pattern", 0);
6179+
}
61736180
}
61746181

61756182
static void zend_compile_pattern(zend_ast **ast_ptr, void *context)
@@ -6204,7 +6211,7 @@ static void zend_compile_is(znode *result, zend_ast *ast)
62046211

62056212
znode expr_node;
62066213
zend_compile_expr(&expr_node, expr_ast);
6207-
6214+
62086215
zend_compile_pattern(pattern_ast_ptr, NULL);
62096216
zval pattern_zv;
62106217
ZVAL_AST(&pattern_zv, zend_ast_copy(*pattern_ast_ptr));

Zend/zend_language_parser.y

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
284284
%type <ast> match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list match_arm_cond
285285
%type <ast> enum_declaration_statement enum_backing_type enum_case enum_case_expr
286286
%type <ast> function_name non_empty_member_modifiers
287-
%type <ast> pattern atomic_pattern compound_pattern type_pattern scalar_pattern or_pattern
287+
%type <ast> pattern atomic_pattern compound_pattern type_pattern scalar_pattern or_pattern and_pattern
288288
%type <ast> object_pattern object_pattern_element_list non_empty_object_pattern_element_list
289289
%type <ast> object_pattern_element range_pattern binding_pattern
290290
%type <ast> array_pattern array_pattern_element_list array_pattern_element
@@ -1297,6 +1297,7 @@ atomic_pattern:
12971297

12981298
compound_pattern:
12991299
or_pattern { $$ = $1; }
1300+
| and_pattern { $$ = $1; }
13001301
;
13011302

13021303
type_pattern:
@@ -1337,6 +1338,10 @@ or_pattern:
13371338
pattern '|' pattern { $$ = zend_ast_create(ZEND_AST_OR_PATTERN, $1, $3); }
13381339
;
13391340

1341+
and_pattern:
1342+
pattern T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG pattern { $$ = zend_ast_create(ZEND_AST_AND_PATTERN, $1, $3); }
1343+
;
1344+
13401345
range_pattern:
13411346
scalar_pattern T_RANGE_EXCLUSIVE_END scalar_pattern { $$ = zend_ast_create(ZEND_AST_RANGE_PATTERN, $1, $3); }
13421347
| scalar_pattern T_RANGE_INCLUSIVE_END scalar_pattern

Zend/zend_pattern_matching.c

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -189,11 +189,18 @@ static pm_result match_array(zval *zv, zend_ast *pattern)
189189
HashTable *ht = Z_ARRVAL_P(zv);
190190
zend_ast_list *element_list = zend_ast_get_list(pattern->child[0]);
191191

192-
if (!(pattern->attr & ZEND_ARRAY_PATTERN_NON_EXHAUSTIVE) && element_list->children != zend_hash_num_elements(ht)) {
193-
return PM_MISMATCH;
192+
if (pattern->attr & ZEND_ARRAY_PATTERN_NON_EXHAUSTIVE) {
193+
if (element_list->children > zend_hash_num_elements(ht)) {
194+
return PM_MISMATCH;
195+
}
196+
} else {
197+
if (element_list->children != zend_hash_num_elements(ht)) {
198+
return PM_MISMATCH;
199+
}
194200
}
195201

196-
// FIXME: Deal with indexes properly
202+
/* Explicit and implicit keys may not be mixed, so there's no need to
203+
* replicate array key sequencing logic. */
197204
zend_long index = 0;
198205

199206
for (uint32_t i = 0; i < element_list->children; i++) {
@@ -213,7 +220,7 @@ static pm_result match_array(zval *zv, zend_ast *pattern)
213220
element_zv = zend_hash_index_find(ht, index);
214221
index++;
215222
}
216-
if (Z_TYPE_P(element_zv) == IS_UNDEF || !zend_pattern_match_ex(element_zv, pattern_ast)) {
223+
if (!element_zv || Z_TYPE_P(element_zv) == IS_UNDEF || !zend_pattern_match_ex(element_zv, pattern_ast)) {
217224
return PM_MISMATCH;
218225
}
219226
}
@@ -235,12 +242,20 @@ pm_result zend_pattern_match_ex(zval *zv, zend_ast *pattern)
235242
return match_object(zv, pattern);
236243
case ZEND_AST_WILDCARD_PATTERN:
237244
return PM_MATCH;
238-
case ZEND_AST_OR_PATTERN:;
245+
case ZEND_AST_OR_PATTERN: {
239246
pm_result lhs = zend_pattern_match_ex(zv, pattern->child[0]);
240247
if (lhs != PM_MISMATCH) {
241248
return lhs;
242249
}
243250
return zend_pattern_match_ex(zv, pattern->child[1]);
251+
}
252+
case ZEND_AST_AND_PATTERN: {
253+
pm_result lhs = zend_pattern_match_ex(zv, pattern->child[0]);
254+
if (lhs != PM_MATCH) {
255+
return lhs;
256+
}
257+
return zend_pattern_match_ex(zv, pattern->child[1]);
258+
}
244259
case ZEND_AST_RANGE_PATTERN:
245260
return match_range(zv, pattern);
246261
case ZEND_AST_BINDING_PATTERN:

0 commit comments

Comments
 (0)