The Language Applications Grid and Groovy DSLs
A Domain Specific Language (DSL) is a computer programming language specialized to a specific application domain. [1]
The Language Applications Grid uses several domain specific languages:
LSD is basically just Groovy bundled with the LAPPS API modules that are automatically imported into user scripts. The other three can be thought of executable configuration languages. The advantage of a DSL over a data format like Yaml or JSON is that it is executable code that can define functions, store commonly used values in variables, and make use of flow of control and looping statements.
It is all just Groovy
While the syntax may look odd at times, every DSL is a syntactically correct Groovy program. So even though the DSL may look like a configuration file it will actually be compiled in to Java bytecode and executed on the JVM. Consider a statement from the Discriminator DSL:
token {
	uri 'http://vocab.lappsgrid.org/Token'
	description 'A string of one or more characters that serves as an indivisible unit for the purposes of morpho-syntactic labeling (part of speech tagging).
}
The above calls a method named token that takes a closure as its only parameter:
public void token(Closure body) { ... }
The closure contains two statements. The first statement calls the method uri with a single String parameter and the second statement calls the method description with a single String parameter.
Compiling Groovy at runtime
The GroovyShell class makes it easy to compile a String into code that can be executed.
String code = 'println "hello world"'
def shell = new GroovyShell()
def script = shell.parse(code)
script.run()
// prints hello world
Groovy allows us to customize the compiler so we can do things like:
- Add import statements to the script
 - Set a base class for the script
 - Provide a Binding object to the script to inject variables.
 
Metaprogramming
Groovy provides several metaprogramming techniques that assist in writing a DSL. Two of the most useful are:
methodMissing(String,List)allows us to synthesize methods dynamically. This is a powerful technique for creating Builders, that is, objects that build other objects based on some sort of description.- Closure delegates. A delegate is like a super class for a closure. If Groovy can not resolve a method call in the closure’s scope it will look in the delegate for the method.
 
Example DSL
Suppose we want to transform the following simplified Discriminator DSL into a JSON representation.
token {
	uri 'http://vocab.lappsgrid.org/Token'
	description 'A string of one or more characters that serves as an indivisible unit.'
}
sentence {
	uri 'http://vocab.lappsgrid.org/Sentence'
	description 'A sequence of one or more words.'
}
Our DSL processor will do three things:
- Implement a delegate class that provides the 
urianddescriptionmethods. - Parse the code into a Script object.
 - Implement 
script.metaClass.methodMissingto intercept method calls. - Generate the JSON from the constructed data structure.
 
1. The delegate class is simple enough.
The Delegate class will provide the uri and description methods.  Each method takes a String and will save it in a field of the same name.
class Delegate {
	String uri
	String description
	
	void uri(String uri) { this.uri = uri }
	void description(String description) { this.description = description }
}
2. Parsing the code
We have already seen how to do this above.
Script script = new GroovyShell().parse(code)
3. Handle missing methods
def objects= [:]
script.metaClass.methodMissing = { String name, args ->
	Closure cl = args[0]
	cl.delegate = new Delegate()
	cl();
	objects[name] = cl.delegate
}
First we create a HashMap to hold the generated objects. We assume that the first parameter to the missing method will always be a closure, but in practice we would need to so some error checking here.  After running the closure we add the Delegate object to the map. In practice we would do some error checking here as well to make sure any required fields in the delegate were initialized and that fields were initialized with proper values.
4. Write the output
Groovy’s JsonBuilder class makes the final step trivial.
println new groovy.json.JsonBuilder(objects).toPrettyString()
The finished product
Putting it all together the complete DSL processor looks like:
class Dsl {
    public static void main(String[] params)
    {
        String code = new File(params[0]).text
        def objects = [:]
        Script script = new GroovyShell().parse(code)
        script.metaClass.methodMissing = { String name, args ->
            Closure cl = args[0]
            cl.delegate = new Delegate()
            cl();
            annotations[name] = cl.delegate
        }
        script.run()
        println new groovy.json.JsonBuilder(objects).toPrettyString()
    }
}
class Delegate {
    String uri
    String description
    
    void uri(String uri) { this.uri = uri }
    void description(String description) { this.description = description }
}
If we did not implement script.metaClass.methodMissing then Groovy would complain about the missing method token when we tried to run the program on the example input.  Similarly, if we did not add a delegate that implemented uri and description to the closure Groovy would complain about the missing method uri when attempting to execute the closure.
References
- https://en.wikipedia.org/wiki/Domain-specific_language