File Coverage

blib/lib/Parse/KeywordX.pm
Criterion Covered Total %
statement 82 93 88.1
branch 14 36 38.8
condition 5 6 83.3
subroutine 14 15 93.3
pod 0 5 0.0
total 115 155 74.1


line stmt bran cond sub pod time code
1 41     41   693 use 5.014;
  41         96  
2 41     41   218 use strict;
  41         53  
  41         800  
3 41     39   277 use warnings;
  39         46  
  39         963  
4              
5 39     39   151 use Exporter::Tiny ();
  39         43  
  39         1830  
6              
7             package Parse::KeywordX;
8              
9             our $AUTHORITY = 'cpan:TOBYINK';
10             our $VERSION = '0.037';
11              
12 39     38   2663 use Text::Balanced qw( extract_bracketed );
  38         71609  
  38         1758  
13 38     38   147 use PadWalker qw( closed_over set_closed_over peek_my );
  38         42  
  38         2089  
14 38     38   136 use Parse::Keyword {};
  38         41  
  38         236  
15              
16             our @ISA = qw( Exporter::Tiny );
17             our @EXPORT = qw( parse_name parse_variable parse_trait parse_block_or_match );
18              
19             #### From p5-mop-redux
20             sub read_tokenish ()
21             {
22 0     0 0 0 my $token = '';
23 0 0       0 if ((my $next = lex_peek) =~ /[\$\@\%]/)
24             {
25 0         0 $token .= $next;
26 0         0 lex_read;
27             }
28 0         0 while ((my $next = lex_peek) =~ /\S/)
29             {
30 0         0 $token .= $next;
31 0         0 lex_read;
32 0 0       0 last if ($next . lex_peek) =~ /^\S\b/;
33             }
34 0         0 return $token;
35             }
36              
37             #### From p5-mop-redux
38             sub parse_name
39             {
40 415     415 0 573 my ($what, $allow_package, $stop_at_single_colon) = @_;
41 415         427 my $name = '';
42            
43             # XXX this isn't quite right, i think, but probably close enough for now?
44 38     38   12740 my $start_rx = qr/^[\p{ID_Start}_]$/;
  38         84  
  38         516  
  415         1012  
45 415         724 my $cont_rx = qr/^\p{ID_Continue}$/;
46 415         414 my $char_rx = $start_rx;
47            
48 415         324 while (1)
49             {
50 1794         2122 my $char = lex_peek;
51            
52 1794 50       2436 last unless length $char;
53 1794 100 100     5975 if ($char =~ $char_rx)
    100          
54             {
55 1373         1137 $name .= $char;
56 1373         1392 lex_read;
57 1373         1172 $char_rx = $cont_rx;
58             }
59             elsif ($allow_package && $char eq ':')
60             {
61 11 0       40 if (lex_peek(3) !~ /^::(?:[^:]|$)/)
62             {
63 5 50       28 return $name if $stop_at_single_colon;
64 0         0 die("Not a valid $what name: $name" . read_tokenish);
65             }
66 6         14 $name .= '::';
67 6         12 lex_read(2);
68             }
69             else
70             {
71 410         504 last;
72             }
73             }
74            
75 410 50       680 die("Not a valid $what name: " . read_tokenish) unless length $name;
76            
77 410 100       1389 ($name =~ /\A::/) ? "main$name" : $name;
78             }
79              
80             sub parse_variable
81             {
82 193     193 0 202 my $allow_bare_sigil = $_[0];
83            
84 193         356 my $sigil = lex_peek(1);
85 193 50 66     735 ($sigil eq '$' or $sigil eq '@' or $sigil eq '%')
86             ? lex_read(1)
87             : die("Not a valid variable name: " . read_tokenish);
88            
89 193         199 my $name = $sigil;
90            
91 193         209 my $escape_char = 0;
92 193 0       338 if (lex_peek(2) eq '{^')
93             {
94 3         50 lex_read(2);
95 3         6 $name .= '{^';
96 3         7 $name .= parse_name('escape-char variable', 0);
97 3 0       10 lex_peek(1) eq '}'
98             ? ( lex_read(1), ($name .= '}') )
99             : die("Expected closing brace after escape-char variable");
100 3         13 return $name;
101             }
102            
103 190 0       734 if (lex_peek =~ /[\w:]/)
104             {
105 182         520 $name .= parse_name('variable', 1, 1);
106 182         437 return $name;
107             }
108            
109 8 50       25 if ($allow_bare_sigil)
110             {
111 8         19 return $name;
112             }
113            
114 0         0 die "Expected variable name";
115             }
116              
117             sub parse_trait
118             {
119 33     33 0 59 my $name = parse_name('trait', 0);
120             #lex_read_space;
121            
122 33         39 my $extracted;
123 33 0       86 if (lex_peek eq '(')
124             {
125 7         18 my $peek = lex_peek(1000);
126 7         22 $extracted = extract_bracketed($peek, '()');
127 7         618 lex_read(length $extracted);
128 7         13 lex_read_space;
129 7         29 $extracted =~ s/(?: \A\( | \)\z )//xgsm;
130             }
131            
132 33         62 my $evaled = 1;
133 33 100       76 if (defined $extracted)
134             {
135 7         14 my $ccstash = compiling_package;
136 7     3   434 $evaled = eval("package $ccstash; no warnings; no strict; local \$SIG{__WARN__}=sub{die}; [$extracted]");
  3     3   11  
  3         3  
  3         89  
  3         11  
  3         5  
  3         157  
137             }
138            
139 33         113 ($name, $extracted, $evaled);
140             }
141              
142             sub parse_block_or_match
143             {
144 4     4 0 6 lex_read_space;
145 4 0       9 return parse_block(@_) if lex_peek eq '{';
146            
147 1         8 require match::simple;
148            
149 1         30 my $___term = parse_arithexpr(@_);
150            
151 1 50       92 eval <<"CODE" or die("could not eval implied match::simple comparison: $@");
152             sub {
153             local \$_ = \@_ ? \$_[0] : \$_;
154             match::simple::match(\$_, \$___term->());
155             };
156             CODE
157             }
158              
159             1;