File Coverage

blib/lib/Pegex/Pegex/AST.pm
Criterion Covered Total %
statement 95 172 55.2
branch 33 98 33.6
condition 7 18 38.8
subroutine 18 24 75.0
pod 0 22 0.0
total 153 334 45.8


line stmt bran cond sub pod time code
1             package Pegex::Pegex::AST;
2 10     10   71 use Pegex::Base;
  10         18  
  10         61  
3             extends 'Pegex::Tree';
4              
5 10     10   3430 use Pegex::Grammar::Atoms;
  10         27  
  10         22907  
6              
7             has atoms => Pegex::Grammar::Atoms->new->atoms;
8             has extra_rules => {};
9              
10             sub got_grammar {
11 10     10 0 27 my ($self, $got) = @_;
12 10         36 my ($meta_section, $rule_section) = @$got;
13             my $grammar = {
14             '+toprule' => $self->{toprule},
15 10         35 %{$self->{extra_rules}},
  10         44  
16             %$meta_section,
17             };
18 10         29 for my $rule (@$rule_section) {
19 33         121 my ($key, $value) = %$rule;
20 33         83 $grammar->{$key} = $value;
21             }
22 10         64 return $grammar;
23             }
24              
25             sub got_meta_section {
26 10     10 0 36 my ($self, $got) = @_;
27 10         21 my $meta = {};
28 10         27 for my $next (@$got) {
29 0         0 my ($key, $val) = @$next;
30 0         0 $key = "+$key";
31 0         0 my $old = $meta->{$key};
32 0 0       0 if (defined $old) {
33 0 0       0 if (ref $old) {
34 0         0 push @$old, $val;
35             }
36             else {
37 0         0 $meta->{$key} = [ $old, $val ];
38             }
39             }
40             else {
41 0         0 $meta->{$key} = $val;
42             }
43             }
44 10         58 return $meta;
45             }
46              
47             sub got_rule_definition {
48 33     33 0 66 my ($self, $got) = @_;
49 33         68 my ($name, $value) = @$got;
50 33         72 $name =~ s/-/_/g;
51 33 50       79 $self->{toprule} = $name if $name eq 'TOP';
52 33   66     104 $self->{toprule} ||= $name;
53 33         213 return +{ $name => $value };
54             }
55              
56             sub got_bracketed_group {
57 4     4 0 8 my ($self, $got) = @_;
58 4         7 my ($prefix, $group, $suffix) = @$got;
59 4         11 set_modifier($group, $prefix);
60 4         11 set_quantity($group, $suffix);
61 4         14 return $group;
62             }
63              
64             sub got_all_group {
65 40     40 0 83 my ($self, $got) = @_;
66 40         106 my $list = $self->get_group($got);
67 40 50       127 die unless @$list;
68 40 100       169 return $list->[0] if @$list == 1;
69 10         52 return { '.all' => $list };
70             }
71              
72             sub got_any_group {
73 37     37 0 72 my ($self, $got) = @_;
74 37         92 my $list = $self->get_group($got);
75 37 50       101 die unless @$list;
76 37 100       169 return $list->[0] if @$list == 1;
77 3         16 return { '.any' => $list };
78             }
79              
80             sub get_group {
81 77     77 0 119 my ($self, $group) = @_;
82             sub get {
83 343     343 0 445 my $it = shift;
84 343 50       657 my $ref = ref($it) or return;
85 343 100       593 if ($ref eq 'HASH') {
    50          
86 101   33     319 return($it || ());
87             }
88             elsif ($ref eq 'ARRAY') {
89 242         576 return map get($_), @$it;
90             }
91             else {
92 0         0 die;
93             }
94             };
95 77         188 return [ get($group) ];
96             }
97              
98             sub got_rule_part {
99 61     61 0 124 my ($self, $got) = @_;
100 61         108 my ($rule, $sep) = @$got;
101 61 50       124 $rule = set_separator($rule, @$sep) if @$sep;
102 61         206 return $rule;
103             }
104              
105             sub got_rule_reference {
106 37     37 0 70 my ($self, $got) = @_;
107 37         75 my ($prefix, $ref1, $ref2, $suffix) = @$got;
108 37   66     89 my $ref = $ref1 || $ref2;
109 37         88 $ref =~ s/-/_/g;
110 37         107 my $node = +{ '.ref' => $ref };
111 37 100       130 if (my $regex = $self->atoms->{$ref}) {
112 7         23 $self->{extra_rules}{$ref} = +{ '.rgx' => $regex };
113             }
114 37         107 set_modifier($node, $prefix);
115 37         91 set_quantity($node, $suffix);
116 37         160 return $node;
117             }
118              
119             sub got_quoted_regex {
120 4     4 0 13 my ($self, $got) = @_;
121 4         11 $got =~ s/([^\w\`\%\:\<\/\,\=\;])/\\$1/g;
122 4         20 return +{ '.rgx' => $got };
123             }
124              
125             sub got_regex_rule_reference {
126 16     16 0 35 my ($self, $got) = @_;
127 16   66     57 my $ref = $got->[0] || $got->[1];
128 16         75 return +{ '.ref' => $ref };
129             }
130              
131             sub got_whitespace_maybe {
132 0     0 0 0 my ($self) = @_;
133 0         0 return +{ '.rgx' => '<_>'};
134             }
135              
136             sub got_whitespace_must {
137 0     0 0 0 my ($self) = @_;
138 0         0 return +{ '.rgx' => '<__>'};
139             }
140              
141             sub got_whitespace_start {
142 0     0 0 0 my ($self, $got) = @_;
143 0 0       0 my $rule = $got eq '+' ? '__' : '_';
144 0         0 return +{ '.rgx' => "<$rule>"};
145             }
146              
147             sub got_regular_expression {
148 18     18 0 42 my ($self, $got) = @_;
149 18         44 my $modifier = shift @$got;
150 18 50       62 if (@$got == 2) {
151 0         0 my $part = shift @$got;
152 0         0 unshift @{$got->[0]}, $part;
  0         0  
153             }
154              
155             my $regex = join '', map {
156 44 100       104 if (ref($_)) {
157 19         29 my $part;
158 19 100       85 if (defined($part = $_->{'.rgx'})) {
    50          
159 3         26 $part;
160             }
161             elsif (defined($part = $_->{'.ref'})) {
162 16         42 "<$part>";
163             }
164             else {
165 0         0 XXX $_;
166             }
167             }
168             else {
169 25         92 $_;
170             }
171 18         35 } @{$got->[0]};
  18         62  
172             # $regex =~ s!\(([ism]?\:|\=|\!)!(?$1!g;
173 18         90 $regex =~ s{\(([ism]?\:|\=|\!|<[=!])}{(?$1}g;
174 18         49 my $rgx = { '.rgx' => $regex };
175 18 50       53 set_modifier($rgx, $modifier) if $modifier;
176 18         89 return $rgx;
177             }
178              
179             sub got_whitespace_token {
180 1     1 0 3 my ($self, $got) = @_;
181 1         2 my $token;
182 1 50       7 if ($got =~ /^\~{1,2}$/) {
    50          
    0          
183 0         0 $token = +{ '.ref' => ('_' x length($got)) };
184             }
185             elsif ($got =~ /^\-{1,2}$/) {
186 1         5 $token = +{ '.ref' => ('_' x length($got)) };
187             }
188             elsif ($got eq '+') {
189 0         0 $token = +{ '.ref' => '__' };
190             }
191             else {
192 0         0 die;
193             }
194 1         4 return $token;
195             }
196              
197             sub got_error_message {
198 0     0 0 0 my ($self, $got) = @_;
199 0         0 return +{ '.err' => $got };
200             }
201              
202             sub set_modifier {
203 41     41 0 78 my ($object, $modifier) = @_;
204 41 100       92 return unless $modifier;
205 1 50       17 if ($modifier eq '=') {
    50          
    0          
    0          
    0          
206 0         0 $object->{'+asr'} = 1;
207             }
208             elsif ($modifier eq '!') {
209 1         4 $object->{'+asr'} = -1;
210             }
211             elsif ($modifier eq '.') {
212 0         0 $object->{'-skip'} = 1;
213             }
214             elsif ($modifier eq '+') {
215 0         0 $object->{'-wrap'} = 1;
216             }
217             elsif ($modifier eq '-') {
218 0         0 $object->{'-flat'} = 1;
219             }
220             else {
221 0         0 die "Invalid modifier: '$modifier'";
222             }
223             }
224              
225             sub set_quantity {
226 41     41 0 73 my ($object, $quantity) = @_;
227 41 100       90 return unless $quantity;
228 2 100       13 if ($quantity eq '?') {
    50          
    50          
    0          
    0          
    0          
229 1         3 $object->{'+max'} = 1;
230             }
231             elsif ($quantity eq '*') {
232 0         0 $object->{'+min'} = 0;
233             }
234             elsif ($quantity eq '+') {
235 1         2 $object->{'+min'} = 1;
236             }
237             elsif ($quantity =~ /^(\d+)$/) {
238 0           $object->{'+min'} = int $1 + 0;
239 0           $object->{'+max'} = int $1 + 0;
240             }
241             elsif ($quantity =~ /^(\d+)-(\d+)$/) {
242 0           $object->{'+min'} = int $1 + 0;
243 0           $object->{'+max'} = int $2 + 0;
244             }
245             elsif ($quantity =~ /^(\d+)\+$/) {
246 0           $object->{'+min'} = int $1 + 0;
247             }
248             else {
249 0           die "Invalid quantifier: '$quantity'";
250             }
251             }
252              
253             sub set_separator {
254 0     0 0   my ($rule, $op, $sep) = @_;
255 0           my $extra = ($op eq '%%');
256 0 0 0       if (not defined $rule->{'+max'} and not defined $rule->{'+min'}) {
    0 0        
    0          
257 0 0         $rule = {'.all' => [ $rule, {%{clone($sep)}, '+max' => 1}, ] }
  0            
258             if $extra;
259 0           return $rule;
260             }
261             elsif (defined $rule->{'+max'} and defined $rule->{'+min'}) {
262 0           my ($min, $max) = delete @{$rule}{qw(+min +max)};
  0            
263 0 0         $min-- if $min > 0;
264 0 0         $max-- if $max > 0;
265 0           $rule = {
266             '.all' => [
267             $rule,
268             {
269             '+min' => $min,
270             '+max' => $max,
271             '-flat' => 1,
272             '.all' => [
273             $sep,
274             clone($rule),
275             ],
276             },
277             ],
278             };
279             }
280             elsif (not defined $rule->{'+max'}) {
281 0           my $copy = clone($rule);
282 0           my $min = delete $copy->{'+min'};
283 0           my $new = {
284             '.all' => [
285             $copy,
286             {
287             '+min' => 0,
288             '-flat' => 1,
289             '.all' => [
290             $sep,
291             clone($copy),
292             ],
293             },
294             ],
295             };
296 0 0         if ($rule->{'+min'} == 0) {
    0          
297 0           $rule = $new;
298 0           $rule->{'+max'} = 1;
299             }
300             elsif ($rule->{'+min'} == 1) {
301 0           $rule = $new;
302             }
303             else {
304 0           $rule = $new;
305 0 0         $min-- if $min > 0;
306 0           $rule->{'.all'}[-1]{'+min'} = $min;
307             }
308             }
309             else {
310 0 0         if ($rule->{'+max'} == 1) {
311 0           delete $rule->{'+min'};
312             $rule = {
313 0           %{clone($rule)},
  0            
314             '+max' => 1,
315             };
316 0 0         if ($extra) {
317 0           $rule = { '.all' => [$rule, {%{clone($sep)}, '+max' => 1}] };
  0            
318             }
319 0           return $rule;
320             }
321             else {
322 0           XXX 'FAIL', $rule, $op, $sep;
323             }
324             }
325 0 0         if ($extra) {
326 0           push @{$rule->{'.all'}}, {%{clone($sep)}, '+max' => 1};
  0            
  0            
327             }
328 0           return $rule;
329             }
330              
331             sub clone {
332 0     0 0   my ($o) = @_;
333             return ref($o) eq 'HASH'
334 0 0         ? { map { my $v = $o->{$_}; ($_, (ref($v) ? clone($v) : $v)) } keys %$o }
  0            
335 0 0         : [ map { ref($_) ? clone($_) : $_ } @$o ];
  0 0          
336             }
337              
338             1;