Skip to content

Commit 02b85d3

Browse files
Zeframkhwilliamson
Zefram
authored andcommitted
chained comparisons
1 parent aa4119b commit 02b85d3

21 files changed

+1930
-1305
lines changed

MANIFEST

+1
Original file line numberDiff line numberDiff line change
@@ -5800,6 +5800,7 @@ t/op/chop.t See if chop works
58005800
t/op/chr.t See if chr works
58015801
t/op/closure.t See if closures work
58025802
t/op/closure_test.pl Extra file for closure.t
5803+
t/op/cmpchain.t See if comparison chaining works
58035804
t/op/concat2.t Tests too complex for concat.t
58045805
t/op/cond.t See if conditional expressions work
58055806
t/op/const-optree.t Tests for sub(){...} becoming constant

embed.fnc

+4
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,10 @@ p |bool |io_close |NN IO* io|NULLOK GV *gv \
10001000
|bool not_implicit|bool warn_on_fail
10011001
: Used in perly.y
10021002
pR |OP* |invert |NULLOK OP* cmd
1003+
pR |OP* |cmpchain_start |Optype type|NULLOK OP* left \
1004+
|NULLOK OP* right
1005+
pR |OP* |cmpchain_extend|Optype type|NN OP* ch|NULLOK OP* right
1006+
pR |OP* |cmpchain_finish|NN OP* ch
10031007
ApR |I32 |is_lvalue_sub
10041008
: Used in cop.h
10051009
XopR |I32 |was_lvalue_sub

embed.h

+3
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,9 @@
12621262
#define ck_tell(a) Perl_ck_tell(aTHX_ a)
12631263
#define ck_trunc(a) Perl_ck_trunc(aTHX_ a)
12641264
#define closest_cop(a,b,c,d) Perl_closest_cop(aTHX_ a,b,c,d)
1265+
#define cmpchain_extend(a,b,c) Perl_cmpchain_extend(aTHX_ a,b,c)
1266+
#define cmpchain_finish(a) Perl_cmpchain_finish(aTHX_ a)
1267+
#define cmpchain_start(a,b,c) Perl_cmpchain_start(aTHX_ a,b,c)
12651268
#define core_prototype(a,b,c,d) Perl_core_prototype(aTHX_ a,b,c,d)
12661269
#define coresub_op(a,b,c) Perl_coresub_op(aTHX_ a,b,c)
12671270
#define create_eval_scope(a,b) Perl_create_eval_scope(aTHX_ a,b)

ext/Opcode/Opcode.pm

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use strict;
66

77
our($VERSION, @ISA, @EXPORT_OK);
88

9-
$VERSION = "1.45";
9+
$VERSION = "1.46";
1010

1111
use Carp;
1212
use Exporter ();
@@ -345,6 +345,8 @@ invert_opset function.
345345
method_super method_redir method_redir_super
346346
-- XXX loops via recursion?
347347
348+
cmpchain_and cmpchain_dup
349+
348350
leaveeval -- needed for Safe to operate, is safe
349351
without entereval
350352

lib/B/Deparse.pm

+58
Original file line numberDiff line numberDiff line change
@@ -3200,6 +3200,64 @@ sub pp_andassign { logassignop(@_, "&&=") }
32003200
sub pp_orassign { logassignop(@_, "||=") }
32013201
sub pp_dorassign { logassignop(@_, "//=") }
32023202

3203+
my %cmpchain_cmpops = (
3204+
eq => ["==", 14],
3205+
i_eq => ["==", 14],
3206+
ne => ["!=", 14],
3207+
i_ne => ["!=", 14],
3208+
seq => ["eq", 14],
3209+
sne => ["ne", 14],
3210+
lt => ["<", 15],
3211+
i_lt => ["<", 15],
3212+
gt => [">", 15],
3213+
i_gt => [">", 15],
3214+
le => ["<=", 15],
3215+
i_le => ["<=", 15],
3216+
ge => [">=", 15],
3217+
i_ge => [">=", 15],
3218+
slt => ["lt", 15],
3219+
sgt => ["gt", 15],
3220+
sle => ["le", 15],
3221+
sge => ["ge", 15],
3222+
);
3223+
sub pp_cmpchain_and {
3224+
my($self, $op, $cx) = @_;
3225+
my($prec, $dep);
3226+
while(1) {
3227+
my($thiscmp, $rightcond);
3228+
if($op->name eq "cmpchain_and") {
3229+
$thiscmp = $op->first;
3230+
$rightcond = $thiscmp->sibling;
3231+
} else {
3232+
$thiscmp = $op;
3233+
}
3234+
my $thiscmptype = $cmpchain_cmpops{$thiscmp->name} // (return "XXX");
3235+
if(defined $prec) {
3236+
$thiscmptype->[1] == $prec or return "XXX";
3237+
$thiscmp->first->name eq "null" &&
3238+
!($thiscmp->first->flags & OPf_KIDS)
3239+
or return "XXX";
3240+
} else {
3241+
$prec = $thiscmptype->[1];
3242+
$dep = $self->deparse($thiscmp->first, $prec);
3243+
}
3244+
$dep .= " ".$thiscmptype->[0]." ";
3245+
my $operand = $thiscmp->last;
3246+
if(defined $rightcond) {
3247+
$operand->name eq "cmpchain_dup" or return "XXX";
3248+
$operand = $operand->first;
3249+
}
3250+
$dep .= $self->deparse($operand, $prec);
3251+
last unless defined $rightcond;
3252+
if($rightcond->name eq "null" && ($rightcond->flags & OPf_KIDS) &&
3253+
$rightcond->first->name eq "cmpchain_and") {
3254+
$rightcond = $rightcond->first;
3255+
}
3256+
$op = $rightcond;
3257+
}
3258+
return $self->maybe_parens($dep, $cx, $prec);
3259+
}
3260+
32033261
sub rv2gv_or_string {
32043262
my($self,$op) = @_;
32053263
if ($op->name eq "gv") { # could be open("open") or open("###")

lib/B/Deparse.t

+27
Original file line numberDiff line numberDiff line change
@@ -3092,3 +3092,30 @@ $r = $h->{'i'}{'j'}{'k'}{'l'}{'m'}{'n'}{'o'}{'p'}{'q'};
30923092
$r = $h->{'i'}{'j'}{'k'}{'l'}{'m'}{'n'}{'o'}{'p'}{'q'}{'r'};
30933093
$r = $h->{'i'}{'j'}{'k'}{'l'}{'m'}{'n'}{'o'}{'p'}{'q'}{'r'}{'s'};
30943094
$r = $h->{'i'}{'j'}{'k'}{'l'}{'m'}{'n'}{'o'}{'p'}{'q'}{'r'}{'s'}{'t'};
3095+
####
3096+
# chained comparison
3097+
my($a, $b, $c, $d, $e, $f, $g);
3098+
$a = $b gt $c >= $d;
3099+
$a = $b < $c <= $d > $e;
3100+
$a = $b == $c != $d;
3101+
$a = $b eq $c ne $d == $e;
3102+
$a = $b << $c < $d << $e <= $f << $g;
3103+
$a = int $b < int $c <= int $d;
3104+
$a = ($b < $c) < ($d < $e) <= ($f < $g);
3105+
$a = ($b == $c) < ($d == $e) <= ($f == $g);
3106+
$a = ($b & $c) < ($d & $e) <= ($f & $g);
3107+
$a = $b << $c == $d << $e != $f << $g;
3108+
$a = int $b == int $c != int $d;
3109+
$a = $b < $c == $d < $e != $f < $g;
3110+
$a = ($b == $c) == ($d == $e) != ($f == $g);
3111+
$a = ($b & $c) == ($d & $e) != ($f & $g);
3112+
$a = $b << ($c < $d <= $e);
3113+
$a = int($c < $d <= $e);
3114+
$a = $b < ($c < $d <= $e);
3115+
$a = $b == $c < $d <= $e;
3116+
$a = $b & $c < $d <= $e;
3117+
$a = $b << ($c == $d != $e);
3118+
$a = int($c == $d != $e);
3119+
$a = $b < ($c == $d != $e);
3120+
$a = $b == ($c == $d != $e);
3121+
$a = $b & $c == $d != $e;

lib/B/Op_private.pm

+2
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ $bits{chr}{0} = $bf[0];
284284
$bits{chroot}{0} = $bf[0];
285285
@{$bits{close}}{3,2,1,0} = ($bf[4], $bf[4], $bf[4], $bf[4]);
286286
$bits{closedir}{0} = $bf[0];
287+
$bits{cmpchain_and}{0} = $bf[0];
288+
$bits{cmpchain_dup}{0} = $bf[0];
287289
$bits{complement}{0} = $bf[0];
288290
@{$bits{concat}}{6,1,0} = ('OPpCONCAT_NESTED', $bf[1], $bf[1]);
289291
$bits{cond_expr}{0} = $bf[0];

op.c

+115
Original file line numberDiff line numberDiff line change
@@ -5499,6 +5499,120 @@ Perl_invert(pTHX_ OP *o)
54995499
return newUNOP(OP_NOT, OPf_SPECIAL, scalar(o));
55005500
}
55015501

5502+
OP *
5503+
Perl_cmpchain_start(pTHX_ Optype type, OP *left, OP *right)
5504+
{
5505+
BINOP *bop;
5506+
OP *op;
5507+
5508+
if (!left)
5509+
left = newOP(OP_NULL, 0);
5510+
if (!right)
5511+
right = newOP(OP_NULL, 0);
5512+
scalar(left);
5513+
scalar(right);
5514+
NewOp(0, bop, 1, BINOP);
5515+
op = (OP*)bop;
5516+
ASSUME((PL_opargs[type] & OA_CLASS_MASK) == OA_BINOP);
5517+
OpTYPE_set(op, type);
5518+
cBINOPx(op)->op_flags = OPf_KIDS;
5519+
cBINOPx(op)->op_private = 2;
5520+
cBINOPx(op)->op_first = left;
5521+
cBINOPx(op)->op_last = right;
5522+
OpMORESIB_set(left, right);
5523+
OpLASTSIB_set(right, op);
5524+
return op;
5525+
}
5526+
5527+
OP *
5528+
Perl_cmpchain_extend(pTHX_ Optype type, OP *ch, OP *right)
5529+
{
5530+
BINOP *bop;
5531+
OP *op;
5532+
5533+
PERL_ARGS_ASSERT_CMPCHAIN_EXTEND;
5534+
if (!right)
5535+
right = newOP(OP_NULL, 0);
5536+
scalar(right);
5537+
NewOp(0, bop, 1, BINOP);
5538+
op = (OP*)bop;
5539+
ASSUME((PL_opargs[type] & OA_CLASS_MASK) == OA_BINOP);
5540+
OpTYPE_set(op, type);
5541+
if (ch->op_type != OP_NULL) {
5542+
UNOP *lch;
5543+
OP *nch, *cleft, *cright;
5544+
NewOp(0, lch, 1, UNOP);
5545+
nch = (OP*)lch;
5546+
OpTYPE_set(nch, OP_NULL);
5547+
nch->op_flags = OPf_KIDS;
5548+
cleft = cBINOPx(ch)->op_first;
5549+
cright = cBINOPx(ch)->op_last;
5550+
cBINOPx(ch)->op_first = NULL;
5551+
cBINOPx(ch)->op_last = NULL;
5552+
cBINOPx(ch)->op_private = 0;
5553+
cBINOPx(ch)->op_flags = 0;
5554+
cUNOPx(nch)->op_first = cright;
5555+
OpMORESIB_set(cright, ch);
5556+
OpMORESIB_set(ch, cleft);
5557+
OpLASTSIB_set(cleft, nch);
5558+
ch = nch;
5559+
}
5560+
OpMORESIB_set(right, op);
5561+
OpMORESIB_set(op, cUNOPx(ch)->op_first);
5562+
cUNOPx(ch)->op_first = right;
5563+
return ch;
5564+
}
5565+
5566+
OP *
5567+
Perl_cmpchain_finish(pTHX_ OP *ch)
5568+
{
5569+
PERL_ARGS_ASSERT_CMPCHAIN_FINISH;
5570+
if (ch->op_type != OP_NULL) {
5571+
Optype cmpoptype = ch->op_type;
5572+
ch = CHECKOP(cmpoptype, ch);
5573+
if(!ch->op_next && ch->op_type == cmpoptype)
5574+
ch = fold_constants(op_integerize(op_std_init(ch)));
5575+
return ch;
5576+
} else {
5577+
OP *condop = NULL;
5578+
OP *rightarg = cUNOPx(ch)->op_first;
5579+
cUNOPx(ch)->op_first = OpSIBLING(rightarg);
5580+
OpLASTSIB_set(rightarg, NULL);
5581+
while (1) {
5582+
OP *cmpop = cUNOPx(ch)->op_first;
5583+
OP *leftarg = OpSIBLING(cmpop);
5584+
Optype cmpoptype = cmpop->op_type;
5585+
OP *nextrightarg;
5586+
bool is_last;
5587+
is_last = !(cUNOPx(ch)->op_first = OpSIBLING(leftarg));
5588+
OpLASTSIB_set(cmpop, NULL);
5589+
OpLASTSIB_set(leftarg, NULL);
5590+
if (is_last) {
5591+
ch->op_flags = 0;
5592+
op_free(ch);
5593+
nextrightarg = NULL;
5594+
} else {
5595+
nextrightarg = newUNOP(OP_CMPCHAIN_DUP, 0, leftarg);
5596+
leftarg = newOP(OP_NULL, 0);
5597+
}
5598+
cBINOPx(cmpop)->op_first = leftarg;
5599+
cBINOPx(cmpop)->op_last = rightarg;
5600+
OpMORESIB_set(leftarg, rightarg);
5601+
OpLASTSIB_set(rightarg, cmpop);
5602+
cmpop->op_flags = OPf_KIDS;
5603+
cmpop->op_private = 2;
5604+
cmpop = CHECKOP(cmpoptype, cmpop);
5605+
if(!cmpop->op_next && cmpop->op_type == cmpoptype)
5606+
cmpop = fold_constants(op_integerize(op_std_init(cmpop)));
5607+
condop = condop ? newLOGOP(OP_CMPCHAIN_AND, 0, cmpop, condop) :
5608+
cmpop;
5609+
if (!nextrightarg)
5610+
return condop;
5611+
rightarg = nextrightarg;
5612+
}
5613+
}
5614+
}
5615+
55025616
/*
55035617
=for apidoc op_scope
55045618

@@ -17432,6 +17546,7 @@ Perl_rpeep(pTHX_ OP *o)
1743217546
case OP_AND:
1743317547
case OP_OR:
1743417548
case OP_DOR:
17549+
case OP_CMPCHAIN_AND:
1743517550
while (cLOGOP->op_other->op_type == OP_NULL)
1743617551
cLOGOP->op_other = cLOGOP->op_other->op_next;
1743717552
while (o->op_next && ( o->op_type == o->op_next->op_type

opcode.h

+15-1
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,8 @@ EXTCONST char* const PL_op_name[] = {
544544
"lvavref",
545545
"anonconst",
546546
"isa",
547+
"cmpchain_and",
548+
"cmpchain_dup",
547549
"freed",
548550
};
549551
#endif
@@ -950,6 +952,8 @@ EXTCONST char* const PL_op_desc[] = {
950952
"lvalue array reference",
951953
"anonymous constant",
952954
"derived class test",
955+
"comparison chaining",
956+
"comparand shuffling",
953957
"freed op",
954958
};
955959
#endif
@@ -1368,6 +1372,8 @@ EXT Perl_ppaddr_t PL_ppaddr[] /* or perlvars.h */
13681372
Perl_pp_lvavref,
13691373
Perl_pp_anonconst,
13701374
Perl_pp_isa,
1375+
Perl_pp_cmpchain_and,
1376+
Perl_pp_cmpchain_dup,
13711377
}
13721378
#endif
13731379
#ifdef PERL_PPADDR_INITED
@@ -1782,6 +1788,8 @@ EXT Perl_check_t PL_check[] /* or perlvars.h */
17821788
Perl_ck_null, /* lvavref */
17831789
Perl_ck_null, /* anonconst */
17841790
Perl_ck_isa, /* isa */
1791+
Perl_ck_null, /* cmpchain_and */
1792+
Perl_ck_null, /* cmpchain_dup */
17851793
}
17861794
#endif
17871795
#ifdef PERL_CHECK_INITED
@@ -2192,6 +2200,8 @@ EXTCONST U32 PL_opargs[] = {
21922200
0x00000b40, /* lvavref */
21932201
0x00000144, /* anonconst */
21942202
0x00000204, /* isa */
2203+
0x00000300, /* cmpchain_and */
2204+
0x00000100, /* cmpchain_dup */
21952205
};
21962206
#endif
21972207

@@ -2861,6 +2871,8 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = {
28612871
234, /* lvavref */
28622872
0, /* anonconst */
28632873
12, /* isa */
2874+
0, /* cmpchain_and */
2875+
0, /* cmpchain_dup */
28642876

28652877
};
28662878

@@ -2879,7 +2891,7 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = {
28792891
*/
28802892

28812893
EXTCONST U16 PL_op_private_bitdefs[] = {
2882-
0x0003, /* scalar, prototype, refgen, srefgen, readline, regcmaybe, regcreset, regcomp, substcont, chop, schop, defined, undef, study, preinc, i_preinc, predec, i_predec, postinc, i_postinc, postdec, i_postdec, negate, i_negate, not, complement, ucfirst, lcfirst, uc, lc, quotemeta, aeach, avalues, each, pop, shift, grepstart, mapstart, mapwhile, range, and, or, dor, andassign, orassign, dorassign, argcheck, argdefelem, method, method_named, method_super, method_redir, method_redir_super, entergiven, leavegiven, enterwhen, leavewhen, untie, tied, dbmclose, getsockname, getpeername, lstat, stat, readlink, readdir, telldir, rewinddir, closedir, localtime, alarm, require, dofile, entertry, ghbyname, gnbyname, gpbyname, shostent, snetent, sprotoent, sservent, gpwnam, gpwuid, ggrnam, ggrgid, lock, once, fc, anonconst */
2894+
0x0003, /* scalar, prototype, refgen, srefgen, readline, regcmaybe, regcreset, regcomp, substcont, chop, schop, defined, undef, study, preinc, i_preinc, predec, i_predec, postinc, i_postinc, postdec, i_postdec, negate, i_negate, not, complement, ucfirst, lcfirst, uc, lc, quotemeta, aeach, avalues, each, pop, shift, grepstart, mapstart, mapwhile, range, and, or, dor, andassign, orassign, dorassign, argcheck, argdefelem, method, method_named, method_super, method_redir, method_redir_super, entergiven, leavegiven, enterwhen, leavewhen, untie, tied, dbmclose, getsockname, getpeername, lstat, stat, readlink, readdir, telldir, rewinddir, closedir, localtime, alarm, require, dofile, entertry, ghbyname, gnbyname, gpbyname, shostent, snetent, sprotoent, sservent, gpwnam, gpwuid, ggrnam, ggrgid, lock, once, fc, anonconst, cmpchain_and, cmpchain_dup */
28832895
0x2fdc, 0x41b9, /* pushmark */
28842896
0x00bd, /* wantarray, runcv */
28852897
0x0438, 0x1a50, 0x426c, 0x3d28, 0x3505, /* const */
@@ -3355,6 +3367,8 @@ EXTCONST U8 PL_op_private_valid[] = {
33553367
/* LVAVREF */ (OPpARG1_MASK|OPpPAD_STATE|OPpLVAL_INTRO),
33563368
/* ANONCONST */ (OPpARG1_MASK),
33573369
/* ISA */ (OPpARG2_MASK),
3370+
/* CMPCHAIN_AND */ (OPpARG1_MASK),
3371+
/* CMPCHAIN_DUP */ (OPpARG1_MASK),
33583372

33593373
};
33603374

opnames.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -412,10 +412,12 @@ typedef enum opcode {
412412
OP_LVAVREF = 395,
413413
OP_ANONCONST = 396,
414414
OP_ISA = 397,
415+
OP_CMPCHAIN_AND = 398,
416+
OP_CMPCHAIN_DUP = 399,
415417
OP_max
416418
} opcode;
417419

418-
#define MAXO 398
420+
#define MAXO 400
419421
#define OP_FREED MAXO
420422

421423
/* the OP_IS_* macros are optimized to a simple range check because

0 commit comments

Comments
 (0)