File Coverage

blib/lib/Parse/Highlife/Compiler.pm
Criterion Covered Total %
statement 18 177 10.1
branch 0 8 0.0
condition n/a
subroutine 6 28 21.4
pod 0 12 0.0
total 24 225 10.6


line stmt bran cond sub pod time code
1             package Parse::Highlife::Compiler;
2              
3 1     1   4 use strict;
  1         2  
  1         30  
4 1     1   5 use Parse::Highlife::Utils qw(dump_tokens dump_ast);
  1         1  
  1         41  
5 1     1   5 use Parse::Highlife::Parser;
  1         1  
  1         15  
6 1     1   5 use Parse::Highlife::Transformer;
  1         1  
  1         16  
7              
8 1     1   892 use File::Slurp qw(read_file);
  1         16302  
  1         66  
9 1     1   9 use Data::Dump qw(dump);
  1         1  
  1         2095  
10              
11             sub new
12             {
13 0     0 0   my( $class, @args ) = @_;
14 0           my $self = bless {}, $class;
15 0           return $self -> _init( @args );
16             }
17              
18             sub _init
19             {
20 0     0     my( $self, @args ) = @_;
21 0           $self->{'parser'} = Parse::Highlife::Parser -> new();
22 0           $self->{'transformer'} = Parse::Highlife::Transformer -> new();
23            
24             # the following set of tokenizer, parser and transformer
25             # is used to tokenize, parse and transform grammars in
26             # a common syntax to create a parser (this grammar makes it
27             # possible to define tokens and rules in a more consisten way)
28 0           $self->{'grammar-parser'} = undef;
29 0           $self->{'grammar-transformer'} = undef;
30            
31 0           return $self;
32             }
33              
34             sub _create_grammar_analyser
35             {
36 0     0     my( $self ) = @_;
37              
38 0           my $p = Parse::Highlife::Parser -> new();
39            
40 0           $p -> rule( -name => 'grammar', -repetition => 'definition' );
41 0           $p -> rule( -name => 'definition', -sequence => [ 'rule-name', 'ignored', 'def-mark', 'rule', 'def-end' ] );
42              
43 0           $p -> rule( -name => 'ignored', -optional => 'ignored-text' );
44 0           $p -> rule( -name => 'ignored-text', -literal => 'ignored' );
45            
46 0           $p -> rule( -name => 'rule-name', -regex => '[a-zA-Z\-]+' );
47              
48 0           $p -> rule( -name => 'rule', -repetition => 'subrule', -min => 1 );
49 0           $p -> rule( -name => 'subrule', -choice => [ 'token-delimited', 'token-literal', 'token-regex', 'rule-sequence', 'rule-repetition', 'rule-optional', 'rule-choice', 'rule-name' ] );
50 0           $p -> rule( -name => 'token-regex', -start => '/', -end => '/' );
51 0           $p -> rule( -name => 'token-delimited', -sequence => [ 'quoted-string', 'dots', 'quoted-string' ] );
52 0           $p -> rule( -name => 'token-literal', -sequence => [ 'quoted-string' ] );
53 0           $p -> rule( -name => 'quoted-string', -choice => [ 'single-quoted-string', 'double-quoted-string' ] );
54 0           $p -> rule( -name => 'single-quoted-string', -start => "'", -end => "'" );
55 0           $p -> rule( -name => 'double-quoted-string', -start => '"', -end => '"' );
56 0           $p -> rule( -name => 'rule-sequence', -sequence => [ 'open-paren', 'rules', 'close-paren' ] );
57 0           $p -> rule( -name => 'rule-repetition', -sequence => [ 'open-curly', 'rule', 'amount-spec', 'close-curly' ] );
58 0           $p -> rule( -name => 'amount-spec', -sequence => [ 'number', 'dots', 'limit' ] );
59 0           $p -> rule( -name => 'number', -regex => '\d+' );
60 0           $p -> rule( -name => 'limit', -choice => [ 'number', 'star' ] );
61 0           $p -> rule( -name => 'rule-optional', -sequence => [ 'open-bracket', 'rule', 'close-bracket' ] );
62 0           $p -> rule( -name => 'rule-choice', -sequence => [ 'open-edge', 'rules', 'close-edge' ] );
63 0           $p -> rule( -name => 'rules', -repetition => 'rule' );
64              
65 0           $p -> rule( -name => 'star', -literal => '*' );
66 0           $p -> rule( -name => 'def-mark', -literal => ':' );
67 0           $p -> rule( -name => 'open-paren', -literal => '(' );
68 0           $p -> rule( -name => 'close-paren', -literal => ')' );
69 0           $p -> rule( -name => 'open-curly', -literal => '{' );
70 0           $p -> rule( -name => 'close-curly', -literal => '}' );
71 0           $p -> rule( -name => 'open-bracket', -literal => '[' );
72 0           $p -> rule( -name => 'close-bracket', -literal => ']' );
73 0           $p -> rule( -name => 'open-edge', -literal => '<' );
74 0           $p -> rule( -name => 'close-edge', -literal => '>' );
75 0           $p -> rule( -name => 'pipe', -literal => '|' );
76 0           $p -> rule( -name => 'dots', -literal => '..' );
77 0           $p -> rule( -name => 'def-end', -literal => ';' );
78              
79 0           $p -> rule( -name => 'multiline-comment', -ignored => 1, -start => '/*', -end => '*/' );
80 0           $p -> rule( -name => 'singleline-comment', -ignored => 1, -regex => "\#[^\n]*\n" );
81 0           $p -> rule( -name => 'space', -ignored => 1, -characters => [' ',"\n","\t","\r"] );
82            
83 0           $p->toprule( -name => 'grammar' );
84            
85             #dump($p->{'tokenizer'}->{'tokens'});
86             #dump($p->{'rules'});
87             #exit;
88            
89 0           my $t = Parse::Highlife::Transformer -> new();
90              
91             $t -> transformer( -rule => 'token-delimited', -fn => sub {
92 0     0     my( $transformer, $ast, $compiler ) = @_;
93 0           my $start = $ast->first_child()->first_child()->first_child();
94 0           my $end = $ast->third_child()->first_child()->first_child();
95 0           my $rulename = $self->{'parser'}->get_unique_rulename();
96 0           $compiler->rule( -name => $rulename, -start => $start, -end => $end );
97 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);
98 0           });
99            
100             $t -> transformer( -rule => 'token-regex', -fn => sub {
101 0     0     my( $transformer, $ast, $compiler ) = @_;
102 0           my $regex = $ast->{'children'};
103 0           my $rulename = $self->{'parser'}->get_unique_rulename();
104 0           $compiler->rule( -name => $rulename, -regex => $regex );
105 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);
106 0           });
107            
108             $t -> transformer( -rule => 'token-literal', -fn => sub {
109 0     0     my( $transformer, $ast, $compiler ) = @_;
110 0           my $literal = $ast->first_child()->first_child()->first_child();
111 0           my $rulename = $self->{'parser'}->get_unique_rulename();
112 0           $compiler->rule( -name => $rulename, -literal => $literal );
113 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);
114 0           });
115              
116             $t -> transformer( -rule => 'rule-repetition', -fn => sub {
117 0     0     my( $transformer, $ast, $compiler ) = @_;
118 0           $ast = $transformer->transform_children( $ast, $compiler );
119 0           my $subrule = $ast->second_child()->first_child();
120 0           my $min = $ast->third_child()->first_child()->first_child();
121 0           my $max = $ast->third_child()->third_child()->first_child()->first_child();
122 0 0         $max = 0 if $max eq '*';
123 0           my $rulename = $self->{'parser'}->get_unique_rulename();
124 0           $compiler->rule( -name => $rulename, -ignored => 0, -repetition => $subrule, -min => $min, -max => $max );
125 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);
126 0           });
127            
128             $t -> transformer( -rule => 'rule-optional', -fn => sub {
129 0     0     my( $transformer, $ast, $compiler ) = @_;
130 0           $ast = $transformer->transform_children( $ast, $compiler );
131 0           my $subrule = $ast->second_child()->first_child();
132 0           my $rulename = $self->{'parser'}->get_unique_rulename();
133 0           $compiler->rule( -name => $rulename, -ignored => 0, -optional => $subrule );
134 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);
135 0           });
136            
137             $t -> transformer( -rule => 'rule-choice', -fn => sub {
138 0     0     my( $transformer, $ast, $compiler ) = @_;
139 0           my @subrules;
140 0           foreach my $subrule (@{$ast->second_child()->first_child()->{'children'}}) {
  0            
141 0           my $_subrule = $transformer->transform_children( $subrule, $compiler );
142 0           push @subrules, $_subrule->first_child()->first_child();
143             }
144 0           my $rulename = $self->{'parser'}->get_unique_rulename();
145 0           $compiler->rule( -name => $rulename, -ignored => 0, -choice => [ @subrules ] );
146 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);
147 0           });
148            
149             $t -> transformer( -rule => 'rule', -fn => sub {
150 0     0     my( $transformer, $ast, $compiler ) = @_;
151 0           $ast = $transformer->transform_children( $ast, $compiler );
152 0 0         if( scalar @{$ast->{'children'}} > 1 ) {
  0            
153 0           my @subrules = map { $_->first_child()->first_child() } @{$ast->{'children'}};
  0            
  0            
154 0           my $rulename = $self->{'parser'}->get_unique_rulename();
155 0           $compiler->rule( -name => $rulename, -sequence => \@subrules );
156 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);
157             }
158             else {
159 0           my $rulename = $ast->first_child()->first_child()->first_child();
160 0           return $compiler->{'grammar-parser'}->make_ast_element('leaf', 'rule-name', $rulename);;
161             }
162 0           });
163            
164             $t -> transformer( -rule => 'definition', -fn => sub {
165 0     0     my( $transformer, $ast, $compiler ) = @_;
166 0           $ast = $transformer->transform_children( $ast, $compiler );
167 0           my $rulename = $ast->first_child()->first_child();
168 0 0         my $ignored = $ast->second_child()->first_child() ? 1 : 0;
169 0           my $old_rulename = $ast->nth_child(4)->first_child();
170            
171             #print "\n-- def --\n";
172             #print "($rulename)($ignored)->($old_rulename)\n";
173             #dump_ast($ast);
174              
175 0           $self->{'parser'}->rename_rule( $old_rulename, $rulename );
176 0           $self->{'grammar-ignore-list'}->{$rulename} = $ignored;
177 0           return $ast;
178 0           });
179              
180 0           $self->{'grammar-ignore-list'} = {}; # flag for each named rule
181 0           $self->{'grammar-parser'} = $p;
182 0           $self->{'grammar-transformer'} = $t;
183 0           return 1;
184             }
185              
186             sub grammar
187             {
188 0     0 0   my( $self, $grammar ) = @_;
189 0           $self->_create_grammar_analyser();
190            
191             # transform the actual grammar into the actual parser
192             # (this will define new tokens and rules)
193            
194 0           my $ast = $self->{'grammar-parser'}->parse( $grammar );
195             #dump_ast($ast);
196             #exit;
197              
198             # we have to give $self
199 0           $self->{'grammar-transformer'}->transform( $ast, $self );
200             #dump($self->{'parser'}->{'tokenizer'}->{'tokennames'});
201             #dump($self->{'parser'}->{'tokenizer'}->{'tokens'});
202             #dump($self->{'parser'}->{'rules'});
203            
204 0           foreach my $rulename (keys %{$self->{'grammar-ignore-list'}}) {
  0            
205 0           my $rule = $self->{'parser'}->get_rule( $rulename );
206 0 0         if( ref $rule eq 'Parse::Highlife::Rule::Token' ) {
207             # set ignored-flag
208 0           my $token = $self->{'parser'}->get_token( $rule->{'token'} );
209 0           $token->{'is-ignored'} = $self->{'grammar-ignore-list'}->{$rulename};
210             }
211             }
212              
213             #dump_ast($ast);
214             #dump($self->{'parser'}->{'tokenizer'});
215             #dump($self->{'parser'}->{'rules'});
216            
217             #dump($self->{'grammar-parser'});
218             #exit;
219             }
220              
221             sub rule
222             {
223 0     0 0   my( $self, @args ) = @_;
224 0           return $self->{'parser'}->rule( @args );
225             }
226              
227             sub toprule
228             {
229 0     0 0   my( $self, @args ) = @_;
230 0           return $self->{'parser'}->toprule( @args );
231             }
232              
233             sub transformer
234             {
235 0     0 0   my( $self, @args ) = @_;
236 0           return $self->{'transformer'}->transformer( @args );
237             }
238              
239             sub stringifier
240             {
241 0     0 0   my( $self, @args ) = @_;
242 0           return $self->{'transformer'}->stringifier( @args );
243             }
244              
245             sub readfile
246             {
247 0     0 0   my( $self, $filename ) = @_;
248 0           return read_file( $filename );
249             }
250              
251             sub parse
252             {
253 0     0 0   my( $self, $string ) = @_;
254 0           my $ast = $self->{'parser'}->parse( $string );
255             #dump_ast($ast);
256 0           return $ast;
257             }
258              
259             sub transform
260             {
261 0     0 0   my( $self, $ast, @args ) = @_;
262 0           my $new_ast = $self->{'transformer'}->transform( $ast, @args );
263             #dump_ast($new_ast);
264 0           return $new_ast;
265             }
266              
267             sub stringify
268             {
269 0     0 0   my( $self, $ast, @args ) = @_;
270 0           return $self->{'transformer'}->stringify( $ast, @args );
271             }
272              
273             sub compile
274             {
275 0     0 0   my( $self, @filenames ) = @_;
276 0           my $stringified = '';
277 0           foreach my $file (@filenames) {
278 0           my $string = $self->readfile( $file );
279 0           my $ast = $self->parse( @filenames );
280 0           my $new_ast = $self->transform( $ast );
281 0           $stringified .= $self->stringify( $new_ast );
282             }
283 0           return $stringified;
284             }
285              
286             sub link
287             {
288 0     0 0   my( $binary_filename ) = @_;
289             # ...
290             }
291              
292             1;