DSL, ANTLR и Groovy в одном сообщении в блоге? Ах, да, это должно быть хорошо: trifecta из интересных ключевых слов. Есть ужасное зло на основанных на Groovy доменных языках, и нет, я не говорю о фигурных скобках. Этот ужасный синтаксис, блокирующий наших пользователей от производительности на естественном языке, — это коварное объединение около 5 пикселей, называемое «запятая». Если бы мы только могли избавить себя и наших пользователей от этого ужасного бремени!
Некоторые пытаются: GEP-3 — это предложение по улучшению Groovy, называемое «DSL на основе командного выражения», которое позволяет использовать некоторые запятые как необязательные. Хотя интересный, в последнее время не было большой общественной активности на нем. Одним из требований удаления запятых из вызовов методов является то, что «оценка должна быть легко объяснима». Я думаю, что большинство людей согласны с тем, что у нас есть еще много работы, прежде чем предложение будет легко объяснено
Так, где это оставляет нас? Groovy навязывает эти поддельные запятые ничего не подозревающим программистам? Едва. Архитектура компилятора Groovy достаточно открыта, и, проявив немного творческого подхода, вы можете сделать запятые необязательными в вашем DSL. Пока у вас есть доступ к CompilerConfiguration, у вас есть опции, будь то преобразование AST или плагин ANTLR. И помните, если вы используете GroovyShell, у вас есть доступ к нему.
В качестве примера рассмотрим синтаксис easyb для поведения. То, что я хотел бы видеть, это запятая. Мммм … это выглядит намного лучше без запятых:
given "some data" {
println '... setting expectations'
}
when "a method is called" {
println '... calling some method'
}
then "some condition should exist" {
println '... making an assertion'
}
В какой-то момент процесса компиляции этот исходный код будет представлен в виде текстового потока. Мы собираемся перехватить этот текстовый поток и предоставить несколько простых правил перезаписи, чтобы добавить запятую в нужном месте. Плагин ANTLR может перехватить этот текст, добавить в него запятые, а затем передать его только компилятору Groovy: Groovy не мудр. Существует некоторая стандартная разводка, но основная часть работы заключается в определении правила перезаписи (также известного как производство). Для простого случая мы можем использовать регулярное выражение для добавления запятой в:
String addCommas(text) {
def pattern = ~/(.*)(given|when|then) "([^"\\]*(\\.[^"\\]*)*)" \{(.*)/
def replacement = /$1$2 "$3", {$4/
(text =~ pattern).replaceAll(replacement)
}
Хорошо ли масштабируется регулярное выражение для больших проблем? Нет, правда. В какой-то момент вам понадобится альтернатива (возможно, даже в этот момент!). Например, это регулярное выражение соответствует вложенным кавычкам, но кавычки должны быть двойными, а одинарные кавычки и многострочные строки Groovy не поддерживаются. Ну что ж, это всего лишь пример.
«Шаблон соединения» состоит из подкласса AntlrParserPlugin, чтобы вы могли написать текст, и подкласса ParserPluginFactory, чтобы вы могли подключиться к своему подклассу AntlrParserPlugin. Затем ParserPluginFactory может быть передан непосредственно в CompilerConfiguration, которая передается в GrovoyShell. Это не имеет смысла для меня, даже когда я пишу это, поэтому, вероятно, лучше всего посмотреть полный список исходного кода в Groovy Web Console.
Для тех из вас, кто использует браузеры, не поддерживающие теги привязки, вот код в строке:
class SourceModifierParserPlugin extends AntlrParserPlugin {
Reduction parseCST(SourceUnit sourceUnit, Reader reader) throws CompilationFailedException {
def text = addCommas(reader.text)
StringReader stringReader = new StringReader(text)
super.parseCST(sourceUnit, stringReader)
}
}
def parserPluginFactory = new ParserPluginFactory() {
ParserPlugin createParserPlugin() {
new SourceModifierParserPlugin()
}
}
def conf = new CompilerConfiguration(pluginFactory: parserPluginFactory)
def binding = ...
def shell = new GroovyShell(binding, conf)
И когда у вас есть GroovyShell, вы можете оценить мир! Включая псевдо-easyb скрипт из начала поста. Работает без проблем … пропущены запятые и все.
Плагины ANTLR уже давно используются в Groovy, и этот пример основан на знаменитом скрипте Groovy Web Console Гийома Лафоржа № 3 . В написании плагина ANTLR нет ничего особенно сложного, но с его поддержкой может быть что-то сложное. DSL поставляются с множеством проблем, включая управление версиями. Если вы создаете внешний DSL, то вы опубликовали язык. Сначала весело, но не намного позже.
И там у вас есть это. Нет больше этих гадких запятых! Теперь нам просто нужно что-то сделать с этими доступными DSL .