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!
