一种解决方案可能是使用bison的错误恢复功能(请参见Bison手册)。
bison将终端令牌error
定义为表示错误的符号(例如,一个在错误位置返回的注释令牌)。这样,您可以在找到错误的注释后关闭括号或花括号。然而,这种方法可能会丢弃一定量的分析,因为我认为bison无法“撤消”规约。(与向stderr打印消息一样,“标记”错误与此无关:您可以有一个错误而不打印错误-这取决于您如何定义yyerror
。)
相反,您可能希望将每个终端包装在一个特殊的非终结符中:
term_wrap: comment TERM
这个实际上做了你害怕做的事情(在每个规则中都放置注释),但只需要在少数地方进行操作。
为了迫使自己吃自己的狗粮,我为自己编写了一个愚蠢的语言。唯一的语法是print <number> please
,但如果在数字和please
之间有至少一条注释(##
),它就会以十六进制形式打印该数字。
像这样:
print 1 please
1
2
print
3
print 4
0x4
print 5
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:]]+
.
并且解析器:
%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:
|
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: {
$$ = 0;
}
|
comment COMMENT {
$$ = 1;
}
;
所以,如你所见,这并不是什么高深的科学,但它确实有用。因为在多个位置匹配空字符串 comment
,所以出现了移位/规约冲突。此外,在最后一个please
和EOF
之间没有适配注释的规则。但总体来说,我认为这是一个很好的例子。
a = b + c ;
与a=b+c;
...痛苦! - Jonathan Leffler