如何获取传递给 Scala 宏的参数的运行时值?



我有一个表面上简单的宏观问题,我已经撞了几个小时,但没有运气。也许有更多经验的人可以提供帮助。

我有以下宏:

import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object MacroObject {
def run(s: String): Unit =
macro runImpl
def runImpl(c: Context)(s: c.Tree): c.Tree = {
import c.universe._
println(s)    // <-- I need the macro to know the value of s at compile time
q"()"
}
}

问题是这样的:我希望宏知道传递给它的值s- 不是s的 AST,而是s本身的值。 具体来说,我希望它具有以下行为:

def runTheMacro(str: String): Unit = MacroObject.run(str)
final val HardCodedString1 = "Hello, world!"
runTheMacro(HardCodedString1)    // the macro should print "Hello, world!"
// to the console during macro expansion
final val HardCodedString2 = "So long!"
runTheMacro(HardCodedString2)    // the macro should print "So long!"
// to the console during macro expansion

可以保证传递给runTheMacro的唯一字符串是硬编码的常量值(即在编译时已知(。

这可能吗,如何做到这一点?

--

编辑:还有以下约束:

  1. 它必须是黑盒宏。
  2. 宏签名必须使用c.Trees,而不是c.Expr[_]s(遗留代码;不能更改该部分(
  3. 如果需要,我确实可以在宏中toolbox
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox
private val toolbox = currentMirror.mkToolBox()
/** Evaluate the given code fragment at compile time. */
private def eval[A](code: String): A = {
import scala.reflect.runtime.{universe => u}
val uTree: u.Tree = toolbox.parse(code)
toolbox.eval(uTree).asInstanceOf[A]
}

你的eval是运行时反射的eval,编译时宏的eval将是c.eval

"Hello, world!"

final val HardCodedString1 = "Hello, world!"
runTheMacro(HardCodedString1) 

是运行时值HardCodedString1

在编译时无法访问运行时值。

在编译时,字符串树HardCodedString1只是不知道val树的右侧。

Scala:Context.eval 中的代码可以引用什么?

如果你真的需要在程序树中使用运行时值,你必须将其编译推迟到运行时

import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
import scala.tools.reflect.ToolBox
object MacroObject {
val toolbox = currentMirror.mkToolBox()
def run(s: String): Unit = {
toolbox.eval(q"""
println($s)
()
""")
}
}
runTheMacro(HardCodedString1)//Hello, world!
runTheMacro(HardCodedString2)//So long!

或者,在编译时,您可以以某种方式找到封闭类的树,并在其中查找val树并获取其右侧

def runImpl(c: blackbox.Context)(s: c.Tree): c.Tree = {
import c.universe._
var rhs: Tree = null
val traverser = new Traverser {
override def traverse(tree: Tree): Unit = {
tree match {
case q"$mods val $tname: $tpt = $expr" if tname == TermName("HardCodedString1") =>
rhs = expr
case _ => ()
}
super.traverse(tree)
}
}
traverser.traverse(c.enclosingClass) // deprecated
val rhsStr =
if (rhs != null) c.eval[String](c.Expr(c.untypecheck(rhs.duplicate)))
else c.abort(c.enclosingPosition, "no val HardCodedString1 defined")
println(rhsStr)
q"()"
}
runTheMacro(HardCodedString1)//Warning:scalac: Hello, world!

或者对于所有此类变量

def runImpl(c: blackbox.Context)(s: c.Tree): c.Tree = {
import c.universe._
val sEvaluated =
try {
c.eval[String](c.Expr(c.untypecheck(s.duplicate)))
} catch {
case e: IllegalArgumentException if e.getMessage.startsWith("Could not find proxy") =>
s match {
case q"$sName" =>
var rhs: Tree = null
val traverser = new Traverser {
override def traverse(tree: Tree): Unit = {
tree match {
case q"$mods val $tname: $tpt = $expr" if tname == sName =>
rhs = expr
case _ => ()
}
super.traverse(tree)
}
}
traverser.traverse(c.enclosingClass)
if (rhs != null) c.eval[String](c.Expr(c.untypecheck(rhs.duplicate)))
else c.abort(c.enclosingPosition, s"no val $sName defined")
case _ => c.abort(c.enclosingPosition, s"unsupported tree $s")
}
}
println(sEvaluated)
q"()"
}
MacroObject.run(HardCodedString1) //Warning:scalac: Hello, world!
MacroObject.run(HardCodedString2) //Warning:scalac: So long!

在这种情况下,runTheMacro将不起作用:Error: no val str defined. 要使其工作,您也可以将其设置为宏

def runTheMacro(str: String): Unit = macro runTheMacroImpl
def runTheMacroImpl(c: blackbox.Context)(str: c.Tree): c.Tree = {
import c.universe._
q"MacroObject.run($str)"
}
runTheMacro(HardCodedString1) //Warning:scalac: Hello, world!
runTheMacro(HardCodedString2) //Warning:scalac: So long!

相关内容

  • 没有找到相关文章

最新更新