Создание парсера для игнорирования строкового комментария, кроме завершающего комментария - PullRequest
0 голосов
/ 01 июня 2018

Я использую Jison (версия Bison на Javascript, очень похожая).

Цель

Я хочу проанализировать вход и получить действительные токены (IDENTIFIERи trailing comments)

Мое определение

  • IDENTIFIER
    • Слово содержит алфавиты
  • A line comment
    1. Целая строка, начинающаяся с -- и нуля или более букв;или
    2. Целая строка, начинающаяся с одного или нескольких пробелов (пробелы, табуляции), за которыми следуют -- и ноль или более букв
  • A trailing comment
    • Строка, которая начинается с --, но следует за IDENTIFIER Для простоты, давайте предположим, что у меня есть входной файл для анализа:

Ввод

I want -- not to ignore this
-- This must be ignored

Чтобы разобрать вышесказанное, я пишу файл Jison / Bison (см. Ниже), но он неполон и анализирует только содержимое без комментариев, например:

Вывод (текущий, неправильный))

I
want

Но я хочу, чтобы анализатор воспринял завершающий комментарий как единственную допустимую строку, например:

Вывод (ожидается)

I
want
-- not to ignore this

УчитываяФайл Jison / Bisnon, как показано ниже, как я могу изменить его, как я предполагал?

Jison / Bison

%lex
%%

\s+         /* skip whitespace */
'--'.*      /* skip comment */
[a-zA-Z]+   return 'IDENTIFIER'
<<EOF>>     return 'EOF'
.           /* skip the others */

/lex

%start expressions
%%

expressions
    : expressions EOF
    | expressions expression
    | expression
        {;}
    ;

expression
    : 'IDENTIFIER'
        {console.log($1); $$ = $1;}
    ;

1 Ответ

0 голосов
/ 01 июня 2018

В (f) lex, я бы просто использовал правило, начинающееся с ^, чтобы заставить его совпадать только в начале строки, но jison не так интерпретирует значения, и в любом случае вы не делаетена самом деле не хочу ограничивать начальные комментарии точным началом строки, а скорее первым непробельным символом в строке.(Мне не ясно, как вы хотите обрабатывать токены, отличные от идентификаторов. Я проигнорировал эту проблему. См. Комментарий, начинающийся с трех вопросительных знаков.)

Один простой способ сделать это с "условиями запуска""(см. пример в документации Jison ), например: (но, пожалуйста, смотрите примечание ниже для лучшего решения) :

%lex
%s TRAILING
%%

\n                  this.begin('INITIAL')
\s                  /* skip whitespace */
<TRAILING>'--'.*    return 'IDENTIFIER'; /* or whatever */
<INITIAL>'--'.*     /* skip initial comments */
[a-zA-Z]+           this.begin('TRAILING'); return 'IDENTIFIER'
.                   /* ??? this.begin('TRAILING'); */ /* skip the others */
<<EOF>>             return 'EOF'

Примечание: (Добавлено после того, как я запомнил это условие запуска Jison.) Приведенный выше код сильно зависит от интерфейса условий запуска (f) lex.И jison, и flex (но не lex) допускают стек начальных условий, что удобно, когда контексты гнездятся.Как показывает приведенный выше пример, контексты не всегда являются вложенными;иногда удобнее просто переключаться с одного на другой, как в автомате.Макрос lex BEGIN (f) делает именно это;flex также предоставляет yy_push_state и yy_pop_state для использования стека условий запуска.(В flex вы должны указать %option stack, чтобы это работало.)

Jison, по какой-то причине, только обеспечивает переходы, ориентированные на стек, а this.begin - это просто псевдоним дляthis.pushStack.Следовательно, в отличие от гибкого интерфейса, вы действительно должны балансировать каждый this.begin с this.popStack (и, возможно, изменить this.begin на this.pushStack, чтобы быть менее запутанным).Вышеприведенный код, как написано, выполняет много бесполезных нажатий стека условий запуска и никаких всплывающих окон, поэтому он будет излишне использовать много памяти, особенно при большом вводе.

Приведенный выше пример можно переписать, чтобы использовать стек болееэкономно, но это потребовало бы либо дублирования большинства правил шаблона (одна версия для условия INITIAL, которая выдвигается, когда ИДЕНТИФИКАТОР найден, но не появляется на новых строках, и другая версия для TRAILING, которая не выдвигает IDENTIFIER, но делаетпоп в правило новой строки).Это кажется мне немного уродливым, поэтому я предоставляю следующую альтернативную реализацию, которая использует настраиваемое поле в объекте лексера для хранения номера строки ранее обнаруженного IDENTIFIER и добавляет комментарии к выводу, только если они находятся в одной строке:

%lex
%%

\s+             /* skip whitespace */
'--'.*          if (yylloc.first_line == this.last_id_line) return 'IDENTIFIER';
[a-zA-Z]+       this.last_id_line = yylloc.first_line; return 'IDENTIFIER'
.               /* skip the others */
<<EOF>>         return 'EOF'

Добавление пользовательских полей в объект lexer должно выполняться с осторожностью;нет документации, определяющей, какие имена зарезервированы (или какие префиксы имен могут быть использованы).

...