File Coverage

blib/lib/Pegex/Compiler.pm
Criterion Covered Total %
statement 82 109 75.2
branch 25 34 73.5
condition 5 8 62.5
subroutine 14 17 82.3
pod 6 11 54.5
total 132 179 73.7


line stmt bran cond sub pod time code
1             package Pegex::Compiler;
2 10     10   398 use Pegex::Base;
  10         15  
  10         73  
3              
4 10     10   754 use Pegex::Parser;
  10         18  
  10         268  
5 10     10   3845 use Pegex::Pegex::Grammar;
  10         20  
  10         69  
6 10     10   3028 use Pegex::Pegex::AST;
  10         25  
  10         240  
7 10     10   54 use Pegex::Grammar::Atoms;
  10         17  
  10         10010  
8              
9             has tree => ();
10              
11             sub compile {
12 10     10 1 24 my ($self, $grammar, @rules) = @_;
13              
14             # Global request to use the Pegex bootstrap compiler
15 10 50       32 if ($Pegex::Bootstrap) {
16 0         0 require Pegex::Bootstrap;
17 0         0 $self = Pegex::Bootstrap->new;
18             }
19              
20 10         19 @rules = map { s/-/_/g; $_ } @rules;
  0         0  
  0         0  
21              
22 10         27 $self->parse($grammar);
23 10         53 $self->combinate(@rules);
24 10         31 $self->native;
25              
26 10         40 return $self;
27             }
28              
29             sub parse {
30 10     10 1 19 my ($self, $input) = @_;
31              
32 10         43 my $parser = Pegex::Parser->new(
33             grammar => Pegex::Pegex::Grammar->new,
34             receiver => Pegex::Pegex::AST->new,
35             );
36              
37 10         63 $self->{tree} = $parser->parse($input);
38              
39 10         979 return $self;
40             }
41              
42             #-----------------------------------------------------------------------------#
43             # Combination
44             #-----------------------------------------------------------------------------#
45             has _tree => ();
46              
47             sub combinate {
48 10     10 1 23 my ($self, @rule) = @_;
49 10 50       30 if (not @rule) {
50 10 50       37 if (my $rule = $self->{tree}->{'+toprule'}) {
51 10         26 @rule = ($rule);
52             }
53             else {
54 0         0 return $self;
55             }
56             }
57             $self->{_tree} = {
58 10         17 map {($_, $self->{tree}->{$_})} grep { /^\+/ } keys %{$self->{tree}}
  10         37  
  46         100  
  10         35  
59             };
60 10         26 for my $rule (@rule) {
61 10         30 $self->combinate_rule($rule);
62             }
63 10         27 $self->{tree} = $self->{_tree};
64 10         19 delete $self->{_tree};
65 10         18 return $self;
66             }
67              
68             sub combinate_rule {
69 45     45 0 61 my ($self, $rule) = @_;
70 45 100       92 return if exists $self->{_tree}->{$rule};
71              
72 37         59 my $object = $self->{_tree}->{$rule} = $self->{tree}->{$rule};
73 37         67 $self->combinate_object($object);
74             }
75              
76             sub combinate_object {
77 74     74 0 94 my ($self, $object) = @_;
78 74 100       138 if (exists $object->{'.rgx'}) {
    100          
    100          
    50          
    0          
79 23         44 $self->combinate_re($object);
80             }
81             elsif (exists $object->{'.ref'}) {
82 38         53 my $rule = $object->{'.ref'};
83 38 100       58 if (exists $self->{tree}{$rule}) {
84 34         54 $self->combinate_rule($rule);
85             }
86             else {
87 4 100       11 if (my $regex = (Pegex::Grammar::Atoms::atoms)->{$rule}) {
88 1         5 $self->{tree}{$rule} = { '.rgx' => $regex };
89 1         3 $self->combinate_rule($rule);
90             }
91             }
92             }
93             elsif (exists $object->{'.any'}) {
94 3         5 for my $elem (@{$object->{'.any'}}) {
  3         6  
95 6         13 $self->combinate_object($elem);
96             }
97             }
98             elsif (exists $object->{'.all' }) {
99 10         13 for my $elem (@{$object->{'.all'}}) {
  10         17  
100 31         52 $self->combinate_object($elem);
101             }
102             }
103             elsif (exists $object->{'.err' }) {
104             }
105             else {
106 0         0 require YAML::PP;
107 0         0 die "Can't combinate:\n" .
108             YAML::PP->new(schema => ['Core', 'Perl'])->dump_string($object);
109             }
110             }
111              
112             sub combinate_re {
113 23     23 0 34 my ($self, $regexp) = @_;
114 23         71 my $atoms = Pegex::Grammar::Atoms->atoms;
115 23         33 my $re = $regexp->{'.rgx'};
116 23         29 while (1) {
117 44         60 $re =~ s[(?']ge;
  0         0  
118 44         98 $re =~ s[<([\w\-]+)>][
119 21         42 (my $key = $1) =~ s/-/_/g;
120             $self->{tree}->{$key} and (
121             $self->{tree}->{$key}{'.rgx'} or
122             die "'$key' not defined as a single RE"
123             )
124 21 50 50     121 or $atoms->{$key}
      66        
      66        
125             or die "'$key' not defined in the grammar"
126             ]e;
127 44 100       109 last if $re eq $regexp->{'.rgx'};
128 21         31 $regexp->{'.rgx'} = $re;
129             }
130             }
131              
132             #-----------------------------------------------------------------------------#
133             # Compile to native Perl regexes
134             #-----------------------------------------------------------------------------#
135             sub native {
136 10     10 0 27 my ($self) = @_;
137 10         30 $self->perl_regexes($self->{tree});
138 10         20 return $self;
139             }
140              
141             sub perl_regexes {
142 148     148 0 200 my ($self, $node) = @_;
143 148 100       288 if (ref($node) eq 'HASH') {
    100          
144 84 100       115 if (exists $node->{'.rgx'}) {
145 23         32 my $re = $node->{'.rgx'};
146 23         418 $node->{'.rgx'} = qr/\G$re/;
147             }
148             else {
149 61         124 for (keys %$node) {
150 101         168 $self->perl_regexes($node->{$_});
151             }
152             }
153             }
154             elsif (ref($node) eq 'ARRAY') {
155 13         34 $self->perl_regexes($_) for @$node;
156             }
157             }
158              
159             #-----------------------------------------------------------------------------#
160             # Serialization formatter methods
161             #-----------------------------------------------------------------------------#
162             sub to_yaml {
163 0     0 1   require YAML::PP;
164 0           my $self = shift;
165 0           my $yaml = YAML::PP->new(schema => ['Core', 'Perl'])
166             ->dump_string($self->tree);
167 0           $yaml =~ s/\n *(\[\]\n)/ $1/g; # Work around YAML::PP formatting issue
168 0           return $yaml;
169             }
170              
171             sub to_json {
172 0     0 1   require JSON::PP;
173 0           my $self = shift;
174 0           return JSON::PP->new->utf8->canonical->pretty->encode($self->tree);
175             }
176              
177             sub to_perl {
178 0     0 1   my $self = shift;
179 0           require Data::Dumper;
180 10     10   77 no warnings 'once';
  10         17  
  10         1739  
181 0           $Data::Dumper::Terse = 1;
182 0           $Data::Dumper::Indent = 1;
183 0           $Data::Dumper::Sortkeys = 1;
184 0           my $perl = Data::Dumper::Dumper($self->tree);
185 0           $perl =~ s/\?\^u?:/?-xism:/g; # the "u" is perl 5.14-18 equiv of /u
186 0           $perl =~ s!(\.rgx.*?qr/)\(\?-xism:(.*)\)(?=/)!$1$2!g;
187 0           $perl =~ s!/u$!/!gm; # perl 5.20+ put /u, older perls don't understand
188 0 0         die "to_perl failed with non compatible regex in:\n$perl"
189             if $perl =~ /\?\^/;
190 0           return $perl;
191             }
192              
193             1;