File Coverage

lib/XS/Parse/Keyword/FromPerl.xs
Criterion Covered Total %
statement 195 225 86.6
branch 99 162 61.1
condition n/a
subroutine n/a
pod n/a
total 294 387 75.9


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, 2023-2024 -- leonerd@leonerd.org.uk
5             */
6             #include "EXTERN.h"
7             #include "perl.h"
8             #include "XSUB.h"
9              
10             #include "XSParseKeyword.h"
11              
12             #include "perl-backcompat.c.inc"
13             #include "perl-additions.c.inc"
14              
15             #include "newSVop.c.inc"
16              
17             struct XPKFPHookdata {
18             /* Phase callbacks */
19             CV *permitcv;
20             CV *checkcv;
21             CV *buildcv;
22              
23             SV *hookdata;
24             };
25              
26             static const struct XSParseKeywordPieceType piece_zero = {0};
27              
28             #define make_pieces_array(piecesav) S_make_pieces_array(aTHX_ piecesav)
29 17           static const struct XSParseKeywordPieceType *S_make_pieces_array(pTHX_ AV *piecesav)
30             {
31 17           U32 npieces = av_count(piecesav);
32 17 100         if(!npieces)
33             return NULL;
34              
35 15           SV *arraypv = newSVpvn("", 0);
36 34 100         for(U32 i = 0; i < npieces; i++) {
37 19           dSP;
38 19           ENTER;
39 19           SAVETMPS;
40              
41 19 50         EXTEND(SP, 1);
42 19 50         PUSHMARK(SP);
43 19           PUSHs(AvARRAY(piecesav)[i]);
44 19           PUTBACK;
45              
46 19           call_method("to_array", G_SCALAR);
47              
48 19           SPAGAIN;
49              
50 19           sv_catsv(arraypv, POPs);
51              
52 19           PUTBACK;
53              
54 19 50         FREETMPS;
55 19           LEAVE;
56             }
57              
58 15           sv_catpvn(arraypv, (char *)&piece_zero, sizeof(piece_zero));
59              
60 15           return (struct XSParseKeywordPieceType *)SvPVX(arraypv); SvLEN(arraypv) = 0; /* steal */
61             }
62              
63 1           static bool cb_permit(pTHX_ void *hookdata)
64             {
65 1           struct XPKFPHookdata *data = hookdata;
66              
67 1           dSP;
68              
69 1           ENTER;
70 1           SAVETMPS;
71              
72 1 50         PUSHMARK(SP);
73 1 50         if(data->hookdata)
74 1 50         XPUSHs(sv_mortalcopy(data->hookdata));
75             else
76 0 0         XPUSHs(&PL_sv_undef);
77 1           PUTBACK;
78              
79 1           call_sv((SV *)data->permitcv, G_SCALAR);
80              
81 1           SPAGAIN;
82 1           bool ret = SvTRUEx(POPs);
83              
84 1 50         FREETMPS;
85 1           LEAVE;
86              
87 1           return ret;
88             }
89              
90 1           static void cb_check(pTHX_ void *hookdata)
91             {
92 1           struct XPKFPHookdata *data = hookdata;
93              
94 1           dSP;
95              
96 1           ENTER;
97 1           SAVETMPS;
98              
99 1 50         PUSHMARK(SP);
100 1 50         if(data->hookdata)
101 1 50         XPUSHs(sv_mortalcopy(data->hookdata));
102             else
103 0 0         XPUSHs(&PL_sv_undef);
104 1           PUTBACK;
105              
106 1           call_sv((SV *)data->checkcv, G_VOID);
107              
108 1 50         FREETMPS;
109 1           LEAVE;
110 1           }
111              
112 21           static int cb_build(pTHX_ OP **out, XSParseKeywordPiece *args[], size_t nargs, void *hookdata)
113             {
114 21           struct XPKFPHookdata *data = hookdata;
115              
116 21           dSP;
117 21           SV *outsv = newSV(0);
118 21           AV *argsav = newAV();
119              
120 39 100         for(U32 i = 0; i < nargs; i++) {
121 18           SV *argsv = newSV(0);
122 18           sv_setiv(newSVrv(argsv, "XS::Parse::Keyword::FromPerl::_Arg"), PTR2IV(args[i]));
123 18           av_push(argsav, argsv);
124             }
125              
126 21           ENTER;
127 21           SAVETMPS;
128              
129 21 50         PUSHMARK(SP);
130 21 50         EXTEND(SP, 3);
131 21           mPUSHs(newRV_noinc(outsv));
132 21           mPUSHs(newRV_noinc((SV *)argsav));
133 21 100         if(data->hookdata)
134 1           PUSHs(sv_mortalcopy(data->hookdata));
135             else
136 20           PUSHs(&PL_sv_undef);
137 21           PUTBACK;
138              
139 21           call_sv((SV *)data->buildcv, G_SCALAR);
140              
141 21           SPAGAIN;
142 21           int ret = POPu;
143              
144 21 100         if(SvOK(outsv)) {
145 19           *out = SvOPo(outsv);
146             }
147              
148 21 50         FREETMPS;
149 21           LEAVE;
150              
151 21           return ret;
152             }
153              
154 10           static void S_setup_constants(pTHX)
155             {
156 10           HV *stash;
157 10           AV *export;
158              
159             #define DO_CONSTANT(c) \
160             newCONSTSUB(stash, #c, newSViv(c)); \
161             av_push(export, newSVpv(#c, 0))
162              
163 10           stash = gv_stashpvs("XS::Parse::Keyword::FromPerl", TRUE);
164 10           export = get_av("XS::Parse::Keyword::FromPerl::EXPORT_OK", TRUE);
165              
166 10           DO_CONSTANT(KEYWORD_PLUGIN_EXPR);
167 10           DO_CONSTANT(KEYWORD_PLUGIN_STMT);
168              
169 10           DO_CONSTANT(XPK_FLAG_EXPR);
170 10           DO_CONSTANT(XPK_FLAG_STMT);
171 10           DO_CONSTANT(XPK_FLAG_AUTOSEMI);
172 10           DO_CONSTANT(XPK_FLAG_BLOCKSCOPE);
173              
174 10           DO_CONSTANT(XPK_LEXVAR_SCALAR);
175 10           DO_CONSTANT(XPK_LEXVAR_ARRAY);
176 10           DO_CONSTANT(XPK_LEXVAR_HASH);
177 10           DO_CONSTANT(XPK_LEXVAR_ANY);
178 10           }
179              
180             MODULE = XS::Parse::Keyword::FromPerl PACKAGE = XS::Parse::Keyword::FromPerl::_Arg
181              
182             SV *line(SV *self)
183             ALIAS:
184             op = 1
185             cv = 2
186             sv = 3
187             has_sv = 4
188             i = 5
189             padix = 6
190             line = 7
191             CODE:
192 19           XSParseKeywordPiece *arg = NUM2PTR(XSParseKeywordPiece *, SvIV(SvRV(self)));
193 19           switch(ix) {
194 6           case 1: RETVAL = newSVop(arg->op); break;
195 0           case 2: RETVAL = newRV_inc((SV *)arg->cv); break;
196 9 50         case 3: RETVAL = arg->sv ? SvREFCNT_inc(arg->sv) : &PL_sv_undef; break;
197 2 100         case 4: RETVAL = arg->sv ? &PL_sv_yes : &PL_sv_no; break;
198 1           case 5: RETVAL = newSViv(arg->i); break;
199 1           case 6: RETVAL = newSVuv(arg->padix); break;
200 0           case 7: RETVAL = newSViv(arg->line); break;
201             }
202             OUTPUT:
203             RETVAL
204              
205             MODULE = XS::Parse::Keyword::FromPerl PACKAGE = XS::Parse::Keyword::FromPerl::_Piece
206              
207             SV *to_array(SV *self)
208             CODE:
209 19           AV *selfav = AV_FROM_REF(self);
210 19           SV **svp = AvARRAY(selfav);
211 19           char *type = SvPV_nolen(svp[0]);
212              
213 19           struct XSParseKeywordPieceType piece;
214             /* Simple */
215 19 100         if (strEQ(type, "XPK_BLOCK")) piece = (struct XSParseKeywordPieceType)XPK_BLOCK;
216 16 50         else if(strEQ(type, "XPK_ANONSUB")) piece = (struct XSParseKeywordPieceType)XPK_ANONSUB;
217 16 50         else if(strEQ(type, "XPK_ARITHEXPR")) piece = (struct XSParseKeywordPieceType)XPK_ARITHEXPR;
218 16 50         else if(strEQ(type, "XPK_ARITHEXPR_OPT")) piece = (struct XSParseKeywordPieceType)XPK_ARITHEXPR_OPT;
219 16 100         else if(strEQ(type, "XPK_TERMEXPR")) piece = (struct XSParseKeywordPieceType)XPK_TERMEXPR;
220 14 50         else if(strEQ(type, "XPK_TERMEXPR_OPT")) piece = (struct XSParseKeywordPieceType)XPK_TERMEXPR_OPT;
221 14 50         else if(strEQ(type, "XPK_LISTEXPR")) piece = (struct XSParseKeywordPieceType)XPK_LISTEXPR;
222 14 50         else if(strEQ(type, "XPK_LISTEXPR_OPT")) piece = (struct XSParseKeywordPieceType)XPK_LISTEXPR_OPT;
223 14 100         else if(strEQ(type, "XPK_IDENT")) piece = (struct XSParseKeywordPieceType)XPK_IDENT;
224 11 100         else if(strEQ(type, "XPK_IDENT_OPT")) piece = (struct XSParseKeywordPieceType)XPK_IDENT_OPT;
225 10 100         else if(strEQ(type, "XPK_PACKAGENAME")) piece = (struct XSParseKeywordPieceType)XPK_PACKAGENAME;
226 9 50         else if(strEQ(type, "XPK_PACKAGENAME_OPT")) piece = (struct XSParseKeywordPieceType)XPK_PACKAGENAME_OPT;
227 9 100         else if(strEQ(type, "XPK_VSTRING")) piece = (struct XSParseKeywordPieceType)XPK_VSTRING;
228 8 50         else if(strEQ(type, "XPK_VSTRING_OPT")) piece = (struct XSParseKeywordPieceType)XPK_VSTRING_OPT;
229 8 100         else if(strEQ(type, "XPK_COMMA")) piece = (struct XSParseKeywordPieceType)XPK_COMMA;
230 7 50         else if(strEQ(type, "XPK_COLON")) piece = (struct XSParseKeywordPieceType)XPK_COLON;
231 7 50         else if(strEQ(type, "XPK_EQUALS")) piece = (struct XSParseKeywordPieceType)XPK_EQUALS;
232 7 50         else if(strEQ(type, "XPK_INTRO_MY")) piece = (struct XSParseKeywordPieceType)XPK_INTRO_MY;
233             /* Single-SV parametric */
234 7 100         else if(strEQ(type, "XPK_LEXVARNAME"))
235 1           piece = (struct XSParseKeywordPieceType)XPK_LEXVARNAME(SvUV(svp[1]));
236 6 100         else if(strEQ(type, "XPK_LEXVAR"))
237 1           piece = (struct XSParseKeywordPieceType)XPK_LEXVAR(SvUV(svp[1]));
238 5 50         else if(strEQ(type, "XPK_LEXVAR_MY"))
239 0           piece = (struct XSParseKeywordPieceType)XPK_LEXVAR_MY(SvUV(svp[1]));
240 5 50         else if(strEQ(type, "XPK_LITERAL"))
241 0           piece = (struct XSParseKeywordPieceType)XPK_LITERAL(savepv(SvPV_nolen(svp[1])));
242 5 100         else if(strEQ(type, "XPK_KEYWORD"))
243 1           piece = (struct XSParseKeywordPieceType)XPK_KEYWORD(savepv(SvPV_nolen(svp[1])));
244 4 50         else if(strEQ(type, "XPK_FAILURE"))
245 0           piece = (struct XSParseKeywordPieceType)XPK_FAILURE(savepv(SvPV_nolen(svp[1])));
246 4 100         else if(strEQ(type, "XPK_WARNING"))
247 1           piece = (struct XSParseKeywordPieceType)XPK_WARNING(savepv(SvPV_nolen(svp[1])));
248 3 50         else if(strEQ(type, "XPK_WARNING_AMBIGUOUS"))
249 0           piece = (struct XSParseKeywordPieceType)XPK_WARNING_AMBIGUOUS(savepv(SvPV_nolen(svp[1])));
250 3 50         else if(strEQ(type, "XPK_WARNING_DEPRECATED"))
251 0           piece = (struct XSParseKeywordPieceType)XPK_WARNING_DEPRECATED(savepv(SvPV_nolen(svp[1])));
252 3 50         else if(strEQ(type, "XPK_WARNING_EXPERIMENTAL"))
253 0           piece = (struct XSParseKeywordPieceType)XPK_WARNING_EXPERIMENTAL(savepv(SvPV_nolen(svp[1])));
254 3 50         else if(strEQ(type, "XPK_WARNING_PRECEDENCE"))
255 0           piece = (struct XSParseKeywordPieceType)XPK_WARNING_PRECEDENCE(savepv(SvPV_nolen(svp[1])));
256 3 50         else if(strEQ(type, "XPK_WARNING_SYNTAX"))
257 0           piece = (struct XSParseKeywordPieceType)XPK_WARNING_SYNTAX(savepv(SvPV_nolen(svp[1])));
258             /* Structural */
259 3 50         else if(strEQ(type, "XPK_SEQUENCE"))
260 0           piece = (struct XSParseKeywordPieceType)XPK_SEQUENCE_pieces(
261             make_pieces_array(AV_FROM_REF(svp[1]))
262             );
263 3 50         else if(strEQ(type, "XPK_OPTIONAL"))
264 0           piece = (struct XSParseKeywordPieceType)XPK_OPTIONAL_pieces(
265             make_pieces_array(AV_FROM_REF(svp[1]))
266             );
267 3 100         else if(strEQ(type, "XPK_REPEATED"))
268 1           piece = (struct XSParseKeywordPieceType)XPK_REPEATED_pieces(
269             make_pieces_array(AV_FROM_REF(svp[1]))
270             );
271 2 50         else if(strEQ(type, "XPK_CHOICE"))
272 0           piece = (struct XSParseKeywordPieceType)XPK_CHOICE_pieces(
273             make_pieces_array(AV_FROM_REF(svp[1]))
274             );
275 2 50         else if(strEQ(type, "XPK_PARENS"))
276 2           piece = (struct XSParseKeywordPieceType)XPK_PARENS_pieces(
277             make_pieces_array(AV_FROM_REF(svp[1]))
278             );
279 0 0         else if(strEQ(type, "XPK_ARGS"))
280 0           piece = (struct XSParseKeywordPieceType)XPK_ARGS_pieces(
281             make_pieces_array(AV_FROM_REF(svp[1]))
282             );
283 0 0         else if(strEQ(type, "XPK_BRACKETS"))
284 0           piece = (struct XSParseKeywordPieceType)XPK_BRACKETS_pieces(
285             make_pieces_array(AV_FROM_REF(svp[1]))
286             );
287 0 0         else if(strEQ(type, "XPK_BRACES"))
288 0           piece = (struct XSParseKeywordPieceType)XPK_BRACES_pieces(
289             make_pieces_array(AV_FROM_REF(svp[1]))
290             );
291 0 0         else if(strEQ(type, "XPK_CHEVRONS"))
292 0           piece = (struct XSParseKeywordPieceType)XPK_CHEVRONS_pieces(
293             make_pieces_array(AV_FROM_REF(svp[1]))
294             );
295             else
296 0           croak("Unrecognised type name %s", type);
297              
298 19           RETVAL = newSVpvn((char *)&piece, sizeof(piece));
299             OUTPUT:
300             RETVAL
301              
302             MODULE = XS::Parse::Keyword::FromPerl PACKAGE = XS::Parse::Keyword::FromPerl
303              
304             void
305             register_xs_parse_keyword(const char *name, ...)
306             CODE:
307 19           dKWARG(1);
308              
309 19           struct XPKFPHookdata data = {0};
310 19           U32 flags = 0;
311 19           SV *permit_hintkeysv = NULL;
312 19           const struct XSParseKeywordPieceType *pieces = NULL;
313              
314 19           static const char *args[] = {
315             "flags",
316             "pieces",
317             "permit_hintkey",
318             "permit",
319             "check",
320             /* TODO: parse? */
321             "build",
322             "hookdata",
323             };
324 93 100         while(KWARG_NEXT(args))
325 55           switch(kwarg) {
326 1           case 0: /* flags */
327 1           flags = SvUV(kwval);
328 1           break;
329              
330 14           case 1: /* pieces */
331             {
332 14 50         if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVAV)
    50          
333 0           croak("Expected 'pieces' to be an array ref");
334 14           pieces = make_pieces_array(AV_FROM_REF(kwval));
335 14           break;
336             }
337              
338 18           case 2: /* permit_hintkey */
339 18           permit_hintkeysv = kwval;
340 18           break;
341              
342 1           case 3: /* permit */
343 1 50         if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVCV)
    50          
344 0           croak("Expected 'permit' to be a CODE ref");
345 1 50         data.permitcv = (CV *)SvREFCNT_inc((SV *)CV_FROM_REF(kwval));
346             break;
347              
348 1           case 4: /* check */
349 1 50         if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVCV)
    50          
350 0           croak("Expected 'check' to be a CODE ref");
351 1 50         data.checkcv = (CV *)SvREFCNT_inc((SV *)CV_FROM_REF(kwval));
352             break;
353              
354 19           case 5: /* build */
355 19 50         if(!SvROK(kwval) || SvTYPE(SvRV(kwval)) != SVt_PVCV)
    50          
356 0           croak("Expected 'build' to be a CODE ref");
357 93 50         data.buildcv = (CV *)SvREFCNT_inc((SV *)CV_FROM_REF(kwval));
358             break;
359              
360 1           case 6: /* hookdata */
361 1           data.hookdata = newSVsv(kwval);
362 1           break;
363             }
364              
365 19 50         if(!data.buildcv)
366 0           croak("Require 'build' for register");
367 19 100         if(!permit_hintkeysv && !data.permitcv)
    50          
368 0           croak("Require at least one of 'permit_hintkey' or 'permit'");
369              
370 19 100         if(!pieces) {
371 7           pieces = &piece_zero;
372             }
373              
374 19           struct XSParseKeywordHooks *hooksptr;
375 19           Newx(hooksptr, 1, struct XSParseKeywordHooks);
376              
377 19           *hooksptr = (struct XSParseKeywordHooks){
378             .flags = flags,
379             .pieces = pieces,
380             };
381 19 100         if(permit_hintkeysv)
382 18           hooksptr->permit_hintkey = savepv(SvPV_nolen(permit_hintkeysv));
383 19 100         if(data.permitcv)
384 1           hooksptr->permit = &cb_permit;
385 19 100         if(data.checkcv)
386 1           hooksptr->check = &cb_check;
387 19           if(data.buildcv)
388 19           hooksptr->build = &cb_build;
389              
390 19           struct XPKFPHookdata *dataptr;
391 19           Newx(dataptr, 1, struct XPKFPHookdata);
392 19           *dataptr = data;
393              
394 19           register_xs_parse_keyword(savepv(name), hooksptr, dataptr);
395              
396             BOOT:
397 10           boot_xs_parse_keyword(0.39);
398              
399 10           S_setup_constants(aTHX);