代码之家  ›  专栏  ›  技术社区  ›  Joa Ebert Emran

无需额外语法的Scala DSL

  •  2
  • Joa Ebert Emran  · 技术社区  · 14 年前

    我问了我自己好几次这个问题,并想出了一个解决办法,感觉非常肮脏。也许你可以给我任何建议,因为我认为这是每个用Scala编写的DSL的基本问题。

    MySpec extends Specification {
      "system" should {
        "example0" in { ... }
        "example1" in { ... }
        "example2" in { ... }
      }
    
      "system" can {
        "example0" in { ... }
      }
    }
    

    例如,我不必写作 "example0" in { ... } :: "example1" in { ... } :: "example2" in { ... } :: Nil .

    这正是我想要的行为。我认为这是通过Specs中Specification类的隐式定义实现的,比如(如果您是Specs的作者,并且我误解了一些东西,请不要生气:)

    implicit def sus2spec(sus: Sus): Specification = {
      suslist += sus
      this
    }
    

    root: statement*;
    
    statement:
        IDENT '{' statement* '}'
      | declaration*
      ;
    
    declaration: IDENT ':=' INT+;
    

    MyRoot extends Root {
      "statement0" is {
        "nested_statement0" is {
          "nested_nested_statement0" is {
            "declaration0" := 0
          }
    
          "declaration1" := 1
          "declaration2" := 2
        }
    
        "declaration3" := 3
      }
    
      "statement1" is {
        "declaration4" := 4
      }
    }
    

    对我来说,这里出现的问题是隐式解决方案不起作用。隐式定义将在根对象的范围内执行,这意味着我将把所有对象添加到根中,层次结构将丢失。

    然后我想我可以使用堆栈[语句]之类的东西。每次打电话我都能把一个物体推到它上面 is

    2 回复  |  直到 14 年前
        1
  •  2
  •   Sandor Murakozi    14 年前

    我在电影里看过一个好把戏 XScalaWT 在DSL中实现嵌套。我没有检查规格是否使用相同或不同的东西。


    printName恰好是这样一个方法,就像addChild一样,为第一个参数列表填充参数。

    对我来说,这是最能说明问题的部分。之后,您可以相对简单地添加许多其他奇特的特性(如隐式魔术、基于结构类型的dsl方法等)。

    在这种情况下,你可能会有

      private def setupChildren[A, B](a : A, setups:(A => B)*) : Seq[B] = {
        for (setup <- setups) yield setup(a)
      }
    

    您将传入“context”,并使用返回的子级创建父级。

    顺便说一句,我认为XScalaWT中需要这个设置,因为它是用于SWT的,在SWT中,子对象需要对其父控件的引用。如果你不需要它(或者任何来自当前“上下文”的东西),那么一切都会变得简单一些。

    这种技巧的一个缺点是,对于要在类上调用的每个方法,必须有一个单独的dsl方法(即使是一个简单的方法)。或者你也可以使用像

    x => x.printName
    

    这会做的工作,但不是那么好(尤其是如果你必须经常这样做)。

    object NestedDsl {
    
      object Nestable {
        def apply(name: String, setups:(Nestable => Unit)*): Nestable = {
          val n = new Nestable(None, name)
          setup(n, setups: _*)
          n
        }
      }
    
      class Nestable(parent: Option[Nestable], name: String) {
        def printName() { println(name) }
      }
    
      // DSL part
    
      def addChild(name: String, setups:(Nestable => Unit)*)(parent: Nestable) = {
        val n = new Nestable(Some(parent), name)
        setup(n, setups: _*)
        n
      }
    
      def printName(n: Nestable) = n.printName
    
      private def setup[T](t : T, setups:(T => Unit)*) : T = {
        setups.foreach(setup => setup(t))
        t
      }
    
      def main(args: Array[String]) {
        Nestable("root",
          addChild(
              "first", 
              addChild("second",
                printName
            )
          )
        )
      }
    }
    
        2
  •  2
  •   Joa Ebert Emran    14 年前

    cssx-dsl

    代码非常简单。基本上我有一个 mutable builder