2012-04-13 9 views
8

Khi tôi cố gắng xây dựng các DSL nội bộ trong Scala, tôi gặp phải một vấn đề chung và tôi không thể tạo ra một giải pháp. Để làm cho mọi thứ trông hơi giống một ngôn ngữ điển hình, tôi muốn cú pháp trông giống như thế này:Xây dựng một danh sách từ một loạt các biểu thức trong Scala

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

Thực tế, có một số vấn đề. Vấn đề đầu tiên là nhận được một đối tượng model ở đây để lấy hai đối số theo cách này. Nếu ai có ý tưởng nào, hãy cho tôi biết. Nhưng những gì tôi đã thực hiện thay vì là để làm điều gì đó nhiều hơn một chút như thế này:

model('Foo) { 
    ... 
} 

đâu mô hình bây giờ là một chức năng mà sau đó trả về một đối tượng với một phương pháp apply mà sau đó tiêu thụ các lambda rằng sau. Điều đó tôi có thể sống với. Tôi có thể sống với một vấn đề tương tự bên trong lambda là tốt, vì vậy những thứ như decl 'Real 'x hoặc decl('Real,'x) ở bên trong. Nhưng những gì tôi muốn làm là để có được kết quả của tất cả những biểu thức bên trong niềng răng nguệch ngoạc để có được "trả lại" như là một danh sách. Nói cách khác, những gì tôi muốn là viết một cái gì đó như thế này:

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

nơi decl(...) đánh giá một cái gì đó kiểu Declaration{...} sau đó đánh giá để List[Declaration]. Tôi nghi ngờ có một số cách sử dụng implicits để làm điều này, nhưng tôi đã không thể tìm thấy nó. Nói tóm lại, tôi muốn thực hiện:

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

... đánh giá để tương đương với ...

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

Comments hoặc gợi ý?

Trả lời

4

Là một ý tưởng đầu tiên, bạn có thể thử lập luận danh sách biến, cho phép bạn sử dụng dấu phẩy thay vì dấu chấm phẩy:

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

Ngoài ra, bạn có thể mở rộng một trait để thoát khỏi một số dấu ngoặc đơn và của dấu phẩy:

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

Vâng, tôi cũng đã thấy cách tiếp cận này với một số DSL GUI mang tính khai báo. Tôi đồng ý rằng điều này là gần. Tôi chỉ hy vọng không phải giới thiệu() xung quanh toàn bộ sự việc và nhu cầu sử dụng "," là vấn đề bởi vì mỗi lần bạn muốn thêm hoặc xóa, bạn cần phải lo lắng về việc có "," giữa mọi thứ, nhưng không cuối cùng. –

+0

Tôi đã sửa đổi câu trả lời của mình để trả lời nhận xét của bạn. – paradigmatic

+0

Ah, rất thông minh. Sử dụng cú pháp hàm dựng và phương thức được xác định cục bộ. Tôi thích nó và tôi nghĩ nó có thể hoạt động. Trong thực tế, tôi có thể đơn giản hóa nó xuống "Mô hình mới ('Foo) {...}" làm giảm mã xuống. Cách tốt để khai thác thực tế là các dấu ngoặc nhọn trong ngữ cảnh của hàm tạo cho phép bạn giới thiệu mọi thứ dễ dàng trong phạm vi đó. Tôi tự hỏi nếu Scala 2.10 macro sẽ làm cho điều này thậm chí còn đơn giản hơn? –

2

OK, hoàn toàn sửa đổi điều này sau khi nhận ra rằng 'Foo được coi là tên mẫu.

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

Sau đó, tại sử dụng tại chỗ:

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

Vì vậy, các mánh lới quảng cáo đây là

1) sử dụng một biến để theo dõi các mô hình hiện tại

2) sử dụng - ký tự đối với tên phương thức (bạn có thể sử dụng apply nếu bạn thích dấu ngoặc đơn)

3) sử dụng một người thợ xây để các lớp trở có thể bất biến

Mặc dù, TBH này có thể là một chút nhiều chỉ để tránh một số dấu phẩy ... :)

4

Oh god những gì đã làm gì?

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

Chúc mừng một câu trả lời tuyệt vời! Tôi đoán đây chỉ là những gì 'Năng động' là cho. –