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

#scala #plugins #compiler-construction

#scala #Плагины #построение компилятора

Вопрос:

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

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

Что я хотел бы сделать, и для чего я еще не нашел примера, так это как мне сгенерировать код внутри плагина компилятора Scala таким же образом, как и в плагине компилятора Java. То есть я сопоставляю / нахожу, где используется моя аннотация, получаю AST для аннотированного интерфейса, а затем прошу API предоставить мне поток / запись, в котором я вывожу сгенерированный исходный код Scala, используя манипуляции со строками.

Эту последнюю часть я не смог найти. Итак, как мне указать API создать новый исходный файл Scala и предоставить мне поток / запись / файл / дескриптор, чтобы я мог просто писать в нем, и когда я закончу, компилятор Scala скомпилирует его в том же прогоне, в котором был вызван плагин?

Зачем мне это делать? Во-первых, потому что оба плагина имеют одинаковую структуру, поэтому обслуживание простое. Во-вторых, я хочу использовать его с открытым исходным кодом, и просто невозможно поддерживать все опции, которые кто-либо захочет, поэтому я ожидаю, что потенциальные пользователи захотят расширить генерацию своим собственным кодом. Это будет намного проще для них, если им просто нужно будет выполнить printf() вместо изучения AST API (это также относится ко мне).

Ответ №1:

Короткий ответ:

Это невозможно сделать

Длинный ответ:

Возможно, вы могли бы сгенерировать свой исходный файл и передать его через экземпляр синтаксического анализатора в вашем плагине. Но это никоим образом не может быть вам полезно, потому что теперь у вас будет более серьезная проблема, с которой нужно бороться:

Чтобы получить всю информацию о типе / имени для генерации delagate / прокси, вам нужно будет выбрать AST аннотированного типа после того, как он пройдет через фазы namer и typer (которые неразделимы). Загвоздка в том, что любые попытки вызвать ваш сгенерированный код уже будут иметь неудачную проверку типов, компилятор выдаст ошибку, и любые дальнейшие ставки отменяются.

Синтез метода возможен в ограниченных случаях, если вы можете каким-то образом обмануть средство проверки типов достаточно долго, чтобы сгенерировать ваш код, и это трюк, который я использовал с помощью моего плагина Autoproxy ‘lite’. Даже тогда вам гораздо лучше работать с TreeDSL генерацией кода, а не выкачивать исходный код.

Комментарии:

1. Таким образом, вы можете сгенерировать «код» как AST, который компилятор будет обрабатывать должным образом?

2. TreeDSL позволяет очень легко добиться того же эффекта, что и при написании кода. Но имейте в виду проблемы, связанные с синтезом класса / метода, и как это невозможно сделать после проверки типа.

3. Существуют ли концептуальные причины не разрешать плагинам выполнять действия типа «Забыть результат анализа имени / типа и повторить», или это просто не реализовано?

4. Внутри компилятор использует изменяемую таблицу символов, которая является общей для всех модулей. Я попробовал технику или запустил «ранний» типизатор (с подавленными ошибками) перед плагином. К сожалению, это привело к тому, что таблица символов оказалась в несогласованном состоянии. Я помню, что компаньоны class / singleton были здесь особенно сложными.

Ответ №2:

Кевин совершенно прав, но для полноты картины стоит упомянуть, что есть еще одна альтернатива — написать плагин компилятора, который генерирует исходный код. Это подход, который я принял в Borachio. Это не очень удовлетворительное решение, но его можно заставить работать.

Редактировать — я просто перечитал ваш вопрос и понял, что вы на самом деле спрашиваете о создании исходного кода в любом случае

Таким образом, для этого нет прямой поддержки, но в основном это просто вопрос открытия файла и записи соответствующих инструкций «print». Невозможно вызвать компилятор «внутри» плагина AFAIK, но я написал плагин sbt, который скрывает большую часть сложности повторного вызова компилятора.

Комментарии:

1. Теперь я понял. Это был не «плагин компилятора», а «плагин sbt». Я полагаю, что вместо этого это может быть плагин ant или maven, но дело в том, что он не может быть плагином компилятора, поэтому я все еще не могу сделать это так.