Skip to content

Commit ed0b82d

Browse files
committed
Implement comparison pattern
We'll go with either ranges or this, yet to decide.
1 parent 1d8c8cf commit ed0b82d

File tree

11 files changed

+793
-564
lines changed

11 files changed

+793
-564
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
--TEST--
2+
Comparison pattern
3+
--FILE--
4+
<?php
5+
6+
echo "Basic comparison\n";
7+
8+
var_dump(0 is <0);
9+
var_dump(0 is <1);
10+
var_dump(1 is <0);
11+
var_dump(0 is <=0);
12+
var_dump(0 is <=1);
13+
var_dump(1 is <=0);
14+
var_dump(0 is >0);
15+
var_dump(0 is >1);
16+
var_dump(1 is >0);
17+
var_dump(0 is >=0);
18+
var_dump(0 is >=1);
19+
var_dump(1 is >=0);
20+
21+
echo "\nNumeric strings\n";
22+
23+
var_dump('1' is >0);
24+
var_dump('1e2' is >99);
25+
var_dump('1e2' is >101);
26+
var_dump('3.141' is >3);
27+
var_dump('3.141' is >4);
28+
29+
echo "\nNon-numeric\n";
30+
31+
var_dump('foo' is >0);
32+
var_dump('10foo' is >0);
33+
var_dump('10 foo' is >0);
34+
var_dump([] is >0);
35+
var_dump(new stdClass is >0);
36+
37+
?>
38+
--EXPECT--
39+
Basic comparison
40+
bool(false)
41+
bool(true)
42+
bool(false)
43+
bool(true)
44+
bool(true)
45+
bool(false)
46+
bool(false)
47+
bool(false)
48+
bool(true)
49+
bool(true)
50+
bool(false)
51+
bool(true)
52+
53+
Numeric strings
54+
bool(true)
55+
bool(true)
56+
bool(false)
57+
bool(true)
58+
bool(false)
59+
60+
Non-numeric
61+
bool(false)
62+
bool(false)
63+
bool(false)
64+
bool(false)
65+
bool(false)

Zend/zend_ast.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ enum _zend_ast_kind {
119119
ZEND_AST_ARRAY_PATTERN,
120120
ZEND_AST_BINDING_PATTERN,
121121
ZEND_AST_EXPR_LIKE_PATTERN,
122+
ZEND_AST_COMPARISON_PATTERN,
122123

123124
/* 2 child nodes */
124125
ZEND_AST_DIM = 2 << ZEND_AST_NUM_CHILDREN_SHIFT,

Zend/zend_compile.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7034,6 +7034,49 @@ static void zend_pm_compile_range(zend_ast *ast, znode *result, znode *expr_node
70347034
}
70357035
}
70367036

7037+
static void zend_pm_compile_comparison(zend_ast *ast, znode *result, znode *expr_node, bool consume_expr, uint32_t false_opnum, zend_pm_context *context)
7038+
{
7039+
zend_op *opline;
7040+
7041+
uint32_t false_label = consume_expr ? zend_pm_label_create(context) : false_opnum;
7042+
7043+
opline = zend_emit_op_tmp(NULL, ZEND_IS_NUMERIC, expr_node, NULL);
7044+
SET_NODE(opline->result, result);
7045+
zend_pm_emit_jmpz_ex(result, false_label);
7046+
7047+
znode expr_node_copy;
7048+
zend_pm_copy_tmp(&expr_node_copy, expr_node, false);
7049+
7050+
zend_ast *const_ast = ast->child[0];
7051+
znode const_node;
7052+
const_node.op_type = IS_CONST;
7053+
ZVAL_COPY(&const_node.u.constant, zend_ast_get_zval(const_ast));
7054+
7055+
switch (ast->attr) {
7056+
case ZEND_COMPARISON_PATTERN_SMALLER:
7057+
opline = zend_emit_op_tmp(NULL, ZEND_IS_SMALLER, &expr_node_copy, &const_node);
7058+
break;
7059+
case ZEND_COMPARISON_PATTERN_SMALLER_OR_EQUAL:
7060+
opline = zend_emit_op_tmp(NULL, ZEND_IS_SMALLER_OR_EQUAL, &expr_node_copy, &const_node);
7061+
break;
7062+
case ZEND_COMPARISON_PATTERN_GREATER:
7063+
opline = zend_emit_op_tmp(NULL, ZEND_IS_SMALLER, &const_node, &expr_node_copy);
7064+
break;
7065+
case ZEND_COMPARISON_PATTERN_GREATER_OR_EQUAL:
7066+
opline = zend_emit_op_tmp(NULL, ZEND_IS_SMALLER_OR_EQUAL, &const_node, &expr_node_copy);
7067+
break;
7068+
EMPTY_SWITCH_DEFAULT_CASE();
7069+
}
7070+
SET_NODE(opline->result, result);
7071+
zend_pm_emit_jmpz_ex(result, false_label);
7072+
7073+
if (consume_expr) {
7074+
zend_pm_label_set_next(context, false_label);
7075+
zend_emit_op(NULL, ZEND_FREE, expr_node, NULL);
7076+
zend_pm_emit_jmpz_ex(result, false_opnum);
7077+
}
7078+
}
7079+
70377080
static void zend_compile_pattern(zend_ast *ast, znode *result, znode *expr_node, bool consume_expr, uint32_t false_opnum, zend_pm_context *context)
70387081
{
70397082
bool create_label = false_opnum == (uint32_t)-1;
@@ -7069,6 +7112,9 @@ static void zend_compile_pattern(zend_ast *ast, znode *result, znode *expr_node,
70697112
case ZEND_AST_RANGE_INCLUSIVE:
70707113
zend_pm_compile_range(ast, result, expr_node, consume_expr, false_opnum, context, true);
70717114
break;
7115+
case ZEND_AST_COMPARISON_PATTERN:
7116+
zend_pm_compile_comparison(ast, result, expr_node, consume_expr, false_opnum, context);
7117+
break;
70727118
EMPTY_SWITCH_DEFAULT_CASE();
70737119
}
70747120

Zend/zend_compile.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,6 +1228,11 @@ static zend_always_inline bool zend_check_arg_send_type(const zend_function *zf,
12281228
/* Array pattern contains ... */
12291229
#define ZEND_ARRAY_PATTERN_NON_EXHAUSTIVE (1<<1)
12301230

1231+
#define ZEND_COMPARISON_PATTERN_SMALLER 0
1232+
#define ZEND_COMPARISON_PATTERN_SMALLER_OR_EQUAL 1
1233+
#define ZEND_COMPARISON_PATTERN_GREATER 2
1234+
#define ZEND_COMPARISON_PATTERN_GREATER_OR_EQUAL 3
1235+
12311236
/* For "use" AST nodes and the seen symbol table */
12321237
#define ZEND_SYMBOL_CLASS (1<<0)
12331238
#define ZEND_SYMBOL_FUNCTION (1<<1)

Zend/zend_language_parser.y

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,12 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
296296
%type <ast> object_pattern object_pattern_element_list non_empty_object_pattern_element_list
297297
%type <ast> object_pattern_element binding_pattern
298298
%type <ast> array_pattern array_pattern_element_list array_pattern_element
299-
%type <ast> range_pattern range_pattern_element
299+
%type <ast> range_pattern range_pattern_element comparison_pattern comparison_pattern_element
300300

301301
%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
302302
%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
303303
%type <num> class_modifiers class_modifier anonymous_class_modifiers anonymous_class_modifiers_optional use_type backup_fn_flags
304+
%type <num> comparison_pattern_op
304305

305306
%type <ptr> backup_lex_pos
306307
%type <str> backup_doc_comment
@@ -1422,6 +1423,7 @@ atomic_pattern:
14221423
| array_pattern { $$ = $1; }
14231424
| binding_pattern { $$ = $1; }
14241425
| class_const_pattern { $$ = zend_ast_create(ZEND_AST_EXPR_LIKE_PATTERN, $1); }
1426+
| comparison_pattern { $$ = $1; }
14251427
| '(' pattern ')' {
14261428
$$ = $2;
14271429
$$->attr = ZEND_PARENTHESIZED_PATTERN;
@@ -1492,6 +1494,22 @@ range_pattern_element:
14921494
| T_DNUMBER { $$ = $1; }
14931495
;
14941496

1497+
comparison_pattern_op:
1498+
'<' { $<num>$ = ZEND_COMPARISON_PATTERN_SMALLER; }
1499+
| T_IS_SMALLER_OR_EQUAL { $<num>$ = ZEND_COMPARISON_PATTERN_SMALLER_OR_EQUAL; }
1500+
| '>' { $<num>$ = ZEND_COMPARISON_PATTERN_GREATER; }
1501+
| T_IS_GREATER_OR_EQUAL { $<num>$ = ZEND_COMPARISON_PATTERN_GREATER_OR_EQUAL; }
1502+
;
1503+
1504+
comparison_pattern_element:
1505+
T_LNUMBER { $$ = $1; }
1506+
| T_DNUMBER { $$ = $1; }
1507+
;
1508+
1509+
comparison_pattern:
1510+
comparison_pattern_op comparison_pattern_element { $$ = zend_ast_create_ex(ZEND_AST_COMPARISON_PATTERN, $1, $2); }
1511+
;
1512+
14951513
binding_pattern:
14961514
T_VARIABLE { $$ = zend_ast_create(ZEND_AST_BINDING_PATTERN, $1); }
14971515
;

Zend/zend_opcode.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -907,7 +907,8 @@ static bool keeps_op1_alive(zend_op *opline) {
907907
|| opline->opcode == ZEND_FETCH_LIST_W
908908
|| opline->opcode == ZEND_COPY_TMP
909909
|| opline->opcode == ZEND_EXT_STMT
910-
|| opline->opcode == ZEND_HAS_TYPE) {
910+
|| opline->opcode == ZEND_HAS_TYPE
911+
|| opline->opcode == ZEND_IS_NUMERIC) {
911912
return true;
912913
}
913914
ZEND_ASSERT(opline->opcode != ZEND_FE_FETCH_R

Zend/zend_vm_def.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9979,6 +9979,33 @@ ZEND_VM_HANDLER(209, ZEND_INIT_PARENT_PROPERTY_HOOK_CALL, CONST, UNUSED|NUM, NUM
99799979
ZEND_VM_NEXT_OPCODE();
99809980
}
99819981

9982+
ZEND_VM_HANDLER(212, ZEND_IS_NUMERIC, ANY, UNUSED)
9983+
{
9984+
USE_OPLINE
9985+
9986+
zval *op1 = GET_OP1_ZVAL_PTR(BP_VAR_R);
9987+
zval *result = EX_VAR(opline->result.var);
9988+
9989+
ZEND_VM_C_LABEL(try_again):
9990+
switch (Z_TYPE_P(op1)) {
9991+
case IS_LONG:
9992+
case IS_DOUBLE:
9993+
ZVAL_TRUE(result);
9994+
break;
9995+
case IS_STRING:
9996+
ZVAL_BOOL(result, is_numeric_string(Z_STRVAL_P(op1), Z_STRLEN_P(op1), NULL, NULL, 0));
9997+
break;
9998+
case IS_REFERENCE:
9999+
op1 = Z_REFVAL_P(op1);
10000+
ZEND_VM_C_GOTO(try_again);
10001+
default:
10002+
ZVAL_FALSE(result);
10003+
break;
10004+
}
10005+
10006+
ZEND_VM_NEXT_OPCODE();
10007+
}
10008+
998210009
ZEND_VM_HOT_TYPE_SPEC_HANDLER(ZEND_JMP, (OP_JMP_ADDR(op, op->op1) > op), ZEND_JMP_FORWARD, JMP_ADDR, ANY)
998310010
{
998410011
USE_OPLINE

0 commit comments

Comments
 (0)