Jan 31, 2013

MWE2 Workflows using Xtend

If you've ever created an Xtext language you know that the Xtext generator is configured and launched using an MWE2 workflow. The MWE2 language is a simple external DSL to define a workflow by building a graph of JavaBeans objects representing the "workflow components". In addition to the ability to very concisely create and string together JavaBeans, the language also comes with support for:

  • defining properties (of type String or boolean),
  • Ant style string interpolation (which can reference properties as in e.g. "../${projectName}"),
  • a means to inject the properties into matching properties of the created JavaBeans,
  • and a way to call other workflows (with values to use for that workflow's properties).
This pretty much sums up what you can do with the MWE2 language. This is a very limited feature set (which generally is a good thing with DSLs) and has been used with great success in the Xtext project.

Recently I thought to myself: What would an MWE2 workflow, as used in an Xtext project, look like if it were written in Xtend? Xtend is also very well suited for concisely creating object graphs and also has support for string interpolation using rich strings. As you can see in the following Xtend defined workflow it really looks quite similar to the original MWE2 file (for brevity I have skipped the import list and also many of the generator fragments):

Some explanations and comments:

  • The @Binding annotation was introduced here to denote that the particular field should be injected into JavaBeans when passed to autoInject() or when obtained from getInstance(). There would certainly be other (maybe more reasonable) ways to do this, but this best resembles what an MWE2 workflow looks like.
  • As you can see the += operator is used quite extensively. This actually required a bit of a hack since it only works seamlessly with EMF-style JavaBeans, which have a getter for collection valued properties (returning a mutable collection) rather than an add-method as in standard JavaBeans. To solve this I introduced an Xtend extension method returning a Collection object on which only add(Object) can be invoked and which will call the corresponding JavaBeans add-method.
  • The JavaBeans are created and injected in different ways (using the normal constructor which can be injected explicitly using autoInject() or using the getInstance() method returning an injected instance). There is no particular reason for not doing it the same way everywhere.
  • A static main() method is required in order to be able to launch the workflow.
Now, why would you want to do this? Actually, I don't think there is any really good reasons, this was mostly a fun toy project. MWE2 works great for most people. It can certainly be harder to debug, takes longer to launch, and has worse tooling, but it works very well and there is a smaller risk of getting something wrong. If however Xtend had existed back in the days when MWE2 was "invented" (to replace the cumbersome XML based MWE1), I think it may very well have ended up as an internal Xtend DSL instead.

I think there is actually some people who might still be interested in writing MWE2 workflows in Xtend: If you set out to implement a whole family of Xtext based DSLs you will very likely want the workflows to be synchronized between them. And you will also quite likely have some custom generator fragments you want to use. This can be done with MWE2, but with Xtend you will certainly have more options. Let's say you for instance have some languages for which you want to generate the full IDE (with editor, etc.), but also some languages for which you basically only need the parser and serializer. How would you handle this with a single MWE2 workflow? In Xtend this would basically only be a matter of declaring a property and then some if-statements around the statements where UI-only generator fragments are added. I am sure more use cases can be concieved.

No comments:

Post a Comment