Ron Savage

NAME

MarpaX::Grammar::GraphViz2 - Convert a Marpa grammar into an image

Synopsis

        use MarpaX::Grammar::GraphViz2;

        my(%option) =
        (               # Inputs:
                legend         => 1,
                marpa_bnf_file => 'share/metag.bnf',
                user_bnf_file  => 'share/stringparser.bnf',
                        # Outputs:
                output_file    => 'html/stringparser.svg',
        );

        MarpaX::Grammar::GraphViz2 -> new(%option) -> run;

See share/*.bnf for input files and html/*.svg for output files.

For more help, run:

        shell> perl -Ilib scripts/bnf2graph.pl -h

Note: Installation includes copying all files from the share/ directory, into a dir chosen by File::ShareDir. Run scripts/find.grammars.pl to display the name of the latter dir.

See also the demo page.

Description

For a given BNF, process the cooked tree output by MarpaX::Grammar::Parser, and turn it into an image.

The tree holds a representation of the user's BNF (SLIF-DSL), and is managed by Tree::DAG_Node.

This modules uses MarpaX::Grammar::Parser internally. It does not read that module's output file.

Installation

Install MarpaX::Grammar::GraphViz2 as you would for any Perl module:

Run:

        cpanm MarpaX::Grammar::GraphViz2

or run:

        sudo cpan MarpaX::Grammar::GraphViz2

or unpack the distro, and then either:

        perl Build.PL
        ./Build
        ./Build test
        sudo ./Build install

or:

        perl Makefile.PL
        make (or dmake or nmake)
        make test
        make install

Note: Installation includes copying all files from the share/ directory, into a dir chosen by File::ShareDir. Run scripts/find.grammars.pl to display the name of the latter dir.

Constructor and Initialization

Call new() as my($parser) = MarpaX::Grammar::GraphViz2 -> new(k1 => v1, k2 => v2, ...).

It returns a new object of type MarpaX::Grammar::GraphViz2.

Key-value pairs accepted in the parameter list (see also the corresponding methods [e.g. "marpa_bnf_file([$bnf_file_name])"]):

o driver aGraphvizDriverName

The name of the Graphviz program to provide to GraphViz2.

Default: 'dot'.

o format => $format_name

This is the format of the output file, to be passed to GraphViz2.

Default: 'svg'.

o graph => $graphviz2_object

Provides an object of type GraphViz2, to do the rendering.

Default:

        GraphViz2 -> new
        (
                edge   => {color => 'grey'},
                global => {directed => 1, driver => $self -> driver, format => $self -> format},
                graph  => {label => basename($self -> user_bnf_file), rankdir => 'TB'},
                logger => $self -> logger,
                node   => {shape => 'rectangle', style => 'filled'},
        );
o legend => $Boolean

Add a legend (1) to the graph, or omit it (0).

Default: 0.

o logger => $logger_object

Specify a logger object.

The default value triggers creation of an object of type Log::Handler which outputs to the screen.

To disable logging, just set logger to the empty string.

The value for logger is passed to GraphViz2.

Default: undef.

o marpa_bnf_file aMarpaBNFFileName

Specify the name of Marpa's own BNF file. This file ships with Marpa::R2. It's name is metag.bnf.

A copy, as of Marpa::R2 V 2.096000, ships with MarpaX::Grammar::GraphViz2. See share/metag.bnf.

This option is mandatory.

Default: ''.

o maxlevel => $level

This option is only used if an object of type Log::Handler is created. See logger above.

See also Log::Handler::Levels.

Default: 'notice'. A typical value is 'debug'.

o minlevel => $level

This option is only used if an object of type Log::Handler is created. See logger above.

See also Log::Handler::Levels.

Default: 'error'.

No lower levels are used.

o output_file => $output_file_name

Write the image to this file.

Use the format option to specify the type of image desired.

If '', the file is not written.

Default: ''.

o user_bnf_file aUserBNFFileName

Specify the name of the file containing your Marpa::R2-style grammar.

See share/stringparser.bnf for a sample.

This option is mandatory.

Default: ''.

Methods

add_legend()

Adds a legend to the graph if new() was called as new(legend => 1).

add_node(%attributes)

Adds (once only) a node to the graph. The node's name is $attributes{name}.

Also, adds that name to the hashref of node names seen, which is returned by "nodes_seen()".

clean_name($name, $skip_symbols)

Cleans the given name to escape or replace characters special to dot.

Note: GraphViz2 also escapes some characters.

$skip_symbols is used by the caller in 1 case to stop a regexp being activated.

See the "FAQ" for details.

Returns the cleaned-up name.

clean_tree()

Calls "clean_name($name, $skip_symbols)" for each node in the tree.

default_count()

Returns the number of :default' rules in the user's input.

discard_count()

Returns the number of :discard rules in the user's input.

driver([$executable_name])

Here, the [] indicate an optional parameter.

Get or set the name of the Graphviz program to provide to GraphViz2.

Note: driver is a parameter to new().

event_count()

Returns the number of event rules in the user's input.

format([$format])

Here, the [] indicate an optional parameter.

Get or set the format of the output file, to be created by the renderer.

Note: format is a parameter to new().

graph([$graph])

Get of set the GraphViz2 object which will do the graphing.

See also "output_file([$output_file_name])".

Note: graph is a parameter to new().

legend([$Boolean])

Here, the [] indicate an optional parameter.

Get or set the option to include (1) or exclude (0) a legend from the image.

Note: legend is a parameter to new().

lexeme_count()

Returns the number of :lexeme rules in the user's input.

lexemes()

Returns a hashref keyed by the clean name, of lexemes seen in the user's input.

The value for each key is an arrayref of hashrefs suitable for forcing GraphViz2 to plot the node as a record structure. See http://www.graphviz.org/content/node-shapes#record for the gory details.

log($level, $s)

Calls $self -> logger -> log($level => $s) if ($self -> logger).

logger([$logger_object])

Here, the [] indicate an optional parameter.

Get or set the logger object.

To disable logging, just set logger to the empty string.

This logger is passed to GraphViz2.

Note: logger is a parameter to new().

marpa_bnf_file([$bnf_file_name])

Here, the [] indicate an optional parameter.

Get or set the name of the file to read Marpa's grammar from.

Note: marpa_bnf_file is a parameter to new().

maxlevel([$string])

Here, the [] indicate an optional parameter.

Get or set the value used by the logger object.

This option is only used if an object of type Log::Handler is created. See Log::Handler::Levels.

Note: maxlevel is a parameter to new().

minlevel([$string])

Here, the [] indicate an optional parameter.

Get or set the value used by the logger object.

This option is only used if an object of type Log::Handler is created. See Log::Handler::Levels.

Note: minlevel is a parameter to new().

new()

The constructor. See "Constructor and Initialization".

nodes_seen()

Returns a hashref keyed by the node name, of nodes passed to GraphViz2.

This is simply used to stop nodes being plotted twice.

output_file([$output_file_name])

Here, the [] indicate an optional parameter.

Get or set the name of the file to which the renderer will write the resultant graph.

If no output file is supplied, nothing is written.

See also graph([$graph]).

Note: output_file is a parameter to new().

parser()

Returns the Marpa::Grammar::Parser object which will do the analysis of the user's grammar.

This object is created automatically during the call to "new()".

rectify_node($node)

For the given $node, which is an object of type Tree::DAG_Node, clean it's real name.

Then it adds the node's quantifier ('', '*' or '+') to that name, to act as the label (visible name) of the node, when the node is finally passed to GraphViz2.

Returns a 2-element list of ($name, $label).

root_name()

Returns a string which is the name of the root node of graph.

run()

The method which does all the work.

See "Synopsis" and scripts/bnf2graph.pl for sample code.

user_bnf_file([$bnf_file_name])

Here, the [] indicate an optional parameter.

Get or set the name of the file to read the user's grammar from.

Note: user_bnf_file is a parameter to new().

Files Shipped with this Module

Data Files

o share/c.ast.bnf

This is part of MarpaX::Languages::C::AST, by Jean-Damien Durand. It's 1,565 lines long.

o html/c.ast.svg

This is the image from c.ast.bnf.

See the next point for how this file is created.

o share/json.1.bnf

It is part of MarpaX::Demo::JSONParser, written as a gist by Peter Stuifzand.

See https://gist.github.com/pstuifzand/4447349.

See the next point for how this file is created.

o html/json.1.svg

This is the image from json.1.bnf.

o share/json.2.bnf

It also is part of MarpaX::Demo::JSONParser, written by Jeffrey Kegler as a reply to the gist above from Peter.

o html/json.2.svg

This is the image from json.2.bnf.

See the previous point for how this file is created.

o share/json.3.bnf

It also is part of MarpaX::Demo::JSONParser, and is written by Jeffrey Kegler.

o html/json.3.svg

This is the image from json.3.bnf.

o share/metag.bnf.

This is a copy of Marpa::R2's BNF, as of Marpa::R2 V 2.096000.

o html/metag.svg

This is the image from metag.bnf.

o share/numeric.expressions.bnf.

This BNF was extracted from MarpaX::Demo::SampleScripts's examples/ambiguous.grammar.01.pl.

o html/numeric.expressions.svg

This is the image from numeric.expressions.bnf.

See the next point for how this file is created.

o share/stringparser.bnf.

This is a copy of MarpaX::Demo::StringParser's BNF.

o html/stringparser.svg

This is the image from stringparser.bnf.

See the next point for how this file is created.

o share/termcap.info.bnf

It also is part of MarpaX::Database::Terminfo, written by Jean-Damien Durand.

o html/termcap.info.svg

This is the image from termcap.info.bnf.

See the next point for how this file is created.

Scripts

o scripts/bnf2graph.pl

This is a neat way of using the module. For help, run:

        shell> perl -Ilib scripts/bnf2graph.pl -h

Of course you are also encouraged to include this module directly in your own code.

o scripts/bnf2graph.sh

This is a quick way for me to run bnf2graph.pl.

o scripts/find.grammars.pl

This prints the path to a grammar file. After installation of the module, run it with:

        shell> perl scripts/find.grammars.pl (Defaults to json.1.bnf)
        shell> perl scripts/find.grammars.pl c.ast.bnf
        shell> perl scripts/find.grammars.pl json.1.bnf
        shell> perl scripts/find.grammars.pl json.2.bnf
        shell> perl scripts/find.grammars.pl stringparser.bnf
        shell> perl scripts/find.grammars.pl termcap.inf.bnf

It will print the name of the path to given grammar file.

o scripts/generate.demo.pl

Generates html/index.html.

o scripts/generate.demo.sh

This calls generate.demo.pl for each grammar shipped with the module.

Actually, it skips c.ast by default, since it takes 6 m 47 s to run that. But if you pass any command line parameter to the script, it includes c.ast.

Then it copies html/* to my web server's doc root (which is in Debian's default RAM disk) at /dev/shm/html.

o scripts/pod2html.sh

This lets me quickly proof-read edits to the docs.

FAQ

Why are some characters in the images replaced by Unicode versions?

Firstly, the Perl module GraphViz2 escapes some characters. Currently, these are:

        [ ] " (in various circumstances)

We let GraphViz2 handle these.

Secondly, Graphviz itself treats some characters specially. Currently, these are:

        < > : "

We use this code to handle these:

        $name =~ s/\\/\\\\/g;             # Escape \.
        $name =~ s/</\\</g;               # Escape <.
        $name =~ s/>/\\>/g;               # Escape >.
        $name =~ s/:/\x{a789}/g;          # Replace : with a Unicode :
        $name =~ s/\"/\x{a78c}\x{a78c}/g; # Replace " with 2 copies of a Unicode ' ...
                                          # ... because I could not find a Unicode ".

Why do some images have a tiny sub-graph, whose root is, e.g., '<comma>'?

This is due to the author using both 'comma' and '<comma>' as tokens within the grammar.

So far this module does not notice the two are the same.

A similar thing can happen elsewhere, e.g. with named event statements, when the rhs name uses (say) '<xyz>' and the rule referred to uses just 'xyz'.

In all such cases, there will be 2 nodes, with 2 names differing in just the brackets.

Why do some nodes have (or lack) a quantifier when I use it both with and without one?

There is simply no way to plot a node both with and without the quantifier. The one which appears is chosen arbitrarily, depending on how the code scans the grammar. This means it is currently beyond control.

Why do the nodes on the demo page lack rule numbers?

I'm undecided as to whether or not they are a good idea. I documented it on the demo page to indicate it was easy (for some nodes), and await feedback.

Can I control the format or placement of the legend?

No, but you can turn it off with the legend option to new().

ToDo

o Perhaps add rule # to each node

This is the rule # within the input stream. Doing this is simple for some nodes, and difficult for others.

Machine-Readable Change Log

The file Changes was converted into Changelog.ini by Module::Metadata::Changes.

Version Numbers

Version numbers < 1.00 represent development versions. From 1.00 up, they are production versions.

Repository

https://github.com/ronsavage/MarpaX-Grammar-GraphViz2

Support

Email the author, or log a bug on RT:

https://rt.cpan.org/Public/Dist/Display.html?Name=MarpaX::Grammar::GraphViz2.

Author

MarpaX::Grammar::GraphViz2 was written by Ron Savage <ron@savage.net.au> in 2013.

Home page: http://savage.net.au/.

Copyright

Australian copyright (c) 2013, Ron Savage.

        All Programs of mine are 'OSI Certified Open Source Software';
        you can redistribute them and/or modify them under the terms of
        The Perl License, a copy of which is available at:
        http://www.opensource.org/licenses/index.html