Разбор блока кода с выражением EBNF
Я использую CocoR для создания java-подобного сканера / парсера:
У меня проблемы с созданием выражения EBNF для соответствия кодовому блоку:
Я предполагаю, что блок кода окружен двумя хорошо известными токенами: <& and &>example:
public method(int a, int b) <&
various code
&>
Если я определю нетерминальный символ
codeblock = "<&" {ANY} "&>"
Если код внутри двух символов содержит символ "<", сгенерированный компилятор не будет обрабатывать его, что приведет к синтаксической ошибке.
Любой намек?
Редактировать:
COMPILER JavaLike
CHARACTERS
nonZeroDigit = "123456789".
digit = '0' + nonZeroDigit .
letter = 'A' .. 'Z' + 'a' .. 'z' + '_' + '$'.
TOKENS
ident = letter { letter | digit }.
PRODUCTIONS
JavaLike = {ClassDeclaration}.
ClassDeclaration ="class" ident ["extends" ident] "{" {VarDeclaration} {MethodDeclaration }"}" .
MethodDeclaration ="public" Type ident "("ParamList")" CodeBlock.
Codeblock = "<&" {ANY} "&>".
Я упустил некоторые постановки ради простоты.
Это моя фактическая реализация грамматики. Основная ошибка заключается в том, что она не работает, если код в блоке содержит один из символов ">" или "&".
2 ответа
Ник, опоздал на вечеринку здесь...
Есть несколько способов сделать это:
Определить токены для <&
а также &>
так что лексер знает о них.
Вы можете использовать директиву COMMENTS
КОММЕНТАРИИ ОТ <&
К &>
- цитирует, как ожидает CoCo.
Или сделайте взломать NextToken() в вашем файле scanner.frame. Сделайте что-то вроде этого (псевдокод):
if (Peek() == CODE_START)
{
while (NextToken() != CODE_END)
{
// eat tokens
}
}
Или можете переопределить метод Read() в буфере и питаться на самом низком уровне.
НТН
Вы можете расширить ЛЮБОЙ термин, чтобы включить <&
, &>
и еще один нетерминал (назовите это ANY_WITHIN_BLOCK скажем).
Тогда вы просто используете
ANY = "<&" | {ANY_WITHIN_BLOCK} | "&>"
codeblock = "<&" {ANY_WITHIN_BLOCK} "&>"
И тогда значение {ЛЮБОЙ} не изменится, если оно вам действительно понадобится позже.
Хорошо, я ничего не знал о CocoR и дал вам бесполезный ответ, так что давайте попробуем еще раз.
Как я начал говорить позже в комментариях, я чувствую, что реальная проблема заключается в том, что ваша грамматика может быть слишком свободной и недостаточно четко заданной.
Когда я написал CFG для одного языка, который я пытался создать, я использовал некий подход "встретиться посередине": я написал структуру верхнего уровня И непосредственные низкоуровневые комбинации токенов. сначала, а затем работал над тем, чтобы заставить их встретиться на среднем уровне (примерно на уровне условностей и потока управления, я полагаю).
Вы сказали, что этот язык немного похож на Java, поэтому позвольте мне показать вам первые строки, которые я напишу в качестве первого черновика для описания его грамматики (в псевдокоде, извините. На самом деле это похоже на yacc/bison. И здесь я использую ваши скобки вместо Java):
/* High-level stuff */
program: classes
classes: main-class inner-classes
inner-classes: inner-classes inner-class
| /* empty */
main-class: class-modifier "class" identifier class-block
inner-class: "class" identifier class-block
class-block: "<&" class-decls "&>"
class-decls: field-decl
| method
method: method-signature method-block
method-block: "<&" statements "&>"
statements: statements statement
| /* empty */
class-modifier: "public"
| "private"
identifier: /* well, you know */
И в то же время, когда вы все это делаете, определите ваши непосредственные комбинации токенов, например, определив "число" как число с плавающей точкой или целое число, а затем создав правила для добавления / вычитания / и т.д. их.
Пока я не знаю, какой у вас подход, но вы определенно хотите убедиться, что вы тщательно все определили и используете новые правила, когда вам нужна конкретная структура. Не смешите себя при создании однозначных правил, но никогда не бойтесь создавать новое правило, если оно поможет вам лучше организовать свои мысли.