Бизон: как игнорировать токен, если он не вписывается в правило - PullRequest
7 голосов
/ 23 июля 2011

Я пишу программу, которая обрабатывает комментарии, а также некоторые другие вещи. Если комментарий находится в определенном месте, то моя программа что-то делает.

Flex передает токен при нахождении комментария, и Бизон смотрит, подходит ли этот токен определенному правилу. Если это так, то он выполняет действие, связанное с этим правилом.

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

Мой вопрос:
Как я могу использовать токен, если он вписывается в правило, но игнорировать его, если нет? Можно ли сделать токен «необязательным»?

(Примечание: единственный способ, которым я могу думать о том, чтобы сделать это прямо сейчас, - это разбрасывать маркер комментария в каждом возможном месте в каждом возможном правиле. Должно быть лучшее решение, чем это. Может быть, какое-то правило, включающее корень?)

Ответы [ 2 ]

4 голосов
/ 22 июля 2012

Одним из решений может быть использование восстановления после ошибки Bison (см. Руководство Bison ).

Подводя итог, bison определяет токен терминала error для представления ошибки (скажем, токен комментария возвращен не в том месте). Таким образом, вы можете (например) закрыть круглые скобки или фигурные скобки после того, как найден своенравный комментарий. Тем не менее, этот метод, вероятно, откажется от определенного количества разборов, потому что я не думаю, что зубр может "отменить" сокращения. («Пометка» ошибки, как и при печати сообщения в stderr, не связана с этим: вы можете иметь ошибку без печати ошибку - это зависит от того, как вы определяете yyerror.)

Вместо этого вы можете захотеть обернуть каждый терминал в специальный нетерминал:

term_wrap: comment TERM

Это эффективно делает то, что вы боитесь делать (вставлять комментарии в каждое правило), но делает это в меньшем количестве мест.

Чтобы заставить себя есть свою собачью еду, я придумал для себя глупый язык. Единственный синтаксис - print <number> please, но если между числом и please есть (по крайней мере) один комментарий (##), вместо этого он печатает число в шестнадцатеричном формате.

Как это:

print 1 please
1
## print 2 please
2
print ## 3 please
3
print 4 ## please
0x4
print 5 ## ## please
0x5
print 6 please ##
6

Мой лексер:

%{
#include <stdio.h>
#include <stdlib.h>
#include "y.tab.h"
%}

%%

print           return PRINT;
[[:digit:]]+    yylval = atoi(yytext); return NUMBER;
please          return PLEASE;
##              return COMMENT;

[[:space:]]+    /* ignore */
.               /* ditto */

и парсер:

%debug
%error-verbose
%verbose
%locations

%{
#include <stdio.h>
#include <string.h>

void yyerror(const char *str) {
        fprintf(stderr, "error: %s\n", str);
}

int yywrap() {
        return 1;
} 

extern int yydebug;
int main(void) {
    yydebug = 0;
    yyparse();
}
%}

%token PRINT NUMBER COMMENT PLEASE

%%

commands: /* empty */
        |
        commands command
    ;

command: print number comment please {
        if ($3) {
            printf("%#x", $2);
        } else {
            printf("%d", $2);
        }
        printf("\n");
     }
     ;

print: comment PRINT
     ;

number: comment NUMBER {
        $$ = $2;
      }
      ;

please: comment PLEASE
      ;

comment: /* empty */ {
            $$ = 0;
       }
       |
        comment COMMENT {
            $$ = 1;
        }
    ;

Так что, как видите, не совсем ракетостроение, но оно делает свое дело. Там есть конфликт сдвига / уменьшения из-за пустой строки, совпадающей с comment в нескольких местах. Кроме того, нет никакого правила, чтобы помещать комментарии между финальными please и EOF. Но в целом, я думаю, что это хороший пример.

1 голос
/ 23 июля 2011

Рассматривать комментарии как пробел на уровне лексераНо оставьте два отдельных правила, одно для пробелов и одно для комментариев, оба возвращают один и тот же идентификатор токена.

  • Правило для комментариев (+ необязательные пробелы) отслеживает комментарии в выделенной структуре.
  • Правило для пробелов сбрасывает структуру.

Когда вы вводите это «конкретное место», посмотрите, был ли последний пробел комментарием, или вызовите ошибку.

...