File Coverage

blib/lib/MarpaX/Grammar/GraphViz2.pm
Criterion Covered Total %
statement 22 24 91.6
branch n/a
condition n/a
subroutine 8 8 100.0
pod n/a
total 30 32 93.7


line stmt bran cond sub pod time code
1             package MarpaX::Grammar::GraphViz2;
2              
3 1     1   523 use strict;
  1         1  
  1         24  
4 1     1   556 use utf8;
  1         8  
  1         5  
5 1     1   24 use warnings;
  1         3  
  1         22  
6 1     1   2 use warnings qw(FATAL utf8); # Fatalize encoding glitches.
  1         2  
  1         61  
7 1     1   510 use open qw(:std :utf8); # Undeclared streams in UTF-8.
  1         975  
  1         5  
8              
9 1     1   108 use File::Basename; # For basename().
  1         2  
  1         96  
10 1     1   464 use File::Which; # For which().
  1         730  
  1         56  
11              
12 1     1   1016 use GraphViz2;
  0            
  0            
13              
14             use List::AllUtils qw/first_index indexes/;
15              
16             use Log::Handler;
17              
18             use Types::Standard qw/Any Bool HashRef Int Str/;
19              
20             use MarpaX::Grammar::Parser;
21              
22             use Moo;
23              
24             has bnf_name =>
25             (
26             default => sub{return 'BNF'},
27             is => 'rw',
28             isa => Str,
29             required => 0,
30             );
31              
32             has default_count =>
33             (
34             default => sub{return 0},
35             is => 'rw',
36             isa => Int,
37             required => 0,
38             );
39              
40             has discard_count =>
41             (
42             default => sub{return 0},
43             is => 'rw',
44             isa => Int,
45             required => 0,
46             );
47              
48             has driver =>
49             (
50             default => sub{return ''},
51             is => 'rw',
52             isa => Str,
53             required => 0,
54             );
55              
56             has event_count =>
57             (
58             default => sub{return 0},
59             is => 'rw',
60             isa => Int,
61             required => 0,
62             );
63              
64             has format =>
65             (
66             default => sub{return ''},
67             is => 'rw',
68             isa => Str,
69             required => 0,
70             );
71              
72             has g1_rules_seen =>
73             (
74             default => sub{return {} },
75             is => 'rw',
76             isa => HashRef,
77             required => 0,
78             );
79              
80             has graph =>
81             (
82             default => sub{return ''},
83             is => 'rw',
84             isa => Any, # Actually an Object.
85             required => 0,
86             );
87              
88             has legend =>
89             (
90             default => sub{return 0},
91             is => 'rw',
92             isa => Int,
93             required => 0,
94             );
95              
96             has lexeme_count =>
97             (
98             default => sub{return 0},
99             is => 'rw',
100             isa => Int,
101             required => 0,
102             );
103              
104             has logger =>
105             (
106             default => sub{return undef},
107             is => 'rw',
108             isa => Any,
109             required => 0,
110             );
111              
112             has marpa_bnf_file =>
113             (
114             default => sub{return ''},
115             is => 'rw',
116             isa => Str,
117             required => 1,
118             );
119              
120             has maxlevel =>
121             (
122             default => sub{return 'notice'},
123             is => 'rw',
124             isa => Str,
125             required => 0,
126             );
127              
128             has minlevel =>
129             (
130             default => sub{return 'error'},
131             is => 'rw',
132             isa => Str,
133             required => 0,
134             );
135              
136             has nodes_seen =>
137             (
138             default => sub{return {} },
139             is => 'rw',
140             isa => HashRef,
141             required => 0,
142             );
143              
144             has output_file =>
145             (
146             default => sub{return ''},
147             is => 'rw',
148             isa => Str,
149             required => 0,
150             );
151              
152             has parser =>
153             (
154             default => sub{return ''},
155             is => 'rw',
156             isa => Any, # Actually an Object.
157             required => 0,
158             );
159              
160             has root_name =>
161             (
162             default => sub{return 'DSL'},
163             is => 'rw',
164             isa => Str,
165             required => 0,
166             );
167              
168             has user_bnf_file =>
169             (
170             default => sub{return ''},
171             is => 'rw',
172             isa => Str,
173             required => 1,
174             );
175              
176             has verbose =>
177             (
178             default => sub{return 0},
179             is => 'rw',
180             isa => Bool,
181             required => 1,
182             );
183              
184             our $VERSION = '2.00';
185              
186             # ------------------------------------------------
187              
188             sub BUILD
189             {
190             my($self) = @_;
191              
192             die "No Marpa BNF file found\n" if (! -e $self -> marpa_bnf_file);
193             die "No user BNF file found\n" if (! -e $self -> user_bnf_file);
194              
195             $self -> driver($self -> driver || which('dot') );
196             $self -> format($self -> format || 'svg');
197              
198             if (! defined $self -> logger)
199             {
200             $self -> logger(Log::Handler -> new);
201             $self -> logger -> add
202             (
203             screen =>
204             {
205             maxlevel => $self -> maxlevel,
206             message_layout => '%m',
207             minlevel => $self -> minlevel,
208             }
209             );
210             }
211              
212             my($graph) ||= GraphViz2 -> new
213             (
214             edge => {color => 'grey'},
215             global => {directed => 1, driver => $self -> driver, format => $self -> format},
216             graph => {label => basename($self -> user_bnf_file), rankdir => 'TB'},
217             logger => $self -> logger,
218             node => {shape => 'rectangle', style => 'filled'},
219             );
220              
221             $self -> graph($graph);
222             $self -> parser
223             (
224             MarpaX::Grammar::Parser -> new
225             (
226             marpa_bnf_file => $self -> marpa_bnf_file,
227             logger => $self -> logger,
228             user_bnf_file => $self -> user_bnf_file,
229             )
230             );
231              
232             } # End of BUILD.
233              
234             # ------------------------------------------------
235              
236             sub add_legend
237             {
238             my($self) = @_;
239              
240             $self -> graph -> push_subgraph
241             (
242             # No options...
243             # Legend: top. Border: no. Label: no.
244             #
245             # label => 'cluster_legend',
246             # Legend: top. Border: no. Label: no.
247             #
248             # name => 'cluster_legend',
249             # Legend: top. Border: yes. Label: *.bnf.
250             #
251             #graph => {label => 'cluster_legend'},
252             # Legend: top. Border: no. Label: no. Not using subgraph => {...}.
253             #graph => {label => 'cluster_Legend'},
254             # Legend: bottom. Border: no. Label: no. Using subgraph => {...}.
255             # Legend: top. Border: no. Label: no. Not using subgraph => {...}.
256             subgraph => {rank => 'max'},
257             # Legend: top. Border: no. Label: no. Using graph => {...}.
258             # Legend: bottom. Border: no. Label: no. Not using graph => {...}.
259             );
260              
261             $self -> graph -> add_node
262             (
263             label =>
264             q|
265             <
266            
267             Pink nodes are ':default' rules
268            
269            
270             Magenta nodes are ':discard' rules
271            
272            
273             The orchid node is the 'discard default' rule
274            
275            
276             The golden node is the 'lexeme default' rule
277            
278            
279             The LightBlue node is the ':start' rule
280            
281            
282             CornflowerBlue nodes are G1 rules
283            
284            
285             LightGreen nodes are ':lexeme' rules
286            
287            
>
288             |,
289             name => 'Legend',
290             shape => 'plaintext',
291             );
292              
293             $self -> graph -> pop_subgraph;
294              
295             } # End of add_legend.
296              
297             # ------------------------------------------------
298              
299             sub add_node
300             {
301             my($self, %attributes) = @_;
302             my($name) = delete $attributes{name};
303             my($seen) = $self -> nodes_seen;
304              
305             $self -> graph -> add_node(name => $name, %attributes) if (! $$seen{$name});
306              
307             $$seen{$name} = 1;
308              
309             $self -> nodes_seen($seen);
310              
311             } # End of add_node.
312              
313             # ------------------------------------------------
314              
315             sub hashref2string
316             {
317             my($self, $hashref) = @_;
318             $hashref ||= {};
319              
320             return '{' . join(', ', map{qq|$_ => "$$hashref{$_}"|} sort keys %$hashref) . '}';
321              
322             } # End of hashref2string.
323              
324             # ------------------------------------------------
325              
326             sub log
327             {
328             my($self, $level, $s) = @_;
329              
330             $self -> logger -> log($level => $s) if ($self -> logger);
331              
332             } # End of log.
333              
334             # ------------------------------------------------
335              
336             sub _process_adverbs
337             {
338             my($self, $daughters) = @_;
339              
340             my($attr);
341             my(@adverbs);
342             my(@grand_kids);
343             my($name);
344             my($token_1, $token_2);
345              
346             for my $j (2 .. $#$daughters)
347             {
348             $name = $$daughters[$j] -> name;
349             $attr = $$daughters[$j] -> attributes;
350             $token_1 = $$attr{token};
351             @grand_kids = $$daughters[$j] -> daughters;
352              
353             last if ($#grand_kids < 0); # TODO. Alternative.
354              
355             $attr = ($$daughters[$j] -> daughters)[0] -> attributes;
356             $token_2 = $$attr{token};
357              
358             push @adverbs, "$token_1 =\\> $token_2";
359             }
360              
361             if ($#adverbs >= 0)
362             {
363             $adverbs[0] = "\{$adverbs[0]";
364             $adverbs[$#adverbs] .= '}';
365             @adverbs = map{ {text => $_} } @adverbs;
366             }
367              
368             return [@adverbs];
369              
370             } # End of _process_adverbs.
371              
372             # ------------------------------------------------
373              
374             sub _process_bare_lexeme
375             {
376             my($self, $daughters) = @_;
377              
378             } # End of _process_bare_lexeme.
379              
380             # ------------------------------------------------
381              
382             sub _process_bare_rule
383             {
384             my($self, $daughters) = @_;
385             my($attr) = ($$daughters[0] -> daughters)[0] -> attributes;
386             my($token_1) = $$attr{token};
387             my($attributes) =
388             {
389             fillcolor => 'LightSkyBlue',
390             label => $token_1,
391             };
392              
393             $self -> add_node(name => $token_1, %$attributes);
394              
395             # Loop over the alternative branches within this rule.
396             # Note: There might by N rule tokens before 'alternative'.
397              
398             my($rules_seen) = $self -> g1_rules_seen;
399              
400             my(@hidden_tokens, @hidden_kids);
401             my(@kids);
402             my(@label);
403             my($name);
404             my($rule_name);
405             my($token_2, $token_3);
406              
407             for (my $index = 2; $index <= $#$daughters; $index++)
408             {
409             $name = $$daughters[$index] -> name;
410              
411             if ($name eq 'alternative')
412             {
413             $rule_name = join(' ', map{s/:/\x{a789}/; $_} @label);
414             $$attributes{label} = $rule_name;
415              
416             $self -> add_node(name => $rule_name, %$attributes);
417             $self -> graph -> add_edge(from => $token_1, to => $rule_name);
418              
419             for (@label)
420             {
421             next if ($_ eq $rule_name);
422              
423             $$attributes{label} = $_;
424              
425             $self -> add_node(name => $_, %$attributes);
426             $self -> graph -> add_edge(from => $rule_name, to => $_);
427             }
428              
429             @label = ();
430              
431             next;
432             }
433             elsif ($name eq 'parenthesized_rhs_primary_list')
434             {
435             @hidden_kids = $$daughters[$index] -> daughters;
436             @hidden_tokens = ('(');
437              
438             for (my $i = 0; $i <= $#hidden_kids; $i++)
439             {
440             $attr = $hidden_kids[$i] -> attributes;
441              
442             push @hidden_tokens, $$attr{token};
443             }
444              
445             push @hidden_tokens, ')';
446             push @label, join(' ', @hidden_tokens);
447              
448             $index++;
449              
450             next;
451             }
452             elsif ($name ne 'rhs')
453             {
454             next;
455             }
456              
457             $attr = $$daughters[$index] -> attributes;
458             @kids = $$daughters[$index] -> daughters;
459              
460             next if ($#kids < 0);
461              
462             $attr = $kids[0] -> attributes;
463             $token_2 = $$attr{token};
464              
465             push @label, $token_2;
466             }
467              
468             if ($#label >= 0)
469             {
470             $rule_name = join(' ', map{s/:/\x{a789}/g; $_} @label);
471             $$attributes{label} = $rule_name;
472              
473             $self -> add_node(name => $rule_name, %$attributes);
474             $self -> graph -> add_edge(from => $token_1, to => $rule_name);
475              
476             for (@label)
477             {
478             next if ($_ eq $rule_name);
479              
480             $$attributes{label} = $_;
481              
482             $self -> add_node(name => $_, %$attributes);
483             $self -> graph -> add_edge(from => $rule_name, to => $_);
484             }
485             }
486              
487             $self -> g1_rules_seen($rules_seen);
488              
489             } # End of _process_bare_rule.
490              
491             # ------------------------------------------------
492              
493             sub _process_default_rule
494             {
495             my($self, $daughters) = @_;
496              
497             $self -> default_count($self -> default_count + 1);
498              
499             my($default_count) = $self -> default_count;
500             my($default_name) = "\x{a789}default";
501             my($attributes) =
502             {
503             fillcolor => 'pink',
504             label => [{text => " $default_name"}], # Warning: Don't delete the ' ' before the \x.
505             };
506              
507             if ($default_count == 1)
508             {
509             $self -> add_node(name => $default_name, %$attributes);
510             $self -> graph -> add_edge(from => $self -> root_name, to => $default_name);
511             }
512              
513             my($adverbs) = $self -> _process_adverbs($daughters);
514              
515             if ($#$adverbs >= 0)
516             {
517             $$attributes{fillcolor} = 'pink';
518             $$attributes{label} = $adverbs;
519             my($adverb_name) = "${default_name}_$default_count";
520              
521             $self -> add_node(name => $adverb_name, %$attributes);
522             $self -> graph -> add_edge(from => $default_name, to => $adverb_name);
523             }
524              
525             } # End of _process_default_rule.
526              
527             # ------------------------------------------------
528              
529             sub _process_discard_default_rule
530             {
531             my($self, $daughters) = @_;
532             my($discard_name) = "discard default";
533             my($attributes) =
534             {
535             fillcolor => 'orchid',
536             label => $discard_name,
537             };
538              
539             $self -> add_node(name => $discard_name, %$attributes);
540             $self -> graph -> add_edge(from => $self -> root_name, to => $discard_name);
541              
542             my($adverbs) = $self -> _process_adverbs($daughters);
543              
544             if ($#$adverbs >= 0)
545             {
546             $$attributes{fillcolor} = 'orchid';
547             $$attributes{label} = $adverbs;
548             my($adverb_name) = "${discard_name}_1";
549              
550             $self -> add_node(name => $adverb_name, %$attributes);
551             $self -> graph -> add_edge(from => $discard_name, to => $adverb_name);
552             }
553              
554              
555             } # End of _process_discard_default_rule.
556              
557             # ------------------------------------------------
558              
559             sub _process_discard_rule
560             {
561             my($self, $daughters) = @_;
562              
563             $self -> discard_count($self -> discard_count + 1);
564              
565             my($discard_count) = $self -> discard_count;
566             my($discard_name) = "\x{a789}discard";
567             my($attributes) =
568             {
569             fillcolor => 'magenta',
570             label => [{text => " $discard_name"}], # Warning: Don't delete the ' ' before the \x.
571             };
572              
573             if ($discard_count == 1)
574             {
575             $self -> add_node(name => $discard_name, %$attributes);
576             $self -> graph -> add_edge(from => $self -> root_name, to => $discard_name);
577             }
578              
579             my($attr) = ($$daughters[2] -> daughters)[0] -> attributes;
580             my($token) = $$attr{token};
581              
582             $$attributes{fillcolor} = 'magenta';
583             $$attributes{label} = $token;
584              
585             $self -> add_node(name => $token, %$attributes);
586             $self -> graph -> add_edge(from => $discard_name, to => $token);
587              
588             } # End of _process_discard_rule.
589              
590             # ------------------------------------------------
591              
592             sub _process_lexeme_adverbs
593             {
594             my($self, $daughters) = @_;
595              
596             my($attr);
597             my(@adverbs);
598             my(@grand_kids);
599             my($name);
600             my($token_1, $token_2);
601              
602             for my $j (2 .. $#$daughters)
603             {
604             $name = $$daughters[$j] -> name;
605             $attr = $$daughters[$j] -> attributes;
606             $token_1 = $$attr{token};
607             $attr = ($$daughters[$j] -> daughters)[0] -> attributes;
608             $token_2 = $$attr{token};
609              
610             push @adverbs, [$token_1, $token_2];
611             }
612              
613             return [@adverbs];
614              
615             } # End of _process_lexeme_adverbs.
616              
617             # ------------------------------------------------
618              
619             sub _process_lexeme_default_rule
620             {
621             my($self, $daughters) = @_;
622             my($lexeme_name) = 'lexeme default';
623             my($attributes) =
624             {
625             fillcolor => 'goldenrod',
626             label => $lexeme_name,
627             };
628              
629             $self -> add_node(name => $lexeme_name, %$attributes);
630             $self -> graph -> add_edge(from => $self -> root_name, to => $lexeme_name);
631              
632             my($adverbs) = $self -> _process_adverbs($daughters);
633              
634             if ($#$adverbs >= 0)
635             {
636             $$attributes{fillcolor} = 'goldenrod';
637             $$attributes{label} = $adverbs;
638             my($adverb_name) = "${lexeme_name}_1";
639              
640             $self -> add_node(name => $adverb_name, %$attributes);
641             $self -> graph -> add_edge(from => $lexeme_name, to => $adverb_name);
642             }
643              
644             } # End of _process_lexeme_default_rule.
645              
646             # ------------------------------------------------
647              
648             sub _process_lexeme_rule
649             {
650             my($self, $daughters) = @_;
651              
652             $self -> lexeme_count($self -> lexeme_count + 1);
653              
654             my($adverbs) = $self -> _process_lexeme_adverbs($daughters);
655             my($lexeme_count) = $self -> lexeme_count;
656             my($lexeme_name) = shift @$adverbs;
657             $lexeme_name = $$lexeme_name[1]; # Discard 'bare_name'.
658             my($attributes) =
659             {
660             fillcolor => 'DeepSkyBlue2',
661             label => [{text => " $lexeme_name"}], # Warning: Don't delete the ' ' before the \x.
662             };
663              
664             if ($#$adverbs >= 0)
665             {
666             @$adverbs = map{"$$_[0] =\\> $$_[1]"} @$adverbs;
667              
668             unshift @$adverbs, " \x{a789}lexeme =\\> $lexeme_name";
669              
670             $$adverbs[0] = "\{$$adverbs[0]";
671             $$adverbs[$#$adverbs] .= '}';
672             @$adverbs = map{ {text => $_} } @$adverbs;
673             $$attributes{label} = $adverbs;
674             my($adverb_name) = "${lexeme_name}_$lexeme_count";
675              
676             $self -> add_node(name => $adverb_name, %$attributes);
677             $self -> graph -> add_edge(from => $lexeme_name, to => $adverb_name);
678             }
679              
680             } # End of _process_lexeme_rule.
681              
682             # ------------------------------------------------
683              
684             sub _process_start_rule
685             {
686             my($self, $daughters) = @_;
687             my($name) = 'start';
688             my($attributes) =
689             {
690             fillcolor => 'Turquoise1',
691             label => [{text => " \x{a789}start"}], # Warning: Don't delete the ' ' before the \x.
692             };
693              
694             $self -> add_node(name => $name, %$attributes);
695             $self -> graph -> add_edge(from => $self -> root_name, to => $name);
696              
697             my($attr) = ($$daughters[2] -> daughters)[0] -> attributes;
698             my($starter) = $$attr{token};
699             $$attributes{label} = [{text => $$attr{token} }];
700              
701             $self -> add_node(name => $starter, %$attributes);
702             $self -> graph -> add_edge(from => $name, to => $starter);
703              
704             } # End of _process_start_rule.
705              
706             # ----------------------------------------------
707              
708             sub run
709             {
710             my($self) = @_;
711             my($result) = $self -> parser -> run;
712              
713             # Return 0 for success and 1 for failure.
714              
715             return $result if ($result == 1);
716              
717             # Look over the statements.
718              
719             my(@statements) = $self -> parser -> cooked_tree -> daughters;
720              
721             my($attributes);
722             my(@daughters);
723             my($name);
724             my($offset);
725             my($token);
726              
727             for my $i (0 .. $#statements)
728             {
729             if ($self -> verbose)
730             {
731             print "i: @{[$i + 1]}. name: ", $statements[$i] -> name, ". \n";
732             }
733              
734             # Loop over the components of a single statement.
735              
736             @daughters = $statements[$i] -> daughters;
737             $name = $daughters[0] -> name;
738             $attributes = $daughters[0] -> attributes;
739             $token = $$attributes{token};
740              
741             next if ($name ne 'lhs');
742              
743             if ($self -> verbose)
744             {
745             print "\t1st daughter. name: $name. token: $token. \n";
746             }
747              
748             if ($token eq 'bare_name')
749             {
750             if ($daughters[1] -> name eq 'op_declare_bnf')
751             {
752             $self -> _process_bare_rule(\@daughters);
753             }
754             else
755             {
756             $self -> _process_bare_lexeme(\@daughters);
757             }
758             }
759             elsif ($token eq ':default')
760             {
761             $self -> _process_default_rule(\@daughters);
762             }
763             elsif ( ($token eq 'discard default') && ($name eq 'lhs') )
764             {
765             $self -> _process_discard_default_rule(\@daughters);
766             }
767             elsif ($token eq ':discard')
768             {
769             $self -> _process_discard_rule(\@daughters);
770             }
771             elsif ($token eq 'lexeme default')
772             {
773             $self -> _process_lexeme_default_rule(\@daughters);
774             }
775             elsif ($token eq ':lexeme')
776             {
777             $self -> _process_lexeme_rule(\@daughters);
778             }
779             elsif ($token eq ':start')
780             {
781             $self -> _process_start_rule(\@daughters);
782             }
783             }
784              
785             $self -> add_legend if ($self -> legend);
786              
787             my($output_file) = $self -> output_file;
788              
789             if ($output_file)
790             {
791             $self -> graph -> run(output_file => $output_file);
792             }
793              
794             # Return 0 for success and 1 for failure.
795              
796             return $result;
797              
798             } # End of run.
799              
800             # ------------------------------------------------
801              
802             1;
803              
804             =pod
805              
806             =encoding utf8
807              
808             =head1 NAME
809              
810             L - Convert a Marpa grammar into an image
811              
812             =head1 Synopsis
813              
814             use MarpaX::Grammar::GraphViz2;
815              
816             my(%option) =
817             ( # Inputs:
818             legend => 1,
819             marpa_bnf_file => 'share/metag.bnf',
820             user_bnf_file => 'share/stringparser.bnf',
821             # Outputs:
822             output_file => 'html/stringparser.svg',
823             );
824              
825             MarpaX::Grammar::GraphViz2 -> new(%option) -> run;
826              
827             See share/*.bnf for input files and html/*.svg for output files.
828              
829             For more help, run:
830              
831             shell> perl -Ilib scripts/bnf2graph.pl -h
832              
833             Note: Installation includes copying all files from the share/ directory, into a dir chosen by
834             L. Run scripts/find.grammars.pl to display the name of the latter dir.
835              
836             See also
837             L.
838              
839             =head1 Description
840              
841             For a given BNF, process the cooked tree output by L, and turn it into an
842             image.
843              
844             The tree holds a representation of the user's BNF (SLIF-DSL), and is managed by L.
845              
846             This modules uses L internally. It does not read that module's output file.
847              
848             =head1 Installation
849              
850             Install L as you would for any C module:
851              
852             Run:
853              
854             cpanm MarpaX::Grammar::GraphViz2
855              
856             or run:
857              
858             sudo cpan MarpaX::Grammar::GraphViz2
859              
860             or unpack the distro, and then either:
861              
862             perl Build.PL
863             ./Build
864             ./Build test
865             sudo ./Build install
866              
867             or:
868              
869             perl Makefile.PL
870             make (or dmake or nmake)
871             make test
872             make install
873              
874             Note: Installation includes copying all files from the share/ directory, into a dir chosen by
875             L. Run scripts/find.grammars.pl to display the name of the latter dir.
876              
877             =head1 Constructor and Initialization
878              
879             Call C as C<< my($parser) = MarpaX::Grammar::GraphViz2 -> new(k1 => v1, k2 => v2, ...) >>.
880              
881             It returns a new object of type C.
882              
883             Key-value pairs accepted in the parameter list (see also the corresponding methods
884             [e.g. L]):
885              
886             =over 4
887              
888             =item o driver aGraphvizDriverName
889              
890             The name of the Graphviz program to provide to L.
891              
892             Default: 'dot'.
893              
894             =item o format => $format_name
895              
896             This is the format of the output file, to be passed to L.
897              
898             Default: 'svg'.
899              
900             =item o graph => $graphviz2_object
901              
902             Provides an object of type L, to do the rendering.
903              
904             Default:
905              
906             GraphViz2 -> new
907             (
908             edge => {color => 'grey'},
909             global => {directed => 1, driver => $self -> driver, format => $self -> format},
910             graph => {label => basename($self -> user_bnf_file), rankdir => 'TB'},
911             logger => $self -> logger,
912             node => {shape => 'rectangle', style => 'filled'},
913             );
914              
915             =item o legend => $Boolean
916              
917             Add a legend (1) to the graph, or omit it (0).
918              
919             Default: 0.
920              
921             =item o logger => $logger_object
922              
923             Specify a logger object.
924              
925             The default value triggers creation of an object of type L which outputs to the
926             screen.
927              
928             To disable logging, just set I to the empty string.
929              
930             The value for I is passed to L.
931              
932             Default: undef.
933              
934             =item o marpa_bnf_file aMarpaBNFFileName
935              
936             Specify the name of Marpa's own BNF file. This file ships with L. It's name is
937             metag.bnf.
938              
939             A copy, as of Marpa::R2 V 2.096000, ships with C. See share/metag.bnf.
940              
941             This option is mandatory.
942              
943             Default: ''.
944              
945             =item o maxlevel => $level
946              
947             This option is only used if an object of type L is created. See I above.
948              
949             See also L.
950              
951             Default: 'notice'. A typical value is 'debug'.
952              
953             =item o minlevel => $level
954              
955             This option is only used if an object of type L is created. See I above.
956              
957             See also L.
958              
959             Default: 'error'.
960              
961             No lower levels are used.
962              
963             =item o output_file => $output_file_name
964              
965             Write the image to this file.
966              
967             Use the C option to specify the type of image desired.
968              
969             If '', the file is not written.
970              
971             Default: ''.
972              
973             =item o user_bnf_file aUserBNFFileName
974              
975             Specify the name of the file containing your Marpa::R2-style grammar.
976              
977             See share/stringparser.bnf for a sample.
978              
979             This option is mandatory.
980              
981             Default: ''.
982              
983             =back
984              
985             =head1 Methods
986              
987             =head2 add_legend()
988              
989             Adds a legend to the graph if new() was called as C<< new(legend => 1) >>.
990              
991             =head2 add_node(%attributes)
992              
993             Adds (once only) a node to the graph. The node's name is C<$attributes{name}>.
994              
995             Also, adds that name to the hashref of node names seen, which is returned by L.
996              
997             =head2 clean_name($name, $skip_symbols)
998              
999             Cleans the given name to escape or replace characters special to L.
1000              
1001             Note: L also escapes some characters.
1002              
1003             $skip_symbols is used by the caller in 1 case to stop a regexp being activated.
1004              
1005             See the L for details.
1006              
1007             Returns the cleaned-up name.
1008              
1009             =head2 clean_tree()
1010              
1011             Calls L for each node in the tree.
1012              
1013             =head2 default_count()
1014              
1015             Returns the number of C<:default>' rules in the user's input.
1016              
1017             =head2 discard_count()
1018              
1019             Returns the number of C<:discard> rules in the user's input.
1020              
1021             =head2 driver([$executable_name])
1022              
1023             Here, the [] indicate an optional parameter.
1024              
1025             Get or set the name of the Graphviz program to provide to L.
1026              
1027             Note: C is a parameter to new().
1028              
1029             =head2 event_count()
1030              
1031             Returns the number of C rules in the user's input.
1032              
1033             =head2 format([$format])
1034              
1035             Here, the [] indicate an optional parameter.
1036              
1037             Get or set the format of the output file, to be created by the renderer.
1038              
1039             Note: C is a parameter to new().
1040              
1041             =head2 graph([$graph])
1042              
1043             Get of set the L object which will do the graphing.
1044              
1045             See also L.
1046              
1047             Note: C is a parameter to new().
1048              
1049             =head2 legend([$Boolean])
1050              
1051             Here, the [] indicate an optional parameter.
1052              
1053             Get or set the option to include (1) or exclude (0) a legend from the image.
1054              
1055             Note: C is a parameter to new().
1056              
1057             =head2 lexeme_count()
1058              
1059             Returns the number of C<:lexeme> rules in the user's input.
1060              
1061             =head2 lexemes()
1062              
1063             Returns a hashref keyed by the clean name, of lexemes seen in the user's input.
1064              
1065             The value for each key is an arrayref of hashrefs suitable for forcing L to plot the node
1066             as a record structure. See L for the gory
1067             details.
1068              
1069             =head2 log($level, $s)
1070              
1071             Calls $self -> logger -> log($level => $s) if ($self -> logger).
1072              
1073             =head2 logger([$logger_object])
1074              
1075             Here, the [] indicate an optional parameter.
1076              
1077             Get or set the logger object.
1078              
1079             To disable logging, just set logger to the empty string.
1080              
1081             This logger is passed to L.
1082              
1083             Note: C is a parameter to new().
1084              
1085             =head2 marpa_bnf_file([$bnf_file_name])
1086              
1087             Here, the [] indicate an optional parameter.
1088              
1089             Get or set the name of the file to read Marpa's grammar from.
1090              
1091             Note: C is a parameter to new().
1092              
1093             =head2 maxlevel([$string])
1094              
1095             Here, the [] indicate an optional parameter.
1096              
1097             Get or set the value used by the logger object.
1098              
1099             This option is only used if an object of type L is created. See
1100             L.
1101              
1102             Note: C is a parameter to new().
1103              
1104             =head2 minlevel([$string])
1105              
1106             Here, the [] indicate an optional parameter.
1107              
1108             Get or set the value used by the logger object.
1109              
1110             This option is only used if an object of type L is created. See
1111             L.
1112              
1113             Note: C is a parameter to new().
1114              
1115             =head2 new()
1116              
1117             The constructor. See L.
1118              
1119             =head2 nodes_seen()
1120              
1121             Returns a hashref keyed by the node name, of nodes passed to L.
1122              
1123             This is simply used to stop nodes being plotted twice.
1124              
1125             =head2 output_file([$output_file_name])
1126              
1127             Here, the [] indicate an optional parameter.
1128              
1129             Get or set the name of the file to which the renderer will write the resultant graph.
1130              
1131             If no output file is supplied, nothing is written.
1132              
1133             See also L.
1134              
1135             Note: C is a parameter to new().
1136              
1137             =head2 parser()
1138              
1139             Returns the L object which will do the analysis of the user's grammar.
1140              
1141             This object is created automatically during the call to L.
1142              
1143             =head2 rectify_node($node)
1144              
1145             For the given $node, which is an object of type L, clean it's real name.
1146              
1147             Then it adds the node's quantifier ('', '*' or '+') to that name, to act as the label (visible name)
1148             of the node, when the node is finally passed to L.
1149              
1150             Returns a 2-element list of ($name, $label).
1151              
1152             =head2 root_name()
1153              
1154             Returns a string which is the name of the root node of graph.
1155              
1156             =head2 run()
1157              
1158             The method which does all the work.
1159              
1160             See L and scripts/bnf2graph.pl for sample code.
1161              
1162             =head2 user_bnf_file([$bnf_file_name])
1163              
1164             Here, the [] indicate an optional parameter.
1165              
1166             Get or set the name of the file to read the user's grammar from.
1167              
1168             Note: C is a parameter to new().
1169              
1170             =head1 Files Shipped with this Module
1171              
1172             =head2 Data Files
1173              
1174             =over 4
1175              
1176             =item o share/c.ast.bnf
1177              
1178             This is part of L, by Jean-Damien Durand. It's 1,565 lines long.
1179              
1180             =item o html/c.ast.svg
1181              
1182             This is the image from c.ast.bnf.
1183              
1184             See the next point for how this file is created.
1185              
1186             =item o share/json.1.bnf
1187              
1188             It is part of L, written as a gist by Peter Stuifzand.
1189              
1190             See L.
1191              
1192             See the next point for how this file is created.
1193              
1194             =item o html/json.1.svg
1195              
1196             This is the image from json.1.bnf.
1197              
1198             =item o share/json.2.bnf
1199              
1200             It also is part of L, written by Jeffrey Kegler as a reply to the gist
1201             above from Peter.
1202              
1203             =item o html/json.2.svg
1204              
1205             This is the image from json.2.bnf.
1206              
1207             See the previous point for how this file is created.
1208              
1209             =item o share/json.3.bnf
1210              
1211             It also is part of L, and is written by Jeffrey Kegler.
1212              
1213             =item o html/json.3.svg
1214              
1215             This is the image from json.3.bnf.
1216              
1217             =item o share/metag.bnf.
1218              
1219             This is a copy of L's BNF, as of Marpa::R2 V 2.096000.
1220              
1221             =item o html/metag.svg
1222              
1223             This is the image from metag.bnf.
1224              
1225             =item o share/numeric.expressions.bnf.
1226              
1227             This BNF was extracted from L's examples/ambiguous.grammar.01.pl.
1228              
1229             =item o html/numeric.expressions.svg
1230              
1231             This is the image from numeric.expressions.bnf.
1232              
1233             See the next point for how this file is created.
1234              
1235             =item o share/stringparser.bnf.
1236              
1237             This is a copy of L's BNF.
1238              
1239             =item o html/stringparser.svg
1240              
1241             This is the image from stringparser.bnf.
1242              
1243             See the next point for how this file is created.
1244              
1245             =item o share/termcap.info.bnf
1246              
1247             It also is part of L, written by Jean-Damien Durand.
1248              
1249             =item o html/termcap.info.svg
1250              
1251             This is the image from termcap.info.bnf.
1252              
1253             See the next point for how this file is created.
1254              
1255             =back
1256              
1257             =head2 Scripts
1258              
1259             =over 4
1260              
1261             =item o scripts/bnf2graph.pl
1262              
1263             This is a neat way of using the module. For help, run:
1264              
1265             shell> perl -Ilib scripts/bnf2graph.pl -h
1266              
1267             Of course you are also encouraged to include this module directly in your own code.
1268              
1269             =item o scripts/bnf2graph.sh
1270              
1271             This is a quick way for me to run bnf2graph.pl.
1272              
1273             =item o scripts/find.grammars.pl
1274              
1275             This prints the path to a grammar file. After installation of the module, run it with:
1276              
1277             shell> perl scripts/find.grammars.pl (Defaults to json.1.bnf)
1278             shell> perl scripts/find.grammars.pl c.ast.bnf
1279             shell> perl scripts/find.grammars.pl json.1.bnf
1280             shell> perl scripts/find.grammars.pl json.2.bnf
1281             shell> perl scripts/find.grammars.pl stringparser.bnf
1282             shell> perl scripts/find.grammars.pl termcap.inf.bnf
1283              
1284             It will print the name of the path to given grammar file.
1285              
1286             =item o scripts/generate.demo.pl
1287              
1288             Generates html/index.html.
1289              
1290             =item o scripts/generate.demo.sh
1291              
1292             This calls generate.demo.pl for each grammar shipped with the module.
1293              
1294             Actually, it skips c.ast by default, since it takes 6 m 47 s to run that. But if you pass any
1295             command line parameter to the script, it includes c.ast.
1296              
1297             Then it copies html/* to my web server's doc root (which is in Debian's default RAM disk) at
1298             /dev/shm/html.
1299              
1300             =item o scripts/pod2html.sh
1301              
1302             This lets me quickly proof-read edits to the docs.
1303              
1304             =back
1305              
1306             =head1 FAQ
1307              
1308             =head2 Why are some characters in the images replaced by Unicode versions?
1309              
1310             Firstly, the Perl module L escapes some characters. Currently, these are:
1311              
1312             [ ] " (in various circumstances)
1313              
1314             We let L handle these.
1315              
1316             Secondly, L itself treats some characters specially. Currently, these
1317             are:
1318              
1319             < > : "
1320              
1321             We use this code to handle these:
1322              
1323             $name =~ s/\\/\\\\/g; # Escape \.
1324             $name =~ s/
1325             $name =~ s/>/\\>/g; # Escape >.
1326             $name =~ s/:/\x{a789}/g; # Replace : with a Unicode :
1327             $name =~ s/\"/\x{a78c}\x{a78c}/g; # Replace " with 2 copies of a Unicode ' ...
1328             # ... because I could not find a Unicode ".
1329              
1330             =head2 Why do some images have a tiny sub-graph, whose root is, e.g., ''?
1331              
1332             This is due to the author using both 'comma' and '' as tokens within the grammar.
1333              
1334             So far this module does not notice the two are the same.
1335              
1336             A similar thing can happen elsewhere, e.g. with named event statements, when the rhs name uses (say)
1337             ''
1338             and the rule referred to uses just 'xyz'.
1339              
1340             In all such cases, there will be 2 nodes, with 2 names differing in just the brackets.
1341              
1342             =head2 Why do some nodes have (or lack) a quantifier when I use it both with and without one?
1343              
1344             There is simply no way to plot a node both with and without the quantifier. The one which appears is
1345             chosen arbitrarily, depending on how the code scans the grammar. This means it is currently beyond
1346             control.
1347              
1348             =head2 Why do the nodes on the demo page lack rule numbers?
1349              
1350             I'm undecided as to whether or not they are a good idea. I documented it on the demo page to
1351             indicate it was easy (for some nodes), and await feedback.
1352              
1353             =head2 Can I control the format or placement of the legend?
1354              
1355             No, but you can turn it off with the C option to C<< new() >>.
1356              
1357             =head1 ToDo
1358              
1359             =over 4
1360              
1361             =item o Perhaps add rule # to each node
1362              
1363             This is the rule # within the input stream. Doing this is simple for some nodes, and difficult for
1364             others.
1365              
1366             =back
1367              
1368             =head1 Machine-Readable Change Log
1369              
1370             The file Changes was converted into Changelog.ini by L.
1371              
1372             =head1 Version Numbers
1373              
1374             Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.
1375              
1376             =head1 Repository
1377              
1378             L
1379              
1380             =head1 Support
1381              
1382             Email the author, or log a bug on RT:
1383              
1384             L.
1385              
1386             =head1 Author
1387              
1388             L was written by Ron Savage Iron@savage.net.auE> in 2013.
1389              
1390             Home page: L.
1391              
1392             =head1 Copyright
1393              
1394             Australian copyright (c) 2013, Ron Savage.
1395              
1396             All Programs of mine are 'OSI Certified Open Source Software';
1397             you can redistribute them and/or modify them under the terms of
1398             The Perl License, a copy of which is available at:
1399             http://www.opensource.org/licenses/index.html
1400              
1401             =cut