gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 15:49:44 33 4
gpt4 key购买 nike

我有一个表面上很简单的宏观问题,几个小时以来我一直在努力解决这个问题,但没有成功。也许有更多经验的人可以提供帮助。

我有以下宏:

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 的唯一字符串是硬编码的常量值(即,在编译时已知)。

这是可能的,人们如何做到这一点?

——

编辑:还有以下限制:
  • 它必须是一个黑盒宏。
  • 宏签名必须使用 c.Tree s,不是 c.Expr[_] s(旧代码;无法更改该部分)
  • 我有一个 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: what can code in Context.eval reference?

    如果您确实需要在程序树中使用运行时值,则必须将其编译推迟到运行时
    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!

    关于scala - 如何获取传递给 Scala 宏的参数的运行时值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59387459/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com