1、编译技术课程设计报告1 / 35编译技术课程设计报告实验名称 编译器设计 姓名 学号 班级 编译技术课程设计报告2 / 35本课设的任务是完成一个完整的编译器,处理用户提交的符合所定文法的源程序代码,生成四元式中间代码,进而翻译成等价的 X86 平台上汇编语言的目标程序。编译程序的工作过程划分为下列 5 个过程:词法分析,语法分析,语义分析和中间代码生成,代码优化,目标代码生成。其中,词法分析阶段的基本任务是从以字符串表示的源程序中识别出具有独立意义的单词符号,并以二元组的形式输出,以作为语法分析阶段的输入。语法分析阶段的基本任务是将词法分析阶段产生的二元组作为输入,根据语言的语法规则,识别出
2、各种语法成分,并判断该单词符号序列是否是该语言的一个句子。语义分析的任务是首先对每种语法单位进行静态的语义审查,然后分析其含义,并用另一种语言形式 (本课设采用四元式) 来描述这种语义。代码优化的任务是对前阶段产生的中间代码进行等价变换或改造,以期获得更为高效即省时间和空间的目标代码。目标代码生成的任务是将中间代码变换成特定机器上的绝对指令代码或可重定位的指令代码或汇编指令代码(本课设生成汇编指令代码)。在词法分析阶段,通过 DOS 环境手动输入字符串序列(以 #作为结束标志)作为带分析的源程序,调用词法扫描子程序将字符串以二元组的形式输出(若有不属于该语言单词符号出现,则进行出错处理),词法
3、扫描子程序包括了对源程序的预处理(忽略多余空格、回车换行符等空白字符),以及对单词的识别和分类,以形成(单词种别,单词自身的值)形式的二元组,并将用户自定义变量信息存入程序变量信息表。在语法分析阶段,采用自上而下的递归下降分析法,从文法的开始符号出发,根据文法规则正向推导出给定句子。根据递归下降分析函数编写规则来编写相应的函数,在各个函数的分析过程中调用词法分析程序中的扫描程序,发出“取下一个单词符号”的命令,以取得下一个单词符号作语法分析。字符串表示的源程序词法分析器语法分析器字 符单 词 符 号取 一 下 个单 词 符 号在语义分析和中间代码生成阶段,采用语法制导翻译法,使用属性文法为工具
4、来描述程序设计语言的语义。首先审查词法分析得到的每个语法结构的静态语义,如果静编译技术课程设计报告3 / 35态语义正确再生成中间代码(本课设中采用四元式)。使用属性文法作为描述程序设计语言语义的工具,采用语法制导翻译法完成对语法成分的翻译工作,即在语法分析过程中,依随分析的过程,根据每个产生式所对应的语义子程序(或语义规则描述的语义处理的加工动作)进行翻译。目标代码生成是编译程序的最后一个阶段,根据符号表等信息,将中间代码转化为等价的目标代码。为减少访问计算机内存的次数,应尽可能把基本块内还要被引用的变量放到寄存器中,而把基本块内不用的变量所占的寄存器释放。为了随时掌握寄存器的使用情况和变量
5、的存放情况,以便生成适当地目标代码,可以建立寄存器描述表和变量地址描述表。在编译程序的各个阶段中都要涉及到表格管理和错误处理。编译程序在工作过程中需要建立一些表格,以登记源程序中所提供的或在编译过程中所产生的一些信息,编译各个阶段的工作都涉及到构造、查找、修改或存取有关表格中的信息(本课设中建立了程序变量信息表,变量地址描述表,寄存器描述表)。一个好的编译程序在编译过程中,应具有广泛的程序查错能力,并能准确地报告错误的种类及出错位置,以便用户查找和纠正,因此,在编译程序中还必须有一个出错处理程序。实验的整体设计思想可由以下图示表示:编译器基本模块设计编译技术课程设计报告4 / 35词法分析的任
6、务是对字符串表示的源程序从左到右地进行扫描和分解,根据语言 的词法规则识别出一个一个具有独立意义的单词符号,包括关键字,标识符,常数,运算0 21ll|d非 l非 d345 6 7 8 911110+d-d. ddee+-dd151218141131191171161= =非= =非=201= := *|/即 T F|T*F|T/FT F 编译技术课程设计报告7 / 35T.place=F.placeT T*F T.place=NewTemp();gen(“*“,T1.place,F.place,T.place)T T/FT.place=NewTemp();gen(“/“,T1.place,F
7、.place,T.place)2) := +|-即 E T|E+T|E+T同 1)3) :=, := |=|=|!=即 C E op E,op |=|=|!= C.etc=index;C.efc=index+1;gen(“goto “+op,E1.place1,E2.place2,“0“);gen(“goto“,“,“,“0“);4) :=ID=即 AS ID=Egen(“=“,E.place,“,ID);bp(AS.chain,index);5) :=ifelse 即 CS if C B |if C B else Bbp(C.etc,index);CS.Chain=C.efc;.(B()CS
8、.chain=merge(B1.chain,index);gen(“goto“,“,“,“0“);bp(C.efc,index);.(B()CS.chain=merge(B2.chain,CS.chain)6) :=do while 即 LS do B while CLS.head=index;.(B().(C()bp(C.etc,LS.head);编译技术课程设计报告8 / 35bp(C.efc,index);CS.chain=merge(B.chain,C.efc)bp(CS.chain,index);c) 目标代码生成采用汇编语言代码作为目标代码生成器的输出,在四元式序列中有 3 类量,
9、常量,程序变量和临时变量。一般地,常量对应立即数出现在目标指令中;程序变量是程序中用户自定义变量,通常是存放在存储单元中的存储器变量,在数据段定义为同名字变量(使用伪指令 DW);而临时变量则是在生成四元式时由编译程序引进的,因为寄存器变量的存取比存储变量的存取快得多,因此,为临时变量安排寄存器。1 寄存器描述表和地址描述表为了反映寄存器使用情况及变量值的存放情况,引进寄存器描述表 registerStatus与地址描述表 registerT。寄存器描述表动态反映了寄存器的使用状态,即寄存器是处于空闲状态还是被临时变量占用,以便分配寄存器给临时变量。由于本课设中程序变量存储在内存中,源代码单语
10、句内所有临时变量都为语句出口后的非活跃变量,因此,每执行完一条源代码语句,可视作寄存器中内容不再使用,描述符清零。地址描述表指明临时变量所在的寄存器,寄存器描述符为字符串数组,索引为临时变量编号,可能多个临时变量同存在一寄存器。2 目标指令与四元式编号对照表控制转移指令分两种:条件控制转移(“goto “+op,argv1,argv2,Lable)和无条件控制转移(“goto“,“,“,Lable)。往往在生成目标代码的时候还不了解控制转移到的目标指令的编号,因此需要回填。为此,引进目标指令与四元式编号对照表 lable,lablei反应了四元式 i 对应的若干目标指令中第一条的编号,在生成目
11、标代码时在第四分量中只填入四元式编号 i,待所有目标指令完全生成后再统一回填 lablei。3 寄存器分配函数寄存器的分配由函数 char* GetfreeR() 实现。为当前值不在寄存器的临时变量分配空闲寄存器。4 四元式对应目标代码根据四元式生成规则,1 中 argv2 为程序变量,2,3,4,5 中 res 为首次出现的临时变量Ti,6,7 中 label 为语句标号 Li。序号 四元式 目标代码 备注1 (=,argv1,“,argv2) (1)MOV R,argv1MOV argv2,R(2)MOV argv2,R(1)argv1 为现行值不在寄存器的临时变量,R 是新分配给argv
12、1 的寄存器(2)argv1 为立即数或现行值在寄存器 R 的临时变量2 (+,argv1,argv2,res) (1)MOV R,argv1ADD R,argv2(2)ADD R,argv2(1)argv1 现行值不在寄存器,R 是新分配给 argv1 的寄存器,对 res=Ti 置 registerTi=R(2)argv1 现行值在寄存器 R,对 res=Ti 置 registerTi=R3 (-,argv1,argv2,res) (1)MOV R,argv1 (1)argv1 现行值不在寄存器,编译技术课程设计报告9 / 35SUB R,argv2(2)SUB R,argv2R 是新分配
13、给 argv1 的寄存器,对 res=Ti 置 registerTi=R(2)argv1 现行值在寄存器 R,对 res=Ti 置 registerTi=R4 (*,argv1,argv2,res) (1)MOV R,argv1IMUL R,argv2(2)IMUL R,argv2(1)argv1 现行值不在寄存器,R 是新分配给 argv1 的寄存器,对 res=Ti 置 registerTi=R(2)argv1 现行值在寄存器 R,对 res=Ti 置 registerTi=R5 (/,argv1,argv2,res) (1)PUSH AX(2)PUSH DX(1)MOV AX,argv1
14、CWD(3)MOV R,argv2(3)IDIV R(4)IDIV argv2(2)POP DX(1)POP AX(1)argv1 现行值不在 AX 且 AX被占用(2)DX 被占用(3)argv2 为立即数(常量)(4)argv2 为临时变量(寄存器变量)或程序变量(存储器变量)对 res=Ti 置 registerTi=R6 (goto,“,“,label) JMP Label 对于 label=LiLabel=“L“+atoi(labeli)7 (goto op,argv1,argv2,label) (1)MOV R,argv1CMP R,argv2(2)CMP R,argv2(3)CM
15、P argv1,argv2JX Label(1)argv1 为立即数(常量)或argv1,argv2 同为存储器操作数(程序变量)(2)argv1 现行值在寄存器 R(Op,JX)=(=,JE)|(!=,JNE)|(=,JGE)|(,JG)|(#include #include #include #include #include /*/* 词法分析 */*/#define max 10char *rwtab9 = “main“,“int“,“float“,“double“,“char“,“if“,“else“,“do“,“while“;char prog100;/源程序int p;/当前处理
16、字符位置char ch; /当前处理字符int flag; /1表示刚读取一个变量或常数,“+/-“为运算符;0反之,“+/-“可能为数值符号编译技术课程设计报告16 / 35int syn; /种别编码char tokenmax; /保留字、内部字符串或操作符double sum; /数值char* variable;/变量信息表int nVar;/*/void scaner() int i;for(i=0;i:tokenm+=ch;ch=progp+;if(ch=)syn=33;tokenm+=ch;elsesyn=32;p-;break;case =:tokenm+=ch;ch=prog
17、p+;if(ch=)syn=36;tokenm+=ch;elsesyn=21;p-;break;case !:tokenm+=ch;ch=progp+;if(ch=)syn=37;tokenm+=ch;编译技术课程设计报告20 / 35elsesyn=-1;break;case *: syn=24; token0=ch; break;case /: syn=25; token0=ch; break;case (: syn=26; token0=ch; break;case ): syn=27; token0=ch; break;case : syn=28; token0=ch; break;c
18、ase : syn=29; token0=ch; break;case ,: syn=30; token0=ch; break;case ;: syn=31; token0=ch; break;case #: syn=0; token0=ch; break;default: syn=-1; printf(“illegal character %c/n“,ch); return ;/*/* 语法语义分析 */*/ 递归下降法 /void P (); /程序void B (int *nChain); /语句块void SS(int *nChain); /语句串void S (int *nChain
19、); /语句void AS(int *nChain); /赋值语句void CS(int *nChain); /条件语句void LS(int *nChain); /循环语句void C (int *etc,int *efc); /条件char * E (); /表达式char * T (); /项char * F (); /因子void error(); /出错处理/ 语法制导翻译 /typedef struct quaternionchar opmax;编译技术课程设计报告21 / 35char argv1max;char argv2max;char resmax;quad;/四元式qua
20、d *pQuad;/四元式组指针int index,nSuffix;/四元式编号, 临时变量编号/*/void gen(char *op,char *argv1,char *argv2,char *result);char *NewTemp();int merg(int p1,int p2);void bp(int p,int t);void printQuad();void Parse();/*/void error()if (syn=20) printf(“Syntax error before %g“,sum); else printf(“Syntax error before %g“,
21、token); syn=50;/ := main() void P()int nChain;scaner();if (syn = 1)scaner();if(syn = 26)scaner();if (syn = 27)scaner();B(else error(); else error();else error(); / := 编译技术课程设计报告22 / 35void B(int *nChain)if (syn=28)scaner();SS(nChain);if (syn = 29) scaner();else error(); else error();/:=;void SS(int
22、*nChain)S(nChain);if (syn=31) scaner();else error();while (syn != 29) S(nChain);if (syn=31) scaner();else error(); /:=|void S(int *nChain)if(syn=10) AS(nChain);else if(syn=6) CS(nChain);else if(syn=8) LS(nChain);else error();/:=ID=void AS(int *nChain)char stempmax;char *place;if (syn=10)strcpy(stemp
23、,token);scaner();编译技术课程设计报告23 / 35if (syn=21)scaner();place=E();gen(“=“,place,“,stemp);*nChain = 0; else error(); else error();bp(*nChain,index);/:=ifelse void CS(int *nChain)int nChaintmp,ntc,nfc;if (syn=6)scaner();C(bp(ntc,index);B(*nChain=merg(nChaintmp,nfc);if (syn=7)int nfc1;scaner();nfc1=index
24、; gen(“goto“,“,“,“0“);bp(*nChain,index);B(*nChain=merg(nChaintmp,nfc1);bp(*nChain,index);else*nChain=merg(nChaintmp,nfc);bp(*nChain,index); else error();编译技术课程设计报告24 / 35/:=do while void LS(int *nChain)int ntc,nfc;if (syn = 8)int nChaintmp;scaner();int indextmp=index;B(if (syn = 9)scaner();C(bp(ntc,
25、indextmp);bp(nfc,index);*nChain=merg(nChaintmp,nfc);bp(*nChain,index);else error();else error();/:=void C(int *etc,int *efc)char opmax,optmpmax,*place1,*place2; place1=E();if (syn31 char *place = (char *)malloc(max);place=place1=T(); while (syn = 22 |syn = 23)sprintf(op,“%s“,token);scaner();place2=T
26、();place=NewTemp();gen(op,place1,place2,place);place1=place;return place;/ := *|/char * T()char opmax,*place1,*place2;char *place ;place=place1=F(); while (syn = 24 |syn = 25) sprintf(op,“%s“,token);scaner(); place2=F();place=NewTemp();gen(op,place1,place2,place);place1=place;return place;/ :=ID|num
27、|()char * F()char *place = (char *)malloc(max);if (syn = 10) 编译技术课程设计报告26 / 35sprintf(place,“%s“,token);scaner();else if(syn = 20)sprintf(place,“%g“,sum); scaner();else if(syn = 26)scaner(); place=E(); if(syn = 27) scaner();else error();else error();return place;/*/生成四元式void gen(char *op,char *argv1
28、,char *argv2,char *result)sprintf(pQuadindex.op,“%s“,op);sprintf(pQuadindex.argv1,“%s“,argv1);sprintf(pQuadindex.argv2,“%s“,argv2);sprintf(pQuadindex.res,“%s“,result);index+;/产生临时变量char *NewTemp()char *tmpID = (char *)malloc(max);sprintf(tmpID,“T%d“,+nSuffix);return tmpID;/合并p1、p2int merg(int p1,int
29、 p2)int p,nRes;if(p2 = 0) nRes = p1;else 编译技术课程设计报告27 / 35p = p2;nRes = p2;while(atoi(pQuadp.res)p = atoi(pQuadp.res);sprintf(pQuadp.res,“%s“,p1);return nRes;/将t回填到p为首的四元式链void bp(int p,int t)int w,q = p;while (q)w=atoi(pQuadq.res);sprintf(pQuadq.res,“%d“,t);q=w;return;/打印四元式序列到文件,控制台输出void printQua
30、d()int n; FILE *fw = fopen(“quaternion.txt“,“w“); printf(“四元式序列如下: n“);for (n=1;nindex;n+)fprintf(fw,“n%2d: %7s,%5s,%5s,%5s“,n,pQuadn.op,pQuadn.argv1,pQuadn.argv2,pQuadn.res);printf(“n%2d: %7s,%5s,%5s,%5s“,n,pQuadn.op,pQuadn.argv1,pQuadn.argv2,pQuadn.res);fclose(fw);/*/语法分析、语义分析和中间代码生成主程序void Parse(
31、)int i;p=0;编译技术课程设计报告28 / 35flag = 0;/程序变量信息表variable = (char*)malloc(strlen(prog)*sizeof(char*); for (i=0;istrlen(prog);i+) variablei=(char *)malloc(max);nVar=0;/四元式序列pQuad = (quad *)malloc(strlen(prog)*sizeof(quad) ; index=1;/四元式临时变量编号nSuffix=0;P();if(syn = 0)printf(“Success!n“);printQuad();else p
32、rintf(“Fail!n“); /*/* 目标代码生成(汇编语言) */*/typedef struct assembly_commandchar Lablemax;char OPmax;char OPDmax;char OPSmax;assemb;/汇编指令assemb *pAssemb;/汇编指令序列指针int indexA;/汇编指令编号int* lable;/四元式编号所对应汇编指令编号char* registerT;/临时变量地址描述表char* registerName7=“AX“,“BX“,“CX“,“DX“,“BP“,“SI“,“DI“;/通用寄存器表int register
33、Status7;/通用寄存器描述表,0 代表未使用,1代表在使用/*/生成汇编指令void genA(char *OP,char *OPD,char *OPS,char *Lable)sprintf(pAssembindexA.OP,“%s“,OP);sprintf(pAssembindexA.OPD,“%s“,OPD);sprintf(pAssembindexA.OPS,“%s“,OPS);sprintf(pAssembindexA.Lable,“%s“,Lable);编译技术课程设计报告29 / 35indexA+;/分配空闲寄存器char* GetfreeR()int i;char *r
34、eg = (char *)malloc(max);for (i=0;i7;i+) if(registerStatusi=0)sprintf(reg,“%s“,registerNamei);registerStatusi=1;return reg;reg=“Full“;return reg;/参数为临时变量,为未分配寄存器的临时变量分配寄存器,返回其所在寄存器/参数为立即数或程序变量,则返回本身char* Place(char *var)if (var0=T)char *place = (char *)malloc(max);if (registerTatoi(var+1) = NULL)pla
35、ce = GetfreeR();registerTatoi(var+1) = place; elseplace = registerTatoi(var+1);return place;else return var;/回填汇编语句中所有转移指令中标号label编译技术课程设计报告30 / 35void bpAll()int i;int nLable=0;int tmp;for (i=1;iindexA;i+)if (pAssembi.OP0=J) tmp=lableatoi(pAssembi.OPD);sprintf(pAssembtmp.Lable,“L%d:“,+nLable);sprin
36、tf(pAssembi.OPD,“L%d“,nLable); /汇编语言文件生成、控制台输出void printAssemb()int n;FILE *fw = fopen(“Assembly.asm“,“w“); /汇编伪指令fprintf(fw,“.386“);fprintf(fw,“nDATAtSEGMENT USE16“);for (n=1;n=nVar;n+) fprintf(fw,“n%stDW 0“,variablen);fprintf(fw,“nDATAtENDS“);fprintf(fw,“nSTACKtSEGMENT USE16 STACK“);fprintf(fw,“nt
37、DB 200 DUP(0)“);fprintf(fw,“nSTACKtENDS“);fprintf(fw,“nCODEtSEGMENT USE16“);fprintf(fw,“ntASSUME DS:DATA,SS:STACK,CS:CODE“);/汇编指令fprintf(fw,“nSTART:tMOVtAX,tDATA“);fprintf(fw,“ntMOVtDS,tAX“); printf(“nn汇编指令序列如下: n“);for (n=1;nindexA;n+)if (!strcmp(pAssembn.OPS,“)fprintf(fw,“n%st%st%s“,pAssembn.Lable,pAssembn.OP,pAssembn.OPD);printf(“n%2d:%5s %5s %5s“,n,pAssembn.Lable,pAssembn.OP,pAssembn.OPD);