我想为解释器使用相同的flex / bison扫描器/解析器以及加载要解释的文件。在这两种情况下,我都无法让换行解析正常工作。
- - - - 切---------
begin(
print("well done"), 1)
----切-------
所以,第一行和'('应该被吃掉之后有一个换行符。
在我的scanner。我有
%%
[ \t] { errorLineCol += strlen(yytext); }
\n { errorLineNumber++;
errorLineCol = 0; }
("-"?[0-9])[0-9]* { errorLineCol += strlen(yytext);
yylval = stringToInteger(yytext);
return TINTEGER; }
.....
这适用于文件场景,但不适用于解释器。我必须按下并在ENTER后按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; }
答案 0 :(得分:2)
如果按ENTER键终止命令,则词法分析器应返回\ n的标记。返回0告诉解析器输入源是完整的(文件的文件结尾或终端的^ D)。在你的语法中添加行尾令牌,让词法分析器在看到\ n时返回。
ETA:但是不要忘记处理不以ENTER结尾的最后一行的情况。让你的词法分析器在文件末尾返回一个行尾令牌,除非最后一个字符是\ n。
答案 1 :(得分:2)
主要问题是您的解析器函数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
仍然不会返回到您的交互式提示循环。
快速而肮脏的黑客是让词法分析者知道交互模式是否有效。然后,如果换行符处于交互模式,它可以为换行符的每个实例设置条件return 0;
。如果输入不是完整语句,则会出现语法错误,因为整个脚本在换行符处结束。在普通文件读取模式下,词法分析器可以在不返回的情况下占用所有空格,就像之前允许使用单个yyparse
处理整个文件一样。
如果您希望交互式输入和文件读取而不在词法分析器中实现两种行为模式,那么您可以做的是更改语法,以便它只解析一种语言语句:yyparse
函数返回每个顶部你的语言水平声明。 (并且lexer像以前一样吃新行,没有返回0)。即语法的开始符号只是一个语句(可能是空的)。然后你的文件解析器必须实现为一个循环(由你编写),它调用yyparse来获取文件中的所有语句,直到yyparse
遇到空输入。这种方法的缺点是如果用户键入不完整的语法(例如悬空开括号),解析器将继续扫描输入直到满足为止。这是不友好的,就像使用scanf
进行交互式用户输入的程序一样(这是同样的问题:scanf
是一个在满意之前不会返回的解析器。)
另一种可能性是使用交互模式执行自己的用户输入,而不是调用yyparse来获取输入和解析它。在此模式下,您将用户的输入读入行缓冲区。然后你有解析器处理行缓冲区。完全可以处理行缓冲区而不是FILE *
流。您只需编写自定义输入处理(您自己定义的YY_INPUT
宏)。如果您使用行编辑和历史记录调用实现一个体面的交互模式,这就是您最终需要的方法。使用libedit
或GNU readline
。