File Coverage

blib/lib/Parse/Stallion/EBNF.pm
Criterion Covered Total %
statement 75 81 92.5
branch 27 34 79.4
condition 5 6 83.3
subroutine 7 7 100.0
pod 0 2 0.0
total 114 130 87.6


line stmt bran cond sub pod time code
1             #Copyright 2008-10 Arthur S Goldstein
2              
3             package Parse::Stallion::EBNF;
4 4     4   27882 use Carp;
  4         8  
  4         329  
5 4     4   24 use strict;
  4         8  
  4         164  
6 4     4   20 use warnings;
  4         9  
  4         289  
7 4     4   1268 use Parse::Stallion;
  4         7  
  4         21815  
8             our $VERSION='0.7';
9              
10             sub ebnf {
11 5     5 0 796 shift;
12 5         11 my $parser = shift;
13              
14 5         10 my @queue;
15 5         11 unshift @queue, keys %{$parser->{rule}};
  5         171  
16 5         37 my $start_rule = $parser->{start_rule};
17 5         21 unshift @queue, $start_rule;
18              
19 5         13 my $results;
20             my %covered;
21 5         25 while (my $rule = shift @queue) {
22 1018 100       3251 if (!$covered{$rule}++) {
23 422         621 $results .= "$rule = ";
24 422 100       1983 if ($parser->{rule}->{$rule}->{rule_type} eq 'MULTIPLE') {
    100          
    100          
    50          
25 50         105 my $min = $parser->{rule}->{$rule}->{minimum_child};
26 50         278 my $max = $parser->{rule}->{$rule}->{maximum_child};
27 50 100 100     199 if ($min == 0 && $max == 1) {
28 36         45 $results .= "[ ";
29 36         91 $results .= $parser->{rule}->{$rule}->{subrule_list}->[0]->{name};
30 36         233 $results .= " ]";
31             }
32             else {
33 14         20 $results .= "{ ";
34 14         41 $results .= $parser->{rule}->{$rule}->{subrule_list}->[0]->{name};
35 14 100 66     65 if ($min != 0 || $max != 0) {
36 4         21 $results .= "($min, $max)";
37             }
38 14         25 $results .= " }";
39             }
40             }
41             elsif ($parser->{rule}->{$rule}->{rule_type} eq 'AND') {
42 433         1203 $results .= join (" , ",
43 145         158 map {$_->{name}} @{$parser->{rule}->{$rule}->{subrule_list}});
  145         343  
44             }
45             elsif ($parser->{rule}->{$rule}->{rule_type} eq 'OR') {
46 108         237 $results .= join (" | ",
47 28         40 map {$_->{name}} @{$parser->{rule}->{$rule}->{subrule_list}});
  28         75  
48             }
49             elsif ($parser->{rule}->{$rule}->{rule_type} eq 'LEAF') {
50 199 100       507 if (defined $parser->{rule}->{$rule}->{leaf_display}) {
51 179         413 $results .= $parser->{rule}->{$rule}->{leaf_display};
52             }
53             }
54             else {
55 0         0 croak "Rule $rule unknown type ".$parser->{rule}->{$rule}->{rule_type};
56             }
57 422 50       1136 if ($parser->{rule}->{$rule}->{subrule_list}) {
58 422         411 my @new_rules;
59 422         406 foreach my $subrule (@{$parser->{rule}->{$rule}->{subrule_list}}) {
  422         1146  
60 591         1009 push @new_rules, $subrule->{name};
61             }
62 422         1098 unshift @queue, @new_rules;
63             }
64 422 50       1088 if ($parser->{rule}->{$rule}->{minimize_children}) {
65 0         0 $results .= ' -MATCH_MIN_FIRST- ';
66             }
67 422 100       989 if ($parser->{rule}->{$rule}->{parsing_evaluation}) {
68 104         165 $results .= ' -EVALUATION- ';
69             }
70 422 50       5628 if ($parser->{rule}->{$rule}->{parsing_unevaluation}) {
71 0         0 $results .= ' -UNEVALUATION- ';
72             }
73 422 50       918 if ($parser->{rule}->{$rule}->{use_string_match}) {
74 0         0 $results .= ' -USE_STRING_MATCH- ';
75             }
76 422 50       862 if ($parser->{rule}->{$rule}->{match_once}) {
77 0         0 $results .= ' -MATCH_ONCE- ';
78             }
79 422 50       813 if ($parser->{rule_info}->{$rule}) {
80 0         0 $results .= ' -RULE_INFO- ';
81             }
82 422         1393 $results .= " ;\n";
83             }
84             }
85 5         134 return $results;
86             }
87              
88             my %ebnf_rules = (
89             ebnf_rule_list => A(L(PF(sub{$_[0]->{parse_hash}->{max_position} = 0;
90             return 1, undef, 0})), 'some_white_space',
91             M(A(O('rule','failed_rule'),'some_white_space')),
92             E(sub {
93             my $parse_hash = $_[3];
94             my $any_errors = 0;
95             $parse_hash->{errors} = [];
96             if ($_[0]->{failed_rule}) {
97             push @{$parse_hash->{errors}}, @{$_[0]->{failed_rule}};
98             $any_errors = 1;
99             }
100             foreach my $rule (@{$_[0]->{rule}}) {
101             if ($rule->{error}) {
102             push @{$parse_hash->{errors}}, $rule->{error};
103             $any_errors = 1;
104             }
105             }
106             if ($any_errors) {croak join("\n",@{$parse_hash->{errors}})}
107             return $_[0]->{rule};})),
108             rule =>
109             A('rule_name', 'some_white_space', qr/\=/, 'some_white_space',
110             'rule_def', 'some_white_space', qr /\;/,
111             E(sub {
112             return {rule_name => $_[0]->{rule_name},
113             rule_definition => $_[0]->{rule_def}}})),
114             real_white_space => A(qr/\s/, 'some_white_space'),
115             some_white_space => A(L(PF(
116             sub {my $parameters = shift;
117             my $cv = $parameters->{current_position};
118             my $ph = $parameters->{parse_hash};
119             if ($ph->{max_position} < $cv) {
120             $ph->{max_position} = $cv;
121             }
122             return 1, undef, 0;
123             })), O(
124             A(qr/\s*\#/, 'comment', 'some_white_space'),
125             qr/\s*/,
126             )),
127             rule_def =>
128             O(
129             A(qr/\(/, 'some_white_space', 'the_rule', 'some_white_space', qr/\)/,
130             Z(A('some_white_space', 'eval_subroutine'))),
131             A('the_rule'),
132             E(sub {
133             my $the_rule = $_[0]->{the_rule};
134             my $rule_def;
135             if ($_[0]->{eval_subroutine}->{sub}) {
136             push @{$the_rule->{elements}}, $_[0]->{eval_subroutine}->{sub};
137             }
138             if ($the_rule->{rule_type} eq 'AND') {
139             $rule_def = A(@{$the_rule->{elements}});
140             }
141             elsif ($the_rule->{rule_type} eq 'OR') {
142             $rule_def = O(@{$the_rule->{elements}});
143             }
144             elsif ($the_rule->{rule_type} eq 'LEAF') {
145             $rule_def = L(@{$the_rule->{elements}});
146             }
147             elsif ($the_rule->{rule_type} eq 'MULTIPLE') {
148             $rule_def = M(@{$the_rule->{elements}});
149             }
150             elsif ($the_rule->{rule_type} eq 'OPTIONAL') {
151             $rule_def = Z(@{$the_rule->{elements}});
152             }
153             return $rule_def})),
154             the_rule => O('leaf', 'quote', 'pf_pb', 'multiple', 'optional', 'and', 'or'),
155             comment => qr/[^\n]*/,
156             failed_rule => A(
157             L(PF(sub {${$_[0]->{__current_node_ref}}->{error_position} =
158             $_[0]->{parse_hash}->{max_position};
159             my $new_position = $_[0]->{current_position};
160             if ($new_position < $_[0]->{parse_hash}->{max_position}) {
161             $new_position = $_[0]->{parse_hash}->{max_position};
162             }
163             return 1, undef, 0;})),
164             qr/[^;]*\;/,
165             E(sub {my (undef, $parameters) = @_;
166             my $text = $parameters->{parse_this_ref};
167             my $pos = $parameters->{current_node}->{error_position} || 0;
168             my ($line, $position) = LOCATION($text, $pos);
169             my $before_length = 10;
170             my $before_start = $pos - 10;
171             if ($pos < 10) {
172             $before_length = $pos;
173             $before_start = 0;
174             }
175             my $before = substr($$text, $before_start, $before_length);
176             $before =~ s/.*\s(.+)/$1/;
177             my $after = substr($$text, $pos, 10);
178             $after =~ s/(.+?)\s(.*)/$1/;
179             return "Error at line $line tab stop $position near '$before".$after."'";
180             })),
181             and => A( 'element' ,
182             M(A('real_white_space', 'element')),
183             E(sub {
184             return {rule_type => 'AND', elements => $_[0]->{element}};})),
185             element => A(Z(A({alias=>'rule_name'}, qr/\./)), 'sub_element',
186             E( sub {
187             if (defined $_[0]->{alias}) {
188             return {$_[0]->{alias} => $_[0]->{sub_element}}
189             }
190             return $_[0]->{sub_element}})),
191             sub_element => O('rule_name', 'sub_rule',
192             'optional_sub_rule',
193             'multiple_sub_rule', 'leaf_sub_rule', 'pf_pb_subrule', 'quote_sub_rule',
194             'use_string_match', 'match_once', 'match_min_first'),
195             use_string_match => L(qr/\=SM/,
196             E(sub {return USE_STRING_MATCH})),
197             match_once => L(qr/\=MO/,
198             E(sub {return MATCH_ONCE})),
199             match_min_first => L(qr/\=MMF/,
200             E(sub {return MATCH_MIN_FIRST})),
201             optional_sub_rule => A( qr/\[/, 'some_white_space',
202             'rule_def', 'some_white_space', qr/\]/i,
203             E(sub {
204             return Z($_[0]->{rule_def});})),
205             multiple_sub_rule => A( qr/\{/,
206             'some_white_space', 'rule_def', 'some_white_space', qr/\}/,
207             Z('use_min_first'), Z('min_max'),
208             E(sub {
209             my $min = 0;
210             my $max = 0;
211             if ($_[0]->{min_max}) {
212             $min = $_[0]->{min_max}->{min};
213             $max = $_[0]->{min_max}->{max};
214             }
215             if ($_[0]->{use_min_first}) {
216             return M($_[0]->{rule_def},$min,$max, MATCH_MIN_FIRST());
217             }
218             return M($_[0]->{rule_def},$min,$max);}
219             )),
220             sub_rule => A( qr/\(/, 'some_white_space', 'rule_def', 'some_white_space',
221             qr/\)/,
222             E(sub { return $_[0]->{rule_def};})
223             ),
224             rule_name => qr/[a-zA-Z]\w*/,
225             or => A( 'element' , M(A('some_white_space', qr/\|/, 'some_white_space',
226             'element'), 1, 0),
227             E(sub {return {rule_type => 'OR', elements => $_[0]->{element}}})),
228             multiple => A( qr/\{/, 'some_white_space',
229             'element', 'some_white_space', qr/\}/, Z('use_min_first'),
230             Z('min_max'),
231             E(sub {
232             my $min = 0;
233             my $max = 0;
234             if ($_[0]->{min_max}) {
235             $min = $_[0]->{min_max}->{min};
236             $max = $_[0]->{min_max}->{max};
237             }
238             if ($_[0]->{use_min_first}) {
239             return {rule_type => 'MULTIPLE',
240             elements => [$_[0]->{element},$min,$max, MATCH_MIN_FIRST()]};
241             }
242             return {rule_type => 'MULTIPLE', elements => [$_[0]->{element},$min,$max]}
243             })),
244             min_max => A(qr/\*/,{min=>qr/\d+/},qr/\,/,{max=>qr/\d+/}),
245             use_min_first => qr/\?/,
246             optional => A( qr/\[/, 'some_white_space',
247             'element', 'some_white_space', qr/\]/,
248             E(sub {
249             return {rule_type => 'OPTIONAL', elements => [$_[0]->{element}]}
250             })),
251             quote_sub_rule => A( O(A(qr/q/i, qr/[^\w\s]/), qr/(\"|\')/), 'leaf_info',
252             E(sub {my $li = $_[0]->{leaf_info}; substr($li, -1) = '';
253             $li =~ s/(\W)/\\$1/g;
254             return L(qr/$li/)})),
255             quote => A( O(A(qr/q/i, qr/[^\w\s]/,), qr/(\"|\')/), 'leaf_info',
256             E(sub {my $li = $_[0]->{leaf_info}; substr($li, -1) = '';
257             $li =~ s/(\W)/\\$1/g;
258             return {rule_type => 'LEAF', elements => [qr/$li/]}})),
259             leaf_sub_rule => A( qr/qr/i, qr/[^\w\s]/, 'leaf_info',
260             E(sub {my $li = $_[0]->{leaf_info}; substr($li, -1) = '';
261             return L(qr/$li/)})),
262             leaf => A( qr/qr/, qr/[^\w\s]/, 'leaf_info',
263             Z({modifiers=>qr/\w+/}),
264             E(sub {my $li = $_[0]->{leaf_info}; substr($li, -1) = '';
265             if (defined $_[0]->{modifiers}) {
266             $li = '(?' . $_[0]->{modifiers}. ')'.$li
267             }
268             return {rule_type => 'LEAF', elements => [qr/$li/]}})),
269             leaf_info => L(PF(
270             sub {my $parameters = shift;
271             my $in_ref = $parameters->{parse_this_ref};
272             my $pos = $parameters->{current_position};
273             my $previous = substr($$in_ref, $pos-1, 1);
274             pos $$in_ref = $pos;
275             if ($$in_ref =~ /\G([^$previous]+$previous)/) {
276             return 1, $1, length($1);
277             }
278             else {
279             return 0;
280             }
281             }
282             )),
283             pf_pb_subrule => A('parse_forward',
284             Z(A('some_white_space', 'parse_backtrack')),
285             E (sub {
286             if ($_[0]->{parse_backtrack}) {
287             return L(PF($_[0]->{parse_forward}),
288             PB($_[0]->{parse_backtrack}));
289             };
290             return L(PF($_[0]->{parse_forward}));
291             }
292             )),
293             pf_pb => A('parse_forward', Z(A('some_white_space', 'parse_backtrack')),
294             E(sub {
295             if ($_[0]->{parse_backtrack}) {
296             return {rule_type => 'LEAF', elements => [
297             PF($_[0]->{parse_forward}),
298             PB($_[0]->{parse_backtrack}),
299             ]};
300             }
301             return {rule_type => 'LEAF', elements => [
302             PF($_[0]->{parse_forward}),
303             ]};
304             })),
305             quote_sub_rule => A( O(A(qr/q/i, qr/[^\w\s]/), qr/(\"|\')/), 'leaf_info',
306             E(sub {my $li = $_[0]->{leaf_info}; substr($li, -1) = '';
307             $li =~ s/(\W)/\\$1/g;
308             return L(qr/$li/)})),
309             quote => A( O(A(qr/q/i, qr/[^\w\s]/,), qr/(\"|\')/), 'leaf_info',
310             E(sub {my $li = $_[0]->{leaf_info}; substr($li, -1) = '';
311             $li =~ s/(\W)/\\$1/g;
312             return {rule_type => 'LEAF', elements => [qr/$li/]}})),
313             parse_backtrack => A( qr/B[^\w\s]/, 'sub_routine',
314             E(sub {
315             my $routine = eval $_[0]->{sub_routine}->{the_sub};
316             if ($@) {croak $@};
317             return $routine;})
318             ),
319             parse_forward => A( qr/F[^\w\s]/, 'sub_routine',
320             E(sub {
321             my $routine = eval $_[0]->{sub_routine}->{the_sub};
322             if ($@) {croak $@};
323             return $routine;})
324             ),
325             eval_subroutine => A( qr/S[^\w\s]/, 'sub_routine',
326             E(sub {return {'sub' => SE($_[0]->{'sub_routine'}->{the_sub},
327             '_matched_string')}})
328             ),
329             sub_routine => L(PARSE_FORWARD(
330             sub {my $parameters = shift;
331             my $in_ref = $parameters->{parse_this_ref};
332             my $pos = $parameters->{current_position};
333             my $previous = substr($$in_ref, $pos-1, 1);
334             my $previous2 = substr($$in_ref, $pos-2, 1);
335             pos $$in_ref = $pos;
336             my $opposite;
337             if ($previous eq '{') {$opposite = '}'};
338             if ($previous eq '[') {$opposite = ']'};
339             if (!defined $opposite) {return 0}
340             if ($$in_ref =~ /\G(.*?$opposite($previous2))/s) {
341             return 1, $1, length($1);
342             }
343             else {
344             return 0;
345             }
346             }),
347             E(sub {
348             my $subroutine = shift;
349             substr($subroutine, -2) = '';
350             return {the_sub => $subroutine};
351             }
352             ))
353             );
354              
355             our $ebnf_parser = new Parse::Stallion(\%ebnf_rules);
356             foreach my $mn (keys %{$ebnf_parser->{rule}}) {
357             if (!$ebnf_parser->{rule}->{$mn}->{rule_type}) {
358             warn "name generated $mn\n";
359             }
360             }
361              
362 4     4   69 use Parse::Stallion::EBNF;
  4         8  
  4         1292  
363             my $ebnf_form = ebnf Parse::Stallion::EBNF($ebnf_parser);
364              
365             sub ebnf_new {
366 26     26 0 18417 my $type = shift;
367 26         61 my $rules_string = shift;
368             #print STDERR "rule string is $rules_string\n";
369             # my @pt;
370 26         49 my $rules_out = eval {$ebnf_parser->parse_and_evaluate(
  26         113  
371             $rules_string
372             # , {parse_trace => \@pt}
373             )};
374             #use Data::Dumper;print STDERR "pt is ".Dumper(\@pt)."\n";
375 26 100       368 if ($@) {croak "\nUnable to create parser due to the following:\n$@\n"};
  1         183  
376             #use Data::Dumper;print STDERR "ro is ".Dumper($rules_out)."\n";
377 25         51 my %rules;
378 25         66 foreach my $rule (@$rules_out) {
379 72         152 my $rule_name = $rule->{rule_name};
380 72 100       198 if ($rules{$rule_name}) {
381 1         229 croak "Unable to create parse: Duplicate rule name $rule_name\n";
382             }
383 71         216 $rules{$rule_name} = $rule->{rule_definition};
384             }
385             #use Data::Dumper;print STDERR "therules is ".Dumper(\%rules)."\n";
386 24         284 my $new_parser = new Parse::Stallion(\%rules, {separator => '.'});
387 24         312 return $new_parser;
388             }
389              
390              
391             1;
392              
393             __END__