2013-05-07 33 views
8

Làm cách nào để nhận macro macro để thay thế cuộc gọi phương thức?Làm thế nào để tôi có được macro scala để thay thế một phương thức gọi

Mục tiêu của tôi là tạo một đặc điểm được gọi là ToStringAdder. Giả sử tôi có một đối tượng x với đặc tính này, sau đó khi tôi gọi x.add(any) Tôi muốn macro thực sự gọi x.add(any, string) trong đó chuỗi là biểu diễn chuỗi của AST. (Điều này là để tôi có thể có tốt đẹp tostring khi 'bất kỳ' là một chức năng).

Ngoại trừ Expecty tất cả các ví dụ này tôi đã thấy đang sử dụng hiệu quả các cuộc gọi phương thức tĩnh: đối tượng mà macro được gọi không được sử dụng. Expecty có các phương pháp sau đây, trong đó cung cấp cho tôi một đầu mối về cách phát hiện 'ngầm này', nhưng tôi không thể tìm thấy một cách để tham khảo nó trong một cuộc gọi reify.

private[this] def recordAllValues(expr: Tree): Tree = expr match { 
    case New(_) => expr // only record after ctor call 
    case Literal(_) => expr // don't record 
    // don't record value of implicit "this" added by compiler; couldn't find a better way to detect implicit "this" than via point 
    case Select([email protected](_), y) if getPosition(expr).point == getPosition(x).point => expr 
    case _ => recordValue(recordSubValues(expr), expr) 
    } 

Vậy làm cách nào để thay thế cuộc gọi đến đối tượng mà macro đã được gọi. Mã tôi có vào lúc này là dưới đây, và nó là mã trong cuộc gọi cụ thể hóa mà cần phải được sắp xếp

trait ToStringAdder { 
    def add(param: Any): Any = macro ToStringAdder.toStringAndValueImpl 
    def add(param: Any, toStringBasedOnAST: String): Any ; //This is the actual method I want the above method call to be replaced by 
} 

object ToStringAdder { 
    def toStringAndValueImpl(c: Context)(param: c.Expr[Any]): c.Expr[Unit] = { 
    import c.universe._ 
    val paramRep = show(param.tree) 
    val paramRepTree = Literal(Constant(paramRep)) 
    val paramRepExpr = c.Expr[String](paramRepTree) 
    //need to put something here 
    reify { c.someMethodCall("something to represent the method any", param.splice, paramRepExpr.splice) } 
    } 
} 
+0

Đây là một câu hỏi thật sự tốt đẹp nếu tôi hiểu nó một cách chính xác. Tôi tin rằng điểm là với một toString trên một 'chức năng' trả về một AST duy nhất bạn có thể thực hiện một toán tử bình đẳng trên' chức năng '. Người ta có thể làm những điều tuyệt vời như không lo lắng về việc sao chép các hàm trong một 'Danh sách' - chỉ cần gọi' toSet'. – samthebest

Trả lời

4

Bạn có thể nhận Tree ví dụ ToStringAdder của bạn như c.prefix.

Hãy thử điều này:

reify { c.Expr[ToStringAdder](c.prefix.tree).splice.add(param.splice, c.literal(paramRep).splice) } 

Proof nó hoạt động:

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

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

trait ToStringAdder { 
    def add(param: Any): Any = macro ToStringAdder.toStringAndValueImpl 
    def add(param: Any, toStringBasedOnAST: String): Any ; //This is the actual method I want the above method call to be replaced by 
} 

object ToStringAdder { 
    def toStringAndValueImpl(c: Context)(param: c.Expr[Any]): c.Expr[Any] = { 
    import c.universe._ 
    val paramRep = show(param.tree) 
    reify { (c.Expr[ToStringAdder](c.prefix.tree)).splice.add(param.splice, c.literal(paramRep).splice) } 
    } 
} 


// Exiting paste mode, now interpreting. 

import scala.language.experimental.macros 
import reflect.macros.Context 
defined trait ToStringAdder 
defined module ToStringAdder 

scala> class ToStringAdder1 extends ToStringAdder { 
    | def add(param: Any, toStringBasedOnAST: String): Any = s"param: $param \ntoStringBasedOnAST: $toStringBasedOnAST" 
    | } 
defined class ToStringAdder1 

scala> new ToStringAdder1().add((i: Int) => i*2) 
res0: Any = 
param: <function1> 
toStringBasedOnAST: ((i: Int) => i.*(2)) 
+0

Cảm ơn bạn đã phản hồi nhanh. Tôi nhận được thông báo "giá trị gia tăng không phải là thành viên của org.autotdd.scalamacros.ToStringAdder". Và thông tin chi tiết về mã kiểm soát/không gian không hiển thị cho tôi bất kỳ tùy chọn nào để thêm. Tôi có cần gõ dàn diễn viên hay gì đó không? –

+0

@StaveEscura: Tôi đã thêm ví dụ mã vào câu trả lời của mình. Có một lỗi trong mã của bạn: 'c.Expr [Unit]' thay vì 'c.Expr [Any]' làm kiểu kết quả của 'toStringAndValueImpl'. – senia

+0

Điều đó chỉ hoạt động. Tôi thực sự đánh giá cao sự giúp đỡ! –

Các vấn đề liên quan