在研究了像 Python 和 SML/NJ 这样的语言如何处理它们的 REPL 后,我在我的解释器中得到了一个很好的结果。我没有将提示/回显逻辑放在最外面的解析器驱动程序循环中,而是将其放在最里面的词法分析器输入例程中。解析器和词法分析器中的操作设置了控制输入例程提示的标志。
我使用的是可重入扫描仪,所以yyextra
包含解释器各层之间传递的状态。它看起来大致是这样的:
typedef struct Interpreter {
char* ps1; // prompt to start statement
char* ps2; // prompt to continue statement
char* echo; // result of last statement to display
BOOL eof; // set by the EOF action in the parser
char* error; // set by the error action in the parser
BOOL completeLine // managed by yyread
BOOL atStart; // true before scanner sees printable chars on line
// ... and various other fields needed by the interpreter
} Interpreter;
词法分析器输入例程:
size_t yyread(FILE* file, char* buf, size_t max, Interpreter* interpreter)
{
// Interactive input is signaled by yyin==NULL.
if (file == NULL) {
if (interpreter->completeLine) {
if (interpreter->atStart && interpreter->echo != NULL) {
fputs(interpreter->echo, stdout);
fputs("\n", stdout);
free(interpreter->echo);
interpreter->echo = NULL;
}
fputs(interpreter->atStart ? interpreter->ps1 : interpreter->ps2, stdout);
fflush(stdout);
}
char ibuf[max+1]; // fgets needs an extra byte for \0
size_t len = 0;
if (fgets(ibuf, max+1, stdin)) {
len = strlen(ibuf);
memcpy(buf, ibuf, len);
// Show the prompt next time if we've read a full line.
interpreter->completeLine = (ibuf[len-1] == '\n');
}
else if (ferror(stdin)) {
// TODO: propagate error value
}
return len;
}
else { // not interactive
size_t len = fread(buf, 1, max, file);
if (len == 0 && ferror(file)) {
// TODO: propagate error value
}
return len;
}
}
顶层解释器循环变为:
while (!interpreter->eof) {
interpreter->atStart = YES;
int status = yyparse(interpreter);
if (status) {
if (interpreter->error)
report_error(interpreter);
}
else {
exec_statement(interpreter);
if (interactive)
interpreter->echo = result_string(interpreter);
}
}
Flex 文件获得以下新定义:
%option extra-type="Interpreter*"
#define YY_INPUT(buf, result, max_size) result = yyread(yyin, buf, max_size, yyextra)
#define YY_USER_ACTION if (!isspace(*yytext)) { yyextra->atStart = NO; }
The YY_USER_ACTION
处理语言语法中的标记和输入行之间棘手的相互作用。我的语言类似于 C 和 ML,需要特殊字符 (';') 来结束语句。在输入流中,该字符可以后跟一个换行符以表示行结束,也可以后跟作为新语句一部分的字符。如果自最后一个语句结束以来扫描的唯一字符是换行符或其他空格,则输入例程需要显示主提示;否则应该显示继续提示。