Yacc 和 Lex 错误
Yacc and Lex error
我正在使用 Yacc 和 Lex 做一个练习。练习是这样的:
创建并编译文件 .y 和 .lex 后,我使用这 2 个命令没有错误:
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(.name, .val);}
;
expr : expr '+' term {$$.val = .val + .val;}
| term {$$.val = .val;}
;
term : term '*' factor {$$.val = .val * .val;}
| factor {$$.val = .val;}
;
factor : '(' expr ')' {$$.val = .val;}
| if_expr {$$.val = .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 = (.val ? .val : .val);}
;
write_stat : WRITE expr {printf("%d\n", .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
眼前的问题是:
#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;
}
在此代码段中,a
和 b
属于 不同类型 ,因为在 C 语言中,每次出现匿名 struct
(也就是说,不带标记名的 struct
是不同的类型。
如果 a
和 b
的声明在不同的文件中分别编译,就不会有问题,因为 a
和 b
的类型兼容。但是在同一个文件中,为了使聚合赋值有效,值和目标变量的类型必须相同,而不仅仅是兼容。由于它们不相同,因此会产生错误。
简单的解决方法是使 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 的大部分内容也不正确。
我正在使用 Yacc 和 Lex 做一个练习。练习是这样的:
创建并编译文件 .y 和 .lex 后,我使用这 2 个命令没有错误:
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(.name, .val);}
;
expr : expr '+' term {$$.val = .val + .val;}
| term {$$.val = .val;}
;
term : term '*' factor {$$.val = .val * .val;}
| factor {$$.val = .val;}
;
factor : '(' expr ')' {$$.val = .val;}
| if_expr {$$.val = .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 = (.val ? .val : .val);}
;
write_stat : WRITE expr {printf("%d\n", .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
眼前的问题是:
#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;
}
在此代码段中,a
和 b
属于 不同类型 ,因为在 C 语言中,每次出现匿名 struct
(也就是说,不带标记名的 struct
是不同的类型。
如果 a
和 b
的声明在不同的文件中分别编译,就不会有问题,因为 a
和 b
的类型兼容。但是在同一个文件中,为了使聚合赋值有效,值和目标变量的类型必须相同,而不仅仅是兼容。由于它们不相同,因此会产生错误。
简单的解决方法是使 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 的大部分内容也不正确。