Domain Specific Languages in Groovy demonstrates how Groovy shines when it comes to domain specific languages.
About Domain Specific Languages
A Domain Specific Languages[1] (DSL) is a language tailored to describe a part of a specific domain. An example of a DSL are regular expressions[2]. Regular expressions provide a language that allow a user to specify which strings are accepted. Formally the domain for regular expression are finite-state machines[3] that accept strings. A regular expression describes a finite-state machine.
A DSL can enhance productivity enormously. By providing an abstraction over a specific domain, DSL’s allow a user to be more expressive. This expressiveness can cut the development time. A DSL also bridges the communication gap between a developer and a domain expert. The book Domain-specific Languages[4] by Martin Fowler and Rebecca Parsons is a great exposition about the subject.
About Groovy
Groovy[5] is a dynamic language running on the Java Virtual Machine (JVM). It takes the power of the JVM and provides a modern programming language for it. Groovy is inspired by successful languages like Python and Ruby. It rids most of the tedious boilerplate from Java code, replacing it with powerful primitives. You can try Groovy in your browser at http://groovyconsole.appspot.com/.
DSL’s in Groovy
Groovy uses a lot of domain specific languages. A notable example is the MarkupBuilder[6]. This class provides a DSL for the creation of xml. For example, the following code
new MarkupBuilder().root {
a( a1:'one' ) {
b { mkp.yield( '3 < 5' ) }
c( a2:'two', 'blah' )
}
}
will produce
<root>
<a a1='one'>
<b>3 < 5</b>
<c a2='two'>blah</c>
</a>
</root>
Custom DSL
Groovy allows the introduction of custom DSLs. Groovy provides a BuilderSupport[7] class. By extending this class a DSL like the MarkupBuilder can be created. We will demonstrate the use of BuilderSupport with the following example.
Let’s assume that we have a Robot. The robot is controlled via the executeCommand method. The executeCommand method accepts command codes. There are three of them 0x37, 0x51 and 0x88 with the following interpretation.
| Code | Command |
| 0×37 | Move Forward |
| 0×51 | Turn Left |
| 0×88 | Turn Right |
It would be tedious to perform complex movements with this robot. Multiple calls to the executeCommand method, all strung together, are necessary to do something trivial as navigate around a room. Instead, one would rather say something like.
do(4) {
forward()
left()
}
We can start bridging the syntactic gap by introducing a few auxiliary classes. The following class diagram shows the participants. It combines the composite[8] pattern with the visitor[9] pattern.
The contract of the Program interface is to execute itself with a provided visiting Robot. There are two concrete programs types. There are BasicPrograms, one for each command, i.e. ForwardProgram, LeftProgram and RightProgram. These Programs execute the corresponding command on the provided Robot. The other concrete Program is the DoProgram. The DoProgram is composed of other Programs. When a DoProgram is asked to execute with a Robot, it asks it’s component Programs in turn to execute with the Robot.
The Program interface and its implementations help to express the intention of a user of the Robot. But this convenience comes at a price. There is a lot of boilerplate code necessary to express a program to navigate the robot around a room.
DoProgram program = new DoProgram(4) program.add(new ForwardProgram()) program.add(new LeftProgram())
This is where DSL’s step in.
ProgramBuilder
We will now use the power of BuilderSupport[7] to go the extra mile and close the syntactic gap. We will use BuilderSupport to create a ProgramBuilder class.
The BuilderSupport class has five abstract methods. These methods are subdivided in two categories. On the one hand there are various createNode methods. On the other hand is the setParent method.
The createNode methods only vary in their signature. The all have a name parameter in common. The createNode methods can also sport an value parameter, an attributes parameter, both parameters or no extra parameter. We chained the various methods to a single method, so we will focus on the createNode(String name) method.
The various createNode methods are called when a method is executed which is not defined. The name parameter is set to the missing method name. For example, in the following code
new ProgramBuilder().forward()
the createNode method is called with the name parameter set to "forward". A createNode method should return an object. In this case it would return a ForwardProgram.
The setParent(Object parent, Object child) method is called when a missing method is called with a closure[10]. First the createNode method should return an object. This object will act as parent parameter for the subsequent calls to createNode from the closure. I.e. every time a createNode method is called, the returned object is the child parameter in a call to setParent.
A code example should clear things up. The call sequence of the following code is determined.
ProgramBuilder b = new ProgramBuilder()
b.do() {
b.forward()
}
The call sequence will be: createNode("do"), createNode("forward") followed by a call to setParent(parent,child) where parent is the return value of the first call to createNode and child is the return value of the second call to createNode. In this example createNode("do") would return a DoProgram, createNode("forward") would return a ForwardProgram and setParent(parent,child) would call parent.add(child).
By using the various createNode method on can imagine that the following code can be used to navigate a Robot around a room. If you would like to look into the specifics the code can be found at GitHub.
ProgramBuilder b = new ProgramBuilder()
Program program = b.do(4) {
b.forward()
b.left()
}
Robot robot = new Robot()
program.executeWith(robot)
Conclusion
Groovy offers ample opportunities to introduce domain specific languages in a project. The BuilderSupport class is a great tool to create domain specific languages with. Domain specific languages can bridge the communication gap between a domain expert and a developer.
References
- http://en.wikipedia.org/wiki/Domain-specific_language
- http://en.wikipedia.org/wiki/Regular_expression
- http://en.wikipedia.org/wiki/Finite-state_machine
- http://martinfowler.com/books.html
- http://groovy.codehaus.org/
- http://groovy.codehaus.org/api/groovy/xml/MarkupBuilder.html
- http://groovy.codehaus.org/api/groovy/util/BuilderSupport.html
- http://en.wikipedia.org/wiki/Composite_pattern
- http://en.wikipedia.org/wiki/Visitor_pattern
- http://en.wikipedia.org/wiki/Closure_(computer_science)

