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