Как я могу повторно использовать поддеревья определения (AST )в макросе?

Я работаю со встроенным DSL Scala, и макросы становятся основным инструментом для достижения моих целей. Я получаю сообщение об ошибке при попытке повторно использовать поддерево из входящего выражения макроса в результирующее. Ситуация достаточно сложная, но (надеюсь )я упростил ее для понимания.

Предположим, у нас есть этот код:

val y = transform {
  val x = 3
  x
}
println(y) // prints 3

где «преобразование» — задействованный макрос. Хотя может показаться, что он абсолютно ничего не делает, на самом деле он преобразует показанный блок в это выражение:

3 match { case x => x }

Это делается с помощью этой реализации макроса:

def transform(c: Context)(block: c.Expr[Int]): c.Expr[Int] = {
  import c.universe._
  import definitions._

  block.tree match {
    /* {
     *   val xNam = xVal
     *   xExp
     * }
     */
    case Block(List(ValDef(_, xNam, _, xVal)), xExp) =>
      println("# " + showRaw(xExp)) // prints Ident(newTermName("x"))
      c.Expr(
        Match(
          xVal, 
          List(CaseDef(
            Bind(xNam, Ident(newTermName("_"))),
            EmptyTree,
            /* xExp */ Ident(newTermName("x")) ))))
    case _ => 
      c.error(c.enclosingPosition, "Can't transform block to function")
      block  // keep original expression
  }
}

Обратите внимание, что xNam соответствует имени переменной, xVal соответствует связанному с ней значению и, наконец, xExp соответствует выражению, содержащему переменную. Что ж, если я распечатаю необработанное дерево xExp, я получу Ident (newTermName ("x")), а именно это и установлено в корпусе RHS. Поскольку выражение можно изменить (, например, x+2 вместо x ), для меня это недопустимое решение. Что я хочу сделать, так это повторно использовать дерево xExp (, см. комментарий xExp ), изменяя значение «x», означающее (, это определение во входном выражении, но будет переменной case LHS в выходном. ), но запускает длинную ошибку, описанную в:

symbol value x does not exist in org.habla.main.Main$delayedInit$body.apply); see the error output for details.

Мое текущее решение состоит в анализе xExp для замены всех идентификаторов новыми, но это полностью зависит от внутренних компонентов компилятора и, следовательно, временного обходного пути. Очевидно, что xExp содержит больше информации, чем предлагает showRaw.Как я могу очистить этот xExp, чтобы разрешить «x» роль переменной case? Может ли кто-нибудь объяснить всю картину этой ошибки?

PS :Я безуспешно пытался использовать заменяющее семейство методов *из TreeApi , но мне не хватает основ, чтобы понять его значение.

11
задан kiritsuku 10 July 2012 в 21:33
поделиться