簡體   English   中英

將參數傳遞給scalameta天堂宏

[英]Passing parameters to scalameta paradise macro

我正在嘗試創建宏注釋,但需要傳遞參數。

class ToPojo(param1: String, param2: String) extends StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
        ...
  }
}

用作

@ToPojo("p1", "p2")
case class Entity(a: Int, m: Map[Long, Boolean])

與上面的代碼的問題是, Entity獲取到applydefn已經與注釋剝離-因此我不能從那里得到的參數。 同樣,由於無法內聯,因此無法通過apply方法訪問param1param2字段。

您能否指出我使用scala meta克服此問題的最簡單方法? 我考慮過要使用兩個注釋

@ToPojo
@ToPojoParams("p1", "p2")
case class Entity(a: Int, m: Map[Long, Boolean])

但這就是駭人的丑陋。

非常感謝

如ScalaMeta Paradise說明中所述, 如何將參數傳遞給宏注釋? 部分

this作為鱗翅目樹進行匹配

package scalaworld.macros

import scala.meta._

class Argument(arg: Int) extends scala.annotation.StaticAnnotation {
  inline def apply(defn: Any): Any = meta {
    // `this` is a scala.meta tree.
    println(this.structure)
    val arg = this match {
      // The argument needs to be a literal like `1` or a string like `"foobar"`.
      // You can't pass in a variable name.
      case q"new $_(${Lit.Int(arg)})" => arg
      // Example if you have more than one argument.
      case q"new $_(${Lit.Int(arg)}, ${Lit.String(foo)})" => arg
      case _ => ??? // default value
    }
    println(s"Arg is $arg")
    defn.asInstanceOf[Stat]
  }
}   

請注意,此代碼有點天真,並且不處理命名參數。 如果您有很多參數,那么編寫普通參數和命名參數的所有可能組合很無聊。 因此,您可以嘗試執行以下操作:

package examples

import scala.collection.immutable
import scala.meta._

class MyMacro(p1: String, p2: Int) extends scala.annotation.StaticAnnotation {


  inline def apply(defn: Any): Any = meta {
    val params = Params.extractParams(this)
    //some implementation
    ... 
  }

}


case class Params(p1: String, p2: Int) {
  def update(name: String, value: Any): Params = name match {
    case "p1" => copy(p1 = value.asInstanceOf[String])
    case "p2" => copy(p2 = value.asInstanceOf[Int])
    case _ => ???
  }
}

object Params {
  private val paramsNames = List("p1", "p2")

  def extractParams(tree: Stat): Params = {
    val args: immutable.Seq[Term.Arg] = tree.asInstanceOf[Term.New].templ.parents.head.asInstanceOf[Term.Apply].args

    args.zipWithIndex.foldLeft(Params(null, 0))((acc, argAndIndex) => argAndIndex._1 match {
      case q"${Lit(value)}" => acc.update(paramsNames(argAndIndex._2), value)
      case q"${Term.Arg.Named(name, Lit(value))}" => acc.update(name.value, value)
    })
  }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM