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