Как правильно обрабатывать блоки кода C ++ в Xtext / ANTLR?
Мы пишем плагин eclipse на основе Xtext для DSL, который поддерживает добавление кода на уровне функций C ++ в четко определенных областях (в основном serial { /* ... */ }
блоки) такие как это:
module m {
chare c {
entry void foo() {
serial {
// C++ code block
}
}
}
}
Увидеть Вот для более полного примера. Затем он передается внешнему инструменту для обработки дальнейших шагов компиляции / компоновки, поэтому мы не генерируем никакого кода из затмения.
Проблема здесь в том, как обрабатывать эти блоки кода C ++, особенно если учесть, что они могут содержать собственные скобки. Это очень похоже на Как включить Java Code Block в Xtext DSL? но на данный момент мы довольны только игнорированием этого блока (то есть отсутствие поддержки содержимого или подсветки синтаксиса не является идеальным, но приемлемым).
В нашем инструменте на основе bison / flex это делается путем разделения переменной между синтаксическим анализатором и лексером, которая включает «режим синтаксического анализа C ++» в рамках определенных правил грамматики, который заставляет лексер возвращать токен CPROGRAM для всего, кроме соответствующих разделителей (например, фигурных скобок. Кажется, что естественный перевод имеет собственный лексер ANTLR, который использует семантические предикаты для того же эффекта, например
RULE_NON_BRACES: {in_braces}? ~('{' | '}')+;
как первое лексическое правило, но я не могу найти, как получить доступ к этой глобальной переменной из грамматики Xtext, так как, похоже, нет понятия «действие правила», как в bison. Существуют другие не «последовательные» контексты грамматики, где ожидается код C ++, поэтому должна быть некоторая координация между синтаксическим анализатором и лексером.
Ваш вопрос кажется более сфокусированным на том, как лексер DSL не теряется в коде C ++. Основной ответ вам нужно матч круглые скобки (например, убедитесь, что они правильно вложены).
Я не знаю, как вы определяете лексическое правило Xtext / ANTLR для этого; Я предполагаю, что есть отвратительный способ перейти к процедурному коду и начать читать символы один за другим. Это может иметь некоторые осложнения; вашей логике сопоставления парен может быть нужно беспокоиться о различных типах цитирования в коде C ++. Например,
{ // this } isn't a match
а также
{ char x[]="} this isnt a match { either" }
Другие строковые кавычки C ++ могут сделать это еще труднее увидеть. Что вы будете делать с макросом C ++, используемым следующим образом?
{
#define rcb }
{ rcb
}
Вам, вероятно, придется установить некоторые особые правила о том, как} обрабатывается во встроенном коде C ++, и ваше посимвольное сканирование должно знать это правило.
Вместо того, чтобы сделать это сложным, я думаю, что вам следует выбрать действительно маловероятную последовательность символов в C ++ в качестве завершения, например,
][[
который я считаю, не может произойти в C ++, кроме как в строке или комментарии, или
}}}
и просто используйте это. Не нужно совпадать с Parens на всех. Почти во всех случаях к C ++ не нужно прикасаться; в редком, редком случае, когда она содержит эту последовательность, ее исправляет тривиальное редактирование (вставка пробела или перенос строки). Теперь ваше правило лексера простое и может быть выражено (я думаю) с помощью стандартного лексера.
Если вы пойдете этим путем, я бы предложил вам выбрать соответствующую начальную последовательность для введения кода C ++, просто чтобы напомнить читателю, что требуется забавная последовательность, например,
serial {{{ <C++code> }}}
или же
serial ]][ <C++code> ][[
С этим соглашением даже мой уродливый пример макроса прост:
serial {{{
{
#define rcb }
{ rcb
}
}}}
PS: этот забавный нотационный трюк называется «выходом из домена (нотации)». Эта проблема возникает в каждой системе (да, не так много в дикой природе, но у меня есть одна :), которая позволяет смешивать несколько обозначений. Последовательность зависит от языка / системы в зависимости от вкуса.
Если вы действительно не можете изменить синтаксис и вам нужно полагаться на соответствующие фигурные скобки, то вам нужно переопределить свое решение на основе flex в Java (например, использовать jflex) и заставить Xtext использовать этот лексер.
Я кратко рассмотрел это в этом Сообщение блога. Он также содержит указатель на пример кода, где я использовал основанный на jflex лексер в Xtext.