I recently needed to show the grammar of our XText based UML guard and action language in the Azmun Wiki. I decided to use railroad diagrams for that purpose, since I remembered that the XText documentation contains such cool diagrams in the MWE2 sub-chapter. For example, here is the railroad diagram for Module definitions:
I don't know how the XText documentation is generated, but I know that the rail package for LaTeX is able to generate such diagarams. So I wanted to integrate such diagrams in our wiki using rail.
We host all the projects of our research training group METRIK in a Redmine instance. One of the Redmine Plug-Ins is the Wiki External Filter, which allows defining macros that process macro argument using external filter program and render its result in Redmine wiki. The Plug-In is shipped with support for PlantUML to draw UML diagrams, Graphviz for abritrary diagrams, ritex for MathML, and ffmpeg to embed videos.
In order to add a new filter, I extended the redmine/config/wiki_external_filter.yml file with the following entry:
rail: description: "Constructs railroad diagrams for (E)BNF grammars, see http://notendur.hi.is/snorri/091263/rail/rail.html" template: image outputs: - command: "SOME_PATH/rail.sh" content_type: "image/png" prolog: "\documentclass{article} \n \usepackage{rail} \n \pagestyle{empty} \n \\begin{document} \n \\begin{figure} \n" epilog: "\n \\end{figure} \n \\end{document} \n"
Our filter is named rail and calls the shell script rail.sh, which I will show in a second. It defines the needed prolog and epilog LaTeX commands including the usage of the rail package and the definition of a figure, so that the user only needs to specify the rail commands.
Here now the code of the rail.sh shell script:
#!/bin/sh # pipe STDIN to file cat - > input.tex # run first time with latex latex input.tex 1> /dev/null 2> /dev/null # run rail rail input 1> /dev/null 2> /dev/null # run second time with latex latex input.tex 1> /dev/null 2> /dev/null # convert DVI file to PNG dvipng -q -Ttight -M -pp1 --noghostscript -D150 -o out.png input.dvi 1> /dev/null 2> /dev/null # remove temporary files rm input.* # pipe contents of PNG to STDOUT cat out.png -
This script has following prerequesites:
- LaTeX installation
- compiled version of the rail binary
- dvipng Installation
Having all the pieces together, we now can use rail scripts to create nice railroad diagarams in Redmine. Here is an example taken from Azmun:
{{rail( \railalias{IMPLIES}{->} \railalias{EQUIVALENCE}{<>} \railalias{OR}{||} \railalias{XOR}{\textasciicircum} \railalias{AND}{\&\&} \railalias{EQ}{==} \railalias{NEQ}{!=} \railalias{LT}{<} \railalias{GT}{>} \railalias{LTE}{<=} \railalias{GTE}{>=} \railalias{SHIFTLEFT}{<<} \railalias{SHIFTRIGHT}{>>} \railalias{MUL}{*} \railalias{DIV}{/} \railalias{MOD}{\%} \railalias{PLUS}{+} \railalias{MINUS}{-} \railalias{NOT}{!} \railalias{PO}{(} \railalias{PC}{)} \railalias{DOT}{.} \railalias{FALSE}{false} \railalias{TRUE}{true} \railalias{INT}{0..9} \railterm{IMPLIES,EQUIVALENCE,OR,XOR,AND,EQ,NEQ,LT,GT,LTE,GTE,SHIFTLEFT,SHIFTRIGHT,MUL,DIV,MOD,PLUS,MINUS,NOT,PO,PC,DOT,FALSE,TRUE,INT} \begin{rail} BasicExpression : [constants] ( BooleanConstant | IntegerConstant ) | [attribute reference] AttributeReference | PO BasicExpression PC | [logical NOT] NOT BasicExpression | ( [integer multiplication] BasicExpression MUL BasicExpression | [integer division] BasicExpression DIV BasicExpression | [integer remainder] BasicExpression MOD BasicExpression ) | ( [integer addition] BasicExpression PLUS BasicExpression | [integer substraction] BasicExpression MINUS BasicExpression ) | ( [bit shift left] BasicExpression SHIFTLEFT BasicExpression | [bit shift right] BasicExpression SHIFTRIGHT BasicExpression ) | ( [equality] BasicExpression EQ BasicExpression | [inequality] BasicExpression NEQ BasicExpression | [less than] BasicExpression LT BasicExpression | [greater than] BasicExpression GT BasicExpression | [less than or equal] BasicExpression LTE BasicExpression | [greater than or equal] BasicExpression GTE BasicExpression ) | [logical AND] BasicExpression AND BasicExpression | ( [logical OR] BasicExpression OR BasicExpression | [logical exclusive OR] BasicExpression XOR BasicExpression ) | [logical equivalence] BasicExpression EQUIVALENCE BasicExpression | [logical implication] BasicExpression IMPLIES BasicExpression ; AttributeReference : UMLPropertyReference ( DOT AttributeReference )?; BooleanConstant : FALSE | TRUE ; IntegerConstant : ( MINUS | PLUS )? (INT+) ; \end{rail} )}}
This script results in the following diagram:
So, what is missing? Yes, an automatic conversion of Xtext grammars to rail scripts.
I am also looking forward to have the new Xtext Syntax Graph View.
Happy grammar hacking!