File Coverage

inc/Pegex/Compiler.pm
Criterion Covered Total %
statement 76 106 71.7
branch 21 34 61.7
condition 5 8 62.5
subroutine 14 17 82.3
pod 6 11 54.5
total 122 176 69.3


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