Groovy for Domain-specific Languages - Second Edition

(nextflipdebug2) #1
Chapter 8

[ 213 ]

What we have is a classic chicken and egg situation. There is nowhere in the pattern
that we can start where we will not encounter the need to reference another pattern
class that is not created yet. The solution that you are seeing in the preceding code is
where we use ClassHelper.make(String) to make a ClassNode, which references
the as yet uncompleted class. What we end up with at the end of this builder method
is an AST structure as follows for the state context class:


ClassNode class StateContext
|_PropertyNode def state
|_ConstructorCallExpression new InitalState
_ArgumentListExpression this

Our DSL does not have a specific means to declare an initial state, so
we just assume the first state encountered in the DSL will be the initial
default state. The StateMachineModel class implements this via the
getInitialState method.

This newly created ClassNode is added to the AST from the module. Now
we need a class to implement the client class for the state machine pattern.
We won't create a new class node for this. Instead, we will make use of the class
created by the compiler for the module. For instance, if our DSL code is in
LEDToggleState.groovy, we will already have a class LEDToggleState in the AST,
which is in fact the class that has the run method from which we parsed the state
machine code.


The next method we encounter in StateMachineBuilder is updateChildClass.
This method adds the elements necessary to the DSL class to turn it into the client
class of the state machine:


void updateClientClass() {
buildClientContextProperty()
buildClientStateGetter()
buildClientEventMethods()
}

def buildClientContextProperty() {
classNode.addProperty(
new PropertyNode(
"context",
Modifier.PUBLIC,
ClassHelper.make(contextClassName),
ClassHelper.make(className),
new ConstructorCallExpression(
Free download pdf