File Coverage

blib/lib/Keyword/Declare.pm
Criterion Covered Total %
statement 366 446 82.0
branch 130 174 74.7
condition 39 69 56.5
subroutine 33 35 94.2
pod n/a
total 568 724 78.4


line stmt bran cond sub pod time code
1             package Keyword::Declare;
2             our $VERSION = '0.001017';
3              
4 19     19   1227551 use 5.012; # required for pluggable keywords plus /.../r
  19         214  
5 19     19   107 use warnings;
  19         39  
  19         569  
6 19     19   102 use Carp;
  19         37  
  19         1658  
7 19     19   144 use List::Util 1.45 'max', 'uniqstr';
  19         440  
  19         2231  
8              
9 19     19   6891 use Keyword::Simple;
  19         355744  
  19         686  
10 19     19   13883 use PPR;
  19         773446  
  19         5083  
11              
12             my $NESTING_THRESHOLD = 100; # How many nested keyword expansions is probably too many?
13              
14             my $TYPE_JUNCTION = qr{ [^\W\d] \w*+ (?: [|] [^\W\d] \w*+ )*+ }x; # How to match a TypeA|TypeB type
15              
16             my @keyword_impls; # Tracks all keyword information in every scope
17              
18             sub import {
19 19     19   299 my (undef, $opt_ref) = @_;
20 19   50     211 $opt_ref //= {};
21              
22             # Don't allow bad arguments to be passed when the module is loaded...
23 19         67 my $arg_type = ref($opt_ref);
24 19 50 33     196 if (@_ > 2 || $arg_type ne 'HASH') {
25 0   0     0 $arg_type ||= $opt_ref;
26 0         0 croak "Invalid option for: use Keyword::Declare.\n",
27             "Expected single hash reference, but found $arg_type instead.\n",
28             "Error detected";
29             }
30              
31             # If debugging requested, set in on for the caller's lexical scope...
32 19 50       93 if ($opt_ref->{debug}) {
33 0         0 ${^H}{'Keyword::Declare debug'} = !!$opt_ref->{debug};
34             }
35              
36             # Install replacement __DATA__ handler...
37             # [REMOVE IF UPSTREAM MODULE (Keyword::Simple) FIXED]
38 19         84 _install_data_handler();
39              
40             # Install the 'keytype' (meta-)keyword...
41             Keyword::Simple::define 'keytype', sub {
42             # Unpack trailing code...
43 10     10   711 my ($src_ref) = @_;
44              
45             # Where was this keyword declared???
46 10         62 my ($file, $line) = (caller)[1,2];
47              
48             # These track error messages and help decompose the parameter list...
49             # (they have to be package vars, so they're visible to in-regex code blocks in older Perls)
50 10         78 our ($expected, $failed_at, $block_start, @params) = ('new type name', 0, 0);
51              
52             # Match and extract the keyword definition...
53 19     19   403 use re 'eval';
  19         57  
  19         11820  
54 10 50       357024 $$src_ref =~ s{
55             \A
56             (?
57             (?&PerlNWS)
58 10         66 (?{ $expected = "new type name"; $failed_at = pos() })
  10         203  
59             (? \$?+ )
60             (? (?&PerlIdentifier) )
61             (?&PerlOWS)
62 10         32 (?{ $expected = "'is '"; $failed_at = pos() })
  10         100  
63             is
64             (?&PerlOWS)
65 10         21 (?{ $expected = "existing typename or literal string or regex after 'is'"; $failed_at = pos() })
  10         542  
66             (?
67             (? (?&PerlMatch) )
68             |
69             (? (?&PerlString) )
70             |
71             (? $TYPE_JUNCTION )
72             )
73             )
74              
75             $PPR::GRAMMAR
76             }{}xms
77             or croak "Invalid keytype definition. Expected $expected\nbut found: ",
78             substr($$src_ref, $failed_at) =~ /(\S+)/;
79              
80             # Save the information from the keyword definition...
81 10         3646 my %keytype_info = ( %+, location => "$file line $line", hashline => "$line $file" );
82              
83             # Set up the sigil...
84 10         55 my $sigil_decl = q{};
85 10 100       49 if ($keytype_info{typesigil}) {
86 3         14 my $var = qq{$keytype_info{typesigil}$keytype_info{newtype}};
87 3 100       19 if ($keytype_info{oldtyperegex}) {
    50          
88 2         12 $keytype_info{oldtyperegex} =~ s{^m}{};
89 2 100       18 if ($keytype_info{oldtyperegex} =~ /\(\?\&Perl[A-Z]/) {
90 1         11 $keytype_info{oldtyperegex} =~ s{^\s*(\S)}{$1\$PPR::GRAMMAR};
91             }
92 2         14 $sigil_decl = qq{my $var; BEGIN { $var = qr$keytype_info{oldtyperegex} }}
93             }
94             elsif ($keytype_info{oldtypestring}) {
95 1         6 $sigil_decl = qq{my $var; BEGIN { $var = $keytype_info{oldtypestring} }}
96             }
97             else {
98 0         0 croak "Invalid keytype definition. Can only specify a sigil on new typename ($keytype_info{typesigil}$keytype_info{newtype}) if type is specified as a string or regex";
99             }
100             }
101              
102             # Debug, if requested...
103 10 50       49 if (${^H}{"Keyword::Declare debug"}) {
104 0         0 my $msg = ("#" x 50) . "\n"
105             . " Installed keytype at $keytype_info{location}:\n\n$keytype_info{syntax}\n\n"
106             . ("#" x 50) . "\n";
107 0         0 $msg =~ s{^}{###}gm;
108 0         0 warn $msg;
109             }
110              
111             # Install the lexical type definition...
112 10         1397 $$src_ref
113             = qq{BEGIN{\$^H{q{Keyword::Declare keytype:$keytype_info{newtype}=$keytype_info{oldtype}}} = 1;}}
114             . $sigil_decl
115             . $$src_ref;
116 19         220 };
117              
118             # Install the 'keyword' (meta-)keyword...
119             Keyword::Simple::define 'keyword', sub {
120             # Unpack trailing code...
121 157     157   36753 my ($src_ref) = @_;
122              
123             # Where was this keyword declared???
124 157         758 my ($file, $line) = (caller)[1,2];
125              
126             # Which keywords are allowed in nested code at this point...
127 157         1412 my @active_IDs = @^H{ grep { m{^ Keyword::Declare \s+ active:}xms } keys %^H };
  5832         14238  
128             my $lexical_keywords
129 157 100       837 = @active_IDs ? join '|', reverse sort map { $keyword_impls[$_]{skip_matcher} } @active_IDs
  5642         12561  
130             : '(?!)';
131              
132             # These track error messages and help decompose the parameter list...
133             # (they have to be package vars, so they're visible to in-regex code blocks in older Perls)
134 157         726 our ($expected, $failed_at, $block_start, @params) = ('keyword name', 0, 0);
135              
136             # Match and extract the keyword definition...
137 19     19   160 use re 'eval';
  19         45  
  19         14160  
138 157 50       5909440 $$src_ref =~ s{
139             \A
140             (?<____K_D___KeywordDeclaration>
141             (?&PerlNWS)
142             (?<____K_D___keyword> (?&PerlIdentifier) )
143             (?&PerlOWS)
144 157         1103 (?{ $expected = "keyword parameters or block, or 'from' specifier"; $failed_at = pos() })
  157         1852  
145             (?<____K_D___params> (?&____K_D___ParamList) )
146             (?&PerlOWS)
147 157         753 (?{ $expected = 'keyword block or attribute'; $failed_at = pos() })
  157         1584  
148             (?<____K_D___attrs> (?&PerlAttributes)?+ )
149             (?&PerlOWS)
150 157         272 (?{ $expected = 'keyword block'; $failed_at = $block_start = pos() })
  157         16361  
151             (?<____K_D___block> \{\{\{ .*? \}\}\} | (?&PerlBlock) )
152             )
153              
154             (?(DEFINE)
155             (?<____K_D___ParamList>
156             \(
157             (?&____K_D___ParamSet)?+
158             (?:
159             (?&PerlOWS) \) (?&PerlOWS) :then\(
160 1         13 (?{ push @Keyword::Declare::params, undef; })
161             (?&____K_D___ParamSet)
162             )?+
163             \)
164             |
165             # Nothing
166             )
167              
168             (?<____K_D___ParamSet>
169             (?&PerlOWS) (?&____K_D___Param)
170             (?: (?&PerlOWS) , (?&PerlOWS) (?&____K_D___Param) )*+
171             ,?+ (?&PerlOWS)
172             )
173              
174             (?<____K_D___Param>
175 180         406 (?{ $expected = 'keyword parameter type'; $failed_at = pos() })
  180         1925  
176             (?<____K_D___type> (?&PerlMatch) | (?&PerlString) | $TYPE_JUNCTION )
177              
178 176         355 (?{ $expected = 'keyword parameter quantifier or variable'; $failed_at = pos() })
  176         2745  
179             (?: (?&PerlOWS) (?<____K_D___quantifier> [?*+][?+]?+ ) )?+
180              
181 176         332 (?{ $expected = 'keyword parameter variable'; $failed_at = pos() })
  176         4633  
182             (?: (?&PerlOWS) (?<____K_D___sigil> [\$\@] )
183             (?<____K_D___name> (?&PerlIdentifier) ) )?+
184              
185             (?: (?&PerlOWS) :sep \(
186 2         4 (?{ $expected = 'keyword parameter separator :sep'; $failed_at = pos() })
  2         68  
187             (?&PerlOWS) (?<____K_D___sep> (?&PerlMatch) | (?&PerlString) | $TYPE_JUNCTION )
188             (?&PerlOWS) \)
189             )?+
190              
191             (?: (?&PerlOWS) =
192 0         0 (?{ $expected = 'keyword parameter default string after ='; $failed_at = pos() })
  0         0  
193             (?&PerlOWS) (?<____K_D___default> (?&PerlQuotelikeQ) ) )?+
194              
195 176         13400 (?{ push @Keyword::Declare::params, { %+ } })
196             )
197              
198              
199             (?
200             keyword (?&____K_D___KeywordDeclaration)
201             |
202             $lexical_keywords
203             )
204             )
205              
206             $PPR::GRAMMAR
207             }{}xms
208             or croak "Invalid keyword definition. Expected $expected\nbut found: ",
209             substr($$src_ref, $failed_at) =~ /(\S+)/;
210              
211             # Save the information from the keyword definition..
212 157         41931 @Keyword::Declare::params = map { _deprefix($_) } @Keyword::Declare::params;
  177         783  
213 157         4387 my %keyword_info = %+;
214 157         544 %keyword_info = ( %{ _deprefix(\%keyword_info) },
215             desc => $keyword_info{____K_D___keyword},
216 157         732 param_list => [ @Keyword::Declare::params],
217             location => "$file line $line",
218             hashline => "$line $file",
219             );
220              
221             # Check for excessive meta-ness...
222 157 50       935 if ($keyword_info{keyword} =~ /^(?:keyword|keytype)$/) {
223 0         0 croak "Can't redefine '$keyword_info{keyword}' keyword";
224             }
225              
226             # Remember where the block started...
227             my $block_location
228 157         989 = ($line + substr($keyword_info{KeywordDeclaration}, 0, $block_start) =~ tr/\n//) . " $file";
229              
230             # Convert any {{{...}}} block...
231 157 100       543 if ($keyword_info{block} =~ m{ \A \{\{\{ }xms) {
232 23         109 $keyword_info{block} = _convert_triple_block(\%keyword_info);
233             }
234              
235             # Extract and verify any attributes...
236 157         605 _unpack_attrs(\%keyword_info);
237              
238             # Extract various useful components from the parameter list...
239 157         555 _unpack_signature(\%keyword_info);
240              
241             # Prepare for a trailing #line directive to keep trailing line numbers straight...
242 157         534 $line += $keyword_info{KeywordDeclaration} =~ tr/\n//;
243              
244             # Record the keyword definition...
245 157         478 my $keyword_ID = $keyword_info{ID} = @keyword_impls;
246 157         370 push @keyword_impls, \%keyword_info;
247              
248             # Create the keyword dispatching function...
249 157         481 my $keyword_defn = _build_keyword_code(\%keyword_info);
250              
251             # Report installation of keyword if requested...
252 157 50       523 if (${^H}{"Keyword::Declare debug"}) {
253 0         0 my $msg = ("#" x 50) . "\n"
254             . " Installed keyword macro at $keyword_info{location}:\n\n$keyword_info{syntax}\n\n"
255             . ("#" x 50) . "\n";
256 0         0 $msg =~ s{^}{###}gm;
257 0         0 warn $msg;
258             }
259              
260             # Install the keyword, exporting it as well if it's in an import() or unimport() sub...
261 157         1618 $$src_ref = qq{ if (((caller 0)[3]//q{}) =~ /\\b(?:un)?import\\Z/) { $keyword_defn } }
262             . q{ Keyword::Declare::_install_data_handler(); }
263             . qq{ BEGIN{ $keyword_defn } }
264             . "\n#line $line $file\n"
265             . $$src_ref;
266              
267             # Pre-empt addition of extraneous trailing newline by Keyword::Simple...
268             # [REMOVE IF UPSTREAM MODULE (Keyword::Simple) IS FIXED]
269 157         87311 $$src_ref =~ s{\n\z}{};
270 19         670 };
271              
272             # Install the 'unkeyword' (anti-meta-)keyword...
273             Keyword::Simple::define 'unkeyword', sub {
274             # Unpack trailing code...
275 1     1   7 my ($src_ref) = @_;
276              
277             # Where was this keyword declared???
278 1         9 my ($file, $line) = (caller)[1,2];
279              
280             # Match and extract the keyword definition...
281 19     19   173 use re 'eval';
  19         56  
  19         9553  
282 1         34241 $$src_ref =~ s{
283             \A
284             (? (?&PerlNWS) )
285             (?:
286             (? (?&PerlIdentifier) )
287             |
288             (? \S+ )
289             )
290              
291             $PPR::GRAMMAR
292             }{}xms;
293              
294 1         107 my %keyword_info = %+;
295              
296             croak "Invalid unkeyword definition. Expected keyword name (identifier)\n"
297             . " but found: $keyword_info{unexpected}"
298 1 50       8 if defined $keyword_info{unexpected};
299              
300             # Check for excessive meta-ness...
301 1 50       7 if ($keyword_info{keyword} =~ /^(?:keyword|keytype)$/) {
302 0         0 croak "Can't undefine '$keyword_info{keyword}' keyword";
303             }
304              
305             # Report installation of keyword if requested...
306 1 50       6 if (${^H}{"Keyword::Declare debug"}) {
307 0         0 my $msg = ("#" x 50) . "\n"
308             . " Uninstalled keyword macro: $keyword_info{keyword}(...)\n"
309             . " at $file line $line\n"
310             . ("#" x 50) . "\n";
311 0         0 $msg =~ s{^}{###}gm;
312 0         0 warn $msg;
313             }
314              
315             # How to remove the Keyword::Simple keyword (with workaround for earlier versions)...
316 1         3 my $keyword_defn = q{Keyword::Simple::undefine( 'KEYWORD' );};
317 1 50       5 if ($Keyword::Simple::VERSION < 0.04) {
318 0         0 $keyword_defn .= "\$^H{'Keyword::Simple/keywords'} =~ s{ KEYWORD:-?\\d*}{}g;" ;
319             }
320              
321             # How to remove the Keyword::Declare keywords...
322 1         5 $keyword_defn .= q{
323             delete @^H{ grep m{^ Keyword::Declare \s+ active:KEYWORD:}xms, keys %^H };
324             };
325 1         10 $keyword_defn =~ s{KEYWORD}{$keyword_info{keyword}}g;
326              
327             # Uninstall the keyword, exporting it as well if it's in an import() or unimport() sub...
328 1         316 $$src_ref = qq{ if (((caller 0)[3]//q{}) =~ /\\b(?:un)?import\\Z/) { $keyword_defn } }
329             . qq{ BEGIN{ $keyword_defn } }
330             . "\n#line $line $file\n"
331             . $$src_ref;
332 19         498 };
333             }
334              
335             # Keyword::Simple::define() has a bug: it can't define keywords starting with _
336             # [REMOVE WHEN UPSTREAM MODULE (Keyword::Simple) IS FIXED]
337             sub _replacement_define {
338             package Keyword::Simple;
339              
340 334     334   796 my ($kw, $sub) = @_;
341 334 50       1799 $kw =~ /^[^\W\d]\w*\z/ or croak "'$kw' doesn't look like an identifier";
342 334 50       1027 ref($sub) eq 'CODE' or croak "'$sub' doesn't look like a coderef";
343              
344 334 50       983 if ($Keyword::Simple::VERSION < 0.04) {
345 0         0 our @meta;
346 0         0 my $n = @meta;
347 0         0 push @meta, $sub;
348              
349 0         0 $^H{+HINTK_KEYWORDS} .= " $kw:$n";
350 19     19   9620 use B::Hooks::EndOfScope;
  19         207307  
  19         136  
351             on_scope_end {
352 0     0   0 delete $meta[$n];
353 0         0 };
354             }
355             else {
356 334   100     493 my %keywords = %{$^H{+HINTK_KEYWORDS} // {}};
  334         1928  
357 334         886 $keywords{$kw} = $sub;
358 334         8356 $^H{+HINTK_KEYWORDS} = \%keywords;
359             }
360             }
361              
362             # Install a __DATA__ keyword to overcome bug in Keyword::Simple...
363             # [REMOVE WHEN UPSTREAM MODULE (Keyword::Simple) IS FIXED]
364             sub _install_data_handler {
365             my $DATA_HANDLER = sub {
366             # Unpack trailing code...
367 1     1   6 my ($src_ref) = @_;
368              
369             # Convert to data handle...
370 1         3 my $data = $$src_ref;
371 1         14 $data =~ s{ \A [^\n]* \n }{}xms;
372 1 50       8 $data .= "\n" unless substr($data,-1) eq "\n";
373              
374             # Create end-of-__DATA__ marker unlikely to be in the data...
375 1         4 my $END_DATA = "\3" x 253; # \3 is ASCII END-OF-TEXT, 253 is max ident length
376              
377             # Replace trailing code with code that opens a local *DATA handle...
378 1         52 $$src_ref = qq{BEGIN {open *DATA, '<', \\<<'$END_DATA'\n$data$END_DATA\n} 1; };
379 176     176   25743 };
380 176         437 _replacement_define('__DATA__', $DATA_HANDLER);
381             }
382              
383              
384             # Remove special prefix on names of internal named captures...
385             sub _deprefix {
386 334     334   748 my ($hash_ref) = @_;
387              
388 334 100       849 return undef if !defined $hash_ref;
389 1488         2530 return { map { my $key = $_; $key =~ s{^____K_D___}{}; $key => $hash_ref->{$_} }
  1488         4621  
  1488         9549  
390 333         464 keys %{$hash_ref} };
  333         1311  
391             }
392              
393              
394             # Generate the source code that actually installs a keyword...
395             sub _build_keyword_code {
396             my ($keyword_name, $keyword_sig, $keyword_ID, $keyword_block, $block_location, $block_hashline, $prefix_var)
397 157     157   297 = @{shift()}{qw< keyword sig_desc ID block location hashline prefix >};
  157         779  
398              
399             # Generate the keyword definition and set up its unique lexical ID...
400 157         1258 return qq{
401             \$^H{"Keyword::Declare active:$keyword_name:\Q$keyword_sig\E"} = $keyword_ID;
402             Keyword::Declare::_replacement_define('$keyword_name', Keyword::Declare::_get_dispatcher_for('$keyword_name',
403             $keyword_ID, sub
404             #line $block_hashline
405             { $keyword_impls[$keyword_ID]{sig_vars_unpack} do $keyword_block }));
406             };
407             }
408              
409             # Locate prefix code for keyword...
410             sub _get_prefix {
411 0     0   0 state $source_cache = {};
412              
413 0         0 my ($trail_ref, $keyword) = @_;
414              
415 0         0 my $filename = (caller 2)[1];
416 0   0     0 my $source = $source_cache->{$filename} //= do { local (*ARGV, $/); @ARGV=$filename; <> };
  0         0  
  0         0  
  0         0  
417              
418 0         0 my $trailing = $$trail_ref;
419 0         0 $trailing =~ s/\s+\z//;
420 0         0 $source =~ s{\b$keyword\s*\Q$trailing\E\s*\z}{};
421              
422 0         0 return 'qq{' . quotemeta($source) . '}';
423             }
424              
425              
426             # Install keyword's source-code generator, and return a dispatcher sub for that keyword
427             # (building a closure for it, if necessary)...
428             sub _get_dispatcher_for {
429 158     158   2500 my ($keyword_name, $keyword_ID, $keyword_generator) = @_;
430              
431             # Install the keyword generator sub...
432 158         496 $keyword_impls[$keyword_ID]{generator} = $keyword_generator;
433              
434             # This will dispatch any keyword of the specified name...
435 158         259 state %dispatcher_for;
436             return $dispatcher_for{$keyword_name} //= sub {
437 76     76   576 my ($src_ref) = @_;
438 76         405 my ($package, $file, $line) = caller;
439 76         213 local $PPR::ERROR;
440              
441             # Which variants of this keyword are currently in scope???
442 76         402 my @candidate_IDs = @^H{ grep { m{^ Keyword::Declare \s+ active:$keyword_name:}xms } keys %^H };
  409         2450  
443              
444             # Which keywords are allowed in nested code at this point...
445 76         326 my @active_IDs = @^H{ grep { m{^ Keyword::Declare \s+ active:}xms } keys %^H };
  409         1327  
446             my $lexical_keywords
447 76 50       386 = @active_IDs ? join '|', reverse sort map { $keyword_impls[$_]{skip_matcher} } @active_IDs
  297         1356  
448             : '(?!)';
449 76         353 $lexical_keywords = "(?(DEFINE) (? $lexical_keywords ) )";
450              
451             # Which of them match the keyword's actual arguments???
452             my @viable_IDs
453 76         193 = grep { $$src_ref =~ m{ \A $keyword_impls[$_]{sig_matcher} $lexical_keywords $PPR::GRAMMAR }xms }
  143         4979123  
454             @candidate_IDs;
455              
456              
457             # If none of them match...game over!!!
458 76 50       14411 if (!@viable_IDs) {
459             my $error = eval "no strict;sub{\n" . ($PPR::ERROR//q{}) . '}'
460             ? " $keyword_name "
461 0         0 . do{ my $src = $$src_ref;
462 0         0 $src =~ s{ \A \s*+ (\S++ [^\n]*+) \n .* }{$1}xs;
463 0         0 $src;
464             }
465 0 0 0     0 : do{ my $err = $@;
  0         0  
466 0         0 $err =~ s{^}{ }gm;
467 0         0 $err =~ s{\(eval \d++\) line \d++}
  0         0  
468 0         0 { "$file line " . $PPR::ERROR->line($line) }eg;
469             $err
470             };
471 0         0 croak "Invalid "
472             . join(" or ", uniqstr map { $keyword_impls[$_]{desc} } @candidate_IDs)
473 0         0 . " at $file line $line.\nExpected:"
  0         0  
474             . join("\n ", q{}, uniqstr map { $keyword_impls[$_]{syntax} } @candidate_IDs)
475             . "\nbut found:\n$error"
476             . "\nCompilation failed";
477             }
478              
479 76 100       394 # If too many of them match...see if we can reduce it to a single match...
480             if (@viable_IDs > 1) {
481 23         106 # Only keep those with the most parameters...
  58         327  
482 23         79 my $max_sig_len = max map { $keyword_impls[$_]{sig_len} } @viable_IDs;
  58         197  
483             @viable_IDs = grep { $keyword_impls[$_]{sig_len} == $max_sig_len } @viable_IDs;
484              
485 23 100       90 # Resolve ambiguous matches, if possible...
486 3         21 if (@viable_IDs > 1) {
487             @viable_IDs = _resolve_matches(@viable_IDs);
488             }
489              
490 23 100       92 # If still too many, see if one is marked :prefer...
491 1 50       3 if (@viable_IDs > 1) {
  3         11  
492 1         3 if (my @preferred_IDs = grep { $keyword_impls[$_]{prefer} } @viable_IDs) {
493             @viable_IDs = @preferred_IDs;
494             }
495             }
496              
497 23 50       93 # If still too many, give up and report the ambiguity...
498             if (@viable_IDs > 1) {
499 0         0 croak "Ambiguous "
500             . join(" or ", uniqstr map { $keyword_impls[$_]{desc} } @viable_IDs)
501 0         0 . " at $file line $line:\n $keyword_name "
502 0         0 . do{ my $src = $$src_ref;
503 0         0 $src =~ s{ \A \s*+ ( \S++ [^\n]*+) \n .* }{$1}xs;
504             $src;
505             }
506 0         0 . "\nCould be:\n"
  0         0  
507             . join("\n", map { " $keyword_impls[$_]{syntax}" } @viable_IDs)
508             . "\nCompilation failed";
509             }
510             }
511              
512 76         328 # If we get here, we have a unique best candidate, so install it...
513             my ($ID) = @viable_IDs;
514              
515 76         440 # Add in the replacement code...
516             _insert_replacement_code($src_ref, $ID, $file, $line, $lexical_keywords);
517 158   100     1336 }
518             }
519              
520             # These help unpack /.../ type specifiers...
521             my $REGEX_TYPE = qr{ \A (?&PerlMatch) \z $PPR::GRAMMAR }x;
522              
523             my $REGEX_PAT = qr{
524             \A
525             (?: (? / ) | m \s* (? \S ))
526             (? .* )
527             (?: \k | [])>\}] )
528             (? [imnsxadlup]*+ )
529             \z
530             }xs;
531              
532             my %ACTUAL_TYPE_OF = (
533             # Specified type... # Translated to...
534              
535              
536             # Autogenerated type translations (from bin/gen_types.pl)...
537              
538             'ArrayIndexer' => '/(?&PerlArrayIndexer)/',
539             'AssignmentOperator' => '/(?&PerlAssignmentOperator)/',
540             'Attributes' => '/(?&PerlAttributes)/',
541             'Comma' => '/(?&PerlComma)/',
542             'Document' => '/(?&PerlDocument)/',
543             'HashIndexer' => '/(?&PerlHashIndexer)/',
544             'InfixBinaryOperator' => '/(?&PerlInfixBinaryOperator)/',
545             'LowPrecedenceInfixOperator' => '/(?&PerlLowPrecedenceInfixOperator)/',
546             'OWS' => '/(?&PerlOWS)/',
547             'PostfixUnaryOperator' => '/(?&PerlPostfixUnaryOperator)/',
548             'PrefixUnaryOperator' => '/(?&PerlPrefixUnaryOperator)/',
549             'StatementModifier' => '/(?&PerlStatementModifier)/',
550             'NWS' => '/(?&PerlNWS)/',
551             'Whitespace' => '/(?&PerlNWS)/',
552             'Statement' => '/(?&PerlStatement)/',
553             'Block' => '/(?&PerlBlock)/',
554             'Comment' => '/(?&PerlComment)/',
555             'ControlBlock' => '/(?&PerlControlBlock)/',
556             'Expression' => '/(?&PerlExpression)/',
557             'Expr' => '/(?&PerlExpression)/',
558             'Format' => '/(?&PerlFormat)/',
559             'Keyword' => '/(?&PerlKeyword)/',
560             'Label' => '/(?&PerlLabel)/',
561             'PackageDeclaration' => '/(?&PerlPackageDeclaration)/',
562             'Pod' => '/(?&PerlPod)/',
563             'SubroutineDeclaration' => '/(?&PerlSubroutineDeclaration)/',
564             'UseStatement' => '/(?&PerlUseStatement)/',
565             'LowPrecedenceNotExpression' => '/(?&PerlLowPrecedenceNotExpression)/',
566             'List' => '/(?&PerlList)/',
567             'CommaList' => '/(?&PerlCommaList)/',
568             'Assignment' => '/(?&PerlAssignment)/',
569             'ConditionalExpression' => '/(?&PerlConditionalExpression)/',
570             'Ternary' => '/(?&PerlConditionalExpression)/',
571             'ListElem' => '/(?&PerlConditionalExpression)/',
572             'BinaryExpression' => '/(?&PerlBinaryExpression)/',
573             'PrefixPostfixTerm' => '/(?&PerlPrefixPostfixTerm)/',
574             'Term' => '/(?&PerlTerm)/',
575             'AnonymousArray' => '/(?&PerlAnonymousArray)/',
576             'AnonArray' => '/(?&PerlAnonymousArray)/',
577             'AnonymousHash' => '/(?&PerlAnonymousHash)/',
578             'AnonHash' => '/(?&PerlAnonymousHash)/',
579             'AnonymousSubroutine' => '/(?&PerlAnonymousSubroutine)/',
580             'Call' => '/(?&PerlCall)/',
581             'DiamondOperator' => '/(?&PerlDiamondOperator)/',
582             'DoBlock' => '/(?&PerlDoBlock)/',
583             'EvalBlock' => '/(?&PerlEvalBlock)/',
584             'Literal' => '/(?&PerlLiteral)/',
585             'Lvalue' => '/(?&PerlLvalue)/',
586             'ParenthesesList' => '/(?&PerlParenthesesList)/',
587             'ParensList' => '/(?&PerlParenthesesList)/',
588             'Quotelike' => '/(?&PerlQuotelike)/',
589             'ReturnStatement' => '/(?&PerlReturnStatement)/',
590             'Typeglob' => '/(?&PerlTypeglob)/',
591             'VariableDeclaration' => '/(?&PerlVariableDeclaration)/',
592             'VarDecl' => '/(?&PerlVariableDeclaration)/',
593             'Variable' => '/(?&PerlVariable)/',
594             'Var' => '/(?&PerlVariable)/',
595             'ArrayAccess' => '/(?&PerlArrayAccess)/',
596             'Bareword' => '/(?&PerlBareword)/',
597             'BuiltinFunction' => '/(?&PerlBuiltinFunction)/',
598             'HashAccess' => '/(?&PerlHashAccess)/',
599             'Number' => '/(?&PerlNumber)/',
600             'Num' => '/(?&PerlNumber)/',
601             'QuotelikeQW' => '/(?&PerlQuotelikeQW)/',
602             'QuotelikeQX' => '/(?&PerlQuotelikeQX)/',
603             'Regex' => '/(?&PerlRegex)/',
604             'Regexp' => '/(?&PerlRegex)/',
605             'ScalarAccess' => '/(?&PerlScalarAccess)/',
606             'String' => '/(?&PerlString)/',
607             'Str' => '/(?&PerlString)/',
608             'Substitution' => '/(?&PerlSubstitution)/',
609             'QuotelikeS' => '/(?&PerlSubstitution)/',
610             'Transliteration' => '/(?&PerlTransliteration)/',
611             'QuotelikeTR' => '/(?&PerlTransliteration)/',
612             'ContextualRegex' => '/(?&PerlContextualRegex)/',
613             'Heredoc' => '/(?&PerlHeredoc)/',
614             'Integer' => '/(?&PerlInteger)/',
615             'Int' => '/(?&PerlInteger)/',
616             'Match' => '/(?&PerlMatch)/',
617             'QuotelikeM' => '/(?&PerlMatch)/',
618             'NullaryBuiltinFunction' => '/(?&PerlNullaryBuiltinFunction)/',
619             'OldQualifiedIdentifier' => '/(?&PerlOldQualifiedIdentifier)/',
620             'QuotelikeQ' => '/(?&PerlQuotelikeQ)/',
621             'QuotelikeQQ' => '/(?&PerlQuotelikeQQ)/',
622             'QuotelikeQR' => '/(?&PerlQuotelikeQR)/',
623             'VString' => '/(?&PerlVString)/',
624             'VariableArray' => '/(?&PerlVariableArray)/',
625             'VarArray' => '/(?&PerlVariableArray)/',
626             'ArrayVar' => '/(?&PerlVariableArray)/',
627             'VariableHash' => '/(?&PerlVariableHash)/',
628             'VarHash' => '/(?&PerlVariableHash)/',
629             'HashVar' => '/(?&PerlVariableHash)/',
630             'VariableScalar' => '/(?&PerlVariableScalar)/',
631             'VarScalar' => '/(?&PerlVariableScalar)/',
632             'ScalarVar' => '/(?&PerlVariableScalar)/',
633             'VersionNumber' => '/(?&PerlVersionNumber)/',
634             'ContextualMatch' => '/(?&PerlContextualMatch)/',
635             'ContextualQuotelikeM' => '/(?&PerlContextualMatch)/',
636             'PositiveInteger' => '/(?&PerlPositiveInteger)/',
637             'PosInt' => '/(?&PerlPositiveInteger)/',
638             'QualifiedIdentifier' => '/(?&PerlQualifiedIdentifier)/',
639             'QualIdent' => '/(?&PerlQualifiedIdentifier)/',
640             'QuotelikeQR' => '/(?&PerlQuotelikeQR)/',
641             'VString' => '/(?&PerlVString)/',
642             'Identifier' => '/(?&PerlIdentifier)/',
643             'Ident' => '/(?&PerlIdentifier)/',
644              
645             # End of autogenerated type translations
646              
647             'Integer' => '/(?:[+-]?+(?&PPR_digit_seq)(?!\.))/',
648             'Int' => '/(?:[+-]?+(?&PPR_digit_seq)(?!\.))/',
649             'PositiveInteger' => '/(?:[+]?+(?&PPR_digit_seq)(?!\.))/',
650             'PosInt' => '/(?:[+]?+(?&PPR_digit_seq)(?!\.))/',
651             'Comment' => '/\#[^\n]*\n/',
652             );
653              
654             our %isa = (
655              
656             # Autogenerated type ISA hierarchy (from bin/gen_types.pl)...
657              
658             "AnonArray\34Assignment"=>1,"AnonArray\34BinaryExpression"=>1,"AnonArray\34CommaList"=>1,"AnonArray\34ConditionalExpression"=>1,"AnonArray\34Document"=>1,"AnonArray\34Expr"=>1,"AnonArray\34Expression"=>1,"AnonArray\34List"=>1,"AnonArray\34ListElem"=>1,"AnonArray\34LowPrecedenceNotExpression"=>1,"AnonArray\34PrefixPostfixTerm"=>1,"AnonArray\34Statement"=>1,"AnonArray\34Term"=>1,"AnonArray\34Ternary"=>1,"AnonHash\34Assignment"=>1,"AnonHash\34BinaryExpression"=>1,"AnonHash\34CommaList"=>1,"AnonHash\34ConditionalExpression"=>1,"AnonHash\34Document"=>1,"AnonHash\34Expr"=>1,"AnonHash\34Expression"=>1,"AnonHash\34List"=>1,"AnonHash\34ListElem"=>1,"AnonHash\34LowPrecedenceNotExpression"=>1,"AnonHash\34PrefixPostfixTerm"=>1,"AnonHash\34Statement"=>1,"AnonHash\34Term"=>1,"AnonHash\34Ternary"=>1,"AnonymousArray\34Assignment"=>1,"AnonymousArray\34BinaryExpression"=>1,"AnonymousArray\34CommaList"=>1,"AnonymousArray\34ConditionalExpression"=>1,"AnonymousArray\34Document"=>1,"AnonymousArray\34Expr"=>1,"AnonymousArray\34Expression"=>1,"AnonymousArray\34List"=>1,"AnonymousArray\34ListElem"=>1,"AnonymousArray\34LowPrecedenceNotExpression"=>1,"AnonymousArray\34PrefixPostfixTerm"=>1,"AnonymousArray\34Statement"=>1,"AnonymousArray\34Term"=>1,"AnonymousArray\34Ternary"=>1,"AnonymousHash\34Assignment"=>1,"AnonymousHash\34BinaryExpression"=>1,"AnonymousHash\34CommaList"=>1,"AnonymousHash\34ConditionalExpression"=>1,"AnonymousHash\34Document"=>1,"AnonymousHash\34Expr"=>1,"AnonymousHash\34Expression"=>1,"AnonymousHash\34List"=>1,"AnonymousHash\34ListElem"=>1,"AnonymousHash\34LowPrecedenceNotExpression"=>1,"AnonymousHash\34PrefixPostfixTerm"=>1,"AnonymousHash\34Statement"=>1,"AnonymousHash\34Term"=>1,"AnonymousHash\34Ternary"=>1,"AnonymousSubroutine\34Assignment"=>1,"AnonymousSubroutine\34BinaryExpression"=>1,"AnonymousSubroutine\34CommaList"=>1,"AnonymousSubroutine\34ConditionalExpression"=>1,"AnonymousSubroutine\34Document"=>1,"AnonymousSubroutine\34Expr"=>1,"AnonymousSubroutine\34Expression"=>1,"AnonymousSubroutine\34List"=>1,"AnonymousSubroutine\34ListElem"=>1,"AnonymousSubroutine\34LowPrecedenceNotExpression"=>1,"AnonymousSubroutine\34PrefixPostfixTerm"=>1,"AnonymousSubroutine\34Statement"=>1,"AnonymousSubroutine\34Term"=>1,"AnonymousSubroutine\34Ternary"=>1,"ArrayAccess\34Assignment"=>1,"ArrayAccess\34BinaryExpression"=>1,"ArrayAccess\34CommaList"=>1,"ArrayAccess\34ConditionalExpression"=>1,"ArrayAccess\34Document"=>1,"ArrayAccess\34Expr"=>1,"ArrayAccess\34Expression"=>1,"ArrayAccess\34List"=>1,"ArrayAccess\34ListElem"=>1,"ArrayAccess\34LowPrecedenceNotExpression"=>1,"ArrayAccess\34PrefixPostfixTerm"=>1,"ArrayAccess\34Statement"=>1,"ArrayAccess\34Term"=>1,"ArrayAccess\34Ternary"=>1,"ArrayAccess\34Var"=>1,"ArrayAccess\34Variable"=>1,"ArrayVar\34ArrayAccess"=>1,"ArrayVar\34Assignment"=>1,"ArrayVar\34BinaryExpression"=>1,"ArrayVar\34CommaList"=>1,"ArrayVar\34ConditionalExpression"=>1,"ArrayVar\34Document"=>1,"ArrayVar\34Expr"=>1,"ArrayVar\34Expression"=>1,"ArrayVar\34List"=>1,"ArrayVar\34ListElem"=>1,"ArrayVar\34LowPrecedenceNotExpression"=>1,"ArrayVar\34PrefixPostfixTerm"=>1,"ArrayVar\34Statement"=>1,"ArrayVar\34Term"=>1,"ArrayVar\34Ternary"=>1,"ArrayVar\34Var"=>1,"ArrayVar\34Variable"=>1,"Assignment\34CommaList"=>1,"Assignment\34Document"=>1,"Assignment\34Expr"=>1,"Assignment\34Expression"=>1,"Assignment\34List"=>1,"Assignment\34LowPrecedenceNotExpression"=>1,"Assignment\34Statement"=>1,"Bareword\34Assignment"=>1,"Bareword\34BinaryExpression"=>1,"Bareword\34CommaList"=>1,"Bareword\34ConditionalExpression"=>1,"Bareword\34Document"=>1,"Bareword\34Expr"=>1,"Bareword\34Expression"=>1,"Bareword\34List"=>1,"Bareword\34ListElem"=>1,"Bareword\34Literal"=>1,"Bareword\34LowPrecedenceNotExpression"=>1,"Bareword\34PrefixPostfixTerm"=>1,"Bareword\34Statement"=>1,"Bareword\34Term"=>1,"Bareword\34Ternary"=>1,"BinaryExpression\34Assignment"=>1,"BinaryExpression\34CommaList"=>1,"BinaryExpression\34ConditionalExpression"=>1,"BinaryExpression\34Document"=>1,"BinaryExpression\34Expr"=>1,"BinaryExpression\34Expression"=>1,"BinaryExpression\34List"=>1,"BinaryExpression\34ListElem"=>1,"BinaryExpression\34LowPrecedenceNotExpression"=>1,"BinaryExpression\34Statement"=>1,"BinaryExpression\34Ternary"=>1,"Block\34Document"=>1,"Block\34Statement"=>1,"BuiltinFunction\34Assignment"=>1,"BuiltinFunction\34BinaryExpression"=>1,"BuiltinFunction\34Call"=>1,"BuiltinFunction\34CommaList"=>1,"BuiltinFunction\34ConditionalExpression"=>1,"BuiltinFunction\34Document"=>1,"BuiltinFunction\34Expr"=>1,"BuiltinFunction\34Expression"=>1,"BuiltinFunction\34List"=>1,"BuiltinFunction\34ListElem"=>1,"BuiltinFunction\34LowPrecedenceNotExpression"=>1,"BuiltinFunction\34PrefixPostfixTerm"=>1,"BuiltinFunction\34Statement"=>1,"BuiltinFunction\34Term"=>1,"BuiltinFunction\34Ternary"=>1,"Call\34Assignment"=>1,"Call\34BinaryExpression"=>1,"Call\34CommaList"=>1,"Call\34ConditionalExpression"=>1,"Call\34Document"=>1,"Call\34Expr"=>1,"Call\34Expression"=>1,"Call\34List"=>1,"Call\34ListElem"=>1,"Call\34LowPrecedenceNotExpression"=>1,"Call\34PrefixPostfixTerm"=>1,"Call\34Statement"=>1,"Call\34Term"=>1,"Call\34Ternary"=>1,"CommaList\34Document"=>1,"CommaList\34Expr"=>1,"CommaList\34Expression"=>1,"CommaList\34List"=>1,"CommaList\34LowPrecedenceNotExpression"=>1,"CommaList\34Statement"=>1,"Comment\34NWS"=>1,"Comment\34OWS"=>1,"Comment\34Whitespace"=>1,"ConditionalExpression\34Assignment"=>1,"ConditionalExpression\34CommaList"=>1,"ConditionalExpression\34Document"=>1,"ConditionalExpression\34Expr"=>1,"ConditionalExpression\34Expression"=>1,"ConditionalExpression\34List"=>1,"ConditionalExpression\34LowPrecedenceNotExpression"=>1,"ConditionalExpression\34Statement"=>1,"ContextualMatch\34Assignment"=>1,"ContextualMatch\34BinaryExpression"=>1,"ContextualMatch\34CommaList"=>1,"ContextualMatch\34ConditionalExpression"=>1,"ContextualMatch\34ContextualRegex"=>1,"ContextualMatch\34Document"=>1,"ContextualMatch\34Expr"=>1,"ContextualMatch\34Expression"=>1,"ContextualMatch\34List"=>1,"ContextualMatch\34ListElem"=>1,"ContextualMatch\34LowPrecedenceNotExpression"=>1,"ContextualMatch\34Match"=>1,"ContextualMatch\34PrefixPostfixTerm"=>1,"ContextualMatch\34Quotelike"=>1,"ContextualMatch\34QuotelikeM"=>1,"ContextualMatch\34Regex"=>1,"ContextualMatch\34Regexp"=>1,"ContextualMatch\34Statement"=>1,"ContextualMatch\34Term"=>1,"ContextualMatch\34Ternary"=>1,"ContextualQuotelikeM\34Assignment"=>1,"ContextualQuotelikeM\34BinaryExpression"=>1,"ContextualQuotelikeM\34CommaList"=>1,"ContextualQuotelikeM\34ConditionalExpression"=>1,"ContextualQuotelikeM\34ContextualRegex"=>1,"ContextualQuotelikeM\34Document"=>1,"ContextualQuotelikeM\34Expr"=>1,"ContextualQuotelikeM\34Expression"=>1,"ContextualQuotelikeM\34List"=>1,"ContextualQuotelikeM\34ListElem"=>1,"ContextualQuotelikeM\34LowPrecedenceNotExpression"=>1,"ContextualQuotelikeM\34Match"=>1,"ContextualQuotelikeM\34PrefixPostfixTerm"=>1,"ContextualQuotelikeM\34Quotelike"=>1,"ContextualQuotelikeM\34QuotelikeM"=>1,"ContextualQuotelikeM\34Regex"=>1,"ContextualQuotelikeM\34Regexp"=>1,"ContextualQuotelikeM\34Statement"=>1,"ContextualQuotelikeM\34Term"=>1,"ContextualQuotelikeM\34Ternary"=>1,"ContextualRegex\34Assignment"=>1,"ContextualRegex\34BinaryExpression"=>1,"ContextualRegex\34CommaList"=>1,"ContextualRegex\34ConditionalExpression"=>1,"ContextualRegex\34Document"=>1,"ContextualRegex\34Expr"=>1,"ContextualRegex\34Expression"=>1,"ContextualRegex\34List"=>1,"ContextualRegex\34ListElem"=>1,"ContextualRegex\34LowPrecedenceNotExpression"=>1,"ContextualRegex\34PrefixPostfixTerm"=>1,"ContextualRegex\34Quotelike"=>1,"ContextualRegex\34Regex"=>1,"ContextualRegex\34Regexp"=>1,"ContextualRegex\34Statement"=>1,"ContextualRegex\34Term"=>1,"ContextualRegex\34Ternary"=>1,"ControlBlock\34Document"=>1,"ControlBlock\34Statement"=>1,"DiamondOperator\34Assignment"=>1,"DiamondOperator\34BinaryExpression"=>1,"DiamondOperator\34CommaList"=>1,"DiamondOperator\34ConditionalExpression"=>1,"DiamondOperator\34Document"=>1,"DiamondOperator\34Expr"=>1,"DiamondOperator\34Expression"=>1,"DiamondOperator\34List"=>1,"DiamondOperator\34ListElem"=>1,"DiamondOperator\34LowPrecedenceNotExpression"=>1,"DiamondOperator\34PrefixPostfixTerm"=>1,"DiamondOperator\34Statement"=>1,"DiamondOperator\34Term"=>1,"DiamondOperator\34Ternary"=>1,"DoBlock\34Assignment"=>1,"DoBlock\34BinaryExpression"=>1,"DoBlock\34CommaList"=>1,"DoBlock\34ConditionalExpression"=>1,"DoBlock\34Document"=>1,"DoBlock\34Expr"=>1,"DoBlock\34Expression"=>1,"DoBlock\34List"=>1,"DoBlock\34ListElem"=>1,"DoBlock\34LowPrecedenceNotExpression"=>1,"DoBlock\34PrefixPostfixTerm"=>1,"DoBlock\34Statement"=>1,"DoBlock\34Term"=>1,"DoBlock\34Ternary"=>1,"EvalBlock\34Assignment"=>1,"EvalBlock\34BinaryExpression"=>1,"EvalBlock\34CommaList"=>1,"EvalBlock\34ConditionalExpression"=>1,"EvalBlock\34Document"=>1,"EvalBlock\34Expr"=>1,"EvalBlock\34Expression"=>1,"EvalBlock\34List"=>1,"EvalBlock\34ListElem"=>1,"EvalBlock\34LowPrecedenceNotExpression"=>1,"EvalBlock\34PrefixPostfixTerm"=>1,"EvalBlock\34Statement"=>1,"EvalBlock\34Term"=>1,"EvalBlock\34Ternary"=>1,"Expr\34Document"=>1,"Expr\34Statement"=>1,"Expression\34Document"=>1,"Expression\34Statement"=>1,"Format\34Document"=>1,"Format\34Statement"=>1,"HashAccess\34Assignment"=>1,"HashAccess\34BinaryExpression"=>1,"HashAccess\34CommaList"=>1,"HashAccess\34ConditionalExpression"=>1,"HashAccess\34Document"=>1,"HashAccess\34Expr"=>1,"HashAccess\34Expression"=>1,"HashAccess\34List"=>1,"HashAccess\34ListElem"=>1,"HashAccess\34LowPrecedenceNotExpression"=>1,"HashAccess\34PrefixPostfixTerm"=>1,"HashAccess\34Statement"=>1,"HashAccess\34Term"=>1,"HashAccess\34Ternary"=>1,"HashAccess\34Var"=>1,"HashAccess\34Variable"=>1,"HashVar\34Assignment"=>1,"HashVar\34BinaryExpression"=>1,"HashVar\34CommaList"=>1,"HashVar\34ConditionalExpression"=>1,"HashVar\34Document"=>1,"HashVar\34Expr"=>1,"HashVar\34Expression"=>1,"HashVar\34HashAccess"=>1,"HashVar\34List"=>1,"HashVar\34ListElem"=>1,"HashVar\34LowPrecedenceNotExpression"=>1,"HashVar\34PrefixPostfixTerm"=>1,"HashVar\34Statement"=>1,"HashVar\34Term"=>1,"HashVar\34Ternary"=>1,"HashVar\34Var"=>1,"HashVar\34Variable"=>1,"Heredoc\34Assignment"=>1,"Heredoc\34BinaryExpression"=>1,"Heredoc\34CommaList"=>1,"Heredoc\34ConditionalExpression"=>1,"Heredoc\34Document"=>1,"Heredoc\34Expr"=>1,"Heredoc\34Expression"=>1,"Heredoc\34List"=>1,"Heredoc\34ListElem"=>1,"Heredoc\34Literal"=>1,"Heredoc\34LowPrecedenceNotExpression"=>1,"Heredoc\34PrefixPostfixTerm"=>1,"Heredoc\34Statement"=>1,"Heredoc\34Str"=>1,"Heredoc\34String"=>1,"Heredoc\34Term"=>1,"Heredoc\34Ternary"=>1,"Ident\34Assignment"=>1,"Ident\34Bareword"=>1,"Ident\34BinaryExpression"=>1,"Ident\34CommaList"=>1,"Ident\34ConditionalExpression"=>1,"Ident\34Document"=>1,"Ident\34Expr"=>1,"Ident\34Expression"=>1,"Ident\34List"=>1,"Ident\34ListElem"=>1,"Ident\34Literal"=>1,"Ident\34LowPrecedenceNotExpression"=>1,"Ident\34OldQualifiedIdentifier"=>1,"Ident\34PrefixPostfixTerm"=>1,"Ident\34QualIdent"=>1,"Ident\34QualifiedIdentifier"=>1,"Ident\34Statement"=>1,"Ident\34Term"=>1,"Ident\34Ternary"=>1,"Identifier\34Assignment"=>1,"Identifier\34Bareword"=>1,"Identifier\34BinaryExpression"=>1,"Identifier\34CommaList"=>1,"Identifier\34ConditionalExpression"=>1,"Identifier\34Document"=>1,"Identifier\34Expr"=>1,"Identifier\34Expression"=>1,"Identifier\34List"=>1,"Identifier\34ListElem"=>1,"Identifier\34Literal"=>1,"Identifier\34LowPrecedenceNotExpression"=>1,"Identifier\34OldQualifiedIdentifier"=>1,"Identifier\34PrefixPostfixTerm"=>1,"Identifier\34QualIdent"=>1,"Identifier\34QualifiedIdentifier"=>1,"Identifier\34Statement"=>1,"Identifier\34Term"=>1,"Identifier\34Ternary"=>1,"Int\34Assignment"=>1,"Int\34BinaryExpression"=>1,"Int\34CommaList"=>1,"Int\34ConditionalExpression"=>1,"Int\34Document"=>1,"Int\34Expr"=>1,"Int\34Expression"=>1,"Int\34List"=>1,"Int\34ListElem"=>1,"Int\34Literal"=>1,"Int\34LowPrecedenceNotExpression"=>1,"Int\34Num"=>1,"Int\34Number"=>1,"Int\34PrefixPostfixTerm"=>1,"Int\34Statement"=>1,"Int\34Term"=>1,"Int\34Ternary"=>1,"Integer\34Assignment"=>1,"Integer\34BinaryExpression"=>1,"Integer\34CommaList"=>1,"Integer\34ConditionalExpression"=>1,"Integer\34Document"=>1,"Integer\34Expr"=>1,"Integer\34Expression"=>1,"Integer\34List"=>1,"Integer\34ListElem"=>1,"Integer\34Literal"=>1,"Integer\34LowPrecedenceNotExpression"=>1,"Integer\34Num"=>1,"Integer\34Number"=>1,"Integer\34PrefixPostfixTerm"=>1,"Integer\34Statement"=>1,"Integer\34Term"=>1,"Integer\34Ternary"=>1,"Keyword\34Document"=>1,"Keyword\34Statement"=>1,"Label\34Document"=>1,"Label\34Statement"=>1,"List\34Document"=>1,"List\34Expr"=>1,"List\34Expression"=>1,"List\34LowPrecedenceNotExpression"=>1,"List\34Statement"=>1,"ListElem\34Assignment"=>1,"ListElem\34CommaList"=>1,"ListElem\34Document"=>1,"ListElem\34Expr"=>1,"ListElem\34Expression"=>1,"ListElem\34List"=>1,"ListElem\34LowPrecedenceNotExpression"=>1,"ListElem\34Statement"=>1,"Literal\34Assignment"=>1,"Literal\34BinaryExpression"=>1,"Literal\34CommaList"=>1,"Literal\34ConditionalExpression"=>1,"Literal\34Document"=>1,"Literal\34Expr"=>1,"Literal\34Expression"=>1,"Literal\34List"=>1,"Literal\34ListElem"=>1,"Literal\34LowPrecedenceNotExpression"=>1,"Literal\34PrefixPostfixTerm"=>1,"Literal\34Statement"=>1,"Literal\34Term"=>1,"Literal\34Ternary"=>1,"LowPrecedenceNotExpression\34Document"=>1,"LowPrecedenceNotExpression\34Expr"=>1,"LowPrecedenceNotExpression\34Expression"=>1,"LowPrecedenceNotExpression\34Statement"=>1,"Lvalue\34Assignment"=>1,"Lvalue\34BinaryExpression"=>1,"Lvalue\34CommaList"=>1,"Lvalue\34ConditionalExpression"=>1,"Lvalue\34Document"=>1,"Lvalue\34Expr"=>1,"Lvalue\34Expression"=>1,"Lvalue\34List"=>1,"Lvalue\34ListElem"=>1,"Lvalue\34LowPrecedenceNotExpression"=>1,"Lvalue\34PrefixPostfixTerm"=>1,"Lvalue\34Statement"=>1,"Lvalue\34Term"=>1,"Lvalue\34Ternary"=>1,"Match\34Assignment"=>1,"Match\34BinaryExpression"=>1,"Match\34CommaList"=>1,"Match\34ConditionalExpression"=>1,"Match\34Document"=>1,"Match\34Expr"=>1,"Match\34Expression"=>1,"Match\34List"=>1,"Match\34ListElem"=>1,"Match\34LowPrecedenceNotExpression"=>1,"Match\34PrefixPostfixTerm"=>1,"Match\34Quotelike"=>1,"Match\34Regex"=>1,"Match\34Regexp"=>1,"Match\34Statement"=>1,"Match\34Term"=>1,"Match\34Ternary"=>1,"NullaryBuiltinFunction\34Assignment"=>1,"NullaryBuiltinFunction\34BinaryExpression"=>1,"NullaryBuiltinFunction\34BuiltinFunction"=>1,"NullaryBuiltinFunction\34Call"=>1,"NullaryBuiltinFunction\34CommaList"=>1,"NullaryBuiltinFunction\34ConditionalExpression"=>1,"NullaryBuiltinFunction\34Document"=>1,"NullaryBuiltinFunction\34Expr"=>1,"NullaryBuiltinFunction\34Expression"=>1,"NullaryBuiltinFunction\34List"=>1,"NullaryBuiltinFunction\34ListElem"=>1,"NullaryBuiltinFunction\34LowPrecedenceNotExpression"=>1,"NullaryBuiltinFunction\34PrefixPostfixTerm"=>1,"NullaryBuiltinFunction\34Statement"=>1,"NullaryBuiltinFunction\34Term"=>1,"NullaryBuiltinFunction\34Ternary"=>1,"Num\34Assignment"=>1,"Num\34BinaryExpression"=>1,"Num\34CommaList"=>1,"Num\34ConditionalExpression"=>1,"Num\34Document"=>1,"Num\34Expr"=>1,"Num\34Expression"=>1,"Num\34List"=>1,"Num\34ListElem"=>1,"Num\34Literal"=>1,"Num\34LowPrecedenceNotExpression"=>1,"Num\34PrefixPostfixTerm"=>1,"Num\34Statement"=>1,"Num\34Term"=>1,"Num\34Ternary"=>1,"Number\34Assignment"=>1,"Number\34BinaryExpression"=>1,"Number\34CommaList"=>1,"Number\34ConditionalExpression"=>1,"Number\34Document"=>1,"Number\34Expr"=>1,"Number\34Expression"=>1,"Number\34List"=>1,"Number\34ListElem"=>1,"Number\34Literal"=>1,"Number\34LowPrecedenceNotExpression"=>1,"Number\34PrefixPostfixTerm"=>1,"Number\34Statement"=>1,"Number\34Term"=>1,"Number\34Ternary"=>1,"NWS\34OWS"=>1,"OldQualifiedIdentifier\34Assignment"=>1,"OldQualifiedIdentifier\34Bareword"=>1,"OldQualifiedIdentifier\34BinaryExpression"=>1,"OldQualifiedIdentifier\34CommaList"=>1,"OldQualifiedIdentifier\34ConditionalExpression"=>1,"OldQualifiedIdentifier\34Document"=>1,"OldQualifiedIdentifier\34Expr"=>1,"OldQualifiedIdentifier\34Expression"=>1,"OldQualifiedIdentifier\34List"=>1,"OldQualifiedIdentifier\34ListElem"=>1,"OldQualifiedIdentifier\34Literal"=>1,"OldQualifiedIdentifier\34LowPrecedenceNotExpression"=>1,"OldQualifiedIdentifier\34PrefixPostfixTerm"=>1,"OldQualifiedIdentifier\34Statement"=>1,"OldQualifiedIdentifier\34Term"=>1,"OldQualifiedIdentifier\34Ternary"=>1,"PackageDeclaration\34Document"=>1,"PackageDeclaration\34Statement"=>1,"ParensList\34Assignment"=>1,"ParensList\34BinaryExpression"=>1,"ParensList\34CommaList"=>1,"ParensList\34ConditionalExpression"=>1,"ParensList\34Document"=>1,"ParensList\34Expr"=>1,"ParensList\34Expression"=>1,"ParensList\34List"=>1,"ParensList\34ListElem"=>1,"ParensList\34LowPrecedenceNotExpression"=>1,"ParensList\34PrefixPostfixTerm"=>1,"ParensList\34Statement"=>1,"ParensList\34Term"=>1,"ParensList\34Ternary"=>1,"ParenthesesList\34Assignment"=>1,"ParenthesesList\34BinaryExpression"=>1,"ParenthesesList\34CommaList"=>1,"ParenthesesList\34ConditionalExpression"=>1,"ParenthesesList\34Document"=>1,"ParenthesesList\34Expr"=>1,"ParenthesesList\34Expression"=>1,"ParenthesesList\34List"=>1,"ParenthesesList\34ListElem"=>1,"ParenthesesList\34LowPrecedenceNotExpression"=>1,"ParenthesesList\34PrefixPostfixTerm"=>1,"ParenthesesList\34Statement"=>1,"ParenthesesList\34Term"=>1,"ParenthesesList\34Ternary"=>1,"Pod\34NWS"=>1,"Pod\34OWS"=>1,"Pod\34Whitespace"=>1,"PosInt\34Assignment"=>1,"PosInt\34BinaryExpression"=>1,"PosInt\34CommaList"=>1,"PosInt\34ConditionalExpression"=>1,"PosInt\34Document"=>1,"PosInt\34Expr"=>1,"PosInt\34Expression"=>1,"PosInt\34Int"=>1,"PosInt\34Integer"=>1,"PosInt\34List"=>1,"PosInt\34ListElem"=>1,"PosInt\34Literal"=>1,"PosInt\34LowPrecedenceNotExpression"=>1,"PosInt\34Num"=>1,"PosInt\34Number"=>1,"PosInt\34PrefixPostfixTerm"=>1,"PosInt\34Statement"=>1,"PosInt\34Term"=>1,"PosInt\34Ternary"=>1,"PositiveInteger\34Assignment"=>1,"PositiveInteger\34BinaryExpression"=>1,"PositiveInteger\34CommaList"=>1,"PositiveInteger\34ConditionalExpression"=>1,"PositiveInteger\34Document"=>1,"PositiveInteger\34Expr"=>1,"PositiveInteger\34Expression"=>1,"PositiveInteger\34Int"=>1,"PositiveInteger\34Integer"=>1,"PositiveInteger\34List"=>1,"PositiveInteger\34ListElem"=>1,"PositiveInteger\34Literal"=>1,"PositiveInteger\34LowPrecedenceNotExpression"=>1,"PositiveInteger\34Num"=>1,"PositiveInteger\34Number"=>1,"PositiveInteger\34PrefixPostfixTerm"=>1,"PositiveInteger\34Statement"=>1,"PositiveInteger\34Term"=>1,"PositiveInteger\34Ternary"=>1,"PrefixPostfixTerm\34Assignment"=>1,"PrefixPostfixTerm\34BinaryExpression"=>1,"PrefixPostfixTerm\34CommaList"=>1,"PrefixPostfixTerm\34ConditionalExpression"=>1,"PrefixPostfixTerm\34Document"=>1,"PrefixPostfixTerm\34Expr"=>1,"PrefixPostfixTerm\34Expression"=>1,"PrefixPostfixTerm\34List"=>1,"PrefixPostfixTerm\34ListElem"=>1,"PrefixPostfixTerm\34LowPrecedenceNotExpression"=>1,"PrefixPostfixTerm\34Statement"=>1,"PrefixPostfixTerm\34Ternary"=>1,"QualIdent\34Assignment"=>1,"QualIdent\34Bareword"=>1,"QualIdent\34BinaryExpression"=>1,"QualIdent\34CommaList"=>1,"QualIdent\34ConditionalExpression"=>1,"QualIdent\34Document"=>1,"QualIdent\34Expr"=>1,"QualIdent\34Expression"=>1,"QualIdent\34List"=>1,"QualIdent\34ListElem"=>1,"QualIdent\34Literal"=>1,"QualIdent\34LowPrecedenceNotExpression"=>1,"QualIdent\34OldQualifiedIdentifier"=>1,"QualIdent\34PrefixPostfixTerm"=>1,"QualIdent\34Statement"=>1,"QualIdent\34Term"=>1,"QualIdent\34Ternary"=>1,"QualifiedIdentifier\34Assignment"=>1,"QualifiedIdentifier\34Bareword"=>1,"QualifiedIdentifier\34BinaryExpression"=>1,"QualifiedIdentifier\34CommaList"=>1,"QualifiedIdentifier\34ConditionalExpression"=>1,"QualifiedIdentifier\34Document"=>1,"QualifiedIdentifier\34Expr"=>1,"QualifiedIdentifier\34Expression"=>1,"QualifiedIdentifier\34List"=>1,"QualifiedIdentifier\34ListElem"=>1,"QualifiedIdentifier\34Literal"=>1,"QualifiedIdentifier\34LowPrecedenceNotExpression"=>1,"QualifiedIdentifier\34OldQualifiedIdentifier"=>1,"QualifiedIdentifier\34PrefixPostfixTerm"=>1,"QualifiedIdentifier\34Statement"=>1,"QualifiedIdentifier\34Term"=>1,"QualifiedIdentifier\34Ternary"=>1,"Quotelike\34Assignment"=>1,"Quotelike\34BinaryExpression"=>1,"Quotelike\34CommaList"=>1,"Quotelike\34ConditionalExpression"=>1,"Quotelike\34Document"=>1,"Quotelike\34Expr"=>1,"Quotelike\34Expression"=>1,"Quotelike\34List"=>1,"Quotelike\34ListElem"=>1,"Quotelike\34LowPrecedenceNotExpression"=>1,"Quotelike\34PrefixPostfixTerm"=>1,"Quotelike\34Statement"=>1,"Quotelike\34Term"=>1,"Quotelike\34Ternary"=>1,"QuotelikeM\34Assignment"=>1,"QuotelikeM\34BinaryExpression"=>1,"QuotelikeM\34CommaList"=>1,"QuotelikeM\34ConditionalExpression"=>1,"QuotelikeM\34Document"=>1,"QuotelikeM\34Expr"=>1,"QuotelikeM\34Expression"=>1,"QuotelikeM\34List"=>1,"QuotelikeM\34ListElem"=>1,"QuotelikeM\34LowPrecedenceNotExpression"=>1,"QuotelikeM\34PrefixPostfixTerm"=>1,"QuotelikeM\34Quotelike"=>1,"QuotelikeM\34Regex"=>1,"QuotelikeM\34Regexp"=>1,"QuotelikeM\34Statement"=>1,"QuotelikeM\34Term"=>1,"QuotelikeM\34Ternary"=>1,"QuotelikeQ\34Assignment"=>1,"QuotelikeQ\34BinaryExpression"=>1,"QuotelikeQ\34CommaList"=>1,"QuotelikeQ\34ConditionalExpression"=>1,"QuotelikeQ\34Document"=>1,"QuotelikeQ\34Expr"=>1,"QuotelikeQ\34Expression"=>1,"QuotelikeQ\34List"=>1,"QuotelikeQ\34ListElem"=>1,"QuotelikeQ\34Literal"=>1,"QuotelikeQ\34LowPrecedenceNotExpression"=>1,"QuotelikeQ\34PrefixPostfixTerm"=>1,"QuotelikeQ\34Quotelike"=>1,"QuotelikeQ\34Statement"=>1,"QuotelikeQ\34Str"=>1,"QuotelikeQ\34String"=>1,"QuotelikeQ\34Term"=>1,"QuotelikeQ\34Ternary"=>1,"QuotelikeQQ\34Assignment"=>1,"QuotelikeQQ\34BinaryExpression"=>1,"QuotelikeQQ\34CommaList"=>1,"QuotelikeQQ\34ConditionalExpression"=>1,"QuotelikeQQ\34Document"=>1,"QuotelikeQQ\34Expr"=>1,"QuotelikeQQ\34Expression"=>1,"QuotelikeQQ\34List"=>1,"QuotelikeQQ\34ListElem"=>1,"QuotelikeQQ\34Literal"=>1,"QuotelikeQQ\34LowPrecedenceNotExpression"=>1,"QuotelikeQQ\34PrefixPostfixTerm"=>1,"QuotelikeQQ\34Quotelike"=>1,"QuotelikeQQ\34Statement"=>1,"QuotelikeQQ\34Str"=>1,"QuotelikeQQ\34String"=>1,"QuotelikeQQ\34Term"=>1,"QuotelikeQQ\34Ternary"=>1,"QuotelikeQR\34Assignment"=>1,"QuotelikeQR\34BinaryExpression"=>1,"QuotelikeQR\34CommaList"=>1,"QuotelikeQR\34ConditionalExpression"=>1,"QuotelikeQR\34ContextualRegex"=>1,"QuotelikeQR\34Document"=>1,"QuotelikeQR\34Expr"=>1,"QuotelikeQR\34Expression"=>1,"QuotelikeQR\34List"=>1,"QuotelikeQR\34ListElem"=>1,"QuotelikeQR\34LowPrecedenceNotExpression"=>1,"QuotelikeQR\34PrefixPostfixTerm"=>1,"QuotelikeQR\34Quotelike"=>1,"QuotelikeQR\34Regex"=>1,"QuotelikeQR\34Regexp"=>1,"QuotelikeQR\34Statement"=>1,"QuotelikeQR\34Term"=>1,"QuotelikeQR\34Ternary"=>1,"QuotelikeQW\34Assignment"=>1,"QuotelikeQW\34BinaryExpression"=>1,"QuotelikeQW\34CommaList"=>1,"QuotelikeQW\34ConditionalExpression"=>1,"QuotelikeQW\34Document"=>1,"QuotelikeQW\34Expr"=>1,"QuotelikeQW\34Expression"=>1,"QuotelikeQW\34List"=>1,"QuotelikeQW\34ListElem"=>1,"QuotelikeQW\34LowPrecedenceNotExpression"=>1,"QuotelikeQW\34PrefixPostfixTerm"=>1,"QuotelikeQW\34Quotelike"=>1,"QuotelikeQW\34Statement"=>1,"QuotelikeQW\34Term"=>1,"QuotelikeQW\34Ternary"=>1,"QuotelikeQX\34Assignment"=>1,"QuotelikeQX\34BinaryExpression"=>1,"QuotelikeQX\34CommaList"=>1,"QuotelikeQX\34ConditionalExpression"=>1,"QuotelikeQX\34Document"=>1,"QuotelikeQX\34Expr"=>1,"QuotelikeQX\34Expression"=>1,"QuotelikeQX\34List"=>1,"QuotelikeQX\34ListElem"=>1,"QuotelikeQX\34LowPrecedenceNotExpression"=>1,"QuotelikeQX\34PrefixPostfixTerm"=>1,"QuotelikeQX\34Quotelike"=>1,"QuotelikeQX\34Statement"=>1,"QuotelikeQX\34Term"=>1,"QuotelikeQX\34Ternary"=>1,"QuotelikeS\34Assignment"=>1,"QuotelikeS\34BinaryExpression"=>1,"QuotelikeS\34CommaList"=>1,"QuotelikeS\34ConditionalExpression"=>1,"QuotelikeS\34Document"=>1,"QuotelikeS\34Expr"=>1,"QuotelikeS\34Expression"=>1,"QuotelikeS\34List"=>1,"QuotelikeS\34ListElem"=>1,"QuotelikeS\34LowPrecedenceNotExpression"=>1,"QuotelikeS\34PrefixPostfixTerm"=>1,"QuotelikeS\34Quotelike"=>1,"QuotelikeS\34Statement"=>1,"QuotelikeS\34Term"=>1,"QuotelikeS\34Ternary"=>1,"QuotelikeTR\34Assignment"=>1,"QuotelikeTR\34BinaryExpression"=>1,"QuotelikeTR\34CommaList"=>1,"QuotelikeTR\34ConditionalExpression"=>1,"QuotelikeTR\34Document"=>1,"QuotelikeTR\34Expr"=>1,"QuotelikeTR\34Expression"=>1,"QuotelikeTR\34List"=>1,"QuotelikeTR\34ListElem"=>1,"QuotelikeTR\34LowPrecedenceNotExpression"=>1,"QuotelikeTR\34PrefixPostfixTerm"=>1,"QuotelikeTR\34Quotelike"=>1,"QuotelikeTR\34Statement"=>1,"QuotelikeTR\34Term"=>1,"QuotelikeTR\34Ternary"=>1,"Regex\34Assignment"=>1,"Regex\34BinaryExpression"=>1,"Regex\34CommaList"=>1,"Regex\34ConditionalExpression"=>1,"Regex\34Document"=>1,"Regex\34Expr"=>1,"Regex\34Expression"=>1,"Regex\34List"=>1,"Regex\34ListElem"=>1,"Regex\34LowPrecedenceNotExpression"=>1,"Regex\34PrefixPostfixTerm"=>1,"Regex\34Quotelike"=>1,"Regex\34Statement"=>1,"Regex\34Term"=>1,"Regex\34Ternary"=>1,"Regexp\34Assignment"=>1,"Regexp\34BinaryExpression"=>1,"Regexp\34CommaList"=>1,"Regexp\34ConditionalExpression"=>1,"Regexp\34Document"=>1,"Regexp\34Expr"=>1,"Regexp\34Expression"=>1,"Regexp\34List"=>1,"Regexp\34ListElem"=>1,"Regexp\34LowPrecedenceNotExpression"=>1,"Regexp\34PrefixPostfixTerm"=>1,"Regexp\34Quotelike"=>1,"Regexp\34Statement"=>1,"Regexp\34Term"=>1,"Regexp\34Ternary"=>1,"ReturnStatement\34Assignment"=>1,"ReturnStatement\34BinaryExpression"=>1,"ReturnStatement\34CommaList"=>1,"ReturnStatement\34ConditionalExpression"=>1,"ReturnStatement\34Document"=>1,"ReturnStatement\34Expr"=>1,"ReturnStatement\34Expression"=>1,"ReturnStatement\34List"=>1,"ReturnStatement\34ListElem"=>1,"ReturnStatement\34LowPrecedenceNotExpression"=>1,"ReturnStatement\34PrefixPostfixTerm"=>1,"ReturnStatement\34Statement"=>1,"ReturnStatement\34Term"=>1,"ReturnStatement\34Ternary"=>1,"ScalarAccess\34Assignment"=>1,"ScalarAccess\34BinaryExpression"=>1,"ScalarAccess\34CommaList"=>1,"ScalarAccess\34ConditionalExpression"=>1,"ScalarAccess\34Document"=>1,"ScalarAccess\34Expr"=>1,"ScalarAccess\34Expression"=>1,"ScalarAccess\34List"=>1,"ScalarAccess\34ListElem"=>1,"ScalarAccess\34LowPrecedenceNotExpression"=>1,"ScalarAccess\34PrefixPostfixTerm"=>1,"ScalarAccess\34Statement"=>1,"ScalarAccess\34Term"=>1,"ScalarAccess\34Ternary"=>1,"ScalarAccess\34Var"=>1,"ScalarAccess\34Variable"=>1,"ScalarVar\34Assignment"=>1,"ScalarVar\34BinaryExpression"=>1,"ScalarVar\34CommaList"=>1,"ScalarVar\34ConditionalExpression"=>1,"ScalarVar\34Document"=>1,"ScalarVar\34Expr"=>1,"ScalarVar\34Expression"=>1,"ScalarVar\34List"=>1,"ScalarVar\34ListElem"=>1,"ScalarVar\34LowPrecedenceNotExpression"=>1,"ScalarVar\34PrefixPostfixTerm"=>1,"ScalarVar\34ScalarAccess"=>1,"ScalarVar\34Statement"=>1,"ScalarVar\34Term"=>1,"ScalarVar\34Ternary"=>1,"ScalarVar\34Var"=>1,"ScalarVar\34Variable"=>1,"Statement\34Document"=>1,"Str\34Assignment"=>1,"Str\34BinaryExpression"=>1,"Str\34CommaList"=>1,"Str\34ConditionalExpression"=>1,"Str\34Document"=>1,"Str\34Expr"=>1,"Str\34Expression"=>1,"Str\34List"=>1,"Str\34ListElem"=>1,"Str\34Literal"=>1,"Str\34LowPrecedenceNotExpression"=>1,"Str\34PrefixPostfixTerm"=>1,"Str\34Quotelike"=>1,"Str\34Statement"=>1,"Str\34Term"=>1,"Str\34Ternary"=>1,"String\34Assignment"=>1,"String\34BinaryExpression"=>1,"String\34CommaList"=>1,"String\34ConditionalExpression"=>1,"String\34Document"=>1,"String\34Expr"=>1,"String\34Expression"=>1,"String\34List"=>1,"String\34ListElem"=>1,"String\34Literal"=>1,"String\34LowPrecedenceNotExpression"=>1,"String\34PrefixPostfixTerm"=>1,"String\34Quotelike"=>1,"String\34Statement"=>1,"String\34Term"=>1,"String\34Ternary"=>1,"SubroutineDeclaration\34Document"=>1,"SubroutineDeclaration\34Statement"=>1,"Substitution\34Assignment"=>1,"Substitution\34BinaryExpression"=>1,"Substitution\34CommaList"=>1,"Substitution\34ConditionalExpression"=>1,"Substitution\34Document"=>1,"Substitution\34Expr"=>1,"Substitution\34Expression"=>1,"Substitution\34List"=>1,"Substitution\34ListElem"=>1,"Substitution\34LowPrecedenceNotExpression"=>1,"Substitution\34PrefixPostfixTerm"=>1,"Substitution\34Quotelike"=>1,"Substitution\34Statement"=>1,"Substitution\34Term"=>1,"Substitution\34Ternary"=>1,"Term\34Assignment"=>1,"Term\34BinaryExpression"=>1,"Term\34CommaList"=>1,"Term\34ConditionalExpression"=>1,"Term\34Document"=>1,"Term\34Expr"=>1,"Term\34Expression"=>1,"Term\34List"=>1,"Term\34ListElem"=>1,"Term\34LowPrecedenceNotExpression"=>1,"Term\34PrefixPostfixTerm"=>1,"Term\34Statement"=>1,"Term\34Ternary"=>1,"Ternary\34Assignment"=>1,"Ternary\34CommaList"=>1,"Ternary\34Document"=>1,"Ternary\34Expr"=>1,"Ternary\34Expression"=>1,"Ternary\34List"=>1,"Ternary\34LowPrecedenceNotExpression"=>1,"Ternary\34Statement"=>1,"Transliteration\34Assignment"=>1,"Transliteration\34BinaryExpression"=>1,"Transliteration\34CommaList"=>1,"Transliteration\34ConditionalExpression"=>1,"Transliteration\34Document"=>1,"Transliteration\34Expr"=>1,"Transliteration\34Expression"=>1,"Transliteration\34List"=>1,"Transliteration\34ListElem"=>1,"Transliteration\34LowPrecedenceNotExpression"=>1,"Transliteration\34PrefixPostfixTerm"=>1,"Transliteration\34Quotelike"=>1,"Transliteration\34Statement"=>1,"Transliteration\34Term"=>1,"Transliteration\34Ternary"=>1,"Typeglob\34Assignment"=>1,"Typeglob\34BinaryExpression"=>1,"Typeglob\34CommaList"=>1,"Typeglob\34ConditionalExpression"=>1,"Typeglob\34Document"=>1,"Typeglob\34Expr"=>1,"Typeglob\34Expression"=>1,"Typeglob\34List"=>1,"Typeglob\34ListElem"=>1,"Typeglob\34LowPrecedenceNotExpression"=>1,"Typeglob\34PrefixPostfixTerm"=>1,"Typeglob\34Statement"=>1,"Typeglob\34Term"=>1,"Typeglob\34Ternary"=>1,"UseStatement\34Document"=>1,"UseStatement\34Statement"=>1,"Var\34Assignment"=>1,"Var\34BinaryExpression"=>1,"Var\34CommaList"=>1,"Var\34ConditionalExpression"=>1,"Var\34Document"=>1,"Var\34Expr"=>1,"Var\34Expression"=>1,"Var\34List"=>1,"Var\34ListElem"=>1,"Var\34LowPrecedenceNotExpression"=>1,"Var\34PrefixPostfixTerm"=>1,"Var\34Statement"=>1,"Var\34Term"=>1,"Var\34Ternary"=>1,"VarArray\34ArrayAccess"=>1,"VarArray\34Assignment"=>1,"VarArray\34BinaryExpression"=>1,"VarArray\34CommaList"=>1,"VarArray\34ConditionalExpression"=>1,"VarArray\34Document"=>1,"VarArray\34Expr"=>1,"VarArray\34Expression"=>1,"VarArray\34List"=>1,"VarArray\34ListElem"=>1,"VarArray\34LowPrecedenceNotExpression"=>1,"VarArray\34PrefixPostfixTerm"=>1,"VarArray\34Statement"=>1,"VarArray\34Term"=>1,"VarArray\34Ternary"=>1,"VarArray\34Var"=>1,"VarArray\34Variable"=>1,"VarDecl\34Assignment"=>1,"VarDecl\34BinaryExpression"=>1,"VarDecl\34CommaList"=>1,"VarDecl\34ConditionalExpression"=>1,"VarDecl\34Document"=>1,"VarDecl\34Expr"=>1,"VarDecl\34Expression"=>1,"VarDecl\34List"=>1,"VarDecl\34ListElem"=>1,"VarDecl\34LowPrecedenceNotExpression"=>1,"VarDecl\34PrefixPostfixTerm"=>1,"VarDecl\34Statement"=>1,"VarDecl\34Term"=>1,"VarDecl\34Ternary"=>1,"VarHash\34Assignment"=>1,"VarHash\34BinaryExpression"=>1,"VarHash\34CommaList"=>1,"VarHash\34ConditionalExpression"=>1,"VarHash\34Document"=>1,"VarHash\34Expr"=>1,"VarHash\34Expression"=>1,"VarHash\34HashAccess"=>1,"VarHash\34List"=>1,"VarHash\34ListElem"=>1,"VarHash\34LowPrecedenceNotExpression"=>1,"VarHash\34PrefixPostfixTerm"=>1,"VarHash\34Statement"=>1,"VarHash\34Term"=>1,"VarHash\34Ternary"=>1,"VarHash\34Var"=>1,"VarHash\34Variable"=>1,"Variable\34Assignment"=>1,"Variable\34BinaryExpression"=>1,"Variable\34CommaList"=>1,"Variable\34ConditionalExpression"=>1,"Variable\34Document"=>1,"Variable\34Expr"=>1,"Variable\34Expression"=>1,"Variable\34List"=>1,"Variable\34ListElem"=>1,"Variable\34LowPrecedenceNotExpression"=>1,"Variable\34PrefixPostfixTerm"=>1,"Variable\34Statement"=>1,"Variable\34Term"=>1,"Variable\34Ternary"=>1,"VariableArray\34ArrayAccess"=>1,"VariableArray\34Assignment"=>1,"VariableArray\34BinaryExpression"=>1,"VariableArray\34CommaList"=>1,"VariableArray\34ConditionalExpression"=>1,"VariableArray\34Document"=>1,"VariableArray\34Expr"=>1,"VariableArray\34Expression"=>1,"VariableArray\34List"=>1,"VariableArray\34ListElem"=>1,"VariableArray\34LowPrecedenceNotExpression"=>1,"VariableArray\34PrefixPostfixTerm"=>1,"VariableArray\34Statement"=>1,"VariableArray\34Term"=>1,"VariableArray\34Ternary"=>1,"VariableArray\34Var"=>1,"VariableArray\34Variable"=>1,"VariableDeclaration\34Assignment"=>1,"VariableDeclaration\34BinaryExpression"=>1,"VariableDeclaration\34CommaList"=>1,"VariableDeclaration\34ConditionalExpression"=>1,"VariableDeclaration\34Document"=>1,"VariableDeclaration\34Expr"=>1,"VariableDeclaration\34Expression"=>1,"VariableDeclaration\34List"=>1,"VariableDeclaration\34ListElem"=>1,"VariableDeclaration\34LowPrecedenceNotExpression"=>1,"VariableDeclaration\34PrefixPostfixTerm"=>1,"VariableDeclaration\34Statement"=>1,"VariableDeclaration\34Term"=>1,"VariableDeclaration\34Ternary"=>1,"VariableHash\34Assignment"=>1,"VariableHash\34BinaryExpression"=>1,"VariableHash\34CommaList"=>1,"VariableHash\34ConditionalExpression"=>1,"VariableHash\34Document"=>1,"VariableHash\34Expr"=>1,"VariableHash\34Expression"=>1,"VariableHash\34HashAccess"=>1,"VariableHash\34List"=>1,"VariableHash\34ListElem"=>1,"VariableHash\34LowPrecedenceNotExpression"=>1,"VariableHash\34PrefixPostfixTerm"=>1,"VariableHash\34Statement"=>1,"VariableHash\34Term"=>1,"VariableHash\34Ternary"=>1,"VariableHash\34Var"=>1,"VariableHash\34Variable"=>1,"VariableScalar\34Assignment"=>1,"VariableScalar\34BinaryExpression"=>1,"VariableScalar\34CommaList"=>1,"VariableScalar\34ConditionalExpression"=>1,"VariableScalar\34Document"=>1,"VariableScalar\34Expr"=>1,"VariableScalar\34Expression"=>1,"VariableScalar\34List"=>1,"VariableScalar\34ListElem"=>1,"VariableScalar\34LowPrecedenceNotExpression"=>1,"VariableScalar\34PrefixPostfixTerm"=>1,"VariableScalar\34ScalarAccess"=>1,"VariableScalar\34Statement"=>1,"VariableScalar\34Term"=>1,"VariableScalar\34Ternary"=>1,"VariableScalar\34Var"=>1,"VariableScalar\34Variable"=>1,"VarScalar\34Assignment"=>1,"VarScalar\34BinaryExpression"=>1,"VarScalar\34CommaList"=>1,"VarScalar\34ConditionalExpression"=>1,"VarScalar\34Document"=>1,"VarScalar\34Expr"=>1,"VarScalar\34Expression"=>1,"VarScalar\34List"=>1,"VarScalar\34ListElem"=>1,"VarScalar\34LowPrecedenceNotExpression"=>1,"VarScalar\34PrefixPostfixTerm"=>1,"VarScalar\34ScalarAccess"=>1,"VarScalar\34Statement"=>1,"VarScalar\34Term"=>1,"VarScalar\34Ternary"=>1,"VarScalar\34Var"=>1,"VarScalar\34Variable"=>1,"VersionNumber\34Assignment"=>1,"VersionNumber\34BinaryExpression"=>1,"VersionNumber\34CommaList"=>1,"VersionNumber\34ConditionalExpression"=>1,"VersionNumber\34Document"=>1,"VersionNumber\34Expr"=>1,"VersionNumber\34Expression"=>1,"VersionNumber\34List"=>1,"VersionNumber\34ListElem"=>1,"VersionNumber\34Literal"=>1,"VersionNumber\34LowPrecedenceNotExpression"=>1,"VersionNumber\34Num"=>1,"VersionNumber\34Number"=>1,"VersionNumber\34PrefixPostfixTerm"=>1,"VersionNumber\34Statement"=>1,"VersionNumber\34Term"=>1,"VersionNumber\34Ternary"=>1,"VString\34Assignment"=>1,"VString\34BinaryExpression"=>1,"VString\34CommaList"=>1,"VString\34ConditionalExpression"=>1,"VString\34Document"=>1,"VString\34Expr"=>1,"VString\34Expression"=>1,"VString\34List"=>1,"VString\34ListElem"=>1,"VString\34Literal"=>1,"VString\34LowPrecedenceNotExpression"=>1,"VString\34Num"=>1,"VString\34Number"=>1,"VString\34PrefixPostfixTerm"=>1,"VString\34Statement"=>1,"VString\34Str"=>1,"VString\34String"=>1,"VString\34Term"=>1,"VString\34Ternary"=>1,"VString\34VersionNumber"=>1,"Whitespace\34OWS"=>1,
659              
660             # End of autogenerated type ISA hierarchy
661              
662             );
663              
664             # Convert type aliases into standard PPR types...
665 168     168   395 sub _resolve_type {
666             my ($type, $user_defined_type_for) = @_;
667              
668 168   100     1714 # Identify valid user-defined types in the current calling scope...
  5883         9869  
669             $user_defined_type_for //= { map { m{ \A Keyword::Declare \s* keytype: (\w+) = (.*) }xms } keys %^H };
670 168         956  
671             while ($type =~ /\A \w++ \Z/x) {
672             $type = $user_defined_type_for->{$type}
673             // $ACTUAL_TYPE_OF{$type}
674 171   66     1339 // croak "Unknown type ($type) for keyword parameter.\nDid you mean: ",
  0   33     0  
675             join(' or ', grep { lc substr($_,0,1) eq lc substr($type,0,1) } keys %ACTUAL_TYPE_OF);
676             }
677 168 100       14709  
    100          
    50          
678 1         11 if ($type =~ m{\A (?: q\s*\S | ' ) (.*) \S \z }x) {
679             return quotemeta $1
680             }
681 166 50       3514 elsif ($type =~ $REGEX_TYPE ) {
682 166         1289 $type =~ $REGEX_PAT or die "Keyword::Declare internal error: weird regex";
683 166         797 my $pat = $+{pattern};
684 166         531 my $mods = $+{modifiers};
685 166         1328 $pat =~ s{(?
686             return "(?$mods:$pat)";
687             }
688 1         6 elsif ($type =~ $TYPE_JUNCTION) {
  2         9  
689             return join '|', map { _resolve_type($_, $user_defined_type_for) } split /[|]/, $type;
690             }
691 0         0 else {
692             die 'Keyword::Declare internal error: incomprehensible type: [$type]';
693             }
694              
695             }
696              
697             # Convert named types and explicit regexes or strings to matcher regex...
698 178     178   341 sub _convert_type_to_matcher {
699             my ($param) = @_;
700 178         259  
701             my $matcher;
702              
703             # Convert type specification to PPR subrule invocations and build a description...
704 178         390 # ...for named types...
705 178 100       1045 my $type = $param->{type};
706             if ($type =~ m{\A \w++ (?: [|] \w++ )* \Z}x) {
707 163         687 # Extract component types...
708             my @types = split /[|]/, $type;
709              
710 163         382 # First set up pseudo-inheritance...
711 166         881 for my $component_type (@types) {
712             $isa{$component_type, $type} = 1;
713             }
714              
715 163   33     820 # Convert component types to regexes...
716 163 100       621 $param->{desc} //= do {
717 163         335 my $desc = $param->{name} ? "<$param->{name}>" : '<'.join(' or ', @types).'>';
718 163         662 $desc =~ tr/_/ /;
719             $desc;
720 163         381 };
  166         405  
721             $type = '/' . join('|', map { _resolve_type( $_ ) } @types) . '/';
722              
723             }
724              
725 178 100       7957 # ...for literal string types...
    50          
726             if ($type =~ m{\A (?: q\s*\S | ' ) (.*) \S \z }x) {
727             $param->{desc}
728 7 100 33     46 //= ($param->{name}
  3         6  
  3         6  
  3         11  
729             ? do{ my $name = "<$param->{name}>"; $name =~ tr/_/ /; $name }
730             : $1
731 7         20 );
732             $matcher = '(?:' . quotemeta($1) . ')';
733             }
734              
735             # ...for regex types...
736 171 50       4039 elsif ($type =~ $REGEX_TYPE ) {
737 171         2250 $type =~ $REGEX_PAT or die "Keyword::Declare internal error: weird regex";
738 171         732 my %match = %+;
739             $match{pattern} =~ s{(?
740             $param->{desc}
741 171 100 66     513 //= ($param->{name}
  7         26  
  7         16  
  7         30  
742             ? do{ my $name = "<$param->{name}>"; $name =~ tr/_/ /; $name }
743             : "/$match{pattern}/$match{modifiers}"
744 171         699 );
745             $matcher = "(?$match{modifiers}:$match{pattern})";
746             }
747              
748             # Incomprehensible types...
749 0         0 else {
750             die "Keyword::Declare internal error: incomprehensible type: [$type]"
751             }
752 178         533  
753             return $matcher;
754             }
755              
756             # This class allows captures from type-regexes to be preserved and accessed...
757             {
758             package Keyword::Declare::Arg;
759 117     117   319375 use overload
760 19         271 '""' => sub { $_[0]{""} },
761 19     19   146555 fallback => 1
  19         77  
762             }
763              
764             # Extract a string or a Keyword::Declare::Arg object from the most recent match...
765 128     128   1287801 sub _objectify {
766             my ($match_str, $captures_ref) = @_;
767              
768 128         795 # Trim any leading Perlish whitespace from the match...
769             $match_str =~ s{^(?: \s*+ (?: [#].*\n \s*+)*+)}{}x;
770              
771 128 100       304 # Just return the match if there were no captures...
  128         35289  
772             return $match_str if !keys %{$captures_ref};
773 92         282  
  92         583  
774 92   100     698 my $obj = { q{}=>$match_str, %{$captures_ref} };
775 92         860 $obj->{':sep'} = delete( $obj->{____KD___sep} ) // q{};
776             return bless $obj, 'Keyword::Declare::Arg';
777             }
778              
779             # Convert the keyword's parameter list to various useful representations...
780 157     157   285 sub _unpack_signature {
781             my ($keyword_info_ref) = @_;
782              
783 157         324 # We're setting up all these entries...
784 157         429 my $sig_vars = ""; # List of variables into which keyword args are unpacked
785 157         363 $keyword_info_ref->{sig_vars_unpack} = ""; # Statements that unpack keyword args
786 157         313 $keyword_info_ref->{sig_matcher} = ""; # Pattern that matches entire arg list
787 157         344 $keyword_info_ref->{sig_skip_matcher} = ""; # Pattern that matches entire arg list without captures
788 157         310 $keyword_info_ref->{sig} = []; # Array of parameter types
789 157         315 $keyword_info_ref->{sig_quantified} = []; # Array of quantified parameter types
790 157         546 $keyword_info_ref->{sig_names} = []; # Names of each parameter ("" if no unnamed)
791             $keyword_info_ref->{sig_defaults} = {}; # Defaults for any parameter that has them
792              
793 157         288 # Walk through the parameters...
794 157         241 my $not_post = 1;
  157         561  
795 177 100       458 for my $param (@{$keyword_info_ref->{param_list}}) {
796 1         2 if (!defined($param)) {
797 1         3 $not_post = 0;
798             next;
799             }
800              
801 176         439 # Generate a regex to match this parameter (note: modifies $param!)...
802             my $matcher = _convert_type_to_matcher($param);
803              
804 176         333 # Generate a regex to match the separator (if any)...
805 176 100       484 my $sep;
806 2         10 if ($param->{sep}) {
807             $sep = _convert_type_to_matcher({name=>':sep', type=>$param->{sep}});
808             }
809              
810 176 50       442 # Resolve implicit quantification (and any default value)...
811 0         0 if (exists $param->{default}) {
812 0         0 my $def = $param->{default};
813 0         0 $def =~ s{\A (?: qq? \s* \S | ["']) (.*) \S \Z }{$1}gx;
814 0 0 0     0 $keyword_info_ref->{sig_defaults}{$param->{name}} = $def;
      0        
815             $param->{quantifier} //= $param->{sigil} && $param->{sigil} eq '@' ? '*' : '?';
816             }
817 176 100 100     1195 else {
      100        
818             $param->{quantifier} //= $param->{sigil} && $param->{sigil} eq '@' ? '+' : '';
819             }
820              
821             # Matchers handle leading whitespace (unless they ARE whitespace)...
822 176 50       686 $matcher = "(?:(?&PerlOWS)$matcher)"
823             if $param->{type} !~ m{^/\(\?\&Perl[ON]WS\)/$};
824              
825 176         360 # Quantified parameters are repeatable...
826 176 100       446 my $single_matcher = $matcher;
827             if ($param->{quantifier}) {
828 22 100       56 # Unseparated parameters are easy...
829 20         72 if (!$sep) {
830             $matcher = "(?:$matcher$param->{quantifier})";
831             }
832             # Separated parameters are more complex...
833 2         9 else {
834 2 100       8 $matcher = "(?:$matcher(?:(?&PerlOWS)$sep(?&PerlOWS)$matcher)*)";
835 2         7 $matcher .= '?' if $param->{quantifier} eq '*';
836             $single_matcher .= "(?=(?<____KD___sep>$sep))?";
837             }
838             }
839              
840              
841 176         322 # Named parameters have to be named captured...
842 176 100       441 my $skip_matcher = $matcher;
843 165         455 if ($param->{name}) {
844             $matcher = "(?<$param->{name}>$matcher)";
845             }
846              
847 176         446 # Accumulate the signature matching pattern...
848 176 100       511 $keyword_info_ref->{sig_matcher} .= $matcher;
849             $keyword_info_ref->{sig_skip_matcher} .= $skip_matcher
850             if $not_post;
851              
852 176         294 # Accumulate signature types and names (if any)...
  176         501  
853 176         276 push @{ $keyword_info_ref->{sig} }, $param->{type};
  176         504  
854 176   100     274 push @{ $keyword_info_ref->{sig_quantified} }, $param->{type}.$param->{quantifier};
  176         534  
855             push @{ $keyword_info_ref->{sig_names} }, $param->{name} // q{};
856              
857 176 100       440 # Accumulate variable list into which parameters will be unpacked...
858 165 100 100     876 if ($param->{name}) {
859             my $match_once = $param->{sigil} ne '$' || $single_matcher =~ /\(\?
860             ? "m{$single_matcher\$PPR::GRAMMAR}"
861             : "m{}";
862 165         426  
863             $sig_vars .= "$param->{sigil}$param->{name},";
864 165 100       1062 $keyword_info_ref->{sig_vars_unpack} .= "$param->{sigil}$param->{name} = "
865             . ( $param->{sigil} eq '$'
866             ? qq{do { my \$arg = shift();
867             \$arg =~ $match_once;
868             Keyword::Declare::_objectify(\$arg,{%+});
869             };
870             }
871             : qq{do { my \@data;
872             my \$arg = shift();
873             while (\$arg =~ /\\S/ && \$arg =~ ${match_once}g) {
874             push \@data,
875             Keyword::Declare::_objectify(\$&,{%+});
876             }
877             \@data;
878             };
879             }
880             )
881             }
882 11         37 else {
883             $keyword_info_ref->{sig_vars_unpack} .= 'shift();';
884             }
885             }
886              
887             # use Data::Show; show $keyword_info_ref->{sig_vars_unpack};
888              
889 157         319 # Build a human readable version of the signature...
  157         571  
890             $keyword_info_ref->{sig_desc} = '(' . join(',', @{$keyword_info_ref->{sig_quantified}}) . ')';
891              
892             # Build a pretty description for debugging and error messages...
893 157         556 $keyword_info_ref->{syntax} = "$keyword_info_ref->{keyword} "
  176         612  
  177         549  
894             . join(" ", map { $_->{desc} } grep {defined} @Keyword::Declare::params);
895              
896 157         511 # Build a regex that matches the keyword plus its arguments...
897 157         443 $keyword_info_ref->{matcher} = "$keyword_info_ref->{keyword}$keyword_info_ref->{sig_matcher}";
898             $keyword_info_ref->{skip_matcher} = "$keyword_info_ref->{keyword}$keyword_info_ref->{sig_skip_matcher}";
899              
900 157         245 # Precompute the length of the signature (for multiple-dispatch tie-breaking)...
  157         360  
901             $keyword_info_ref->{sig_len} = scalar @{ $keyword_info_ref->{sig} };
902              
903             # Consolidate the signature-unpacking code...
904 157 100       701 $keyword_info_ref->{sig_vars_unpack} =
905             $sig_vars ? "my ($sig_vars); $keyword_info_ref->{sig_vars_unpack}" : q{};
906             }
907              
908              
909             # Extract and verify any attrs specified on the keyword...
910 157     157   342 sub _unpack_attrs {
911             my ($keyword_info_ref) = @_;
912              
913 157 100       518 # Are there any keyword attrs to unpack???
914             if ($keyword_info_ref->{attrs}) {
915             # Extract any :prefer or :keepspace attr...
916 3         30 $keyword_info_ref->{prefer}
917             = $keyword_info_ref->{attrs} =~ s{\bprefer\b}{}xms;
918 3         16 $keyword_info_ref->{keepspace}
919             = $keyword_info_ref->{attrs} =~ s{\bkeepspace\b}{}xms;
920              
921 3 50       85 # Extract any :desc(...) attr...
922 0         0 if ($keyword_info_ref->{attrs} =~ s{\bdesc\( (.*?) \)}{}xms) {
923             $keyword_info_ref->{desc} = $1;
924             }
925 3         19 else {
926             $keyword_info_ref->{desc} = $keyword_info_ref->{keyword};
927             }
928              
929 3 50       19 # Extract any :prefix(...) attr...
930 0         0 if ($keyword_info_ref->{attrs} =~ s{\bprefix\( \s* (\$[^\W\d]\w*) \s* \)}{}xms) {
931             $keyword_info_ref->{prefix} = $1;
932             }
933              
934             # Complain about anything else...
935 3 50       13 croak ":then attribute specified too late (must come immediately after parameter list)"
936             if $keyword_info_ref->{attrs} =~ s{\bthen\b}{}xms;
937 3 50       19 croak "Invalid attribute: $keyword_info_ref->{attrs}"
938             if $keyword_info_ref->{attrs} =~ /[^\s:]/;
939             }
940             }
941              
942              
943             # Convert a {{{...}}} interpolated keyword body to a normal body...
944 23     23   62 sub _convert_triple_block {
  23         116  
945             my ($block, $keyword) = @{shift()}{qw< block keyword >};
946              
947 23         81 # Peel off extra curlies...
948             $block = substr($block, 3, -3);
949              
950 23 100       143 # Report unclosed «...}> interpolations...
951 6         74 if ($block =~ m{ <\{ (? (? \s* \S*) .*? ) (?: <\{ | « | \Z ) }xms) {
952             my %match = %+;
953             croak qq[Missing }> on interpolation <{$match{leader}...\n]
954 6 50       55 . qq[in string-style block of keyword $keyword\ndefined]
955             if $match{interpolation} !~ m{ \}> }xms;
956 23 100       153 }
957 10         138 if ($block =~ m{ « (? (? \s* \S*) .*? ) (?: <\{ | « | \Z ) }xms) {
958             my %match = %+;
959             croak qq[Missing » on interpolation «$match{leader}...\n]
960 10 50       78 . qq[in string-style block of keyword $keyword\ndefined]
961             if $match{interpolation} !~ m{ » }xms;
962             }
963              
964 23         237 # Convert the inter polated text to code that does the interpolations...
965             $block =~ s{
966             « (? .*? ) »
967             |
968             <\{ (? .*? ) \}>
969             |
970             (? .+? ) (?= <\{ | « | \z )
971 65 100       427 }{
  43 50       358  
972 22         194 if (exists $+{literal_code} ) { 'qq{' . quotemeta($+{literal_code}) . '},'; }
973 0         0 elsif (exists $+{interpolation}) { qq{ do{$+{interpolation}}, }; }
  0         0  
  0         0  
974             else { say {*STDERR} 'Keyword::Declare internal error in {{{...}}} block'; exit; }
975             }gexms;
976              
977 23         136 # Build and return the block's new source code...
978             return "{ return join '', $block; }";
979             }
980              
981              
982             # Transform a keyword invocation into the code generated by the keyword's body...
983 76     76   422 sub _insert_replacement_code {
984             my ($src_ref, $ID, $file, $line, $active_keywords) = @_;
985              
986 76         263 # Unpack keyword information...
987             my $keyword = $keyword_impls[$ID];
988              
989 76         2637484 # Remove the arguments from the source code...
990 76         29067 $$src_ref =~ s{ \A ($keyword->{sig_matcher}) $active_keywords $PPR::GRAMMAR }{}xms;
991 76         602 my %args = %+;
992             for my $argname (keys %args) {
993 118 100 50     740 $args{$argname} = $keyword->{sig_defaults}{$argname} // q{}
994             if $args{$argname} eq q{};
995 76         385 }
996 76         207 my $arg_list = $1;
  76         653  
997             my @args = @args{ @{$keyword_impls[$ID]{sig_names}} };
998              
999             # Tidy them, if requested...
1000             @args = map {
1001 130 100       964 !defined($_) ? undef
  91 100       199  
1002 91         432 : m{\S} ? do { my $arg = $_;
1003 91         914 $arg =~ s{\A\s*+(?:\#.*\n\s*+)*+}{};
1004 91         364 $arg =~ s{\s*+(?:\#.*\n\s*+)*+\z}{};
1005             $arg;
1006             }
1007             : $_
1008 76 50       488 } @args
1009             if !$keyword->{keepspace};
1010              
1011 76         340 # Adjust the line number so trailing code stays correct...
1012             $line += $arg_list =~ tr/\n//;
1013              
1014 76   50     538 # Generate replacement code...
1015             my $replacement_code = $keyword->{generator}->(@args) // q{};
1016              
1017 76 50       50555 # If debugging requested, provide a summary of the substitution...
1018 0         0 if (${^H}{"Keyword::Declare debug"}) {
1019 0         0 my $keyword = " $keyword_impls[$ID]{syntax}";
1020 0         0 my $from = " $keyword_impls[$ID]{keyword} $arg_list";
1021 0         0 my $to = $replacement_code;
1022 0         0 $to =~ s{\A\s*\n|\n\s*\Z}{}gm;
1023 0         0 $to =~ s{\h+}{ }g;
1024             $to =~ s{^}{ }gm;
1025 0         0  
1026             my $msg
1027             = ("#" x 50) . "\n"
1028             . " Keyword macro defined at $keyword_impls[$ID]{location}:\n\n$keyword\n\n"
1029             . " Converted code at $file line $line:" . "\n\n$from\n\n"
1030             . " Into:" . "\n\n$to\n\n"
1031 0         0 . ("#" x 50) . "\n";
1032 0         0 $msg =~ s{^}{###}gm;
1033             warn $msg;
1034             }
1035              
1036 76         411 # Track possible cycles...
  4         30  
1037 4 50 33     29 $$src_ref =~ s{^(\#KDCT:_:_:)(\d+)([^\n]*)}
1038             { my ($comment, $count, $trace) = ($1, $2, $3);
1039 4         38 croak "Likely keyword substitution cycle:\n $trace\nCompilation abandoned",
1040             if $count > $NESTING_THRESHOLD && $trace =~ m{(\w++) --> .+ --> \1};
1041             $comment.($count+1).$trace." --> $keyword->{keyword}";
1042             }gexms;
1043 76         690  
1044             # Install the replacement code...
1045             $$src_ref = "$replacement_code\n#KDCT:_:_:1 $keyword->{keyword}\n#line $line $file\n" . $$src_ref;
1046              
1047 76         35085 # Pre-empt addition of extraneous trailing newline by Keyword::Simple...
1048             # [REMOVE WHEN UPSTREAM MODULE (Keyword::Simple) IS FIXED]
1049             $$src_ref =~ s{\n\z}{};
1050             }
1051              
1052 8     8   25 # Compare two types...
1053             sub _is_narrower {
1054             my ($type_a, $type_b) = @_;
1055 8 100       26  
1056             # Short-circuit on identity...
1057             return 0 if $type_a eq $type_b;
1058 7 100       51  
    50          
1059 7 100       40 # Otherwise, work out the metatypes of the types...
    50          
1060             my $kind_a = $type_a =~ /\A'|\Aq\W/ ? 'literal' : $type_a =~ m{\A/|\Am\W}xms ? 'pattern' : 'typename';
1061             my $kind_b = $type_b =~ /\A'|\Aq\W/ ? 'literal' : $type_b =~ m{\A/|\Am\W}xms ? 'pattern' : 'typename';
1062 7 100 100     39  
1063 5 100       22 # If both are named types, try the standard inheritance hierarchy rules...
1064 2 50       9 if ($kind_a eq 'typename' && $kind_b eq 'typename') {
1065             return +1 if $isa{$type_a,$type_b};
1066             return -1 if $isa{$type_b,$type_a};
1067             }
1068 2         10  
1069             # Otherwise, the metatype names "just happen" to be in narrowness order ;-)...
1070             return $kind_a cmp $kind_b;
1071             }
1072              
1073 5     5   16 # Compare two type signatures (of equal length)...
1074             sub _cmp_signatures {
1075             my ($sig_a, $sig_b) = @_;
1076 5         9  
1077 5         17 # Track relative ordering parameter-by-parameter...
1078             my $partial_ordering = 0;
1079 8         29 for my $n (0 .. $#$sig_a) {
1080             # Find the ordering of the next pair from the two lists...
1081             my $is_narrower = _is_narrower($sig_a->[$n], $sig_b->[$n]);
1082 8 100 100     46  
1083             # If this pair's ordering contradicts the ordering so far, there is no ordering...
1084             return 0 if $is_narrower && $is_narrower == -$partial_ordering;
1085 6   66     35  
1086             # Otherwise if there's an ordering, it becomes the "ordering so far"...
1087             $partial_ordering ||= $is_narrower;
1088             }
1089 3         10  
1090             # If we make it through the entire list, return the resulting ordering...
1091             return $partial_ordering;
1092             }
1093              
1094 3     3   30 # Resolve ambiguous argument lists using Perl6-ish multiple dispatch rules...
1095             sub _resolve_matches {
1096             my @IDs = @_;
1097 3         27  
  14         49  
1098 14 50       35 # Extend type hierarchy...
1099 0         0 my @keytype_isa = map { my ($derived, $base) = m{ \A Keyword::Declare \s+ keytype:(\w+)=(\w+) \z}xms;
1100 0         0 if ($derived) {
1101 0         0 my @ancestors = map { my $anc = $_;
1102             $anc =~ s{ \A $base $; }
1103 0         0 {$derived$;}xms;
  0         0  
1104             $anc => 1
1105 0         0 }
1106             grep { m{ \A $base $; }xms }
1107             keys %isa;
1108 14         33 $derived.$;.$base => 1, @ancestors;
1109             }
1110             else {
1111 3         2180 ();
1112             }
1113             } keys %^H;
1114 3         145 local %isa = ( %isa, @keytype_isa );
  7         36  
1115              
1116             # Track narrownesses...
1117 3         28 my %narrower = map { $_ => [] } 0..$#IDs;
1118 7         29  
1119             # Compare all signatures, recording definitive differences in narrowness...
1120 5         29 for my $index_1 (0 .. $#IDs) {
1121             for my $index_2 ($index_1+1 .. $#IDs) {
1122 5 100       25 my $narrowness = _cmp_signatures($keyword_impls[$IDs[$index_1]]{sig},
  2 100       5  
  2         10  
1123 1         2 $keyword_impls[$IDs[$index_2]]{sig});
  1         6  
1124              
1125             if ($narrowness > 0) { push @{$narrower{$index_1}}, $index_2; }
1126             elsif ($narrowness < 0) { push @{$narrower{$index_2}}, $index_1; }
1127             }
1128 3         13 }
  7         11  
  7         25  
1129 3         11  
1130             # Was there a signature narrower than all the others???
1131             my $max_narrower = max map { scalar @{$_} } values %narrower;
1132 3 100       80 my $unique_narrowest = $max_narrower == $#IDs;
1133              
1134             # If not, return the entire set...
1135 2         9 return @IDs if !$unique_narrowest;
  4         6  
  4         135  
1136              
1137             # Otherwise, return the narrowest...
1138             return @IDs[ grep { @{$narrower{$_}} >= $max_narrower } keys %narrower ];
1139             }
1140              
1141             1; # Magic true value required at end of module
1142             __END__