Jim Driscoll's Blog

Notes on Technology and the Web

Archive for April 2012

Using optional parenthesis in Groovy to your advantage

leave a comment »

Now that we’ve discussed adding default methods to your scripts with a BaseScript, it’s time to talk about one way to hide the fact that you’re calling a method:   Groovy allows you to omit parenthesis in cases where there is a parameter, and there is no ambiguity in use.

We can use this optional parens feature of Groovy to create more natural looking DSLs, and with judicious use of method chaining, we can create a simple grammar with very little code.  Let’s say that I want to create a simple DSL that moves a marker around in a confined space, and that the language should look similar to Logo, with a series of commands all in a line.  Since I don’t enjoy trigonometry,  I’ll forgo the usual two vector (x,y) space of Logo for a simpler single line.  That means than instead of needing to allow for commands like “forward 10 right 90 forward 6”, I can just use two verbs – “forward” and “back”.  We’ll add one more, “traveled”, to report total progress.  So, to define our DSL in a simple manner, I want to accept commands like “forward 10 forward 6 back 3 traveled”.

So, we can start by creating a simple BaseScript with the three verbs (let’s do it this time in Groovy, I’m feeling lazy):

 

abstract class BaseScript extends Script {

  int distance = 0;
    
  def forward(int i) {
    distance += i
    println "moved forward "+i
    return this
  }
    
  def back(int i) {
    distance -= i;
    println "moved back "+i
    return this
  }
    
  def traveled() {
    println "total distance traveled " + distance
    return this
  }

}

 

The script is storing the distance as an instance variable, and then all the methods are acting on that variable in some way.  Also, note that each method returns the current script object, enabling method chaining.

Once this is set up, we can use the following code to execute a script:

  CompilerConfiguration config = new CompilerConfiguration();
  config.setScriptBaseClass("groovybasescriptlogo.BaseScript");
  GroovyShell shell = new GroovyShell(config);
  Script script = shell.parse("forward 10 back 3 forward 5 traveled()");
  script.run();

 

The output when we run this is as you’d expect:

moved forward 10
moved back 3
moved forward 5
total distance traveled 12

 

But note there’s a catch here:  because traveled is a method without a parameter, there still needs to be a set of parenthesis at the end.  The reason why is also a clue as to how we can fix it.  Without the parenthesis, the traveled keyword will be interpreted as a binding variable instead of a method.  Since we’ve already covered how to handle Binding variables, we can change the code by using a Binding class.  First, eliminate the first bolded line in the BaseScript – that changes the distance variable from an instance variable on the Script class to a Binding variable.  You can also remove the traveled() method from the BaseScript as well, or leave it in there, to cover the case of using a parenthesis.  Then create a Binding class like so:

 

public class SimpleBinding extends Binding {
    
  int distance = 0;
  Script script;
    
  public SimpleBinding(Script script) {
    super();
    this.script = script;
  }

  public Object getVariable(String name) {
    if ("distance".equals(name)) {
      return distance;
    }
    if ("traveled".equals(name)) {
      System.out.println("total distance traveled " + distance);
      return script;
    }
    return super.getVariable(name);
    }
        
  public void setVariable(String name, Object value) {
    if ("distance".equals(name)) {
      distance = (Integer)value;
      return;
    }
    super.setVariable(name, value);
  }
    
}

 

So, the distance variable moves from the BaseScript to the Binding.  In the setVariable, add the ability to set the distance variable.  That covers the usage in the BaseScript.  We’ve also moved code around traveled to the Binding as well:  Now, when traveled is used without a parenthesis, it will go to the binding, print out the message, and still return the Script object (which is passed into the Binding at creation).  To use this code, just modify the calling code like so:

 

  CompilerConfiguration config = new CompilerConfiguration();
  config.setScriptBaseClass("groovybasescriptlogo.BaseScript");
  GroovyShell shell = new GroovyShell(config);
  Script script = shell.parse("forward 10 back 3 forward 5 traveled");
  script.setBinding(new SimpleBinding(script));
  script.run();

 

The output remains the same.  Doing this now eliminates the need for the trailing parenthesis on the traveled keyword.  That concludes this topic – feel free to ask any questions, below – I know this example is a little more involved than previous ones, but the concepts are still pretty simple, even if there’s more code on the page.  The next posting will cover a different way to handle adding verbs dynamically, via the invokeMethod mechanism.

 

Advertisements

Written by jamesgdriscoll

April 29, 2012 at 1:48 PM

Posted in DSL, Groovy

Adding default methods with a Groovy BaseScript

with one comment

Having talked about nouns in our DSL by adding new variables from the Binding, it’s time to talk a little bit about verbs – there’s a number of ways to do this in Groovy, so let’s start by looking at a BaseScript for Scripts.

A BaseScript is, effectively, the super class for the Script that’s executed dynamically from GroovyShell.    To use a BaseScript, create an abstract class that extends Script, implementing the methods you want to enable in your scripts.  Here’s a very simple example:

package basescripttest;

import groovy.lang.Script;

abstract public class BaseScript extends Script {
    void hello(String name) {
        System.out.println("hello "+name);
    }
}

This BaseScript will provide a single method to your script: hello – taking a single argument, a String.  Note that you could just as easily use a Groovy class for the same purpose:

package basescripttest

abstract class BaseScript extends Script{
    def hello(name) {
        println "hello $name"
    }
}
 

Which does the same thing.  To use it in a program, use the CompilerConfiguration class, which allows various modifications of how the GroovyShell parser acts at runtime.  Note that you need to pass the fully qualified name of the class as a String to the setScriptBaseClass.

CompilerConfiguration config = new CompilerConfiguration();
config.setScriptBaseClass("basescripttest.BaseScript");
GroovyShell shell = new GroovyShell(config);
Script script = shell.parse("hello('Jim')");
script.run();

 

So, to recap, we’re executing the following script:

hello('Jim')

Which is outputting the following text to stdout:

Jim

In this example, we just use the (now default provided) hello function in the BaseScript from within the provided class. This is a very simple example – we’ll go into more detail next time.

 

Written by jamesgdriscoll

April 22, 2012 at 8:21 PM

Posted in DSL, Groovy

Groovy Bindings – or adding keywords to your DSL

leave a comment »

Having now covered executing Groovy scripts in your Java programs, it’s time to make our first DSL modification.   One thing that would be very handy would be to add keywords to our new language.  We can do that by passing a custom Binding to our Groovy Script.

The Binding in a Groovy Script offers a fairly interesting feature – it’s an outermost scoping which doesn’t require a variable definition.  So, in a Groovy Script, I can say:

name = 'Jim'
println "hello $name"

 

In this code, I’m not actually declaring a local variable called name – instead, I’m creating a slot in the Groovy Binding with a name of name, and a value of a String object (‘Jim’).  Under the hood, the default Binding that Groovy gives us keeps a map – “name” will be the key, and “Jim” will be the value.  Since this is effectively an outermost scope, you can shadow the Binding values with local values as well – more on that later.

We can use this Binding in our code to pass values into and out of the script in a way that’s hopefully fairly easy for an enduser to understand.   You can use this facility to provide:

  • Constant values (e.g., the name of the current user, provided as a String)
  • Read only variables  (e.g., the current date and time, provided as a String, or some other object)
  • Default values for variables (e.g., The name of the application, as a String, overridable at runtime)

Creating a Binding is pretty straightforward – just extend the existing Binding class, overriding the setVariable and getVariable methods.

public class MyBinding extends Binding {
            
    public Object  getVariable(String name) {
        if ("name".equals(name)) {
            return "Jim";
        } else if ("date".equals(name)) {
            return new Date();
        }
        return super.getVariable(name);
    }
    public void setVariable(String name, Object value) {
        if ("date".equals(name) || "name".equals(name)) {
            throw new RuntimeException("variable "+name+" is read only");
        }
        super.setVariable(name, value);
    }
}

For this example, we’re creating two read only Binding variables – “name”, a constant String value equal to my name, and “date”, a variable value equal to the current date, returned as a Date object.   When the variables are used in a access context, getVariable is called, and we return the results we want the user to see.  When the variables are used in a modify context, we throw an exception if it’s one of the protected values.  Otherwise, we fall through to the underlying Binding class, which you’ll recall I mentioned just uses an underlying Map.  To use this new Binding, set it with the setBinding method of Script.  Then, the variables are accessible from the script.  Like so:

String runMe =
     "println 'hello '+ name \n"+
     "println 'it is now: ' +date";
GroovyShell shell = new GroovyShell();
Script script = shell.parse(runMe);
script.setBinding(new MyBinding());
script.run();

To recap, the script that’s run looks like this:

println 'hello ' + name
println 'it is now: ' + date

 

The output of this when run:

hello Jim
it is now: Sat Mar 17 11:19:27 PDT 2012

 

This is a pretty powerful technique, and I’ve only scratched the surface.  We’ll revisit more about this in later posts.

Written by jamesgdriscoll

April 14, 2012 at 12:40 PM

Posted in DSL, Groovy