首页 > 解决方案 > 是什么决定了解析器尝试哪个产生式?

问题描述

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

%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,"13\n");printf("%c has been assigned the value %f.",$1,$3);symbolTable[$1]=$3;}
    |   E           {fprintf(debug,"12\n");result = $$;}
    ;

E   :   E '+' T     {fprintf(debug,"11\n");$$ = $1+$3;}
    |   E '-' T     {fprintf(debug,"10\n");$$ = $1-$3;}
    |   T           {fprintf(debug,"9\n");$$ = $1;}
    ;

T   :   T '*' F     {fprintf(debug,"7\n");$$ = $1*$3;}
    |   T '/' F     {fprintf(debug,"6\n");$$ = $1/$3;}
    |   F           {fprintf(debug,"5\n");$$ = $1;}
    ; 

F   :   G '@' F     {fprintf(debug,"4\n");$$ = pow($1,$3);}
    |   G           {fprintf(debug,"3\n");$$ = $1;}
    ;

G   :   '(' E ')'   {fprintf(debug,"2\n");$$ = $2;}
    |   NUM         {fprintf(debug,"1\n");$$ = $1;}
    |   ID          {fprintf(debug,"0\n");$$ = 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(for 'a') 时,解析器将使用fprintf(debug,"0\n"). 但我希望它用于生产fprintf(debug,"13\n")

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

标签: parsingbisonyacclex

解决方案


您实际指定的是翻译语法,由以下内容给出:
C → S ';' 14 | CS ';' 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 '@' F 4 | G 3
G → '(' E ')' 2 | 数字 1 | ID 0
具有顶级/启动配置 C。(为了完整起见,我在 8 和 14 中添加了)。

按照这个翻译语法,只有一个从C生成的词,包含ID '=' NUM '+' NUM 作为输入记号的子词,即ID ('a') '=' NUM('2') 1 3 5 9 '+' NUM('3') 1 3 5 11 13 ';' 14,等于输入输出对(ID'='NUM'+'NUM';', 1 3 5 9 1 3 5 11 13 14)。因此,序列 1 3 5 9 1 3 5 11 13 14 是唯一的翻译。如果语法是 LALR(1),那么这个翻译就会产生,结果;语法是 LALR(1)。

如果你没有得到这个结果,那只能意味着你在描述中遗漏了任何东西:即词法分析器......或者你的语法处理器有错误或你的机器有故障。

和不; 实际上,您所做的是查看正在发生的事情的更好方法 - 只需将单个 printf 语句粘贴到每个规则的右侧并以这种方式运行它以查看生成的翻译序列。由于这个原因,解析器生成器中的“跟踪”工具是多余的……至少是它通常实现的方式(更多内容见下文)。此外,您可以使用 -v 选项直接查看所有内容,该选项会生成带有 LALR(1) 注释的 LR(0) 表。

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

POSIX 规范实际上留下了“YYDEBUG”、“yydebug”和“-t”如何在符合 yacc 的实现中实现的问题,以便为像这样的替代方法腾出空间。


推荐阅读