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 |