File Coverage

src/keyword.c
Criterion Covered Total %
statement 359 419 85.6
branch 171 260 65.7
condition n/a
subroutine n/a
pod n/a
total 530 679 78.0


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 3           static void parse_prefix_pieces(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *pieces, void *hookdata)
346             {
347 9 100         while(pieces->type) {
348 6 100         if(pieces->type == XS_PARSE_KEYWORD_SETUP)
349 2           (pieces->u.callback)(aTHX_ hookdata);
350             else {
351 4           parse_piece(aTHX_ argsv, argidx, pieces, hookdata);
352 4           lex_read_space(0);
353             }
354              
355 6           pieces++;
356             }
357              
358 3           intro_my(); /* in case any of the pieces was XPK_LEXVAR_MY */
359 3           }
360              
361 124           static void parse_piece(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *piece, void *hookdata)
362             {
363 124           int argi = *argidx;
364              
365             #define CHECK_GROW_ARGSV \
366             do { \
367             if(argi >= (SvLEN(argsv) / sizeof(XSParseKeywordPiece))) \
368             SvGROW(argsv, SvLEN(argsv) * 2); \
369             } while(0)
370              
371             #define THISARG ((XSParseKeywordPiece *)SvPVX(argsv))[argi]
372              
373 124 50         CHECK_GROW_ARGSV;
    0          
    0          
374              
375 124 50         THISARG.line =
376             #if HAVE_PERL_VERSION(5, 20, 0)
377             /* on perl 5.20 onwards, CopLINE(PL_curcop) is only set at runtime; during
378             * parse the parser stores the line number directly */
379 124           (PL_parser->preambling != NOLINE) ? PL_parser->preambling :
380             #endif
381 124           CopLINE(PL_curcop);
382              
383 124           bool is_optional = !!(piece->type & XPK_TYPEFLAG_OPT);
384 124           bool is_special = !!(piece->type & XPK_TYPEFLAG_SPECIAL);
385             U8 want = 0;
386 124           switch(piece->type & (3 << 18)) {
387 0           case XPK_TYPEFLAG_G_VOID: want = G_VOID; break;
388 2           case XPK_TYPEFLAG_G_SCALAR: want = G_SCALAR; break;
389 1           case XPK_TYPEFLAG_G_LIST: want = G_LIST; break;
390             }
391 124           bool is_enterleave = !!(piece->type & XPK_TYPEFLAG_ENTERLEAVE);
392              
393 124           U32 type = piece->type & 0xFFFF;
394              
395 124           switch(type) {
396             case 0:
397             return;
398              
399             case XS_PARSE_KEYWORD_LITERALCHAR:
400 3           lex_expect_unichar(piece->u.c);
401 3           return;
402              
403             case XS_PARSE_KEYWORD_LITERALSTR:
404 7           lex_expect_str(piece->u.str, is_special);
405 7           return;
406              
407             case XS_PARSE_KEYWORD_AUTOSEMI:
408 2           parse_autosemi();
409 2           return;
410              
411             case XS_PARSE_KEYWORD_FAILURE:
412 0           yycroak(piece->u.str);
413             NOT_REACHED;
414              
415             case XS_PARSE_KEYWORD_BLOCK:
416             {
417 8 50         if(is_enterleave)
418 0           ENTER;
419              
420 8           I32 save_ix = block_start(1);
421              
422 8 100         if(piece->u.pieces) {
423 2           parse_prefix_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
424              
425 2 100         if(*argidx > argi) {
426 1           argi = *argidx;
427 1 50         CHECK_GROW_ARGSV;
    0          
    0          
428             }
429             }
430              
431             /* TODO: Can we name the syntax keyword here to make a better message? */
432 8 50         if(lex_peek_unichar(0) != '{')
433 0           yycroak("Expected a block");
434              
435 8           OP *body = parse_block(0);
436 8 50         CHECK_PARSEFAIL;
437              
438 8           THISARG.op = block_end(save_ix, body);
439              
440 8 100         if(is_special)
441 2           THISARG.op = op_scope(THISARG.op);
442              
443 8 100         if(want)
444 2           THISARG.op = op_contextualize(THISARG.op, want);
445              
446 8           (*argidx)++;
447              
448 8 50         if(is_enterleave)
449 0           LEAVE;
450              
451             return;
452             }
453              
454             case XS_PARSE_KEYWORD_ANONSUB:
455             {
456 1           I32 floor_ix = start_subparse(FALSE, CVf_ANON);
457 1           SAVEFREESV(PL_compcv);
458              
459 1           I32 save_ix = block_start(0);
460 1           OP *body = parse_block(0);
461 1 50         CHECK_PARSEFAIL;
462              
463 1           SvREFCNT_inc(PL_compcv);
464 1           body = block_end(save_ix, body);
465              
466 1           THISARG.cv = newATTRSUB(floor_ix, NULL, NULL, NULL, body);
467 1           (*argidx)++;
468 1           return;
469             }
470              
471             case XS_PARSE_KEYWORD_ARITHEXPR:
472             case XS_PARSE_KEYWORD_TERMEXPR:
473             {
474 20 100         if(is_enterleave)
475 1           ENTER;
476              
477 20 100         if(piece->u.pieces) {
478 1           parse_prefix_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
479              
480 1 50         if(*argidx > argi) {
481 0           argi = *argidx;
482 0 0         CHECK_GROW_ARGSV;
    0          
    0          
483             }
484             }
485              
486             /* TODO: This auto-parens behaviour ought to be tuneable, depend on how
487             * many args, open at i=0 and close at i=MAX, etc...
488             */
489 20 100         if(lex_peek_unichar(0) == '(') {
490             /* consume a fullexpr and stop at the close paren */
491 2           lex_read_unichar(0);
492              
493 2           THISARG.op = parse_fullexpr(0);
494 2 50         CHECK_PARSEFAIL;
495              
496 2           lex_read_space(0);
497              
498 2           lex_expect_unichar(')');
499             }
500             else {
501 18           switch(type) {
502             case XS_PARSE_KEYWORD_ARITHEXPR:
503 3           THISARG.op = parse_arithexpr(0);
504 3           break;
505             case XS_PARSE_KEYWORD_TERMEXPR:
506 15           THISARG.op = parse_termexpr(0);
507 15           break;
508             }
509 18 50         CHECK_PARSEFAIL;
510             }
511              
512 20 100         if(want)
513 1           THISARG.op = op_contextualize(THISARG.op, want);
514              
515 20           (*argidx)++;
516              
517 20 100         if(is_enterleave)
518 1           LEAVE;
519              
520             return;
521             }
522              
523             case XS_PARSE_KEYWORD_LISTEXPR:
524 3           THISARG.op = parse_listexpr(0);
525 3 50         CHECK_PARSEFAIL;
526              
527 3 50         if(want)
528 0           THISARG.op = op_contextualize(THISARG.op, want);
529              
530 3           (*argidx)++;
531 3           return;
532              
533             case XS_PARSE_KEYWORD_IDENT:
534 10           THISARG.sv = lex_scan_ident();
535 10 100         if(!THISARG.sv && !is_optional)
    100          
536 1           yycroak("Expected an identifier");
537 9           (*argidx)++;
538 9           return;
539              
540             case XS_PARSE_KEYWORD_PACKAGENAME:
541 2           THISARG.sv = lex_scan_packagename();
542 2 100         if(!THISARG.sv && !is_optional)
    50          
543 1           yycroak("Expected a package name");
544 1           (*argidx)++;
545 1           return;
546              
547             case XS_PARSE_KEYWORD_LEXVARNAME:
548             case XS_PARSE_KEYWORD_LEXVAR:
549             {
550             /* name vs. padix begin with similar structure */
551 5           SV *varname = lex_scan_lexvar();
552 5 50         if(!varname)
553 0           yycroak("Expected a lexical variable name");
554 5           switch(SvPVX(varname)[0]) {
555             case '$':
556 3 50         if(!(piece->u.c & XPK_LEXVAR_SCALAR))
557 0           yycroak("Lexical scalars are not permitted");
558             break;
559             case '@':
560 1 50         if(!(piece->u.c & XPK_LEXVAR_ARRAY))
561 0           yycroak("Lexical arrays are not permitted");
562             break;
563             case '%':
564 1 50         if(!(piece->u.c & XPK_LEXVAR_HASH))
565 0           yycroak("Lexical hashes are not permitted");
566             break;
567             }
568 5 100         if(type == XS_PARSE_KEYWORD_LEXVARNAME) {
569 3           THISARG.sv = varname;
570 3           (*argidx)++;
571 3           return;
572             }
573              
574 2           SAVEFREESV(varname);
575              
576             /* Forbid $_ / @_ / %_ */
577 2 50         if(SvCUR(varname) == 2 && SvPVX(varname)[1] == '_')
    0          
578 0           yycroakf("Can't use global %s in \"my\"", SvPVX(varname));
579              
580 2 50         if(is_special)
581 2           THISARG.padix = pad_add_name_pvn(SvPVX(varname), SvCUR(varname), 0, NULL, NULL);
582             else
583 0           yycroak("TODO: XS_PARSE_KEYWORD_LEXVAR without LEXVAR_MY");
584              
585 2           (*argidx)++;
586 2           return;
587             }
588              
589             case XS_PARSE_KEYWORD_ATTRS:
590             {
591 4           THISARG.i = 0;
592 4           (*argidx)++;
593              
594 4 100         if(lex_peek_unichar(0) == ':') {
595 3           lex_read_unichar(0);
596 3           lex_read_space(0);
597              
598 3           SV *attrname = newSV(0), *attrval = newSV(0);
599 3           SAVEFREESV(attrname); SAVEFREESV(attrval);
600              
601 13 100         while(lex_scan_attrval_into(attrname, attrval)) {
602 7           lex_read_space(0);
603              
604 7 50         if(*argidx >= (SvLEN(argsv) / sizeof(XSParseKeywordPiece)))
605 0 0         SvGROW(argsv, SvLEN(argsv) * 2);
    0          
606              
607 7           XSParseKeywordPiece *arg = &((XSParseKeywordPiece *)SvPVX(argsv))[*argidx];
608 7           arg->attr.name = newSVsv(attrname);
609 7           arg->attr.value = newSVsv(attrval);
610              
611 7           THISARG.i++;
612 7           (*argidx)++;
613              
614             /* Accept additional colons to prefix additional attrs, but do not require them */
615 7 100         if(lex_peek_unichar(0) == ':') {
616 2           lex_read_unichar(0);
617 5           lex_read_space(0);
618             }
619             }
620             }
621              
622             return;
623             }
624              
625             case XS_PARSE_KEYWORD_VSTRING:
626 3           THISARG.sv = lex_scan_version(is_optional ? PARSE_OPTIONAL : 0);
627 3           (*argidx)++;
628 3           return;
629              
630             case XS_PARSE_KEYWORD_INFIX:
631             {
632 4 100         if(!XSParseInfix_parse(aTHX_ piece->u.c, &THISARG.infix))
633 1           yycroak("Expected an infix operator");
634 3           (*argidx)++;
635 3           return;
636             }
637              
638             case XS_PARSE_KEYWORD_SETUP:
639 0           croak("ARGH parse_piece() should never see XS_PARSE_KEYWORD_SETUP!");
640              
641             case XS_PARSE_KEYWORD_SEQUENCE:
642             {
643 32           const struct XSParseKeywordPieceType *pieces = piece->u.pieces;
644              
645 32 100         if(is_optional) {
646 31           THISARG.i = 0;
647 31           (*argidx)++;
648 31 100         if(!probe_piece(aTHX_ argsv, argidx, pieces, hookdata))
649             return;
650 17           THISARG.i++;
651 17           pieces++;
652             }
653              
654 18           parse_pieces(aTHX_ argsv, argidx, pieces, hookdata);
655 18           return;
656             }
657              
658             case XS_PARSE_KEYWORD_REPEATED:
659 2           THISARG.i = 0;
660 2           (*argidx)++;
661 8 100         while(probe_piece(aTHX_ argsv, argidx, piece->u.pieces + 0, hookdata)) {
662 6           THISARG.i++;
663 6           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces + 1, hookdata);
664             }
665             return;
666              
667             case XS_PARSE_KEYWORD_CHOICE:
668             case XS_PARSE_KEYWORD_TAGGEDCHOICE:
669 6 100         if(!probe_piece(aTHX_ argsv, argidx, piece, hookdata)) {
670 1           THISARG.i = -1;
671 1           (*argidx)++;
672             }
673             return;
674              
675             case XS_PARSE_KEYWORD_SEPARATEDLIST:
676 2           THISARG.i = 0;
677 2           (*argidx)++;
678             while(1) {
679 4           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces + 1, hookdata);
680 4           THISARG.i++;
681              
682 4 100         if(!probe_piece(aTHX_ argsv, argidx, piece->u.pieces + 0, hookdata))
683             break;
684             }
685             return;
686              
687             case XS_PARSE_KEYWORD_PARENSCOPE:
688             {
689 4           bool has_paren = (lex_peek_unichar(0) == '(');
690              
691 4 50         if(is_optional) {
692 0           THISARG.i = 0;
693 0           (*argidx)++;
694 0 0         if(!has_paren) return;
695 0           THISARG.i++;
696             }
697              
698 4 100         if(has_paren) {
699 3           lex_expect_unichar('(');
700 3           lex_read_space(0);
701              
702 3           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
703              
704 3           lex_expect_unichar(')');
705             }
706 1 50         else if(piece->type & XPK_TYPEFLAG_MAYBEPARENS) {
707             /* We didn't find a '(' but that's OK; they're optional */
708 1           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
709             }
710             else
711             /* We know this should fail */
712 0           lex_expect_unichar('(');
713              
714             return;
715             }
716              
717             case XS_PARSE_KEYWORD_BRACKETSCOPE:
718 2 50         if(is_optional) {
719 0           THISARG.i = 0;
720 0           (*argidx)++;
721 0 0         if(lex_peek_unichar(0) != '[') return;
722 0           THISARG.i++;
723             }
724              
725 2           lex_expect_unichar('[');
726 2           lex_read_space(0);
727              
728 2           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
729              
730 2           lex_expect_unichar(']');
731              
732 2           return;
733              
734             case XS_PARSE_KEYWORD_BRACESCOPE:
735 2 50         if(is_optional) {
736 0           THISARG.i = 0;
737 0           (*argidx)++;
738 0 0         if(lex_peek_unichar(0) != '{') return;
739 0           THISARG.i++;
740             }
741              
742 2           lex_expect_unichar('{');
743 2           lex_read_space(0);
744              
745 2           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
746              
747 2           lex_expect_unichar('}');
748              
749 2           return;
750              
751             case XS_PARSE_KEYWORD_CHEVRONSCOPE:
752 2 50         if(is_optional) {
753 0           THISARG.i = 0;
754 0           (*argidx)++;
755 0 0         if(lex_peek_unichar(0) != '<') return;
756 0           THISARG.i++;
757             }
758              
759 2           lex_expect_unichar('<');
760 2           lex_read_space(0);
761              
762 2           parse_pieces(aTHX_ argsv, argidx, piece->u.pieces, hookdata);
763              
764 2           lex_expect_unichar('>');
765              
766 2           return;
767             }
768              
769 0           croak("TODO: parse_piece on type=%d\n", type);
770             }
771              
772 94           static void parse_pieces(pTHX_ SV *argsv, size_t *argidx, const struct XSParseKeywordPieceType *pieces, void *hookdata)
773             {
774             size_t idx;
775 167 100         for(idx = 0; pieces[idx].type; idx++) {
776 73           parse_piece(aTHX_ argsv, argidx, pieces + idx, hookdata);
777 73           lex_read_space(0);
778             }
779 94           }
780              
781 98           static int parse(pTHX_ OP **op, struct Registration *reg)
782             {
783 98           const struct XSParseKeywordHooks *hooks = reg->hooks;
784              
785 98 100         if(hooks->parse)
786 2           return (*hooks->parse)(aTHX_ op, reg->hookdata);
787              
788             /* parse in pieces */
789              
790             /* use the PV buffer of this SV as a growable array of args */
791             size_t maxargs = 4;
792 96           SV *argsv = newSV(maxargs * sizeof(XSParseKeywordPiece));
793 96           SAVEFREESV(argsv);
794              
795 96           size_t argidx = 0;
796 96 100         if(hooks->build)
797 55           parse_pieces(aTHX_ argsv, &argidx, hooks->pieces, reg->hookdata);
798             else
799 41           parse_piece(aTHX_ argsv, &argidx, &hooks->piece1, reg->hookdata);
800              
801 93 100         if(hooks->flags & XPK_FLAG_AUTOSEMI) {
802 2           lex_read_space(0);
803              
804 2           parse_autosemi();
805             }
806              
807 93           XSParseKeywordPiece *args = (XSParseKeywordPiece *)SvPVX(argsv);
808              
809             int ret;
810 93 100         if(hooks->build) {
811             /* build function takes an array of pointers to piece structs, so we can
812             * add new fields to the end of them without breaking back-compat. */
813             XSParseKeywordPiece **argptrs = NULL;
814 55 50         if(argidx) {
815 55           SV *ptrssv = newSV(argidx * sizeof(XSParseKeywordPiece *));
816 55           SAVEFREESV(ptrssv);
817              
818 55           argptrs = (XSParseKeywordPiece **)SvPVX(ptrssv);
819             }
820              
821             int i;
822 138 100         for(i = 0; i < argidx; i++)
823 83           argptrs[i] = &args[i];
824              
825 55           ret = (*hooks->build)(aTHX_ op, argptrs, argidx, reg->hookdata);
826             }
827 38 50         else if(reg->apiver < 2) {
828             /* version 1 ->build1 used to take a struct directly, not a pointer thereto */
829 0           int (*v1_build1)(pTHX_ OP **out, XSParseKeywordPiece_v1 arg0, void *hookdata) =
830             (int (*)())hooks->build1;
831             XSParseKeywordPiece_v1 arg0_v1;
832             Copy(args + 0, &arg0_v1, 1, XSParseKeywordPiece_v1);
833 0           ret = (*v1_build1)(aTHX_ op, arg0_v1, reg->hookdata);
834             }
835             else
836 38           ret = (*hooks->build1)(aTHX_ op, args + 0, reg->hookdata);
837              
838 93           switch(hooks->flags & (XPK_FLAG_EXPR|XPK_FLAG_STMT)) {
839             case XPK_FLAG_EXPR:
840 0 0         if(ret && (ret != KEYWORD_PLUGIN_EXPR))
841 0           yycroakf("Expected parse function for '%s' keyword to return KEYWORD_PLUGIN_EXPR but it did not",
842             reg->kwname);
843             break;
844              
845             case XPK_FLAG_STMT:
846 2 50         if(ret && (ret != KEYWORD_PLUGIN_STMT))
847 0           yycroakf("Expected parse function for '%s' keyword to return KEYWORD_PLUGIN_STMT but it did not",
848             reg->kwname);
849             break;
850             }
851              
852             return ret;
853             }
854              
855             static struct Registration *registrations;
856              
857 304           static void reg(pTHX_ const char *kwname, int apiver, const struct XSParseKeywordHooks *hooks, void *hookdata)
858             {
859 304 100         if(!hooks->build1 && !hooks->build && !hooks->parse)
    100          
    50          
860 0           croak("struct XSParseKeywordHooks requires either a .build1, a .build, or .parse stage");
861              
862             struct Registration *reg;
863 304           Newx(reg, 1, struct Registration);
864              
865 304           reg->kwname = savepv(kwname);
866 304           reg->kwlen = strlen(kwname);
867              
868 304           reg->apiver = apiver;
869 304           reg->hooks = hooks;
870 304           reg->hookdata = hookdata;
871              
872 304 50         if(hooks->permit_hintkey)
873 304           reg->permit_hintkey_len = strlen(hooks->permit_hintkey);
874              
875             {
876 304           reg->next = registrations;
877 304           registrations = reg;
878             }
879 304           }
880              
881 0           void XSParseKeyword_register_v1(pTHX_ const char *kwname, const struct XSParseKeywordHooks *hooks, void *hookdata)
882             {
883 0           reg(aTHX_ kwname, 1, hooks, hookdata);
884 0           }
885              
886 304           void XSParseKeyword_register_v2(pTHX_ const char *kwname, const struct XSParseKeywordHooks *hooks, void *hookdata)
887             {
888 304           reg(aTHX_ kwname, 2, hooks, hookdata);
889 304           }
890              
891             static int (*next_keyword_plugin)(pTHX_ char *, STRLEN, OP **);
892              
893 22753           static int my_keyword_plugin(pTHX_ char *kw, STRLEN kwlen, OP **op)
894             {
895 22753 50         if(PL_parser && PL_parser->error_count)
    50          
896 0           return (*next_keyword_plugin)(aTHX_ kw, kwlen, op);
897              
898 22753           HV *hints = GvHV(PL_hintgv);
899              
900             struct Registration *reg;
901 339340 100         for(reg = registrations; reg; reg = reg->next) {
902 316685 100         if(reg->kwlen != kwlen || !strEQ(reg->kwname, kw))
    100          
903 316585           continue;
904              
905 100 50         if(reg->hooks->permit_hintkey &&
    50          
906 100 100         (!hints || !hv_fetch(hints, reg->hooks->permit_hintkey, reg->permit_hintkey_len, 0)))
907 1           continue;
908              
909 102           if(reg->hooks->permit &&
910 3           !(*reg->hooks->permit)(aTHX_ reg->hookdata))
911 1           continue;
912              
913 98 100         if(reg->hooks->check)
914 2           (*reg->hooks->check)(aTHX_ reg->hookdata);
915              
916 98           *op = NULL;
917              
918 98           lex_read_space(0);
919              
920 98           int ret = parse(aTHX_ op, reg);
921              
922 95           lex_read_space(0);
923              
924 95 50         if(ret && !*op)
    50          
925 0           *op = newOP(OP_NULL, 0);
926              
927             return ret;
928             }
929              
930 22655           return (*next_keyword_plugin)(aTHX_ kw, kwlen, op);
931             }
932              
933 22           void XSParseKeyword_boot(pTHX)
934             {
935             wrap_keyword_plugin(&my_keyword_plugin, &next_keyword_plugin);
936 22           }