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

时间:2018-03-13 15:39:23

标签: grammar bison yacc lex

我正在编写一个基本的解析器,它读取表单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;
}

任何帮助将不胜感激。谢谢!

1 个答案:

答案 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