代码之家  ›  专栏  ›  技术社区  ›  FrancMo

Scala Def宏,从值传递参数

  •  0
  • FrancMo  · 技术社区  · 4 年前

    我有一个工作宏,即:

    object Main extends App {
    
      println("Testing assert macro...")
      val result = Asserts.assert(false, "abc")
    
    }
    

    import scala.reflect.macros.blackbox.Context
    import scala.language.experimental.macros
    
    object Asserts {
      val assertionsEnabled: Boolean = true
    
      def assert(cond: Boolean, msg: String): Unit = macro assertImpl
    
      def assertImpl(c: Context)(cond: c.Expr[Boolean], msg: c.Expr[String]) : c.Expr[Unit] = {
        import c.universe._
        cond.tree match {
          case Literal(Constant(cond: Boolean)) =>
            if (!cond) c.abort(c.enclosingPosition, "Fix the code, whatever.") else c.Expr(q"()")
        }
      }
    }
    

    但问题是:

      val cond: Boolean = false
      val result = Asserts.assert(cond, "abc")
    

    现在我有一个错误:

    错误:(8,30)宏扩展期间出现异常:scala.MatchError: Main.this.cond(属于scala.reflect.internal.Trees$Select类)位于 断言

    0 回复  |  直到 4 年前
        1
  •  5
  •   Dmytro Mitin    4 年前

    简短的回答 false 是的运行时值 cond 在里面

    val cond: Boolean = false
    

    你试着匹配 Literal 但是那棵树/ Expr 条件 不是字面意思。

    所以呢 Asserts.assert(false, "abc") 会有用但是 Asserts.assert(cond, "abc")

    长话短说 有几个小把戏。

    你可以把你的项目分成三个子项目 核心 使用 c.eval Literal(Constant) (如果你把 条件 而不是 常见的

    object App {
      val cond: Boolean = false
    }
    

    核心 (取决于 常见的

    import App.cond
    
    object Main {
      def main(args: Array[String]): Unit = {
        println("Testing assert macro...")
        val result = Asserts.assert(cond, "abc") 
        //scalac: macro expansion has failed: Fix the code, whatever.
      }
    }
    

    (取决于 常见的 )

    import scala.language.experimental.macros
    import scala.reflect.macros.blackbox
    
    object Asserts {
      def assert(cond: Boolean, msg: String): Unit = macro assertImpl
    
      def assertImpl(c: blackbox.Context)(cond: c.Expr[Boolean], msg: c.Expr[String]) : c.Expr[Unit] = {
        import c.universe._
        val condEvaluated = c.eval(c.Expr[Boolean](c.untypecheck(cond.tree)))
        if (!condEvaluated) c.abort(c.enclosingPosition, "Fix the code, whatever.") else c.Expr(q"()")
      }
    }
    

    Scala: what can code in Context.eval reference?

    第二招 如果你继续 条件 在里面 然后你可以试着找到树的定义 条件 从右边开始:

    (取决于 )

    object Main {
      def main(args: Array[String]): Unit = {
        println("Testing assert macro...")
        val cond: Boolean = false
        val result = Asserts.assert(cond, "abc")
        //scalac: macro expansion has failed: Fix the code, whatever.
      }
    }
    

    import scala.language.experimental.macros
    import scala.reflect.macros.blackbox
    
    object Asserts {    
      def assert(cond: Boolean, msg: String): Unit = macro assertImpl
    
      def assertImpl(c: blackbox.Context)(cond: c.Expr[Boolean], msg: c.Expr[String]) : c.Expr[Unit] = {
        import c.universe._
    
        var condValue: Option[Boolean] = None
    
        val traverser = new Traverser {
          override def traverse(tree: Tree): Unit = tree match {
            case q"$_ val cond: $_ = $expr" if tree.symbol == cond.tree.symbol => 
              expr match {
                case Literal(Constant(cond: Boolean)) =>
                  condValue = Some(cond)
              }
            case _ => super.traverse(tree)
          }
        }
    
        c.enclosingRun.units.foreach(unit => traverser.traverse(unit.body))
    
        condValue.map(cond =>
          if (!cond) c.abort(c.enclosingPosition, "Fix the code, whatever.") else c.Expr(q"()")
        ).getOrElse(
          c.abort(c.enclosingPosition, "can't find cond")
        )
      }
    }
    

    Creating a method definition tree from a method symbol and a body

    Scala macro how to convert a MethodSymbol to DefDef with parameter default values?

    How to get the runtime value of parameter passed to a Scala macro?