Одним из решений может быть использование восстановления после ошибки 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
. Но в целом, я думаю, что это хороший пример.