scala AST Выбор узла не может найти членов, унаследованных от родителя - PullRequest
0 голосов
/ 03 апреля 2020

Я пишу макрос с именем assign, задачей которого является присвоение значений от членов одного экземпляра другому с определенным префиксом, добавленным к имени члена. Например, у меня есть экземпляр с членами с именем my_prefix_data, my_prefix_rden, ..., и я хочу присвоить значения этим элементам из другого экземпляра с соответствующими элементами с именем data, rden, .... Я сделал прототип версии макроса, который просто обрабатывает назначение my_prefix_data <- data. Назначения должны выполняться с использованием специального метода :=, поскольку этот макрос в конечном итоге будет применяться к коду Долото .

К сожалению, он не работает. Реализация макроса выглядит следующим образом

package my_macros

import scala.language.experimental.macros
import scala.reflect.macros.blackbox


object Macro {
  def assign(foo: Any, prefix: String, bundle: Any): Unit = macro Impl.assign
}

class Impl(val c: blackbox.Context) {
  def assign(
    foo: c.Tree, // Foo
    prefix: c.Tree, // string
    bundle: c.Tree // bundle
  ): c.Tree = {
    import c.universe._
    val Literal(Constant(p: String)) = prefix
    Apply(
      Select(
        Select(
          foo,
          TermName(s"${p}_data")
        ),
        TermName(":=")
      ),
      List(
        Select(
          bundle,
          TermName("data")
        )
      )
    )
  }
}

У меня установлен неудачный тестовый пример, подобный следующему

package my_macros

import org.scalatest._

class MacroSpec extends FlatSpec {
  behavior of "Macro"
  it should "access parent methods" in {
    class Data(initial: Int) {
      var value: Int = initial
      def :=(other: Data): Unit = {
        value += other.value
      }
    }

    class UInt(initial: Int) extends Data(initial) {}

    class FooBundle {
      val my_prefix_data = new UInt(1)
    }

    class BarBundle {
      val data = new UInt(2)
    }

    val foo = new FooBundle
    val bar = new BarBundle

    Macro.assign(foo, "my_prefix", bar)

    assert(foo.my_prefix_data.value == bar.data.value)
  }
}

Сбой при ошибке

[error] value := is not a member of UInt
[error]   Expression does not convert to assignment because receiver is not assignable.
[error]     Macro.assign(foo, "my_prefix", bar)
[error]                 ^

Почему Select Узлу AST не удалось найти унаследованный := из родительского класса UInt Data?

1 Ответ

1 голос
/ 03 апреля 2020

Наследование не имеет значения. Для

class UInt(initial: Int) {
  var value: Int = initial
  def :=(other: UInt): Unit = {
    value += other.value
  }
}

поведение такое же.

Заменить TermName(":=") на TermName("$colon$eq") или TermName(":=").encodedName или заменить построенное вручную дерево

val Literal(Constant(p: String)) = prefix
Apply(
  Select(
    Select(
      foo,
      TermName(s"${p}_data")
    ),
    TermName(":=")
  ),
  List(
    Select(
      bundle,
      TermName("data")
    )
  )
)

на квазицитатуру

val q"${p: String}" = prefix
val pdata = TermName(s"${p}_data")
q"$foo.$pdata.:=($bundle.data)"

Затем код компилируется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...