2011-09-23 117 views
2

我正在編寫一個執行爲Script的DSL;它有各種不同的語法位。例如,對於需要關閉的「foo」關鍵字,我有一個FooSyntax類,並用「該語法的一個實例評估閉包」。這很好,例如Groovy腳本如何與propertyMissing交互?

bar = thing {} // make a thing 
baz = foo { 
    mykeyword bar 
} 

將名爲bar的東西傳遞給調用FooSyntax#mykeyword

我想添加一些更好的錯誤消息,當有一個未知的變量引用。這表現爲MissingPropertyException,所以我現在的方法是將propertyMissing方法添加到FooSyntax。這確實適用於缺少的變量。

不幸的是,它打破了上面的例子:bar成爲一個缺少的屬性,而不是落入到綁定。爲什麼添加propertyMissing導致Binding不被諮詢? (這是否與Closure的解決策略有關?)我該如何解決這個問題?

您可以在https://gist.github.com/1237768

+0

你有你的代碼執行這個腳本,所以我可以試試看,看看我能看到什麼是錯的? –

+0

我添加了一個鏈接到Gist可以玩。該腳本似乎掛在http://groovyconsole.appspot.com上,但在命令行罰款(我使用1.8.1)上運行。 –

回答

2

我將我的答案委託給我評論的要點。基本上,您不應該使用with()方法對FooSyntax委託人執行封閉。對於未來的參考,標準的做法是:

def foo(Closure cl) { 
    def f = new FooSyntax() 
    def c = cl.clone() 
    c.delegate = f 
    c.call() 
} 

您可以通過更改分辨率策略上封閉,像這樣微調行爲:

c.resolveStrategy = Closure.DELEGATE_FIRST 

但在這種情況下,你希望默認Closure.OWNER_FIRST到確保首先查詢綁定。

0

玩這個了示例腳本由於酒吧沒有被宣佈(剛分配),它被擊中的財產丟失,因爲沒有在已定義的酒吧腳本或語法類。

在你的例子中,我認爲你想實現一個方法缺少。在你的場景中,你試圖用非Thing類型來調用Foo.myKeyword。所以這實在是一個缺失的方法,而不是缺失的屬性。

我已經改變了你的腳本,改變了propertyMissing爲methodMissing併爲foo添加了def,並且還定義了bar作爲String。

class Thing { } 

class FooSyntax { 
    def myKeyword(Thing t) { println "Hello Foo " + t.toString(); }  
    def methodMissing(String name, args) { 
     println "no method named ${name} for ${args} exists" 
    } 
} 

class ScriptSyntax { 
    def foo(Closure cl) { 
     def f = new FooSyntax(); 
     f.with cl 
    } 
    def thing() { new Thing() } 

    def dispatchKeyword(String methodName, Object args) { 
     invokeMethod(methodName, args) 
    } 
} 

def runner(String text) { 
    def source = new GroovyCodeSource(text, "inlineScript", "inline.groovy") 

    def script = new GroovyClassLoader().parseClass(source).newInstance(binding) as Script 
    def dsl = new ScriptSyntax() 
    script.metaClass.methodMissing = { name, args -> dsl.dispatchKeyword(name, args) } 
    script.run() 
} 

runner("""def bar = thing() 
def baz = "not a thing" 
foo { myKeyword bar } 
foo { myKeyword baz }""") 

Output: 
Hello Foo [email protected] 
no method named myKeyword for [not a thing] exists 
+0

我明白,如果你添加一個'def',它可以正常工作。但是,我想避免在可能的情況下向DSL的用戶引入'def'。我認爲發生的事情是有某種默認的'propertyMissing'或'methodMissing'被越權覆蓋?此外,我想錯誤讀取「沒有變量baz定義」,而不是「沒有方法命名myKeyword」。 –