【问题标题】:Flex/Bison mini C compiler lexical and semantic analysis shift/reduce conflictFlex/Bison mini C 编译器词法和语义分析移位/减少冲突
【发布时间】:2017-12-21 10:34:59
【问题描述】:

我想使用 flex 和 bison 为迷你 C 语言编写编译器。我的语言示例如下所示:

/* This is an example uC program. */
int fac(int n)
{
    if (n < 2)
        return n;
    return n * fac(n - 1);
}

int sum(int n, int a[])
{
    int i;
    int s;

    i = 0;
    s = 0;
    while (i < n) {
        s = s + a[i];
        i = i + 1;
    }
    return s;
}

int main(void)
{
    int a[2];

    a[0] = fac(5);
    a[1] = 27;
    return 0;
}

这是我解析这种语言的语法:

program         ::= topdec_list
topdec_list     ::= /empty/ | topdec topdec_list
topdec          ::= vardec ";"
                  | funtype ident "(" formals ")" funbody
vardec          ::= scalardec | arraydec
scalardec       ::= typename ident
arraydec        ::= typename ident "[" intconst "]"
typename        ::= "int" | "char"
funtype         ::= typename | "void"
funbody         ::= "{" locals stmts "}" | ";"
formals         ::= "void" | formal_list
formal_list     ::= formaldec | formaldec "," formal_list
formaldec       ::= scalardec | typename ident "[" "]"
locals          ::= /empty/ | vardec ";" locals
stmts           ::= /empty/ | stmt stmts
stmt            ::= expr ";"
                  | "return" expr ";" | "return" ";"
                  | "while" condition stmt
                  | "if" condition stmt else_part
                  | "{" stmts "}"
                  | ";"
else_part       ::= /empty/ | "else" stmt
condition       ::= "(" expr ")"
expr            ::= intconst
                  | ident | ident "[" expr "]"
                  | unop expr
                  | expr binop expr
                  | ident "(" actuals ")"
                  | "(" expr ")"
unop            ::= "-" | "!"
binop           ::= "+" | "-" | "*" | "/"
                  | "<" | ">" | "<=" | ">=" | "!=" | "=="
                  | "&&"
                  | "="
actuals         ::= /empty/ | expr_list
expr_list       ::= expr | expr "," expr_list

flex 模块工作正常并根据需要返回值,但野牛不断给我奇怪的错误,表明我的语法错误(不是)。这是我的野牛文件:

%{
#include <stdio.h>
#include <stdlib.h>

extern int yylex();
extern int yyparse();
FILE* yyin;
extern int lineNumber;

void yyerror(const char* s);
%}

%union {
    int intvalue;
}

%token<intvalue> INTCONST
%token IDENT
%token CHARK
%token ELSEK
%token IFK
%token INTK
%token RETURNK
%token VOIDK
%token WHILEK
%token NOTK
%token ANDK
%token COMMAK
%token DIVIDEK
%token MULTIPLYK
%token MINUSK
%token PLUSK
%token SEMICOLONK
%token NEQUALK
%token EQUALK
%token ASSIGNMENTK
%token RECOMPARATORK
%token LECOMPARATORK
%token RCOMPARATORK
%token LCOMPARATORK
%token RPARANTESESK
%token LPARANTESESK
%token RBRACKETK
%token LBRACKETK
%token RCURLY
%token LCURLY

%right ASSIGNMENTK
%left ANDK
%left EQUALK NEQUALK
%left LCOMPARATORK RCOMPARATORK LECOMPARATORK RECOMPARATORK
%left PLUSK
%left MULTIPLYK DIVIDEK
%left MINUSK NOTK

%start program

%%

program: topdec_list
       ;
topdec_list: /*empty*/
                     | topdec topdec_list
                 ;
topdec: vardec SEMICOLONK
            | funtype IDENT LPARANTESESK formals RPARANTESESK funbody
      ;
vardec: scalardec
            | arraydec
      ;
scalardec: typename IDENT
                 ;
arraydec: typename IDENT LBRACKETK INTCONST RBRACKETK
                ;
typename: INTK
                | CHARK
                ;
funtype: typename
             | VOIDK
       ;
funbody: LCURLY locals stmts RCURLY
             | SEMICOLONK
       ;
formals: VOIDK
             | formal_list
       ;
formal_list: formaldec
                     | formaldec COMMAK formal_list
                 ;
formaldec: scalardec
         | typename IDENT LBRACKETK RBRACKETK
           ;
locals: /*empty*/
      | vardec SEMICOLONK locals
        ;
stmts: /*empty*/
     | stmt stmts
     ;
stmt: expr SEMICOLONK
    | RETURNK expr SEMICOLONK
        | RETURNK SEMICOLONK
        | WHILEK condition stmt
        | IFK condition stmt else_part
        | LCURLY stmts RCURLY
        | SEMICOLONK
        ;
else_part: /*empty*/ | ELSEK stmt
                 ;
condition: LPARANTESESK expr RPARANTESESK
                 ;
expr: INTCONST
        | IDENT
        | IDENT LBRACKETK expr RBRACKETK
        | unop expr
        | expr binop expr
        | IDENT LPARANTESESK actuals RPARANTESESK
        | LPARANTESESK expr RPARANTESESK
        ;
unop: MINUSK | NOTK
    ;
binop: PLUSK
     | MINUSK
         | MULTIPLYK
         | DIVIDEK
         | LCOMPARATORK
         | RCOMPARATORK
     | LECOMPARATORK
         | RECOMPARATORK
         | NEQUALK
         | EQUALK
         | ANDK
         | ASSIGNMENTK
     ;
actuals: /*empty*/
             | expr_list
             ;
expr_list: expr
                 | expr COMMAK expr_list
                 ;

%%

int main(int argc, char **argv)
{
    yyin = fopen("input.c", "r");
    do
    {
        yyparse();
    } while (!feof(yyin));
    fclose(yyin);
    return 0;
}

void yyerror(const char* error)
{
    fprintf(stderr, "Parse error in line %d: %s\n", lineNumber, error);
}

例如对于这个输入:

int i;
i = 0;

我收到一个错误,即在识别第二个 i 之后立即发生语法错误(我在我的 flex 文件中打印标记,所以我知道它在到达分配字符之前没有问题)。

或者作为另一个例子当通过这一行时:

int fac(int n);

在第一个 paranteses 之后,我得到了相同的语法错误(正是 Parse error in line 1: syntax error),这意味着它将第二个 int 视为语法错误,这不应该是因为我的语法看起来不错。

另外,bison 产生的警告如下(flex 和 gcc 都可以):

semantic_analyzer.y: warning: 26 shift/reduce conflicts [-Wconflicts-sr]
semantic_analyzer.y:78.10-17: warning: rule useless in parser due to conflicts [-Wother]
 funtype: typename
          ^^^^^^^^

任何建议或更正表示赞赏:) 提前致谢。

【问题讨论】:

  • 在生成解析器时,您没有提到 yacc/bison 产生的警告。
  • 谢谢@rici 我刚刚添加。

标签: bison flex-lexer


【解决方案1】:
int i;
i = 0;

不是有效的 C 程序,您的语法正确地拒绝了它。 (如您的语法所示,程序是声明的序列,i = 10; 不是声明。)

您提到的第二个问题与移位归约冲突有关,这是由于您的语法坚持将int fac(int n) 中的第一个int 必须归约为funtype,而int i; 中的inttypename。在解析器需要决定是移动IDENT 还是将typename 减少到funtype 时,它无法知道要采取什么行动,因为它只能看到一个前瞻标记——IDENT——并且决定取决于 second 下一个前瞻标记(可能是也可能不是左括号)。默认情况下,yacc/bison 通过 shift 来解决 shift-reduce 冲突,这意味着 int fac 不能作为 topdec 的第二个产生式的前缀;因此,( 将触发错误。

【讨论】:

  • 哦,我明白了,我的第一个程序确实不正确。所以这意味着我必须将最终标记整合到 topdec 句子中(而不是一种 funtype 将是 int | char | void )?
  • 如果你想让你的语法正常工作,你必须修复 shift-reduce 冲突。请注意,bison 实际上告诉您,`funtype: typename' 规则是无用的,这基本上是我在回答中所说的。
  • @mazhar:简单的解决方案是在语法中添加void 作为可能的typename,这样您的语法就不需要区分变量类型和函数返回类型。您可以在关联的缩减操作中检查void x;
  • 我不太了解移位/减少问题。不过你的描述很清楚,谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多