Groovy is a great language to write DSL implementations. The Groovy syntax allows for example to leave out parenthesis or semi colons, which results in better readable DSL (which is actually Groovy code).
To run a DSL script we can use the GroovyShell class and evaluate the script. By default the script is evaluated with an instance of groovy.lang.Script
class. But we can extends this Script
class and write our DSL allowed methods, which can then be used by the DSL script. We pass our own Script
class to the GroovyShell
with an CompilerConfiguration
object. The CompilerConfiguration
allows us to set a new base script class to be used.
The following sample is a simple DSL to change the state of a Car
object. Notice we implicitly access the Car
object that is passed to the GroovyShell
via a binding. The custom CarScript
class can access the car object via the binding and change it's state.
import org.codehaus.groovy.control.CompilerConfiguration // Simple Car class to save state and distance. class Car { String state Long distance = 0 } // Custom Script with methods that change the Car's state. // The Car object is passed via the binding. abstract class CarScript extends Script { def start() { this.binding.car.state = 'started' } def stop() { this.binding.car.state = 'stopped' } def drive(distance) { this.binding.car.distance += distance } } // Use custom CarScript. def compilerConfiguration = new CompilerConfiguration() compilerConfiguration.scriptBaseClass = CarScript.class.name // Define Car object here, so we can use it in assertions later on. def car = new Car() // Add to script binding (CarScript references this.binding.car). def binding = new Binding(car: car) // Configure the GroovyShell. def shell = new GroovyShell(this.class.classLoader, binding, compilerConfiguration) // Simple DSL to start, drive and stop the car. // The methods are defined in the CarScript class. def carDsl = ''' start() drive 20 stop() ''' // Run DSL script. shell.evaluate carDsl // Checks to see that Car object has changed. assert car.distance == 20 assert car.state == 'stopped'