【问题标题】:Bison compiler: remove conflictsBison 编译器:消除冲突
【发布时间】:2020-05-20 11:47:45
【问题描述】:

我正在使用 Bison 和 Flex 为类 C 语言开发编译器。目前,编译器能够识别具有声明、赋值和打印语句以及算术和逻辑表达式的语言(仅使用 int 变量)。它生成一个 3AC(以及一些管理内存的指令)。这是我的 Bison 代码:

%{

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "list.h"

int yylex();
void yyerror(char *s);

TList list = NULL;
int i=0;

char* tmp() {
    char* t = (char*)malloc(sizeof(char*));
    sprintf(t, "t%d", i);
    i++;
    return t;
}

%}

%union {
    int number;
    char* identifier;
}

%token <number> NUM
%token <identifier> ID
%token PRINT INT ENDFILE 
%left '+' '-'
%left '*' '/'
%right UMINUS
%left OR
%left AND
%right NOT
%nonassoc EQ LT GT LE GE NE
%type <identifier> expr
%type <identifier> comp
%type <identifier> bexpr


%%
program :   lstmt ENDFILE               { return 0; }
            ;

lstmt       : lstmt stmt ';'
            | stmt ';'
            | lstmt openb lstmt closeb
            | openb lstmt closeb
            ;

openb       : '{'                       { printf("list = insertElement(list);\n"); }
            ;

closeb      :  '}'                      { printf("list = removeElement(list);\n"); }
            ;

stmt        : INT ID                    { printf("addVar(\"%s\", &list->table);\n", $2); }
            | INT ID '=' NUM            {
                                            printf("addVar(\"%s\", &list->table);\n", $2);
                                            printf("setVarList(\"%s\", %d, list);\n", $2, $4);
                                        }
            | ID '=' expr               { printf("setVarList(\"%s\", %s, list);\n", $1, $3); }
            | PRINT '(' ID ')'          { printf("printf(\"%%s: %%d\\n\", \"%s\", getVarList(\"%s\", list));\n", $3, $3); }
            | ID '=' bexpr              { printf("setVarList(\"%s\", %s, list);\n", $1, $3); }
            ;

bexpr       : bexpr OR bexpr            {
                                            $$ = tmp();
                                            printf("%s = %s || %s;\n", $$, $1, $3);
                                        }
            | bexpr AND bexpr           {
                                            $$ = tmp();
                                            printf("%s = %s && %s;\n", $$, $1, $3);
                                        }
            | expr OR bexpr             {
                                            $$ = tmp();
                                            printf("%s = %s || %s;\n", $$, $1, $3);
                                        }
            | expr AND bexpr            {
                                            $$ = tmp();
                                            printf("%s = %s && %s;\n", $$, $1, $3);
                                        }
            | bexpr OR expr             {
                                            $$ = tmp();
                                            printf("%s = %s || %s;\n", $$, $1, $3);
                                        }
            | bexpr AND expr            {
                                            $$ = tmp();
                                            printf("%s = %s && %s;\n", $$, $1, $3);
                                        }
            | NOT bexpr                 {
                                            $$ = tmp();
                                            printf("%s = !%s;\n", $$, $2);
                                        }
            | '(' bexpr ')'             { $$ = $2; }
            | comp                      { $$ = $1; }
            ;

comp        : expr LT expr              {
                                            $$ = tmp();
                                            printf("%s = %s < %s;\n", $$, $1, $3);
                                        }
            | expr LE expr              {
                                            $$ = tmp();
                                            printf("%s = %s <= %s;\n", $$, $1, $3);
                                        }
            | expr GT expr              {
                                            $$ = tmp();
                                            printf("%s = %s > %s;\n", $$, $1, $3);
                                        }
            | expr GE expr              {
                                            $$ = tmp();
                                            printf("%s = %s >= %s;\n", $$, $1, $3);
                                        }
            | expr EQ expr              {
                                            $$ = tmp();
                                            printf("%s = %s == %s;\n", $$, $1, $3);
                                        }
            | expr NE expr              {
                                            $$ = tmp();
                                            printf("%s = %s != %s;\n", $$, $1, $3);
                                        }
            | expr AND expr             {
                                            $$ = tmp();
                                            printf("%s = %s && %s;\n", $$, $1, $3);
                                        }
            | expr OR expr              {
                                            $$ = tmp();
                                            printf("%s = %s || %s;\n", $$, $1, $3);
                                        }
            | NOT expr                  {
                                            $$ = tmp();
                                            printf("%s = !%s;\n", $$, $2);
                                        }
            ;

expr        : expr '+' expr             {  
                                            $$ = tmp();
                                            printf("%s = %s + %s;\n", $$, $1, $3);
                                        }
            | expr '-' expr             { 
                                            $$ = tmp();
                                            printf("%s = %s - %s;\n", $$, $1, $3);
                                        }
            | expr '*' expr             {
                                            $$ = tmp();
                                            printf("%s = %s * %s;\n", $$, $1, $3);
                                        }
            | expr '/' expr             {
                                            $$ = tmp();
                                            printf("%s = %s / %s;\n", $$, $1, $3);
                                        }
            | '(' expr ')'              { $$ = $2; }
            | '-' expr %prec UMINUS     { 
                                            $$ = tmp();
                                            printf("%s = -%s;\n", $$, $2); 
                                        }
            | ID                        { 
                                            $$ = tmp();
                                            printf("%s = getVarList(\"%s\", list);\n", $$, $1);
                                        }
            | NUM                       {
                                            $$ = tmp();
                                            printf("%s = %d;\n", $$, $1);
                                        }
            ;

%%

int main () {
    list = insertElement(list);
    if(yyparse() !=0)
        fprintf(stderr, "Abonormal exit\n");

    fprintf(fopen("temp.h", "w"), "#include \"list.h\"\n\nTList list = NULL;\nint t" );
    for(int j=0; j<i-1; j++) {
        fprintf(fopen("temp.h", "a"), "%d, t", j);
    }
    fprintf(fopen("temp.h", "a"), "%d;", i-1);
    return 0;
}

void yyerror (char *s) {
    fprintf(stderr, "Error: %s\n", s);
}

正如您所见,逻辑表达式的语法有点复杂,但编译器会做它应该做的事情。该行为类似于 C,因此整数值可以用于 AND/OR/NOT。

我对语法的想法是这样的:

bexpr       : bexpr OR bexpr            
            | bexpr AND bexpr
            | NOT bexpr
            | '(' bexpr ')'
            | comp
            | expr
            ;

comp        : expr LT expr
            | expr LE expr
            | expr GT expr
            | expr GE expr
            | expr EQ expr
            | expr NE expr
            ;

但是这样我得到了两个冲突,1 个 shift/reduce 和 1 个 reduce/reduce。 有没有办法简化语法?

【问题讨论】:

  • compexpr 以相同的非终结符开头 - expr,因此解析器不知道如何减少以 expr 开头的 bexpr:将其视为 @ 987654328@或comp
  • 是的,我知道,但是有没有办法使用第二种语法?在使用实现的语法之前,我做了几次尝试(上面的一个和另一个类似的:comp-&gt;expr 而不是bexpr-&gt;expr)但没有结果。我真的不喜欢我使用的语法,我想写一个不太复杂的语法。

标签: c compiler-construction bison ambiguous-grammar


【解决方案1】:

我的建议是不要试图在语法上区分 bexprexpr。它不能真正准确地完成,因为您允许将变量用作布尔值。可以肯定的是,您当前的语法是一项勇敢的努力,但是当您在语法中添加条件语句时,您会发现

if ((a)) ...

将被标记为语法错误(假设条件语句的语法类似于 C:"if" '(' bexpr ')' stmt | "if" '(' bexpr ')' stmt "else" stmt)。并且禁止使用算术表达式作为布尔运算符的参数的尝试很容易被规避,因为a AND (1 + -1) 是有效的bexpr。有人可能会问为什么(a &lt; b) == (b &lt; c) 不应该是有效的语法。诚然,它有点混淆,但它是一种方便的写法:“b 介于 ac 之间”。

如果您真的想禁止使用算术运算作为布尔运算符 ANDORNOT 的参数,您可以通过创建两个并行层次结构来改进您的语法,或者只需标记类型表达式作为其语义值的一部分,并在每个语义动作中进行检查。第二个选项的优点是双重的:它简化了语法,消除了重复,并且它提供了更精确的错误消息的可能性(“尝试使用算术表达式作为布尔值”而不是“语法错误”)。

【讨论】:

  • 我不明白为什么在我的语法中添加 if-else 时会出现语法错误。我可能是错的,但我很确定在 C:int a = 20; if(a) {...} 中工作正常,因为“a”被认为是真的(因为大于 1)。我不想禁止这种行为,因为正如我所说,我的语言充当 C。如果可能的话,我只想简化语法。如果不是,我会继续尝试添加 if-else 规则,检查它是否有效
  • @forzalupi1912: if(a)... 可以正常工作。 if((a))... 不会,因为 (a) 不是 bexpr
  • 好的,我明白了。我必须执行 if-else 来验证,但我认为你是对的,这就是为什么我希望 expr 非终结符出现在 compbexpr 的规则中。有没有办法做到这一点?我想不通。我第一次想把所有规则放在expr 中,而不是使用其他两个非终结符,但我宁愿不这样做,我想保持代码(和语法)现在的样子。
  • 如果你想尝试将 bexprs 与 exprs 分开,你的语法会很复杂,并且会有很多极端情况。这就是为什么我建议你不要尝试。如果您想禁止某些原本有效的表达式,因为您的语法让 expr 成为布尔运算符的操作数,那么您可以在语义操作中这样做,正如我所说的(然后您会收到很好的错误消息)。如果你构建一个 AST 而不是直接进入 TAC 会更容易,但它可以通过任何一种方式完成。但很少有语言这样做是有原因的。
  • @forzalupi1912:如果您想尝试提供一个明确的列表,列出您想要允许哪些表达式以及您想要禁止哪些表达式,我们可以尝试生成一个语法。但这不会很漂亮。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-07
  • 1970-01-01
相关资源
最近更新 更多