我正在寻找使用groovy创建一个新的DSL,但是我很难找到最好的方法来让groovy读取dsl。我希望用户能够创建一个dsl并实际运行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'即可。
答案 0 :(得分:1)
我今天花了一些时间尝试不同的方式来完成你所描述的内容,并且我认为你所链接的“完美风暴”博客上的建议可能是最直接和最错综复杂的。
我的第一个想法是在Game类中创建一个静态初始化程序,它会自动处理初始化游戏,但不幸的是,即使Game类只是从'import'语句加载(通过运行 - 来证明 - verbose:在JAVA_OPTS中设置的类),静态初始值设定项在以某种方式引用Game类之前不会执行。这需要new Game()
DSL中的game.groovy
行。
即使假设您对此感到满意,处理游戏DSL中的功能和属性的唯一方法是以某种方式添加对游戏类的支持。在静态初始化程序中,您无法直接访问游戏类,但您可以访问其超类:groovy.lang.Script
。您可以将go()
和search()
等方法添加到Script.metaClass
,但是您要为Script
的所有实例添加它们,这几乎肯定不是您想要的。< / p>
这导致需要使用游戏DSL作为参数调用Game
的某些方法,la Game.init (this)
。你可以做的一件事就是让它在视觉上更清晰一些,就是静态导入Game的init方法:
import static com.foo.groovygame.Game.init
init (this);
go north, 10
...
最后一点:你仍然需要在你的DSL中使用Groovy语法,这意味着方法参数之间的逗号,没有参数的方法调用的括号等等:
go north, 10
search ()
find ana
turn right
我很感兴趣,如果其他人能够提出更清晰的解决方案,或者甚至只是某种方式来执行静态初始化器而不必参考封闭类。
答案 1 :(得分:1)
我对使用编译器语法实现DSL真的不是很疯狂 - 对不起,我知道有些人喜欢它,我很乐意承认这是一个可爱的技巧,但编写自己的解析器很容易,为什么不这样做它?然后,您的文本中没有随机逗号和下划线。
这是我用来实现一个简单语法的简单技巧,如你所描述的那样:
首先,看看你的命令 - 请注意,大多数都采用“动词名词参数”的格式
这很好地映射到methodName,objectName,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左右的行解析器将解决你的许多DSL类型问题。除此之外,您可以非常轻松地添加功能:
如果参数(2 ...)的格式为“name = value”,则扫描名为“name”的参数并为该特定参数传递“value”。这可能不适用于这种特定情况,但可以用于其他用途。
如果你的单字命令需要参数,那么即使有多个单词,也要尝试将s [0]实例化为一个类。如果失败,请恢复上面的多字算法。
我有一个案例,我需要在实例化后保留对象。我使用了语法:
find person:ana
(通过将表格映射到人并检查此表以及尝试实例化对象,可以将语法修复回原始语法)
从那以后,ana是person类的一个实例(换句话说,在实例化“person”并在其上调用方法“find”之后,我将person对象存储在名为“ana”的哈希中,下次他们使用如下命令:
talk ana
首先搜索哈希值,抓住存储在那里的对象并在该现有对象上调用“talk”(此时它可能会检查ana是否设置了“found”标志,如果不是,它可能返回一个不同的消息)。通过这种方式,您可以拥有多个朋友,每个朋友都拥有自己的状态信息。
这个系统有一些限制,但仍然比Ruby风格的DSL灵活得多,实际上并不难实现。
答案 2 :(得分:0)
RTBarnard的答案似乎是最好的。执行脚本后执行某些处理的最佳方法是什么?
Ronald.init this
go right
back ten
...etc...
在处理完所有dsl后,Ronald应该做一些额外的工作,但用户不必说Ronald.run()
之类的内容。