是什么决定解析器尝试哪个结果?



我正在尝试为桌面计算器构建解析器,并使用以下野牛代码。

%union{
float f;
char c;
// int 
}
%token <f> NUM
%token <c> ID
%type <f> S E T F G
%%
C   :   S ';'
|   C S ';'
;
S   :   ID '=' E    {fprintf(debug,"13n");printf("%c has been assigned the value %f.",$1,$3);symbolTable[$1]=$3;}
|   E           {fprintf(debug,"12n");result = $$;}
;
E   :   E '+' T     {fprintf(debug,"11n");$$ = $1+$3;}
|   E '-' T     {fprintf(debug,"10n");$$ = $1-$3;}
|   T           {fprintf(debug,"9n");$$ = $1;}
;
T   :   T '*' F     {fprintf(debug,"7n");$$ = $1*$3;}
|   T '/' F     {fprintf(debug,"6n");$$ = $1/$3;}
|   F           {fprintf(debug,"5n");$$ = $1;}
; 
F   :   G '@' F     {fprintf(debug,"4n");$$ = pow($1,$3);}
|   G           {fprintf(debug,"3n");$$ = $1;}
;
G   :   '(' E ')'   {fprintf(debug,"2n");$$ = $2;}
|   NUM         {fprintf(debug,"1n");$$ = $1;}
|   ID          {fprintf(debug,"0n");$$ = symbolTable[$1];}
;
%%
我的LEX规则是
digit   [0-9]
num     {digit}+
alpha   [A-Za-z]
id      {alpha}({alpha}|{digit})*
white   [ t]
%%
let     {printf("let");return LET;}
{num}   {yylval.f = atoi(yytext);return NUM;}
{alpha} {yylval.c = yytext[0];return ID;}
[+-*/@()]   {return yytext[0];}
.       {}
%%

我给出的输入是a=2+3当词法分析器返回ID(对于'a')时,解析器将使用fprintf(debug,"0n")进行生成。但我想把它用于生产fprintf(debug,"13n")

所以,我想知道是什么让我的解析器减少生产0,而不是将=转移到堆栈,以及我如何控制它?

您实际指定的是翻译语法,如下所示:
C→S ';' 14 | C S ';' 8
S→ID '=' E 13 | E 12
E→E '+' T 11 | E '-' T 10 | T 9
T→T '*' F 7 | T "/"F 6 | F 5
F→G '@' f4 | G 3
G→'(' E ')' 2 | NUM 1 | ID 0
顶级/启动配置c(为了完整,我添加了8和14)

根据这个翻译语法,从C语言中只生成一个包含ID '=' NUM '+' NUM作为输入令牌子词的单词,那就是ID ('a')'=' NUM('2') 13 5 9 '+' NUM('3') 13 5 11 13 ';' 14,等于输入输出对(ID '=' NUM '+' NUM ';', 13 5 9 13 5 11 13 14)。因此,序列13 5 9 13 5 11 13 14是唯一的翻译。如果语法为LALR(1),则结果将生成此翻译;语法为LALR(1)。

如果你没有得到这个结果,那么这只能意味着你实现了错误的任何你离开了你的描述:即lexer…或者你的语法处理器有bug,或者你的机器有故障。

,没有;实际上,您所做的是查看正在发生的事情的更好的方法—只需在每个规则的右侧插入一个printf语句,并以这种方式运行它,以查看产生了什么转换序列。"trace"正是由于这个原因,解析器生成器中的工具是多余的…至少它通常的实现方式是这样的(详见下文)。此外,您可以使用-v选项直接查看所有内容,该选项将生成带有LALR(1)注释的LR(0)表。

实际上更有帮助的内置测试工具—特别是对于像这样的示例—正是我所描述的:它响应与输出操作交错的输入。因此,当你在& a = 2 + 3 "下运行它时,它会给你ID('a')'=' NUM('2') 13 5 9 '+' NUM('3') 13 5 11 13 ';' 14, echo打开,只是13 5 9 13 5 11 13 14,echo关闭。作为一种内置功能,而不是通常在yacc实现中看到的跟踪模式,实际上会更有用。

POSIX规范实际上没有解决如何"YYDEBUG", "YYDEBUG"one_answers";-t"要在一个兼容的yacc实现中实现,以便为这样的替代方法腾出空间。

嗯,事实证明,问题是我没有在这里将=标识为令牌,在我的LEX中。

虽然听起来很傻,但它指出了yacc/Bison的一个非常重要的概念。是移动还是减少的问题是通过检查下一个符号来回答的,也称为向前看。在本例中,由于我的LEX代码有问题,前瞻是NUM(对于2)而不是=。由于没有涉及IDNUM的生产,它将减少到G

关于我是如何发现的,原来野牛有一个内置的跟踪功能。无论它在解析时做什么,它都像日记条目一样整齐地排列。每一步都写下来了。

-Dparse.trace选项运行bison

bison calc.y -d -Dparse.trace

在解析器的主函数中获取外部yydebug并将其设置为非零值

int main(){
extern int yydebug;
yydebug = 1;
.
.
.
}

最新更新