[groovy-user] groovy Command Expression howto do nested expression?

27 views
Skip to first unread message

Nils Petersohn

unread,
Feb 18, 2012, 11:10:28 AM2/18/12
to us...@groovy.codehaus.org
Hello,

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 failes:

Anyone can show me an example how to do this?

import groovy.time.DatumDependentDuration

<pre>
/**
* 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)

}

}

</pre>

the error:

<pre>
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)
......

</pre>

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

http://xircles.codehaus.org/manage_email


Dinko Srkoc

unread,
Feb 18, 2012, 1:24:26 PM2/18/12
to us...@groovy.codehaus.org
On 18 February 2012 17:10, Nils Petersohn <po...@berlin.de> wrote:
> Hello,
>
> 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.

The DSL "difference from today to tomorrow" is resolved like this:

difference(from).today(to).tomorrow

Therefore, `from` is expected to be a property. Likewise, `to` should
be a property.

Here is a piece of code that can work with the DSL given above. Note
that it is written for this specific form and cannot handle anything
else (and written quickly and dirty if I may add).

8<------------------------------------------
class SimpleTest2 { // difference(from).today(to).tomorrow
private fromVal
private vals = [today: new Date(), tomorrow: new Date() + 1]

// this is where a call to method 'today()' ends
def methodMissing(String name, args) {
fromVal = vals[name] // name == 'today'
this
}

// this is where a call to property 'tomorrow' ends
def propertyMissing(String name) {
// we now have all the data and can
// perform our calculation
calc(fromVal, vals[name]) // name == 'tomorrow'
}

// parameter 'from' is the result of invoking
// property 'from'; we have nothing to do here
// as all the action happens later
def difference(String from) { this }

// dummy properties, called as arguments
// to 'difference()' and 'today()' methods
def from = 'from'
def to = 'to'

private calc(fromDate, toDate) {
toDate - fromDate
}
}

def eval(content) {
def shell = new GroovyShell()
def runner = shell.evaluate("{-> $content }")
runner.delegate = new SimpleTest2()
runner()
}

assert eval("x = difference from today to tomorrow") == 1
------------------------------------------>8

Cheers,
Dinko

Nils Petersohn

unread,
Feb 18, 2012, 2:29:30 PM2/18/12
to us...@groovy.codehaus.org
Hello Dinko,

thank you. It works.

But it feels more like a hack than "natural".

I thought the method "difference(Range range)" is waiting until the following dsl text evaluates to that Argument-Type (Range) and passes it then to the method.
So if it evaluates everything in the argument (from today to tomorrow) until its a Range it would "pass" it to the diference Method.
Otherwise it would throw a argument-type-mismatch-exception

Just wondering: Is there a more elegant way of nesting methods in methods in methods ... for a dsl without chaining them ?

for now i think the best available solution for me is with named parameters:
difference from: today, to: tomorrow

with this method:

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

again thank you very much for your intelligent solution :)

best regards,
nils

Dinko Srkoc

unread,
Feb 18, 2012, 9:13:07 PM2/18/12
to us...@groovy.codehaus.org
On 18 February 2012 20:29, Nils Petersohn <po...@berlin.de> wrote:
> Hello Dinko,
>
> thank you. It works.
>
> But it feels more like a hack than "natural".

I guess it depends on what "natural" means for you. One might argue
that the whole "dynamically typed languages" thing is a hack. ;-)

>
> I thought the method "difference(Range range)" is waiting until the following dsl text evaluates to that Argument-Type (Range) and passes it then to the method.
> So if it evaluates everything in the argument (from today to tomorrow) until its a Range it would "pass" it to the diference Method.
> Otherwise it would throw a argument-type-mismatch-exception

I think that would introduce ambiguity. It might not be clear how much
should be evaluated before result is sent to the first method,
depending on different methods' signatures.

As it stands now, `a b c d e` is deterministically and
straightforwardly interpreted as `a(b).c(d).e`.

>
> Just wondering: Is there a more elegant way of nesting methods in methods in methods ... for a dsl without chaining them ?
>
> for now i think the best available solution for me is with named parameters:
> difference from: today, to: tomorrow

That's certainly the simplest and probably the most robust solution.

Cheers,
Dinko

Nils Petersohn

unread,
Feb 19, 2012, 7:42:12 AM2/19/12
to us...@groovy.codehaus.org
with the following rule I don't really agree that it would introduce ambiguity:
a method has only one argument. It evaluates the arguments contents as long as the type of the argument matches. Then it should take the first match as that argument.

But you are right, what happens when this method has more arguments. And what if there are nested methods in nested methods ....
And yes, without brackets you only have the determination in even or odd locations which is insufficient.

Maybe a new "type" of brackets would help to define units of meaning for this case?

best regards,
nils

Nils M. Petersohn
xing.com/profile/Nils_Petersohn
blog.srvme.de
twitter.com/snackycracky
facebook.com/nils.petersohn
myspace.com/electrash

po...@berlin.de
0049 (0)151 40 511 351
skype: nilz_berlin

Kopenhagener Str. 41
10437 Berlin

Wujek Srujek

unread,
Feb 19, 2012, 8:28:12 AM2/19/12
to us...@groovy.codehaus.org
Hi. Nesting, new kinds of brackets - just a thought, how about using closures?

Nils Petersohn

unread,
Feb 19, 2012, 8:33:00 AM2/19/12
to us...@groovy.codehaus.org
difference { from start to end } ? is that what you mean? i think that does not work.
can you specify your thought please.

Wujek Srujek

unread,
Feb 19, 2012, 8:59:31 AM2/19/12
to us...@groovy.codehaus.org
By 'it doesn't work' you mean it doesn't work for you and your use case, or that it is impossible?

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

def s

def from(start) { s = start; this }
def to(end) {
    use(groovy.time.TimeCategory) {
        (s..end)
    }
}

def difference(c) {
    def r = c()
    r.size()
}

difference { from today to tomorrow }

This prints out something and it works.

Nils Petersohn

unread,
Feb 19, 2012, 3:20:49 PM2/19/12
to us...@groovy.codehaus.org
you are right, thank you :D

Dinko Srkoc

unread,
Feb 19, 2012, 6:36:39 PM2/19/12
to us...@groovy.codehaus.org
[ this is just for fun ;-) ]

No need for brackets. How about operators, say like this:

differenc e >>= from today to tomorrow

the code:
8<------------------------------
def today = new Date(),
tomorrow = today + 1

def start

def from(s) { start = s; this }
def to(end) {
use(groovy.time.TimeCategory) { start..end }
}

def difference = new Object() { def rightShift(r) { r } }

difference >>= from today to tomorrow
assert difference.size() == 2
------------------------------>8

Cheers,
Dinko

Reply all
Reply to author
Forward
0 new messages