Sunday, August 14, 2011

Xtext Grammar Visualization

When developing EBNF-style grammars, e.g. for Xtext, syntax graphs can be a great help. One of the new features in Xtext 2 is the Xtext Syntax Graph which can be selected in the Eclipse menu under Views - Xtext - Xtext Syntax Graph. In case the grammar has ambiguities, users get an error message similar to the following when trying to generate the code for the grammar by running the MWE2 workflow file.

Decision can match input such as [...] using multiple alternatives 1,2

If the error isn't obvious, the alternatives can be displayed graphically with ANTLRWorks (the graph analysis can be expensive and thus is not included in the standard Xtext Syntax Graph). Here is an example of an Eclipse Xtext project with a simple, ambiguous grammar and the Xtext Syntax Graph.



To analyze the grammar in ANTLRWorks, the executable jar file from the ANTLR website can be downloaded and run using the JRE (java -jar antlrworks-1.x.x.jar). ANTLRWorks expects the ANTLR grammar file from the Xtext project, which is called Internal[...].g. A debug-friendly version of this file may be generated by adding fragment = parser.antlr.DebugAntlrGeneratorFragment { } to your MWE2 workflow file and running it againThe file is located in the Eclipse Xtext project in the src-gen folder in the [...].antlr.internal package (see first screenshot).
After opening it, the grammar can be checked by selecting Grammar - Check Grammar (Ctrl-R) from the menu. It will show an error message. Selecting the incorrect rule ruleModel and ticking both checkboxes for the alternatives (on the lower right on the screenshot) shows the ambiguity graphically.


Monday, February 7, 2011

Quickly formatting DSLs with Xtext

One typical task of developing new domain specific languages with Xtext is to customize the formatting in order to have a nice text layout. If the majority of grammar keywords should be formatted in the same way and only a few in a specific way, a generic formatter could be used. As an example, for DSLs with many key-value pairs (e.g. domain models), one might want to indent everything between curly braces and start a new line before each keyword. Instead of manually listing the keywords for the findKeywords()-method of the IGrammarAccess, GrammarUtil.getAllKeywords() can be used to get all the keywords, so a generic formatting method could look like this:
public class GenericFormatter {

/**
* In your implementation of
* {@link org.eclipse.xtext.formatting.impl.AbstractDeclarativeFormatter#configureFormatting(org.eclipse.xtext.formatting.impl.FormattingConfig)}
* you may call this generic formatting method first. It indents blocks between curly braces and sets a linewrap
* before each keyword. Add your own behavior afterwards, e.g.
*
* keywords = grammar.findKeywords(...);
* for (final Keyword keyword : keywords) {
* config.setNoLinewrap().before(keyword);
* }
*/
public static void genericFormatting(final FormattingConfig config, final IGrammarAccess grammar) {
for (final Pair pair : grammar.findKeywordPairs("{", "}")) { //$NON-NLS-1$ //$NON-NLS-2$
// a space before the first '{'
config.setSpace(" ").before(pair.getFirst()); //$NON-NLS-1$
// indentation between
config.setIndentation(pair.getFirst(), pair.getSecond());
// and a linewrap before the last '{'
config.setLinewrap(1).before(pair.getSecond());
}

// linewrap before all keywords
final Set allKeywords = GrammarUtil.getAllKeywords(grammar.getGrammar());
final List keywords = grammar.findKeywords(allKeywords.toArray(new String[allKeywords.size()]));
for (final Keyword keyword : keywords) {
config.setLinewrap().before(keyword);
}

}
}
It could be called inside the configureFormatting()-method in [NameOfTheDSL]Formatter. Formatting code that overrides this behavior for specific keywords can be added after the call to the generic method like this:
keywords = grammar.findKeywords([keywords w/o wrap before]);
for (final Keyword keyword : keywords) {
config.setNoLinewrap().before(keyword);
}