Bison Yacc过早停止lexer?

Bison Yacc过早停止lexer?,bison,flex-lexer,Bison,Flex Lexer,我有一个包含以下规则的yacc文件: %start program . . . statement : ';' { $$ = NULL; } | expression ';' { $$ = exprStmt($1); } | compound ';' { $$ = cmpndStmt($1); } ; routine : routine statement { $$ = rtn($1, $2); } | { $$ = NULL; } ;

我有一个包含以下规则的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,比如说,如果一个>8的文本在EOF之前是lexed。我不明白原因。有人帮我一下。

你抱怨YACC提前终止。这很容易解释;您的解析器包括:empasis added

program
    : routine { printf("YACC finish\n"); exit(0); }
exit0的全部目的是提前终止。所以它在做你要求它做的工作。然而,很难想象在一个用例中,从解析器操作调用exit是正确的,并且您得到的结果说明了原因

特别重要的是要注意,bison/yacc解析器,尤其是bison解析器,可能会在发出错误信号之前执行缩减,即使在错误标记未出现在要缩减的产品的后续列表中的情况下也是如此。这里可能就是这样;如果您没有调用exit,您将得到一个语法错误指示,它至少不会那么神秘,并且可以通过语法中的错误处理产品来处理

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

您不显示扫描仪,并且您显示的唯一跟踪是扫描仪的跟踪,而不是解析器的跟踪。因此,当扫描器遇到else令牌时,不可能知道解析器接收到的令牌类型。启用替代或以及扫描仪跟踪将在调试解析器时提供更有用的信息,包括传递给解析器的每个令牌的令牌类型


然而,令我震惊的是,if和else都由与扫描器描述第47行的标识符相同的扫描器操作处理。这意味着您的扫描器没有针对每个关键字的特定规则。您可能正在做一些效率低下且容易出错的事情,比如将标识符标记与每个可能的关键字进行比较,以便将正确的标记类型返回给解析器。但是,由于缺乏信息,除了猜测之外,很难做到这一点。

您抱怨YACC提前终止。这很容易解释;您的解析器包括:empasis added

program
    : routine { printf("YACC finish\n"); exit(0); }
exit0的全部目的是提前终止。所以它在做你要求它做的工作。然而,很难想象在一个用例中,从解析器操作调用exit是正确的,并且您得到的结果说明了原因

特别重要的是要注意,bison/yacc解析器,尤其是bison解析器,可能会在发出错误信号之前执行缩减,即使在错误标记未出现在要缩减的产品的后续列表中的情况下也是如此。这里可能就是这样;如果您没有调用exit,您将得到一个语法错误指示,它至少不会那么神秘,并且可以通过语法中的错误处理产品来处理

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

您不显示扫描仪,并且您显示的唯一跟踪是扫描仪的跟踪,而不是解析器的跟踪。因此,当扫描器遇到else令牌时,不可能知道解析器接收到的令牌类型。启用替代或以及扫描仪跟踪将在调试解析器时提供更有用的信息,包括传递给解析器的每个令牌的令牌类型


然而,令我震惊的是,if和else都由与扫描器描述第47行的标识符相同的扫描器操作处理。这意味着您的扫描器没有针对每个关键字的特定规则。您可能正在做一些效率低下且容易出错的事情,比如将标识符标记与每个可能的关键字进行比较,以便将正确的标记类型返回给解析器。但由于缺乏信息,除了猜测之外很难做到这一点。

请有人告诉我,直到解析器一直调用yylex?请有人告诉我,直到解析器一直调用yylex?苏拉夫:我强烈反对该教程,至少在t方面
o关键字解析。它确实会产生状态更少的lexer,但由此产生的节省是微不足道的,除非可能是在内存非常有限的嵌入式设备上,而且您仍然必须存储令牌查找表和使用它的代码,这在可执行大小和执行时间方面都是一项成本。@Sourav:假设lexer没有发送正确的令牌退出,然后解析器看到了一个完整的例程。它没有看到仅由完整例程组成的输入,因此存在语法错误,因为它需要EOF标记,但它清楚地看到了完整例程。所以它做了缩减,缩减调用exit。如果这来自同一教程中的一个示例,我会将教程扔进垃圾箱,然后找到一个更好的。阅读野牛手册本身几乎总是一个比在互联网上随机阅读教程更好的主意。@Sourav:为扫描仪插入一个你没有包括的痕迹是毫无意义的。与其包括扫描仪,我建议包括野牛产生的痕迹。但是,正如我所说的,如果你观察野牛的踪迹,你很可能会立即发现问题所在。在任何情况下,在将扫描器粘贴到问题中之前,我建议您只需打印出代币类型以及代币代表序列的语义值(如果合适),包括您希望它识别的各种关键字。@Sourav:相反,您的教程在第24页上显示了完全相同的错误,这可能会产生同样的结果:错误的输入将被默默地丢弃,而不是生成语法错误消息。@sourav:只是不要调用exit。解析器将调用yyerror,它应该打印它接收到的错误消息。然后解析器将返回1,因此您可能希望在对yyparse的调用中检查返回值。@Sourav:我强烈反对该教程,至少在关键字解析方面是这样。它确实会产生状态更少的lexer,但由此产生的节省是微不足道的,除非可能是在内存非常有限的嵌入式设备上,而且您仍然必须存储令牌查找表和使用它的代码,这在可执行大小和执行时间方面都是一项成本。@Sourav:假设lexer没有发送正确的令牌退出,然后解析器看到了一个完整的例程。它没有看到仅由完整例程组成的输入,因此存在语法错误,因为它需要EOF标记,但它清楚地看到了完整例程。所以它做了缩减,缩减调用exit。如果这来自同一教程中的一个示例,我会将教程扔进垃圾箱,然后找到一个更好的。阅读野牛手册本身几乎总是一个比在互联网上随机阅读教程更好的主意。@Sourav:为扫描仪插入一个你没有包括的痕迹是毫无意义的。与其包括扫描仪,我建议包括野牛产生的痕迹。但是,正如我所说的,如果你观察野牛的踪迹,你很可能会立即发现问题所在。在任何情况下,在将扫描器粘贴到问题中之前,我建议您只需打印出代币类型以及代币代表序列的语义值(如果合适),包括您希望它识别的各种关键字。@Sourav:相反,您的教程在第24页上显示了完全相同的错误,这可能会产生同样的结果:错误的输入将被默默地丢弃,而不是生成语法错误消息。@sourav:只是不要调用exit。解析器将调用yyerror,它应该打印它接收到的错误消息。然后解析器将返回1,因此您可能希望在对yyparse的调用中检查返回值。