我想使用相同的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
宏)。如果您实现了一个带有行编辑和历史回忆的良好交互模式,例如使用libedit
或GNU readline
,那么您最终将需要这种方法。