File Coverage

blib/lib/Parse/YALALR/Parser.pm
Criterion Covered Total %
statement 25 116 21.5
branch 0 12 0.0
condition 0 6 0.0
subroutine 9 29 31.0
pod 0 20 0.0
total 34 183 18.5


line stmt bran cond sub pod time code
1             # -*- cperl -*-
2             # FYI: -*-mode: Lisp; fill-column: 75; comment-column: 50; -*-
3             #
4              
5             # Design of conditionals:
6             # Parser reads code as either {: ... :} or {? ... ?}. The latter case is a
7             # conditional. (future extension: {c: ... :} vs {perl: ... :})
8             # When a conditional is read, it is replaced with a dummy token. Any state
9             # containing a rule A -> \alpha . DUMMY \beta is flagged as special
10             # (action is multi? yes, that would correctly disambiguate based on lookahead
11             # before falling back to checking the conditional.) All conditionals valid
12             # for the observed lookahead are executed in an arbitrary order. One of the
13             # true ones (but do not short circuit! unless order becomes not arbitrary)
14             # gets its DUMMY token shifted. If multiple are true, issue a warning or
15             # maybe user-configurably abort or go to error recovery. If none are true,
16             # pretty much the same, though the default should probably then be error
17             # recovery.
18             #
19             # DUMMY tokens are considered nullable for the purposes of FIRST computation
20             # (??). They are allowed at the beginning of the RHS (?). Should code be
21             # allowed there too? I suppose. Run all of it, again in an arbitrary order.
22              
23             # BEGIN {
24             # $SIG{__WARN__} = sub { print STDERR shift; $DB::single = 1; };
25             # };
26              
27             package item;
28 1     1   4 use fields qw(GRAMIDX LA EFFECTS LA_WHY CAUSES SOURCES DESTS);
  1         3  
  1         9  
29              
30             package Parse::YALALR::Parser;
31              
32 1     1   100 use Parse::YALALR::Common;
  1         2  
  1         9  
33 1     1   4 use Parse::YALALR::Vector;
  1         2  
  1         27  
34 1     1   5 use Parse::YALALR::Kernel;
  1         2  
  1         53  
35              
36             # Load in the dumping extensions. The BEGIN {require} stuff is just
37             # to make it clear that this is not an independent module; it would
38             # work to say use instead.
39 1     1   566 BEGIN { require 'Parse/YALALR/Dump.pl'; };
40              
41             use fields
42             # Major overarching things
43 1         12 (grammar => # array of symbols in all rules, separated by nils
44             symmap => # Parse::YALALR::Vector
45              
46             # Fundamental data
47             states => # [ state id => state ]
48             nstates => # integer (number of states)
49             rules => # [ rule number => grammar index of rule ]
50             items => # { grammar index -> [ item w/ same gramidx ] }
51             nonterminals => # [ symbol ]
52             tokens => # [ symbol ]
53             precedence => # [ token => ]
54             rule_code => # [ rulepos => code_subroutine ]
55              
56             # Fundamental computed data
57             ruletable => # [ nonterminal => [ grammar_index of lhs for rule ] ]
58             rule_precedence => # [ rule => ]
59              
60             # Lookup tables
61             rulenum => # { grammar index of rule => rule number }
62              
63             # Attributes of data
64             codesyms => # [ symbol ]
65             code => # [ code_index => code_subroutine ]
66             epsilonrules => # [ grammar index of rule X -> /*empty*/ ]
67             end_action_symbols => # { symbol '@n' from converting A -> x {...} to
68             # A -> x @n and @n -> /*empty*/ }
69              
70             # Silly singletons
71             nil => # symbol
72             end => # symbol
73             error => # symbol
74             startsym => # symbol
75             startrule => # rule START -> (start symbol)
76             nilvec => # vec
77             init_state => # state START -> . (start symbol), $
78              
79             # misc & unclassified
80             ntflag => # [ symbol => boolean ]
81              
82             dump_format => # default format (undef or 'xml') for dump()
83              
84 1     1   7 'temp_tokmap'); #
  1         2  
85              
86 1     1   180 use strict;
  1         1  
  1         35  
87 1     1   6 use Carp qw(verbose croak);
  1         1  
  1         255  
88              
89             sub new {
90 0     0 0   my ($class, %opts) = @_;
91              
92 1     1   7 no strict 'refs';
  1         2  
  1         1480  
93 0           my Parse::YALALR::Parser $self = bless [\%{"$class\::FIELDS"}], $class;
  0            
94 0           $self->{nstates} = 0;
95              
96 0           my $symmap = $self->{symmap} = Parse::YALALR::Vector->new;
97 0           $self->{nil} = $symmap->add_value('');
98 0           $self->{end} = $symmap->add_value('');
99 0           $self->{error} = $symmap->add_value('error');
100              
101 0           return $self;
102             }
103              
104             sub register_token {
105 0     0 0   my Parse::YALALR::Build $self = shift;
106 0           my ($token) = @_;
107 0           $self->{temp_tokmap}->{$token} = 1;
108             }
109              
110             sub new_item {
111 0     0 0   my ($self, $item, $la) = @_;
112 0           return bless [ \%item::FIELDS, $item, $la ], 'item';
113             }
114              
115             sub get_rule {
116 0     0 0   my ($self, $item) = @_;
117 0 0         $item = $item->{GRAMIDX} if (ref $item);
118 0           my $grammar = $self->{grammar};
119 0           my $nil = $self->{nil};
120 0   0       --$item while ($item && ($grammar->[$item-1] != $nil));
121 0           return $item;
122             }
123              
124             sub get_rules {
125 0     0 0   my ($self, $A) = @_;
126 0           my $set = $self->{ruletable}->{$A};
127 0 0         return defined $set ? @$set : ();
128             }
129              
130             sub get_chains {
131 0     0 0   my ($self, $A, $B) = @_;
132 0           my $chains = $self->{chainrules}->{$A}->{$B};
133 0 0         return defined $chains ? @$chains : ();
134             }
135              
136             # integer var: 17usec/incr
137             # vector var: 24usec/incr
138             # array var: 18usec/incr
139             # hash var: 43usec/incr
140              
141             # changing index
142             # array var: 71usec/incr
143             # vector var: 85usec/incr
144             # hash var: 91usec/incr
145              
146             #sub epsilon_rule {
147             # my ($self, $rule) = @_;
148             # return vec($self->{grammar}, $rule+1, 32) == $self->{nil};
149             #}
150              
151             # Returns
152             # undef if a CODE symbol
153             # 0 if a nonterminal
154             # 1 if a terminal
155             sub is_token {
156 0     0 0   my Parse::YALALR::Parser $self = shift;
157 0           my ($sym) = @_;
158 0           return ! $self->{ntflag}->[$sym];
159             }
160              
161             sub is_nonterminal {
162 0     0 0   my Parse::YALALR::Parser $self = shift;
163 0           my ($sym) = @_;
164 0           return $self->{ntflag}->[$sym];
165             }
166              
167             sub is_codesym {
168 0     0 0   my Parse::YALALR::Parser $self = shift;
169 0           my ($sym) = @_;
170 0           return exists $self->{codesyms}->{$sym};
171             }
172              
173             sub get_dot {
174 0     0 0   my Parse::YALALR::Parser $self = shift;
175 0           my ($I) = @_;
176 0           return $self->{grammar}->[$I->{GRAMIDX}];
177             }
178              
179             sub get_shift {
180 0     0 0   my Parse::YALALR::Parser $self = shift;
181 0           my ($I) = @_;
182 0 0         croak("shifted off end of item")
183             if $self->{grammar}->[$I->{GRAMIDX}] == $self->{nil};
184 0           return $self->make_shift($I->{GRAMIDX}, $I->{LA});
185             }
186              
187             sub make_shift {
188             # return bless [ \%item::FIELDS,
189             # $_[1] + 1, $_[2]
190             # ], 'item';
191              
192 0     0 0   my Parse::YALALR::Parser $self = shift;
193 0           my ($item, $first) = @_;
194 0 0         croak("bad thing")
195             if $self->{grammar}->[$item] == $self->{nil};
196 0           return bless [ \%item::FIELDS,
197             $item + 1, $first
198             ], 'item';
199             }
200              
201             sub get_dotalpha {
202 0     0 0   my ($self, $item) = @_;
203 0           my $grammar = $self->{grammar};
204 0           my $nil = $self->{nil};
205              
206 0           my @alpha;
207 0           while ($grammar->[$item] != $nil) {
208 0           push(@alpha, $grammar->[$item++]);
209             }
210              
211 0           return @alpha;
212             }
213              
214             sub get_la {
215 0     0 0   my ($self, $I) = @_;
216 0           return $I->{LA};
217             }
218              
219             sub get_item_lhs {
220 0     0 0   my ($self, $I) = @_;
221 0           my $grammar = $self->{grammar};
222 0           my $nil = $self->{nil};
223              
224 0           my $rule = $I->{GRAMIDX};
225 0   0       while ($rule > 0 && $grammar->[$rule - 1] != $nil) { $rule--; }
  0            
226              
227 0           return $self->{grammar}->[$rule];
228             }
229              
230             # make_item
231             #
232             # INPUT:
233             # $rule : grammar_index of rule
234             # $pos : position of . within rule
235             # $first : FIRST set
236             #
237             # OUTPUT:
238             # [ GRAMIDX, LA ] : item
239             # GRAMIDX : grammar_index of symbol just past $pos for $rule
240             # LA : Lookahead set of tokens
241             #
242             sub make_item {
243 0     0 0   my Parse::YALALR::Parser $self = shift;
244 0           my ($rule, $pos, $first) = @_;
245 0 0         if ($pos < 0) {
246 0           my $nil = $self->{nil};
247 0           my $grammar = $self->{grammar};
248 0           while ($grammar->[$rule] != $nil) { $rule++; }
  0            
249             }
250 0           return bless [ \%item::FIELDS, $rule + $pos + 1, $first ], 'item';
251             }
252              
253             sub add_shift {
254 0     0 0   my ($self, $K, $sym, $K2) = @_;
255 0           $K->{shifts}->{$sym} = $K2->{id};
256             }
257              
258             # $self->{reduces} : [ ]
259             sub add_reduce {
260 0     0 0   my ($self, $K, $rule, $la, $parent, $reason) = @_;
261             # REASON is ignored
262 0           push(@{$K->{reduces}}, [ $la, $rule, $parent ]);
  0            
263 0           $K->{REDUCE_WHY}->{$la} = [ $rule, $parent, 'generated' ];
264             }
265              
266             sub rule_size {
267 0     0 0   my ($self, $rule) = @_;
268 0           my $i = 0;
269 0           while ($self->{grammar}->[$rule + $i + 1] != $self->{nil}) { $i++; };
  0            
270 0           return $i;
271             }
272              
273             sub stats {
274 0     0 0   my Parse::YALALR::Parser $self = shift;
275 0           my $str = '';
276 0           $str .= "Number of states: $self->{nstates}\n";
277 0           $str .= "Number of terminals: " . (0+@{$self->{tokens}}) . "\n";
  0            
278 0           $str .= "Number of nonterminals: " . (0+@{$self->{nonterminals}}) . "\n";
  0            
279 0           $str .= "Number of rules: " . (0+@{$self->{rules}}) . "\n";
  0            
280 0           return $str;
281             }
282              
283             1;