File Coverage

blib/lib/Pegex/Compiler.pm
Criterion Covered Total %
statement 82 107 76.6
branch 25 34 73.5
condition 5 8 62.5
subroutine 14 17 82.3
pod 6 11 54.5
total 132 177 74.5


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