The Xtext parser generator is generating much more than a parser—otherwise ANTLR by itself would be a simpler solution. As noted in my post on migrating the Xtext grammar, just by deploying the plugins generated by Xtext, a basic outline is provided while editing the defined language.
For my analyzer, I have to add checks that flag errors for programs that contain multiple definitions for certain types of structures. The legacy language allows a single name to be associated with multiple, different structure definitions as the program executes, but part of the upgrade will cause this to be illegal, so it's the job of the static analyzer (parser) to detect this case so the programmers don't have to search out and test each program manually.
The Xtext manual section on Validation includes Custom Validation describes the classes involved in implementing validations for the language. In order to get the Xtext customization framework for validation, add
<fragment class="org.eclipse.xtext.generator.validation.JavaValidatorFragment"/>
to the
GenerateLanguageName
.mwe file in the parser project. Unfortunately, the Java-based checks are not so easy to write as the oAW Check language. One of the nice things about oAW was the content assist—the editor for Check knows the AST model and makes it easy to write checks that work. There's another fragment to add to the MWE file in order to get the Check capability:
<fragment class="org.eclipse.xtext.generator.validation.CheckFragment"/>
Since the TMF Xtext plugins don't seem to include a Check editor with content assist, it's actually more convenient to write the validations in Java. Referring to the LanguageName
.ecore file in the visual editor (should open when you double-click the ecore file) makes it pretty easy to see the types and their features when working on validations.
I just needed to add two validations (so far). The Java is pretty small, but it took me a little while to get comfortable with the CST classes.
/**
* Issue warning if the View object is using the dynamic resolution syntax
* @param view object to be validated
*/
@Check
public void checkViewNotDynamic(View view) {
if (null != view.getDynamic())
warning("View defined with dynamic target", PdlPackage.VIEW__DYNAMIC );
}
/**
* Issue error if there there exist multiple Join objects identified by the same name
* @param join one of the Join objects in the current ProcessDefinition
*/
@Check
public void checkJoinUnique(final Join join) {
final String name = join.getName(); // This name must not be used to identify any other Join object
// Now loop on all the other elements that are in the ProcessDefintion
for (EObject sibling : ((ProcessDefinition) join.eContainer()).getCommands()) {
// Check for another object instance that's a Join with the same name as the one being validated
if (join != sibling && sibling instanceof Join && name.equals(((Join) sibling).getName()))
error("duplicate join '" + name + "'", PdlPackage.JOIN__NAME);
}
}
These two validators are picked up by the Xtext framework and invoked by the editor and the generator. The warnings and errors show up in the editor as well as the Package Explorer, Problems and other views.
With the parser doing the right thing, I copied the Xpand template that I wrote under oAW into the generator project as templates/Template.xpt. The workflow/LanguageNameGenerator.mwe had a problem—it got a NoClassDefFoundError because it specified a class name in the register element with an initial lowercase letter. I don't know if that was because of something I did or a bug in the project wizard. Changing the MWE file was all that was needed to test the template. It's the same as oAW, which is to say it's a convenient way to traverse the CST and emit code.
«DEFINE main FOR ProcessDefinition»
«FILE "test"»
«EXPAND viewDef FOREACH commands.typeSelect(View)»
«EXPAND joinDef FOREACH commands.typeSelect(Join)»
«ENDFILE»
«ENDDEFINE»
//In the output file, there will be one line for each instance of Join
//Within the tempate, the properties of the instance are accessed like
//local variables.
«DEFINE joinDef FOR Join »
// Pay no attention to the man behind the curtain named «name»
«ENDDEFINE»
«DEFINE viewDef FOR View »
«IF null!=name && null!=source -» «REM» Generate mappings for statically-defined views «ENDREM»
views["«name.toUpperCase()»"] = joins.createView("«source.toUpperCase()»");
«ENDIF -»
«ENDDEFINE»
You'll notice that the template language uses guillemets to enclose the executable instructions. To make sure that you read all of the documentation, the authors only tell you that the characters are bound to CTRL-< and CTRL-> at the end. The Xpand editor was pretty nice to use in oAW. I haven't tried it yet in TMF, partly because it wasn't installed. I'm not quite sure how I got the runtime plugin for Xpand installed—perhaps p2 resolved a dependency. I'm pretty sure that once I install the Xpand UI from the
M2T Xpand update site, I'll get the content assist that I used when I was originally developing the template.