云南大学编译技术实验报告
一、实验目的
1. 进一步理解递归下降分析原理和实现方法;
2. 理解语法、语义分析程序为中心的单遍编译程序组织方法;
3. 理解语义分析的基本机制;
4. 掌握语义子程序的构造方法;
5. 理解编译程序的基本逻辑过程(词法分析、语法分析、语义分析及目标代码的生成;
6. 理解编译过程中的符号表的基本管理方法;
二、实验内容
文法G[E]如下:
E→E+T | E-T |T
T→T*F | T/F | F
F→( E )
F→ id
注 id 为字母打头后面是数字或者字母的串;
在starter files基础上
1. 实现增加乘法 * 和除法 / 运算,并且,/优先级别高于+;其次,和/满足左结合规则;
2. 对包含乘除法运算的表达式生成对应的四元式;
3. 禁止同名重复声明,所以登记符号之前要检查有没有同名变量声明过;
4. 增加类型检查;
三、实验的分析与设计
1.经过修改后的翻译模式:
start-> DS.
D->B; D
D->ε
B->int L { L.type := int }|real L { L.type := real }
L->id { A.Type := L.type enter(v.entry,L.type)} A
A-> ,idA { A1.Type := A.type enter(v.entry,A.type)}
A->ε
S→ V := E { gen( “:=”, E.place,0,V.place) } H
H→;S | ε
E->T { R.i:=T.place} R {E.place:=R.s}
R->+T { R1.i:= newtemp; gen( “*”, R.i, T.place , R1.i) } R {R.s:= R1.s; }
R-> ε {Rs=R.i}
T→F {p.i:=F.place} P {T.place:=P.s}
P->F {p1.i:=newtemp;gen(““,P.i,T.place,P.i)} P {P.s:=P1.s;}
P->/F {p1.i:=newtemp;gen(“/”,P.i,T.place,P.i)} P {P.s:=P1.s;}
P-> ε {Ps=P.i}
F->( E ) { F.place := E.place}
F->id {F.place:=position (id)}
V->id {V.place:=position(id)}
type、i属性为继承属性,place、s属性为综合属性。
- 实现增加乘法 * 和除法 / 运算,并且,/优先级别高于+;其次,和/满足左结合规则;
- 对包含乘除法运算的表达式生成对应的四元式;
E->T { R.i:=T.place} R {E.place:=R.s}
R->+T { R1.i:= newtemp; gen( “*”, R.i, T.place , R1.i) } R {R.s:= R1.s; }
R-> ε {Rs=R.i}
T→F {p.i:=F.place} P {T.place:=P.s}
P->F {p1.i:=newtemp;gen(““,P.i,T.place,P.i)} P {P.s:=P1.s;}
P->/F {p1.i:=newtemp;gen(“/”,P.i,T.place,P.i)} P {P.s:=P1.s;}
P-> ε {Ps=P.i}
3. 禁止同名重复声明,所以登记符号之前要检查有没有同名变量声明过;
登记符号之前要检查有没有同名变量声明过。
在enter( )这个存放声明变量的函数中,增加相应检验变量名重复的函数:
- 增加类型检查;
检验’:=’前后字符类型是否匹配
检验’+’前后类型是否匹配
检验’*’前后的类型是否匹配
检验’/’前后的类型是否匹配
四、实验的实现
五、运行的结果以及存在问题及其原因
#include <stdio.h>
#include<dos.h>
#include<stdlib.h>
#include<string.h>
//#include <iostream.h>
#include<ctype.h>
#include <windows.h>
#define txmax 100 /* 符号表最大容量 */
#define al 10 /* 符号的最大长度 */
#define tvmax 500 /* 最多能够申请的临时变量取值范围[100, tvmax] */
#define norw 2 /* 关键字个数 */
#define cxmax 500 /* 最多的虚拟机代码数 */
int tx; //名字表指针, 取值范围[0, txmax-1]
int tv ; //临时变量计数
/* 符号 */
enum symbol {
nul, eeof, ident, plus, times, lparen,divide,
rparen, comma, semicolon, becomes, period, realsym, intsym,
};
enum symbol sym; /* 当前的符号 */
char ch; /* 获取字符的缓冲区,getch 使用 */
char id[al+1]; /* 当前ident, 多出的一个字节用于存放0 */
char a[al+1]; /* 临时符号, 多出的一个字节用于存放0 */
/* 符号表结构 */
struct tablestruct
{
char name[al]; /* 名字 */
enum symbol type; // 类型
};
struct tablestruct table[txmax]; /* 符号表 */
char word[norw][al]; /* 保留字 */
enum symbol wsym[norw]; /* 保留字对应的符号值 */
enum symbol ssym[256]; /* 单字符的符号值,散列表 */
int cx; /* 四元式代码指针, 取值范围[0, cxmax-1]*/
struct instruction
{
char f[al+1]; /* 操作码 */
char l[al+1]; /* 左操作数 */
char r[al+1]; /* 右操作数*/
char t[al+1]; /* 结果 */
};
struct instruction code[cxmax]; /* 存放虚拟机代码的数组 */
FILE* fin;
FILE* fout;
int getsym();
void enter(enum symbol type);
void init();
int position(char* idt);
int gen(enum symbol op, int arg1, int arg2,int result ); //中间代码分析
void writecode(char *op, char *arg1, char *arg2,char *result ); //写缓存
void start();
void D();
void B();
void L(enum symbol type);
void A(enum symbol type);
void S();
void H();
int E();
int R(int Ri);
int T();
int P(int Pi);
int F();
int V();
#include "语义分析.h"
int main()
{
char filename[20];
printf("请输入分析的文件名:");
gets(filename);
if((fin=fopen(filename,"r"))==NULL)
{
printf("不能打开文件.\n");
exit(0);
}
init(); //初始化
getsym(); //读第一个单词,将单词类别放入sym中,单词值放入id中
start(); //开始按start->DS. 分析
if (sym==eeof)
{
printf("语法正确\n\n将中间代码保存到文件请输入文件名,否则回车");
gets(filename);
if(strlen(filename)<=0) return 0;
if((fout=fopen(filename,"w"))==NULL)
{
printf("不能打开文件.\n");
exit(0);
}
for (int cx1=0; cx1<cx; cx1++)
fprintf(fout,"(%s,%s,%s,%s)\n",code[cx1].f,code[cx1].l,code[cx1].r,code[cx1].t);
return 0;
}
else
{
printf("语法错1: . 后不能再有句子");
exit(0);
}
fclose(fin);
fclose(fout);
return 0;
}
//初始化函数
void init()
{
int i;
/* 设置单字符符号 */
for (i=0; i<=255; i++)
{
ssym[i] = nul; //不正确的单字符符号为nul,先预置初值nul
}
ssym['+'] = plus;
ssym['*'] = times;
ssym['('] = lparen;
ssym[')'] = rparen;
ssym['.'] = period;
ssym[','] = comma;
ssym[';'] = semicolon;
ssym['/']=divide;
/* 设置保留字名字 */
strcpy(&(word[0][0]), "real");
strcpy(&(word[1][0]), "int");
/* 设置保留字符号 */
wsym[0] = realsym;
wsym[1] = intsym;
tv=100; //临时变量指针初值,让Tx和tv的取值没有交集,区别到底是临时变变量和声明的变量
tx=0; //表指针初值
cx=0; //指令计数器
}
/*
* 词法分析,获取一个符号
*/
int getsym()
{
int i,k;
ch=fgetc(fin);
if (ch==EOF)
{
sym=eeof; //文件结束
return 0;
}
while (ch==' ' || ch==10 || ch==13 || ch==9) /* 忽略空格、换行、回车和TAB */
ch=fgetc(fin);
if (ch>='a' && ch<='z')
{
/* 名字或保留字以a..z开头 ,*/
k = 0;
do
{
if(k<al) /* 符号的最大长度为al ,超长就截尾处理*/
{
a[k] = ch;
k++;
}
ch=fgetc(fin);
}
while ((ch>='a' && ch<='z' )|| (ch>='0' && ch<='9'));
a[k] = 0;
strcpy(id, a);//a的地址赋予id
fseek(fin,-1,1);//文件指针偏移,向后读一个
for (i=0; i<norw; i++) /* 搜索当前符号是否为保留字 */
if (strcmp(id,word[i]) == 0)//如果是
break;
if (i <norw)//已经经过定义,不可作为变量和过程名
{
sym = wsym[i];
}
else
{
sym = ident; /* 搜索失败则,类型是标识符 */
}
}
else if(ch == ':') /* 检测赋值符号 */
{
ch=fgetc(fin);
if (ch == '=')
{
sym = becomes;
}
else
{
sym = nul; /* 不能识别的符号 */
}
}
else sym = ssym[ch]; /* 当符号不满足上述条件时,全部按照单字符符号处理 */
return 0;
}
/*
* 在符号表中加入一项
*/
void enter(enum symbol type)//将声明过的变量保存在结构体table[tx]中
{
tx=tx+1;
if (tx > txmax)
{
printf(" 符号表越界 "); /* 符号表越界 */
return;
}
for(int i=1; i<txmax; i++)
{
if(strcmp(id,table[i].name)==0)
{
printf("错误1:变量名重复定义\n");
exit(0);
}
}
strcpy(table[tx].name, id); /* 全局变量id中已存有当前名字的名字,Tx为插入当前符号之前表尾指针 */
table[tx].type = type;
}
/*
* 查找名字的位置.
* 找到则返回在名字表中的位置,否则返回0.
*
* idt: 要查找的名字
* tx: 当前名字表尾指针,全局变量
*/
/*判断是否已经存在*/
int position(char* idt)
{
int i;
strcpy(table[0].name, idt);
i = tx;
while (strcmp(table[i].name, idt) != 0)
{
i--;
}
return i;
}
/* 中间代码,生成四元式,目标代码*/
int gen(enum symbol op, int arg1, int arg2,int result )//通过传来的参数产生四元式
{
char temp1[al+1],temp2[al+1],temp3[al+1];
if(arg1>=100) //模拟申请临时变量
{
wsprintf(temp1,"T%d",arg1);//temp1是一个缓冲区,将arg1内容输出到temp1中
}
else
{
strcpy(temp1, table[arg1].name);//把table[arg1].name的内容copy到temp1中
}
if(arg2>=100)
{
wsprintf(temp2,"T%d",arg2);
}
else
{
strcpy(temp2, table[arg2].name);
}
if(result>=100)
{
wsprintf(temp3,"T%d",result);
}
else
{
strcpy(temp3,table[result].name);
}
if (op==becomes)//赋值符
{
if(table[arg1].type!=table[result].type) //检验':='前后字符类型是否匹配
{
printf("(:=,%s,%s,%s)\n",temp1,temp2,temp3);//输出四元式
writecode(":=",temp1,temp2,temp3);//写入缓存
printf("语法错:%s和%s的类型不一致\n",temp1,temp3);//':='前后字符类型不匹配,报错
}
else //:=前后字符类型匹配
{
printf("(:=,%s,%s,%s)\n",temp1,temp2,temp3);
writecode(":=",temp1,temp2,temp3);//写入缓存
}
}
else if (op==plus) //+运算
{
if(table[arg1].type==table[arg2].type)//检验'+'前后类型是否匹配
{
table[result].type=table[arg1].type;
writecode("+",temp1,temp2,temp3);
printf("(+,%s,%s,%s)\n",temp1,temp2,temp3);
}
else
{
printf("(+,%s,%s,%s)\n",temp1,temp2,temp3);
writecode("+",temp1,temp2,temp3);
printf("语法错:%s和%s的类型不一致\n",temp1,temp2);
}
}
/*乘法*/
else if(op==times)
{
if(table[arg1].type==table[arg2].type)//检验'*'前后的类型是否匹配
{
table[result].type=table[arg1].type;
printf("(*,%s,%s,%s)\n",temp1,temp2,temp3);
writecode("*",temp1,temp2,temp3);
}
else
{
printf("(*,%s,%s,%s)\n",temp1,temp2,temp3);
writecode("*",temp1,temp2,temp3);
printf("语法错:%s和%s的类型不一致\n",temp1,temp2);
}
}
/*除法*/
else if(op==divide)
{
if(table[arg1].type==table[arg2].type)//检验'/'前后的类型是否匹配
{
table[result].type=table[arg1].type;
printf("(/,%s,%s,%s)\n",temp1,temp2,temp3);
writecode("/",temp1,temp2,temp3);
}
else
{
printf("(/,%s,%s,%s)\n",temp1,temp2,temp3);
writecode("/",temp1,temp2,temp3);
printf("语法错:%s和%s的类型不一致\n",temp1,temp2);
}
}
return 0;
}
//处理代码段,打印
void writecode(char op[al+1], char arg1[al+1], char arg2[al+1],char result[al+1] )
{
if (cx >= cxmax)
{
printf("Program too long"); /* 程序过长 */
return ;
}
strcpy(code[cx].f, op);
strcpy(code[cx].l,arg1);
strcpy(code[cx].r,arg2);
strcpy(code[cx].t,result);
cx++;
return ;
}
/*分析产生式 X->DS. */
void start()
{
if (sym==intsym||sym==realsym)//如果变量是int或者real型
{
D();
S();
if (sym==period)
{
getsym();//调用词法分析
return;
}
else
{
printf("语法错2: 缺少程序结束.");
exit(0);
}
}
else
{
printf("语法错3: 程序只能用int,和real开始,而且区分大小写");
exit(0);
}
}
/*递归下降分析
D-> B; D
D->ε
*/
void D()
{
if (sym==intsym ||sym==realsym)
{
B();
if (ch=';')
{
getsym();
D();
}
else
{
printf("语法错4");
exit(0);
}
}
else if(sym==ident) return;
else
{
printf("语法错5");
exit(0);
}
}
/*
B-> int L { L.type := int }|real L { L.type := real }
*/
void B()
{
if (sym==intsym )//继承L的类型
{
getsym();
L(intsym);
}
else if (sym==realsym)
{
getsym();
L(realsym);
}
else
{
printf("语法错6:变量不是real或int型\n");
exit(0);
}
}
/*
L-> id { A.Type := L.type enter(v.entry,L.type)} A V.entry通过全局变量tx隐性传递
有参,有几个继承属性就有几个参数
*/
void L(enum symbol type)//L的id赋予A
{
int Vplace;
if (sym==ident)
{
Vplace=position(id);
if(Vplace>0)
{
printf("语法错7:变量重复声明\n");
exit(0);
}
enter(type);
getsym();
A(type);
}
else
{
printf("语法错8:缺少变量");
exit(0);
}
}
/*
A-> ,id A { A1.Type := A.type enter(v.entry,A.type)}
A->ε
*/
void A(enum symbol type)
{
int Vplace;
if (sym==comma) //当前单词为,
{
getsym();
if (sym==ident)
{
Vplace=position(id);
if(Vplace>0)
{
printf("语法错7:变量重复声明\n");
exit(0);
}
enter(type);
getsym();
A(type);
}
else
{
printf("语法错9");
exit(0);
}
}
else if (sym== semicolon) return ;//当前单词为;即A的follow集元素相当于进行A->ε
else
{
printf("语法错10");
exit(0);
}
}
/*
S→ V := E { gen( ":=", E.place,0,V.place) } H
*/
void S()
{
int vplace,Eplace;
if (sym==ident)
{
vplace=V();
//getsym();
if (sym==becomes) //当前单词为:=
{
getsym();
Eplace=E();
gen(becomes,Eplace,-1,vplace);
H();
}
else
{
printf("语法错11");
exit(0);
}
}
else
{
printf("语法错12");
exit(0);
}
}
/*
H→;S | ε
*/
void H()//执行下一条运算
{
if (sym==semicolon) //当前单词为indent类型
{
getsym();
S();
}
else if (sym==period) return ;
else
{
printf("语法错13");
exit(0);
}
}
/*
E->T { R.i:=T.place} R {E.place:=R.s}
综合属性,有返回值
*/
int E()//分析赋值号后面的运算
{
int ri,tplace,Rs;
if (sym==ident || sym== lparen)//当前为标识符或左括号
{
tplace=T();
ri=tplace;
Rs=R(ri);
}
else
{
printf("语法错14");
exit(0);
}
return Rs;
}
/*
R->+T { R1.i:= newtemp; gen( "*", R.i, T.place , R1.i) } R {R.s:= R1.s; }
R-> ε {R.s=R.i}
R既有参数又有返回值
*/
int R(int Ri)//检验+运算
{
int Rs,tplace;
if (sym==plus)
{
getsym();
tplace=T();
tv=tv+1; //生成临时变量
gen(plus,Ri,tplace,tv);
Rs=R(tv);
}
else if (sym== semicolon || sym==rparen|| sym==period)
{
Rs=Ri;
}
else
{
printf("语法错15");
exit(0);
}
return Rs;
}
/*实现乘除的左结合及优先级
F->( E ) { F.place := E.place}
F->id {F.place:=position (id)}
*/
int F()//实现*、/运算的优先级高于+运算
{
int Fplace;
if(sym==ident)
{
Fplace=position(id);//获取当前位置
if(Fplace==0)
{
printf("变量没有声明\n");
exit(0);
}
getsym();
}
else if(sym==lparen)
{
getsym();
Fplace=E();
if(sym==rparen)getsym();
else
{
printf("语法错16,缺)");
exit(0);
}
}
else
{
printf("语法错17,,缺(");
exit(0);
}
return Fplace;
}
/*
P->*F {p1.i:=newtemp;gen("*",P.i,T.place,P.i)} P {P.s:=P1.s;}
P->/F {p1.i:=newtemp;gen("/",P.i,T.place,P.i)} P {P.s:=P1.s;}
P-> ε {Ps=P.i}
*/
int P(int Pi)//检验*、/运算
{
int ps,Fplace;
if(sym==times)
{
getsym();
Fplace=F();
tv=tv+1;//生成临时变量
gen(times,Pi,Fplace,tv);
ps=P(tv);
}
else if(sym==divide)
{
getsym();
Fplace=F();
tv=tv+1;
gen(divide,Pi,Fplace,tv);
ps=P(tv);
}
else if(sym==plus||sym==semicolon||sym==rparen||sym==period)
//求出P的follow集,进行语法检查,如判断左括号是否匹配
{
ps=Pi;
}
else
{
printf("语法错18:缺少变量或(");
exit(0);
}
return ps;
}
/*
将T的位置指向新增加乘除
T→F {p.i:=F.place} P {T.place:=P.s}
*/
int T()
{
int Pi,Fplace,Ps;
if(sym==ident||sym==lparen)
{
Fplace=F();
Pi=Fplace;
Ps=P(Pi);
}
else
{
printf("语法错19");
exit(0);
}
return Ps;
}
/*
V->id {V.place:=position(id)}
*/
int V()
{
int Vplace;
if (sym==ident)
{
Vplace=position (id);
if (Vplace==0)
{
printf("变量没有声明");
exit(0);
}
getsym();
}
else
{
printf("语法错20");
exit(0);
}
return Vplace;
}