主要问题是你的解析器函数ypparse
直到将整个语言缩减为起始符号后才返回。
如果你的语法的顶层是这样的:
language : commands ;
commands : command commands | /* empty */ ;
当然,机器会期望一个完整的脚本(通过按 Ctrl-D 终止)。如果你的解释器是这样的逻辑:
loop:
print("prompt>")
yyparse()
if (empty statement)
break
从那以后它就行不通了yyparse
返回之前正在消耗整个脚本。
The return 0;
解决了此交互模式的问题,因为令牌值 0 表示EOF
到解析器,使其认为脚本已经结束。
我不同意制作的解决方案\n
一个令牌。它只会使语法复杂化(迄今为止无关紧要的空白现在很重要)并且最终不起作用,因为yyparse
函数仍然想要处理完整的语法。也就是说,如果你有换行符作为标记,但语法的开始符号代表整个脚本,yyparse
仍然不会返回到交互式提示循环。
一个快速而肮脏的技巧是让词法分析器知道交互模式是否有效。那么就可以有条件地return 0;
对于换行符的每个实例(如果处于交互模式)。如果输入不是完整的语句,则会出现语法错误,因为整个脚本以换行符结束。在正常的文件读取模式下,您的词法分析器可以吃掉所有空白而不返回,就像以前允许使用单个处理整个文件一样yyparse
.
如果您想要交互式输入和文件读取而不在词法分析器中实现两种行为模式,您可以做的是更改语法,使其仅解析该语言的一个语句:yyparse
函数返回您语言的每个顶级语句。 (词法分析器像以前一样吃换行符,不返回 0)。即语法的起始符号只是一个语句(可能是空的)。然后你的文件解析器必须实现为一个循环(由你编写),它调用 yyparse 从文件中获取所有语句,直到yyparse
遇到空输入。这种方法的缺点是,如果用户输入不完整的语法(例如悬空的左括号),解析器将继续扫描输入,直到满意为止。这是不友好的,就像使用的程序一样scanf
对于交互式用户输入(这是同样的问题:scanf
是一个解析器,在满足之前不会返回)。
另一种可能性是有一个交互模式,它执行自己的用户输入,而不是调用 yyparse 来获取输入and解析它。在此模式下,您将用户的输入读入行缓冲区。然后让解析器处理行缓冲区。处理行缓冲区而不是FILE *
流是完全可能的。您只需要编写自定义输入处理(您自己的定义YY_INPUT
宏)。如果您实现具有行编辑和历史记录的良好交互模式,那么您最终将需要这种方法,例如使用libedit
or GNU readline
.