File Coverage

blib/lib/MarpaX/G4/Symboltable.pm
Criterion Covered Total %
statement 67 231 29.0
branch 19 98 19.3
condition 5 87 5.7
subroutine 13 23 56.5
pod 0 19 0.0
total 104 458 22.7


line stmt bran cond sub pod time code
1             # ----------------------------------------------------------------------------------------------------- #
2             # MarpaX::Symboltable #
3             # #
4             # manage a symbol table with rules parsed from an antlr4 grammar. #
5             # #
6             # ----------------------------------------------------------------------------------------------------- #
7              
8             package MarpaX::G4::Symboltable;
9              
10 2     2   11 use strict;
  2         4  
  2         56  
11 2     2   24 use warnings FATAL => 'all';
  2         4  
  2         65  
12 2     2   10 use Data::Dumper;
  2         4  
  2         84  
13 2     2   11 use MarpaX::G4::Parser;
  2         2  
  2         3864  
14              
15             sub new
16             {
17 1     1 0 2 my $invocant = shift;
18 1   33     6 my $class = ref($invocant) || $invocant; # Object or class name
19 1         3 my $self = {}; # initiate our handy hashref
20 1         2 bless($self,$class); # make it usable
21              
22 1         7 $self->{symboltable} = {};
23 1         2 $self->{startrule} = undef;
24 1         2 $self->{currentidx} = -1;
25 1         3 $self->{ruletable} = [];
26              
27 1         2 return $self;
28             }
29              
30 2     2 0 4 sub symbols { my ($self) = @_; return keys %{$self->{symboltable}}; }
  2         4  
  2         11  
31 3     3 0 6 sub startrule { my ($self) = @_; return $self->{startrule}; }
  3         7  
32 2     2 0 3 sub ruletable { my ($self) = @_; return $self->{ruletable}; }
  2         4  
33              
34             sub setStartRule
35             {
36 0     0 0 0 my ($self, $rulename) = @_;
37 0 0       0 die "can't set non-existent rule $rulename as start rule" if !exists $self->{symboltable}{$rulename};
38 0         0 my $symbol = $self->{symboltable}{$rulename};
39 0         0 $self->{startrule} = { name => $rulename, index => $symbol->{index} };
40             }
41              
42             sub rule
43             {
44 85     85 0 99 my ($self, $rulename) = @_;
45 85 50 33     123 $self->addEOF() if $rulename eq "EOF" && !exists $self->{symboltable}{$rulename};
46 85 100 66     191 return undef if !defined $rulename || !exists $self->{symboltable}{$rulename};
47 67         108 return $self->{symboltable}{$rulename};
48             }
49              
50             sub tagrule
51             {
52 28     28 0 36 my ($self, $rulename, $status) = @_;
53 28 50       44 die "trying to tag nonexistent rule '$rulename'" if !exists $self->{symboltable}{$rulename};
54 28         34 my $symbol = $self->{symboltable}{$rulename};
55 28 50       54 $symbol->{generationstatus} = defined($status) ? $status : 'todo';
56             }
57              
58             ##
59             # create a synthetic 'EOF' token, so that the grammar won't fail.
60             ##
61             sub addEOF
62             {
63 0     0 0 0 my ($self) = @_;
64              
65 0         0 $self->addRule( -1,
66             {
67             name => 'EOF',
68             type => 'fragment',
69             generationstatus => 'synthetic',
70             'rightsides' => [
71             {
72             'rhs' => {
73             'token' => {
74             'value' => '\z',
75             'type' => 'literal'
76             }
77             }
78             }
79             ],
80             });
81             }
82              
83             sub rulestatus
84             {
85 23     23 0 31 my ($self, $rulename, $status) = @_;
86              
87 23 50 33     38 $self->addEOF() if $rulename eq "EOF" && !exists $self->{symboltable}{$rulename};
88              
89 23 50       36 die "trying to query nonexistent rule '$rulename'" if !exists $self->{symboltable}{$rulename};
90 23         27 my $symbol = $self->{symboltable}{$rulename};
91 23 100       42 return exists $symbol->{generationstatus} ? $symbol->{generationstatus} : undef;
92             }
93              
94             ## -----------
95             # import the parse tree into the symbol table
96             ## -----------
97             sub importParseTree
98             {
99 1     1 0 5 my ($self, $tree) = @_;
100              
101 1 50       4 die "parse tree must be an array of rules" if ref($tree) ne "ARRAY";
102 1         3 my $ruleindex = $self->{currentidx};
103              
104 1         3 for my $rule (@$tree)
105             {
106 16         16 ++$ruleindex;
107 16 50       22 die "rule[$ruleindex] is not a hash" if ref($rule) ne "HASH";
108              
109             SWITCH: {
110 16 100       15 (exists $rule->{name}) && do {
  16         22  
111 14         16 my $name = $rule->{name};
112             # printf "rule[$ruleindex] : %s\n", $name;
113 14         21 $self->addRule($ruleindex, $rule);
114 14 100       20 $self->{startrule} = { name => $name, index => $ruleindex } if !defined $self->{startrule};
115 14         17 last SWITCH;
116             };
117 2 50       4 (exists $rule->{grammarspec}) && do {
118             # printf "rule[$ruleindex] : grammar %s\n", $rule->{grammarspec};
119 2         3 last SWITCH;
120             };
121 0 0       0 (exists $rule->{comment}) && do {
122             # printf "rule[$ruleindex] : comment\n";
123 0         0 $self->addComment($ruleindex, $rule);
124 0         0 last SWITCH;
125             };
126 0         0 do {
127 0         0 die "rule[$ruleindex] : can't process";
128 0         0 last SWITCH;
129             };
130             }
131             }
132              
133 1         3 $self->{currentidx} = $ruleindex;
134             }
135              
136             sub addRule
137             {
138 14     14 0 17 my ($self, $ruleindex, $rule) = @_;
139              
140 14         15 my $name = $rule->{name};
141 14         12 my $symboltable = \%{$self->{symboltable}};
  14         17  
142              
143             SWITCH: {
144 14 50       14 (exists $rule->{rightsides}) && do {
  14         18  
145 14 50       19 die "$name is a duplicate rule" if exists $symboltable->{$name};
146 14         16 $rule->{index} = $ruleindex;
147 14         22 $symboltable->{$name} = $rule;
148 14         14 last SWITCH;
149             };
150 0         0 do {
151 0         0 die "can't import rule[$ruleindex] : $name";
152 0         0 last SWITCH;
153             };
154             }
155              
156             # add the rule to the index-based lookup table if it is not a synthetic rule.
157 14 50       27 $self->{ruletable}->[$ruleindex] = $rule if $ruleindex != -1;
158             }
159              
160             sub addComment
161             {
162 0     0 0   my ($self, $ruleindex, $rule) = @_;
163 0           $self->{ruletable}->[$ruleindex] = $rule;
164             }
165              
166             ## -----------
167             # recursively walk the symbol table to verify consistency
168             ## -----------
169              
170             sub walkgroup
171             {
172 0     0 0   my ($rulename, $tokengroup) = @_;
173              
174 0           my $namelist = [];
175              
176 0           my $definition = $tokengroup->{definition};
177 0           for my $e (@$definition)
178             {
179 0 0         if (ref $e->{token} eq "")
180             {
181 0           push @$namelist, $e->{token};
182             }
183             else
184             {
185 0 0 0       if (ref $e eq "HASH" && exists $e->{token})
186             {
187 0           my $sr = walktoken($rulename, $e->{token});
188 0           push (@$namelist, @$sr);
189             }
190             else
191             {
192 0           $Data::Dumper::Indent = 1;
193 0           print Dumper($tokengroup);
194 0           die "can't process group for rule $rulename";
195             }
196             }
197             }
198              
199 0           return $namelist;
200             }
201              
202             sub walktoken
203             {
204 0     0 0   my ($rulename, $token) = @_;
205              
206 0           my $namelist = [];
207              
208             SWITCH:
209             {
210 0 0 0       (ref $token eq "HASH" && exists $token->{type} && $token->{type} eq "rulegroup") && do {
  0   0        
211 0           my $sr = walkgroup($rulename, $token->{token});
212 0           push (@$namelist, @$sr);
213 0           last SWITCH;
214             };
215 0 0 0       (ref $token eq "HASH" && exists $token->{type} && $token->{type} eq "tokengroup") && do {
      0        
216 0           my $sr = walkgroup($rulename, $token->{token});
217 0           push (@$namelist, @$sr);
218 0           last SWITCH;
219             };
220 0 0 0       (ref $token eq "HASH" && exists $token->{token}) && do {
221 0           my $nestedtoken = $token->{token};
222 0           my $sr = walktoken($rulename, $nestedtoken);
223 0           push (@$namelist, @$sr);
224 0           last SWITCH;
225             };
226 0 0         (ref $token eq "") && do {
227 0           push @$namelist, $token;
228 0           last SWITCH;
229             };
230 0 0 0       (ref $token eq "HASH" && exists $token->{type} && $token->{type} eq "literal") && do {
      0        
231 0           last SWITCH;
232             };
233 0 0 0       (ref $token eq "HASH" && exists $token->{type} && $token->{type} eq "class") && do {
      0        
234 0           last SWITCH;
235             };
236 0 0 0       (ref $token eq "HASH" && exists $token->{type} && $token->{type} eq "regex") && do {
      0        
237 0           last SWITCH;
238             };
239 0 0 0       (ref $token eq "HASH" && exists $token->{type} && $token->{type} eq "range") && do {
      0        
240 0           last SWITCH;
241             };
242 0 0 0       (ref $token eq "HASH" && exists $token->{type} && $token->{type} eq "value") && do {
      0        
243 0           last SWITCH;
244             };
245 0 0 0       (ref $token eq "HASH" && exists $token->{comment}) && do {
246 0           last SWITCH;
247             };
248 0 0 0       (ref $token eq "HASH" && exists $token->{action}) && do {
249 0           last SWITCH;
250             };
251 0           do {
252 0           $Data::Dumper::Indent = 1;
253 0           print Dumper($token);
254 0           die "can't process token for rule $rulename";
255 0           last SWITCH;
256             };
257             }
258              
259 0           return $namelist;
260             }
261              
262             sub walknonterminal
263             {
264 0     0 0   my ( $rulename, $nonterminal ) = @_;
265              
266 0           my $namelist = [];
267              
268             SWITCH:
269             {
270 0 0         (exists $nonterminal->{rhs}) && do {
  0            
271 0           my $rhs = $nonterminal->{rhs};
272 0           my $sr = walktoken($rulename, $rhs);
273 0           push (@$namelist, @$sr);
274 0           last SWITCH;
275             };
276 0           do {
277 0           $Data::Dumper::Indent = 1;
278 0           print Dumper($nonterminal);
279 0           die "can't process nonterminal for rule $rulename";
280 0           last SWITCH;
281             };
282             }
283              
284 0           return $namelist;
285             }
286              
287             sub walksubrule
288             {
289 0     0 0   my ($rulename, $rule) = @_;
290              
291 0 0 0       if (ref $rule ne "HASH" || !exists $rule->{rightsides})
292             {
293 0           $Data::Dumper::Indent = 1;
294 0           print Dumper($rule);
295 0           die "'rule' is not a hash";
296             }
297              
298 0           my $rhs = $rule->{rightsides};
299              
300 0 0         return [] if !defined $rhs;
301              
302 0 0         if (ref $rhs ne "ARRAY")
303             {
304 0           $Data::Dumper::Indent = 1;
305 0           print Dumper($rhs);
306 0           die "'rhs' is not an array ref";
307             }
308              
309 0           my $namelist = [];
310              
311 0           for my $r (@$rhs)
312             {
313 0           my $sr = walknonterminal($rulename, $r);
314 0           push (@$namelist, @$sr);
315             }
316              
317 0           return $namelist;
318             }
319              
320             sub joinReferences
321             {
322 0     0 0   my ($sr) = @_;
323              
324 0           my $temp = {};
325 0           my $result = "";
326 0           my $delim = "";
327              
328 0           for my $s (@$sr)
329             {
330 0 0         if (!exists $temp->{$s})
331             {
332 0           $temp->{$s} = 1;
333 0           my $len = 16 - length($s);
334 0           my $ts = $s;
335 0 0         if ($len < 0)
336             {
337 0           $len = 0;
338 0           $ts = substr($ts, 0, 16);
339             }
340 0           my $pad = "";
341 0 0         $pad = ' ' x $len if $len > 0;
342 0           $result .= $delim . $ts . $pad;
343 0           $delim = " ";
344             }
345             }
346              
347 0           return $result;
348             }
349              
350             sub verifySymbolNames
351             {
352 0     0 0   my ($self, $rulename, $symbolnames ) = @_;
353              
354 0           my $symboltable = \%{$self->{symboltable}};
  0            
355              
356 0           for my $sn (@$symbolnames)
357             {
358 0 0         if (!exists $symboltable->{$sn})
359             {
360 0           printf "[%-1s][%-45s][%-2s] missing from symbol table : %s\n", "", $rulename, "", $sn;
361             }
362             }
363             }
364              
365             sub validateSymbolTable
366             {
367 0     0 0   my ($self) = @_;
368              
369 0           my $symboltable = \%{$self->{symboltable}};
  0            
370              
371 0           printf "===\n=== Composite Rules\n===\n\n";
372 0           printf <<'END_OF_SOURCE';
373             +-------------------------------------------------------- rule name
374             +--!-------------------------------------------------------- Fragment (F), Lexeme (L) or regular rule
375             ! ! +--------- redirected (->) or contributing rule
376             ! ! ! +----- number of rule references
377             ! ! ! ! +- list of rule references
378             ! ! ! ! !
379             V V V V V
380             END_OF_SOURCE
381              
382 0           for my $name (sort keys %$symboltable)
383             {
384 0           my $rule = $symboltable->{$name};
385              
386             SWITCH:
387             {
388 0           (exists $rule->{name}) && do
389 0 0         {
390 0           my $name = $rule->{name};
391             # if ($name eq "alter_table_properties")
392             # {
393             # printf "found!\n";
394             # }
395 0           my $symbolreferences = walksubrule($name, $rule);
396              
397 0 0         if (scalar @$symbolreferences > 0)
398             {
399 0           my $strReferences = joinReferences($symbolreferences);
400 0           my $type = "";
401 0 0 0       $type = "L" if exists $rule->{isLexeme} || (exists $rule->{grammarstate} && $rule->{grammarstate} eq "lexer");
      0        
402 0 0 0       $type = "F" if exists $rule->{type} && $rule->{type} eq "fragment";
403 0 0         printf "[%-1s][%-45s][%-2s][%2d] %s\n", $type, $name, (exists $rule->{redirect}) ? "->" : "", scalar @$symbolreferences, $strReferences;
404 0           $self->verifySymbolNames( $name, $symbolreferences );
405             }
406 0           last SWITCH;
407             };
408             do
409 0           {
410 0           die "can't process rule";
411 0           last SWITCH;
412             };
413             }
414             }
415              
416 0           printf "\n===\n=== Basic Rules\n===\n\n";
417 0           printf <<'END_OF_SOURCE';
418             +-------------------------------------------------------- rule name
419             +--!-------------------------------------------------------- Fragment (F), Lexeme (L) or regular rule
420             ! ! +--------- redirected (->) or contributing rule
421             ! ! ! +----- n/a
422             ! ! ! !
423             V V V V
424             END_OF_SOURCE
425              
426 0           for my $name (sort keys %$symboltable)
427             {
428 0           my $rule = $symboltable->{$name};
429              
430             SWITCH:
431             {
432 0           (exists $rule->{name}) && do
433 0 0         {
434 0           my $name = $rule->{name};
435 0           my $symbolreferences = walksubrule($name, $rule);
436              
437 0 0         if ($name eq "TILDE_OPERATOR_PART")
438             {
439 0           printf "found!\n";
440             }
441 0 0         if (scalar @$symbolreferences == 0)
442             {
443 0           my $type = "";
444 0 0 0       $type = "L" if exists $rule->{isLexeme} || (exists $rule->{grammarstate} && $rule->{grammarstate} eq "lexer");
      0        
445 0 0 0       $type = "F" if exists $rule->{type} && $rule->{type} eq "fragment";
446 0 0         printf "[%-1s][%-45s][%-2s][%2s] %s\n", $type, $name, (exists $rule->{redirect}) ? "->" : "", "", "";
447             }
448 0           last SWITCH;
449             };
450             do
451 0           {
452 0           die "can't process rule";
453 0           last SWITCH;
454             };
455             }
456             }
457              
458 0           printf "\n";
459             }
460              
461             1;
462              
463             # ABSTRACT: manage symbol table of rules parsed from antlr grammar
464              
465             =head1 SYNOPSIS
466             use MarpaX::G4::Symboltable;
467             my $symboltable = new MarpaX::G4::Symboltable;
468              
469             my $grammartext = readFile($infile);
470             my $data = MarpaX::G4::Parser::parse_rules($grammartext);
471             $symboltable->importParseTree($data);
472             $symboltable->validateSymbolTable();
473              
474             =head1 DESCRIPTION
475             Import the rules from the ANTLR4 parse tree into a symbol table.
476             'validateSymbolTable' does a depth-first tree traversal of the symbol table to produce a report of productions and terminal symbols.
477             =cut
478