2013-08-27 157 views
2

這是一個簡化的示例,但問題依然存在。斯卡拉宏:將樹轉換/解析爲名稱

我想要實現這個使用宏(斯卡拉基於僞代碼):

(a: Int) => { 
    val z = "toShort" 
    a.z 
} 

如果我具體化,我會獲得類似這樣:

Function(
    List(
    ValDef(
     Modifiers(Flag.PARAM), 
     newTermName("a"), 
     Ident(scala.Int), 
     EmptyTree 
    ) 
), 
    Block(
    List(
     ValDef(
     Modifiers(), 
     newTermName("z"), 
     TypeTree(), 
     Literal(Constant("toShort")) 
    ) 
    ), 
    Apply(
     Select(
     Ident(newTermName("a")), 
     newTermName("toShort") 
    ), 
     List() 
    ) 
) 
) 

我不知道如何訪問爲一個值,然後將其用作TermName。

我試圖取代newTermName("toShort")newTermName(c.Expr[String](Select(Ident(newTermName("z")))).splice)但是編譯器似乎並不喜歡:

exception during macro expansion: java.lang.UnsupportedOperationException: the function you're calling has not been spliced by > the compiler. this means there is a cross-stage evaluation involved, and it needs to be invoked explicitly. if you're sure this is not an oversight, add scala-compiler.jar to the classpath, import scala.tools.reflect.Eval and call <your expr>.eval instead.

我也試過「EVAL」的建議由編譯器:newTermName(c.eval(c.Expr[String](...))但既不工作。

我怎麼能轉換像Select(Ident(newTermName("z")))樹(這是一個本地的val值的訪問) 一個名稱 一個字符串,它可以被用來作爲newTermName參數?可能嗎?

UPDATE:

帶到這裏你作爲一個gist真正的問題!

由於提前,

+0

'newTermName(「z」)''已經返回一個'Name'。我不明白那個有什麼問題。 – sschaef

+0

你的意思是你想從現有的樹中提取它嗎?像'val Select(Ident(name))= tree'? – sschaef

+0

我想使用'val z'的值作爲'newTermName'方法的參數。 'Select(Ident(newTermName(「z」)))'是對該值的訪問,然後是:'newTermName(「」)''。所以可以這樣做:'a.toShort'。 –

回答

3

我很難理解你想要什麼來實現的,以及爲什麼你正在使用的樹木隨處可見。樹木真的很低級,很難使用,很棘手,而且很難理解代碼的作用。 Quasiquotes(http://docs.scala-lang.org/overviews/macros/quasiquotes.html)是確實可行的方法,感謝macro paradise插件(http://docs.scala-lang.org/overviews/macros/paradise.html),您可以在scala 2.10.x版本上使用它們。你可以簡單地寫q"(a: Int) => {val z = "toShort"; a.z}",你直接得到你剛輸入的樹形表達式。

要回答你的問題,第一點是要記住宏在編譯時進行評估。因此它們不能生成依賴於運行時值的代碼。這就是爲什麼編譯器在抱怨你的splice。但是如果你傳遞一個可以在編譯時計算的值,通常是一個文字,那麼你可以使用eval在你的宏代碼中得到它的值。正如scaladoc所指出的,Eval確實遭受了一個錯誤。它只能在未定義的樹上調用。因此,在s: c.Expr[String]表達式上調用eval的方式將爲val s2 = c.eval(c.Expr[String](c.resetAllAttrs(c.tree.duplicate))),這會給您一個String,然後您可以在代碼中正常使用它,例如q"(a: Int) => a.${newTermName(s2)}"

把它放在一起,讓我們想象你創建一個宏,它將從一個對象和其中的一個String字段中輸出一個字符串值。它會給類似

def attr[A](a: A, field: String): String = macro attrImpl[A] 

def attrImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A], field: c.Expr[String]) = { 
    import c.universe._ 

    val s = c.eval(c.Expr[String](c.resetAllAttrs(field.tree.duplicate))) 
    c.Expr[String](q"a.${newTermName(s)}") 

} 

REPL會話測試:

scala> object a { val field1 = "field1"; val field2 = "field2" } 
defined module a 

scala> attr(a, "field1") 
res0: String = field1 

scala> attr(a, "field2") 
res1: String = field2 

要了解編譯時和運行時的區別,你可以沉思有關下列結果REPL ;-)

scala> val s = "field1"; attr(a, s) 
error: exception during macro expansion: 
scala.tools.reflect.ToolBoxError: reflective compilation has failed: 

$iw is not an enclosing class 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:311) 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:244) 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:408) 
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:411) 
    at scala.reflect.macros.runtime.Evals$class.eval(Evals.scala:16) 
    at scala.reflect.macros.runtime.Context.eval(Context.scala:6) 
    at .attrImpl(<console>:14) 


scala> val s = "field1" 
s: String = field1 

scala> attr(a, s) 
res3: String = field1 

希望它有幫助;))

+1

感謝您的回答!可能quasiquotes是要走的路,但它可用於尚未正式發佈的scala 2.11。我已經把z作爲一個在函數內部定義的值,以便在編譯時避免運行時值。你能用這個例子的可能解決方案來擴展你的答案嗎? 'c.eval(c.Expr [String](Select(Ident(newTermName(「z」)))))'不起作用:\。看看第197行的要點。 –

+1

您可以在2.10.x產品發佈中使用包含quasiquotes的macro paradise插件,如http://docs.scala-lang.org/overviews/macros/paradise.html中所述 – Leo

+0

再次感謝,我認爲他們只是在2.11 ... ...你知道爲什麼'c.Expr [Unit](q「」「println(」hello world!「)」「」)'只是工作和'c .Expr [Int => Short](q「」「(a:Int)=> a.toShort」「」)'不?拋出此錯誤:\('scala:無法讀取事件:rO0ABXNyADlvcmcuamV0YnJhaW5zLmpwcy5pbmNyZW1lbnRhbC ...錯誤:未知的源文件:[email protected]159c0 java.lang.RuntimeException:未知的源文件:embeddedFile - QuasiquoteCompat .scala @ 36a12aaa83f74d5b8ce323d542d159c0' –