2012-12-07 19 views
8

Có cách nào thuận tiện để biến một số MethodSymbol sang bên trái của cây định nghĩa phương thức (ví dụ: DefDef) trong Scala 2.10 không?Tạo cây định nghĩa phương thức từ biểu tượng phương thức và một nội dung

Ví dụ: giả sử tôi muốn tạo macro sẽ lấy một cá thể của một đặc điểm và bao bọc tất cả các phương pháp của đặc điểm đó bằng một số chức năng gỡ lỗi. Tôi có thể viết như sau:

import scala.language.experimental.macros 
import scala.reflect.macros.Context 

object WrapperExample { 
    def wrap[A](a: A): A = macro wrap_impl[A] 

    def wrap_impl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A]) = { 
    import c.universe._ 

    val wrapped = weakTypeOf[A] 
    val f = Select(reify(Predef).tree, "println") 

    val methods = wrapped.declarations.collect { 
     case m: MethodSymbol if !m.isConstructor => DefDef(
     Modifiers(Flag.OVERRIDE), 
     m.name, 
     Nil, Nil, 
     TypeTree(), 
     Block(
      Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil), 
      Select(a.tree, m.name) 
     ) 
    ) 
    }.toList 

    //... 
} 

Tôi đã elided kinh doanh nhàm chán của những phương pháp này gắn bó trong một lớp ẩn danh mới mà thực hiện các đặc điểm và sau đó instantiating rằng đẳng cấp bạn có thể tìm thấy trọn vẹn một ví dụ làm việc here nếu bạn' lại quan tâm.

Bây giờ tôi có thể viết những dòng này, ví dụ:

scala> trait X { def foo = 1; def bar = 'a } 
defined trait X 

scala> val x = new X {} 
x: X = [email protected] 

scala> val w: X = WrapperExample.wrap[X](x) 
w: X = [email protected] 

scala> w.foo 
Calling: foo 
res0: Int = 1 

scala> w.bar 
Calling: bar 
res1: Symbol = 'a 

Vì vậy, nó hoạt động, nhưng chỉ trong trường hợp rất đơn giản-nó sẽ không nếu đặc điểm có phương pháp với danh sách tham số, với bổ truy cập, chú thích, v.v.

Điều tôi thực sự muốn là hàm sẽ lấy biểu tượng phương thức và cây cho phần nội dung mới và trả lại DefDef. Tôi đã bắt đầu viết một bằng tay, nhưng nó liên quan đến rất nhiều thứ khó sử dụng như thế này:

List(if (method.isImplicit) Some(Flag.IMPLICIT) else None, ...) 

Đó là gây phiền nhiễu, tiết và dễ bị lỗi. Tôi có thiếu một số cách hay hơn để thực hiện điều này trong API phản chiếu mới không?

Trả lời

4

Theo hiểu biết tốt nhất của tôi, không có cách nào tiêu chuẩn để đi từ biểu tượng đến cây xác định.

Đặt cược tốt nhất của bạn có thể sẽ lặp lại thông qua c.enclosingRun.units, đệ quy vào từng cây unit.body khi bạn di chuyển. Nếu bạn thấy một số DefDef, có số symbol bằng với ký hiệu của bạn, thì bạn đã đến đích. cập nhật. Đừng quên duplicate cây xác định trước khi sử dụng lại nó!

Kỹ thuật này không phải là điều thuận tiện nhất trên thế giới, nhưng nó sẽ hoạt động.

+0

Cảm ơn (và +1), nhưng nếu đặc điểm tôi đang tìm kiếm là gì? Trong trường hợp đó tôi bị mắc kẹt với cách tiếp cận ở trên, phải không? –

+1

Ah, tôi hiểu ý của bạn là gì. Bạn có thể sử dụng API này: https://github.com/scalamacros/kepler/blob/0acb8a30c379f268e8a3e1340504530493a1a1dc/src/reflect/scala/reflect/api/Trees.scala#L2480. Chúng tôi đã không dùng nó trong phiên bản 2.10.1, nhưng bạn có thể xem cách triển khai: https://github.com/scalamacros/kepler/blob/0acb8a30c379f268e8a3e1340504530493a1a1dc/src/reflect/scala/reflect/internal/Trees.scala#L975 . –

2

Bạn có thể thử cách sau. Điều này làm việc với nhiều tham số và chức năng được curried và các thông số loại;)

val methods = wrapped.declarations.collect { 
    case m: MethodSymbol if !m.isConstructor => DefDef(
    Modifiers(Flag.OVERRIDE), 
    m.name, 
    m.typeParams.map(TypeDef(_)), 
    m.paramss.map(_.map(ValDef(_))), 
    TypeTree(m.returnType), 
    Block(
     Apply(f, c.literal("Calling: " + m.name.decoded).tree :: Nil), 
     m.paramss.foldLeft(Select(a.tree.duplicate, m.name): Tree)((prev, params) => 
     Apply(prev, params.map(p => Ident(p.name))) 
    ) 
    ) 
) 
}.toList 
+0

Ngoài ra còn có 'DefDef (sym, tree)' cũng giống như vậy, nhưng như được đề cập bên dưới, phương thức này đã không được chấp nhận cùng với 'TypeDef (sym)' và 'ValDef (sym)'. cập nhật. Nó không làm như vậy, vì mã của bạn dường như bỏ qua các công cụ sửa đổi hiện tại. –

+0

Cũng xem nhận xét của tôi về ý kiến ​​của OP: https://gist.github.com/4234441. Mã của bạn có cùng một vấn đề tinh tế như mã đó. –

+0

Cảm ơn Eugene. Tôi đã cập nhật câu trả lời của mình cho phù hợp. Điều này có thể giải thích một số vấn đề tôi có trên một số macro của tôi với sự cố trình biên dịch khó chịu. – Leo