File Coverage

blib/lib/MarpaX/Grammar/Parser.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package MarpaX::Grammar::Parser;
2              
3 1     1   40137 use strict;
  1         3  
  1         42  
4 1     1   881 use utf8;
  1         9  
  1         5  
5 1     1   31 use warnings;
  1         6  
  1         39  
6 1     1   5 use warnings qw(FATAL utf8); # Fatalize encoding glitches.
  1         2  
  1         46  
7 1     1   782 use open qw(:std :utf8); # Undeclared streams in UTF-8.
  1         1137  
  1         5  
8 1     1   1720 use charnames qw(:full :short); # Unneeded in v5.16.
  1         40476  
  1         7  
9              
10 1     1   1897 use Data::TreeDumper (); # For DumpTree().
  0            
  0            
11             use Data::TreeDumper::Renderer::Marpa; # Used by DumpTree().
12              
13             use Log::Handler;
14              
15             use Marpa::R2;
16              
17             use Moo;
18              
19             use Perl6::Slurp; # For slurp().
20              
21             use Tree::DAG_Node;
22              
23             has bind_attributes =>
24             (
25             default => sub{return 0},
26             is => 'rw',
27             #isa => 'Bool',
28             required => 0,
29             );
30              
31             has cooked_tree =>
32             (
33             default => sub{return ''},
34             is => 'rw',
35             #isa => 'Tree::DAG_Node',
36             required => 0,
37             );
38              
39             has cooked_tree_file =>
40             (
41             default => sub{return ''},
42             is => 'rw',
43             #isa => 'Str',
44             required => 0,
45             );
46              
47             has logger =>
48             (
49             default => sub{return undef},
50             is => 'rw',
51             # isa => 'Str',
52             required => 0,
53             );
54              
55             has marpa_bnf_file =>
56             (
57             default => sub{return ''},
58             is => 'rw',
59             #isa => 'Str',
60             required => 0,
61             );
62              
63             has maxlevel =>
64             (
65             default => sub{return 'info'},
66             is => 'rw',
67             # isa => 'Str',
68             required => 0,
69             );
70              
71             has minlevel =>
72             (
73             default => sub{return 'error'},
74             is => 'rw',
75             # isa => 'Str',
76             required => 0,
77             );
78              
79             has raw_tree =>
80             (
81             default => sub{return ''},
82             is => 'rw',
83             #isa => 'Tree::DAG_Node',
84             required => 0,
85             );
86              
87             has raw_tree_file =>
88             (
89             default => sub{return ''},
90             is => 'rw',
91             #isa => 'Str',
92             required => 0,
93             );
94              
95             has user_bnf_file =>
96             (
97             default => sub{return ''},
98             is => 'rw',
99             #isa => 'Str',
100             required => 0,
101             );
102              
103             our $VERSION = '1.01';
104              
105             # ------------------------------------------------
106              
107             sub BUILD
108             {
109             my($self) = @_;
110              
111             die "No Marpa SLIF-DSL file found\n" if (! -e $self -> marpa_bnf_file);
112             die "No user SLIF-DSL file found\n" if (! -e $self -> user_bnf_file);
113              
114             if (! defined $self -> logger)
115             {
116             $self -> logger(Log::Handler -> new);
117             $self -> logger -> add
118             (
119             screen =>
120             {
121             maxlevel => $self -> maxlevel,
122             message_layout => '%m',
123             minlevel => $self -> minlevel,
124             }
125             );
126             }
127              
128             $self -> cooked_tree
129             (
130             Tree::DAG_Node -> new
131             ({
132             name => 'statements',
133             })
134             );
135              
136             $self -> raw_tree
137             (
138             Tree::DAG_Node -> new
139             ({
140             attributes => {type => 'Marpa'},
141             name => 'statements',
142             })
143             );
144              
145             } # End of BUILD.
146              
147             # --------------------------------------------------
148              
149             sub clean_name
150             {
151             my($self, $name) = @_;
152             my($attributes) = {bracketed_name => 0, quantifier => '', real_name => $name};
153              
154             # Expected cases:
155             # o {bare_name => $name}.
156             # o {bracketed_name => $name}.
157             # o $name.
158             #
159             # Quantified names are handled in sub compress_branch.
160              
161             if (ref $name eq 'HASH')
162             {
163             if (defined $$name{bare_name})
164             {
165             $$attributes{real_name} = $name = $$name{bare_name};
166             }
167             else
168             {
169             $$attributes{real_name} = $name = $$name{bracketed_name};
170             $$attributes{bracketed_name} = 1;
171             $name =~ s/^
172             $name =~ s/>$//;
173             }
174             }
175              
176             return ($name, $attributes);
177              
178             } # End of clean_name.
179              
180             # --------------------------------------------------
181              
182             sub compress_branch
183             {
184             my($self, $index, $a_node) = @_;
185              
186             my($name);
187             my($token);
188              
189             $a_node -> walk_down
190             ({
191             callback => sub
192             {
193             my($node, $option) = @_;
194             $name = $node -> name;
195              
196             if ($name eq 'default_rule')
197             {
198             $token = $self -> _process_default_rule($index, $node);
199             }
200             elsif ($name eq 'discard_rule')
201             {
202             $token = $self -> _process_discard_rule($index, $node);
203             }
204             elsif ($name eq 'empty_rule')
205             {
206             $token = $self -> _process_empty_rule($index, $node);
207             }
208             elsif ($name =~ /(.+)_event_declaration$/)
209             {
210             $token = $self -> _process_event_declaration($index, $node, $1);
211             }
212             elsif ($name eq 'lexeme_default_statement')
213             {
214             $token = $self -> _process_lexeme_default($index, $node);
215             }
216             elsif ($name eq 'lexeme_rule')
217             {
218             $token = $self -> _process_lexeme_rule($index, $node);
219             }
220             elsif ($name eq 'priority_rule')
221             {
222             $token = $self -> _process_priority_rule($index, $node);
223             }
224             elsif ($name eq 'quantified_rule')
225             {
226             $token = $self -> _process_quantified_rule($index, $node);
227             }
228             elsif ($name eq 'start_rule')
229             {
230             $token = $self -> _process_start_rule($index, $node);
231             }
232              
233             return 1; # Keep walking.
234             },
235             _depth => 0,
236             });
237              
238             my($attributes);
239              
240             ($name, $attributes) = $self -> clean_name(shift @$token);
241             my($node) = Tree::DAG_Node -> new
242             ({
243             attributes => $attributes,
244             name => $name,
245             });
246              
247             $self -> cooked_tree -> add_daughter($node);
248              
249             for (my $i = 0; $i <= $#$token; $i++)
250             {
251             $name = $$token[$i];
252             ($name, $attributes) = $self -> clean_name($name);
253              
254             # Special case handling: Quantitied rules.
255              
256             if ( ($i < $#$token) && (ref $$token[$i + 1] eq 'HASH') && ($$token[$i + 1]{quantifier}) )
257             {
258             $i++;
259              
260             $$attributes{quantifier} = $$token[$i]{quantifier};
261             }
262              
263             $node -> add_daughter
264             (
265             Tree::DAG_Node -> new
266             ({
267             attributes => $attributes,
268             name => $name,
269             })
270             );
271             }
272              
273             } # End of compress_branch.
274              
275             # --------------------------------------------------
276              
277             sub compress_tree
278             {
279             my($self) = @_;
280              
281             # Phase 1: Process the children of the root:
282             # o First daughter is offset of start within input stream.
283             # o Second daughter is offset of end within input stream.
284             # o Remainder are statements.
285              
286             my(@daughters) = $self -> raw_tree -> daughters;
287             my($start) = (shift @daughters) -> name;
288             my($end) = (shift @daughters) -> name;
289              
290             # Phase 2: Process each statement.
291              
292             for my $index (0 .. $#daughters)
293             {
294             $self -> compress_branch($index + 1, $daughters[$index]);
295             }
296              
297             } # End of compress_tree.
298              
299             # --------------------------------------------------
300              
301             sub log
302             {
303             my($self, $level, $s) = @_;
304              
305             $self -> logger -> log($level => $s) if ($self -> logger);
306              
307             } # End of log.
308              
309             # --------------------------------------------------
310              
311             sub _process_default_rule
312             {
313             my($self, $index, $a_node) = @_;
314             my(%map) =
315             (
316             action => 'action',
317             blessing => 'bless',
318             );
319              
320             my($name);
321             my(@token);
322              
323             $a_node -> walk_down
324             ({
325             callback => sub
326             {
327             my($node, $option) = @_;
328             $name = $node -> name;
329              
330             # Skip the first 2 daughters, which hold offsets for the
331             # start and end of the token within the input stream.
332              
333             return 1 if ($node -> my_daughter_index < 2);
334              
335             if ($node -> mother -> name =~ /op_declare_+/)
336             {
337             push @token, ':default', $name;
338             }
339             elsif ($node -> mother -> mother -> name =~ /(action|blessing)_name/)
340             {
341             push @token, $map{$1}, '=>', $name;
342             }
343              
344             return 1; # Keep walking.
345             },
346             _depth => 0,
347             });
348              
349             return [@token];
350              
351             } # End of _process_default_rule.
352              
353             # --------------------------------------------------
354              
355             sub _process_discard_rule
356             {
357             my($self, $index, $a_node) = @_;
358              
359             my($name);
360             my(@token);
361              
362             $a_node -> walk_down
363             ({
364             callback => sub
365             {
366             my($node, $option) = @_;
367             $name = $node -> name;
368              
369             # Skip the first 2 daughters, which hold offsets for the
370             # start and end of the token within the input stream.
371              
372             return 1 if ($node -> my_daughter_index < 2);
373              
374             if ($node -> mother -> mother -> name eq 'symbol_name')
375             {
376             push @token, ':discard', '=>', {$node -> mother -> name => $name};
377             }
378              
379             return 1; # Keep walking.
380             },
381             _depth => 0,
382             });
383              
384             return [@token];
385              
386             } # End of _process_discard_rule.
387              
388             # --------------------------------------------------
389              
390             sub _process_empty_rule
391             {
392             my($self, $index, $a_node) = @_;
393              
394             my($name);
395             my(@token);
396              
397             $a_node -> walk_down
398             ({
399             callback => sub
400             {
401             my($node, $option) = @_;
402             $name = $node -> name;
403              
404             # Skip the first 2 daughters, which hold offsets for the
405             # start and end of the token within the input stream.
406              
407             return 1 if ($node -> my_daughter_index < 2);
408              
409             if ($node -> mother -> name =~ /op_declare_+/)
410             {
411             push @token, $name;
412             }
413             elsif ($node -> mother -> mother -> name eq 'symbol_name')
414             {
415             push @token, {$node -> mother -> name => $name};
416             }
417              
418             return 1; # Keep walking.
419             },
420             _depth => 0,
421             });
422              
423             return [@token];
424              
425             } # End of _process_empty_rule.
426              
427             # --------------------------------------------------
428              
429             sub _process_event_declaration
430             {
431             my($self, $index, $a_node, $type) = @_;
432             my(%type) =
433             (
434             completion => 'completed',
435             nulled => 'nulled',
436             prediction => 'predicted',
437             );
438              
439             my($name);
440             my(@token);
441              
442             $a_node -> walk_down
443             ({
444             callback => sub
445             {
446             my($node, $option) = @_;
447             $name = $node -> name;
448              
449             # Skip the first 2 daughters, which hold offsets for the
450             # start and end of the token within the input stream.
451              
452             return 1 if ($node -> my_daughter_index < 2);
453              
454             if ($node -> mother -> mother -> name eq 'event_name')
455             {
456             push @token, 'event', $name, '=', $type{$type};
457             }
458             elsif ($node -> mother -> mother -> name eq 'symbol_name')
459             {
460             push @token, {$node -> mother -> name => $name};
461             }
462              
463             return 1; # Keep walking.
464             },
465             _depth => 0,
466             });
467              
468             return [@token];
469              
470             } # End of _process_event_declaration.
471              
472             # --------------------------------------------------
473              
474             sub _process_lexeme_default
475             {
476             my($self, $index, $a_node) = @_;
477             my(%map) =
478             (
479             action => 'action',
480             blessing => 'bless',
481             );
482             my(@token) = ('lexeme default', '=');
483              
484             my($name);
485              
486             $a_node -> walk_down
487             ({
488             callback => sub
489             {
490             my($node, $option) = @_;
491             $name = $node -> name;
492              
493             # Skip the first 2 daughters, which hold offsets for the
494             # start and end of the token within the input stream.
495              
496             return 1 if ($node -> my_daughter_index < 2);
497              
498             if ($node -> mother -> mother -> name =~ /(action|blessing)_name/)
499             {
500             push @token, $map{$1}, '=>', $name;
501             }
502              
503             return 1; # Keep walking.
504             },
505             _depth => 0,
506             });
507              
508             return [@token];
509              
510             } # End of _process_lexeme_default.
511              
512             # --------------------------------------------------
513              
514             sub _process_lexeme_rule
515             {
516             my($self, $index, $a_node) = @_;
517             my(@token) = (':lexeme', '~');
518              
519             my($name);
520              
521             $a_node -> walk_down
522             ({
523             callback => sub
524             {
525             my($node, $option) = @_;
526             $name = $node -> name;
527              
528             # Skip the first 2 daughters, which hold offsets for the
529             # start and end of the token within the input stream.
530              
531             return 1 if ($node -> my_daughter_index < 2);
532              
533             if ($node -> mother -> mother -> name eq 'event_name')
534             {
535             push @token, 'event', '=>', $name;
536             }
537             elsif ($node -> mother -> mother -> name eq 'pause_specification')
538             {
539             push @token, 'pause', '=>', $name;
540             }
541             elsif ($node -> mother -> mother -> name eq 'priority_specification')
542             {
543             push @token, 'priority', '=>', $name;
544             }
545             elsif ($node -> mother -> mother -> name eq 'symbol_name')
546             {
547             push @token, {$node -> mother -> name => $name};
548             }
549              
550             return 1; # Keep walking.
551             },
552             _depth => 0,
553             });
554              
555             return [@token];
556              
557             } # End of _process_lexeme_rule.
558              
559             # --------------------------------------------------
560              
561             sub _process_parenthesized_list
562             {
563             my($self, $index, $a_node, $depth_under) = @_;
564              
565             my($name);
566             my(@rhs);
567              
568             $a_node -> walk_down
569             ({
570             callback => sub
571             {
572             my($node, $option) = @_;
573             $name = $node -> name;
574              
575             # Skip the first 2 daughters, which hold offsets for the
576             # start and end of the token within the input stream.
577              
578             return 1 if ($node -> my_daughter_index < 2);
579              
580             if ($$option{_depth} == $depth_under)
581             {
582             push @rhs, $name;
583             }
584              
585             return 1; # Keep walking.
586             },
587             _depth => 0,
588             });
589              
590             $rhs[0] = "($rhs[0]";
591             $rhs[$#rhs] = "$rhs[$#rhs])";
592              
593             return [@rhs];
594              
595             } # End of _process_parenthesized_list.
596              
597             # --------------------------------------------------
598              
599             sub _process_priority_rule
600             {
601             my($self, $index, $a_node) = @_;
602              
603             my($alternative_count) = 0;
604              
605             my($continue);
606             my($depth_under);
607             my($name);
608             my(@token);
609              
610             $a_node -> walk_down
611             ({
612             callback => sub
613             {
614             my($node, $option) = @_;
615             $name = $node -> name;
616             $continue = 1;
617              
618             # Skip the first 2 daughters, which hold offsets for the
619             # start and end of the token within the input stream.
620              
621             return 1 if ($node -> my_daughter_index < 2);
622              
623             if ($node -> mother -> mother -> name eq 'action_name')
624             {
625             push @token, 'action', '=>', $name;
626             }
627             elsif ($name eq 'alternative')
628             {
629             $alternative_count++;
630              
631             push @token, '|' if ($alternative_count > 1);
632             }
633             elsif ($node -> mother -> mother -> name eq 'blessing_name')
634             {
635             push @token, 'bless', '=>', $name;
636             }
637             elsif ($node -> mother -> name eq 'character_class')
638             {
639             push @token, $name;
640             }
641             elsif ($node -> mother -> name =~ /op_declare_+/)
642             {
643             push @token, $name;
644             }
645             elsif ($name eq 'parenthesized_rhs_primary_list')
646             {
647             $continue = 0;
648             $depth_under = $node -> depth_under;
649              
650             push @token, @{$self -> _process_parenthesized_list($index, $node, $depth_under)};
651             }
652             elsif ($node -> mother -> mother -> name eq 'rank_specification')
653             {
654             push @token, 'rank', '=>', $name;
655             }
656             elsif ($node -> mother -> mother -> name eq 'separator_specification')
657             {
658             push @token, 'separator', '=>';
659             }
660             elsif ($node -> mother -> name eq 'single_quoted_string')
661             {
662             push @token, $name;
663             }
664             elsif ($node -> mother -> mother -> name eq 'symbol_name')
665             {
666             push @token, {$node -> mother -> name => $name};
667             }
668              
669             return $continue;
670             },
671             _depth => 0,
672             });
673              
674             return [@token];
675              
676             } # End of _process_priority_rule.
677              
678             # --------------------------------------------------
679              
680             sub _process_quantified_rule
681             {
682             my($self, $index, $a_node) = @_;
683              
684             my($name);
685             my(@token);
686              
687             $a_node -> walk_down
688             ({
689             callback => sub
690             {
691             my($node, $option) = @_;
692             $name = $node -> name;
693              
694             # Skip the first 2 daughters, which hold offsets for the
695             # start and end of the token within the input stream.
696              
697             return 1 if ($node -> my_daughter_index < 2);
698              
699             if ($node -> mother -> mother -> name eq 'action_name')
700             {
701             push @token, 'action', '=>', $name;
702             }
703             elsif ($node -> mother -> name eq 'character_class')
704             {
705             push @token, $name;
706             }
707             elsif ($node -> mother -> name =~ /op_declare_+/)
708             {
709             push @token, $name;
710             }
711             elsif ($node -> mother -> name eq 'quantifier')
712             {
713             push @token, {quantifier => $name};
714             }
715             elsif ($node -> mother -> mother -> name eq 'separator_specification')
716             {
717             push @token, 'separator', '=>';
718             }
719             elsif ($node -> mother -> mother -> name eq 'symbol_name')
720             {
721             push @token, {$node -> mother -> name => $name};
722             }
723              
724             return 1; # Keep walking.
725             },
726             _depth => 0,
727             });
728              
729             return [@token];
730              
731             } # End of _process_quantified_rule.
732              
733             # --------------------------------------------------
734              
735             sub _process_start_rule
736             {
737             my($self, $index, $a_node) = @_;
738              
739             my($name);
740             my(@token);
741              
742             $a_node -> walk_down
743             ({
744             callback => sub
745             {
746             my($node, $option) = @_;
747             $name = $node -> name;
748              
749             # Skip the first 2 daughters, which hold offsets for the
750             # start and end of the token within the input stream.
751              
752             return 1 if ($node -> my_daughter_index < 2);
753              
754             if ($node -> mother -> mother -> name eq 'symbol_name')
755             {
756             push @token, ':start', '::=', {$node -> mother -> name => $name};
757             }
758              
759             return 1; # Keep walking.
760             },
761             _depth => 0,
762             });
763              
764             return [@token];
765              
766             } # End of _process_start_rule.
767              
768             # ------------------------------------------------
769              
770             sub run
771             {
772             my($self) = @_;
773             my($package) = 'MarpaX::Grammar::Parser::Dummy'; # This is actually included below.
774             my $marpa_bnf = slurp $self -> marpa_bnf_file, {utf8 => 1};
775             my($marpa_grammar) = Marpa::R2::Scanless::G -> new({bless_package => $package, source => \$marpa_bnf});
776             my $user_bnf = slurp $self -> user_bnf_file, {utf8 => 1};
777             my($recce) = Marpa::R2::Scanless::R -> new({grammar => $marpa_grammar});
778              
779             $recce -> read(\$user_bnf);
780              
781             Data::TreeDumper::DumpTree
782             (
783             ${$recce -> value},
784             '', # No title since Data::TreeDumper::Renderer::Marpa prints nothing.
785             DISPLAY_ROOT_ADDRESS => 1,
786             NO_WRAP => 1,
787             RENDERER =>
788             {
789             NAME => 'Marpa', # I.e.: Data::TreeDumper::Renderer::Marpa.
790             package => $package, # I.e.: MarpaX::Grammar::Parser::Dummy.
791             root => $self -> raw_tree,
792             }
793             );
794              
795             my($raw_tree_file) = $self -> raw_tree_file;
796              
797             if ($raw_tree_file)
798             {
799             open(OUT, '>', $raw_tree_file) || die "Can't open(> $raw_tree_file): $!\n";
800             print OUT map{"$_\n"} @{$self -> raw_tree -> tree2string({no_attributes => 1 - $self -> bind_attributes})};
801             close OUT;
802             }
803              
804             $self -> compress_tree;
805              
806             my($cooked_tree_file) = $self -> cooked_tree_file;
807              
808             if ($cooked_tree_file)
809             {
810             open(OUT, '>', $cooked_tree_file) || die "Can't open(> $cooked_tree_file): $!\n";
811             print OUT map{"$_\n"} @{$self -> cooked_tree -> tree2string({no_attributes => 1 - $self -> bind_attributes})};
812             close OUT;
813             }
814              
815             # Return 0 for success and 1 for failure.
816              
817             return 0;
818              
819             } # End of run.
820              
821             # ------------------------------------------------
822              
823             package MarpaX::Grammar::Parser::Dummy;
824              
825             our $VERSION = '1.01';
826              
827             sub new{return {};}
828              
829             #-------------------------------------------------
830              
831             1;
832              
833             =pod
834              
835             =head1 NAME
836              
837             C - Converts a Marpa grammar into a forest using Tree::DAG_Node
838              
839             =head1 Synopsis
840              
841             use MarpaX::Grammar::Parser;
842              
843             my(%option) =
844             ( # Inputs:
845             marpa_bnf_file => 'share/metag.bnf',
846             user_bnf_file => 'share/stringparser.bnf',
847             # Outputs:
848             cooked_tree_file => 'share/stringparser.cooked.tree',
849             raw_tree_file => 'share/stringparser.raw.tree',
850             );
851              
852             MarpaX::Grammar::Parser -> new(%option) -> run;
853              
854             See share/*.bnf for input files and share/*.tree for output files.
855              
856             For more help, run:
857              
858             shell> perl -Ilib scripts/bnf2tree.pl -h
859              
860             See share/*.bnf for input files and share/*.tree for output files.
861              
862             Note: Installation includes copying all files from the share/ directory, into a dir chosen by L.
863             Run scripts/find.grammars.pl to display the name of the latter dir.
864              
865             Note: The cooked tree can be graphed with L. That module has its own
866             L.
867              
868             =head1 Description
869              
870             C uses L to convert a user's SLIF-DSL into a tree of Marpa-style attributes,
871             (see L), and then post-processes that (see L) to create another tree, this time
872             containing just the original grammar (see L).
873              
874             So, currently, the forest contains just 2 trees, acessible via the methods L and L.
875             The nature of these trees is discussed in the L.
876              
877             These trees are managed by L.
878              
879             Lastly, the major purpose of the cooked tree is to serve as input to L.
880              
881             =head1 Installation
882              
883             Install C as you would for any C module:
884              
885             Run:
886              
887             cpanm MarpaX::Grammar::Parser
888              
889             or run:
890              
891             sudo cpan MarpaX::Grammar::Parser
892              
893             or unpack the distro, and then either:
894              
895             perl Build.PL
896             ./Build
897             ./Build test
898             sudo ./Build install
899              
900             or:
901              
902             perl Makefile.PL
903             make (or dmake or nmake)
904             make test
905             make install
906              
907             Note: Installation includes copying all files from the share/ directory, into a dir chosen by L.
908             Run scripts/find.grammars.pl to display the name of the latter dir.
909              
910             =head1 Constructor and Initialization
911              
912             C is called as C<< my($parser) = MarpaX::Grammar::Parser -> new(k1 => v1, k2 => v2, ...) >>.
913              
914             It returns a new object of type C.
915              
916             Key-value pairs accepted in the parameter list (see also the corresponding methods
917             [e.g. L]):
918              
919             =over 4
920              
921             =item o bind_attributes Boolean
922              
923             Include (1) or exclude (0) attributes in the tree file(s) output.
924              
925             Default: 0.
926              
927             =item o cooked_tree_file aTextFileName
928              
929             The name of the text file to write containing the grammar as a cooked tree.
930              
931             If '', the file is not written.
932              
933             Default: ''.
934              
935             Note: The bind_attributes option/method affects the output.
936              
937             =item o logger aLog::HandlerObject
938              
939             By default, an object of type L is created which prints to STDOUT, but in this version nothing
940             is actually printed.
941              
942             See C and C below.
943              
944             Set C to '' (the empty string) to stop a logger being created.
945              
946             Default: undef.
947              
948             =item o marpa_bnf_file aMarpaSLIF-DSLFileName
949              
950             Specify the name of Marpa's own SLIF-DSL file. This file ships with L. It's name is metag.bnf.
951              
952             A copy, as of Marpa::R2 V 2.068000, ships with C. See share/metag.bnf.
953              
954             This option is mandatory.
955              
956             Default: ''.
957              
958             =item o maxlevel logOption1
959              
960             This option affects L objects.
961              
962             See the L docs.
963              
964             Default: 'info'.
965              
966             =item o minlevel logOption2
967              
968             This option affects L object.
969              
970             See the L docs.
971              
972             Default: 'error'.
973              
974             No lower levels are used.
975              
976             =item o raw_tree_file aTextFileName
977              
978             The name of the text file to write containing the grammar as a raw tree.
979              
980             If '', the file is not written.
981              
982             Default: ''.
983              
984             Note: The bind_attributes option/method affects the output.
985              
986             =item o user_bnf_file aUserSLIF-DSLFileName
987              
988             Specify the name of the file containing your Marpa::R2-style grammar.
989              
990             See share/stringparser.bnf for a sample.
991              
992             This option is mandatory.
993              
994             Default: ''.
995              
996             =back
997              
998             =head1 Methods
999              
1000             =head2 bind_attributes([$Boolean])
1001              
1002             Here, the [] indicate an optional parameter.
1003              
1004             Get or set the option which includes (1) or excludes (0) node attributes from the output C
1005             and C.
1006              
1007             Note: C is a parameter to new().
1008              
1009             =head2 clean_name($name)
1010              
1011             Returns a list of 2 elements: ($name, $attributes).
1012              
1013             $name is just the name of the token.
1014              
1015             $attributes is a hashref with these keys:
1016              
1017             =over 4
1018              
1019             =item o bracketed_name => $Boolean
1020              
1021             Indicates the token's name is (1) or is not (0) of the form '<...>'.
1022              
1023             =item o quantifier => $char
1024              
1025             Indicates the token is quantified. $char is one of '', '*' or '+'.
1026              
1027             If $char is '' (the empty string), the token is not quantified.
1028              
1029             =item o real_name => $string
1030              
1031             The user-specified version of the name of the token, including leading '<' and trailing '>' if any.
1032              
1033             =back
1034              
1035             =head2 compress_branch($index, $node)
1036              
1037             Called by L.
1038              
1039             Converts 1 sub-tree of the raw tree into one sub-tree of the cooked tree.
1040              
1041             =head2 compress_tree()
1042              
1043             Called automatically by L.
1044              
1045             Converts the raw tree into the cooked tree, calling L once for each
1046             daughter of the raw tree.
1047              
1048             Output is the tree returned by L.
1049              
1050             =head2 cooked_tree()
1051              
1052             Returns the root node, of type L, of the cooked tree of items in the user's grammar.
1053              
1054             By cooked tree, I mean as post-processed from the raw tree so as to include just the original user's SLIF-DSL tokens.
1055              
1056             The cooked tree is optionally written to the file name given by L.
1057              
1058             The nature of this tree is discussed in the L.
1059              
1060             See also L.
1061              
1062             =head2 cooked_tree_file([$output_file_name])
1063              
1064             Here, the [] indicate an optional parameter.
1065              
1066             Get or set the name of the file to which the cooked tree form of the user's grammar will be written.
1067              
1068             If no output file is supplied, nothing is written.
1069              
1070             See share/stringparser.cooked.tree for the output of post-processing Marpa's analysis of share/stringparser.bnf.
1071              
1072             This latter file is the grammar used in L.
1073              
1074             Note: C is a parameter to new().
1075              
1076             Note: The bind_attributes option/method affects the output.
1077              
1078             =head2 log($level, $s)
1079              
1080             Calls $self -> logger -> log($level => $s) if ($self -> logger).
1081              
1082             =head2 logger([$logger_object])
1083              
1084             Here, the [] indicate an optional parameter.
1085              
1086             Get or set the logger object.
1087              
1088             To disable logging, just set logger to the empty string.
1089              
1090             Note: C is a parameter to new().
1091              
1092             =head2 marpa_bnf_file([$bnf_file_name])
1093              
1094             Here, the [] indicate an optional parameter.
1095              
1096             Get or set the name of the file to read Marpa's grammar from.
1097              
1098             Note: C is a parameter to new().
1099              
1100             =head2 maxlevel([$string])
1101              
1102             Here, the [] indicate an optional parameter.
1103              
1104             Get or set the value used by the logger object.
1105              
1106             This option is only used if an object of type L is created. See L.
1107              
1108             Note: C is a parameter to new().
1109              
1110             =head2 minlevel([$string])
1111              
1112             Here, the [] indicate an optional parameter.
1113              
1114             Get or set the value used by the logger object.
1115              
1116             This option is only used if an object of type L is created. See L.
1117              
1118             Note: C is a parameter to new().
1119              
1120             =head2 new()
1121              
1122             The constructor. See L.
1123              
1124             =head2 raw_tree()
1125              
1126             Returns the root node, of type L, of the raw tree of items in the user's grammar.
1127              
1128             By raw tree, I mean as derived directly from Marpa.
1129              
1130             The raw tree is optionally written to the file name given by L.
1131              
1132             The nature of this tree is discussed in the L.
1133              
1134             See also L.
1135              
1136             =head2 raw_tree_file([$output_file_name])
1137              
1138             Here, the [] indicate an optional parameter.
1139              
1140             Get or set the name of the file to which the raw tree form of the user's grammar will be written.
1141              
1142             If no output file is supplied, nothing is written.
1143              
1144             See share/stringparser.raw.tree for the output of Marpa's analysis of share/stringparser.bnf.
1145              
1146             This latter file is the grammar used in L.
1147              
1148             Note: C is a parameter to new().
1149              
1150             Note: The bind_attributes option/method affects the output.
1151              
1152             =head2 run()
1153              
1154             The method which does all the work.
1155              
1156             See L and scripts/bnf2tree.pl for sample code.
1157              
1158             =head2 user_bnf_file([$bnf_file_name])
1159              
1160             Here, the [] indicate an optional parameter.
1161              
1162             Get or set the name of the file to read the user's grammar's SLIF-DSL from. The whole file is slurped in as
1163             a single string.
1164              
1165             See share/stringparser.bnf for a sample. It is the grammar used in L.
1166              
1167             Note: C is a parameter to new().
1168              
1169             =head1 Files Shipped with this Module
1170              
1171             =head2 Data Files
1172              
1173             =over 4
1174              
1175             =item o share/c.ast.bnf
1176              
1177             This is part of L, by Jean-Damien Durand. It's 1,565 lines long.
1178              
1179             The outputs are share/c.ast.cooked.tree and share/c.ast.raw.tree.
1180              
1181             =item o share/c.ast.cooked.tree
1182              
1183             This is the output from post-processing Marpa's analysis of share/c.ast.bnf.
1184              
1185             The command to generate this file is:
1186              
1187             shell> scripts/bnf2tree.sh c.ast
1188              
1189             =item o share/c.ast.raw.tree
1190              
1191             This is the output from processing Marpa's analysis of share/c.ast.bnf. It's 56,723 lines long, which indicates
1192             the complexity of Jean-Damien's grammar for C.
1193              
1194             The command to generate this file is:
1195              
1196             shell> scripts/bnf2tree.sh c.ast
1197              
1198             =item o share/json.1.bnf
1199              
1200             It is part of L, written as a gist by Peter Stuifzand.
1201              
1202             See L.
1203              
1204             The outputs are share/json.1.cooked.tree and share/json.1.raw.tree.
1205              
1206             =item o share/json.1.cooked.tree
1207              
1208             This is the output from post-processing Marpa's analysis of share/json.1.bnf.
1209              
1210             The command to generate this file is:
1211              
1212             shell> scripts/bnf2tree.sh json.1
1213              
1214             =item o share/json.1.raw.tree
1215              
1216             This is the output from processing Marpa's analysis of share/json.1.bnf.
1217              
1218             The command to generate this file is:
1219              
1220             shell> scripts/bnf2tree.sh json.1
1221              
1222             =item o share/json.2.bnf
1223              
1224             It also is part of L, written by Jeffrey Kegler as a reply to the gist above from Peter.
1225              
1226             The outputs are share/json.2.cooked.tree and share/json.2.raw.tree.
1227              
1228             =item o share/json.2.cooked.tree
1229              
1230             This is the output from post-processing Marpa's analysis of share/json.2.bnf.
1231              
1232             The command to generate this file is:
1233              
1234             shell> scripts/bnf2tree.sh json.2
1235              
1236             =item o share/json.2.raw.tree
1237              
1238             This is the output from processing Marpa's analysis of share/json.2.bnf.
1239              
1240             The command to generate this file is:
1241              
1242             shell> scripts/bnf2tree.sh json.2
1243              
1244             =item o share/metag.bnf.
1245              
1246             This is a copy of L's SLIF-DSL, as of Marpa::R2 V 2.068000.
1247              
1248             See L above.
1249              
1250             =item o share/stringparser.bnf.
1251              
1252             This is a copy of L's SLIF-DSL.
1253              
1254             The outputs are share/stringparser.cooked.tree and share/stringparser.raw.tree.
1255              
1256             See L above.
1257              
1258             =item o share/stringparser.cooked.tree
1259              
1260             This is the output from post-processing Marpa's analysis of share/stringparser.bnf.
1261              
1262             The command to generate this file is:
1263              
1264             shell> scripts/bnf2tree.sh stringparser
1265              
1266             =item o share/stringparser.raw.tree
1267              
1268             This is the output from processing Marpa's analysis of share/stringparser.bnf.
1269              
1270             The command to generate this file is:
1271              
1272             shell> scripts/bnf2tree.sh stringparser
1273              
1274             See also the next item.
1275              
1276             =item o share/stringparser.treedumper
1277              
1278             This is the output of running:
1279              
1280             shell> perl scripts/metag.pl share/metag.bnf share/stringparser.bnf > share/stringparser.treedumper
1281              
1282             That script, metag.pl, is discussed just below, and in the L.
1283              
1284             =item o share/termcap.info.bnf
1285              
1286             It is part of L, written by Jean-Damien Durand.
1287              
1288             The outputs are share/termcap.cooked.tree and share/termcap.info.raw.tree.
1289              
1290             =item o share/termcap.info.cooked.tree
1291              
1292             This is the output from post-processing Marpa's analysis of share/termcap.info.bnf.
1293              
1294             The command to generate this file is:
1295              
1296             shell> scripts/bnf2tree.sh termcap.info
1297              
1298             =item o share/termcap.info.raw.tree
1299              
1300             This is the output from processing Marpa's analysis of share/termcap.info.bnf.
1301              
1302             The command to generate this file is:
1303              
1304             shell> scripts/bnf2tree.sh termcap.info
1305              
1306             =back
1307              
1308             =head2 Scripts
1309              
1310             =over 4
1311              
1312             =item o scripts/bnf2tree.pl
1313              
1314             This is a neat way of using this module. For help, run:
1315              
1316             shell> perl -Ilib scripts/bnf2tree.pl -h
1317              
1318             Of course you are also encouraged to include the module directly in your own code.
1319              
1320             =item o scripts/bnf2tree.sh
1321              
1322             This is a quick way for me to run bnf2tree.pl.
1323              
1324             =item o scripts/find.grammars.pl
1325              
1326             This prints the path to a grammar file. After installation of the module, run it with:
1327              
1328             shell> perl scripts/find.grammars.pl (Defaults to json.1.bnf)
1329             shell> perl scripts/find.grammars.pl c.ast.bnf
1330             shell> perl scripts/find.grammars.pl json.1.bnf
1331             shell> perl scripts/find.grammars.pl json.2.bnf
1332             shell> perl scripts/find.grammars.pl stringparser.bnf
1333             shell> perl scripts/find.grammars.pl termcap.inf.bnf
1334              
1335             It will print the name of the path to given grammar file.
1336              
1337             =item o scripts/metag.pl
1338              
1339             This is Jeffrey Kegler's code. See the L for more.
1340              
1341             =item o scripts/pod2html.sh
1342              
1343             This lets me quickly proof-read edits to the docs.
1344              
1345             =back
1346              
1347             =head1 FAQ
1348              
1349             =head2 What is this SLIF-DSL thingy?
1350              
1351             Marpa's grammars are written in what we call a SLIF-DSL. Here, SLIF stands for Marpa's Scanless Interface, and DSL
1352             is L.
1353              
1354             Many programmers will have heard of L. Well, Marpa's
1355             SLIF-DSL is an extended BNF. That is, it includes special tokens which only make sense within the context of a Marpa
1356             grammar. Hence the 'Domain Specific' part of the name.
1357              
1358             In practice, this means you express your grammar in a string, and Marpa treats that as a set of rules as to how
1359             you want Marpa to process your input stream.
1360              
1361             Marpa's docs for its SLIF-DSL L.
1362              
1363             =head2 What is the difference between the cooked tree and the raw tree?
1364              
1365             The raw tree is generated by processing the output of Marpa's parse of the user's grammar file.
1366             It contains Marpa's view of that grammar.
1367              
1368             The cooked tree is generated by post-processing the raw tree, to extract just the user's grammar's tokens.
1369             It contains the user's view of their grammar.
1370              
1371             The cooked tree can be graphed with L. That module has its own
1372             L.
1373              
1374             The following items explain this in more detail.
1375              
1376             =head2 What are the details of the nodes in the cooked tree?
1377              
1378             Under the root, there are a set of nodes:
1379              
1380             =over 4
1381              
1382             =item o N nodes, 1 per statement in the grammar
1383              
1384             The node's names are the left-hand side of each statement in the grammar.
1385              
1386             Each node is the root of a subtree describing the statement.
1387              
1388             Under those nodes are a set of nodes:
1389              
1390             =over 4
1391              
1392             =item o 1 node for the separator between the left and right sides of the statement
1393              
1394             So, the node's name is one of: '=' '::=' or '~'.
1395              
1396             =item o 1 node per token from the right-hand side of each statement
1397              
1398             The node's name is the token itself.
1399              
1400             =back
1401              
1402             =back
1403              
1404             The attributes of each node are a hashref, with these keys:
1405              
1406             =over 4
1407              
1408             =item o bracketed_name => $Boolean
1409              
1410             Indicates the token's name is or is not of the form '<...>'.
1411              
1412             =item o quantifier => $char
1413              
1414             Indicates the token is quantified. $char is one of '', '*' or '+'.
1415              
1416             If $char is ' ' (the empty string), the token is not quantified.
1417              
1418             =item o real_name => $string
1419              
1420             The user-specified version of the name of the token, including leading '<' and trailing '>' if any.
1421              
1422             =back
1423              
1424             See share/stringparser.cooked.tree.
1425              
1426             =head2 What are the details of the nodes in the raw tree?
1427              
1428             Under the root, there are a set of nodes:
1429              
1430             =over 4
1431              
1432             =item o One node for the offset of the start of the grammar within the input stream.
1433              
1434             The node's name is the integer start offset.
1435              
1436             =item o One node for the offset of the end of the grammar within the input stream.
1437              
1438             The node's name is the integer end offset.
1439              
1440             =item o N nodes, 1 per statement in the grammar
1441              
1442             The node's names are either an item from the user's grammar (when the attribute 'type' is 'Grammar')
1443             or a Marpa-assigned token (when the attribute 'type' is 'Marpa').
1444              
1445             Each node is the root of a subtree describing the statement.
1446              
1447             See share/stringparser.raw.attributes.tree for a tree with attributes displayed (bind_attributes => 1), and
1448             share/stringparser.raw.tree for the same tree without attributes (bind_attributes => 0).
1449              
1450             =back
1451              
1452             The attributes of each node are a hashref, with these keys:
1453              
1454             =over 4
1455              
1456             =item o type
1457              
1458             This indicates what type of node it is. Values:
1459              
1460             =over 4
1461              
1462             =item o 'Grammar' => The node's name is an item from the user-specified grammar.
1463              
1464             =item o 'Marpa' => Marpa has assigned a class to the node (or to one of its parents)
1465              
1466             The class name is for the form: $class_name::$node_name.
1467              
1468             C<$class_name> is a constant provided by this module, and is 'MarpaX::Grammar::Parser::Dummy'.
1469              
1470             See share/stringparser.treedumper, which will make this much clearer.
1471              
1472             The technique used to generate this file is discussed above, under L.
1473              
1474             Note: The file share/stringparser.treedumper shows some class names, but they are currently I stored
1475             in the tree returned by the method L.
1476              
1477             =back
1478              
1479             =back
1480              
1481             See share/stringparser.raw.tree.
1482              
1483             =head2 Why are attributes used to identify bracketed names?
1484              
1485             Because L assigns a special meaning to labels which begin with '<' and '<<'.
1486              
1487             =head2 How do I sort the daughters of a node?
1488              
1489             Here's one way, using the node names as sort keys.
1490              
1491             As an example, choose $root as either $self -> cooked_tree or $self -> raw_tree, and then:
1492              
1493             @daughters = sort{$a -> name cmp $b -> name} $root -> daughters;
1494              
1495             $root -> set_daughters(@daughters);
1496              
1497             Note: Since the original order of the daughters, in both the cooked and raw trees, is significant, sorting is
1498             contra-indicated.
1499              
1500             =head2 Where did the basic code come from?
1501              
1502             Jeffrey Kegler wrote it, and posted it on the Google Group dedicated to Marpa, on 2013-07-22,
1503             in the thread 'Low-hanging fruit'. I modified it slightly for a module context.
1504              
1505             The original code is shipped as scripts/metag.pl.
1506              
1507             As you can see he uses a different way of reading the files, one which avoids loading a separate module.
1508             I've standardized on L, especially when I want utf8, and L when I want to read a
1509             directory. Of course I try not to use both in the same module.
1510              
1511             =head2 Why did you use Data::TreeDump?
1512              
1513             It offered the output which was most easily parsed of the modules I tested.
1514             The others were L, L, L and L.
1515              
1516             =head2 Where is Marpa's Homepage?
1517              
1518             L.
1519              
1520             =head2 Are there any articles discussing Marpa?
1521              
1522             Yes, many by its author, and several others. See Marpa's homepage, just above, and:
1523              
1524             L, (in progress, by Peter Stuifzand and Ron Savage).
1525              
1526             L, by Peter Stuifzand.
1527              
1528             L, by Peter Stuifzand.
1529              
1530             L, by Ron Savage.
1531              
1532             =head1 See Also
1533              
1534             L.
1535              
1536             L.
1537              
1538             L.
1539              
1540             L.
1541              
1542             L.
1543              
1544             L.
1545              
1546             =head1 Machine-Readable Change Log
1547              
1548             The file Changes was converted into Changelog.ini by L.
1549              
1550             =head1 Version Numbers
1551              
1552             Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.
1553              
1554             =head1 Support
1555              
1556             Email the author, or log a bug on RT:
1557              
1558             L.
1559              
1560             =head1 Author
1561              
1562             L was written by Ron Savage Iron@savage.net.auE> in 2013.
1563              
1564             Home page: L.
1565              
1566             =head1 Copyright
1567              
1568             Australian copyright (c) 2013, Ron Savage.
1569              
1570             All Programs of mine are 'OSI Certified Open Source Software';
1571             you can redistribute them and/or modify them under the terms of
1572             The Artistic License 2.0, a copy of which is available at:
1573             http://www.opensource.org/licenses/index.html
1574              
1575             =cut