我正在编写一个基本的解析器,它读取表单stdin并将结果打印到stdout。问题是我遇到了这个语法的麻烦:
%token WORD NUM TERM
%%
stmt: /* empty */
| word word term { printf("[stmt]\n"); }
| word number term { printf("[stmt]\n"); }
| word term
| number term
;
word: WORD { printf("[word]\n"); }
;
number: NUM { printf("[number]\n"); }
;
term: TERM { printf("[term]\n"); /* \n */}
;
%%
当我运行程序时,我输入: hello world \ n 输出(如我所料) [word] [word] [term] [stmt] 。到目前为止,这么好,但是如果我输入: hello world \ n (再次),我得到语法错误[word] [term] 。
当我输入 hello world \ n (第三次)时,它会起作用,然后再次失败,然后它会起作用,依此类推。
我在这里错过了一些明显的东西吗?
(我在手工卷制编译器方面有一些经验,但我没有使用lex / yacc等。)
这是主要功能:
int main() {
do {
yyparse();
} while(!feof(yyin));
return 0;
}
任何帮助将不胜感激。谢谢!
答案 0 :(得分:2)
您的语法识别单个stmt
。 Yacc / bison期望语法描述整个输入,因此在识别语句之后,解析器等待输入结束指示。但它没有得到一个,因为你输入了第二个声明。这会导致解析器报告语法错误。但请注意,它现在已经读取了第二行中的第一个标记。
您在循环中调用yyparse()
并且在获得语法错误返回值时不停止。因此,当您再次呼叫yyparse()
时,它将在最后一个停止的位置继续,这位于第二行中的第二个令牌之前。剩下的只是一个单词,然后正确解析。
你可能应该做的是编写你的解析器,使它接受任意数量的语句,并且可能使它在遇到错误时不会死亡。这看起来像这样:
%%
prog: %empty
| prog line
line: stmt '\n' { puts("Got a statement"); }
| error '\n' { yyerrok; /* Simple error recovery */ }
...
请注意,只有在我知道正确解析了该行之后,才会为语句打印消息。这通常会变得更加混乱。但最好的解决方案不是使用printf,而是使用Bison的跟踪工具,就像将-t
放在bison命令行上并设置全局变量{{1}一样简单。 }。见Tracing your parser