0
votes

i am developing a nice little DSL with Groovy.

I really like the Command Expression with higher order functions.

with little code i can do:

timerange = from today to tomorrow

this is actually

timerange = from(today).to(tomorrow)

but now i would like to do something like this:

difference = difference from today to tomorrow

which should result in something like this:

difference = difference(from(today).to(event.start))

I always get the error:

No such property: from for class: Script1.

Here is a test class with a main Method. The third assertion fails:

Anyone can show me an example how to do this?

import groovy.time.DatumDependentDuration

/**
 * Created by IntelliJ IDEA.
 * User: nils
 * Date: 2/18/12
 * Time: 4:41 PM
 */
class SimpleTest {

    def static today = new Date();
    def static tomorrow = new Date() + 1;

    def loadDSL(Closure cl) {

        cl.delegate = this
        return cl()

    }

    def toMethod = { date ->
        [to: { timeThing ->
            if (timeThing instanceof Date) {
                use(groovy.time.TimeCategory) {
                    (date..timeThing) //return Range
                }
            }
        }]
    }

    def from(Date date) {
        toMethod(date)
    }

    def difference(Range range) {
        range.size() //for the sake of simplicity
    }

    static void eval(dslContent, assertion) {
        SimpleTest runner = new SimpleTest()
        def dsl = """
      run {
        ${dslContent}
      }
    """

        def binding = new Binding()
        binding.run = { Closure cl -> runner.loadDSL(cl) }

        binding.today = today;
        binding.tomorrow = tomorrow;

        GroovyShell shell = new GroovyShell(binding)
        shell.evaluate(dsl)
        assert binding.variables.x == assertion

    }

    static void main(String[] args) {
        eval("x = from today to tomorrow", (today..tomorrow))
        eval("x = difference(from(today).to(tomorrow))", 2)
        eval("x = difference from today to tomorrow ", 2)

    }

}

this here is the complete exception:

Exception in thread "main" groovy.lang.MissingPropertyException: No such property: from for class: Script1
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:50)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:231)
    at Script1$_run_closure1.doCall(Script1.groovy:3)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:883)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
    at Script1$_run_closure1.doCall(Script1.groovy)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:883)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:39)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:54)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
    at SimpleTest.loadDSL(SimpleTest.groovy:17)
    at SimpleTest$loadDSL.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:42)
    at SimpleTest$loadDSL.call(Unknown Source)
    at SimpleTest$_eval_closure2.doCall(SimpleTest.groovy:48)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:272)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:883)
    at groovy.lang.MetaClassImpl.invokePropertyOrMissing(MetaClassImpl.java:1099)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1055)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:883)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:66)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:46)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:141)
    at Script1.run(Script1.groovy:2)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:580)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:618)
    at groovy.lang.GroovyShell.evaluate(GroovyShell.java:589)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrap.invoke(PogoMetaMethodSite.java:247)
    at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.call(PogoMetaMethodSite.java:64)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:116)
    at SimpleTest.eval(SimpleTest.groovy:54)
    at SimpleTest$eval.callStatic(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallStatic(CallSiteArray.java:50)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:157)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callStatic(AbstractCallSite.java:169)
    at SimpleTest.main(SimpleTest.groovy:62)
2

2 Answers

1
votes

If you add a getDifference method to the returning range, you can call difference at the end of your expression, but other than that, groovy won't parse difference from start to end as difference(from(start).to(end)), but rather as difference(from).start(to).end

    def toMethod = { date ->
        [to: { timeThing ->
            if (timeThing instanceof Date) {
                use(groovy.time.TimeCategory) {
                    def range = date..timeThing
                    range.metaClass.getDifference = { difference delegate } 
                    return range
                }
            }
        }]
    }

And a small change in your tests:

    static void main(String[] args) {
        eval("x = from today to tomorrow", (today..tomorrow))
        eval("x = difference(from(today).to(tomorrow))", 2)
        eval("x = from today to tomorrow difference", 2)

    }

Is two and half years later too late?

0
votes

Use named parameters: difference from: today, to: tomorrow

def difference(args){
    Math.abs(args.from - args.to)
}

See more on the Groovy mailing list discussion [groovy-user]: groovy Command Expression howto do nested expression?