书接上文,在之前的两篇文章中,我们已经实现了词法分析、语法分析,接下来,我们需要实现的是语义分析。
具体目标:
其他要求:
在本次实验中,我们对C-语言做如下假设,你可以认为这些就是C-语言的特性(注意,假设3、假设4、假设5可能因后面的不同选做要求而有所改变):
你的程序需要对输入文件进行语义分析并检查如下类型的错误,必做部分不会出现结构体和数组:
除此之外,你的程序可以选择完成以下部分或全部的要求:
以上是根据实验要求给出的错误类型检查示例,你不一定要完全遵循这里给出的错误类型的检查,只要给出符合实验报告语义要求的错误类型检查都可以,需要你在实验报告中说明。这里给出的实验测试代码中的函数均无参数,若完成有参数的函数并进行参数检查有额外加分。此外选做部分均无测试代码,请根据自己给出的错误类型,自行给出测试样例说明。
// 抽象语法树
typedef struct treeNode
{
// 行数
int line;
// Token类型
char *name;
// 1变量 2函数 3常数 4数组 5结构体
int tag;
// 使用孩子数组表示法
struct treeNode *cld[10];
int ncld;
// 语义值
char *content;
// 数据类型 int 或 float
char *type;
// 变量的值
float value;
} * Ast, *tnode;
为了便于获取孩子节点属性,修改了实验二中语法分析树的实现方式,用数组存储子节点。
// 表示当前节点不是终结符号,还有子节点
if (num > 0)
{
father->ncld = num;
// 第一个孩子节点
temp = va_arg(list, tnode);
father->cld[0] = temp;
setChildTag(temp);
// 父节点行号为第一个孩子节点的行号
father->line = temp->line;
if (num == 1)
{
//父节点的语义值等于左孩子的语义值
father->content = temp->content;
father->tag = temp->tag;
}
else
{
for (i = 1; i < num; i++)
{
temp = va_arg(list, tnode);
(father->cld)[i] = temp;
// 该节点为其他节点的子节点
setChildTag(temp);
}
}
}
为了在语法分析的同时实现语义分析,除了在Bison代码中进行修改外,还需要修改语法分析树节点的构造函数,实现属性/类型/语义值的传递。父节点的类型、语义值可以从子节点处获得。
我用线性链表实现了符号表,将不同种类的符号组织成不同的表,维护每张表的表头和表尾,从表尾插入,从表头开始遍历。
// 变量符号表的结点
typedef struct var_
{
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
struct var_ *next;
}var;
var *varhead, *vartail;
// 建立变量符号
void newvar(int num,...);
// 变量是否已经定义
int findvar(tnode val);
// 变量类型
char* typevar(tnode val);
// 这样赋值号左边仅能出现ID、Exp LB Exp RB 以及 Exp DOT ID
int checkleft(tnode val);
实现了创建遍历符号、变量类型检查、查看变量是否已经定义、检查赋值号左边变量类型的功能函数。为了实现检查结构体域的功能,在符号表节点中设置了inStruc 和 strucNum变量,用于标注该变量属于哪一个结构体。
// 建立变量符号
void newvar(int num, ...)
{
va_list valist;
va_start(valist, num);
var *res = (var *)malloc(sizeof(var));
tnode temp = (tnode)malloc(sizeof(tnode));
if (inStruc && LCnum)
{
// 是结构体的域
res->inStruc = 1;
res->strucNum = strucNum;
}
else
{
res->inStruc = 0;
res->strucNum = 0;
}
// 变量声明 int i
temp = va_arg(valist, tnode);
res->type = temp->content;
temp = va_arg(valist, tnode);
res->name = temp->content;
vartail->next = res;
vartail = res;
}
// 查找变量
int findvar(tnode val)
{
var *temp = (var *)malloc(sizeof(var *));
temp = varhead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
{
if (inStruc && LCnum) // 当前变量是结构体域
{
if (!temp->inStruc)
{
// 结构体域与变量重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else if (temp->inStruc && temp->strucNum != strucNum)
{
// 不同结构体中的域重名
printf("Error type 10 at Line %d:Struct Fields use the same name.\n", yylineno);
}
else
{
// 同一结构体中域名重复
return 1;
}
}
else // 当前变量是全局变量
{
if (temp->inStruc)
{
// 变量与结构体域重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else
{
// 变量与变量重名,即重复定义
return 1;
}
}
}
temp = temp->next;
}
return 0;
}
由于设置了相关属性值,可以实现在寻找变量的同时判断错误类型9和10,这两种错误类型只做提醒,不影响变量的定义以及变量符号表的插入操作。
符号表的插入和遍历操作大致相同,这里展示变量符号表的相关操作,其他符号表只说明不同的地方。
// 赋值号左边只能出现ID、Exp LB Exp RB 以及 Exp DOT ID
int checkleft(tnode val)
{
if (val->ncld == 1 && !strcmp((val->cld)[0]->name, "ID"))
return 1;
else if (val->ncld == 4 && !strcmp((val->cld)[0]->name, "Exp") && !strcmp((val->cld)[1]->name, "LB") && !strcmp((val->cld)[2]->name, "Exp") && !strcmp((val->cld)[3]->name, "RB"))
return 1;
else if (val->ncld == 3 && !strcmp((val->cld)[0]->name, "Exp") && !strcmp((val->cld)[1]->name, "DOT") && !strcmp((val->cld)[2]->name, "ID"))
return 1;
else
return 0;
}
在赋值操作中,左值表示地址,这说明表达式“x = 3”是正确的,而“x + 2 = 3”是错误的。这样赋值号左边仅能出现ID、Exp LB Exp RB 以及 Exp DOT ID,因此需要检查赋值号左边的语法分析树节点的子节点是否符合上述三种情况,用来判断是否为对应的错误类型。
// 函数符号表的结点
typedef struct func_
{
int tag; //0表示未定义,1表示定义
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
char *rtype; //声明返回值类型
int va_num; //记录函数形参个数
char *va_type[10];
struct func_ *next;
}func;
func *funchead,*functail;
// 记录函数实参
int va_num;
char* va_type[10];
void getdetype(tnode val);//定义的参数
void getretype(tnode val);//实际的参数
void getargs(tnode Args);//获取实参
int checkrtype(tnode ID,tnode Args);//检查形参与实参是否一致
// 建立函数符号
void newfunc(int num, ...);
// 函数是否已经定义
int findfunc(tnode val);
// 函数类型
char *typefunc(tnode val);
// 函数的形参个数
int numfunc(tnode val);
// 函数实际返回值类型
char *rtype[10];
int rnum;
void getrtype(tnode val);
// 创建函数符号
void newfunc(int num, ...)
{
int i;
va_list valist;
va_start(valist, num);
tnode temp = (tnode)malloc(sizeof(struct treeNode));
switch (num)
{
case 1:
if (inStruc && LCnum)
{
// 是结构体的域
functail->inStruc = 1;
functail->strucNum = strucNum;
}
else
{
functail->inStruc = 0;
functail->strucNum = 0;
}
//设置函数返回值类型
temp = va_arg(valist, tnode);
functail->rtype = temp->content;
functail->type = temp->type;
for (i = 0; i < rnum; i++)
{
if (rtype[i] == NULL || strcmp(rtype[i], functail->rtype))
printf("Error type 12 at Line %d:Func return type error.\n", yylineno);
}
functail->tag = 1; //标志为已定义
func *new = (func *)malloc(sizeof(func));
functail->next = new; //尾指针指向下一个空结点
functail = new;
break;
case 2:
//记录函数名
temp = va_arg(valist, tnode);
functail->name = temp->content;
//设置函数声明时的参数
temp = va_arg(valist, tnode);
functail->va_num = 0;
getdetype(temp);
break;
default:
break;
}
}
本次实验中,我对函数的声明和定义分别作了处理(进行FunDec和ExtDef规约时作不同处理)。在进行ExtDef规约时可以获取函数的返回值类型,并且同时判断函数的声明返回类型、实际返回值类型是否相同。在FunDec规约时检测函数是否已定义并且设置函数的形参和函数名称。
//定义的参数
void getdetype(tnode val)
{
int i;
if (val != NULL)
{
if (!strcmp(val->name, "ParamDec"))
{
functail->va_type[functail->va_num] = val->cld[0]->content;
functail->va_num++;
return;
}
for (i = 0; i < val->ncld; ++i)
{
getdetype((val->cld)[i]);
}
}
else
return;
}
//实际的参数
void getretype(tnode val)
{
int i;
if (val != NULL)
{
if (!strcmp(val->name, "Exp"))
{
va_type[va_num] = val->type;
va_num++;
return;
}
for (i = 0; i < val->ncld; ++i)
{
getretype((val->cld)[i]);
}
}
else
return;
}
//检查形参与实参是否一致,没有错误返回0
int checkrtype(tnode ID, tnode Args)
{
int i;
va_num = 0;
getretype(Args);
func *temp = (func *)malloc(sizeof(func *));
temp = funchead->next;
while (temp != NULL && temp->name != NULL && temp->tag == 1)
{
if (!strcmp(temp->name, ID->content))
break;
temp = temp->next;
}
if (va_num != temp->va_num)
return 1;
for (i = 0; i < temp->va_num; i++)
{
if (temp->va_type[i] == NULL || va_type[i] == NULL || strcmp(temp->va_type[i], va_type[i]) != 0)
return 1;
}
return 0;
}
由于在Bison代码中,对于函数形参以及实参规约的节点都是父节点,需要用先序遍历获取树结构中所有的参数节点,并进行存储,以便于检测相关类型错误。
// 数组符号表的结点
typedef struct array_
{
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
struct array_ *next;
}array;
array *arrayhead,*arraytail;
// 建立数组符号
void newarray(int num, ...);
// 查找数组是否已经定义
int findarray(tnode val);
// 数组类型
char *typearray(tnode val);
本次实验中我没有对数组符号做太多处理,该符号表和变量符号表操作基本一致。
// 结构体符号表的结点
typedef struct struc_
{
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
struct struc_ *next;
}struc;
struc *struchead, *structail;
// 建立结构体符号
void newstruc(int num, ...);
// 查找结构体是否已经定义
int findstruc(tnode val);
// 当前是结构体域
int inStruc;
// 判断结构体域,{ 和 }是否抵消
int LCnum;
// 当前是第几个结构体
int strucNum;
由于错误类型11需要检测结构体定义是否和之前定义的结构体、变量名称一致,因此在检测重复定义时做一些改动:
// 结构体是否和结构体或变量的名字重复
int findstruc(tnode val)
{
struc *temp = (struc *)malloc(sizeof(struc *));
temp = struchead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
return 1;
temp = temp->next;
}
if (findvar(val) == 1)
return 1;
return 0;
}
需要实现在语法分析的同时进行语义分析,因此在规约时对某些类型的节点需要进行符号表插入、遍历以及错误类型的检查工作。
错误类型定义:
Exp:ID {
$$=newAst("Exp",1,$1);
// 错误类型1:变量在使用时未经定义
if(!findvar($1)&&!findarray($1))
printf("Error type 1 at Line %d:undefined variable %s\n",yylineno,$1->content);
else
$$->type=typevar($1);
}
操作数ID会被规约为Exp,ID就是变量的名称,因此需要在这里检测变量是否已经定义。
Exp:Exp ASSIGNOP Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 当有一边变量是未定义时,不进行处理
if($1->type==NULL || $3->type==NULL){
return;
}
// 错误类型2:赋值号两边的表达式类型不匹配
if(strcmp($1->type,$3->type))
printf("Error type 2 at Line %d:Type mismatched for assignment.\n ",yylineno);
// 错误类型3:赋值号左边出现一个只有右值的表达式
if(!checkleft($1))
printf("Error type 3 at Line %d:The left-hand side of an assignment must be a variable.\n ",yylineno);
}
由于在进行语法分析树建立时修改了节点构造函数,使得子节点类型能够传递给父节点,在检测赋值号两端表达式类型时就可以直接检测表达式节点的type属性是否相同。这里判断了type属性是否为NULL(表达式是否未定义),如果不进行相关处理,碰到未定义的表达式,其type属性为NULL,直接对NULL使用strcmp函数会报出段错误。错误类型3和错误类型2都是在规约到ASSIGNOP符号时进行检测。
Exp:ID LP Args RP {
$$=newAst("Exp",4,$1,$2,$3,$4);
// 错误类型4:对普通变量使用“(...)”或“()”(函数调用)操作符
if(!findfunc($1) && (findvar($1)||findarray($1)))
printf("Error type 4 at Line %d:'%s' is not a function.\n ",yylineno,$1->content);
// 错误类型5:函数在调用时未经定义
else if(!findfunc($1))
printf("Error type 5 at Line %d:Undefined function %s\n ",yylineno,$1->content);
// 函数实参和形参类型不一致
else if(checkrtype($1,$3)){
printf("Error type 13 at Line %d:Function parameter type error.\n ",yylineno);
}else{}
}
|ID LP RP {
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型4:对普通变量使用“(...)”或“()”(函数调用)操作符
if(!findfunc($1) && (findvar($1)||findarray($1)))
printf("Error type 4 at Line %d:'%s' is not a function.\n ",yylineno,$1->content);
// 错误类型5:函数在调用时未经定义
else if(!findfunc($1))
printf("Error type 5 at Line %d:Undefined function %s\n ",yylineno,$1->content);
else {}
}
在检测到ID LP … RP时进行函数调用错误类型的检测,我的处理方式是当函数已经定义时就不需要检测是否对普通变量进行()操作。另外对于(Args)类型的函数定义规约,需要检测实参和形参类型是否一致。
Exp:Exp PLUS Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
|Exp MINUS Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
|Exp STAR Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
|Exp DIV Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
在本次实验中我只对加减乘除四种运算符号进行了操作数类型是否一致的检测。
ParamDec:Specifire VarDec {
$$=newAst("ParamDec",2,$1,$2);
// 错误类型7:变量出现重复定义
if(findvar($2)||findarray($2))
printf("Error type 7 at Line %d:Redefined Variable '%s'\n",yylineno,$2->content);
else if($2->tag==4)
newarray(2,$1,$2);
else
newvar(2,$1,$2);
}
;
Def:Specifire DecList SEMI {
$$=newAst("Def",3,$1,$2,$3);
// 错误类型7:变量出现重复定义
if(findvar($2)||findarray($2))
printf("Error type 7 at Line %d:Redefined Variable '%s'\n",yylineno,$2->content);
else if($2->tag==4)
newarray(2,$1,$2);
else
newvar(2,$1,$2);
}
;
本次实验中假设函数形参也是全局变量,因此在对变量类型错误检测以及变量符号表插入时,需要在两个地方添加代码,分别是变量定义、函数定义时。
FunDec:ID LP VarList RP {
$$=newAst("FunDec",4,$1,$2,$3,$4); $$->content=$1->content;
// 错误类型8:函数出现重复定义(即同样的函数名出现了不止一次定义)
if(findfunc($1))
printf("Error type 8 at Line %d:Redefined Function '%s'\n",yylineno,$1->content);
// 设置函数名称以及参数列表
else newfunc(2,$1,$3);
}
|ID LP RP {
$$=newAst("FunDec",3,$1,$2,$3); $$->content=$1->content;
// 错误类型8:函数出现重复定义(即同样的函数名出现了不止一次定义)
if(findfunc($1))
printf("Error type 8 at Line %d:Redefined Function '%s'\n",yylineno,$1->content);
// 设置函数名称以及参数列表
else newfunc(2,$1,$3);
}
;
我对函数声明和函数定义(有函数体时)分别进行了处理,函数定义时检测是否重复定义,并且设置函数名称以及参数列表。
// 当前是结构体域
int inStruc;
// 判断结构体域,{ 和 }是否抵消
int LCnum;
// 当前是第几个结构体
int strucNum;
为了实现结构体域的检测,设置了相关的全局变量。
在词法分析过程中,当遇到STRUCT TOKEN时,表示有结构体定义,这时将inStruc置1并且strucNum++。
{STRUCT} {
yylval.type_tnode=newAst("STRUCT",0,yylineno);
// 结构体数加一
strucNum++;
// 开始扫描结构体定义内容
inStruc=1;
return STRUCT;}
同时为了区分结构体域和结构体声明之外的其他全局变量,我通过两种方式来进行检测,一种是在进行StructSpecifire:STRUCT OptTag LC DefList RC规约时,将inStruc置0;
StructSpecifire:STRUCT OptTag LC DefList RC {
// 结构体定义完成,当前在结构体定义外部
inStruc = 0;
另一种是在词法分析器遇到LC TOKEN时判断inStruc,如果是在结构体声明中,则LCnum++,如果词法分析器遇到RC并且也在结构体声明中,则LCnum—
{LC} {
yylval.type_tnode=newAst("LC",0,yylineno);
if(inStruc){
// 结构体定义内部存在LC左花括号
LCnum++;
}
return LC;}
{RC} {
yylval.type_tnode=newAst("RC",0,yylineno);
if(inStruc){
// 结构体定义内部存在RC右花括号
LCnum--;
}
return RC;}
当inStruc为1且LCnum不为0时定义的变量就是结构体的域,此时会调用findvar和findarray函数进行相关错误类型的判断。
Exp:ID {
$$=newAst("Exp",1,$1);
// 错误类型1:变量在使用时未经定义
if(!findvar($1)&&!findarray($1))
printf("Error type 1 at Line %d:undefined variable %s\n",yylineno,$1->content);
else
$$->type=typevar($1);
}
// 函数是否已经定义
int findfunc(tnode val)
{
func *temp = (func *)malloc(sizeof(func *));
temp = funchead->next;
while (temp != NULL && temp->name != NULL && temp->tag == 1)
{
if (!strcmp(temp->name, val->content))
{
if (inStruc && LCnum) // 当前变量是结构体域
{
if (!temp->inStruc)
{
// 结构体域与变量重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else if (temp->inStruc && temp->strucNum != strucNum)
{
// 不同结构体中的域重名
printf("Error type 10 at Line %d:Struct Fields use the same name.\n", yylineno);
}
else
{
// 同一结构体中域名重复
return 1;
}
}
else // 当前变量是全局变量
{
if (temp->inStruc)
{
// 变量与结构体域重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else
{
// 变量与变量重名,即重复定义
return 1;
}
}
}
temp = temp->next;
}
return 0;
}
StructSpecifire:STRUCT OptTag LC DefList RC {
// 结构体定义完成,当前在结构体定义外部
inStruc = 0;
$$=newAst("StructSpecifire",5,$1,$2,$3,$4,$5);
// 错误类型11:结构体的名字与前面定义过的结构体或变量的名字重复
if(findstruc($2))
printf("Error type 11 at Line %d:Duplicated name '%s'\n",yylineno,$2->content);
else newstruc(1,$2);
}
在结构体声明时检测是否有重复定义。
Stmt:Exp SEMI {$$=newAst("Stmt",2,$1,$2); }
|Compst {$$=newAst("Stmt",1,$1); }
|RETURN Exp SEMI {
$$=newAst("Stmt",3,$1,$2,$3);
getrtype($2);
}
//函数实际返回值类型
void getrtype(tnode val)
{
rtype[rnum] = val->type;
rnum++;
}
在检测到return语句时将返回值类型存入到全局遍历数组中。
ExtDef:Specifire FunDec Compst {
$$=newAst("ExtDef",3,$1,$2,$3);
// 设置函数声明的返回值类型并检查返回类型错误
newfunc(1,$1);
}
;
在规约完函数声明以及函数体后可以进行返回类型错误检测,调用newfunc函数,传入参数 1:
// 创建函数符号
void newfunc(int num, ...)
{
int i;
va_list valist;
va_start(valist, num);
tnode temp = (tnode)malloc(sizeof(struct treeNode));
switch (num)
{
case 1:
if (inStruc && LCnum)
{
// 是结构体的域
functail->inStruc = 1;
functail->strucNum = strucNum;
}
else
{
functail->inStruc = 0;
functail->strucNum = 0;
}
//设置函数返回值类型
temp = va_arg(valist, tnode);
functail->rtype = temp->content;
functail->type = temp->type;
for (i = 0; i < rnum; i++)
{
if (rtype[i] == NULL || strcmp(rtype[i], functail->rtype))
printf("Error type 12 at Line %d:Func return type error.\n", yylineno);
}
functail->tag = 1; //标志为已定义
func *new = (func *)malloc(sizeof(func));
functail->next = new; //尾指针指向下一个空结点
functail = new;
break;
......
根据当前函数符号表中存储的返回类型值和返回值类型全局数组存储的类型值做对比,检测返回值类型不匹配的错误。
目标完成情况:
程序代码编译过程:
bision -d syntax_tree.y
flex syntax_tree.l
gcc syntax_tree.tab.c syntax_tree.c lex.yy.c -lfl -ly -o parser
测试代码:
// Type1.cmm
int main()
{
int i = 0;
j = i +1;
}
// Type2.cmm
int main()
{
int i;
i = 3.7;
}
// Type3.cmm
int main()
{
int i;
10 = i;
}
// Type4.cmm
int main()
{
int i;
i();
}
// Type5.cmm
int main()
{
int i;
inc();
return 0;
}
// Type6.cmm
int main()
{
float j;
10 + j;
}
// Type7.cmm
int main()
{
int i;
float i;
int j;
float j;
}
// Type8.cmm
int func(int i)
{
return i;
}
int func()
{
return 0;
}
int main()
{
}
// Type9.cmm
struct Position
{
float x;
};
int main()
{
float x;
}
// Type10.cmm
struct Position
{
float x;
};
struct Number
{
float x;
};
int main()
{
}
// Type11.cmm
struct Position
{
float x;
};
struct Position
{
float y;
};
int main()
{
}
// Type12.cmm
int main(){
float j;
return j;
}
// Type13.cmm
int sum(int a,int b)
{
}
int main(){
int i;
float j;
sum(i,j);
}
/*
*bison语法分析,对每条规则 按照孩子兄弟表示法建立语法结点
*/
%{
#include
#include
#include "syntax_tree.h"
%}
%union{
tnode type_tnode;
// 这里声明double是为了防止出现指针错误(segmentation fault)
double d;
}
/*声明记号*/
%token <type_tnode> INT FLOAT
%token <type_tnode> TYPE STRUCT RETURN IF ELSE WHILE ID COMMENT SPACE SEMI COMMA ASSIGNOP PLUS
%token <type_tnode> MINUS STAR DIV AND OR DOT NOT LP RP LB RB LC RC AERROR RELOP EOL
%type <type_tnode> Program ExtDefList ExtDef ExtDecList Specifire StructSpecifire
%type <type_tnode> OptTag Tag VarDec FunDec VarList ParamDec Compst StmtList Stmt DefList Def DecList Dec Exp Args
/*优先级*/
/*C-minus中定义的运算符的优先级,并没有包括所有C语言的*/
%right ASSIGNOP
%left OR
%left AND
%left RELOP
%left PLUS MINUS
%left STAR DIV
%right NOT
%left LP RP LB RB DOT
%nonassoc LOWER_THAN_ELSE
%nonassoc ELSE
/*产生式*/
/*$$表示左表达式 ${num}表示右边的第几个表达式*/
%%
/*High-level Definitions*/
Program:ExtDefList {$$=newAst("Program",1,$1); }
;
ExtDefList:ExtDef ExtDefList {$$=newAst("ExtDefList",2,$1,$2); }
| {$$=newAst("ExtDefList",0,-1); }
;
ExtDef:Specifire ExtDecList SEMI {
$$=newAst("ExtDef",3,$1,$2,$3);
// 错误类型7:变量出现重复定义
if(findvar($2))
printf("Error type 7 at Line %d:Redefined Variable '%s'\n",yylineno,$2->content);
else newvar(2,$1,$2);
}
|Specifire SEMI {$$=newAst("ExtDef",2,$1,$2); }
|Specifire FunDec Compst {
$$=newAst("ExtDef",3,$1,$2,$3);
// 设置函数声明的返回值类型并检查返回类型错误
newfunc(1,$1);
}
;
ExtDecList:VarDec {$$=newAst("ExtDecList",1,$1); }
|VarDec COMMA ExtDecList {$$=newAst("ExtDecList",3,$1,$2,$3); }
;
/*Specifire*/
Specifire:TYPE {$$=newAst("Specifire",1,$1);}
|StructSpecifire {$$=newAst("Specifire",1,$1); }
;
StructSpecifire:STRUCT OptTag LC DefList RC {
// 结构体定义完成,当前在结构体定义外部
inStruc = 0;
$$=newAst("StructSpecifire",5,$1,$2,$3,$4,$5);
// 错误类型11:结构体的名字与前面定义过的结构体或变量的名字重复
if(findstruc($2))
printf("Error type 11 at Line %d:Duplicated name '%s'\n",yylineno,$2->content);
else newstruc(1,$2);
}
|STRUCT Tag {$$=newAst("StructSpecifire",2,$1,$2); }
;
OptTag:ID {$$=newAst("OptTag",1,$1); }
|{$$=newAst("OptTag",0,-1); }
;
Tag:ID {$$=newAst("Tag",1,$1); }
;
/*Declarators*/
VarDec:ID {$$=newAst("VarDec",1,$1); $$->tag=1;}
|VarDec LB INT RB {$$=newAst("VarDec",4,$1,$2,$3,$4); $$->content=$1->content;$$->tag=4;}
;
FunDec:ID LP VarList RP {
$$=newAst("FunDec",4,$1,$2,$3,$4); $$->content=$1->content;
// 错误类型8:函数出现重复定义(即同样的函数名出现了不止一次定义)
if(findfunc($1))
printf("Error type 8 at Line %d:Redefined Function '%s'\n",yylineno,$1->content);
// 设置函数名称以及参数列表
else newfunc(2,$1,$3);
}
|ID LP RP {
$$=newAst("FunDec",3,$1,$2,$3); $$->content=$1->content;
// 错误类型8:函数出现重复定义(即同样的函数名出现了不止一次定义)
if(findfunc($1))
printf("Error type 8 at Line %d:Redefined Function '%s'\n",yylineno,$1->content);
// 设置函数名称以及参数列表
else newfunc(2,$1,$3);
}
;
VarList:ParamDec COMMA VarList {$$=newAst("VarList",3,$1,$2,$3); }
|ParamDec {$$=newAst("VarList",1,$1); }
;
ParamDec:Specifire VarDec {
$$=newAst("ParamDec",2,$1,$2);
// 错误类型7:变量出现重复定义
if(findvar($2)||findarray($2))
printf("Error type 7 at Line %d:Redefined Variable '%s'\n",yylineno,$2->content);
else if($2->tag==4)
newarray(2,$1,$2);
else
newvar(2,$1,$2);
}
;
/*Statement*/
Compst:LC DefList StmtList RC {$$=newAst("Compst",4,$1,$2,$3,$4); }
;
StmtList:Stmt StmtList{$$=newAst("StmtList",2,$1,$2); }
| {$$=newAst("StmtList",0,-1); }
;
Stmt:Exp SEMI {$$=newAst("Stmt",2,$1,$2); }
|Compst {$$=newAst("Stmt",1,$1); }
|RETURN Exp SEMI {
$$=newAst("Stmt",3,$1,$2,$3);
getrtype($2);
}
|IF LP Exp RP Stmt %prec LOWER_THAN_ELSE {$$=newAst("Stmt",5,$1,$2,$3,$4,$5); }
|IF LP Exp RP Stmt ELSE Stmt {$$=newAst("Stmt",7,$1,$2,$3,$4,$5,$6,$7); }
|WHILE LP Exp RP Stmt {$$=newAst("Stmt",5,$1,$2,$3,$4,$5); }
;
/*Local Definitions*/
DefList:Def DefList{$$=newAst("DefList",2,$1,$2); }
| {$$=newAst("DefList",0,-1); }
;
Def:Specifire DecList SEMI {
$$=newAst("Def",3,$1,$2,$3);
// 错误类型7:变量出现重复定义
if(findvar($2)||findarray($2))
printf("Error type 7 at Line %d:Redefined Variable '%s'\n",yylineno,$2->content);
else if($2->tag==4)
newarray(2,$1,$2);
else
newvar(2,$1,$2);
}
;
DecList:Dec {$$=newAst("DecList",1,$1); }
|Dec COMMA DecList {$$=newAst("DecList",3,$1,$2,$3); $$->tag=$3->tag;}
;
Dec:VarDec {$$=newAst("Dec",1,$1); }
|VarDec ASSIGNOP Exp {$$=newAst("Dec",3,$1,$2,$3); $$->content=$1->content;}
;
/*Expressions*/
Exp:Exp ASSIGNOP Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 当有一边变量是未定义时,不进行处理
if($1->type==NULL || $3->type==NULL){
return;
}
// 错误类型2:赋值号两边的表达式类型不匹配
if(strcmp($1->type,$3->type))
printf("Error type 2 at Line %d:Type mismatched for assignment.\n ",yylineno);
// 错误类型3:赋值号左边出现一个只有右值的表达式
if(!checkleft($1))
printf("Error type 3 at Line %d:The left-hand side of an assignment must be a variable.\n ",yylineno);
}
|Exp AND Exp{$$=newAst("Exp",3,$1,$2,$3); }
|Exp OR Exp{$$=newAst("Exp",3,$1,$2,$3); }
|Exp RELOP Exp{$$=newAst("Exp",3,$1,$2,$3); }
|Exp PLUS Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
|Exp MINUS Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
|Exp STAR Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
|Exp DIV Exp{
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型6:操作数类型不匹配或操作数类型与操作符不匹配
if(strcmp($1->type,$3->type))
printf("Error type 6 at Line %d:Type mismatched for operands.\n ",yylineno);
}
|LP Exp RP{$$=newAst("Exp",3,$1,$2,$3); }
|MINUS Exp {$$=newAst("Exp",2,$1,$2); }
|NOT Exp {$$=newAst("Exp",2,$1,$2); }
|ID LP Args RP {
$$=newAst("Exp",4,$1,$2,$3,$4);
// 错误类型4:对普通变量使用“(...)”或“()”(函数调用)操作符
if(!findfunc($1) && (findvar($1)||findarray($1)))
printf("Error type 4 at Line %d:'%s' is not a function.\n ",yylineno,$1->content);
// 错误类型5:函数在调用时未经定义
else if(!findfunc($1))
printf("Error type 5 at Line %d:Undefined function %s\n ",yylineno,$1->content);
// 函数实参和形参类型不一致
else if(checkrtype($1,$3)){
printf("Error type 13 at Line %d:Function parameter type error.\n ",yylineno);
}else{}
}
|ID LP RP {
$$=newAst("Exp",3,$1,$2,$3);
// 错误类型4:对普通变量使用“(...)”或“()”(函数调用)操作符
if(!findfunc($1) && (findvar($1)||findarray($1)))
printf("Error type 4 at Line %d:'%s' is not a function.\n ",yylineno,$1->content);
// 错误类型5:函数在调用时未经定义
else if(!findfunc($1))
printf("Error type 5 at Line %d:Undefined function %s\n ",yylineno,$1->content);
else {}
}
|Exp LB Exp RB {$$=newAst("Exp",4,$1,$2,$3,$4); }
|Exp DOT ID {$$=newAst("Exp",3,$1,$2,$3); }
|ID {
$$=newAst("Exp",1,$1);
// 错误类型1:变量在使用时未经定义
if(!findvar($1)&&!findarray($1))
printf("Error type 1 at Line %d:undefined variable %s\n",yylineno,$1->content);
else
$$->type=typevar($1);
}
|INT {$$=newAst("Exp",1,$1); $$->tag=3;$$->type="int";}
|FLOAT{$$=newAst("Exp",1,$1); $$->tag=3;$$->type="float";$$->value=$1->value;}
;
Args:Exp COMMA Args {$$=newAst("Args",3,$1,$2,$3);}
|Exp {$$=newAst("Args",1,$1);}
;
%%
/*
按照C-Tokens文件中要求定义
对终结符建立叶子结点,返回Token
*/
/*第一部分 头文件和变量*/
%{
#include
#include
#include "syntax_tree.h"
#include "syntax_tree.tab.h"
%}
/*flex属性,记录符号所在行号*/
%option yylineno
/*第二部分 定义正则表达式*/
/*十进制*/
INT_DEC 0|[1-9][0-9]*
/*十六进制*/
INT_HEX 0[xX][a-fA-F0-9]+
/*八进制*/
INT_OCT 0[1-7][0-7]*
/*二进制*/
INT_BIN 0[bB][01]+
/*INT类型汇总*/
INT {INT_HEX}|{INT_DEC}|{INT_OCT}|{INT_BIN}|{INT_HEX_ERROR}|{INT_OCT_ERROR}|{INT_BIN_ERROR}
/*浮点数-科学计数法*/
FLOAT ((([0-9]+\.[0-9]*)|([0-9]*\.[0-9]+)|INT)[Ee][-+]?[0-9]+)|({INT}\.[0-9])
/*词法分析输出错误,但是语法分析当做INT进行处理*/
/*十六进制错误*/
INT_HEX_ERROR 0[xX][a-fA-F0-9]*[g-zG-Z]+[a-fA-F0-9]*
/*八进制错误*/
INT_OCT_ERROR 0[0-7]*[89]+[0-7]*
/*二进制错误*/
INT_BIN_ERROR 0[bB][01]*[2-9]+[01]*
/*标识符*/
ID [a-z_A-Z][a-z_A-Z0-9]*
/*关键字*/
STRUCT struct
RETURN return
IF if
ELSE else
WHILE while
TYPE int|float
/*符号*/
SEMI ;
COMMA ,
ASSIGNOP =
PLUS \+
MINUS \-
STAR \*
DIV \/
AND &&
DOT \.
NOT \!
LP \(
RP \)
LB \[
RB \]
LC \{
RC \}
RELOP >|<|>=|<=|==|!=
/*注释*/
COMMENT ("//".*)|("/*"([*]*(([^*/])+([/])*)*)*"*/")
/*空白符*/
SPACE [ \f\r\t\v]+
/*换行*/
EOL \n
/*未定义字符*/
AERROR .
/*第三部分 操作 action 这里面的注释必须顶格一个空格*/
%%
/*跳过空白和注释*/
{SPACE} {}
{COMMENT} {}
{EOL} {}
/*关键字*/
{TYPE} {yylval.type_tnode=newAst("TYPE",0,yylineno);return TYPE;}
{STRUCT} {
yylval.type_tnode=newAst("STRUCT",0,yylineno);
// 结构体数加一
strucNum++;
// 开始扫描结构体定义内容
inStruc=1;
return STRUCT;}
{RETURN} {yylval.type_tnode=newAst("RETURN",0,yylineno); return RETURN;}
{IF} {yylval.type_tnode=newAst("IF",0,yylineno);return IF;}
{ELSE} {yylval.type_tnode=newAst("ELSE",0,yylineno); return ELSE;}
{WHILE} {yylval.type_tnode=newAst("WHILE",0,yylineno); return WHILE;}
/*数字类型错误*/
{INT_HEX_ERROR} {printf("INT_HEX_ERROR at line %d: charachters \"%s\"\n",yylineno,yytext);}
{INT_OCT_ERROR} {printf("INT_OCT_ERROR at line %d: charachters \"%s\"\n",yylineno,yytext);}
{INT_BIN_ERROR} {printf("INT_BIN_ERROR at line %d: charachters \"%s\"\n",yylineno,yytext);}
/*数字类型表示*/
{INT} {yylval.type_tnode=newAst("INT",0,yylineno); return INT;}
{FLOAT} {yylval.type_tnode=newAst("FLOAT",0,yylineno); return FLOAT;}
/*符号*/
{SEMI} {yylval.type_tnode=newAst("SEMI",0,yylineno); return SEMI;}
{COMMA} {yylval.type_tnode=newAst("COMMA",0,yylineno); return COMMA;}
{ASSIGNOP} {yylval.type_tnode=newAst("ASSIGNOP",0,yylineno); return ASSIGNOP;}
{PLUS} {yylval.type_tnode=newAst("PLUS",0,yylineno); return PLUS;}
{MINUS} {yylval.type_tnode=newAst("MINUS",0,yylineno); return MINUS;}
{STAR} {yylval.type_tnode=newAst("STAR",0,yylineno); return STAR;}
{DIV} {yylval.type_tnode=newAst("DIV",0,yylineno); return DIV;}
{AND} {yylval.type_tnode=newAst("AND",0,yylineno); return AND;}
{DOT} {yylval.type_tnode=newAst("DOT",0,yylineno); return DOT;}
{NOT} {yylval.type_tnode=newAst("NOT",0,yylineno); return NOT;}
{LP} {yylval.type_tnode=newAst("LP",0,yylineno); return LP;}
{RP} {yylval.type_tnode=newAst("RP",0,yylineno); return RP;}
{LB} {yylval.type_tnode=newAst("LB",0,yylineno); return LB;}
{RB} {yylval.type_tnode=newAst("RB",0,yylineno); return RB;}
{LC} {
yylval.type_tnode=newAst("LC",0,yylineno);
if(inStruc){
// 结构体定义内部存在LC左花括号
LCnum++;
}
return LC;}
{RC} {
yylval.type_tnode=newAst("RC",0,yylineno);
if(inStruc){
// 结构体定义内部存在RC右花括号
LCnum--;
}
return RC;}
{RELOP} {yylval.type_tnode=newAst("RELOP",0,yylineno); return RELOP;}
/*标识符*/
{ID} {yylval.type_tnode=newAst("ID",0,yylineno); return ID;}
/*错误*/
{AERROR} {
hasFault=1;
printf("Error type A at line %d: Mystirious charachter '%s'\n",yylineno,yytext);
}
%%
/*第四部分 函数 function*/
int yywrap()
{
/*此函数必须由用户提供,或者声明 %option noyywrap
当词法分析程序遇到文件结尾时,它调用例程yywrap()来找出下一步要做什么
如果返回0,扫描程序继续扫描,如果返回1,扫描程序就返回报告文件结尾*/
return 1;
}
#include
#include
#include
#include // 变长参数函数 头文件
/**********************语法分析**************************/
// 行数
extern int yylineno;
// 文本
extern char *yytext;
// 错误处理
void yyerror(char *msg);
// 抽象语法树
typedef struct treeNode
{
// 行数
int line;
// Token类型
char *name;
// 1变量 2函数 3常数 4数组 5结构体
int tag;
// 使用孩子数组表示法
struct treeNode *cld[10];
int ncld;
// 语义值
char *content;
// 数据类型 int 或 float
char *type;
// 变量的值
float value;
} * Ast, *tnode;
// 构造抽象语法树(节点)
Ast newAst(char *name, int num, ...);
// 先序遍历语法树
void Preorder(Ast ast, int level);
// 所有节点数量
int nodeNum;
// 存放所有节点
tnode nodeList[5000];
int nodeIsChild[5000];
// 设置节点打印状态
void setChildTag(tnode node);
// bison是否有词法语法错误
int hasFault;
/**********************语义分析**************************/
// 分析语法树,建立符号表
void analysis(tnode val);
// 变量符号表的结点
typedef struct var_
{
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
struct var_ *next;
}var;
var *varhead, *vartail;
// 建立变量符号
void newvar(int num,...);
// 变量是否已经定义
int findvar(tnode val);
// 变量类型
char* typevar(tnode val);
// 这样赋值号左边仅能出现ID、Exp LB Exp RB 以及 Exp DOT ID
int checkleft(tnode val);
// 函数符号表的结点
typedef struct func_
{
int tag; //0表示未定义,1表示定义
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
char *rtype; //声明返回值类型
int va_num; //记录函数形参个数
char *va_type[10];
struct func_ *next;
}func;
func *funchead,*functail;
// 记录函数实参
int va_num;
char* va_type[10];
void getdetype(tnode val);//定义的参数
void getretype(tnode val);//实际的参数
void getargs(tnode Args);//获取实参
int checkrtype(tnode ID,tnode Args);//检查形参与实参是否一致
// 建立函数符号
void newfunc(int num, ...);
// 函数是否已经定义
int findfunc(tnode val);
// 函数类型
char *typefunc(tnode val);
// 函数的形参个数
int numfunc(tnode val);
// 函数实际返回值类型
char *rtype[10];
int rnum;
void getrtype(tnode val);
// 数组符号表的结点
typedef struct array_
{
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
struct array_ *next;
}array;
array *arrayhead,*arraytail;
// 建立数组符号
void newarray(int num, ...);
// 查找数组是否已经定义
int findarray(tnode val);
// 数组类型
char *typearray(tnode val);
// 结构体符号表的结点
typedef struct struc_
{
char *name;
char *type;
// 是否为结构体域
int inStruc;
// 所属的结构体编号
int strucNum;
struct struc_ *next;
}struc;
struc *struchead, *structail;
// 建立结构体符号
void newstruc(int num, ...);
// 查找结构体是否已经定义
int findstruc(tnode val);
// 当前是结构体域
int inStruc;
// 判断结构体域,{ 和 }是否抵消
int LCnum;
// 当前是第几个结构体
int strucNum;
#include "syntax_tree.h"
// 用于遍历
int i;
Ast newAst(char *name, int num, ...)
{
// 生成父节点
tnode father = (tnode)malloc(sizeof(struct treeNode));
// 添加子节点
tnode temp = (tnode)malloc(sizeof(struct treeNode));
if (!father)
{
yyerror("create treenode error");
exit(0);
}
father->name = name;
// 参数列表,详见 stdarg.h 用法
va_list list;
// 初始化参数列表
va_start(list, num);
// 表示当前节点不是终结符号,还有子节点
if (num > 0)
{
father->ncld = num;
// 第一个孩子节点
temp = va_arg(list, tnode);
father->cld[0] = temp;
setChildTag(temp);
// 父节点行号为第一个孩子节点的行号
father->line = temp->line;
if (num == 1)
{
//父节点的语义值等于左孩子的语义值
father->content = temp->content;
father->tag = temp->tag;
}
else
{
for (i = 1; i < num; i++)
{
temp = va_arg(list, tnode);
(father->cld)[i] = temp;
// 该节点为其他节点的子节点
setChildTag(temp);
}
}
}
else //表示当前节点是终结符(叶节点)或者空的语法单元,此时num表示行号(空单元为-1)
{
father->ncld = 0;
father->line = va_arg(list, int);
// strcmp()==0 表示相同
if (!strcmp(name, "INT"))
{
father->type = "int";
father->value = atoi(yytext);
}
else if (!strcmp(name, "FLOAT"))
{
father->type = "float";
father->value = atof(yytext);
}
else
{
// 存储词法单元语义值
char *str;
str = (char *)malloc(sizeof(char) * 40);
strcpy(str, yytext);
father->content = str;
}
}
nodeList[nodeNum] = father;
nodeNum++;
return father;
}
// 父节点->左子节点->右子节点....
void Preorder(Ast ast, int level)
{
int i;
if (ast != NULL)
{
// 层级结构缩进
for (i = 0; i < level; ++i)
{
printf(" ");
}
if (ast->line != -1)
{
// 打印节点类型
printf("%s", ast->name);
// 根据不同类型打印节点数据
if ((!strcmp(ast->name, "ID")) || (!strcmp(ast->name, "TYPE")))
{
printf(": %s", ast->content);
}
else if (!strcmp(ast->name, "INT"))
{
printf(": %d", (int)ast->value);
}
else if (!strcmp(ast->name, "FLOAT"))
{
printf(": %f", ast->value);
}
else
{
// 非叶节点打印行号
printf("(%d)", ast->line);
}
}
printf("\n");
for (i = 0; i < ast->ncld; ++i)
{
Preorder((ast->cld)[i], level + 1);
}
}
else
{
return;
}
}
// 错误处理
void yyerror(char *msg)
{
hasFault = 1;
fprintf(stderr, "Error type B at Line %d: %s before %s\n", yylineno, msg, yytext);
}
// 设置节点打印状态 该节点为子节点
void setChildTag(tnode node)
{
int i;
for (i = 0; i < nodeNum; i++)
{
if (nodeList[i] == node)
{
nodeIsChild[i] = 1;
}
}
}
// 先序遍历分析
void analysis(Ast ast)
{
int i;
if (ast != NULL)
{
for (i = 0; i < ast->ncld; ++i)
{
analysis((ast->cld)[i]);
}
}
else
return;
}
// 建立变量符号
void newvar(int num, ...)
{
va_list valist;
va_start(valist, num);
var *res = (var *)malloc(sizeof(var));
tnode temp = (tnode)malloc(sizeof(tnode));
if (inStruc && LCnum)
{
// 是结构体的域
res->inStruc = 1;
res->strucNum = strucNum;
}
else
{
res->inStruc = 0;
res->strucNum = 0;
}
// 变量声明 int i
temp = va_arg(valist, tnode);
res->type = temp->content;
temp = va_arg(valist, tnode);
res->name = temp->content;
vartail->next = res;
vartail = res;
}
// 查找变量
int findvar(tnode val)
{
var *temp = (var *)malloc(sizeof(var *));
temp = varhead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
{
if (inStruc && LCnum) // 当前变量是结构体域
{
if (!temp->inStruc)
{
// 结构体域与变量重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else if (temp->inStruc && temp->strucNum != strucNum)
{
// 不同结构体中的域重名
printf("Error type 10 at Line %d:Struct Fields use the same name.\n", yylineno);
}
else
{
// 同一结构体中域名重复
return 1;
}
}
else // 当前变量是全局变量
{
if (temp->inStruc)
{
// 变量与结构体域重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else
{
// 变量与变量重名,即重复定义
return 1;
}
}
}
temp = temp->next;
}
return 0;
}
// 变量类型
char *typevar(tnode val)
{
var *temp = (var *)malloc(sizeof(var *));
temp = varhead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
return temp->type; //返回变量类型
temp = temp->next;
}
return NULL;
}
// 赋值号左边只能出现ID、Exp LB Exp RB 以及 Exp DOT ID
int checkleft(tnode val)
{
if (val->ncld == 1 && !strcmp((val->cld)[0]->name, "ID"))
return 1;
else if (val->ncld == 4 && !strcmp((val->cld)[0]->name, "Exp") && !strcmp((val->cld)[1]->name, "LB") && !strcmp((val->cld)[2]->name, "Exp") && !strcmp((val->cld)[3]->name, "RB"))
return 1;
else if (val->ncld == 3 && !strcmp((val->cld)[0]->name, "Exp") && !strcmp((val->cld)[1]->name, "DOT") && !strcmp((val->cld)[2]->name, "ID"))
return 1;
else
return 0;
}
// 创建函数符号
void newfunc(int num, ...)
{
int i;
va_list valist;
va_start(valist, num);
tnode temp = (tnode)malloc(sizeof(struct treeNode));
switch (num)
{
case 1:
if (inStruc && LCnum)
{
// 是结构体的域
functail->inStruc = 1;
functail->strucNum = strucNum;
}
else
{
functail->inStruc = 0;
functail->strucNum = 0;
}
//设置函数返回值类型
temp = va_arg(valist, tnode);
functail->rtype = temp->content;
functail->type = temp->type;
for (i = 0; i < rnum; i++)
{
if (rtype[i] == NULL || strcmp(rtype[i], functail->rtype))
printf("Error type 12 at Line %d:Func return type error.\n", yylineno);
}
functail->tag = 1; //标志为已定义
func *new = (func *)malloc(sizeof(func));
functail->next = new; //尾指针指向下一个空结点
functail = new;
break;
case 2:
//记录函数名
temp = va_arg(valist, tnode);
functail->name = temp->content;
//设置函数声明时的参数
temp = va_arg(valist, tnode);
functail->va_num = 0;
getdetype(temp);
break;
default:
break;
}
}
//定义的参数
void getdetype(tnode val)
{
int i;
if (val != NULL)
{
if (!strcmp(val->name, "ParamDec"))
{
functail->va_type[functail->va_num] = val->cld[0]->content;
functail->va_num++;
return;
}
for (i = 0; i < val->ncld; ++i)
{
getdetype((val->cld)[i]);
}
}
else
return;
}
//实际的参数
void getretype(tnode val)
{
int i;
if (val != NULL)
{
if (!strcmp(val->name, "Exp"))
{
va_type[va_num] = val->type;
va_num++;
return;
}
for (i = 0; i < val->ncld; ++i)
{
getretype((val->cld)[i]);
}
}
else
return;
}
//函数实际返回值类型
void getrtype(tnode val)
{
rtype[rnum] = val->type;
rnum++;
}
//检查形参与实参是否一致,没有错误返回0
int checkrtype(tnode ID, tnode Args)
{
int i;
va_num = 0;
getretype(Args);
func *temp = (func *)malloc(sizeof(func *));
temp = funchead->next;
while (temp != NULL && temp->name != NULL && temp->tag == 1)
{
if (!strcmp(temp->name, ID->content))
break;
temp = temp->next;
}
if (va_num != temp->va_num)
return 1;
for (i = 0; i < temp->va_num; i++)
{
if (temp->va_type[i] == NULL || va_type[i] == NULL || strcmp(temp->va_type[i], va_type[i]) != 0)
return 1;
}
return 0;
}
// 函数是否已经定义
int findfunc(tnode val)
{
func *temp = (func *)malloc(sizeof(func *));
temp = funchead->next;
while (temp != NULL && temp->name != NULL && temp->tag == 1)
{
if (!strcmp(temp->name, val->content))
{
if (inStruc && LCnum) // 当前变量是结构体域
{
if (!temp->inStruc)
{
// 结构体域与变量重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else if (temp->inStruc && temp->strucNum != strucNum)
{
// 不同结构体中的域重名
printf("Error type 10 at Line %d:Struct Fields use the same name.\n", yylineno);
}
else
{
// 同一结构体中域名重复
return 1;
}
}
else // 当前变量是全局变量
{
if (temp->inStruc)
{
// 变量与结构体域重名
printf("Error type 9 at Line %d:Struct Field and Variable use the same name.\n", yylineno);
}
else
{
// 变量与变量重名,即重复定义
return 1;
}
}
}
temp = temp->next;
}
return 0;
}
// 函数类型
char *typefunc(tnode val)
{
func *temp = (func *)malloc(sizeof(func *));
temp = funchead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
return temp->type; //返回函数类型
temp = temp->next;
}
return NULL;
}
// 形参个数
int numfunc(tnode val)
{
func *temp = (func *)malloc(sizeof(func *));
temp = funchead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
return temp->va_num; //返回形参个数
temp = temp->next;
}
}
// 创建数组符号表
void newarray(int num, ...)
{
va_list valist;
va_start(valist, num);
array *res = (array *)malloc(sizeof(array));
tnode temp = (tnode)malloc(sizeof(struct treeNode));
if (inStruc && LCnum)
{
// 是结构体的域
res->inStruc = 1;
res->strucNum = strucNum;
}
else
{
res->inStruc = 0;
res->strucNum = 0;
}
// int a[10]
temp = va_arg(valist, tnode);
res->type = temp->content;
temp = va_arg(valist, tnode);
res->name = temp->content;
arraytail->next = res;
arraytail = res;
}
// 数组是否已经定义
int findarray(tnode val)
{
array *temp = (array *)malloc(sizeof(array *));
temp = arrayhead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
return 1;
temp = temp->next;
}
return 0;
}
// 数组类型
char *typearray(tnode val)
{
array *temp = (array *)malloc(sizeof(array *));
temp = arrayhead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
return temp->type; //返回数组类型
temp = temp->next;
}
return NULL;
}
// 创建结构体符号表
void newstruc(int num, ...)
{
va_list valist;
va_start(valist, num);
struc *res = (struc *)malloc(sizeof(struc));
tnode temp = (tnode)malloc(sizeof(struct treeNode));
// struct name{}
temp = va_arg(valist, tnode);
res->name = temp->content;
structail->next = res;
structail = res;
}
// 结构体是否和结构体或变量的名字重复
int findstruc(tnode val)
{
struc *temp = (struc *)malloc(sizeof(struc *));
temp = struchead->next;
while (temp != NULL)
{
if (!strcmp(temp->name, val->content))
return 1;
temp = temp->next;
}
if (findvar(val) == 1)
return 1;
return 0;
}
// 主函数 扫描文件并且分析
// 为bison会自己调用yylex(),所以在main函数中不需要再调用它了
// bison使用yyparse()进行语法分析,所以需要我们在main函数中调用yyparse()和yyrestart()
int main(int argc, char **argv)
{
int j, tem;
if (argc < 2)
{
return 1;
}
for (i = 1; i < argc; i++)
{
// 初始化,用于记录结构体域
inStruc = 0;
LCnum = 0;
strucNum = 0;
// 初始化符号表
varhead = (var *)malloc(sizeof(var));
vartail = varhead;
funchead = (func *)malloc(sizeof(func));
functail = (func *)malloc(sizeof(func));
funchead->next = functail;
functail->va_num = 0;
arrayhead = (array *)malloc(sizeof(array));
arraytail = arrayhead;
struchead = (struc *)malloc(sizeof(struc));
structail = struchead;
rnum = 0;
// 初始化节点记录列表
nodeNum = 0;
memset(nodeList, 0, sizeof(tnode) * 5000);
memset(nodeIsChild, 0, sizeof(int) * 5000);
hasFault = 0;
FILE *f = fopen(argv[i], "r");
if (!f)
{
perror(argv[i]);
return 1;
}
yyrestart(f);
yyparse();
fclose(f);
// 遍历所有非子节点的节点
if (hasFault)
continue;
for (j = 0; j < nodeNum; j++)
{
if (nodeIsChild[j] != 1)
{
// Preorder(nodeList[j], 0);
}
}
}
}