| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* You may distribute under the terms of either the GNU General Public License |
|
2
|
|
|
|
|
|
|
* or the Artistic License (the same terms as Perl itself) |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* (C) Paul Evans, 2021-2023 -- leonerd@leonerd.org.uk |
|
5
|
|
|
|
|
|
|
*/ |
|
6
|
|
|
|
|
|
|
#define PERL_NO_GET_CONTEXT |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
#include "EXTERN.h" |
|
9
|
|
|
|
|
|
|
#include "perl.h" |
|
10
|
|
|
|
|
|
|
#include "XSUB.h" |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
#include "XSParseKeyword.h" |
|
13
|
|
|
|
|
|
|
#include "XSParseInfix.h" |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
#define HAVE_PERL_VERSION(R, V, S) \ |
|
16
|
|
|
|
|
|
|
(PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S)))))) |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
#include "hax/perl-backcompat.c.inc" |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,32,0) |
|
21
|
|
|
|
|
|
|
# define HAVE_OP_ISA |
|
22
|
|
|
|
|
|
|
#endif |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,18,0) |
|
25
|
|
|
|
|
|
|
# define HAVE_BOOL_SvIV_please_nomg |
|
26
|
|
|
|
|
|
|
#endif |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,35,9) |
|
29
|
|
|
|
|
|
|
# define HAVE_SV_NUMEQ_FLAGS |
|
30
|
|
|
|
|
|
|
#endif |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
#include "dispatchop.h" |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#ifndef HAVE_SV_NUMEQ_FLAGS |
|
35
|
|
|
|
|
|
|
/* We'd like to call Perl_do_ncmp, except that isn't an exported API function |
|
36
|
|
|
|
|
|
|
* Here's a near-copy of it for num-equality testing purposes */ |
|
37
|
|
|
|
|
|
|
#define do_numeq(left, right) S_do_numeq(aTHX_ left, right) |
|
38
|
5
|
|
|
|
|
|
static bool S_do_numeq(pTHX_ SV *left, SV *right) |
|
39
|
|
|
|
|
|
|
{ |
|
40
|
|
|
|
|
|
|
#ifndef HAVE_BOOL_SvIV_please_nomg |
|
41
|
|
|
|
|
|
|
/* Before perl 5.18, SvIV_please_nomg() was void-returning */ |
|
42
|
|
|
|
|
|
|
SvIV_please_nomg(left); |
|
43
|
|
|
|
|
|
|
SvIV_please_nomg(right); |
|
44
|
|
|
|
|
|
|
#endif |
|
45
|
|
|
|
|
|
|
|
|
46
|
10
|
100
|
|
|
|
|
if( |
|
47
|
|
|
|
|
|
|
#ifdef HAVE_BOOL_SvIV_please_nomg |
|
48
|
9
|
50
|
|
|
|
|
SvIV_please_nomg(right) && SvIV_please_nomg(left) |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
#else |
|
50
|
|
|
|
|
|
|
SvIOK(left) && SvIOK(right) |
|
51
|
|
|
|
|
|
|
#endif |
|
52
|
|
|
|
|
|
|
) { |
|
53
|
|
|
|
|
|
|
/* Compare as integers */ |
|
54
|
3
|
100
|
|
|
|
|
switch((SvUOK(left) ? 1 : 0) | (SvUOK(right) ? 2 : 0)) { |
|
55
|
|
|
|
|
|
|
case 0: /* IV == IV */ |
|
56
|
0
|
|
|
|
|
|
return SvIVX(left) == SvIVX(right); |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
case 1: /* UV == IV */ |
|
59
|
|
|
|
|
|
|
{ |
|
60
|
1
|
|
|
|
|
|
const IV riv = SvUVX(right); |
|
61
|
1
|
50
|
|
|
|
|
if(riv < 0) |
|
62
|
|
|
|
|
|
|
return 0; |
|
63
|
1
|
|
|
|
|
|
return (SvUVX(left) == riv); |
|
64
|
|
|
|
|
|
|
} |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
case 2: /* IV == UV */ |
|
67
|
|
|
|
|
|
|
{ |
|
68
|
0
|
|
|
|
|
|
const IV liv = SvUVX(left); |
|
69
|
0
|
0
|
|
|
|
|
if(liv < 0) |
|
70
|
|
|
|
|
|
|
return 0; |
|
71
|
0
|
|
|
|
|
|
return (liv == SvUVX(right)); |
|
72
|
|
|
|
|
|
|
} |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
case 3: /* UV == UV */ |
|
75
|
2
|
|
|
|
|
|
return SvUVX(left) == SvUVX(right); |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
else { |
|
79
|
|
|
|
|
|
|
/* Compare NVs */ |
|
80
|
2
|
50
|
|
|
|
|
NV const rnv = SvNV_nomg(right); |
|
81
|
2
|
50
|
|
|
|
|
NV const lnv = SvNV_nomg(left); |
|
82
|
|
|
|
|
|
|
|
|
83
|
2
|
|
|
|
|
|
return lnv == rnv; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
0
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
#endif |
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
#define newPADSVOP(type, flags, padix) MY_newPADSVOP(aTHX_ type, flags, padix) |
|
89
|
|
|
|
|
|
|
static OP *MY_newPADSVOP(pTHX_ I32 type, I32 flags, PADOFFSET padix) |
|
90
|
|
|
|
|
|
|
{ |
|
91
|
86
|
|
|
|
|
|
OP *op = newOP(type, flags); |
|
92
|
86
|
|
|
|
|
|
op->op_targ = padix; |
|
93
|
|
|
|
|
|
|
return op; |
|
94
|
|
|
|
|
|
|
} |
|
95
|
|
|
|
|
|
|
|
|
96
|
4
|
|
|
|
|
|
static OP *pp_dispatch_numeq(pTHX) |
|
97
|
|
|
|
|
|
|
{ |
|
98
|
4
|
|
|
|
|
|
dDISPATCH; |
|
99
|
4
|
|
|
|
|
|
dTARGET; |
|
100
|
|
|
|
|
|
|
int idx; |
|
101
|
|
|
|
|
|
|
|
|
102
|
4
|
50
|
|
|
|
|
bool has_magic = SvAMAGIC(TARG); |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
|
104
|
8
|
50
|
|
|
|
|
for(idx = 0; idx < n_cases; idx++) { |
|
105
|
8
|
|
|
|
|
|
SV *val = values[idx]; |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
SV *ret; |
|
108
|
8
|
50
|
|
|
|
|
if(has_magic && |
|
|
|
0
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
(ret = amagic_call(TARG, val, eq_amg, 0))) { |
|
110
|
0
|
0
|
|
|
|
|
if(SvTRUE(ret)) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
111
|
0
|
|
|
|
|
|
return dispatch[idx]; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
|
|
|
|
|
|
#ifdef HAVE_SV_NUMEQ_FLAGS |
|
114
|
|
|
|
|
|
|
else if(sv_numeq_flags(TARG, val, SV_SKIP_OVERLOAD)) |
|
115
|
|
|
|
|
|
|
#else |
|
116
|
|
|
|
|
|
|
/* stolen from core's pp_hot.c / pp_eq() */ |
|
117
|
8
|
100
|
|
|
|
|
else if((SvIOK_notUV(TARG) && SvIOK_notUV(val)) ? |
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
118
|
3
|
|
|
|
|
|
SvIVX(TARG) == SvIVX(val) : (do_numeq(TARG, val))) |
|
119
|
|
|
|
|
|
|
#endif |
|
120
|
4
|
|
|
|
|
|
return dispatch[idx]; |
|
121
|
|
|
|
|
|
|
} |
|
122
|
|
|
|
|
|
|
|
|
123
|
0
|
|
|
|
|
|
return cDISPATCHOP->op_other; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
|
|
126
|
1000012
|
|
|
|
|
|
static OP *pp_dispatch_streq(pTHX) |
|
127
|
|
|
|
|
|
|
{ |
|
128
|
1000012
|
|
|
|
|
|
dDISPATCH; |
|
129
|
1000012
|
|
|
|
|
|
dTARGET; |
|
130
|
|
|
|
|
|
|
int idx; |
|
131
|
|
|
|
|
|
|
|
|
132
|
1000012
|
100
|
|
|
|
|
bool has_magic = SvAMAGIC(TARG); |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
|
|
134
|
10000059
|
100
|
|
|
|
|
for(idx = 0; idx < n_cases; idx++) { |
|
135
|
9000057
|
|
|
|
|
|
SV *val = values[idx]; |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
SV *ret; |
|
138
|
9000057
|
100
|
|
|
|
|
if(has_magic && |
|
|
|
50
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
(ret = amagic_call(TARG, val, seq_amg, 0))) { |
|
140
|
3
|
50
|
|
|
|
|
if(SvTRUE(ret)) |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
141
|
1
|
|
|
|
|
|
return dispatch[idx]; |
|
142
|
|
|
|
|
|
|
} |
|
143
|
9000054
|
100
|
|
|
|
|
else if(sv_eq(TARG, val)) |
|
144
|
9
|
|
|
|
|
|
return dispatch[idx]; |
|
145
|
|
|
|
|
|
|
} |
|
146
|
|
|
|
|
|
|
|
|
147
|
1000002
|
|
|
|
|
|
return cDISPATCHOP->op_other; |
|
148
|
|
|
|
|
|
|
} |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
#ifdef HAVE_OP_ISA |
|
151
|
|
|
|
|
|
|
static OP *pp_dispatch_isa(pTHX) |
|
152
|
|
|
|
|
|
|
{ |
|
153
|
|
|
|
|
|
|
dDISPATCH; |
|
154
|
|
|
|
|
|
|
dTARGET; |
|
155
|
|
|
|
|
|
|
int idx; |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
for(idx = 0; idx < n_cases; idx++) |
|
158
|
|
|
|
|
|
|
if(sv_isa_sv(TARG, values[idx])) |
|
159
|
|
|
|
|
|
|
return dispatch[idx]; |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
return cDISPATCHOP->op_other; |
|
162
|
|
|
|
|
|
|
} |
|
163
|
|
|
|
|
|
|
#endif |
|
164
|
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
struct MatchCaseBlock { |
|
166
|
|
|
|
|
|
|
int n_cases; |
|
167
|
|
|
|
|
|
|
struct MatchCase { |
|
168
|
|
|
|
|
|
|
bool is_if; |
|
169
|
|
|
|
|
|
|
OP *expr; |
|
170
|
|
|
|
|
|
|
} *cases; |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
OP *op; |
|
173
|
|
|
|
|
|
|
}; |
|
174
|
|
|
|
|
|
|
|
|
175
|
55
|
|
|
|
|
|
static OP *build_cases_nondispatch(pTHX_ XSParseInfixInfo *matchinfo, PADOFFSET padix, struct MatchCaseBlock *block, OP *elseop) |
|
176
|
|
|
|
|
|
|
{ |
|
177
|
55
|
|
|
|
|
|
size_t n_cases = block->n_cases; |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
assert(n_cases); |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
OP *testop = NULL; |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
U32 i; |
|
184
|
112
|
100
|
|
|
|
|
for(i = 0; i < n_cases; i++) { |
|
185
|
57
|
|
|
|
|
|
bool is_if = block->cases[i].is_if; |
|
186
|
57
|
|
|
|
|
|
OP *caseop = block->cases[i].expr; |
|
187
|
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
OP *thistestop; |
|
189
|
|
|
|
|
|
|
|
|
190
|
57
|
100
|
|
|
|
|
if(is_if) |
|
191
|
|
|
|
|
|
|
thistestop = caseop; |
|
192
|
56
|
|
|
|
|
|
else switch(matchinfo->opcode) { |
|
193
|
|
|
|
|
|
|
#ifdef HAVE_OP_ISA |
|
194
|
|
|
|
|
|
|
case OP_ISA: |
|
195
|
|
|
|
|
|
|
#endif |
|
196
|
|
|
|
|
|
|
case OP_SEQ: |
|
197
|
|
|
|
|
|
|
case OP_EQ: |
|
198
|
45
|
|
|
|
|
|
thistestop = newBINOP(matchinfo->opcode, 0, |
|
199
|
|
|
|
|
|
|
newPADSVOP(OP_PADSV, 0, padix), caseop); |
|
200
|
45
|
|
|
|
|
|
break; |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
case OP_MATCH: |
|
203
|
4
|
50
|
|
|
|
|
if(caseop->op_type != OP_MATCH || cPMOPx(caseop)->op_first) |
|
|
|
50
|
|
|
|
|
|
|
204
|
0
|
|
|
|
|
|
croak("Expected a regexp match"); |
|
205
|
|
|
|
|
|
|
thistestop = caseop; |
|
206
|
|
|
|
|
|
|
#if HAVE_PERL_VERSION(5,22,0) |
|
207
|
4
|
|
|
|
|
|
thistestop->op_targ = padix; |
|
208
|
|
|
|
|
|
|
#else |
|
209
|
|
|
|
|
|
|
cPMOPx(thistestop)->op_first = newPADSVOP(OP_PADSV, 0, padix); |
|
210
|
|
|
|
|
|
|
thistestop->op_flags |= OPf_KIDS|OPf_STACKED; |
|
211
|
|
|
|
|
|
|
#endif |
|
212
|
4
|
|
|
|
|
|
break; |
|
213
|
|
|
|
|
|
|
case OP_CUSTOM: |
|
214
|
|
|
|
|
|
|
thistestop = xs_parse_infix_new_op(matchinfo, 0, |
|
215
|
|
|
|
|
|
|
newPADSVOP(OP_PADSV, 0, padix), caseop); |
|
216
|
7
|
|
|
|
|
|
break; |
|
217
|
|
|
|
|
|
|
} |
|
218
|
|
|
|
|
|
|
|
|
219
|
57
|
100
|
|
|
|
|
if(testop) |
|
220
|
2
|
|
|
|
|
|
testop = newLOGOP(OP_OR, 0, testop, thistestop); |
|
221
|
|
|
|
|
|
|
else |
|
222
|
|
|
|
|
|
|
testop = thistestop; |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
assert(testop); |
|
226
|
|
|
|
|
|
|
|
|
227
|
55
|
100
|
|
|
|
|
if(elseop) |
|
228
|
36
|
|
|
|
|
|
return newCONDOP(0, testop, block->op, elseop); |
|
229
|
|
|
|
|
|
|
else |
|
230
|
19
|
|
|
|
|
|
return newLOGOP(OP_AND, 0, testop, block->op); |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
8
|
|
|
|
|
|
static OP *build_cases_dispatch(pTHX_ OPCODE matchtype, PADOFFSET padix, size_t n_cases, struct MatchCaseBlock *blocks, OP *elseop) |
|
234
|
|
|
|
|
|
|
{ |
|
235
|
|
|
|
|
|
|
assert(n_cases); |
|
236
|
|
|
|
|
|
|
assert(matchtype != OP_MATCH); |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
U32 blocki; |
|
239
|
|
|
|
|
|
|
|
|
240
|
8
|
|
|
|
|
|
ENTER; |
|
241
|
|
|
|
|
|
|
|
|
242
|
8
|
|
|
|
|
|
SV *valuessv = newSV(n_cases * sizeof(SV *)); |
|
243
|
8
|
|
|
|
|
|
SV *dispatchsv = newSV(n_cases * sizeof(OP *)); |
|
244
|
8
|
|
|
|
|
|
SAVEFREESV(valuessv); |
|
245
|
8
|
|
|
|
|
|
SAVEFREESV(dispatchsv); |
|
246
|
|
|
|
|
|
|
|
|
247
|
8
|
|
|
|
|
|
SV **values = (SV **)SvPVX(valuessv); |
|
248
|
8
|
|
|
|
|
|
OP **dispatch = (OP **)SvPVX(dispatchsv); |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
DISPATCHOP *o = alloc_DISPATCHOP(); |
|
251
|
8
|
|
|
|
|
|
o->op_type = OP_CUSTOM; |
|
252
|
8
|
|
|
|
|
|
o->op_targ = padix; |
|
253
|
|
|
|
|
|
|
|
|
254
|
8
|
|
|
|
|
|
switch(matchtype) { |
|
255
|
|
|
|
|
|
|
#ifdef HAVE_OP_ISA |
|
256
|
|
|
|
|
|
|
case OP_ISA: o->op_ppaddr = &pp_dispatch_isa; break; |
|
257
|
|
|
|
|
|
|
#endif |
|
258
|
3
|
|
|
|
|
|
case OP_SEQ: o->op_ppaddr = &pp_dispatch_streq; break; |
|
259
|
5
|
|
|
|
|
|
case OP_EQ: o->op_ppaddr = &pp_dispatch_numeq; break; |
|
260
|
|
|
|
|
|
|
} |
|
261
|
|
|
|
|
|
|
|
|
262
|
8
|
|
|
|
|
|
o->op_first = NULL; |
|
263
|
|
|
|
|
|
|
|
|
264
|
8
|
|
|
|
|
|
o->n_cases = n_cases; |
|
265
|
8
|
|
|
|
|
|
o->values = values; |
|
266
|
8
|
|
|
|
|
|
o->dispatch = dispatch; |
|
267
|
|
|
|
|
|
|
|
|
268
|
8
|
|
|
|
|
|
OP *retop = newUNOP(OP_NULL, 0, (OP *)o); |
|
269
|
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
U32 idx = 0; |
|
271
|
|
|
|
|
|
|
blocki = 0; |
|
272
|
38
|
100
|
|
|
|
|
while(n_cases) { |
|
273
|
30
|
|
|
|
|
|
struct MatchCaseBlock *block = &blocks[blocki]; |
|
274
|
|
|
|
|
|
|
|
|
275
|
30
|
|
|
|
|
|
U32 this_n_cases = block->n_cases; |
|
276
|
|
|
|
|
|
|
|
|
277
|
30
|
|
|
|
|
|
OP *blockop = block->op; |
|
278
|
30
|
50
|
|
|
|
|
OP *blockstart = LINKLIST(blockop); |
|
279
|
30
|
|
|
|
|
|
blockop->op_next = retop; |
|
280
|
|
|
|
|
|
|
|
|
281
|
30
|
|
|
|
|
|
n_cases -= this_n_cases; |
|
282
|
|
|
|
|
|
|
|
|
283
|
60
|
100
|
|
|
|
|
for(U32 casei = 0; casei < this_n_cases; casei++) { |
|
284
|
30
|
|
|
|
|
|
bool is_if = block->cases[casei].is_if; |
|
285
|
30
|
|
|
|
|
|
OP *caseop = block->cases[casei].expr; |
|
286
|
|
|
|
|
|
|
|
|
287
|
30
|
50
|
|
|
|
|
if(is_if) |
|
288
|
0
|
|
|
|
|
|
croak("TODO: case if dispatch"); |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
assert(caseop->op_type == OP_CONST); |
|
291
|
60
|
|
|
|
|
|
values[idx] = SvREFCNT_inc(cSVOPx(caseop)->op_sv); |
|
292
|
30
|
|
|
|
|
|
op_free(caseop); |
|
293
|
|
|
|
|
|
|
|
|
294
|
30
|
|
|
|
|
|
dispatch[idx] = blockstart; |
|
295
|
|
|
|
|
|
|
|
|
296
|
30
|
|
|
|
|
|
idx++; |
|
297
|
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
/* TODO: link chain of siblings */ |
|
300
|
|
|
|
|
|
|
|
|
301
|
30
|
|
|
|
|
|
blocki++; |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
8
|
100
|
|
|
|
|
if(elseop) { |
|
305
|
7
|
100
|
|
|
|
|
o->op_other = LINKLIST(elseop); |
|
306
|
7
|
|
|
|
|
|
elseop->op_next = retop; |
|
307
|
|
|
|
|
|
|
/* TODO: sibling linkage */ |
|
308
|
|
|
|
|
|
|
} |
|
309
|
|
|
|
|
|
|
else { |
|
310
|
1
|
|
|
|
|
|
o->op_other = retop; |
|
311
|
|
|
|
|
|
|
} |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
/* Steal the SV buffers */ |
|
314
|
8
|
|
|
|
|
|
SvPVX(valuessv) = NULL; SvLEN(valuessv) = 0; |
|
315
|
8
|
|
|
|
|
|
SvPVX(dispatchsv) = NULL; SvLEN(dispatchsv) = 0; |
|
316
|
|
|
|
|
|
|
|
|
317
|
8
|
|
|
|
|
|
LEAVE; |
|
318
|
|
|
|
|
|
|
|
|
319
|
8
|
|
|
|
|
|
return retop; |
|
320
|
|
|
|
|
|
|
} |
|
321
|
|
|
|
|
|
|
|
|
322
|
34
|
|
|
|
|
|
static int build_match(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata) |
|
323
|
|
|
|
|
|
|
{ |
|
324
|
|
|
|
|
|
|
/* args: |
|
325
|
|
|
|
|
|
|
* [0]: topic expression |
|
326
|
|
|
|
|
|
|
* [1]: match type |
|
327
|
|
|
|
|
|
|
* [2]: count of blocks |
|
328
|
|
|
|
|
|
|
* [3]: count of case exprs = $N |
|
329
|
|
|
|
|
|
|
* [4,5...]: $N * [if, case expr]s |
|
330
|
|
|
|
|
|
|
* []: block |
|
331
|
|
|
|
|
|
|
* [LAST]: default case if present |
|
332
|
|
|
|
|
|
|
*/ |
|
333
|
|
|
|
|
|
|
U32 argi = 0; |
|
334
|
|
|
|
|
|
|
|
|
335
|
34
|
|
|
|
|
|
OP *topic = args[argi++]->op; |
|
336
|
34
|
|
|
|
|
|
XSParseInfixInfo *matchinfo = args[argi++]->infix; |
|
337
|
34
|
|
|
|
|
|
int n_blocks = args[argi++]->i; |
|
338
|
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
/* Extract the raw args into a better data structure we can work with */ |
|
340
|
|
|
|
|
|
|
struct MatchCaseBlock *blocks; |
|
341
|
|
|
|
|
|
|
|
|
342
|
34
|
50
|
|
|
|
|
Newx(blocks, n_blocks, struct MatchCaseBlock); |
|
343
|
34
|
|
|
|
|
|
SAVEFREEPV(blocks); |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
int blocki; |
|
346
|
119
|
100
|
|
|
|
|
for(blocki = 0; blocki < n_blocks; blocki++) { |
|
347
|
85
|
|
|
|
|
|
struct MatchCaseBlock *block = &blocks[blocki]; |
|
348
|
|
|
|
|
|
|
|
|
349
|
85
|
|
|
|
|
|
int n_cases = args[argi++]->i; |
|
350
|
|
|
|
|
|
|
|
|
351
|
85
|
|
|
|
|
|
block->n_cases = n_cases; |
|
352
|
|
|
|
|
|
|
|
|
353
|
85
|
50
|
|
|
|
|
Newx(block->cases, n_cases, struct MatchCase); |
|
354
|
85
|
|
|
|
|
|
SAVEFREEPV(block->cases); |
|
355
|
|
|
|
|
|
|
|
|
356
|
172
|
100
|
|
|
|
|
for(int i = 0; i < n_cases; i++) { |
|
357
|
87
|
|
|
|
|
|
block->cases[i].is_if = args[argi++]->i; |
|
358
|
87
|
|
|
|
|
|
block->cases[i].expr = args[argi++]->op; |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
|
|
361
|
85
|
|
|
|
|
|
block->op = args[argi++]->op; |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
|
|
364
|
34
|
|
|
|
|
|
bool has_default = args[argi]->i; |
|
365
|
|
|
|
|
|
|
OP *o = NULL; |
|
366
|
34
|
100
|
|
|
|
|
if(has_default) |
|
367
|
14
|
|
|
|
|
|
o = args[argi + 1]->op; |
|
368
|
|
|
|
|
|
|
|
|
369
|
34
|
|
|
|
|
|
bool use_dispatch = hv_fetchs(GvHV(PL_hintgv), "Syntax::Keyword::Match/experimental(dispatch)", 0); |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
/* The name is totally meaningless and never used, but if we don't set a |
|
372
|
|
|
|
|
|
|
* name and instead use pad_alloc(SVs_PADTMP) then the peephole optimiser |
|
373
|
|
|
|
|
|
|
* for aassign will crash |
|
374
|
|
|
|
|
|
|
*/ |
|
375
|
34
|
|
|
|
|
|
PADOFFSET padix = pad_add_name_pvs("$(Syntax::Keyword::Match/topic)", 0, NULL, NULL); |
|
376
|
34
|
|
|
|
|
|
intro_my(); |
|
377
|
|
|
|
|
|
|
|
|
378
|
34
|
|
|
|
|
|
OP *startop = newBINOP(OP_SASSIGN, 0, |
|
379
|
|
|
|
|
|
|
topic, newPADSVOP(OP_PADSV, OPf_MOD|OPf_REF|(OPpLVAL_INTRO << 8), padix)); |
|
380
|
34
|
|
|
|
|
|
PL_hints |= HINT_BLOCK_SCOPE; /* ensures that op_scope() creates a full ENTER+LEAVE pair */ |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
int n_dispatch = 0; |
|
383
|
|
|
|
|
|
|
|
|
384
|
34
|
|
|
|
|
|
blocki = n_blocks-1; |
|
385
|
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
/* Roll up the blocks backwards, from end to beginning */ |
|
387
|
119
|
100
|
|
|
|
|
while(blocki >= 0) { |
|
388
|
85
|
|
|
|
|
|
struct MatchCaseBlock *block = &blocks[blocki--]; |
|
389
|
|
|
|
|
|
|
|
|
390
|
85
|
|
|
|
|
|
int n_cases = block->n_cases; |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
/* perl expects a strict optree, where each block appears exactly once. |
|
393
|
|
|
|
|
|
|
* We can't reüse the block between dispatch and non-dispatch ops, so |
|
394
|
|
|
|
|
|
|
* we'll have to decide which strategy to use here |
|
395
|
|
|
|
|
|
|
*/ |
|
396
|
|
|
|
|
|
|
bool this_block_dispatch = use_dispatch; |
|
397
|
|
|
|
|
|
|
|
|
398
|
172
|
100
|
|
|
|
|
for(U32 casei = 0; casei < n_cases; casei++) { |
|
399
|
87
|
100
|
|
|
|
|
if(block->cases[casei].is_if) { |
|
400
|
|
|
|
|
|
|
this_block_dispatch = false; |
|
401
|
1
|
|
|
|
|
|
continue; |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
/* TODO: forbid the , operator in the case label */ |
|
405
|
86
|
|
|
|
|
|
OP *caseop = block->cases[casei].expr; |
|
406
|
|
|
|
|
|
|
|
|
407
|
86
|
|
|
|
|
|
switch(matchinfo->opcode) { |
|
408
|
|
|
|
|
|
|
#ifdef HAVE_OP_ISA |
|
409
|
|
|
|
|
|
|
case OP_ISA: |
|
410
|
|
|
|
|
|
|
/* bareword class names are permitted */ |
|
411
|
|
|
|
|
|
|
if(caseop->op_type == OP_CONST && caseop->op_private & OPpCONST_BARE) |
|
412
|
|
|
|
|
|
|
caseop->op_private &= ~(OPpCONST_BARE|OPpCONST_STRICT); |
|
413
|
|
|
|
|
|
|
/* FALLTHROUGH */ |
|
414
|
|
|
|
|
|
|
#endif |
|
415
|
|
|
|
|
|
|
case OP_SEQ: |
|
416
|
|
|
|
|
|
|
case OP_EQ: |
|
417
|
75
|
100
|
|
|
|
|
if(use_dispatch && caseop->op_type == OP_CONST) |
|
|
|
100
|
|
|
|
|
|
|
418
|
30
|
|
|
|
|
|
continue; |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
/* FALLTHROUGH */ |
|
421
|
|
|
|
|
|
|
case OP_MATCH: |
|
422
|
|
|
|
|
|
|
case OP_CUSTOM: |
|
423
|
|
|
|
|
|
|
this_block_dispatch = false; |
|
424
|
56
|
|
|
|
|
|
break; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
|
|
428
|
85
|
100
|
|
|
|
|
if(this_block_dispatch) { |
|
429
|
30
|
|
|
|
|
|
n_dispatch += n_cases; |
|
430
|
30
|
|
|
|
|
|
continue; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
|
|
433
|
55
|
100
|
|
|
|
|
if(n_dispatch) { |
|
434
|
1
|
|
|
|
|
|
o = build_cases_dispatch(aTHX_ matchinfo->opcode, padix, |
|
435
|
|
|
|
|
|
|
n_dispatch, block + 1, o); |
|
436
|
|
|
|
|
|
|
n_dispatch = 0; |
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
|
|
439
|
85
|
|
|
|
|
|
o = build_cases_nondispatch(aTHX_ matchinfo, padix, block, o); |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
|
|
442
|
34
|
100
|
|
|
|
|
if(n_dispatch) |
|
443
|
7
|
|
|
|
|
|
o = build_cases_dispatch(aTHX_ matchinfo->opcode, padix, |
|
444
|
|
|
|
|
|
|
n_dispatch, blocks, o); |
|
445
|
|
|
|
|
|
|
|
|
446
|
34
|
|
|
|
|
|
*out = newLISTOP(OP_LINESEQ, 0, startop, o); |
|
447
|
|
|
|
|
|
|
|
|
448
|
34
|
|
|
|
|
|
return KEYWORD_PLUGIN_STMT; |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
static const struct XSParseKeywordHooks hooks_match = { |
|
452
|
|
|
|
|
|
|
.flags = XPK_FLAG_BLOCKSCOPE, |
|
453
|
|
|
|
|
|
|
.permit_hintkey = "Syntax::Keyword::Match/match", |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
.pieces = (const struct XSParseKeywordPieceType []){ |
|
456
|
|
|
|
|
|
|
XPK_PARENS( /* ( EXPR : OP ) */ |
|
457
|
|
|
|
|
|
|
XPK_TERMEXPR_SCALARCTX, |
|
458
|
|
|
|
|
|
|
XPK_COLON, |
|
459
|
|
|
|
|
|
|
XPK_INFIX_MATCH_NOSMART |
|
460
|
|
|
|
|
|
|
), |
|
461
|
|
|
|
|
|
|
XPK_INTRO_MY, |
|
462
|
|
|
|
|
|
|
XPK_BRACES( /* { blocks... } */ |
|
463
|
|
|
|
|
|
|
XPK_REPEATED( /* case (EXPR) {BLOCK} */ |
|
464
|
|
|
|
|
|
|
XPK_COMMALIST( |
|
465
|
|
|
|
|
|
|
XPK_KEYWORD("case"), |
|
466
|
|
|
|
|
|
|
XPK_OPTIONAL( XPK_KEYWORD("if") ), |
|
467
|
|
|
|
|
|
|
XPK_PARENS( XPK_TERMEXPR_SCALARCTX ) |
|
468
|
|
|
|
|
|
|
), |
|
469
|
|
|
|
|
|
|
XPK_BLOCK |
|
470
|
|
|
|
|
|
|
), |
|
471
|
|
|
|
|
|
|
XPK_OPTIONAL( /* default { ... } */ |
|
472
|
|
|
|
|
|
|
XPK_KEYWORD("default"), |
|
473
|
|
|
|
|
|
|
XPK_BLOCK |
|
474
|
|
|
|
|
|
|
) |
|
475
|
|
|
|
|
|
|
), |
|
476
|
|
|
|
|
|
|
0, |
|
477
|
|
|
|
|
|
|
}, |
|
478
|
|
|
|
|
|
|
.build = &build_match, |
|
479
|
|
|
|
|
|
|
}; |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
#ifndef HAVE_OP_ISA |
|
482
|
|
|
|
|
|
|
#include "hax/newOP_CUSTOM.c.inc" |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
/* Can't use sv_isa_sv() because that was only added in 5.32 */ |
|
485
|
6
|
|
|
|
|
|
static bool S_sv_isa_sv(pTHX_ SV *sv, SV *namesv) |
|
486
|
|
|
|
|
|
|
{ |
|
487
|
6
|
50
|
|
|
|
|
if(!SvROK(sv) || !SvOBJECT(SvRV(sv))) |
|
|
|
50
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
return FALSE; |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
/* Also can't use GV_NOUNIVERSAL here because that also only turned up in 5.32 */ |
|
491
|
6
|
|
|
|
|
|
GV *isagv = gv_fetchmeth_pvn(SvSTASH(SvRV(sv)), "isa", 3, -1, 0); |
|
492
|
|
|
|
|
|
|
/* This probably finds UNIVERSAL::isa; if so we can avoid it and just do it |
|
493
|
|
|
|
|
|
|
* directly ourselves by calling sv_derived_from_sv() |
|
494
|
|
|
|
|
|
|
*/ |
|
495
|
6
|
50
|
|
|
|
|
if(isagv && !strEQ(HvNAME(GvSTASH(isagv)), "UNIVERSAL")) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
496
|
2
|
|
|
|
|
|
dSP; |
|
497
|
2
|
50
|
|
|
|
|
CV *isacv = isGV(isagv) ? GvCV(isagv) : MUTABLE_CV(isagv); |
|
498
|
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
PUTBACK; |
|
500
|
|
|
|
|
|
|
|
|
501
|
2
|
|
|
|
|
|
ENTER; |
|
502
|
2
|
|
|
|
|
|
SAVETMPS; |
|
503
|
|
|
|
|
|
|
|
|
504
|
2
|
50
|
|
|
|
|
EXTEND(SP, 2); |
|
505
|
2
|
50
|
|
|
|
|
PUSHMARK(SP); |
|
506
|
2
|
|
|
|
|
|
PUSHs(sv); |
|
507
|
2
|
|
|
|
|
|
PUSHs(namesv); |
|
508
|
2
|
|
|
|
|
|
PUTBACK; |
|
509
|
|
|
|
|
|
|
|
|
510
|
2
|
|
|
|
|
|
call_sv((SV *)isacv, G_SCALAR); |
|
511
|
|
|
|
|
|
|
|
|
512
|
2
|
|
|
|
|
|
SPAGAIN; |
|
513
|
2
|
|
|
|
|
|
SV *retsv = POPs; |
|
514
|
2
|
50
|
|
|
|
|
bool ret = SvTRUE(retsv); |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
515
|
2
|
|
|
|
|
|
PUTBACK; |
|
516
|
|
|
|
|
|
|
|
|
517
|
2
|
50
|
|
|
|
|
FREETMPS; |
|
518
|
2
|
|
|
|
|
|
LEAVE; |
|
519
|
|
|
|
|
|
|
|
|
520
|
2
|
|
|
|
|
|
return ret; |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
|
|
523
|
4
|
|
|
|
|
|
return sv_derived_from_sv(sv, namesv, 0); |
|
524
|
|
|
|
|
|
|
} |
|
525
|
|
|
|
|
|
|
|
|
526
|
6
|
|
|
|
|
|
static OP *pp_isa(pTHX) |
|
527
|
|
|
|
|
|
|
{ |
|
528
|
6
|
|
|
|
|
|
dSP; |
|
529
|
6
|
|
|
|
|
|
SV *rhs = POPs; |
|
530
|
6
|
|
|
|
|
|
SV *lhs = TOPs; |
|
531
|
|
|
|
|
|
|
|
|
532
|
6
|
100
|
|
|
|
|
SETs(boolSV(S_sv_isa_sv(aTHX_ lhs, rhs))); |
|
533
|
6
|
|
|
|
|
|
RETURN; |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
7
|
|
|
|
|
|
static OP *newop_isa(pTHX_ U32 flags, OP *lhs, OP *rhs, SV **parsedata, void *hookdata) |
|
537
|
|
|
|
|
|
|
{ |
|
538
|
|
|
|
|
|
|
/* Avoid strictness failure on bareword RHS */ |
|
539
|
7
|
50
|
|
|
|
|
if(rhs->op_type == OP_CONST && rhs->op_private & OPpCONST_BARE) |
|
|
|
50
|
|
|
|
|
|
|
540
|
7
|
|
|
|
|
|
rhs->op_private &= ~(OPpCONST_BARE|OPpCONST_STRICT); |
|
541
|
|
|
|
|
|
|
|
|
542
|
7
|
|
|
|
|
|
return newBINOP_CUSTOM(&pp_isa, flags, lhs, rhs); |
|
543
|
|
|
|
|
|
|
} |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
static const struct XSParseInfixHooks hooks_isa = { |
|
546
|
|
|
|
|
|
|
.flags = 0, |
|
547
|
|
|
|
|
|
|
.cls = XPI_CLS_ISA, |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
.new_op = &newop_isa, |
|
550
|
|
|
|
|
|
|
}; |
|
551
|
|
|
|
|
|
|
#endif |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
MODULE = Syntax::Keyword::Match PACKAGE = Syntax::Keyword::Match |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
BOOT: |
|
556
|
17
|
|
|
|
|
|
boot_xs_parse_keyword(0.36); |
|
557
|
17
|
|
|
|
|
|
boot_xs_parse_infix(0); |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
register_xs_parse_keyword("match", &hooks_match, NULL); |
|
560
|
|
|
|
|
|
|
#ifndef HAVE_OP_ISA |
|
561
|
|
|
|
|
|
|
register_xs_parse_infix("isa", &hooks_isa, NULL); |
|
562
|
|
|
|
|
|
|
#endif |