我想使用相同的flex/bison scanner/parser作为解释器和加载要解释的文件。我无法使换行分析在这两种情况下都正常工作。
解释器:有一个提示,我可以按回车键输入终止的命令。
文件:下面是一个示例输入文件:
-----切割---------

begin(
    print("well done"), 1)

----切割-------
所以,在第一行和“(”后面有一个新行,应该吃。
在我的扫描仪里。我有
%%
[ \t]                       {   errorLineCol += strlen(yytext); }

\n                          {   errorLineNumber++;
                                errorLineCol = 0; }

("-"?[0-9])[0-9]*           {   errorLineCol += strlen(yytext);
                                yylval = stringToInteger(yytext);
                                return TINTEGER; }

.....
然后,这适用于文件场景,但不适用于解释器。我必须按下并在输入后附加Ctrl+D。如果我换成
\n                          {   errorLineNumber++;
                                errorLineCol = 0;
                                return 0; }

然后解释器工作,但不读取文件;然后在遇到第一个换行符后停止。解决这个问题的好办法是什么?
编辑:
下面是解析器的顶层:
input: uexpr                        {   parseValue = $1; }
    | /* empty */                   {   parseValue = myNull; }
    | error                         {   parseValue = myNull; }
    ;

uexpr: list
    | atom
    ;

可能的解决方法:似乎是
\n                          {   errorLineNumber++;
                                errorLineCol = 0;
                                if (yyin == stdin) return 0; }

最佳答案

主要问题是解析器函数ypparse在将整个语言缩减为起始符号之前不会返回。
如果你的语法的最高层次是:

language : commands ;

commands : command commands | /* empty */ ;

当然,机器需要一个完整的脚本(按Ctrl-D结束)。如果你的翻译是这个逻辑:
loop:
  print("prompt>")
  yyparse()
  if (empty statement)
    break

它不会工作,因为yyparse在返回之前正在消耗整个脚本。
return 0;解决了这个交互模式的问题,因为标记值0向解析器指示EOF,使它认为脚本已经结束。
我不同意把\n作为代币的解决方案。它只会使语法复杂化(到目前为止,一段不重要的空白现在是有意义的),并且最终不会起作用,因为yyparse函数仍然希望处理完整的语法。也就是说,如果您使用换行符作为标记,但是语法的开始符号表示整个脚本,yyparse仍然不会返回到交互式提示循环。
一个快速而肮脏的方法是让lexer知道交互模式是否有效。如果换行符处于交互模式,则它可以有条件地return 0;。如果输入不是完整的语句,则会出现语法错误,因为脚本作为一个整体结束于换行符。在正常的文件读取模式下,lexer可以不返回就吃掉所有的空白,就像以前一样,允许用一个yyparse来处理整个文件。
如果您希望交互式输入和文件读取而不在lexer中实现两种行为模式,那么您可以做的是更改语法,使其只解析语言的一个语句:函数为您的语言的每个顶级语句返回yyparse。(lexer像以前一样吃新行,不返回0)。也就是说,语法的开始符号只是一个语句(可能是空的)。然后,文件解析器必须实现为一个循环(由您编写),该循环调用yyparse从文件中获取所有语句,直到yyparse遇到空输入为止。这种方法的缺点是,如果用户键入不完整的语法(例如,悬空的左括号),解析器将继续扫描输入,直到满意为止。这是不友好的,就像使用scanf进行交互式用户输入的程序一样(这是同一个问题:scanf是一个解析器,在满足之前不会返回)。
另一种可能是有一个交互模式,它执行自己的用户输入,而不是调用yyparse来获取输入并解析它。在此模式下,您将用户的输入读取到行缓冲区中。然后让解析器处理行缓冲区。处理行缓冲区而不是FILE *流是完全可能的。您只需编写自定义输入处理(您自己定义的YY_INPUT宏)。如果您实现了一个带有行编辑和历史回忆的良好交互模式,例如使用libeditGNU readline,那么您最终将需要这种方法。

09-08 00:09