将字符串插值器宏包装在另一个宏中

时间:2019-05-07 22:24:13

标签: scala scala-macros scala-reflect

我有内插器xxx是由宏实现的(我确实拥有代码)并返回XXX。我想在有函数yyy

时实现插值器f: XXX => YYY

代码

class XXXMacro(val c: blackbox.Context) {
  import c.universe._

  final def xxx(args: c.Expr[Any]*): c.Expr[XXX] = {
      val Apply(_, Apply(_, parts) :: Nil) = c.prefix.tree
      ???
  }
}

implicit class XXXInterpolator(sc: StringContext) {
  def xxx(args: Any*): XXX = macro XXXMacro.xxx
}

class YYYMacro(val c: blackbox.Context) {
  import c.universe._

  final def yyy(args: c.Expr[Any]*): c.Expr[YYY] = q"yyy($xxx\"args\")"
}

implicit class YYYInterpolator(sc: StringContext) {
  def yyy(args: Any*): YYY = macro XXXMacro.yyy
}

问题是我无法轻易地参数化代码。编译器不允许我执行macro XXXMacro(f).yyymacro XXXMacro.yyy(f)

另外,当我像将def yyy(args: Any*): YYY = f(new XXXInterpolator(sc).xxx(args))包裹起来时,上下文在宏中是完全不同的并使它复杂化,因此我无法重用它。有什么办法使它变得更容易吗?

2 个答案:

答案 0 :(得分:2)

尝试

class XXXMacro(val c: blackbox.Context) {
  import c.universe._

  final def xxx(args: c.Expr[Any]*): c.Expr[XXX] = {
    val Apply(_, Apply(_, parts) :: Nil) = c.prefix.tree
    ???
  }

  final def yyy(args: c.Expr[Any]*): c.Expr[YYY] =
    reify {
      f(xxx(args: _*).splice)
    }
}

implicit class XXXInterpolator(sc: StringContext) {
  def xxx(args: Any*): XXX = macro XXXMacro.xxx
}

implicit class YYYInterpolator(sc: StringContext) {
  def yyy(args: Any*): YYY = macro XXXMacro.yyy
}

答案 1 :(得分:2)

我不希望XXXMacro依赖YYY。因此,解决方案是为用户traitTree

trait XXXMacroTree {
  val c: blackbox.Context
  import c.universe._

  final def xxxTree(args: c.Expr[Any]*): Tree = ???

  final def xxx(args: c.Expr[Any]*): c.Expr[XXX] = c.Expr[XXX](xxxTree(args: _*))
}
class XXXMacro(val c: blackbox.Context) extends XXXMacroTree

implicit class XXXInterpolator(sc: StringContext) {
  def xxx(args: Any*): XXX = macro XXXMacro.xxx
}

class YYYMacro(val c: blackbox.Context) extends XXXMacroTree {
  import c.universe._

  final def yyy(args: c.Expr[Any]*): c.Expr[YYY] = c.Expr[YYY](q"f(${xxxTree(args: _*)}))
}

implicit class YYYInterpolator(sc: StringContext) {
  def yyy(args: Any*): YYY = macro YYYMacro.yyy
}