即使语法是每个语法,Yacc 的语法错误

问题描述

即使语法符合语法,我的 yacc 解析器也显示语法错误。 我的 Yacc 代码

%{
    void yyerror (char *s);
    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    int symbols[100];
    int yylex();
    int symbolVal(char symbol);
    void updateSymbolVal(char symbol,int val);
%}

%union {int num; char id;}
%start line
%token WHILE
%token lt
%token gt
%token exit_command
%token <num> number
%token <id> identifier
%type <num> line exp term
%type <id> assignment
%type <num> condition


%%


line:   assignment          {;} 
        |line assignment    {;}
        |exit_command       {exit(EXIT_SUCCESS);}
        |line exit_command  {exit(EXIT_SUCCESS);}
        |whileLoop          {;}
        |condition          {;}
        ;

whileLoop: WHILE '(' condition ')' '{' assignment '}'          {printf("while loop condition var:%d\n",$3);}
         ;


assignment  : identifier '=' exp {updateSymbolVal($1,$3);}
            ;

exp         :   term                {$$ = $1;}
            |   exp '+' term        {$$ = $1 + $3;}
            |   exp '-' term        {$$ = $1 - $3;}
            ;

term        :   number              {$$ = $1;}
            |   identifier          {$$ = symbolVal($1);}
            ;

condition  :  identifier cond_op identifier     {$$ = $1;}
              |identifier cond_op number        {$$ = $1;}
              ;


cond_op   :  lt
            | gt
            ;

%%

int computeSymbolIndex(char token){
    int idx = -1;
    if(islower(token)){
        idx = token - 'a' +26;
    }
    else if(isupper(token)){
        idx = token - 'A' + 26;
    }
    return idx;
}

int symbolVal(char symbol){
    int bucket = computeSymbolIndex(symbol);
    return symbols[bucket];
}

void updateSymbolVal(char symbol,int val){
    int bucket = computeSymbolIndex(symbol);
    symbols[bucket] = val;
}

int main(void){
    int i;
    for(i=0;i<52;i++){
        symbols[i] = 0;
    }

    return yyparse();
}

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

我的 Lex 代码

%{
    #include "y.tab.h"
%}

%%

"while"         {printf("while\n"); return WHILE;}
"exit"          {return exit_command;}
[a-zA-Z]        {yylval.id = yytext[0]; return identifier;}
[0-9]+          {yylval.num = atoi(yytext); return number;}
"<"             {return lt;}
">"             {return gt;}
[ \t\n]         ;
[-+=;]          {return yytext[0];}
.               ;

%%
int yywrap (void) 
{
    return 1;
}

显示语法错误的示例文本:

while(i>0){i = i-1}

"while" 按照 lex 打印,但下一行输出是 "Syntax Error"。

即使是“while 循环条件变量”也没有被打印出来。

特别是while循环的语法错误

诸如条件语句赋值等所有其他事情似乎都可以正常工作。

为什么会这样?

解决方法

您有一个词法分析器回退规则,它会默默地丢弃无法识别的字符:

.               ;

正如您刚刚发现的那样,这真的不是一个好主意。在这种情况下,没有其他规则识别 (),因此上述规则将忽略它们。但是,您的解析器需要一个括号。它没有得到一个,所以它报告了一个语法错误。

更好的是以下回退规则,它可以取代前面的规则:

   /* [-+=;]          {return yytext[0];} */ /* now redundant*/
.               {return yytext[0];}

这接受词法分析器中的任何字符。但是,大多数字符在语法中的任何地方都没有用作字符文字,因此它们会被解析器视为无效标记,从而导致语法错误。

您可以通过在 lex 回退规则中写入错误来获得更精确的错误消息,但是您需要确保所有 vslid 字符都被识别:

[-+=;(){}]      {return yytext[0];}
.               {return yytext[0];}

就我个人而言,我会将 <> 添加到该列表中,而不是拥有专用规则(和不必要的令牌名称。)