9

Scalaで内部DSLを構築しようとすると、一般的な問題が発生し、解決策を作成できませんでした。物事をもう少し典型的な言語のように見せるために、構文を次のようにしたいと思います。

model 'Foo {
  decl 'Real 'x;
  decl 'Real 'y;
}

実際には、いくつかの問題があります。最初の問題はmodel、この方法で2つの引数を取るオブジェクトをここで取得することです。誰かアイデアがあれば教えてください。しかし、代わりに私がやったことは、もう少し次のようなことをすることです。

model('Foo) {
  ...
}

applyここで、modelは、後続のラムダを消費するメソッドを持つオブジェクトを返す関数になりました。一緒に暮らせること。私はラムダの内部でも同様の問題を抱えて生きることができたので、内部のようなものdecl 'Real 'xですdecl('Real,'x)。しかし、私がやりたいのは、波状の中括弧内のすべての式の結果を取得して、リストとして「返される」ことです。言い換えれば、私が欲しいのは次のようなものを書くことです:

model 'Foo {
  decl('Real,'x);
  decl('Real,'y);
}

ここで、decl(...)はある種のタイプに評価されDeclaration{...}次にに評価されList[Declaration]ます。これを行うために暗黙的に使用する方法があると思いますが、私はそれを見つけることができませんでした。要するに、私は作りたいです:

model 'Foo {
  decl('Real,'x);
  decl('Real,'y);
}

...と同等に評価します...

model 'Foo {
  decl('Real,'x) ::
  decl('Real,'y) ::
  Nil
}

コメントや提案?

4

3 に答える 3

4

最初のアイデアとして、可変引数リストを試すことができます。これにより、セミコロンの代わりにコンマを使用できます。

case class Declaration(name: String)

def decl( s: String ) = Declaration(s)

case class Model( sym: Symbol, decls: List[Declaration] )

def model( sym: Symbol)( decls: Declaration* ) =
  Model( sym, decls.toList )

val m = model( 'Foo )(
  decl( "bar" ), 
  decl( "baz" ) 
)

または、 a を拡張しtraitて、いくつかの括弧とコンマを取り除くこともできます:

case class ModelBuilder( sym: Symbol ) {
  def using( decls: Declarations ) = Model( sym, decls.toList )
}

trait Declarations {

  protected var decls = List[Declaration]()

  protected def decl( s: String ) = 
decls ::= Declaration( s )

  def toList = decls
}

def model( sym: Symbol ) = ModelBuilder( sym )

model( 'Foo ) using new Declarations {
  decl( "bar" )
  decl( "baz" )
}
于 2012-04-13T10:28:18.933 に答える
4

何てことをしてしまったのか?

import scala.collection.mutable.ListBuffer

case class Declaration(t: Symbol, name: Symbol)
case class Model(name: Symbol, declarations: List[Declaration])

object model extends Dynamic {
  val buffer = ListBuffer.empty[Model]

  def applyDynamic(name: String)(args: Any*) {
    buffer += Model(Symbol(name), decl.buffer.toList)
    decl.buffer.clear()
  }
}

object decl extends Dynamic {
  val buffer = ListBuffer.empty[Declaration]

  def applyDynamic(t: String)(args: Any*) {
    args match {
      case Seq(name: Symbol) => buffer += Declaration(Symbol(t), name)
    }
  }
}

model Foo {
  decl Real 'x
  decl Real 'y
}

assert(model.buffer.head == Model('Foo, List(
  Declaration('Real, 'x), Declaration('Real, 'y))))
于 2012-04-14T00:22:27.517 に答える
2

'FooOK、それがモデル名であることを認識した後、これを完全に修正しました。

trait DSL {

  private var currentModel: ModelBuilder = null
  case class Declaration(kind: Symbol, name: Symbol)
  case class Model(name: Symbol, declarations: List[Declaration])
  case class ModelBuilder(name: Symbol, var declarations: Vector[Declaration]) {
    def -(f: => Unit) = { 
      currentModel = this
      f
      Model(name, declarations.toList)
    }
  }

  def decl (s1: Symbol,  s2: Symbol) {
    currentModel.declarations :+= Declaration(s1, s2)
  }

  object model {
    def - (s: Symbol) = ModelBuilder(s, Vector.empty)
  }
}

次に、使用サイトで:

object UseSite extends App with DSL {

  val m =

    model - 'Foo - {
      decl ('Real, 'x)
      decl ('Real, 'y)
    }

  println(m)  
    //Model('Foo,List(Declaration('Real,'x), Declaration('Real,'y)))
}

そこで今回のギミックは

1) 変数を使用して現在のモデルを追跡する

2)-メソッド名に記号を使用する (apply括弧を使用したい場合は代わりに使用できます)

3) ビルダーを使用して、返されたクラスを不変にする

ただし、TBH、これはいくつかのコンマを避けるためだけに少し多いかもしれません... :)

于 2012-04-13T15:16:13.673 に答える