Showing posts with label azmun. Show all posts
Showing posts with label azmun. Show all posts

14.6.11

Railroad Diagrams in Redmine Wiki

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:
(Note that there are precompiled Debian and RPM packages for LaTeX and dvipng available.)

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!

21.3.11

Verify And Beautify Model-2-Text Transformation Output

One of the projects which I developed during my PhD thesis is Azmun, a model based testing framework based on various MDD technologies. One of the tasks within Azmun is to transform an UML-based test model to a model checking problem in order to use a model checker for automated test case generation. Although Azmun does not require a specific model checker, I mostly use NuSMV for my research. NuSMV has an own language to describe a system, and I developed a model-2-text (m2t) transformation from UML to NuSMV using Xpand. And here comes the problem:

Q: How do we ensure that the output of our Xpand transformation is syntactically correct, and how can we format our output?

These two questions may seem to be independend, but as you will see in a minute, we can tackle both problems with one approach. As a side project, I created the nusmv-tools Eclipselabs project, which hosts some Eclipse-based tools. One of these tools is a rich text editor based on Xtext. And now we have the solution for our problem:

A: We create a Xpand beautifier based on the Xtext parser and serializer.

The following lines show how we solve our problem in Azmun:

public final class NuSMVCodeGenerator extends WorkflowComponentWithModelSlot {
  private Generator m_generator = null;

  @Override
  public void checkConfiguration(final Issues p_issues) {
    super.checkConfiguration(p_issues);

    m_generator = new Generator();
    m_generator.addMetaModel(new UML2MetaModel());
    // add other metamodels

    m_generator.setFileEncoding("utf-8");
    m_generator.setExpand("foo::bar::UML2NuSMV() FOR " + getModelSlot());

    final Outlet outlet = new Outlet("/some/path");
    outlet.addPostprocessor(new PostProcessor() {

      @Override
      public void beforeWriteAndClose(final FileHandle p_fileHandle) {
      }

      @Override
      public void afterClose(final FileHandle p_fileHandle) {
        try {
          if (p_fileHandle.getAbsolutePath() == null 
            || !p_fileHandle.getAbsolutePath().endsWith(".nusmv")) {
            return;
          }
          NuSMVStandaloneSetup.doSetup();
          final ResourceSet resourceSet = new ResourceSetImpl();
          final URI fileURI = URI.createFileURI(p_fileHandle.getAbsolutePath());
          final Resource resource = resourceSet.getResource(fileURI, true);
          for (final Diagnostic diagnostic : resource.getWarnings()) {
            p_issues.addWarning(diagnostic.getMessage());
          }
          for (final Diagnostic diagnostic : resource.getErrors()) {
            p_issues.addError(diagnostic.getMessage());
          }
          if (!resource.getErrors().isEmpty()) {
            return;
          }
          final SaveOptions saveOptions = SaveOptions.newBuilder().format().getOptions();
          resource.save(saveOptions.toOptionsMap());
        } catch (final Exception e) {
          throw new RuntimeException(e);
        }
      }
    });
    m_generator.addOutlet(outlet);
    m_generator.checkConfiguration(p_issues);
  }

  @Override
  protected void invokeInternal(final WorkflowContext p_ctx, 
      final ProgressMonitor p_monitor, final Issues p_issues) {
    m_generator.invoke(p_ctx, p_monitor, p_issues);
    m_generator = null;
  }
}

We configure the Xpand workflow component with a PostProcessor, which is called for every file we create during the m2t transformation. In the afterClose method, we load the generated file using the standard EMF ResourceSet mechanism, and immediately save the resource. The options we add to the Resource.save method specify that we want the output to be formatted by Xtext.

With this approach, we can be sure that the output we generate in the m2t transformations conforms to the NuSMV language. And as a side-effect we format our output.

Happy transforming!