2010-02-23 77 views
2

我正在寻找创建一个新的DSL使用groovy,但我很难找出最佳的方式来阅读dsl的groovy。我希望用户能够创建一个dsl并实际运行dsl,而无需与应用程序代码进行交谈。Groovy阅读/运行DSL

game.groovy(如DSL):

import com.foo.groovygame.Game 

go north 10 
search 
find ana 
turn right 

我希望用户能够说:

groovy game.groovy 

,它会运行游戏。我知道我可以这样做:

groovy Game game.groovy 

但我宁愿能够直接运行dsl。

我注意到this blog笔者使用

Ronald.init(this) 

我可以做这样的事情,并有init方法处理来自DSL的比赛,但我想知道,这是最好的方法?它似乎有点马虎。我真的很喜欢处理这个的红宝石方式。您可以简单地创建一个dsl并在dsl中指定require'foo'。

回答

1

我今天花了一些时间试验完成你所描述的不同方式,并且我决定在你所链接的“完美风暴”博客上提出的建议可能是最直接,最不复杂的。我的第一个想法是在Game类中创建一个静态初始化器,它将自动处理初始化游戏,但不幸的是,尽管Game类只是从'import'语句加载的(如通过运行-verbose:在JAVA_OPTS中设置的类),静态初始化程序不会执行,直到以某种方式引用Game类为止。这需要您的game.groovyDSL中的new Game()行。

即使假设你没有问题,处理游戏DSL中函数和属性的唯一方法是以某种方式为游戏类添加支持。在静态初始化器中,您将无法直接访问游戏类,但可以访问其超类:groovy.lang.Script。您可以将方法go()search()添加到Script.metaClass,但是您将其添加到Script的所有实例中,这几乎肯定不是您想要的。

这导致需要用游戏DSL作为参数调用一些方法Game,la Game.init (this)。有一两件事可以做,使之一点视觉上清洁剂是静态地导入游戏的init方法:

import static com.foo.groovygame.Game.init 

init (this); 
go north, 10 
... 

最后一点:你仍然需要在你的DSL,这意味着法之间的逗号使用Groovy语法参数,要求法无括号参数等:

go north, 10 
search() 
find ana 
turn right 

我很感兴趣,如果其他人能够拿出解决方案,清洁剂,甚至只是一些方法来使静态初始化,而不必被执行请参阅封闭类。

1

我真的不用疯狂使用编译器语法来实现DSL--对不起,我知道一些人喜欢它,我很乐意承认这是一个可爱的技巧,但是编写自己的解析器非常容易,为什么不呢做它?然后,您的文本中不会有随机逗号和下划线。

下面是一个简单的窍门,我用来实现一个简单的语法像你描述的一个:

首先,看你的命令 - 注意,大部分是在“动词名词PARAMS”格式

此映射真的很好地方法名,对象名,则params

所以一个很好的程序是:

split sentence into string array s 

for a line with a single word (if s.length == 1): 
    instantiate an object with that name 
    call a default method on that object 
    done 
for a line with more than one word 
    instantiate the object s[1] 
    call method s[0] with s[2...] as parameters 
    done 

这种简单的5-10左右线解析r将解决您的许多DSL类型问题。除此之外,您可以轻松添加功能:

如果参数(2 ...)采用“name = value”形式,则扫描名为“name”的参数并传递该参数的“value”。这可能不适用于这种特定情况,但可以适用于其他用途。

如果您的单词命令需要参数,则尝试将s [0]实例化为一个类,即使有多个单词。如果失败,请回到上面的多字算法。

我有一种情况,在实例化后需要保持物体。我使用的语法:

find person:ana 

,并从那时起(语法可以通过保持一个表映射全日空的人,一起试图实例化对象检查该表可以固定回原来的语法) ,ana是person类的一个实例(换句话说,在实例化“person”并调用方法“find”之后,我将该person对象存储在名为“ana”的散列中,下次他们使用命令如:

talk ana 

它会搜索散列f首先,抓住存储在那里的对象,并在该现有对象上调用“talk”(在这一点上,它可以检查ana是否设置了“发现”标志,否则它可能会返回不同的消息)。通过这种方式,您可以拥有多个朋友,每个朋友都拥有自己的所有状态信息。

这个系统有一些限制,但仍比Ruby风格的DSL灵活得多,实际上并不难实现。

0

RTBarnard的答案似乎是最好的。脚本执行后进行一些处理的最佳方式是什么?

Ronald.init this 

go right 
back ten 
...etc... 

所有的DSL已经被处理后,再罗纳德应该做一些额外的工作,但不必说一些诸如Ronald.run()用户。