WHILE 循环和语句的 YACC 语法不起作用

问题描述

我正在尝试为 while 循环和一般语句生成以下语法的中间代码,但我一直遇到语法错误。虽然之前语句和表达式的语法有效,但在添加 while 循环产生式后,该程序不起作用。

这是我的 lex.l 文件

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


NUMBER      [0-9]       
ALPHABET    [a-zA-Z]

%%

[\t];
{NUMBER}+   { strcpy(yylval.str,yytext); return ID; }
{ALPHABET}  { strcpy(yylval.str,yytext); return ID; }
"while"     { return WHILE; }
"do"        { return DO; }
"<"     { yylval.symbol=yytext[0]; return OP; }
">"     { yylval.symbol=yytext[0]; return OP; }
"!="        { yylval.symbol=yytext[0]; return OP; }
"=="        { yylval.symbol=yytext[0]; return OP; }
[\n];
.       { return yytext[0]; }

%%

这是我的 yacc.y 文件

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


    char result_gen();
    char quadruple_entry(char a[],char b,char c[]);
    char quadruple_entry_assign(char a[],char c[]);
    char quadruple_entry_loop();
    char quadruple_entry_do();
    void three_address_code();

    int q_index = 0;
    char result[3] = {'t','0','\0'};
    char result2[3] = {'L','\0'};
    char temp[3];
    char temp2[3];

    struct QuadrupleStructure {
        char arg1[10];
        char op;
        char arg2[10];
        char rslt[3];
    }quadruple[25];

    
 
%}

%union {
    char str[10];
    char symbol;
}

%token WHILE DO
%token <str> ID
%token <symbol> OP
%type  <str> expr 

%right '='
%left '+' '-'
%left '/' '*'


%%
wstmt   :   WHILE { quadruple_entry_loop(); } stmt  DO { quadruple_entry_do(); }
    ;

stmt    :   ID '=' expr  { quadruple_entry_assign($1,'=',$3); }
    |   ID OP ID { quadruple_entry($1,$2,$3); }
    ;


expr    :   expr '+' expr { quadruple_entry($1,'+',$3); strcpy($$,temp); }

    |   expr '-' expr { quadruple_entry($1,'-',temp); }

    |   expr '/' expr { quadruple_entry($1,'/',temp); }

    |   expr '*' expr { quadruple_entry($1,'*',temp); }

    |   '(' expr ')'  { strcpy($$,$2); }

    |   ID        { strcpy($$,$1); }
    ;

%%




char result_gen() {
    strcpy(temp,result);
    result[1]++;
}


char quadruple_entry(char a[],char c[]) {
    result_gen();

    strcpy(quadruple[q_index].arg1,a);
    quadruple[q_index].op = b;
    strcpy(quadruple[q_index].arg2,c);
    strcpy(quadruple[q_index].rslt,temp);

    q_index++;
}

char quadruple_entry_assign(char a[],char c[]) {
    char tempLocal[3] = {' ',' ','\0'};
    strcpy(quadruple[q_index].arg1,tempLocal);

    q_index++;
}

char quadruple_entry_loop() {
    char tempLocal[3];
    strcpy(tempLocal,result2);

    char tempLocal2[] = " if ";
    char tempLocal3 = ' ';
    char tempLocal4[] = " ";
    
    strcpy(quadruple[q_index].rslt,tempLocal);
    strcpy(quadruple[q_index].arg1,tempLocal4);
    quadruple[q_index].op = tempLocal3;
    strcpy(quadruple[q_index].arg2,tempLocal2);
 
    q_index++;  
}

char quadruple_entry_do() {
    char tempLocal[4];
    strcpy(tempLocal,result2);
    tempLocal[3] = ':';
    strcpy(quadruple[q_index].arg1,tempLocal);

    char tempLocal2[] = " ";
    char tempLocal3 = ' ';

    quadruple[q_index].op = tempLocal3;
    strcpy(quadruple[q_index].arg2,tempLocal2);

    q_index++;
    result2[1]++;
    
    char tempLocal4[4];
    strcpy(tempLocal4,result2);
    tempLocal4[3] = ':';
    strcpy(quadruple[q_index].arg1,tempLocal4);

    char tempLocal5[] = " ";
    char tempLocal6 = ' ';

    quadruple[q_index].op = tempLocal6;
    strcpy(quadruple[q_index].arg2,tempLocal5);

    q_index++;
    result2[1]++;
}
     

void three_address_code() {
    int i;
    for(i=0 ; i<q_index ; i++) 
    printf("\n%s := %s %c %s",quadruple[i].rslt,quadruple[i].arg1,quadruple[i].op,quadruple[i].arg2);
}

void yyerror(char *s){
    printf("Errror %s",s);
}

int yywrap() {
    return 1;
}

int main() {
    yyparse();
    three_address_code();
    return 0;
} 

输入:

i=2*5-10
while i<5 do 

输出

Errror Syntax error

如果有人能找出语法错误的地方,或者我的代码是否错误,那将非常有帮助。

解决方法

您的开始符号是 wstmt,因此程序接受单个 while 语句作为输入。如果需要接受语句序列,则需要一个扩展为语句序列的符号作为起始符号。

此外,您的词法分析器不会吞下空格,因此任何包含空格的程序都会出错。

,

在我看来,你的规则

wstmt   :   WHILE { quadruple_entry_loop(); } stmt  DO { quadruple_entry_do(); }
    ;

错了。由于您的 stmt 仅考虑赋值表达式,因此您应该在表达式中包含 stmt 以使语法有效。

你的 wstmt 语法规则,没有穿插代码是:

wstmt   : WHILE stmt DO ;

你应该把它改成:

wstmt   :   WHILE  expr  DO stmt ;

并且正确输出代码的精确点应该是:

wstmt: WHILE { 
        /* get a new label and place it at
         * this point,you'll need to jump
         * here,push the label name in a
         * stack */
    }
    expr { 
        /* include code here to evaluate
         * (probably you do it inside expr */
    }
    DO {
        /* get a new label but don't place it
         * yet,and push it's name in the
         * stack */
    }
    stmt {
        /* a jump to the first label you
         * pushed (the one that is already
         * placed),then emit code for the
         * second label (the one that is not
         * placed yet */
    };  

(并且您还应该包括在 < 语法中使用 >expr 以及评估布尔运算符的可能性)

stmt 非终结符强制您作为 while 条件放置的内容为赋值,而这不是您作为输入编写的内容。

在我看来,你应该分两个阶段实现这个编译器......首先尝试进行完整的语言解析(因为它是一个单独的、不相关的、完全不同的问题),一旦你让解析器做出正确的语法树(您可以尝试构建正确的语法树并打印它),一旦它工作……然后您就可以散布代码生成代码。