File Coverage

blib/lib/Parse/Highlife/Parser.pm
Criterion Covered Total %
statement 39 112 34.8
branch 0 32 0.0
condition 0 3 0.0
subroutine 13 24 54.1
pod 0 9 0.0
total 52 180 28.8


line stmt bran cond sub pod time code
1             package Parse::Highlife::Parser;
2              
3 1     1   6 use strict;
  1         1  
  1         42  
4 1     1   5 use Parse::Highlife::Utils qw(params dump_tokens);
  1         2  
  1         43  
5 1     1   793 use Parse::Highlife::Rule::Sequence;
  1         3  
  1         31  
6 1     1   578 use Parse::Highlife::Rule::Choice;
  1         2  
  1         28  
7 1     1   662 use Parse::Highlife::Rule::Repetition;
  1         2  
  1         23  
8 1     1   530 use Parse::Highlife::Rule::Optional;
  1         3  
  1         22  
9             #use Parse::Highlife::Rule::Literal; # not needed anymore
10             #use Parse::Highlife::Rule::Regex; # not needed anymore
11 1     1   482 use Parse::Highlife::Rule::Token;
  1         3  
  1         20  
12 1     1   6 use Parse::Highlife::Token::Characters;
  1         1  
  1         22  
13 1     1   5 use Parse::Highlife::Token::Delimited;
  1         2  
  1         18  
14 1     1   4 use Parse::Highlife::Token::Regex;
  1         1  
  1         16  
15 1     1   3 use Parse::Highlife::Tokenizer;
  1         2  
  1         14  
16 1     1   490 use Parse::Highlife::AST;
  1         3  
  1         36  
17 1     1   6 use Data::Dump qw(dump);
  1         2  
  1         1334  
18             $Data::Dump::INDENT = ". ";
19              
20             sub new
21             {
22 0     0 0   my( $class, @args ) = @_;
23 0           my $self = bless {}, $class;
24 0           return $self -> _init( @args );
25             }
26              
27             sub _init
28             {
29 0     0     my( $self, @args ) = @_;
30 0           $self->{'tokenizer'} = Parse::Highlife::Tokenizer -> new();
31 0           $self->{'rules'} = {};
32 0           $self->{'top-rule'} = '';
33 0           $self->{'current-indent'} = 0;
34 0           $self->{'rulename-counter'} = 0;
35 0           $self->{'debug'} = 0;
36 0           return $self;
37             }
38              
39             sub make_ast_element
40             {
41 0     0 0   my( $self, @args ) = @_;
42 0           return Parse::Highlife::AST->new( @args );
43             }
44              
45             sub get_unique_rulename
46             {
47 0     0 0   my( $self ) = @_;
48 0           $self->{'rulename-counter'} ++;
49 0           return '#AUTORULE-'.$self->{'rulename-counter'};
50             }
51              
52             sub toprule
53             {
54 0     0 0   my( $self, $name )
55             = params( \@_,
56             -name => '',
57             );
58 0           $self->{'top-rule'} = $name;
59 0           return 1;
60             }
61              
62             sub get_rule
63             {
64 0     0 0   my( $self, $rulename ) = @_;
65 0 0         die "ERR: I do not know about a rule named '$rulename'\n"
66             unless exists $self->{'rules'}->{$rulename};
67 0           return $self->{'rules'}->{$rulename};
68             }
69              
70             sub get_token
71             {
72 0     0 0   my( $self, $tokenname ) = @_;
73 0           return $self->{'tokenizer'}->get_token( $tokenname );
74             }
75              
76             sub rename_rule
77             {
78 0     0 0   my( $self, $old_name, $new_name ) = @_;
79 0 0         if( exists $self->{'rules'}->{$old_name} ) {
80 0           $self->{'rules'}->{$new_name} = $self->{'rules'}->{$old_name};
81 0           $self->{'rules'}->{$new_name}->{'name'} = $new_name;
82 0           delete $self->{'rules'}->{$old_name};
83             }
84 0           return $self;
85             }
86              
87             sub rule
88             {
89             #print join(',',@_)."\n";
90 0     0 0   my( $self,
91             $name, $ignored,
92             $sequence, $choice, $repetition, $min, $max, $optional, $literal, $token,
93             $start, $end, $escape, $characters, $regex )
94             = params( \@_,
95             -name => '',
96             -ignored => 0,
97            
98             # rules
99             -sequence => '',
100             -choice => '',
101             -repetition => '', -min => 0, -max => 0,
102             -optional => '',
103             -literal => '',
104             -token => '',
105              
106             # tokens encapsulated in rules of type "token"
107             -start => '',
108             -end => '',
109             -escape => "\\",
110             -characters => [],
111             -regex => '',
112             );
113 0           my @args = splice( @_, 1 );
114            
115             #dump(\@args);
116             #print "($ignored)\n";
117              
118 0 0         die "ERR: rule has no name.\n" unless length $name;
119 0 0         die "ERR: rule '$name' is alreay defined.\n"
120             if exists $self->{'rules'}->{$name};
121              
122 0           my $rule;
123 0 0 0       if( ref $sequence ) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
124 0           $rule = Parse::Highlife::Rule::Sequence -> new( @args );
125             }
126             elsif( ref $choice ) {
127 0           $rule = Parse::Highlife::Rule::Choice -> new( @args );
128             }
129             elsif( length $repetition ) {
130 0           $rule = Parse::Highlife::Rule::Repetition -> new( @args );
131             }
132             elsif( length $optional ) {
133 0           $rule = Parse::Highlife::Rule::Optional -> new( @args );
134             }
135             elsif( length $token ) { # NOTE: this is only for internal use, because
136             # the programmer does not know the names of
137             # defined tokens (because they are auto-generated)
138 0           $rule = Parse::Highlife::Rule::Token -> new( @args );
139             }
140             elsif( length $literal ) {
141             # create a regex-token in the tokenizer
142             # and a rule that matches that fixed string
143 0           my $t = $self->{'tokenizer'}->token(
144             -name => '#AUTOTOKEN-'.$name,
145             -ignored => $ignored,
146             -regex => quotemeta $literal,
147             );
148 0           $rule = $self->rule( -name => $name, -token => $t->{'name'} );
149             }
150 0           elsif( length $regex ) {
151             # create a regex-token in the tokenizer
152             # and a rule that matches that type of token
153 0           my $t = $self->{'tokenizer'}->token(
154             -name => '#AUTOTOKEN-'.$name,
155             -ignored => $ignored,
156             -regex => $regex,
157             );
158 0           $rule = $self->rule( -name => $name, -token => $t->{'name'} );
159             }
160             elsif( scalar @{$characters} ) {
161             # create a characters-token in the tokenizer
162             # and a rule that matches that type of token
163 0           my $t = $self->{'tokenizer'}->token(
164             -name => '#AUTOTOKEN-'.$name,
165             -ignored => $ignored,
166             -characters => $characters,
167             );
168 0           $rule = $self->rule( -name => $name, -token => $t->{'name'} );
169             }
170             elsif( length $start && length $end ) {
171             # create a delimited-token in the tokenizer
172             # and a rule that matches that type of token
173 0           my $t = $self->{'tokenizer'}->token(
174             -name => '#AUTOTOKEN-'.$name,
175             -ignored => $ignored,
176             -start => $start,
177             -end => $end,
178             -escape => $escape
179             );
180 0           $rule = $self->rule( -name => $name, -token => $t->{'name'} );
181             }
182             else {
183 0           die "ERR: incomplete rule definition.\n";
184             }
185            
186 0           $self->{'rules'}->{$name} = $rule;
187            
188 0           return $rule;
189             }
190              
191             sub parse
192             {
193 0     0 0   my( $self, $string ) = @_;
194              
195             #dump($self->{'rules'});
196             #dump($self->{'tokenizer'}->{'tokennames'});
197             #dump($self->{'tokenizer'}->{'tokens'});
198              
199 0           my $tokens = $self->{'tokenizer'}->tokenize( $string );
200             #dump_tokens($tokens);
201            
202             # the return value of each parser function:
203             # [0] = status flag (1 = success, 0 = failure)
204             # [1] = the token offset after the last token parsed for the result
205             # [2] = the result (abstract syntax tree)
206 0           my ($status, $t, $ast)
207             = $self->_parse_rule_from_token( $self->{'top-rule'}, $tokens, 0 );
208 0 0         die "ERR: failed to parse string.\n" if $status == 0;
209             #print "parsed #$t tokens of ".scalar(@{$tokens})."\n";
210            
211 0           my $_t;
212 0           ($_t) = Parse::Highlife::Rule::_parse_ignored_tokens( undef, $tokens, $t );
213 0 0         if( $_t < scalar @{$tokens} ) {
  0            
214 0           my @_tokens = splice @{$tokens}, $_t, 10;
  0            
215            
216 0           print "INFO: there are unparsed tokens after the parsed rule.\n";
217 0           print "The tokens after the syntax error are:\n";
218 0           dump_tokens(\@_tokens);
219 0           print "...\n";
220 0           exit;
221             #dump($tokens->[$_t]);
222             }
223              
224             #dump($self->{'rules'});
225 0           return $ast;
226             }
227              
228             sub _parse_rule_from_token
229             {
230 0     0     my( $self, $rulename, $tokens, $t ) = @_;
231             #return (0,0,0) if $t >= scalar(@{$tokens});
232 0 0         return (0,0,0) unless exists $self->{'rules'}->{$rulename};
233 0           my $rule = $self->{'rules'}->{$rulename};
234 0           return $rule->wrap_parse_from_token( $self, $tokens, $t );
235             }
236              
237             1;
238              
239