Как использовать квазицитаты с ранее определенным объектом - PullRequest
2 голосов
/ 18 июня 2020

Я только начал изучать рефлексию scala во время компиляции, и меня познакомили с квазиквотами в официальных руководствах Scala.

Одна концепция, с которой я все еще борюсь, - это то, как я должен работать с квазиквотами (или reify, если на то пошло), если я хочу сгенерировать AST для уже определенного объекта. Предположим, у меня есть объект:

object MyObject {
  def method1() = "m1"
}

Чтобы получить дерево, я знаю, что могу сделать

q"""{object MyObject {
  def method1() = "m1"
}}
"""

Однако выполнение этого не позволяет мне фактически определить объект в моя область видимости (и мне также нужно полностью определить ее внутри String, выбросив всю безопасность кода за пределы окна).

Что бы я хотел сделать, чтобы получить это дерево, выглядит примерно так:

object MyObject {
  def method1() = "m1"
}

q"$MyObject" // or q"{MyObject}", I still don't fully understand the table on the Scala guide

Я хочу определить объект, а затем использовать это определение для выполнения над ним некоторых проверок (и, если необходимо, выбросить какое-то исключение во время компиляции), используя макрос. Чтобы использовать макрос, мне нужно дерево (или, по крайней мере, выражение), насколько я понял.

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

Что я здесь не понимаю?

1 Ответ

2 голосов
/ 19 июня 2020

Quasiquote

q"""{object MyObject {
  def method1() = "m1"
}}
"""

или

reify{
  object MyObject {
    def method1() = "m1"
  }
}.tree

- это просто способы написать дерево

Block(
  List(
    ModuleDef(Modifiers(), TermName("MyObject"), 
      Template(
        List(Select(Ident(scala), TypeName("AnyRef"))), 
        noSelfType, 
        List(
          DefDef(Modifiers(), termNames.CONSTRUCTOR, List(), List(List()), TypeTree(), 
            Block(List(pendingSuperCall), Literal(Constant(())))
          ), 
          DefDef(Modifiers(), TermName("method1"), List(), List(List()), TypeTree(), 
            Literal(Constant("m1"))
          )
        )
      )
    )
  ),
  Literal(Constant(()))
)

То же самое можно получить с помощью context.parse (compile- время) / toolBox.parse (время выполнения) из обычного String

val str: String = 
  """object MyObject {
    |  def method1() = "m1"
    |}""".stripMargin

toolBox.parse(str)

Есть время компиляции макросов и время выполнения макросов. Есть время компиляции основного кода и время его выполнения. Время выполнения макросов - это время компиляции основного кода.

MyObject в

object MyObject {
  def method1() = "m1"
}

и MyObject в

q"""{object MyObject {
  def method1() = "m1"
}}
"""

существуют в разных контекстах. Первый существует в текущем контексте, последний существует в контексте сайта вызова макроса.

Вы можете вставить (соединить) дерево в дерево. Вы не можете вставить реальный объект в дерево. Если у вас есть реальный объект (скомпилированное дерево), уже слишком поздно вставлять его в дерево.

Когда вы видите, что что-то вставляется в дерево, это означает, что «что-то» - это просто компактный способ написать дерево т.е. экземпляр типа class Liftable

object MyObject {
  def method1() = "m1"
}

implicit val myObjectLiftable: Liftable[MyObject.type] = new Liftable[MyObject.type] {
  override def apply(value: MyObject.type): Tree =
    q"""
      object MyObject {
        def method1() = "m1"
      }"""
}

q"""
   class SomeClass {
     $MyObject
   }"""

Я думаю, ваш макрос может выглядеть как

def foo[A](a: A) = macro impl[A]

или

def foo[A] = macro impl[A]

, поэтому вы можете называть его как foo(MyObject) или foo[MyObject.type] а внутри

def impl[A: c.WeakTypeTag](c: blackbox.Context)...

у вас есть доступ к weakTypeOf[A], затем к его символу. Имея символ, вы можете иметь подписи методов и т. Д. c.

...