Парсер ANTLR с ручным лексером - PullRequest
9 голосов
/ 11 декабря 2010

Я перевожу компилятор языка программирования на C # из ручного лексера / парсера в Antlr.

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

Я обнаружил, что большинство моих головных болей вызваны лексером Antlr, а не парсером. Затем я заметил parser grammar X; и понял, что, возможно, у меня может быть свой написанный вручную лексер, а затем парсер, сгенерированный Antlr.

Так что я ищу дополнительную документацию по этой теме. Я полагаю, что пользовательский ITokenStream мог бы работать, но, по-видимому, практически нет онлайн-документации по этой теме ...

1 Ответ

7 голосов
/ 11 декабря 2010

Я узнал как.Возможно, это не лучший подход, но, похоже, он работает.

  1. Парсеры Antlr получают ITokenStream параметр
  2. Лексеры Antlr сами по себе ITokenSource s
  3. ITokenSource - значительно более простой интерфейс, чем ITokenStream
  4. Самый простой способ преобразовать ITokenSource в ITokenStream - это использовать CommonSourceStream, который получает параметр ITokenSource

Так что теперь нам нужно сделать только 2 вещи:

  1. Настройте грамматику только для синтаксического анализа
  2. Реализация ITokenSource

Настройка грамматики очень проста.Просто удалите все объявления лексера и убедитесь, что вы объявили грамматику как parser grammar.Простой пример приведен здесь для удобства:

parser grammar mygrammar;

options
{
    language=CSharp2;
}

@parser::namespace { MyNamespace }

document:   (WORD {Console.WriteLine($WORD.text);} |
        NUMBER {Console.WriteLine($NUMBER.text);})*;

Обратите внимание, что следующий файл будет выводить class mygrammar вместо class mygrammarParser.

Так что теперь мы хотим реализовать «подделку»лексер.Я лично использовал следующий псевдокод:

TokenQueue q = new TokenQueue();
//Do normal lexer stuff and output to q
CommonTokenStream cts = new CommonTokenStream(q);
mygrammar g = new mygrammar(cts);
g.document();

Наконец, нам нужно определить TokenQueue.TokenQueue не является строго необходимым, но я использовал его для удобства.У него должны быть методы для получения токенов лексера и методы для вывода токенов Antlr.Поэтому, если вы не используете нативные токены Antlr, нужно реализовать метод token-to-Antlr-token.Кроме того, TokenQueue должен реализовывать ITokenSource.

Имейте в виду, что очень важно правильно установить переменные токена.Первоначально у меня были некоторые проблемы, потому что я неправильно просчитал CharPositionInLine.Если эти переменные установлены неправильно, синтаксический анализатор может дать сбой.Кроме того, нормальный канал (не скрытый) имеет значение 0.

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

...