我希望使用groovy创建一个新的dsl,但我找不到让groovy读取DSL的最佳方法。我希望用户能够创建dsl并实际运行dsl,而不必与应用程序代码对话。
game.groovy (作为数字用户线):
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的游戏,但我想知道这是不是最好的方法?这看起来有点草率。我真的很喜欢ruby处理这件事的方式。您可以简单地创建一个dsl并在dsl中指定一个必需的'foo‘。
发布于 2010-02-24 14:37:52
我今天花了一些时间尝试不同的方法来实现你所描述的内容,我决定你链接的“完美风暴”博客上的建议可能是最直接和最不费解的。
我的第一个想法是在游戏类中创建一个静态初始化器,它将自动负责初始化游戏,但不幸的是,即使游戏类是从'import‘语句加载的(通过在JAVA_OPTS中设置-verbose:class来运行证明),静态初始化器直到以某种方式引用游戏类时才会执行。这需要在您的game.groovy数字用户线路中有一条new Game()线路。
即使假设您对此没有意见,处理游戏DSL中的函数和属性的唯一方法也是以某种方式向游戏类添加支持。在静态初始化器中,您不能直接访问游戏类,但可以访问它的超类:groovy.lang.Script。您可以将像go()和search()这样的方法添加到Script.metaClass中,但随后要为Script的所有实例添加这些方法,这几乎肯定不是您想要的。
这导致需要使用游戏DSL作为参数来调用Game的一些方法,就像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如果其他人能够想出更干净的解决方案,或者甚至只是用某种方法来执行静态初始化器,而不必引用封闭的类,我非常感兴趣。
发布于 2010-02-26 07:34:20
我真的不喜欢使用编译器语法来实现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实例化为类,即使有多个字也是如此。如果此操作失败,则恢复到上面的多字算法。
我有一个例子,在实例化对象之后,我需要保留它们。我使用的语法是:
find person:ana (通过保留一个表,将ana映射到person,并在尝试实例化对象的同时检查该表,可以将语法修复回原始语法)
从那时起,ana就是person类的一个实例(换句话说,在实例化" person“并对其调用方法"find”之后,我将person对象存储在名为"ana“的散列中,下次他们使用如下命令:
talk ana它将首先搜索散列,获取存储在其中的对象,并对现有对象调用"talk“(此时,它可能会检查ana是否设置了”找到“标志,如果没有,则可能返回不同的消息)。通过这种方式,您可以拥有多个朋友,每个朋友都有自己的状态信息。
这个系统有一些局限性,但仍然比Ruby风格的DSL灵活得多,而且实现起来一点也不困难。
发布于 2010-02-28 06:46:34
RTBarnard的答案似乎是最好的。在脚本执行后进行某些处理的最佳方式是什么?
Ronald.init this
go right
back ten
...etc...在处理完所有的dsl之后,Ronald应该做一些额外的工作,但不需要用户说一些像Ronald.run()这样的话。
https://stackoverflow.com/questions/2318859
复制相似问题