8

有没有办法定义一个 dsl,这将允许以下形式?

variable identifier identifier variable

例如:

1 equals to 2

我知道如何创建一个更简单的表单:1 equals to (2),但我想避免使用括号。有没有办法做到这一点?

4

3 回答 3

13

你可以问解析器:

$ 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

……不,也许不是。

于 2012-05-08T19:21:22.340 回答
3

据我所知,目前不可能按照您的意愿实施(我很高兴被证明是错误的)。当我创建用于注入的小型 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/48f7e4186cf7eb441116087003d7f45f16e0ac6c/src/test/scala/scaldi/InjectableSpec.scala#L47

它的实现可以在这里找到:

https://github.com/OlegIlyenko/scaldi/blob/master/src/main/scala/scaldi/Injectable.scala

我使用ByWordIdentifiedWord类类型作为方法参数。例如:

def is(by: ByWord) = and(by)

这留下了可能性,图书馆的用户将扩展ByWorld类并且通常可以提供他们自己的ByWorld实现。现在回想起来,我觉得这不是很好。更好的解决方案是为常量词创建单例对象,然后像这样使用它们的类型:

object by
def is(byWord: by.type) = and(by)

这通常将参数限制为仅一个可能的by单词实例。

于 2012-05-08T19:42:17.400 回答
1

一元运算符

好吧,如果最后一个是一元运算符,您可以有两个连续的标识符:

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”。

于 2013-12-21T14:52:20.917 回答