line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
/* |
2
|
|
|
|
|
|
|
Copyright 2013 Lukas Mai. |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
5
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
6
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
9
|
|
|
|
|
|
|
*/ |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
#ifdef __GNUC__ |
12
|
|
|
|
|
|
|
#if (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ >= 5 |
13
|
|
|
|
|
|
|
#define PRAGMA_GCC_(X) _Pragma(#X) |
14
|
|
|
|
|
|
|
#define PRAGMA_GCC(X) PRAGMA_GCC_(GCC X) |
15
|
|
|
|
|
|
|
#endif |
16
|
|
|
|
|
|
|
#endif |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
#ifndef PRAGMA_GCC |
19
|
|
|
|
|
|
|
#define PRAGMA_GCC(X) |
20
|
|
|
|
|
|
|
#endif |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
#ifdef DEVEL |
23
|
|
|
|
|
|
|
#define WARNINGS_RESET PRAGMA_GCC(diagnostic pop) |
24
|
|
|
|
|
|
|
#define WARNINGS_ENABLEW(X) PRAGMA_GCC(diagnostic warning #X) |
25
|
|
|
|
|
|
|
#define WARNINGS_ENABLE \ |
26
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wall) \ |
27
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wextra) \ |
28
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wundef) \ |
29
|
|
|
|
|
|
|
/* WARNINGS_ENABLEW(-Wshadow) :-( */ \ |
30
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wbad-function-cast) \ |
31
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wcast-align) \ |
32
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wwrite-strings) \ |
33
|
|
|
|
|
|
|
/* WARNINGS_ENABLEW(-Wnested-externs) wtf? */ \ |
34
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wstrict-prototypes) \ |
35
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wmissing-prototypes) \ |
36
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Winline) \ |
37
|
|
|
|
|
|
|
WARNINGS_ENABLEW(-Wdisabled-optimization) |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
#else |
40
|
|
|
|
|
|
|
#define WARNINGS_RESET |
41
|
|
|
|
|
|
|
#define WARNINGS_ENABLE |
42
|
|
|
|
|
|
|
#endif |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#define PERL_NO_GET_CONTEXT |
46
|
|
|
|
|
|
|
#include "EXTERN.h" |
47
|
|
|
|
|
|
|
#include "perl.h" |
48
|
|
|
|
|
|
|
#include "XSUB.h" |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
#include |
51
|
|
|
|
|
|
|
#include |
52
|
|
|
|
|
|
|
#include |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
WARNINGS_ENABLE |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
#define HAVE_PERL_VERSION(R, V, S) \ |
59
|
|
|
|
|
|
|
(PERL_REVISION > (R) || (PERL_REVISION == (R) && (PERL_VERSION > (V) || (PERL_VERSION == (V) && (PERL_SUBVERSION >= (S)))))) |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
#if !HAVE_PERL_VERSION(5, 13, 6) |
63
|
|
|
|
|
|
|
static OP *my_append_elem(pTHX_ I32 type, OP *first, OP *last) { |
64
|
|
|
|
|
|
|
if (!first) |
65
|
|
|
|
|
|
|
return last; |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
if (!last) |
68
|
|
|
|
|
|
|
return first; |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
if (first->op_type != (unsigned)type |
71
|
|
|
|
|
|
|
|| (type == OP_LIST && (first->op_flags & OPf_PARENS))) |
72
|
|
|
|
|
|
|
{ |
73
|
|
|
|
|
|
|
return newLISTOP(type, 0, first, last); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
if (first->op_flags & OPf_KIDS) |
77
|
|
|
|
|
|
|
((LISTOP*)first)->op_last->op_sibling = last; |
78
|
|
|
|
|
|
|
else { |
79
|
|
|
|
|
|
|
first->op_flags |= OPf_KIDS; |
80
|
|
|
|
|
|
|
((LISTOP*)first)->op_first = last; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
((LISTOP*)first)->op_last = last; |
83
|
|
|
|
|
|
|
return first; |
84
|
|
|
|
|
|
|
} |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
#define op_append_elem(type, first, last) my_append_elem(aTHX_ type, first, last) |
87
|
|
|
|
|
|
|
#endif |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
#define MY_PKG "Quote::Ref" |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
#define HINTK_QWA MY_PKG "/qwa" |
92
|
|
|
|
|
|
|
#define HINTK_QWH MY_PKG "/qwh" |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
enum QxType { |
95
|
|
|
|
|
|
|
QX_ARRAY, |
96
|
|
|
|
|
|
|
QX_HASH |
97
|
|
|
|
|
|
|
}; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **); |
100
|
|
|
|
|
|
|
|
101
|
15
|
|
|
|
|
|
static void free_ptr_op(pTHX_ void *vp) { |
102
|
15
|
|
|
|
|
|
OP **pp = vp; |
103
|
15
|
|
|
|
|
|
op_free(*pp); |
104
|
15
|
|
|
|
|
|
Safefree(pp); |
105
|
15
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
typedef struct { |
108
|
|
|
|
|
|
|
enum QxType type; |
109
|
|
|
|
|
|
|
I32 delim_start, delim_stop; |
110
|
|
|
|
|
|
|
} QxSpec; |
111
|
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
static void missing_terminator(pTHX_ const QxSpec *spec, line_t line) { |
113
|
0
|
|
|
|
|
|
I32 c = spec->delim_stop; |
114
|
0
|
|
|
|
|
|
SV *sv = sv_2mortal(newSVpvs("'\"'")); |
115
|
|
|
|
|
|
|
|
116
|
0
|
0
|
|
|
|
|
if (c != '"') { |
117
|
|
|
|
|
|
|
U8 utf8_tmp[UTF8_MAXBYTES + 1], *d; |
118
|
0
|
|
|
|
|
|
d = uvchr_to_utf8(utf8_tmp, c); |
119
|
0
|
|
|
|
|
|
pv_uni_display(sv, utf8_tmp, d - utf8_tmp, 100, UNI_DISPLAY_QQ); |
120
|
0
|
|
|
|
|
|
sv_insert(sv, 0, 0, "\"", 1); |
121
|
0
|
|
|
|
|
|
sv_catpvs(sv, "\""); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
0
|
0
|
|
|
|
|
if (line) { |
125
|
0
|
|
|
|
|
|
CopLINE_set(PL_curcop, line); |
126
|
|
|
|
|
|
|
} |
127
|
0
|
|
|
|
|
|
croak("Can't find string terminator %"SVf" anywhere before EOF", SVfARG(sv)); |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
98
|
|
|
|
|
|
static void my_sv_cat_c(pTHX_ SV *sv, U32 c) { |
131
|
|
|
|
|
|
|
U8 ds[UTF8_MAXBYTES + 1], *d; |
132
|
98
|
|
|
|
|
|
d = uvchr_to_utf8(ds, c); |
133
|
98
|
100
|
|
|
|
|
if (d - ds > 1) { |
134
|
8
|
|
|
|
|
|
sv_utf8_upgrade(sv); |
135
|
|
|
|
|
|
|
} |
136
|
98
|
|
|
|
|
|
sv_catpvn(sv, (char *)ds, d - ds); |
137
|
98
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
15
|
|
|
|
|
|
static OP *parse_qxtail(pTHX_ const QxSpec *spec) { |
140
|
|
|
|
|
|
|
I32 c; |
141
|
|
|
|
|
|
|
OP **gen_sentinel; |
142
|
|
|
|
|
|
|
SV *sv; |
143
|
|
|
|
|
|
|
int nesting; |
144
|
15
|
|
|
|
|
|
const int is_utf8 = lex_bufutf8(); |
145
|
15
|
|
|
|
|
|
const line_t start = CopLINE(PL_curcop); |
146
|
|
|
|
|
|
|
|
147
|
15
|
100
|
|
|
|
|
nesting = spec->delim_start == spec->delim_stop ? -1 : 0; |
148
|
|
|
|
|
|
|
|
149
|
15
|
|
|
|
|
|
Newx(gen_sentinel, 1, OP *); |
150
|
15
|
|
|
|
|
|
*gen_sentinel = NULL; |
151
|
15
|
|
|
|
|
|
SAVEDESTRUCTOR_X(free_ptr_op, gen_sentinel); |
152
|
|
|
|
|
|
|
|
153
|
15
|
|
|
|
|
|
sv = sv_2mortal(newSVpvs("")); |
154
|
15
|
100
|
|
|
|
|
if (is_utf8) { |
155
|
6
|
|
|
|
|
|
SvUTF8_on(sv); |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
for (;;) { |
159
|
142
|
|
|
|
|
|
c = lex_peek_unichar(0); |
160
|
142
|
50
|
|
|
|
|
if (c == -1) { |
161
|
0
|
|
|
|
|
|
missing_terminator(aTHX_ spec, start); |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
142
|
|
|
|
|
|
lex_read_unichar(0); |
165
|
|
|
|
|
|
|
|
166
|
142
|
100
|
|
|
|
|
if (nesting != -1 && c == spec->delim_start) { |
|
|
100
|
|
|
|
|
|
167
|
2
|
|
|
|
|
|
nesting++; |
168
|
140
|
100
|
|
|
|
|
} else if (c == spec->delim_stop) { |
169
|
17
|
100
|
|
|
|
|
if (nesting == -1 || nesting == 0) { |
|
|
100
|
|
|
|
|
|
170
|
|
|
|
|
|
|
break; |
171
|
|
|
|
|
|
|
} |
172
|
2
|
|
|
|
|
|
nesting--; |
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
127
|
100
|
|
|
|
|
if (c == '\\') { |
176
|
10
|
|
|
|
|
|
const I32 d = lex_peek_unichar(0); |
177
|
|
|
|
|
|
|
|
178
|
10
|
100
|
|
|
|
|
if (d == '\\' || d == spec->delim_start || d == spec->delim_stop) { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
179
|
10
|
|
|
|
|
|
c = d; |
180
|
10
|
|
|
|
|
|
lex_read_unichar(0); |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
127
|
100
|
|
|
|
|
if (!isSPACE_uni(c)) { |
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
185
|
98
|
|
|
|
|
|
my_sv_cat_c(aTHX_ sv, c); |
186
|
29
|
50
|
|
|
|
|
} else if (SvCUR(sv)) { |
187
|
29
|
|
|
|
|
|
*gen_sentinel = op_append_elem( |
188
|
|
|
|
|
|
|
OP_LIST, |
189
|
|
|
|
|
|
|
*gen_sentinel, |
190
|
|
|
|
|
|
|
newSVOP(OP_CONST, 0, SvREFCNT_inc_NN(sv)) |
191
|
|
|
|
|
|
|
); |
192
|
29
|
|
|
|
|
|
sv = sv_2mortal(newSVpvs("")); |
193
|
29
|
100
|
|
|
|
|
if (is_utf8) { |
194
|
17
|
|
|
|
|
|
SvUTF8_on(sv); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
} |
197
|
127
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
15
|
100
|
|
|
|
|
if (SvCUR(sv)) { |
200
|
13
|
|
|
|
|
|
*gen_sentinel = op_append_elem( |
201
|
|
|
|
|
|
|
OP_LIST, |
202
|
|
|
|
|
|
|
*gen_sentinel, |
203
|
|
|
|
|
|
|
newSVOP(OP_CONST, 0, SvREFCNT_inc_NN(sv)) |
204
|
|
|
|
|
|
|
); |
205
|
13
|
|
|
|
|
|
sv = NULL; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
{ |
209
|
15
|
100
|
|
|
|
|
OP *gen = spec->type == QX_ARRAY ? newANONLIST(*gen_sentinel) : newANONHASH(*gen_sentinel); |
210
|
15
|
|
|
|
|
|
*gen_sentinel = NULL; |
211
|
|
|
|
|
|
|
|
212
|
15
|
|
|
|
|
|
return gen; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
15
|
|
|
|
|
|
static void parse_qx(pTHX_ OP **op_ptr, const enum QxType t) { |
217
|
|
|
|
|
|
|
I32 c; |
218
|
|
|
|
|
|
|
|
219
|
15
|
|
|
|
|
|
c = lex_peek_unichar(0); |
220
|
|
|
|
|
|
|
|
221
|
15
|
50
|
|
|
|
|
if (c != '#') { |
222
|
15
|
|
|
|
|
|
lex_read_space(0); |
223
|
15
|
|
|
|
|
|
c = lex_peek_unichar(0); |
224
|
15
|
50
|
|
|
|
|
if (c == -1) { |
225
|
0
|
0
|
|
|
|
|
croak("Unexpected EOF after qw%c", t == QX_ARRAY ? 'a' : 'h'); |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
} |
228
|
15
|
|
|
|
|
|
lex_read_unichar(0); |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
{ |
231
|
15
|
|
|
|
|
|
I32 delim_start = c; |
232
|
15
|
|
|
|
|
|
I32 delim_stop = |
233
|
28
|
100
|
|
|
|
|
c == '(' ? ')' : |
234
|
26
|
50
|
|
|
|
|
c == '[' ? ']' : |
235
|
26
|
50
|
|
|
|
|
c == '{' ? '}' : |
236
|
13
|
100
|
|
|
|
|
c == '<' ? '>' : |
237
|
|
|
|
|
|
|
c |
238
|
|
|
|
|
|
|
; |
239
|
15
|
|
|
|
|
|
const QxSpec spec = { |
240
|
|
|
|
|
|
|
t, |
241
|
|
|
|
|
|
|
delim_start, delim_stop |
242
|
|
|
|
|
|
|
}; |
243
|
|
|
|
|
|
|
|
244
|
15
|
|
|
|
|
|
*op_ptr = parse_qxtail(aTHX_ &spec); |
245
|
|
|
|
|
|
|
} |
246
|
15
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
|
248
|
18
|
|
|
|
|
|
static int qx_enabled(pTHX_ const char *hk_ptr, size_t hk_len) { |
249
|
|
|
|
|
|
|
HV *hints; |
250
|
|
|
|
|
|
|
SV *sv, **psv; |
251
|
|
|
|
|
|
|
|
252
|
18
|
50
|
|
|
|
|
if (!(hints = GvHV(PL_hintgv))) { |
253
|
0
|
|
|
|
|
|
return FALSE; |
254
|
|
|
|
|
|
|
} |
255
|
18
|
100
|
|
|
|
|
if (!(psv = hv_fetch(hints, hk_ptr, hk_len, 0))) { |
256
|
3
|
|
|
|
|
|
return FALSE; |
257
|
|
|
|
|
|
|
} |
258
|
15
|
|
|
|
|
|
sv = *psv; |
259
|
15
|
50
|
|
|
|
|
return SvTRUE(sv); |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
#define qx_enableds(S) qx_enabled(aTHX_ "" S "", sizeof (S) - 1) |
262
|
|
|
|
|
|
|
|
263
|
4276
|
|
|
|
|
|
static int my_keyword_plugin(pTHX_ char *keyword_ptr, STRLEN keyword_len, OP **op_ptr) { |
264
|
|
|
|
|
|
|
int ret; |
265
|
|
|
|
|
|
|
enum QxType t; |
266
|
|
|
|
|
|
|
|
267
|
4294
|
100
|
|
|
|
|
if ( |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
268
|
939
|
100
|
|
|
|
|
keyword_len == 3 && |
269
|
18
|
50
|
|
|
|
|
keyword_ptr[0] == 'q' && |
270
|
18
|
|
|
|
|
|
keyword_ptr[1] == 'w' && |
271
|
|
|
|
|
|
|
( |
272
|
12
|
|
|
|
|
|
keyword_ptr[2] == 'a' ? t = QX_ARRAY, qx_enableds(HINTK_QWA) : |
273
|
6
|
|
|
|
|
|
keyword_ptr[2] == 'h' ? t = QX_HASH , qx_enableds(HINTK_QWH) : |
274
|
|
|
|
|
|
|
0 |
275
|
|
|
|
|
|
|
) |
276
|
|
|
|
|
|
|
) { |
277
|
15
|
|
|
|
|
|
ENTER; |
278
|
15
|
|
|
|
|
|
parse_qx(aTHX_ op_ptr, t); |
279
|
15
|
|
|
|
|
|
LEAVE; |
280
|
15
|
|
|
|
|
|
ret = KEYWORD_PLUGIN_EXPR; |
281
|
|
|
|
|
|
|
} else { |
282
|
4261
|
|
|
|
|
|
ret = next_keyword_plugin(aTHX_ keyword_ptr, keyword_len, op_ptr); |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
4276
|
|
|
|
|
|
return ret; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
WARNINGS_RESET |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
MODULE = Quote::Ref PACKAGE = Quote::Ref |
292
|
|
|
|
|
|
|
PROTOTYPES: ENABLE |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
BOOT: |
295
|
|
|
|
|
|
|
WARNINGS_ENABLE { |
296
|
4
|
|
|
|
|
|
HV *const stash = gv_stashpvs(MY_PKG, GV_ADD); |
297
|
|
|
|
|
|
|
/**/ |
298
|
4
|
|
|
|
|
|
newCONSTSUB(stash, "HINTK_QWA", newSVpvs(HINTK_QWA)); |
299
|
4
|
|
|
|
|
|
newCONSTSUB(stash, "HINTK_QWH", newSVpvs(HINTK_QWH)); |
300
|
|
|
|
|
|
|
/**/ |
301
|
4
|
|
|
|
|
|
next_keyword_plugin = PL_keyword_plugin; |
302
|
4
|
|
|
|
|
|
PL_keyword_plugin = my_keyword_plugin; |
303
|
|
|
|
|
|
|
} WARNINGS_RESET |