Yacc 解析不会完成减少生产

时间:2021-05-14 08:17:19

标签: c yacc lex

我正在通过 lax 和 yacc 构建一个小型解释器,该解释器使用基本编程语言执行加法和乘法以及打印整数列表。

例如指令:

Print(2,3,4);

应该输出:2 3 4

和指令:

Print(+(2,3));

应该输出:5

第一条打印指令运行良好。然而,任何加法指令(+ 后跟一个列表)都会自行解决,并且该操作会返回正确的答案(通过 printf 发现),但在加法指令之后执行外部打印指令之前,yacc 似乎停止了。

这是我的 .l 文件:

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

digit [0-9]

%%
{digit}{digit}*     {yylval.str = strdup(yytext); return IntLit;}
Print               {return Print;}
\+                  {yylval.str = strdup(yytext); return '+';}
\*                  {yylval.str = strdup(yytext); return '*';}
\(                  {return '(';}
\)                  {return ')';}
\,                  {return ',';}
\;                  {return ';';}
\t                  {}
\r                  {}
\n                  {}

%%

    
int yywrap () {
    return 1;
}

这是我的 .y 文件:

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


extern int yylex();
extern int yyparse();
extern int yyerror(char *s);
extern char *yytext;

void doPrint(char *s);
int evaluate(char *c, char *s);
char* append(char *s, char *s2);
char* makeSingle(char *s);

%}

%union {
  char character;
  char *str;
}

%type <str> Item
%type <str> IntLit
%type <str> List
%type <str> Func

%token Print
%token IntLit

%%

Prog        :   StmtSeq                 { };
StmtSeq     :   Stmt StmtSeq            { };
StmtSeq     :                           { };
Stmt        : Print '(' List ')' ';'    { doPrint($3); };   
List        : List ',' Item             { $$ = append($1, $3); };
List        : Item                      { $$ = makeSingle($1); };
Item        : Func '(' List ')'         { $$ = evaluate($1, $3); };
Item        : IntLit                    { $$ = $1;};
Func        : '+'                       {$$ = yylval.str; };
Func        : '*'                       {$$ = yylval.str; };

%%

int main(int argc, char *argv[]){
    //yydebug = 1;
    return yyparse();
}

void doPrint(char *s){
    char * token = strtok(s, ",");
    while(token != NULL){
        printf("%s", token);
        printf(" ");
        token = strtok(NULL, ",");
    }
    printf("\n");
}

int evaluate(char *c, char *s){
    char * result;
    int res;
    int x;
    char * token;
    int cmp = strcmp(c, "+");
    if(cmp == 0){
        token = strtok(s, ",");
        res = 0;
        while(token != NULL){
            x = atoi(token);
            res = res + x;
            token = strtok(NULL, ",");
        }
        sprintf(result,"%d",res);
    } else {
        token = strtok(s, ",");
        res = 1;
        while(token != NULL){
            x = atoi(token);
            res = res * x;
            token = strtok(NULL, ",");
        }
        sprintf(result,"%d",res);
    }
    printf("resultstring is: '%s'\n", result);
    return result;
}

char* append(char *s, char *s2){
    char * result = s;
    strcat(result, ",");
    strcat(result, s2);
    return result;
}

char* makeSingle(char *s){
    //char * result;
    //sprintf(result, "%d", c);
    //return result;
    return s;
}

extern int yyerror(char *s)  {
  printf(s);
  return 1;
}

如果我输入说明:

Print(2,3,4);
Print(+(2,3));

第一条打印指令按预期工作,但第二条指令在打印加法结果之前停止,但在评估完成之后。

我是 yacc/lex 的新手,我不确定为什么 yacc 停止而不打印添加的结果。添加的结果不应该是可以重写为“列表”然后正确打印的“项目”吗?任何帮助或建议将不胜感激,谢谢!

编辑 在对 yydebug = 1; 时的输出进行了更多研究后,我发现解析在完全减少之前突然结束(我相信)。调试过程的最后一部分是:

Reducing stack by rule 7 (line 40):
    $1 = nterm Func ()
    $2 = token '(' ()
    $3 = nterm List ()
    $4 = token ')' ()

在此缩减中没有 $$,因为之前完成的所有其他缩减中都有

1 个答案:

答案 0 :(得分:0)

当你这样做时你看到了什么:doPrint(1);
您在 doPrint 中有一行:char * token = strtok(s, ",");
当您通过评估函数时,您会消耗所有逗号,只留下一个数字。当您调用 doPrint 时,字符串中没有逗号,因此返回 NULL。
如果,当它返回 NULL(但字符串有内容)时,您可以有第二个,或者您可以重新处理解析字符串的方式。

顺便说一下,strdup() 返回一个你没有释放的 malloced 字符串。
您还使用从 strdup 接收到的字符串调用 strcat(),您正在写入内存并且您不知道大小。 strcat() 会让你溢出缓冲区。
更好的方法是,当您将一个字符串附加到另一个字符串并且您不知道它们的大小时,可以执行以下操作:

char *mergestrings(char *str1,char *str2) {
  char *newstr=(char*)malloc(sizeof(char)*(strlen(str1)+strlen(str2)+1));
  strcpy(newstr,str1);
  strcat(newstr,str2);
  free(str1);
  free(str2);
  return newstr;
}

或者使用 realloc 或其他东西,但你应该注意你对内存的处理。

最大的问题是评估函数的定义和指针的使用。
扫描仪.l:

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

digit 0|(([1-9])[0-9]*)
operator [+*]

%%
{digit}             {yylval.str = strdup(yytext); return IntLit;}
{operator}          {yylval.character=yytext[0]; return Operator;}
"Print"             {return Print;}
\(                  {return LParen;}
\)                  {return RParen;}
\,                  {return Comma;}
\;                  {return SemiCln;}
\t                  {}
\r                  {}
\n                  {}

%%

parser.y:

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


extern int yylex();
extern int yyparse();
extern int yyerror(char *s);
extern char *yytext;

void doPrint(char *s);
char* evaluate(char c, char *s);
char* append(char *s, char *s2);

%}

%union {
  char character;
  char *str;
}

%type <str> Item List
%type <character> Func

%token Print Operator IntLit LParen RParen SemiCln Comma

%%

Prog       : Stmt                                { }
           | Prog Stmt                           { }
           ;
Stmt       : Print LParen List RParen SemiCln    { doPrint($3); }
           ;
List       : List Comma Item                     { $$ = append($1, $3); }
           | Item                                { $$ = $1; }
           ;
Item       : Func LParen List RParen             { $$ = evaluate($1, $3); }
           | IntLit                              { $$ = yylval.str;}
           ;
Func       : Operator                            { $$ = yylval.character; }
           ;

%%

int main(int argc, char *argv[]){
    return yyparse();
}

void doPrint(char *s){
    char *stop = strchr(s, ','), *start=s;
    while(stop){
        *stop='\0';
        printf("%s ", start);
        start = stop+1;
        stop = strchr(start, ',');
    }
    printf("%s\n",start);
    free(s);
}

char *evaluate(char c, char *s){
    int res=0;
    if(c == '+'){
        char *stop = strchr(s, ','), *start=s;
        while(stop){
            *stop='\0';
            res += atoi(start);
            start = stop+1;
            stop = strchr(start, ',');
        }
        res += atoi(start);
    } else {
        char *stop = strchr(s, ','), *start=s;
        res = 1;
        while(stop){
            *stop = '\0';
            res *= atoi(start);
            start = stop+1;
            stop = strchr(start, ',');
        }
        res *= atoi(start);
    }
    free(s);
    char *result = (char*)malloc(12); // big enough for a 32 bit integer
    sprintf(result,"%d",res);
    return result;
}

char* append(char *s, char *s2){
    char *result = (char*)realloc(s,strlen(s)+strlen(s2)+2);
    if (result) {
      strcat(result, ",");
      strcat(result, s2);
    }
    else {
        result=(char*)malloc(strlen(s)+strlen(s2)+2);
        sprintf(result,"%s,%s",s,s2);
        free(s);
    }
    free(s2);
    return result;
}

extern int yyerror(char *s)  {
  printf(s);
  return 1;
}
相关问题