有没有办法定义一个 dsl,这将允许以下形式?
variable identifier identifier variable
例如:
1 equals to 2
我知道如何创建一个更简单的表单:1 equals to (2)
,但我想避免使用括号。有没有办法做到这一点?
有没有办法定义一个 dsl,这将允许以下形式?
variable identifier identifier variable
例如:
1 equals to 2
我知道如何创建一个更简单的表单:1 equals to (2)
,但我想避免使用括号。有没有办法做到这一点?
你可以问解析器:
$ scala -Xprint:parser
Welcome to Scala version 2.9.2 ... blah
scala> variable1 identifier1 identifier2 variable2
// lots of stuff and inside:
val res0 = variable1.identifier1(identifier2).variable2
// this is how the parser sees it.
// if you can make that work (dynamic classes…?), you’re good to go.
但是,有一个问题:这只适用于variable2
标识符(以便它可以用作方法名称)。和
scala> 1 equals to 2
解析器已经失败:
<console>:1: error: ';' expected but integer literal found.
1 equals to 2
^
括号确实是您唯一的解决方法(*):
scala> 1 equals to (2)
// ...
val res1 = 1.equals(to(2))
(*) 除非您2
通过将其与反引号一起使用来创建标识符
scala> 1 equals to `2`
// ...
val res2 = 1.equals(to).2
……不,也许不是。
据我所知,目前不可能按照您的意愿实施(我很高兴被证明是错误的)。当我创建用于注入的小型 DSL 时,我也遇到了这个问题。但是我已经意识到,即使两个标识符之间不能有 2 个变量,它们之间仍然可以有三个标识符。它看起来像这样:
variable1 method constant method variable2
这与以下内容相同:
variable1.method(constant).method(variable2)
有了这个,我能够想出看起来像这样的非常好的 DSL:
by default defaultDb and identified by 'remote
'remote is by default defaultDb
您可以在此规范中找到更多使用示例:
它的实现可以在这里找到:
https://github.com/OlegIlyenko/scaldi/blob/master/src/main/scala/scaldi/Injectable.scala
我使用ByWord
和IdentifiedWord
类类型作为方法参数。例如:
def is(by: ByWord) = and(by)
这留下了可能性,图书馆的用户将扩展ByWorld
类并且通常可以提供他们自己的ByWorld
实现。现在回想起来,我觉得这不是很好。更好的解决方案是为常量词创建单例对象,然后像这样使用它们的类型:
object by
def is(byWord: by.type) = and(by)
这通常将参数限制为仅一个可能的by
单词实例。
好吧,如果最后一个是一元运算符,您可以有两个连续的标识符:
a identifier1 - d
被解析为
a.identifier1(d.unary_-)
请注意,一元函数只能是 ! 〜和 -
如果您不介意将您的最后一个变量名称视为字符串(例如,您可以稍后从映射或解释器本身检索其值),您可以使用动态进行以下操作:
import scala.Dynamic
import scala.language.dynamics
class I2
class Res(i: I2) extends Dynamic {
def selectDynamic(obj: String): Unit = {
println(obj)
}
}
class V1 { def i1(i2: I2): Res= new Res(i2) }
val v1 = new V1
val i2 = new I2
v1 i1 i2 v2
结果为字符串“v2”。