File Coverage

Ref.xs
Criterion Covered Total %
statement 88 102 86.2
branch 76 138 55.0
condition n/a
subroutine n/a
pod n/a
total 164 240 68.3


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