问题描述
%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 而不是(或同时)扫描器跟踪将提供在调试解析器时更有用的信息(包括传递给解析器的每个令牌的令牌类型)。
但是,if
和 else
都由与您的标识符相同的扫描仪操作处理,这一事实令我震惊(在您的扫描仪说明的第 47 行)。这意味着您的扫描仪没有针对每个关键字的特定规则。据推测,您正在做一些效率低下且容易出错的事情,例如将标识符标记与每个可能的关键字进行比较,以便将正确的标记类型返回给解析器。但由于缺乏信息,很难做更多的猜测。