Yacc 过早停止词法分析器?

问题描述

我有一个包含这些规则的 yacc 文件

%start program
.
.
.
statement
    : ';' { $$ = NULL; }
    | expression ';' { $$ = exprStmt($1); }
    | compound ';' { $$ = cmpndStmt($1); }
    ;

routine
    : routine statement { $$ = rtn($1,$2); }
    | { $$ = NULL; }
    ;

block
    : '{' routine '}' { $$ = $2; }
    ;

compound
    : block { $$ = cmpnd($1); }
    | if_cmpnd { $$ = $1; }
    ;

if_cmpnd
    : IF expression compound { $$ = ifcmpnd($2,$3); }
    | if_cmpnd ELSE compound { $$ = elsecmpnd($1,$3); }
    ;

program
    : routine { printf("YACC finish\n"); exit(0); }
    ;

对于这个解析器,我提供了一个示例文本文件,其中包含:

var a := 6
var b := 5
if a<5 {
b = 2
}
else {
b = 20
}

但是在解析过程中,通过调试,我得到了这个输出

.
.
.
--accepting rule at line 47 ("if")
--accepting rule at line 62 (" ")
--accepting rule at line 47 ("a")
--accepting rule at line 56 ("<")
--accepting rule at line 29 ("5")
--accepting rule at line 62 (" ")
--accepting rule at line 57 ("{")
--accepting rule at line 60 ("
")
--accepting rule at line 47 ("b")
--accepting rule at line 56 (" = ")
--accepting rule at line 29 ("2")
--accepting rule at line 60 ("
")
--accepting rule at line 57 ("}")
--accepting rule at line 60 ("
")
--accepting rule at line 47 ("else")
YACC finish

可以看出,else 之后的块没有被词法化。但是相反,如果我将“else”替换为“if a>8”之类的其他内容,则文本将被词法分析到 EOF。我不明白它的原因。请有人帮帮我。

解决方法

您抱怨 YACC 过早终止。这很容易解释;您的解析器包括:(添加 empasis)

program
    : routine { printf("YACC finish\n"); exit(0); }

exit(0) 的全部目的是提前终止。所以它正在做你要求它做的工作。但是,很难想象从解析器操作调用 exit() 是正确的用例,并且您获得的结果显示了原因。

特别重要的是要注意,bison/yacc 解析器(尤其是 bison 解析器)可能会在发出错误信号之前执行归约,即使错误标记没有出现在被归约的产生式的后续列表中也是如此。这里可能就是这种情况;如果你没有调用 exit(),你会得到一个语法错误指示,这至少不会那么神秘(并且可以在你的语法中使用 error-handling 产生式处理)。

yyparse 将返回(返回值为 0),如果它设法解析整个输入;如果遇到语法错误,它将返回 1,如果检测到内存过度使用,则返回 2。 (这些常量有预处理器符号,但通常足以测试 yyparse() 的返回值是否为零。)您应该始终让解析器正常返回,给它一个清理的机会返回前分配资源。

您没有展示您的扫描仪,您提供的唯一跟踪是扫描仪的跟踪,而不是解析器的跟踪。因此,当扫描器遇到 else 令牌时,无法知道解析器收到的令牌类型。启用 parser traces 而不是(或同时)扫描器跟踪将提供在调试解析器时更有用的信息(包括传递给解析器的每个令牌的令牌类型)。

但是,ifelse 都由与您的标识符相同的扫描仪操作处理,这一事实令我震惊(在您的扫描仪说明的第 47 行)。这意味着您的扫描仪没有针对每个关键字的特定规则。据推测,您正在做一些效率低下且容易出错的事情,例如将标识符标记与每个可能的关键字进行比较,以便将正确的标记类型返回给解析器。但由于缺乏信息,很难做更多的猜测。