Skip to content

Commit 1d8c8cf

Browse files
committed
Implement range pattern
1 parent 8333a54 commit 1d8c8cf

File tree

8 files changed

+163
-2
lines changed

8 files changed

+163
-2
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
--TEST--
2+
Range pattern
3+
--FILE--
4+
<?php
5+
6+
var_dump(-1 is 0..<10);
7+
var_dump(0 is 0..<10);
8+
var_dump(1 is 0..<10);
9+
var_dump(9 is 0..<10);
10+
var_dump(10 is 0..<10);
11+
12+
var_dump(-1 is 0..=10);
13+
var_dump(0 is 0..=10);
14+
var_dump(1 is 0..=10);
15+
var_dump(9 is 0..=10);
16+
var_dump(10 is 0..=10);
17+
18+
var_dump(10.0 is 0..<10);
19+
var_dump(10.0 is 0..=10);
20+
21+
var_dump(9.5 is ..<10);
22+
var_dump(9.5 is ..=10);
23+
var_dump(10.0 is ..<10);
24+
var_dump(10.0 is ..=10);
25+
var_dump(10.5 is ..<10);
26+
var_dump(10.5 is ..=10);
27+
28+
var_dump(0.5 is 0..<);
29+
var_dump(0.5 is 0..=);
30+
var_dump(0.0 is 0..<);
31+
var_dump(0.0 is 0..=);
32+
var_dump(-0.5 is 0..<);
33+
var_dump(-0.5 is 0..=);
34+
35+
var_dump(0.09 is 0.0..=0.1);
36+
var_dump(0.1 is 0.0..=0.1);
37+
var_dump(0.11 is 0.0..=0.1);
38+
39+
?>
40+
--EXPECT--
41+
bool(false)
42+
bool(true)
43+
bool(true)
44+
bool(true)
45+
bool(false)
46+
bool(false)
47+
bool(true)
48+
bool(true)
49+
bool(true)
50+
bool(true)
51+
bool(false)
52+
bool(true)
53+
bool(true)
54+
bool(true)
55+
bool(false)
56+
bool(true)
57+
bool(false)
58+
bool(false)
59+
bool(true)
60+
bool(true)
61+
bool(true)
62+
bool(true)
63+
bool(false)
64+
bool(false)
65+
bool(true)
66+
bool(true)
67+
bool(false)

Zend/zend_ast.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ enum _zend_ast_kind {
167167
ZEND_AST_OBJECT_PATTERN_ELEMENT,
168168
ZEND_AST_ARRAY_PATTERN_ELEMENT,
169169
ZEND_AST_CLASS_CONST_PATTERN,
170+
ZEND_AST_RANGE_EXCLUSIVE,
171+
ZEND_AST_RANGE_INCLUSIVE,
170172

171173
/* 3 child nodes */
172174
ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT,

Zend/zend_compile.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6992,6 +6992,48 @@ static void zend_pm_compile_container(
69926992
}
69936993
}
69946994

6995+
static void zend_pm_compile_range(zend_ast *ast, znode *result, znode *expr_node, bool consume_expr, uint32_t false_opnum, zend_pm_context *context, bool is_inclusive)
6996+
{
6997+
zend_ast *lo_ast = ast->child[0];
6998+
zend_ast *hi_ast = ast->child[1];
6999+
bool consume_branch = consume_expr && lo_ast && hi_ast;
7000+
uint32_t false_label = consume_branch ? zend_pm_label_create(context) : false_opnum;
7001+
7002+
if (lo_ast) {
7003+
znode expr_node_copy;
7004+
zend_pm_copy_tmp(&expr_node_copy, expr_node, consume_expr && !consume_branch);
7005+
7006+
znode lo_node;
7007+
lo_node.op_type = IS_CONST;
7008+
ZVAL_COPY(&lo_node.u.constant, zend_ast_get_zval(lo_ast));
7009+
7010+
zend_op *opline = zend_emit_op_tmp(NULL, ZEND_IS_SMALLER_OR_EQUAL, &lo_node, &expr_node_copy);
7011+
SET_NODE(opline->result, result);
7012+
7013+
zend_pm_emit_jmpz_ex(result, false_label);
7014+
}
7015+
if (hi_ast) {
7016+
znode expr_node_copy;
7017+
zend_pm_copy_tmp(&expr_node_copy, expr_node, consume_expr && !consume_branch);
7018+
7019+
znode hi_node;
7020+
hi_node.op_type = IS_CONST;
7021+
ZVAL_COPY(&hi_node.u.constant, zend_ast_get_zval(hi_ast));
7022+
7023+
zend_op *opline = zend_emit_op_tmp(NULL,
7024+
is_inclusive ? ZEND_IS_SMALLER_OR_EQUAL : ZEND_IS_SMALLER,
7025+
&expr_node_copy, &hi_node);
7026+
SET_NODE(opline->result, result);
7027+
7028+
zend_pm_emit_jmpz_ex(result, false_label);
7029+
}
7030+
if (consume_branch) {
7031+
zend_pm_label_set_next(context, false_label);
7032+
zend_emit_op(NULL, ZEND_FREE, expr_node, NULL);
7033+
zend_pm_emit_jmpz_ex(result, false_opnum);
7034+
}
7035+
}
7036+
69957037
static void zend_compile_pattern(zend_ast *ast, znode *result, znode *expr_node, bool consume_expr, uint32_t false_opnum, zend_pm_context *context)
69967038
{
69977039
bool create_label = false_opnum == (uint32_t)-1;
@@ -7021,6 +7063,12 @@ static void zend_compile_pattern(zend_ast *ast, znode *result, znode *expr_node,
70217063
case ZEND_AST_OBJECT_PATTERN:
70227064
zend_pm_compile_container(ast, result, expr_node, consume_expr, false_opnum, context, false);
70237065
break;
7066+
case ZEND_AST_RANGE_EXCLUSIVE:
7067+
zend_pm_compile_range(ast, result, expr_node, consume_expr, false_opnum, context, false);
7068+
break;
7069+
case ZEND_AST_RANGE_INCLUSIVE:
7070+
zend_pm_compile_range(ast, result, expr_node, consume_expr, false_opnum, context, true);
7071+
break;
70247072
EMPTY_SWITCH_DEFAULT_CASE();
70257073
}
70267074

Zend/zend_language_parser.y

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
240240
%token T_COALESCE "'??'"
241241
%token T_POW "'**'"
242242
%token T_POW_EQUAL "'**='"
243-
%token T_PIPE "'|>'"
243+
%token T_PIPE "'|>'"
244+
%token T_RANGE_EXCLUSIVE "'..<'"
245+
%token T_RANGE_INCLUSIVE "'..='"
244246
/* We need to split the & token in two to avoid a shift/reduce conflict. For T1&$v and T1&T2,
245247
* with only one token lookahead, bison does not know whether to reduce T1 as a complete type,
246248
* or shift to continue parsing an intersection type. */
@@ -294,6 +296,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
294296
%type <ast> object_pattern object_pattern_element_list non_empty_object_pattern_element_list
295297
%type <ast> object_pattern_element binding_pattern
296298
%type <ast> array_pattern array_pattern_element_list array_pattern_element
299+
%type <ast> range_pattern range_pattern_element
297300

298301
%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
299302
%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
@@ -1428,6 +1431,7 @@ atomic_pattern:
14281431
compound_pattern:
14291432
or_pattern { $$ = $1; }
14301433
| and_pattern { $$ = $1; }
1434+
| range_pattern { $$ = $1; }
14311435
;
14321436

14331437
type_pattern:
@@ -1477,6 +1481,17 @@ and_pattern:
14771481
| pattern T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG pattern { $$ = zend_ast_merge_lists(ZEND_AST_AND_PATTERN, $1, $3); }
14781482
;
14791483

1484+
range_pattern:
1485+
range_pattern_element T_RANGE_EXCLUSIVE range_pattern_element { $$ = zend_ast_create(ZEND_AST_RANGE_EXCLUSIVE, $1, $3); }
1486+
| range_pattern_element T_RANGE_INCLUSIVE range_pattern_element { $$ = zend_ast_create(ZEND_AST_RANGE_INCLUSIVE, $1, $3); }
1487+
;
1488+
1489+
range_pattern_element:
1490+
%empty { $$ = NULL; }
1491+
| T_LNUMBER { $$ = $1; }
1492+
| T_DNUMBER { $$ = $1; }
1493+
;
1494+
14801495
binding_pattern:
14811496
T_VARIABLE { $$ = zend_ast_create(ZEND_AST_BINDING_PATTERN, $1); }
14821497
;

Zend/zend_language_scanner.l

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,6 +1622,14 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
16221622
RETURN_TOKEN(T_ELLIPSIS);
16231623
}
16241624

1625+
<ST_IN_SCRIPTING>"..<" {
1626+
RETURN_TOKEN(T_RANGE_EXCLUSIVE);
1627+
}
1628+
1629+
<ST_IN_SCRIPTING>"..=" {
1630+
RETURN_TOKEN(T_RANGE_INCLUSIVE);
1631+
}
1632+
16251633
<ST_IN_SCRIPTING>"??" {
16261634
RETURN_TOKEN(T_COALESCE);
16271635
}
@@ -2104,6 +2112,7 @@ OPTIONAL_WHITESPACE_OR_COMMENTS ({WHITESPACE}|{MULTI_LINE_COMMENT}|{SINGLE_LINE_
21042112
}
21052113

21062114
<ST_IN_SCRIPTING>{LNUM} {
2115+
lnum_handler:;
21072116
size_t len = yyleng;
21082117
char *end, *lnum = yytext;
21092118
bool is_octal = lnum[0] == '0';
@@ -2237,6 +2246,12 @@ string:
22372246
const char *end;
22382247
size_t len = yyleng;
22392248
char *dnum = yytext;
2249+
2250+
if (dnum[len - 1] == '.' && dnum[len] == '.' && (dnum[len + 1] == '<' || dnum[len + 1] == '=')) {
2251+
yyless(1);
2252+
goto lnum_handler;
2253+
}
2254+
22402255
bool contains_underscores = (memchr(dnum, '_', len) != NULL);
22412256

22422257
if (contains_underscores) {

ext/tokenizer/tokenizer_data.c

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/tokenizer/tokenizer_data.stub.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,16 @@
752752
* @cvalue T_PIPE
753753
*/
754754
const T_PIPE = UNKNOWN;
755+
/**
756+
* @var int
757+
* @cvalue T_RANGE_EXCLUSIVE
758+
*/
759+
const T_RANGE_EXCLUSIVE = UNKNOWN;
760+
/**
761+
* @var int
762+
* @cvalue T_RANGE_INCLUSIVE
763+
*/
764+
const T_RANGE_INCLUSIVE = UNKNOWN;
755765
/**
756766
* @var int
757767
* @cvalue T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG

ext/tokenizer/tokenizer_data_arginfo.h

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)