不稳定的解析器。相同的语法,相同的输入,循环产生不同的结果。我错过了什么?

Erratic parser. Same grammar, same input, cycles through different results. What am I missing?

我正在编写一个基本的解析器,它读取标准输入并将结果打印到标准输出。问题是我在使用这个语法时遇到了麻烦:

%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,我会收到 syntax error [word][term].
当我输入 hello world\n(第三次)时,它起作用了,然后再次失败,然后又起作用,依此类推。

我是不是遗漏了什么明显的东西?

(我有一些手动编译器的经验,但我没有使用过 lex/yacc 等)

这是主要功能:

int main() {
    do {
        yyparse();
    } while(!feof(yyin));

    return 0;
}

如有任何帮助,我们将不胜感激。谢谢!

您的语法只识别一个 stmt。 Yacc/bison 期望语法描述整个输入,因此在语句被识别后,解析器等待 end-of-input 指示。但是它没有得到一个,因为你输入了第二个语句。这会导致解析器报告语法错误。但请注意,它现在已经读取了第二行中的第一个标记。

您正在循环调用 yyparse(),并且在收到语法错误 return 值时没有停止。因此,当您再次调用 yyparse() 时,它将从最后一个停止的地方继续,也就是第二行中第二个标记之前。剩下的只是一个单词,然后它会正确解析。

您可能应该做的是编写您的解析器,以便它接受任意数量的语句,也许这样它就不会在遇到错误时死掉。那看起来像这样:

%%
prog: %empty
    | prog line
line: stmt '\n'    { puts("Got a statement"); }
    | error '\n'   { yyerrok; /* Simple error recovery */ }
...

请注意,只有在我知道该行已被正确解析后,我才会为语句打印一条消息。这通常会减少混淆。但最好的解决方案不是使用 printf,而是使用 Bison 的跟踪工具,这就像在 bison 命令行上输入 -t 并设置全局变量 yydebug = 1; 一样简单。参见 Tracing your parser