File Coverage

lib/Parser/LR.pm
Criterion Covered Total %
statement 226 242 93.3
branch 47 66 71.2
condition 4 8 50.0
subroutine 26 28 92.8
pod 12 13 92.3
total 315 357 88.2


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             #-------------------------------------------------------------------------------
3             # Parser::LR - Create and use an LR(1) parser.
4             # Philip R Brenan at gmail dot com, Appa Apps Ltd Inc., 2019
5             #-------------------------------------------------------------------------------
6             # podDocumentation
7             package Parser::LR;
8             require v5.26;
9             our $VERSION = 20191122;
10 1     1   660 use warnings FATAL => qw(all);
  1         7  
  1         36  
11 1     1   5 use strict;
  1         1  
  1         27  
12 1     1   5 use Carp;
  1         2  
  1         77  
13 1     1   531 use Data::Dump qw(dump);
  1         7487  
  1         62  
14 1     1   3257 use Data::Table::Text qw(:all);
  1         124565  
  1         4296  
15 1     1   1411 use Data::DFA;
  1         16749  
  1         47  
16 1     1   8 use Data::NFA;
  1         2  
  1         82  
17 1     1   532 use Math::Cartesian::Product;
  1         723  
  1         4979  
18              
19             our $logFile = q(/home/phil/z/z/z/zzz.txt); # Log printed results if developing
20              
21             #D1 Create and use an LR(1) parser. # Construct an LR(1) parser from a set of rules using L; use the parser produced to parse sequences of terminal symbols using L; print the resulting parse tree with L or L.
22              
23             sub printRule($) #P Print a rule
24 0     0 1 0 {my ($rule) = @_; # Rule
25 0         0 my $r = $rule->rule;
26 0         0 my $x = $rule->expandable;
27 0         0 my $e = join ' ', $rule->expansion->@*;
28 0         0 qq($r($e)$x)
29             }
30              
31             sub compileGrammar($%) # Compile a grammar from a set of rules expressed as a string with one rule per line. Returns a L.
32             {my ($rules, %options) = @_; # Rule definitions, options
33              
34             my @lines = map {[split /\s+/]} split /\n/, $rules =~ s(#.*?\n) (\n)gsr; # Words from lines from grammar minus comments
35              
36             my sub newRule(%) # Create a new rule
37             {my (%options) = @_; # Options
38             genHash(q(Parser::LR::Rule), # A parsing rule
39             expandable => undef, # Symbol to expand
40             expansion => undef, # Symbol expansion
41             print => \&printRule, # Rule printer
42             rule => undef, # Rule number
43             %options,
44             );
45             }
46              
47             my @rules; # {symbol}[rule] = a rule to expand symbol
48              
49             for my $line(@lines) # Lines of input
50             {if (my ($expandable, @expansion) = @$line) # Symbol to expand, expansion
51             {push @rules, newRule # A parsing rule
52             (expandable => $expandable, # Symbol to expand
53             expansion => [@expansion], # Symbol expansion
54             print => \&printRule, # Rule printer
55             rule => scalar @rules, # Rule number
56             );
57             }
58             }
59              
60             my $grammar = bless \@rules, q(Parser::LR::Grammar); # {symbol to expand => {expansion=>[]}}
61             my $startSymbol = $$grammar[0]->expandable; # Locate the start symbol as the symbol expanded by the first rule
62              
63             my %expandables; my %terminals; # Find the expandable and terminal symbols
64              
65             for my $rule(@rules) # Expandables
66             {$expandables{$rule->expandable}++;
67             }
68              
69             for my $rule(@rules) # Terminals
70             {$terminals{$_}++ for grep {!$expandables{$_}} $rule->expansion->@*;
71             }
72              
73             my %reducers; # The expandables an expandable can reduce to
74             for my $r(@rules)
75             {my $e = $r->expandable;
76             my @e = $r->expansion->@*;
77             if (@e == 1)
78             {$reducers{$e[0]}{$e}++;
79             }
80             }
81              
82             for my $r (sort keys %reducers) # Propogate reduction of expandables
83             {my $n = 0;
84             for my $r1(sort keys %reducers)
85             {for my $r2(sort keys $reducers{$r1}->%*)
86             {for my $r3(sort keys $reducers{$r2}->%*)
87             {++$n;
88             $reducers{$r1}{$r3}++;
89             }
90             }
91             }
92             }
93              
94             my %recursiveExpandables; # Find self recursive expandables
95             for my $e(sort keys %reducers)
96             {if ($reducers{$e}{$e})
97             {$recursiveExpandables{$e}++
98             }
99             }
100              
101             if (keys %recursiveExpandables) # Check for self recursive expandables
102             {die "Recursive expandables:". dump([sort keys %recursiveExpandables]);
103             }
104              
105             my %optionalExpandables;
106             for my $rule(@rules) # Expandables that have empty expansions
107             {my @e = $rule->expansion->@*;
108             if (!@e)
109             {$optionalExpandables{$rule->expandable}++;
110             }
111             }
112              
113             if (!$options{nosub}) # Substitute rules that do not refer to themselves
114             {my sub appearances($$) # How many times an expandable appears in the expansion of a rule
115             {my ($expandable, $rule) = @_;
116             my $n = 0;
117             for my $e($rule->expansion->@*)
118             {++$n if $e eq $expandable;
119             }
120             $n # Number of times this expandable appears in this rule
121             } # appearances
122              
123             my sub selfRecursive($) # The number of rules the expandable is self recursive in
124             {my ($expandable) = @_;
125             my $n = 0;
126             for my $rule(@rules)
127             {if ($expandable eq $rule->expandable) # A rule expanding this expandable
128             {$n += appearances($expandable, $rule);
129             }
130             }
131             $n # Number of times this expandable appears in this rule
132             } # selfRecursive
133              
134             my sub rulesForExpandable($) # The rules that expand an expandable
135             {my ($expandable) = @_;
136             my @e;
137             for my $rule(@rules) # Terminals that start any rule
138             {if ($rule->expandable eq $expandable)
139             {push @e, $rule;
140             }
141             }
142             @e # Rules for this expandable
143             } # rulesForExpandable
144              
145             my sub addRulesBySubstituting($$$@) # Add rules by substituting the expansions of a non self recursive expandable
146             {my ($expandable, $appearances, $rule, @rules) = @_; # Expandable to substitute, appearances in rule to substitute into, rule to substitute into, rules to substitute from.
147             my @expansions = map {$_->expansion} @rules; # Expansion for each rule of the expandable being substituted
148             my @c; cartesian {push @c, [@_]} map {[@expansions]} 1..$appearances; # Cartesian product of the expansion rules by the number of times it appears in the rule
149             my @n; # New rules
150              
151             for my $c(@c) # Create new rules for each element of the cartesian product
152             {my @f;
153             for my $e($rule->expansion->@*)
154             {if ($expandable eq $e) # Substitute
155             {my $f = shift @$c;
156             push @f, @$f;
157             }
158             else
159             {push @f, $e; # Retain
160             }
161             }
162             push @n, newRule(expandable => $rule->expandable, expansion => [@f]); # Create new rule from substitution
163             }
164             @n # New rules
165             } # addRulesBySubstituting
166              
167             if (1) # Substitute non self recurring expandables to create more rules with fewer expandables
168             {my $changes = 1;
169             for my $a(1..10) # Maximum number of expansion passes
170             {next unless $changes; $changes = 0; # While there were changes made
171             for my $e(sort keys %expandables) # Each expandable
172             {if (!selfRecursive($e)) # Each non self recursive expandable symbol
173             {my @r = rulesForExpandable($e); # Rule set for the expandable being substituted
174             my @n;
175             for my $r(@rules) # Each rule
176             {if (my $n = appearances($e, $r)) # Number of times this expandable is mentioned in this rule - rules where it is the expandable will be ignored because we are only processing non self recursive expandables.
177             {push @n, addRulesBySubstituting($e, $n, $r, @r);
178             ++$changes;
179             }
180             elsif ($r->expandable ne $e) # Retain a rule which has no contact with the expandable being substituted
181             {push @n, $r;
182             }
183             }
184             @rules = @n;
185             }
186             }
187             }
188             }
189             }
190              
191             my %startTerminals;
192             for my $rule(@rules) # Terminals that start any rule
193             {my @e = $rule->expansion->@*;
194             if (my ($e) = $rule->expansion->@*)
195             {if ($terminals{$e})
196             {$startTerminals{$e}++;
197             }
198             }
199             }
200              
201             my $longestRule = 0;
202             for my $rule(@rules) # Longest rule
203             {my $l = $rule->expansion->@*;
204             $longestRule = max($longestRule, $l);
205             }
206              
207             my $nfa = bless {}, q(Data::NFA); # NFA being constructed
208              
209             my $newState = sub # Create a new state in the specified B<$nfa>
210 284     284   352 {my $n = scalar keys %$nfa; # Current NFA size
211 284         448 $$nfa{$n} = Data::NFA::newNfaState; # Create new state
212 284         11658 $n # Number of state created
213             };
214              
215             my $start = &$newState($nfa); # The expansions of each symbol are located from the start state by applying the symbol to be expanded
216              
217             for my $rule(@$grammar) # For each symbol in the expansion
218             {my $expansion = $rule->expansion; # Expansion part of rule
219             my $pos = $start; # Start state for rule
220             for my $e(@$expansion) # Transition to the next state on symbol being consumed
221             {my $p = $pos;
222             my $q = &$newState($nfa);
223             $$nfa{$p}->jumps->{$q}++;
224             $pos = $$nfa{$q}->transitions->{$e} = &$newState($nfa);
225             }
226              
227             $$nfa{$pos}->final = $rule; # Mark the final state with the sub to be called when we reduce by this rule
228             }
229              
230             my $finalState = $$nfa{$start}->transitions->{$startSymbol}=&$newState($nfa); # Transition on start symbol
231              
232             for my $i(sort keys %$nfa) # Add a jump to the symbol jump in state for each expandable symbol
233             {my $state = $$nfa{$i}; # State
234             delete $$state{final} unless defined $$state{final};
235             delete $$state{jumps} unless keys $$state{jumps}->%*;
236             delete $$state{transitions} unless keys $$state{transitions}->%*;
237             }
238              
239             my $dfa = Data::DFA::fromNfa($nfa); # DFA from grammar NFA
240              
241             for my $state(values $dfa->%*) # Remove irrelevant fields from each state
242             {delete @$state{qw(nfaStates pump sequence state)};
243              
244             for(grep {!defined $$state{$_}} qw(final transitions))
245             {delete $$state{$_};
246             }
247             }
248              
249             for my $state(sort keys %$dfa) # Check for multiple reductions
250             {if (my $final = $$dfa{$state}->final)
251             {if (@$final > 1)
252             {lll $dfa->print;
253             die "More than one reduction in state $state";
254             }
255             }
256             }
257              
258             my %expansionStates;
259             for my $state(sort keys %$dfa) # Mark expansions states
260             {for my $symbol(sort keys $$dfa{$state}->transitions->%*)
261             {if ($expandables{$symbol})
262             {$expansionStates{$state}++;
263             }
264             }
265             }
266              
267             for my $i(keys @rules) # Renumber rules
268             {$rules[$i]->rule = $i;
269             }
270              
271             genHash(q(Parser::LR::Grammar), # LR parser produced
272             grammar => $grammar, # Grammar from which the NFA was derived
273             nfa => $nfa, # NFA from grammar
274             dfa => $dfa, # DFA from grammar
275             expandables => \%expandables, # Expandable symbols
276             expansionStates => \%expansionStates, # States we can expand in
277             terminals => \%terminals, # Terminal symbols
278             reducers => \%reducers, # The expandables an expandable can reduce to
279             startSymbol => $startSymbol, # Start symbol
280             finalState => $$dfa{0}->transitions->{$startSymbol}, # Final state at end of parse
281             longestRule => $longestRule, # Longest rule
282             rules => [@rules], # Rules
283             startTerminals => \%startTerminals, # Terminals that start rules
284             optionalExpandables => \%optionalExpandables, # Expandables that can expand to nothing
285             );
286             } # compileGrammar
287              
288             sub longestMatchingRule($@) #P Find the longest rule that completely matches the top of the stack.
289 141     141 1 325 {my ($grammar, @stack) = @_; # Grammar, stack
290 141         2371 my $dfa = $grammar->dfa;
291 141         2549 my $L = $grammar->longestRule;
292 141 100       606 $L = @stack if $L > @stack;
293 141         203 my $N = @stack;
294 141         210 my $S = $N-$L;
295 141         158 my $F = $N-1;
296              
297 141         237 position: for my $i($S..$F) # Scan forward on stack for each possible rule
298 258         265 {my $state = 0;
299 258         324 symbol: for my $j($i..$F) # Scan forward from start in state 0 at selected point
300 450         508 {my $symbol = $stack[$j];
301 450 100       6872 if (my $next = $$dfa{$state}->transitions->{$symbol})
302 332         1362 {$state = $next;
303 332         474 next symbol;
304             }
305 118         507 next position;
306             }
307 140         2163 my $final = $$dfa{$state}->final;
308 140 100       811 return $final->[0] if $final; # Return matching rule
309             }
310             undef
311 17         52 }
312              
313             sub partialMatch($@) #P Check whether we have a partial match with the top of the stack.
314 342     342 1 772 {my ($grammar, @stack) = @_; # Grammar, stack
315 342         5598 my $dfa = $grammar->dfa;
316 342         5974 my $L = $grammar->longestRule;
317 342 100       1352 $L = @stack if $L > @stack;
318 342         375 my $N = @stack;
319              
320 342         644 position: for my $i($N-$L..$N-1) # Scan forward on stack from each possible position
321 899         993 {my $state = 0;
322 899         1260 symbol: for my $j($i..@stack-1) # Scan forward with this rule
323 1539         1667 {my $symbol = $stack[$j];
324 1539 100       23290 if (my $next = $$dfa{$state}->transitions->{$symbol})
325 801         3163 {$state = $next;
326 801         1061 next symbol;
327             }
328 738         3110 next position;
329             }
330 161         390 return @stack-$i; # Matches this many characters
331             }
332             0
333 181         381 }
334              
335             sub reduceStackWithRule($$$) #P Reduce by the specified rule and update the stack and parse tree to match.
336 125     125 1 202 {my ($rule, $stack, $tree) = @_; # Rule, stack, parse tree
337 125         1881 my $L = $rule->expansion->@*;
338 125 50       542 if ($L <= @$stack) # Remove expansion
339 125         241 {my @r = splice(@$stack, -$L);
340 125         1905 push @$stack, $rule->expandable;
341 125         2151 my $e = $rule->expansion->@*;
342 125         508 my @s = splice @$tree, -$e;
343 125         1894 push @$tree, bless [$rule->rule, @s], q(Parser::LR::Reduce);
344             }
345             else # Stack too small
346 0         0 {die "Stack too small";
347             }
348             }
349              
350             sub parseWithGrammarAndLog($@) #P Parse, using a compiled B<$grammar>, an array of terminals and return a log of parsing actions taken.
351             {my ($grammar, @terminals) = @_; # Compiled grammar, terminals to parse
352             my $dfa = $grammar->dfa; # Dfa for grammar
353             my @stack;
354             my @log;
355             my @tree;
356              
357             my sub printStack{return join ' ', @stack if @stack; q/(empty)/} # Logging
358             my sub log(@) {my $m = join '', @_; push @log, $m; say STDERR $m}
359             my sub lll(@) {my $m = join '', @_; say STDERR $m}
360              
361             lll join '', "Parse : ", join ' ', @terminals;
362             terminal: while(@terminals) # Parse the terminals
363             {my $terminal = shift @terminals;
364             log "Terminal: $terminal, stack: ", printStack;
365             if (!@stack) # First terminal
366             {push @stack, $terminal;
367             push @tree, $terminal;
368             log " Accept first terminal: $terminal to get stack: ", printStack;
369             }
370             else
371             {my $p = partialMatch($grammar, @stack, $terminal);
372             if (partialMatch($grammar, @stack, $terminal) >= 2) # Fits as is
373             {push @stack, $terminal;
374             push @tree, $terminal;
375             log " Accept $terminal to get stack: ", printStack;
376             }
377             else
378             {if ($grammar->startTerminals->{$terminal}) # Starting terminal = shift now and hope for a later reduction
379             {push @stack, $terminal;
380             push @tree, $terminal;
381             log "Accepted terminal: $terminal as is, stack: ".printStack;
382             next terminal;
383             }
384             else # Not a starting terminal so we will have to reduce to fit now
385             {reduction: for my $r(1..10)
386             {if (my $rule = longestMatchingRule($grammar, @stack))
387             {my $n = $rule->rule;
388             my $e = $rule->expandable;
389             reduceStackWithRule($rule, \@stack, \@tree);
390             log " Reduced by rule $n, expandable: $e, stack: ", printStack;
391             my $P = partialMatch($grammar, @stack, $terminal);
392             if (partialMatch($grammar, @stack, $terminal) >= 2)
393             {push @stack, $terminal;
394             push @tree, $terminal;
395             log " Accept $terminal after $r reductions to get: ", printStack;
396             next terminal;
397             }
398             else
399             {next reduction;
400             }
401             }
402             next terminal;
403             }
404             die "No match after all reductions possible for $terminal on stack "
405             .printStack;
406             }
407             }
408             }
409             }
410              
411             for my $r(1..10) # Final reductions
412             {if (my $rule = longestMatchingRule($grammar, @stack))
413             {my $n = $rule->rule;
414             my $e = $rule->expandable;
415             reduceStackWithRule($rule, \@stack, \@tree);
416             log " Reduced in finals by rule $n, expandable: $e, stack: ", printStack;
417             next;
418             }
419             last;
420             }
421              
422             !@tree and die "No parse tree"; # Check for single parse tree
423             @tree > 1 and die "More than one parse block";
424              
425             log " Parse tree is:\n", &printParseTree($grammar, $tree[0]) if @tree; # Results
426              
427             my $r = join "\n", @log, ''; $r =~ s(\s+\Z) (\n)s; # Remove trailing new lines
428              
429             owf($logFile, $r) if -e $logFile; # Log the result if requested
430             $r
431             } # parseWithGrammarAndLog
432              
433             sub parseWithGrammar($@) # Parse, using a compiled B<$grammar>, an array of terminals and return a parse tree.
434 9     9 1 36 {my ($grammar, @terminals) = @_; # Compiled grammar, terminals to parse
435 9         161 my $dfa = $grammar->dfa; # Dfa for grammar
436 9         49 my @stack;
437             my @log;
438 9         0 my @tree;
439              
440 9         25 terminal: while(@terminals) # Parse the terminals
441 72         98 {my $terminal = shift @terminals;
442 72 100       121 if (!@stack) # First terminal
443 9         19 {push @stack, $terminal;
444 9         22 push @tree, $terminal;
445             }
446             else
447 63         105 {my $p = partialMatch($grammar, @stack, $terminal);
448 63 100       107 if (partialMatch($grammar, @stack, $terminal) >= 2) # Fits as is
449 4         8 {push @stack, $terminal;
450 4         10 push @tree, $terminal;
451             }
452             else
453 59 100       909 {if ($grammar->startTerminals->{$terminal}) # Starting terminal = shift now and hope for a later reduction
454 27         128 {push @stack, $terminal;
455 27         40 push @tree, $terminal;
456 27         57 next terminal;
457             }
458             else # Not a starting terminal so we will have to reduce to fit now
459 32         164 {reduction: for my $r(1..10)
460 67 100       117 {if (my $rule = longestMatchingRule($grammar, @stack))
461 65         1006 {my $n = $rule->rule;
462 65         1168 my $e = $rule->expandable;
463 65         294 reduceStackWithRule($rule, \@stack, \@tree);
464 65         411 my $P = partialMatch($grammar, @stack, $terminal);
465 65 100       108 if (partialMatch($grammar, @stack, $terminal) >= 2)
466 30         51 {push @stack, $terminal;
467 30         46 push @tree, $terminal;
468 30         77 next terminal;
469             }
470             else
471 35         65 {next reduction;
472             }
473             }
474 2         7 next terminal;
475             }
476 0         0 die "No match after all reductions possible for $terminal on stack "
477             .dump(\@stack). "\n";
478             }
479             }
480             }
481             }
482              
483 9         19 for my $r(1..10) # Final reductions
484 27 100       46 {if (my $rule = longestMatchingRule($grammar, @stack))
485 18         279 {my $n = $rule->rule;
486 18         311 my $e = $rule->expandable;
487 18         82 reduceStackWithRule($rule, \@stack, \@tree);
488 18         107 next;
489             }
490 9         17 last;
491             }
492              
493 9 50       18 !@tree and die "No parse tree"; # Check for single parse tree
494 9 50       30 @tree > 1 and die "More than one parse block";
495              
496 9         103 $tree[0]
497             } # parseWithGrammar
498              
499             sub printGrammar($) # Print a B<$grammar>.
500 1     1 1 4 {my ($grammar) = @_; # Grammar
501 1         2 my @r;
502 1         17 for my $rule($grammar->grammar->@*) # Each rule
503 16         802 {push @r, [$rule->rule, $rule->expandable, $rule->expansion->@*];
504             }
505 1         107 my $r = formatTable([@r], [qw(Rule Expandable Expansion)]);
506 1         1896 $r =~ s(\s+\Z) (\n)gs;
507 1 50       34 owf($logFile, $r) if -e $logFile; # Log the result if requested
508 1         19 $r
509             } # printGrammar
510              
511             sub printSymbolAsXml($) #P Print a symbol in a form acceptable as Xml
512 170     170 1 229 {my ($symbol) = @_; # Symbol
513 170 100       456 $symbol =~ m(\A[0-9a-z]+\Z)i ? $symbol : qq("$symbol");
514             }
515              
516             sub printGrammarAsXml($;$) #P Print a B<$grammar> as XML.
517 1     1 1 3 {my ($grammar, $indent) = @_; # Grammar, indentation level
518 1         2 my @r;
519 1   50     7 my $space = q( )x($indent//0); # Indentation
520              
521 1         21 for my $rule($grammar->grammar->@*) # Create an NFA for each rule as a choice of sequences
522 16         267 {my $r = $rule->rule;
523 16         270 my $s = $rule->expandable;
524 16         57 my $S = printSymbolAsXml($s);
525 16         38 push @r, qq(\n$space <$S>); # Rule
526              
527 16         247 for my $e($rule->expansion->@*) # Expansion
528 50         106 {my $E = printSymbolAsXml($e);
529 50         130 push @r, qq(<$E/>);
530             }
531 16         31 push @r, qq();
532             }
533              
534 1         12 my $r = join "", qq($space), @r, qq(\n$space), "\n"; # Result
535 1         20 $r =~ s(\s+\Z) (\n)gs;
536 1 50       24 owf($logFile, $r) if -e $logFile; # Log the result if requested
537 1         11 $r
538             } # printGrammarAsXml
539              
540             sub printParseTree($$;$) # Print a parse tree.
541 8     8 1 21 {my ($grammar, $tree, $indent) = @_; # Grammar, parse tree, optional indent level
542 8         9 my @r;
543 8         137 my @rules = $grammar->rules->@*;
544              
545 8         55 my $print; $print = sub # Print sub tree
546 145     145   207 {my ($stack, $depth) = @_;
547              
548 145 100       213 if (ref($stack))
549 84 50       170 {if (defined(my ($r) = @$stack))
550 84         96 {my ($rule) = $rules[$r];
551 84         127 my (undef, @exp) = @$stack;
552 84         1397 push @r, [$rule->rule, (q( )x$depth).$rule->expandable];
553 84         1799 for my $s(@exp)
554 137         321 {$print->($s, $depth+1);
555             }
556             }
557             }
558             else
559 61         190 {push @r, [q(), q(), $stack];
560             }
561 8         62 };
562              
563 8 50       21 return q() unless $tree; # Empty tree
564              
565 8   50     39 $print->($tree, $indent//0);
566              
567 8         55 my $r = formatTable([@r], [qw(Rule Expandable Terminal)]);
568 8         11688 $r =~ s(\s+\Z) (\n)gs;
569 8 50       139 owf($logFile, $r) if -e $logFile; # Log the result if requested
570 8         65 $r
571             } # printParseTree
572              
573             sub printParseTreeAsBrackets($$;$) # Print a parse tree as XML.
574 5     5 1 15 {my ($grammar, $tree, $indent) = @_; # Grammar, parse tree, optional indent level
575 5         8 my @r;
576 5         79 my @rules = $grammar->rules->@*;
577              
578 5         28 my $print; $print = sub # Print sub tree
579 52     52   75 {my ($stack, $depth) = @_;
580 52         78 my $s = q( ) x $depth;
581              
582 52 100       78 if (ref($stack))
583 27 50       57 {if (defined(my ($r) = @$stack))
584 27         34 {my ($rule) = $rules[$r];
585 27         42 my (undef, @exp) = @$stack;
586 27         438 my $n = $rule->rule;
587 27         464 my $e = $rule->expandable;
588 27         104 my $E = printSymbolAsXml($e);
589 27         62 push @r, qq(\n$s$E);
590 27         38 for my $s(@exp)
591 47         112 {$print->($s, $depth+1);
592             }
593 27         65 push @r, qq(\n$s$E);
594             }
595             }
596             else
597 25         35 {my $t = printSymbolAsXml($stack);
598 25         53 push @r, $t;
599             }
600 5         38 };
601              
602 5 50       13 return q() unless $tree; # Empty tree
603              
604 5   50     25 $print->($tree, $indent//0);
605              
606 5         19 my $r = join ' ', @r, "\n";
607 5         59 $r =~ s( +\n) (\n)gs;
608 5         31 $r =~ s(\s+\Z) (\n)gs;
609 5         24 $r =~ s(\A\s+) ()gs;
610 5 50       84 owf($logFile, $r) if -e $logFile; # Log the result if requested
611 5         46 $r
612             } # printParseTreeAsBrackets
613              
614             sub printParseTreeAsXml($$;$) # Print a parse tree as XML.
615 2     2 1 6 {my ($grammar, $tree, $indent) = @_; # Grammar, parse tree, optional indent level
616 2         4 my @r;
617 2         41 my @rules = $grammar->rules->@*;
618 2         12 my $terminal = 0;
619              
620 2         5 my $print; $print = sub # Print sub tree
621 52     52   76 {my ($stack, $depth) = @_;
622 52         73 my $s = q( ) x $depth;
623              
624 52 100       81 if (ref($stack))
625 27 50       54 {if (defined(my ($r) = @$stack))
626 27         35 {my ($rule) = $rules[$r];
627 27         43 my (undef, @exp) = @$stack;
628 27         479 my $n = $rule->rule;
629 27         484 my $e = $rule->expandable;
630 27         103 my $E = printSymbolAsXml($e);
631 27         74 push @r, qq($s<$E rule="$n">);
632 27         38 for my $s(@exp)
633 50         107 {$print->($s, $depth+1);
634             }
635 27         69 push @r, qq($s);
636             }
637             }
638             else
639 25         28 {my $t = printSymbolAsXml($stack);
640 25         56 push @r, qq($s<$t pos="$terminal"/>);
641 25         43 ++$terminal;
642             }
643 2         15 };
644              
645 2 50       7 return q() unless $tree; # Empty tree
646              
647 2   50     14 $print->($tree, $indent//0);
648              
649 2         37 my $r = join "\n", @r, '';
650 2         36 $r =~ s(\s+\Z) (\n)gs;
651 2 50       30 owf($logFile, $r) if -e $logFile; # Log the result if requested
652 2         17 $r
653             } # printParseTreeAsXml
654              
655             sub printParseTreeAndGrammarAsXml($$) #P Print a parse tree produced from a grammar by L as XML.
656 0     0 1 0 {my ($tree, $grammar) = @_; # Parse tree, grammar
657 0         0 my @r;
658              
659 0         0 push @r, q(), q( );
660 0         0 push @r, printParseTreeAsXml($tree, 2).q( );
661 0         0 push @r, printGrammarAsXml ($grammar, 1).q();
662 0         0 my $r = join "\n", @r, '';
663 0         0 $r =~ s(\s+\Z) (\n)gs;
664 0 0       0 owf($logFile, $r) if -e $logFile; # Log the result if requested
665 0         0 $r
666             }
667              
668             #D0
669             #-------------------------------------------------------------------------------
670             # Export - eeee
671             #-------------------------------------------------------------------------------
672              
673 1     1   10 use Exporter qw(import);
  1         3  
  1         31  
674              
675 1     1   11 use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         4  
  1         419  
676              
677             @ISA = qw(Exporter);
678             @EXPORT = qw();
679             @EXPORT_OK = qw();
680              
681             %EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
682              
683             #D
684             # podDocumentation
685              
686             =pod
687              
688             =encoding utf-8
689              
690             =head1 Name
691              
692             Parser::LR - Create and use an LR(1) parser.
693              
694             =head1 Synopsis
695              
696             Create an LR grammar from rules expressed one rule per line of a string. Each
697             rule starts with an expandable symbol followed by one possible expansion as a
698             mixture of expandable and terminal symbols:
699              
700             my $grammar = compileGrammar(<
701             A A + B
702             A B
703             B B * C
704             B C
705             C n
706             C D C
707             D ++
708             D --
709             C C E
710             E **
711             E //
712              
713             C ( A )
714             C [ A ]
715             C { A }
716             C ( )
717             C [ ]
718             C { }
719              
720             C D n
721             END
722              
723             Use the grammar so created to parse a string an array of terminal symbols
724             into a parse tree with L:
725              
726             my $tree = parseWithGrammar($grammar, qw{n * ( ++ -- n ** // + -- ++ n // ** )});
727              
728             Print the parse tree tree, perhaps with L or L:
729              
730             ok printParseTreeAsBrackets($grammar, $tree) eq <
731             A
732             B
733             B
734             C n
735             C
736             B "*"
737             C "("
738             A
739             A
740             B
741             C "++"
742             C
743             C
744             C "--" n
745             C "**"
746             C "//"
747             C
748             C
749             B
750             A "+"
751             B
752             C "--"
753             C
754             C
755             C "++" n
756             C "//"
757             C "**"
758             C
759             C
760             B
761             A ")"
762             C
763             B
764             A
765             END
766              
767             =head1 Description
768              
769             Create and use an LR(1) parser.
770              
771              
772             Version 20191121.
773              
774              
775             The following sections describe the methods in each functional area of this
776             module. For an alphabetic listing of all methods by name see L.
777              
778              
779              
780             =head1 Create and use an LR(1) parser.
781              
782             Construct an LR(1) parser from a set of rules using L; use the parser produced to parse sequences of terminal symbols using L; print the resulting parse tree with L or L.
783              
784             =head2 compileGrammar($%)
785              
786             Compile a grammar from a set of rules expressed as a string with one rule per line. Returns a L.
787              
788             Parameter Description
789             1 $rules Rule definitions
790             2 %options Options
791              
792             B
793              
794              
795             if (1) {
796             my $grammar = 𝗰𝗼𝗺𝗽𝗶𝗹𝗲𝗚𝗿𝗮𝗺𝗺𝗮𝗿(<
797             A A + B
798             A B
799             B B * C
800             B C
801             C n
802             C ( A )
803             C [ A ]
804             C { A }
805             C ( )
806             C [ ]
807             C { }
808             END
809              
810             ok printGrammar($grammar) eq <
811             Rule Expandable Expansion
812             1 0 A A + B
813             2 1 A B
814             3 2 B B * n
815             4 3 B B * ( A )
816             5 4 B B * [ A ]
817             6 5 B B * { A }
818             7 6 B B * ( )
819             8 7 B B * [ ]
820             9 8 B B * { }
821             10 9 B n
822             11 10 B ( A )
823             12 11 B [ A ]
824             13 12 B { A }
825             14 13 B ( )
826             15 14 B [ ]
827             16 15 B { }
828             END
829              
830             my $tree = parseWithGrammar($grammar, qw/( [ { } ] + [ { n } ] ) * [ n + n ] /);
831              
832             ok printParseTree($grammar, $tree) eq <
833             Rule Expandable Terminal
834             1 1 A
835             2 4 B
836             3 10 B
837             4 (
838             5 0 A
839             6 1 A
840             7 11 B
841             8 [
842             9 1 A
843             10 15 B
844             11 {
845             12 }
846             13 ]
847             14 +
848             15 11 B
849             16 [
850             17 1 A
851             18 12 B
852             19 {
853             20 1 A
854             21 9 B
855             22 n
856             23 }
857             24 ]
858             25 )
859             26 *
860             27 [
861             28 0 A
862             29 1 A
863             30 9 B
864             31 n
865             32 +
866             33 9 B
867             34 n
868             35 ]
869             END
870              
871             ok printParseTreeAsXml($grammar, $tree) eq <
872            
873            
874            
875             <"(" pos="0"/>
876            
877            
878            
879             <"[" pos="1"/>
880            
881            
882             <"{" pos="2"/>
883             <"}" pos="3"/>
884            
885            
886             <"]" pos="4"/>
887            
888            
889             <"+" pos="5"/>
890            
891             <"[" pos="6"/>
892            
893            
894             <"{" pos="7"/>
895            
896            
897            
898            
899            
900             <"}" pos="9"/>
901            
902            
903             <"]" pos="10"/>
904            
905            
906             <")" pos="11"/>
907            
908             <"*" pos="12"/>
909             <"[" pos="13"/>
910            
911            
912            
913            
914            
915            
916             <"+" pos="15"/>
917            
918            
919            
920            
921             <"]" pos="17"/>
922            
923            
924             END
925              
926             ok printGrammarAsXml($grammar) eq <
927            
928             <"+"/>
929            
930             <"*"/>
931             <"*"/><"("/><")"/>
932             <"*"/><"["/><"]"/>
933             <"*"/><"{"/><"}"/>
934             <"*"/><"("/><")"/>
935             <"*"/><"["/><"]"/>
936             <"*"/><"{"/><"}"/>
937            
938             <"("/><")"/>
939             <"["/><"]"/>
940             <"{"/><"}"/>
941             <"("/><")"/>
942             <"["/><"]"/>
943             <"{"/><"}"/>
944            
945             END
946             }
947              
948              
949             =head2 parseWithGrammar($@)
950              
951             Parse, using a compiled B<$grammar>, an array of terminals and return a parse tree.
952              
953             Parameter Description
954             1 $grammar Compiled grammar
955             2 @terminals Terminals to parse
956              
957             B
958              
959              
960             if (1) {
961             my $grammar = compileGrammar(<
962             A A + B
963             A B
964             B B * C
965             B C
966             C n
967             C ( A )
968             C [ A ]
969             C { A }
970             C ( )
971             C [ ]
972             C { }
973             END
974              
975             ok printGrammar($grammar) eq <
976             Rule Expandable Expansion
977             1 0 A A + B
978             2 1 A B
979             3 2 B B * n
980             4 3 B B * ( A )
981             5 4 B B * [ A ]
982             6 5 B B * { A }
983             7 6 B B * ( )
984             8 7 B B * [ ]
985             9 8 B B * { }
986             10 9 B n
987             11 10 B ( A )
988             12 11 B [ A ]
989             13 12 B { A }
990             14 13 B ( )
991             15 14 B [ ]
992             16 15 B { }
993             END
994              
995             my $tree = 𝗽𝗮𝗿𝘀𝗲𝗪𝗶𝘁𝗵𝗚𝗿𝗮𝗺𝗺𝗮𝗿($grammar, qw/( [ { } ] + [ { n } ] ) * [ n + n ] /);
996              
997             ok printParseTree($grammar, $tree) eq <
998             Rule Expandable Terminal
999             1 1 A
1000             2 4 B
1001             3 10 B
1002             4 (
1003             5 0 A
1004             6 1 A
1005             7 11 B
1006             8 [
1007             9 1 A
1008             10 15 B
1009             11 {
1010             12 }
1011             13 ]
1012             14 +
1013             15 11 B
1014             16 [
1015             17 1 A
1016             18 12 B
1017             19 {
1018             20 1 A
1019             21 9 B
1020             22 n
1021             23 }
1022             24 ]
1023             25 )
1024             26 *
1025             27 [
1026             28 0 A
1027             29 1 A
1028             30 9 B
1029             31 n
1030             32 +
1031             33 9 B
1032             34 n
1033             35 ]
1034             END
1035              
1036             ok printParseTreeAsXml($grammar, $tree) eq <
1037            
1038            
1039            
1040             <"(" pos="0"/>
1041            
1042            
1043            
1044             <"[" pos="1"/>
1045            
1046            
1047             <"{" pos="2"/>
1048             <"}" pos="3"/>
1049            
1050            
1051             <"]" pos="4"/>
1052            
1053            
1054             <"+" pos="5"/>
1055            
1056             <"[" pos="6"/>
1057            
1058            
1059             <"{" pos="7"/>
1060            
1061            
1062            
1063            
1064            
1065             <"}" pos="9"/>
1066            
1067            
1068             <"]" pos="10"/>
1069            
1070            
1071             <")" pos="11"/>
1072            
1073             <"*" pos="12"/>
1074             <"[" pos="13"/>
1075            
1076            
1077            
1078            
1079            
1080            
1081             <"+" pos="15"/>
1082            
1083            
1084            
1085            
1086             <"]" pos="17"/>
1087            
1088            
1089             END
1090              
1091             ok printGrammarAsXml($grammar) eq <
1092            
1093             <"+"/>
1094            
1095             <"*"/>
1096             <"*"/><"("/><")"/>
1097             <"*"/><"["/><"]"/>
1098             <"*"/><"{"/><"}"/>
1099             <"*"/><"("/><")"/>
1100             <"*"/><"["/><"]"/>
1101             <"*"/><"{"/><"}"/>
1102            
1103             <"("/><")"/>
1104             <"["/><"]"/>
1105             <"{"/><"}"/>
1106             <"("/><")"/>
1107             <"["/><"]"/>
1108             <"{"/><"}"/>
1109            
1110             END
1111             }
1112              
1113              
1114             =head2 printGrammar($)
1115              
1116             Print a B<$grammar>.
1117              
1118             Parameter Description
1119             1 $grammar Grammar
1120              
1121             B
1122              
1123              
1124             if (1) {
1125             my $grammar = compileGrammar(<
1126             A A + B
1127             A B
1128             B B * C
1129             B C
1130             C n
1131             C ( A )
1132             C [ A ]
1133             C { A }
1134             C ( )
1135             C [ ]
1136             C { }
1137             END
1138              
1139             ok 𝗽𝗿𝗶𝗻𝘁𝗚𝗿𝗮𝗺𝗺𝗮𝗿($grammar) eq <
1140             Rule Expandable Expansion
1141             1 0 A A + B
1142             2 1 A B
1143             3 2 B B * n
1144             4 3 B B * ( A )
1145             5 4 B B * [ A ]
1146             6 5 B B * { A }
1147             7 6 B B * ( )
1148             8 7 B B * [ ]
1149             9 8 B B * { }
1150             10 9 B n
1151             11 10 B ( A )
1152             12 11 B [ A ]
1153             13 12 B { A }
1154             14 13 B ( )
1155             15 14 B [ ]
1156             16 15 B { }
1157             END
1158              
1159             my $tree = parseWithGrammar($grammar, qw/( [ { } ] + [ { n } ] ) * [ n + n ] /);
1160              
1161             ok printParseTree($grammar, $tree) eq <
1162             Rule Expandable Terminal
1163             1 1 A
1164             2 4 B
1165             3 10 B
1166             4 (
1167             5 0 A
1168             6 1 A
1169             7 11 B
1170             8 [
1171             9 1 A
1172             10 15 B
1173             11 {
1174             12 }
1175             13 ]
1176             14 +
1177             15 11 B
1178             16 [
1179             17 1 A
1180             18 12 B
1181             19 {
1182             20 1 A
1183             21 9 B
1184             22 n
1185             23 }
1186             24 ]
1187             25 )
1188             26 *
1189             27 [
1190             28 0 A
1191             29 1 A
1192             30 9 B
1193             31 n
1194             32 +
1195             33 9 B
1196             34 n
1197             35 ]
1198             END
1199              
1200             ok printParseTreeAsXml($grammar, $tree) eq <
1201            
1202            
1203            
1204             <"(" pos="0"/>
1205            
1206            
1207            
1208             <"[" pos="1"/>
1209            
1210            
1211             <"{" pos="2"/>
1212             <"}" pos="3"/>
1213            
1214            
1215             <"]" pos="4"/>
1216            
1217            
1218             <"+" pos="5"/>
1219            
1220             <"[" pos="6"/>
1221            
1222            
1223             <"{" pos="7"/>
1224            
1225            
1226            
1227            
1228            
1229             <"}" pos="9"/>
1230            
1231            
1232             <"]" pos="10"/>
1233            
1234            
1235             <")" pos="11"/>
1236            
1237             <"*" pos="12"/>
1238             <"[" pos="13"/>
1239            
1240            
1241            
1242            
1243            
1244            
1245             <"+" pos="15"/>
1246            
1247            
1248            
1249            
1250             <"]" pos="17"/>
1251            
1252            
1253             END
1254              
1255             ok printGrammarAsXml($grammar) eq <
1256            
1257             <"+"/>
1258            
1259             <"*"/>
1260             <"*"/><"("/><")"/>
1261             <"*"/><"["/><"]"/>
1262             <"*"/><"{"/><"}"/>
1263             <"*"/><"("/><")"/>
1264             <"*"/><"["/><"]"/>
1265             <"*"/><"{"/><"}"/>
1266            
1267             <"("/><")"/>
1268             <"["/><"]"/>
1269             <"{"/><"}"/>
1270             <"("/><")"/>
1271             <"["/><"]"/>
1272             <"{"/><"}"/>
1273            
1274             END
1275             }
1276              
1277              
1278             =head2 printParseTree($$$)
1279              
1280             Print a parse tree.
1281              
1282             Parameter Description
1283             1 $grammar Grammar
1284             2 $tree Parse tree
1285             3 $indent Optional indent level
1286              
1287             B
1288              
1289              
1290             if (1) {
1291             my $grammar = compileGrammar(<
1292             A A + B
1293             A B
1294             B B * C
1295             B C
1296             C n
1297             C ( A )
1298             C [ A ]
1299             C { A }
1300             C ( )
1301             C [ ]
1302             C { }
1303             END
1304              
1305             ok printGrammar($grammar) eq <
1306             Rule Expandable Expansion
1307             1 0 A A + B
1308             2 1 A B
1309             3 2 B B * n
1310             4 3 B B * ( A )
1311             5 4 B B * [ A ]
1312             6 5 B B * { A }
1313             7 6 B B * ( )
1314             8 7 B B * [ ]
1315             9 8 B B * { }
1316             10 9 B n
1317             11 10 B ( A )
1318             12 11 B [ A ]
1319             13 12 B { A }
1320             14 13 B ( )
1321             15 14 B [ ]
1322             16 15 B { }
1323             END
1324              
1325             my $tree = parseWithGrammar($grammar, qw/( [ { } ] + [ { n } ] ) * [ n + n ] /);
1326              
1327             ok 𝗽𝗿𝗶𝗻𝘁𝗣𝗮𝗿𝘀𝗲𝗧𝗿𝗲𝗲($grammar, $tree) eq <
1328             Rule Expandable Terminal
1329             1 1 A
1330             2 4 B
1331             3 10 B
1332             4 (
1333             5 0 A
1334             6 1 A
1335             7 11 B
1336             8 [
1337             9 1 A
1338             10 15 B
1339             11 {
1340             12 }
1341             13 ]
1342             14 +
1343             15 11 B
1344             16 [
1345             17 1 A
1346             18 12 B
1347             19 {
1348             20 1 A
1349             21 9 B
1350             22 n
1351             23 }
1352             24 ]
1353             25 )
1354             26 *
1355             27 [
1356             28 0 A
1357             29 1 A
1358             30 9 B
1359             31 n
1360             32 +
1361             33 9 B
1362             34 n
1363             35 ]
1364             END
1365              
1366             ok printParseTreeAsXml($grammar, $tree) eq <
1367            
1368            
1369            
1370             <"(" pos="0"/>
1371            
1372            
1373            
1374             <"[" pos="1"/>
1375            
1376            
1377             <"{" pos="2"/>
1378             <"}" pos="3"/>
1379            
1380            
1381             <"]" pos="4"/>
1382            
1383            
1384             <"+" pos="5"/>
1385            
1386             <"[" pos="6"/>
1387            
1388            
1389             <"{" pos="7"/>
1390            
1391            
1392            
1393            
1394            
1395             <"}" pos="9"/>
1396            
1397            
1398             <"]" pos="10"/>
1399            
1400            
1401             <")" pos="11"/>
1402            
1403             <"*" pos="12"/>
1404             <"[" pos="13"/>
1405            
1406            
1407            
1408            
1409            
1410            
1411             <"+" pos="15"/>
1412            
1413            
1414            
1415            
1416             <"]" pos="17"/>
1417            
1418            
1419             END
1420              
1421             ok printGrammarAsXml($grammar) eq <
1422            
1423             <"+"/>
1424            
1425             <"*"/>
1426             <"*"/><"("/><")"/>
1427             <"*"/><"["/><"]"/>
1428             <"*"/><"{"/><"}"/>
1429             <"*"/><"("/><")"/>
1430             <"*"/><"["/><"]"/>
1431             <"*"/><"{"/><"}"/>
1432            
1433             <"("/><")"/>
1434             <"["/><"]"/>
1435             <"{"/><"}"/>
1436             <"("/><")"/>
1437             <"["/><"]"/>
1438             <"{"/><"}"/>
1439            
1440             END
1441             }
1442              
1443              
1444             =head2 printParseTreeAsBrackets($$$)
1445              
1446             Print a parse tree as XML.
1447              
1448             Parameter Description
1449             1 $grammar Grammar
1450             2 $tree Parse tree
1451             3 $indent Optional indent level
1452              
1453             B
1454              
1455              
1456             if (1) {
1457             my $grammar = compileGrammar(<
1458             A A + B
1459             A B
1460             B B * C
1461             B C
1462             C n
1463             C D C
1464             D ++
1465             D --
1466             C C E
1467             E **
1468             E //
1469              
1470             C ( A )
1471             C [ A ]
1472             C { A }
1473             C ( )
1474             C [ ]
1475             C { }
1476              
1477             C D n
1478             END
1479              
1480             my $tree = parseWithGrammar($grammar, qw{n * ( ++ -- n ** // + -- ++ n // ** )});
1481              
1482             ok 𝗽𝗿𝗶𝗻𝘁𝗣𝗮𝗿𝘀𝗲𝗧𝗿𝗲𝗲𝗔𝘀𝗕𝗿𝗮𝗰𝗸𝗲𝘁𝘀($grammar, $tree) eq <
1483             A
1484             B
1485             B
1486             C n
1487             C
1488             B "*"
1489             C "("
1490             A
1491             A
1492             B
1493             C "++"
1494             C
1495             C
1496             C "--" n
1497             C "**"
1498             C "//"
1499             C
1500             C
1501             B
1502             A "+"
1503             B
1504             C "--"
1505             C
1506             C
1507             C "++" n
1508             C "//"
1509             C "**"
1510             C
1511             C
1512             B
1513             A ")"
1514             C
1515             B
1516             A
1517             END
1518             }
1519              
1520              
1521             =head2 printParseTreeAsXml($$$)
1522              
1523             Print a parse tree as XML.
1524              
1525             Parameter Description
1526             1 $grammar Grammar
1527             2 $tree Parse tree
1528             3 $indent Optional indent level
1529              
1530             B
1531              
1532              
1533             if (1) {
1534             my $grammar = compileGrammar(<
1535             A A + B
1536             A B
1537             B B * C
1538             B C
1539             C n
1540             C ( A )
1541             C [ A ]
1542             C { A }
1543             C ( )
1544             C [ ]
1545             C { }
1546             END
1547              
1548             ok printGrammar($grammar) eq <
1549             Rule Expandable Expansion
1550             1 0 A A + B
1551             2 1 A B
1552             3 2 B B * n
1553             4 3 B B * ( A )
1554             5 4 B B * [ A ]
1555             6 5 B B * { A }
1556             7 6 B B * ( )
1557             8 7 B B * [ ]
1558             9 8 B B * { }
1559             10 9 B n
1560             11 10 B ( A )
1561             12 11 B [ A ]
1562             13 12 B { A }
1563             14 13 B ( )
1564             15 14 B [ ]
1565             16 15 B { }
1566             END
1567              
1568             my $tree = parseWithGrammar($grammar, qw/( [ { } ] + [ { n } ] ) * [ n + n ] /);
1569              
1570             ok printParseTree($grammar, $tree) eq <
1571             Rule Expandable Terminal
1572             1 1 A
1573             2 4 B
1574             3 10 B
1575             4 (
1576             5 0 A
1577             6 1 A
1578             7 11 B
1579             8 [
1580             9 1 A
1581             10 15 B
1582             11 {
1583             12 }
1584             13 ]
1585             14 +
1586             15 11 B
1587             16 [
1588             17 1 A
1589             18 12 B
1590             19 {
1591             20 1 A
1592             21 9 B
1593             22 n
1594             23 }
1595             24 ]
1596             25 )
1597             26 *
1598             27 [
1599             28 0 A
1600             29 1 A
1601             30 9 B
1602             31 n
1603             32 +
1604             33 9 B
1605             34 n
1606             35 ]
1607             END
1608              
1609             ok 𝗽𝗿𝗶𝗻𝘁𝗣𝗮𝗿𝘀𝗲𝗧𝗿𝗲𝗲𝗔𝘀𝗫𝗺𝗹($grammar, $tree) eq <
1610            
1611            
1612            
1613             <"(" pos="0"/>
1614            
1615            
1616            
1617             <"[" pos="1"/>
1618            
1619            
1620             <"{" pos="2"/>
1621             <"}" pos="3"/>
1622            
1623            
1624             <"]" pos="4"/>
1625            
1626            
1627             <"+" pos="5"/>
1628            
1629             <"[" pos="6"/>
1630            
1631            
1632             <"{" pos="7"/>
1633            
1634            
1635            
1636            
1637            
1638             <"}" pos="9"/>
1639            
1640            
1641             <"]" pos="10"/>
1642            
1643            
1644             <")" pos="11"/>
1645            
1646             <"*" pos="12"/>
1647             <"[" pos="13"/>
1648            
1649            
1650            
1651            
1652            
1653            
1654             <"+" pos="15"/>
1655            
1656            
1657            
1658            
1659             <"]" pos="17"/>
1660            
1661            
1662             END
1663              
1664             ok printGrammarAsXml($grammar) eq <
1665            
1666             <"+"/>
1667            
1668             <"*"/>
1669             <"*"/><"("/><")"/>
1670             <"*"/><"["/><"]"/>
1671             <"*"/><"{"/><"}"/>
1672             <"*"/><"("/><")"/>
1673             <"*"/><"["/><"]"/>
1674             <"*"/><"{"/><"}"/>
1675            
1676             <"("/><")"/>
1677             <"["/><"]"/>
1678             <"{"/><"}"/>
1679             <"("/><")"/>
1680             <"["/><"]"/>
1681             <"{"/><"}"/>
1682            
1683             END
1684             }
1685              
1686              
1687              
1688             =head2 Parser::LR::Grammar Definition
1689              
1690              
1691             LR parser produced
1692              
1693              
1694              
1695              
1696             =head3 Output fields
1697              
1698              
1699             B - DFA from grammar
1700              
1701             B - Expandable symbols
1702              
1703             B - States we can expand in
1704              
1705             B - Final state at end of parse
1706              
1707             B - Grammar from which the NFA was derived
1708              
1709             B - Longest rule
1710              
1711             B - NFA from grammar
1712              
1713             B - Expandables that can expand to nothing
1714              
1715             B - The expandables an expandable can reduce to
1716              
1717             B - Rules
1718              
1719             B - Start symbol
1720              
1721             B - Terminals that start rules
1722              
1723             B - Terminal symbols
1724              
1725              
1726              
1727             =head2 Parser::LR::Rule Definition
1728              
1729              
1730             A parsing rule
1731              
1732              
1733              
1734              
1735             =head3 Output fields
1736              
1737              
1738             B - Symbol to expand
1739              
1740             B - Symbol expansion
1741              
1742             B - Rule printer
1743              
1744             B - Rule number
1745              
1746              
1747              
1748             =head1 Private Methods
1749              
1750             =head2 printRule($)
1751              
1752             Print a rule
1753              
1754             Parameter Description
1755             1 $rule Rule
1756              
1757             =head2 longestMatchingRule($@)
1758              
1759             Find the longest rule that completely matches the top of the stack.
1760              
1761             Parameter Description
1762             1 $grammar Grammar
1763             2 @stack Stack
1764              
1765             =head2 partialMatch($@)
1766              
1767             Check whether we have a partial match with the top of the stack.
1768              
1769             Parameter Description
1770             1 $grammar Grammar
1771             2 @stack Stack
1772              
1773             =head2 reduceStackWithRule($$$)
1774              
1775             Reduce by the specified rule and update the stack and parse tree to match.
1776              
1777             Parameter Description
1778             1 $rule Rule
1779             2 $stack Stack
1780             3 $tree Parse tree
1781              
1782             =head2 parseWithGrammarAndLog($@)
1783              
1784             Parse, using a compiled B<$grammar>, an array of terminals and return a log of parsing actions taken.
1785              
1786             Parameter Description
1787             1 $grammar Compiled grammar
1788             2 @terminals Terminals to parse
1789              
1790             =head2 printSymbolAsXml($)
1791              
1792             Print a symbol in a form acceptable as Xml
1793              
1794             Parameter Description
1795             1 $symbol Symbol
1796              
1797             =head2 printGrammarAsXml($$)
1798              
1799             Print a B<$grammar> as XML.
1800              
1801             Parameter Description
1802             1 $grammar Grammar
1803             2 $indent Indentation level
1804              
1805             =head2 printParseTreeAndGrammarAsXml($$)
1806              
1807             Print a parse tree produced from a grammar by L as XML.
1808              
1809             Parameter Description
1810             1 $tree Parse tree
1811             2 $grammar Grammar
1812              
1813              
1814             =head1 Index
1815              
1816              
1817             1 L - Compile a grammar from a set of rules expressed as a string with one rule per line.
1818              
1819             2 L - Find the longest rule that completely matches the top of the stack.
1820              
1821             3 L - Parse, using a compiled B<$grammar>, an array of terminals and return a parse tree.
1822              
1823             4 L - Parse, using a compiled B<$grammar>, an array of terminals and return a log of parsing actions taken.
1824              
1825             5 L - Check whether we have a partial match with the top of the stack.
1826              
1827             6 L - Print a B<$grammar>.
1828              
1829             7 L - Print a B<$grammar> as XML.
1830              
1831             8 L - Print a parse tree.
1832              
1833             9 L - Print a parse tree produced from a grammar by L as XML.
1834              
1835             10 L - Print a parse tree as XML.
1836              
1837             11 L - Print a parse tree as XML.
1838              
1839             12 L - Print a rule
1840              
1841             13 L - Print a symbol in a form acceptable as Xml
1842              
1843             14 L - Reduce by the specified rule and update the stack and parse tree to match.
1844              
1845             =head1 Installation
1846              
1847             This module is written in 100% Pure Perl and, thus, it is easy to read,
1848             comprehend, use, modify and install via B:
1849              
1850             sudo cpan install Parser::LR
1851              
1852             =head1 Author
1853              
1854             L
1855              
1856             L
1857              
1858             =head1 Copyright
1859              
1860             Copyright (c) 2016-2019 Philip R Brenan.
1861              
1862             This module is free software. It may be used, redistributed and/or modified
1863             under the same terms as Perl itself.
1864              
1865             =cut
1866              
1867              
1868              
1869             # Tests and documentation
1870              
1871             sub test
1872 1     1 0 8 {my $p = __PACKAGE__;
1873 1         7 binmode($_, ":utf8") for *STDOUT, *STDERR;
1874 1 50       51 return if eval "eof(${p}::DATA)";
1875 1         38 my $s = eval "join('', <${p}::DATA>)";
1876 1 50       50 $@ and die $@;
1877 1     1   646 eval $s;
  1         64250  
  1         8  
  1         69  
1878 1 50       681 $@ and die $@;
1879 1         141 1
1880             }
1881              
1882             test unless caller;
1883              
1884             1;
1885             #podDocumentation
1886             __DATA__