Подтвердить что ты не робот

Как добавить новый класс в плагин Scala для компилятора?

В плагине компилятора Scala я пытаюсь создать новый класс, который реализует ранее существовавшую черту. Пока мой код выглядит так:

def trait2Impl(original: ClassDef, newName: String): ClassDef = {
    val impl = original.impl
    // Seems OK to have same self, but does not make sense to me ...
    val self = impl.self
    // TODO: implement methods ...
    val body = impl.body
    // We implement original
    val parents = original :: impl.parents
    val newImpl = treeCopy.Template(impl, parents, self, body)
    val name = newTypeName(newName)
    // We are a syntheic class, not a user-defined trait
    val mods = (original.mods | SYNTHETIC) &~ TRAIT
    val tp = original.tparams
    val result = treeCopy.ClassDef(original, mods, name, tp, newImpl)
    // Same Package?
    val owner = original.symbol.owner
    // New symbol. What a Position good for?
    val symbol = new TypeSymbol(owner, NoPosition, name)
    result.setSymbol(symbol)
    symbol.setFlag(SYNTHETIC)
    symbol.setFlag(ABSTRACT)
    symbol.resetFlag(INTERFACE)
    symbol.resetFlag(TRAIT)
    owner.info.decls.enter(symbol)
    result
}

Но он, похоже, не добавляется в пакет. Я подозреваю, что это потому, что на самом деле пакет получил "пройденный" перед признаком, который вызывает генерацию, и/или потому, что "переопределение метода def transform (tree: Tree): Tree" метода TypingTransformer может возвращать только одно дерево, для каждого дерева что он получает, поэтому он не может фактически создать новое дерево, но только изменить его.

Итак, как вы добавляете новый класс в существующий пакет? Может быть, это сработает, если я преобразую пакет, когда "transform (Tree)" получит его, но я еще не знаю содержимого пакета, поэтому я не могу создать новый класс раньше (или не мог бы?), Или, возможно, это связано с параметром "Позиция" символа?

До сих пор я нашел несколько примеров, где деревья изменены, но никого, где полностью новый класс создается в плагине компилятора.

4b9b3361

Ответ 1

Полный исходный код здесь: https://gist.github.com/1794246

Хитрость заключается в том, чтобы сохранить вновь созданный ClassDef и использовать их при создании нового PackageDef. Обратите внимание, что вам нужно иметь дело как с символами, так и с деревьями: символ пакета - это просто дескриптор. Чтобы генерировать код, вам нужно сгенерировать AST (как и для класса, где символ содержит имя и тип класса, но код находится в деревьях ClassDef).

Как вы отметили, определения пакетов выше дерева, чем классы, поэтому вам нужно сначала выполнить рекурсию (предполагая, что вы создадите новый класс из существующего класса). Затем, как только пройдены поддеревья, вы можете подготовить новый PackageDef (каждый модуль компиляции имеет определение пакета, которое по умолчанию является пустым пакетом) с новыми классами.

В примере, предполагая, что исходный код

class Foo {
  def foo {
    "spring"
  }
}

компилятор заключает его в

package <empty> {
  class Foo {
    def foo {
      "spring"
    }
  }
}

и плагин преобразует его в

package <empty> {
  class Foo {
    def foo {
      "spring"
    }
  }
  package mypackage {
    class MyClass extends AnyRef
  }
}