2012-04-13 6 views
8

Scalaで内部DSLを構築しようとすると、共通の問題が発生し、解決策を作ることができませんでした。物事はより一般的な言語のようなビットを見えるようにするために、私は、構文は次のようになりたいのです。実際にはScalaの一連の式からリストを作成する

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

は、いくつかの問題があります。最初の問題は、ここでmodelオブジェクトにこのように2つの引数を取ることです。誰かにアイディアがあれば教えてください。モデルは今、その後、その後、次のラムダを消費applyメソッドを持つオブジェクトを返す関数である

model('Foo) { 
    ... 
} 

:しかし、私が代わりにやったことは、もう少しこのような何かを行うことです。私は一緒に暮らすことができます。私もラムダの中で同様の問題で生きることができたので、内側にはdecl 'Real 'xまたはdecl('Real,'x)のようなものがあります。しかし、私がしたいのは、括弧内のすべての式の結果をリストとして返されるようにすることです。言い換えれば、私が欲しいのは、このような何かを書くことです:decl(...)はタイプDeclarationの何かに評価され、{...}が、その後List[Declaration]と評価さ

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

。私は、これを行うために暗黙的な方法を使用するいくつかの方法があると思うが、私はそれを見つけることができませんでした。要するに、私がしたいのですが:

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

... ...

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

コメントや提案の同等に評価しますか?

答えて

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") 
) 

を、あなたはいくつかの括弧のとを取り除くために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") 
} 
+0

はい、私は、宣言的なGUI DSLのいくつかについてもこのアプローチを見てきました。私はこれが近いことに同意する。私はちょうどあなたが追加したり削除したりするたびに、物事の間に "、"があることを心配する必要があるので、すべてのものを取り巻く必要はなく、 "、"というのは問題があります。最後に。 –

+0

あなたのコメントに答えるために自分の答えを変更しました。 – paradigmatic

+0

ああ、とても賢い。コンストラクタの構文とローカルで定義されたメソッドの使用。私はそれが好きで、うまくいくと思う。実際、コードを簡略化して "new Model( 'Foo){...}'にすることができます。コンストラクタの文脈で括弧で囲まれた中カッコで、そのスコープに簡単に物事を導入できるという事実を利用するうまい方法です。 Scala 2.10のマクロがこれをもっと簡単にするのだろうか? –

2

OKは、完全に'Fooは、モデル名ことになっていることを認識した後、これを修正しました。

使用-サイトで次に
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が、これはほんの一部のカンマを避けるために、少しくらいかもしれません、が、返されるクラスは

不変できるようにビルダーを使用して... :)

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)))) 
+0

おめでとうございます!私はこれがちょうど「ダイナミック」が何であるかと思います。 –

関連する問題