【问题标题】:Yacc and Lex errorYacc 和 Lex 错误
【发布时间】:2018-04-13 10:13:26
【问题描述】:

我正在使用 Yacc 和 Lex 进行练习。练习是这样的:

image

在创建和编译文件 .y 和 .lex 后,这两个命令没有错误:

bison -vd -o parser.c es.y
flex es.lex

在这些之后,我编译 parser.c:

gcc -g -c parser.c

我有这些错误:

In function yyparse:
parser.c:1304: error: incompatible types in assignment
parser.c:1334: error: incompatible types in assignment 
parser.c:1436: error: incompatible types in assignment
parser.c:1576: error: incompatible types in assignment

和其他警告。 我的 lex 文件是这样的:

%{
#include "parser.h"
#include "def.h"
Value lexval;
%}
%option noyywrap
delimiter       [ \t\n]
spacing         {delimiter}+
digit           [0-9]
num             {digit}+
id              [a-zA-Z]+
sugar           [()*+=;]
%%
{spacing}       ;
{sugar}         {return(yytext[0]);}
if              {return(IF);}
else            {return(ELSE);}
then            {return(THEN);}
end             {return(END);}
write           {return(WRITE);}
{id}            {lexval.name = newstring(yytext); return(ID);}
{num}           {lexval.val=atoi(yytext); return(NUM);}
.               {return(ERROR);}
%%
char *newstring(char *s)
{
  char *p;
  p = malloc(sizeof(strlen(s)+1));
  strcpy(p, s);
  return(p);
}

我的 yacc 文件是:

%{
#include "def.h"
#define YYSTYPE struct{char *name; int val;}
#define NIL -1
extern Value lexval;
struct SymbTab{char label[30];int value;};
struct SymbTab tab[1000];
int val;
int size=0;
%}
%token ID NUM IF THEN ELSE END WRITE ERROR
%%
program : stat_list
             ;

stat_list : stat ';' stat_list
       | stat
       ;

stat : assign_stat
       | write_stat
       ;

assign_stat : ID {$$.name = lexval.name;} '=' expr {assign($2.name, $4.val);}
                  ;

expr : expr '+' term {$$.val = $1.val + $3.val;}
       | term {$$.val = $1.val;}
       ;

term : term '*' factor {$$.val = $1.val * $3.val;}
       | factor {$$.val = $1.val;}
       ;

factor : '(' expr ')' {$$.val = $2.val;}
       | if_expr {$$.val = $1.val;}
       | ID {if((val =lookup(lexval.name)) == NIL) error(); else $$.val = val;}
       | NUM {$$.val = lexval.val;}
       ;

if_expr : IF expr THEN expr ELSE expr END {$$.val = ($2.val ? $4.val : $6.val);}
       ;

write_stat : WRITE expr {printf("%d\n", $2.val);}
        ;
%%
int isPresent(char *lab)
{
    int i;
    for(i=0; i<size; i++)
        if(strcmp(tab[i].label,lab)==0)
            return i;
    return -1000;
}
void assign(char *l,int n)
{
    if(isPresent(l)==-1000)
    {
        strcpy(tab[size].label,l);
        tab[size].value=n;
        size++;
    }
    else
        tab[isPresent(l)].value=n;
}
int lookup(char *lab)
{
    int i;
    for(i=0; i<size; i++)
        if(strcmp(tab[i].label,lab)==0)
            return tab[i].value;
    return NIL;
}

void error(){ fprintf(stderr, "Syntax error\n"); }


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

我的 def.h 是:

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

char *newstring(char*),
     *strcpy(char*, const char*);

void error(),assign(char *l,int n);

int lookup(char *lab),isPresent(char *lab),yylex(),main();

typedef union
{
    int val;
    char *name;
} Value;

我不知道如何解决我在 parser.c 中遇到的错误

【问题讨论】:

    标签: compiler-construction yacc lex


    【解决方案1】:

    当前的问题是:

    #define YYSTYPE struct{char *name; int val;}
    

    下面是宏定义的一个小例子;你可以用你的 C 编译器试试:

    #define YYSTYPE struct{char *name; int val;}
    int main(void) {
      YYSTYPE a = {"", 42};
      YYSTYPE b;
      b = a;
      return 0;
    }
    

    我无法使用我手头的任何版本的 gcc 重现该错误消息。 gcc 6.x 和 gcc 7.x 都会产生更有意义的错误消息(以及大量警告):

    struct.c:5:5: error: incompatible types when assigning to type ‘struct <anonymous>’ from type ‘struct <anonymous>’
    

    即便如此,这个错误还是需要解释一下。由于YYSTYPE是一个宏,所以上面的代码完全等价于扩展宏的代码:

    int main(void) {
      struct{char *name; int val;} a = {"", 42};
      struct{char *name; int val;} b;
      b = a;
      return 0;
    }
    

    在这个 sn-p 中,ab 属于不同的类型,因为在 C 中,每次出现匿名 struct(即,struct 没有tagname) 是一个独特的类型。

    如果ab 的声明分别在不同的文件中编译,则不会有问题,因为ab 的类型是兼容的。但是在同一个文件中,为了使聚合赋值有效,值和目标变量的类型必须相同,而不仅仅是兼容。由于它们不相同,因此会产生错误。

    简单的解决方法是将YYSTYPE 设为类型别名而不是宏:

    typedef struct{char *name; int val;} YYSTYPE;
    

    类型别名是特定类型的编译时标识符;该类型仍然是匿名struct,但现在每个变量声明为YYSTYPE 都是相同的匿名struct

    但这对于 bison/yacc 来说还不够,因为您还必须告诉它您正在提供自己的 YYSTYPE 定义。否则,它将插入自己的定义(这是int 的类型别名),这将产生不同的编译器错误,因为YYSTYPE 将有两个不兼容的定义。所以你还需要包含以下看起来很奇怪的宏定义:

    #define YYSTYPE YYSTYPE
    

    如果YYSTYPE 被定义为宏,则不会包含bison/yacc 的默认定义。 (它受#ifdef YYSTYPE 保护。)显然递归定义不是问题,因为C 预处理器只替换给定名称一次;一旦将YYSTYPE 替换为YYSTYPE,它就不会再次尝试替换。


    在我看来,这项任务本身似乎暴露了对 yacc/lex 工具集的可悲缺乏知识。使用lexval 而不是使用标准的yylval 机制会给语法带来许多问题;例如,它强制在您的assignment 生产中使用中间规则操作。我不知道你为什么被要求这样做,而且我怀疑你所学到的关于 yacc 和 lex 的其他大部分内容也是不正确的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-19
      • 1970-01-01
      • 2010-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多