File Coverage

src/keyword.c
Criterion Covered Total %
statement 349 409 85.3
branch 163 244 66.8
condition n/a
subroutine n/a
pod n/a
total 512 653 78.4


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-2022 -- leonerd@leonerd.org.uk
5             */
6              
7             #define PERL_NO_GET_CONTEXT
8              
9             #include "EXTERN.h"
10             #include "perl.h"
11             #include "XSUB.h"
12              
13             #include "XSParseKeyword.h"
14             #include "XSParseInfix.h"
15              
16             #include "keyword.h"
17             #include "infix.h"
18              
19             #include "perl-backcompat.c.inc"
20              
21             #ifndef wrap_keyword_plugin
22             # include "wrap_keyword_plugin.c.inc"
23             #endif
24              
25             #include "lexer-additions.c.inc"
26              
27             /* yycroak() is a long function and hard to emulate or copy-paste for our
28             * purposes; we'll reïmplement a smaller version of it
29             *
30             * ours will croak instead of warn
31             */
32              
33             #define LEX_IGNORE_UTF8_HINTS 0x00000002
34              
35             #define PL_linestr (PL_parser->linestr)
36              
37             #ifdef USE_UTF8_SCRIPTS
38             # define UTF cBOOL(!IN_BYTES)
39             #elif HAVE_PERL_VERSION(5, 16, 0)
40             # define UTF cBOOL((PL_linestr && DO_UTF8(PL_linestr)) || ( !(PL_parser->lex_flags & LEX_IGNORE_UTF8_HINTS) && (PL_hints & HINT_UTF8)))
41             #else
42             # define UTF cBOOL((PL_linestr && DO_UTF8(PL_linestr)) || (PL_hints & HINT_UTF8))
43             #endif
44              
45             #if HAVE_PERL_VERSION(5, 20, 0)
46             # define HAVE_UTF8f
47             #endif
48              
49             #define yycroak(s) S_yycroak(aTHX_ s)
50 3           static void S_yycroak(pTHX_ const char *s)
51             {
52 3           SV *message = sv_2mortal(newSVpvs_flags("", 0));
53              
54 3           char *context = PL_parser->oldbufptr;
55 3           STRLEN contlen = PL_parser->bufptr - PL_parser->oldbufptr;
56              
57 3 50         sv_catpvf(message, "%s at %s line %" IVdf,
58 9           s, OutCopFILE(PL_curcop), (IV)CopLINE(PL_curcop));
59              
60 3 50         if(context)
61             #ifdef HAVE_UTF8f
62 3 50         sv_catpvf(message, ", near \"%" UTF8f "\"", UTF8fARG(UTF, contlen, context));
    50          
    0          
    50          
    50          
63             #else
64             sv_catpvf(message, ", near \"%" SVf "\"", SVfARG(newSVpvn_flags(context, contlen, SVs_TEMP | (UTF ? SVf_UTF8 : 0))));
65             #endif
66              
67 3           sv_catpvf(message, "\n");
68              
69 3           PL_parser->error_count++;
70 3           croak_sv(message);
71             }
72              
73             #define yycroakf(fmt, ...) yycroak(Perl_form(aTHX_ fmt, __VA_ARGS__))
74              
75             #define lex_expect_unichar(c) MY_lex_expect_unichar(aTHX_ c)
76 23           void MY_lex_expect_unichar(pTHX_ int c)
77             {
78 23 50         if(lex_peek_unichar(0) != c)
79             /* TODO: A slightly different message if c == '\'' */
80 0           yycroakf("Expected '%c'", c);
81              
82 23           lex_read_unichar(0);
83 23           }
84              
85             #define CHECK_PARSEFAIL \
86             if(PL_parser->error_count) \
87             croak("parse failed--compilation aborted")
88              
89             /* TODO: Only ASCII */
90             #define lex_probe_str(s, b) MY_lex_probe_str(aTHX_ s, b)
91 43           STRLEN MY_lex_probe_str(pTHX_ const char *s, bool boundarycheck)
92             {
93             STRLEN i;
94 124 100         for(i = 0; s[i]; i++) {
95 101 100         if(s[i] != PL_parser->bufptr[i])
96             return 0;
97             }
98              
99 23 100         if(boundarycheck && isALNUM(PL_parser->bufptr[i]))
    50          
100             return 0;
101              
102 23           return i;
103             }
104              
105             #define lex_expect_str(s, b) MY_lex_expect_str(aTHX_ s, b)
106 7           void MY_lex_expect_str(pTHX_ const char *s, bool boundarycheck)
107             {
108 7           STRLEN len = lex_probe_str(s, boundarycheck);
109 7 50         if(!len)
110 0           yycroakf("Expected \"%s\"", s);
111              
112 7           lex_read_to(PL_parser->bufptr + len);
113 7           }
114              
115             #define parse_autosemi() MY_parse_autosemi(aTHX)
116 4           void MY_parse_autosemi(pTHX)
117             {
118 4           int c = lex_peek_unichar(0);
119 4 100         if(c == ';')
120 2           lex_read_unichar(0);
121 2 50         else if(!c || c == '}')
122             ; /* all is good */
123             else
124 0           yycroak("Expected: ';' or end of block");
125 4           }
126              
127             struct Registration;
128             struct Registration {
129             struct Registration *next;
130             const char *kwname;
131             STRLEN kwlen;
132              
133             int apiver;
134             const struct XSParseKeywordHooks *hooks;
135             void *hookdata;
136              
137             STRLEN permit_hintkey_len;
138             };
139              
140             /* version 1's struct did not have the line on it */
141             typedef struct
142             {
143             union {
144             OP *op;
145             CV *cv;
146             SV *sv;
147             int i;
148             struct { SV *name; SV *value; } attr;
149             PADOFFSET padix;
150             };
151             } XSParseKeywordPiece_v1;
152              
153             static bool probe_piece(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *piece, void *hookdata);
154             static void parse_piece(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *piece, void *hookdata);
155             static void parse_pieces(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *pieces, void *hookdata);
156              
157 81           static bool probe_piece(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *piece, void *hookdata)
158             {
159 81           int argi = *argidx;
160              
161 81 100         if(argi >= (SvLEN(argsv) / sizeof(XSParseKeywordPiece)))
162 1 50         SvGROW(argsv, SvLEN(argsv) * 2);
    50          
163              
164             #define THISARG ((XSParseKeywordPiece *)SvPVX(argsv))[argi]
165              
166 81 50         THISARG.line =
167             #if HAVE_PERL_VERSION(5, 20, 0)
168             /* on perl 5.20 onwards, CopLINE(PL_curcop) is only set at runtime; during
169             * parse the parser stores the line number directly */
170 81           (PL_parser->preambling != NOLINE) ? PL_parser->preambling :
171             #endif
172 81           CopLINE(PL_curcop);
173              
174 81           bool is_special = !!(piece->type & XPK_TYPEFLAG_SPECIAL);
175              
176 81           U32 type = piece->type & 0xFFFF;
177              
178 81           switch(type) {
179             case XS_PARSE_KEYWORD_LITERALCHAR:
180 9 100         if(lex_peek_unichar(0) != piece->u.c)
181             return FALSE;
182              
183 4           lex_read_unichar(0);
184 4           lex_read_space(0);
185 4           return TRUE;
186              
187             case XS_PARSE_KEYWORD_LITERALSTR:
188             {
189 36           STRLEN len = lex_probe_str(piece->u.str, is_special);
190 36 100         if(!len)
191             return FALSE;
192              
193 16           lex_read_to(PL_parser->bufptr + len);
194 16           lex_read_space(0);
195 16           return TRUE;
196             }
197              
198             case XS_PARSE_KEYWORD_FAILURE:
199 0           yycroak(piece->u.str);
200             NOT_REACHED;
201              
202             case XS_PARSE_KEYWORD_BLOCK:
203 4 100         if(lex_peek_unichar(0) != '{')
204             return FALSE;
205              
206 2           parse_piece(aTHX_ argsv, argidx, piece, hookdata);
207 2           return TRUE;
208              
209             case XS_PARSE_KEYWORD_IDENT:
210 5           THISARG.sv = lex_scan_ident();
211 5 100         if(!THISARG.sv)
212             return FALSE;
213 3           (*argidx)++;
214 3           return TRUE;
215              
216             case XS_PARSE_KEYWORD_PACKAGENAME:
217 2           THISARG.sv = lex_scan_packagename();
218 2 100         if(!THISARG.sv)
219             return FALSE;
220 1           (*argidx)++;
221 1           return TRUE;
222              
223             case XS_PARSE_KEYWORD_VSTRING:
224 2           THISARG.sv = lex_scan_version(PARSE_OPTIONAL);
225 2 100         if(!THISARG.sv)
226             return FALSE;
227              
228 1           (*argidx)++;
229 1           return TRUE;
230              
231             case XS_PARSE_KEYWORD_INFIX:
232             {
233 0 0         if(!XSParseInfix_parse(aTHX_ piece->u.c, &THISARG.infix))
234             return FALSE;
235 0           (*argidx)++;
236 0           return TRUE;
237             }
238              
239             case XS_PARSE_KEYWORD_SETUP:
240 0           croak("ARGH probe_piece() should never see XS_PARSE_KEYWORD_SETUP!");
241              
242             case XS_PARSE_KEYWORD_SEQUENCE:
243             {
244 0           const struct XSParseKeywordPieceType *pieces = piece->u.pieces;
245              
246 0 0         if(!probe_piece(aTHX_ argsv, argidx, pieces++, hookdata))
247             return FALSE;
248              
249 0           parse_pieces(aTHX_ argsv, argidx, pieces, hookdata);
250 0           return TRUE;
251             }
252              
253             case XS_PARSE_KEYWORD_CHOICE:
254             {
255 7           const struct XSParseKeywordPieceType *choices = piece->u.pieces;
256 7           THISARG.i = 0;
257 7           (*argidx)++; /* tentative */
258 19 100         while(choices->type) {
259 17 100         if(probe_piece(aTHX_ argsv, argidx, choices + 0, hookdata)) {
260             return TRUE;
261             }
262 12           choices++;
263 12           THISARG.i++;
264             }
265 2           (*argidx)--;
266 2           return FALSE;
267             }
268              
269             case XS_PARSE_KEYWORD_TAGGEDCHOICE:
270             {
271 5           const struct XSParseKeywordPieceType *choices = piece->u.pieces;
272 5           (*argidx)++; /* tentative */
273 10 100         while(choices->type) {
274 9 100         if(probe_piece(aTHX_ argsv, argidx, choices + 0, hookdata)) {
275 4           THISARG.i = choices[1].type;
276 4           return TRUE;
277             }
278 5           choices += 2;
279             }
280 1           (*argidx)--;
281 1           return FALSE;
282             }
283              
284             case XS_PARSE_KEYWORD_SEPARATEDLIST:
285             {
286 3           const struct XSParseKeywordPieceType *pieces = piece->u.pieces;
287 3           (*argidx)++; /* tentative */
288 3 100         if(!probe_piece(aTHX_ argsv, argidx, pieces + 1, hookdata)) {
289 1           (*argidx)--;
290 1           return FALSE;
291             }
292             /* we're now committed */
293 2           THISARG.i = 1;
294 2 50         if(pieces[2].type)
295 0           parse_pieces(aTHX_ argsv, argidx, pieces + 2, hookdata);
296              
297 2 100         if(!probe_piece(aTHX_ argsv, argidx, pieces + 0, hookdata))
298             return TRUE;
299              
300             while(1) {
301 1           parse_pieces(aTHX_ argsv, argidx, pieces + 1, hookdata);
302 1           THISARG.i++;
303              
304 1 50         if(!probe_piece(aTHX_ argsv, argidx, pieces + 0, hookdata))
305             break;
306             }
307             return TRUE;
308             }
309              
310             case XS_PARSE_KEYWORD_PARENSCOPE:
311 2 50         if(piece->type & XPK_TYPEFLAG_MAYBEPARENS)
312 0           croak("TODO: probe_piece on type=PARENSCOPE+MAYBEPARENS");
313              
314 2 100         if(lex_peek_unichar(0) != '(')
315             return FALSE;
316              
317 1           parse_piece(aTHX_ argsv, argidx, piece, hookdata);
318 1           return TRUE;
319              
320             case XS_PARSE_KEYWORD_BRACKETSCOPE:
321 2 100         if(lex_peek_unichar(0) != '[')
322             return FALSE;
323              
324 1           parse_piece(aTHX_ argsv, argidx, piece, hookdata);
325 1           return TRUE;
326              
327             case XS_PARSE_KEYWORD_BRACESCOPE:
328 2 100         if(lex_peek_unichar(0) != '{')
329             return FALSE;
330              
331 1           parse_piece(aTHX_ argsv, argidx, piece, hookdata);
332 1           return TRUE;
333              
334             case XS_PARSE_KEYWORD_CHEVRONSCOPE:
335 2 100         if(lex_peek_unichar(0) != '<')
336             return FALSE;
337              
338 1           parse_piece(aTHX_ argsv, argidx, piece, hookdata);
339 1           return TRUE;
340             }
341              
342 0           croak("TODO: probe_piece on type=%d\n", type);
343             }
344              
345 123           static void parse_piece(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *piece, void *hookdata)
346             {
347 123           int argi = *argidx;
348              
349 123 50         if(argi >= (SvLEN(argsv) / sizeof(XSParseKeywordPiece)))
350 0 0         SvGROW(argsv, SvLEN(argsv) * 2);
    0          
351              
352             #define THISARG ((XSParseKeywordPiece *)SvPVX(argsv))[argi]
353              
354 123 50         THISARG.line =
355             #if HAVE_PERL_VERSION(5, 20, 0)
356             /* on perl 5.20 onwards, CopLINE(PL_curcop) is only set at runtime; during
357             * parse the parser stores the line number directly */
358 123           (PL_parser->preambling != NOLINE) ? PL_parser->preambling :
359             #endif
360 123           CopLINE(PL_curcop);
361              
362 123           bool is_optional = !!(piece->type & XPK_TYPEFLAG_OPT);
363 123           bool is_special = !!(piece->type & XPK_TYPEFLAG_SPECIAL);
364             U8 want = 0;
365 123           switch(piece->type & (3 << 18)) {
366 0           case XPK_TYPEFLAG_G_VOID: want = G_VOID; break;
367 2           case XPK_TYPEFLAG_G_SCALAR: want = G_SCALAR; break;
368 1           case XPK_TYPEFLAG_G_LIST: want = G_LIST; break;
369             }
370 123           bool is_enterleave = !!(piece->type & XPK_TYPEFLAG_ENTERLEAVE);
371              
372 123           U32 type = piece->type & 0xFFFF;
373              
374 123           switch(type) {
375             case 0:
376             return;
377              
378             case XS_PARSE_KEYWORD_LITERALCHAR:
379 3           lex_expect_unichar(piece->u.c);
380 3           return;
381              
382             case XS_PARSE_KEYWORD_LITERALSTR:
383 7           lex_expect_str(piece->u.str, is_special);
384 7           return;
385              
386             case XS_PARSE_KEYWORD_AUTOSEMI:
387 2           parse_autosemi();
388 2           return;
389              
390             case XS_PARSE_KEYWORD_FAILURE:
391 0           yycroak(piece->u.str);
392             NOT_REACHED;
393              
394             case XS_PARSE_KEYWORD_BLOCK:
395             {
396 8 50         if(is_enterleave)
397 0           ENTER;
398              
399 8           I32 save_ix = block_start(1);
400              
401 8 100         if(piece->u.pieces) {
402             /* The prefix pieces */
403             const struct XSParseKeywordPieceType *pieces = piece->u.pieces;
404              
405 7 100         while(pieces->type) {
406 5 100         if(pieces->type == XS_PARSE_KEYWORD_SETUP)
407 1           (pieces->u.callback)(aTHX_ hookdata);
408             else {
409 4           parse_piece(aTHX_ argsv, argidx, pieces, hookdata);
410 4           lex_read_space(0);
411             }
412              
413 5           pieces++;
414             }
415              
416 2 100         if(*argidx > argi) {
417 1           argi = *argidx;
418              
419 1 50         if(argi >= (SvLEN(argsv) / sizeof(XSParseKeywordPiece)))
420 0 0         SvGROW(argsv, SvLEN(argsv) * 2);
    0          
421              
422 1           intro_my(); /* in case any of the pieces was XPK_LEXVAR_MY */
423             }
424             }
425              
426             /* TODO: Can we name the syntax keyword here to make a better message? */
427 8 50         if(lex_peek_unichar(0) != '{')
428 0           yycroak("Expected a block");
429              
430 8           OP *body = parse_block(0);
431 8 50         CHECK_PARSEFAIL;
432              
433 8           THISARG.op = block_end(save_ix, body);
434              
435 8 100         if(is_special)
436 2           THISARG.op = op_scope(THISARG.op);
437              
438 8 100         if(want)
439 2           THISARG.op = op_contextualize(THISARG.op, want);
440              
441 8           (*argidx)++;
442              
443 8 50         if(is_enterleave)
444 0           LEAVE;
445              
446             return;
447             }
448              
449             case XS_PARSE_KEYWORD_ANONSUB:
450             {
451 1           I32 floor_ix = start_subparse(FALSE, CVf_ANON);
452 1           SAVEFREESV(PL_compcv);
453              
454 1           I32 save_ix = block_start(0);
455 1           OP *body = parse_block(0);
456 1 50         CHECK_PARSEFAIL;
457              
458 1           SvREFCNT_inc(PL_compcv);
459 1           body = block_end(save_ix, body);
460              
461 1           THISARG.cv = newATTRSUB(floor_ix, NULL, NULL, NULL, body);
462 1           (*argidx)++;
463 1           return;
464             }
465              
466             case XS_PARSE_KEYWORD_ARITHEXPR:
467             case XS_PARSE_KEYWORD_TERMEXPR:
468             /* TODO: This auto-parens behaviour ought to be tuneable, depend on how
469             * many args, open at i=0 and close at i=MAX, etc...
470             */
471 19 100         if(lex_peek_unichar(0) == '(') {
472             /* consume a fullexpr and stop at the close paren */
473 2           lex_read_unichar(0);
474              
475 2           THISARG.op = parse_fullexpr(0);
476 2 50         CHECK_PARSEFAIL;
477              
478 2           lex_read_space(0);
479              
480 2           lex_expect_unichar(')');
481             }
482             else {
483 17           switch(type) {
484             case XS_PARSE_KEYWORD_ARITHEXPR:
485 3           THISARG.op = parse_arithexpr(0);
486 3           break;
487             case XS_PARSE_KEYWORD_TERMEXPR:
488 14           THISARG.op = parse_termexpr(0);
489 14           break;
490             }
491 17 50         CHECK_PARSEFAIL;
492             }
493              
494 19 100         if(want)
495 1           THISARG.op = op_contextualize(THISARG.op, want);
496              
497 19           (*argidx)++;
498 19           return;
499              
500             case XS_PARSE_KEYWORD_LISTEXPR:
501 3           THISARG.op = parse_listexpr(0);
502 3 50         CHECK_PARSEFAIL;
503              
504 3 50         if(want)
505 0           THISARG.op = op_contextualize(THISARG.op, want);
506              
507 3           (*argidx)++;
508 3           return;
509              
510             case XS_PARSE_KEYWORD_IDENT:
511 10           THISARG.sv = lex_scan_ident();
512 10 100         if(!THISARG.sv && !is_optional)
    100          
513 1           yycroak("Expected an identifier");
514 9           (*argidx)++;
515 9           return;
516              
517             case XS_PARSE_KEYWORD_PACKAGENAME:
518 2           THISARG.sv = lex_scan_packagename();
519 2 100         if(!THISARG.sv && !is_optional)
    50          
520 1           yycroak("Expected a package name");
521 1           (*argidx)++;
522 1           return;
523              
524             case XS_PARSE_KEYWORD_LEXVARNAME:
525             case XS_PARSE_KEYWORD_LEXVAR:
526             {
527             /* name vs. padix begin with similar structure */
528 5           SV *varname = lex_scan_lexvar();
529 5 50         if(!varname)
530 0           yycroak("Expected a lexical variable name");
531 5           switch(SvPVX(varname)[0]) {
532             case '$':
533 3 50         if(!(piece->u.c & XPK_LEXVAR_SCALAR))
534 0           yycroak("Lexical scalars are not permitted");
535             break;
536             case '@':
537 1 50         if(!(piece->u.c & XPK_LEXVAR_ARRAY))
538 0           yycroak("Lexical arrays are not permitted");
539             break;
540             case '%':
541 1 50         if(!(piece->u.c & XPK_LEXVAR_HASH))
542 0           yycroak("Lexical hashes are not permitted");
543             break;
544             }
545 5 100         if(type == XS_PARSE_KEYWORD_LEXVARNAME) {
546 3           THISARG.sv = varname;
547 3           (*argidx)++;
548 3           return;
549             }
550              
551 2           SAVEFREESV(varname);
552              
553             /* Forbid $_ / @_ / %_ */
554 2 50         if(SvCUR(varname) == 2 && SvPVX(varname)[1] == '_')
    0          
555 0           yycroakf("Can't use global %s in \"my\"", SvPVX(varname));
556              
557 2 50         if(is_special)
558 2           THISARG.padix = pad_add_name_pvn(SvPVX(varname), SvCUR(varname), 0, NULL, NULL);
559             else
560 0           yycroak("TODO: XS_PARSE_KEYWORD_LEXVAR without LEXVAR_MY");
561              
562 2           (*argidx)++;
563 2           return;
564             }
565              
566             case XS_PARSE_KEYWORD_ATTRS:
567             {
568 4           THISARG.i = 0;
569 4           (*argidx)++;
570              
571 4 100         if(lex_peek_unichar(0) == ':') {
572 3           lex_read_unichar(0);
573 3           lex_read_space(0);
574              
575 3           SV *attrname = newSV(0), *attrval = newSV(0);
576 3           SAVEFREESV(attrname); SAVEFREESV(attrval);
577              
578 13 100         while(lex_scan_attrval_into(attrname, attrval)) {
579 7           lex_read_space(0);
580              
581 7 50         if(*argidx >= (SvLEN(argsv) / sizeof(XSParseKeywordPiece)))
582 0 0         SvGROW(argsv, SvLEN(argsv) * 2);
    0          
583              
584 7           XSParseKeywordPiece *arg = &((XSParseKeywordPiece *)SvPVX(argsv))[*argidx];
585 7           arg->attr.name = newSVsv(attrname);
586 7           arg->attr.value = newSVsv(attrval);
587              
588 7           THISARG.i++;
589 7           (*argidx)++;
590              
591             /* Accept additional colons to prefix additional attrs, but do not require them */
592 7 100         if(lex_peek_unichar(0) == ':') {
593 2           lex_read_unichar(0);
594 5           lex_read_space(0);
595             }
596             }
597             }
598              
599             return;
600             }
601              
602             case XS_PARSE_KEYWORD_VSTRING:
603 3           THISARG.sv = lex_scan_version(is_optional ? PARSE_OPTIONAL : 0);
604 3           (*argidx)++;
605 3           return;
606              
607             case XS_PARSE_KEYWORD_INFIX:
608             {
609 4 100         if(!XSParseInfix_parse(aTHX_ piece->u.c, &THISARG.infix))
610 1           yycroak("Expected an infix operator");
611 3           (*argidx)++;
612 3           return;
613             }
614              
615             case XS_PARSE_KEYWORD_SETUP:
616 0           croak("ARGH parse_piece() should never see XS_PARSE_KEYWORD_SETUP!");
617              
618             case XS_PARSE_KEYWORD_SEQUENCE:
619             {
620 32           const struct XSParseKeywordPieceType *pieces = piece->u.pieces;
621              
622 32 100         if(is_optional) {
623 31           THISARG.i = 0;
624 31           (*argidx)++;
625 31 100         if(!probe_piece(aTHX_ argsv, argidx, pieces, hookdata))
626             return;
627 17           THISARG.i++;
628 17           pieces++;
629             }
630              
631 18           parse_pieces(aTHX_ argsv, argidx, pieces, hookdata);
632 18           return;
633             }
634              
635             case XS_PARSE_KEYWORD_REPEATED:
636 2           THISARG.i = 0;
637 2           (*argidx)++;
638 8 100         while(probe_piece(aTHX_ argsv, argidx, piece->u.pieces + 0, hookdata)) {
639 6           THISARG.i++;
640 6           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces + 1, hookdata);
641             }
642             return;
643              
644             case XS_PARSE_KEYWORD_CHOICE:
645             case XS_PARSE_KEYWORD_TAGGEDCHOICE:
646 6 100         if(!probe_piece(aTHX_ argsv, argidx, piece, hookdata)) {
647 1           THISARG.i = -1;
648 1           (*argidx)++;
649             }
650             return;
651              
652             case XS_PARSE_KEYWORD_SEPARATEDLIST:
653 2           THISARG.i = 0;
654 2           (*argidx)++;
655             while(1) {
656 4           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces + 1, hookdata);
657 4           THISARG.i++;
658              
659 4 100         if(!probe_piece(aTHX_ argsv, argidx, piece->u.pieces + 0, hookdata))
660             break;
661             }
662             return;
663              
664             case XS_PARSE_KEYWORD_PARENSCOPE:
665             {
666 4           bool has_paren = (lex_peek_unichar(0) == '(');
667              
668 4 50         if(is_optional) {
669 0           THISARG.i = 0;
670 0           (*argidx)++;
671 0 0         if(!has_paren) return;
672 0           THISARG.i++;
673             }
674              
675 4 100         if(has_paren) {
676 3           lex_expect_unichar('(');
677 3           lex_read_space(0);
678              
679 3           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
680              
681 3           lex_expect_unichar(')');
682             }
683 1 50         else if(piece->type & XPK_TYPEFLAG_MAYBEPARENS) {
684             /* We didn't find a '(' but that's OK; they're optional */
685 1           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
686             }
687             else
688             /* We know this should fail */
689 0           lex_expect_unichar('(');
690              
691             return;
692             }
693              
694             case XS_PARSE_KEYWORD_BRACKETSCOPE:
695 2 50         if(is_optional) {
696 0           THISARG.i = 0;
697 0           (*argidx)++;
698 0 0         if(lex_peek_unichar(0) != '[') return;
699 0           THISARG.i++;
700             }
701              
702 2           lex_expect_unichar('[');
703 2           lex_read_space(0);
704              
705 2           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
706              
707 2           lex_expect_unichar(']');
708              
709 2           return;
710              
711             case XS_PARSE_KEYWORD_BRACESCOPE:
712 2 50         if(is_optional) {
713 0           THISARG.i = 0;
714 0           (*argidx)++;
715 0 0         if(lex_peek_unichar(0) != '{') return;
716 0           THISARG.i++;
717             }
718              
719 2           lex_expect_unichar('{');
720 2           lex_read_space(0);
721              
722 2           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
723              
724 2           lex_expect_unichar('}');
725              
726 2           return;
727              
728             case XS_PARSE_KEYWORD_CHEVRONSCOPE:
729 2 50         if(is_optional) {
730 0           THISARG.i = 0;
731 0           (*argidx)++;
732 0 0         if(lex_peek_unichar(0) != '<') return;
733 0           THISARG.i++;
734             }
735              
736 2           lex_expect_unichar('<');
737 2           lex_read_space(0);
738              
739 2           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
740              
741 2           lex_expect_unichar('>');
742              
743 2           return;
744             }
745              
746 0           croak("TODO: parse_piece on type=%d\n", type);
747             }
748              
749 94           static void parse_pieces(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *pieces, void *hookdata)
750             {
751             size_t idx;
752 167 100         for(idx = 0; pieces[idx].type; idx++) {
753 73           parse_piece(aTHX_ argsv, argidx, pieces + idx, hookdata);
754 73           lex_read_space(0);
755             }
756 94           }
757              
758 97           static int parse(pTHX_ OP **op, struct Registration *reg)
759             {
760 97           const struct XSParseKeywordHooks *hooks = reg->hooks;
761              
762 97 100         if(hooks->parse)
763 2           return (*hooks->parse)(aTHX_ op, reg->hookdata);
764              
765             /* parse in pieces */
766              
767             /* use the PV buffer of this SV as a growable array of args */
768             size_t maxargs = 4;
769 95           SV *argsv = newSV(maxargs * sizeof(XSParseKeywordPiece));
770 95           SAVEFREESV(argsv);
771              
772 95           size_t argidx = 0;
773 95 100         if(hooks->build)
774 55           parse_pieces(aTHX_ argsv, &argidx, hooks->pieces, reg->hookdata);
775             else
776 40           parse_piece(aTHX_ argsv, &argidx, &hooks->piece1, reg->hookdata);
777              
778 92 100         if(hooks->flags & XPK_FLAG_AUTOSEMI) {
779 2           lex_read_space(0);
780              
781 2           parse_autosemi();
782             }
783              
784 92           XSParseKeywordPiece *args = (XSParseKeywordPiece *)SvPVX(argsv);
785              
786             int ret;
787 92 100         if(hooks->build) {
788             /* build function takes an array of pointers to piece structs, so we can
789             * add new fields to the end of them without breaking back-compat. */
790 55           SV *ptrssv = newSV(argidx * sizeof(XSParseKeywordPiece *));
791 55           XSParseKeywordPiece **argptrs = (XSParseKeywordPiece **)SvPVX(ptrssv);
792 55           SAVEFREESV(ptrssv);
793              
794             int i;
795 138 100         for(i = 0; i < argidx; i++)
796 83           argptrs[i] = &args[i];
797              
798 55           ret = (*hooks->build)(aTHX_ op, argptrs, argidx, reg->hookdata);
799             }
800 37 50         else if(reg->apiver < 2) {
801             /* version 1 ->build1 used to take a struct directly, not a pointer thereto */
802 0           int (*v1_build1)(pTHX_ OP **out, XSParseKeywordPiece_v1 arg0, void *hookdata) =
803             (int (*)())hooks->build1;
804             XSParseKeywordPiece_v1 arg0_v1;
805             Copy(args + 0, &arg0_v1, 1, XSParseKeywordPiece_v1);
806 0           ret = (*v1_build1)(aTHX_ op, arg0_v1, reg->hookdata);
807             }
808             else
809 37           ret = (*hooks->build1)(aTHX_ op, args + 0, reg->hookdata);
810              
811 92           switch(hooks->flags & (XPK_FLAG_EXPR|XPK_FLAG_STMT)) {
812             case XPK_FLAG_EXPR:
813 0 0         if(ret && (ret != KEYWORD_PLUGIN_EXPR))
814 0           yycroakf("Expected parse function for '%s' keyword to return KEYWORD_PLUGIN_EXPR but it did not",
815             reg->kwname);
816              
817             case XPK_FLAG_STMT:
818 2 50         if(ret && (ret != KEYWORD_PLUGIN_STMT))
819 0           yycroakf("Expected parse function for '%s' keyword to return KEYWORD_PLUGIN_STMT but it did not",
820             reg->kwname);
821             }
822              
823             return ret;
824             }
825              
826             static struct Registration *registrations;
827              
828 293           static void reg(pTHX_ const char *kwname, int apiver, const struct XSParseKeywordHooks *hooks, void *hookdata)
829             {
830 293 100         if(!hooks->build1 && !hooks->build && !hooks->parse)
    100          
    50          
831 0           croak("struct XSParseKeywordHooks requires either a .build1, a .build, or .parse stage");
832              
833             struct Registration *reg;
834 293           Newx(reg, 1, struct Registration);
835              
836 293           reg->kwname = savepv(kwname);
837 293           reg->kwlen = strlen(kwname);
838              
839 293           reg->apiver = apiver;
840 293           reg->hooks = hooks;
841 293           reg->hookdata = hookdata;
842              
843 293 50         if(hooks->permit_hintkey)
844 293           reg->permit_hintkey_len = strlen(hooks->permit_hintkey);
845              
846             {
847 293           reg->next = registrations;
848 293           registrations = reg;
849             }
850 293           }
851              
852 0           void XSParseKeyword_register_v1(pTHX_ const char *kwname, const struct XSParseKeywordHooks *hooks, void *hookdata)
853             {
854 0           reg(aTHX_ kwname, 1, hooks, hookdata);
855 0           }
856              
857 293           void XSParseKeyword_register_v2(pTHX_ const char *kwname, const struct XSParseKeywordHooks *hooks, void *hookdata)
858             {
859 293           reg(aTHX_ kwname, 2, hooks, hookdata);
860 293           }
861              
862             static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
863              
864 22750           static int my_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op)
865             {
866 22750 50         if(PL_parser && PL_parser->error_count)
    50          
867 0           return (*next_keyword_plugin)(aTHX_ kw, kwlen, op);
868              
869 22750           HV *hints = GvHV(PL_hintgv);
870              
871             struct Registration *reg;
872 327847 100         for(reg = registrations; reg; reg = reg->next) {
873 305194 100         if(reg->kwlen != kwlen || !strEQ(reg->kwname, kw))
    100          
874 305095           continue;
875              
876 99 50         if(reg->hooks->permit_hintkey &&
    50          
877 99 100         (!hints || !hv_fetch(hints, reg->hooks->permit_hintkey, reg->permit_hintkey_len, 0)))
878 1           continue;
879              
880 101           if(reg->hooks->permit &&
881 3           !(*reg->hooks->permit)(aTHX_ reg->hookdata))
882 1           continue;
883              
884 97 100         if(reg->hooks->check)
885 2           (*reg->hooks->check)(aTHX_ reg->hookdata);
886              
887 97           *op = NULL;
888              
889 97           lex_read_space(0);
890              
891 97           int ret = parse(aTHX_ op, reg);
892              
893 94           lex_read_space(0);
894              
895 94 50         if(ret && !*op)
    50          
896 0           *op = newOP(OP_NULL, 0);
897              
898             return ret;
899             }
900              
901 22653           return (*next_keyword_plugin)(aTHX_ kw, kwlen, op);
902             }
903              
904 22           void XSParseKeyword_boot(pTHX)
905             {
906             wrap_keyword_plugin(&my_keyword_plugin, &next_keyword_plugin);
907 22           }