Macro to access source code text at runtime

前端 未结 2 490
离开以前
离开以前 2020-12-16 21:48

Is there already or is it possible to have a Scala macro that gives me access to the text of the source? For instance I would like to write code like this:

v         


        
2条回答
  •  天命终不由人
    2020-12-16 22:12

    I was not able to reuse withSource directly to just print the source and value and return the value. The withSource macro cannot be utilized from the same object itself (so I cannot just add my slightly modified version of withSource right in that file) and I am not able to call withSource from a subclass of WithSourceHelper, limiting reuse through inheritance.

    In case anyone is interested, here is a complement to Senia's answer to just log the value with the source and return the value so the rest of the computation can occur.

    def logValueImpl[T](c: Context): c.Expr[T] = {
      import c.universe._
      val source = c.prefix.tree match {
        case Apply(_, List(s)) => s
        case _ => c.abort(c.enclosingPosition, "can't find source")
      }
      val freshName = newTermName(c.fresh("logValue$"))
      val valDef = ValDef(Modifiers(), freshName, TypeTree(source.tpe), source)
      val ident = Ident(freshName)
      val print = reify{
        println(c.literal(show(source)).splice + ": " + c.Expr[T](ident).splice) }
      c.Expr[T](Block(List(valDef, print.tree), ident))
    }
    

    I then define it as an implicit conversion on def p = macro Debug.logValueImpl[T]. I can then use like this:

    List(1, 2, 3).reverse.p.head 
    // prints: immutable.this.List.apply[Int](1, 2, 3).reverse: List(3, 2, 1)
    

    The funny part is that I can apply it twice:

    List(1, 2, 3).reverse.p.p
    

    And it will show me what the logValueImpl macro did:

    {
      val logValue$7: List[Int] = immutable.this.List.apply[Int](1, 2, 3).reverse;
      Predef.println("immutable.this.List.apply[Int](1, 2, 3).reverse: ".+(logValue$7));
      logValue$7
    }
    

    It seems to work with other macros as well:

    f"float ${1.3f}%3.2f; str ${"foo".reverse}%s%n".p`
    //prints:
    {
      val arg$1: Float = 1.3;
      val arg$2: Any = scala.this.Predef.augmentString("foo").reverse;
      scala.this.Predef.augmentString("float %3.2f; str %s%%n").format(arg$1, arg$2)
    }: float 1.30; str oof%n
    

    Even more interestingly if I used showRaw instead of show I can even see the tree of the expanded macro, which may come handy to figure out how to write other macros.

提交回复
热议问题