基于扩展C0文法的编译器设计(Part2)

2.类/方法/函数功能

词法分析部分:

函数名功能
Int isSpace()等 判断相关字符种类
void getch() 读取一个字符
void getsym() 读取一个标识符(词)
###***语法分析和语义分析***
函数名功能
void declHead()声明头部子程序
void constDef()常量定义子程序
void constDecl()常量声明子程序
void varDef()变量定义子程序
void varDecl()变量声明子程序
void compoundSta()复合语句子程序
void mianFunc()主函数子程序
void expr()表达式子程序
void item()项子程序
void factor()因子子程序
void state()语句子程序
void conditionSta()条件语句子程序
void codition()条件
void loopSta()循环语句子程序
void valueOfPara()值参数子程序
void paraTable参数子程序
void states()语句列子程序
void readSta()读语句子程序
void writeSta()写语句子程序
void returnSta()返回语句子程序
void funcWithRetVlaDef()有返回值函数定义
void funcWithNotRetValDef()无返回值函数定义
void program()程序子程序
void init()变量初始化函数
void stt()Debug输出函数
void insertSymTab(char* name,int type, int value, int address, int para) 符号表插入函数
void insertPara(int para) 参数值修改函数
int searchSymTab(char* name, int flag) 符号表检索函数
int arrLength(char* name) 数组长度检索函数

中间代码生成:

函数名 功能
void genMidCode(char* op,char* a, int b, char* result)生成四元式
void genMidCode(char* op,char* a, char* b, char* result)生成四元式
char* int2array(int a)整型转换为字符型
char* genLab()生成新标签
char* genVar()生成新变量名
int isNumber(char*a, int len)判断一个数组是否代表一个数

目标程序生成:

由于笔者选择mips作为最终的目标代码,因此对文法分析后判断需要实现的mips指令表如下:

指令名 指令格式指令意义
系统调用 syscall 根据c0参数不同功能不同
取立即数 li s1,100 s1 = 100
取地址 la $t1, array1 $t1 = array1的首地址
add s1,s2,s3 s1 = s2 + s3
sub s1,s2,s3 s1 = s2 – s3
mult s1,s2 hi,lo = s1*s2
div s1,s2 lo = s1/s2, hi = s1mod s2
立即数加 addi s1,s2,100 s1 = s2 + 100
立即数减 subi s1,s2,100 s1 = s2 - 100
mfhi mfhi a a = [hi]
mflo mflo a a = [lo]
movemove s1 s2 s1 = s2
取字lw $t2,4($t1) $t2 = [$t1+1]
存字sw $t2,4($t1) [$t1+1] = $t2
相等跳转beq s1,s2,label if(s1==s2) goto label
不等跳转bne s1,s2,label if(s1!=s2) goto label
小于跳转blt s1,s2,labelif(s1 小于 s2 ) goto label
大于跳转 bgt s1,s2,label if(s1>s2) goto label
小于等于跳转 ble s1,s2,label if(s1不大于s2) goto label
大于等于跳转 bge s1,s2,label if(s1>=s2) goto label
j j label goto label
jr jr $ra goto [$ra]
jal jal label goto and link label
由上表可以得到一部分汇编相关的函数:
函数名 功能
int findaddr(char* name) 寻找变量在运行栈中的位置
void insertaddr(char* name) 在运行栈中插入变量位置
void globalcondef() 全局常量定义生成
void globalvardef() 全局变量定义生成
void initasm() 预处理整个目标代码
int isNum() 判断是否是数字字符
void func_mips() 函数目标吗生成
void myend_mips() 用于数组结束判断
void ret_mips() 返回目标码生成
void ints_mips() 整型变量目标码生成
void chars_mips() 字符型变量目标码生成
void inta_mips() 整型数组目标码生成
void chara_mips() 字符型数组目标码生成
void add_mips() 加指令目标码生成
void sub_mips() 减指令目标码生成
void mul_mips() 乘指令目标码生成
void div_mips() 除指令目标码生成
void prtf_mips() 输出指令目标码生成
void scf_mips() 读指令目标码生成
void lab_mips() 标签目标码生成
void paraop_mips() 函数指参传递指令目标码生成
void calpara_mips() 函数调用参数目标码生成
void call_mips() 函数调用指令目标码生成
void jmp_mips() 跳转指令目标码生成
void jne_mips() 跳转目的指令生成
void ass_mipa() 普通赋值语句目标码生成
void bt_mips() 大于指令目标码生成
void st_mips() 小于指令目标码生成
void eql_mips() 等于指令目标码生成
void neq_mips() 不等于指令目标码生成
void nbt_mips() 不大于指令目标码生成
void nst_mips() 不小于指令目标码生成
void assignA_mips() 赋值数组指令目标码生成
void aAssign_mips() 数组赋值指令目标码生成
void exit_mips() 退出指令目标码生成
void run() 运行函数

错误处理:

函数名功能
error(int errorcode, itn indexInFile) 打印错误信息

优化部分:

函数名功能
void combine() 常数合并
void replace(char* result, char*a) 对固定的字符串做替换

3.调用依赖关系

这里写图片描述

4.符号表管理

为了便于实现,这里采用无序符号表的组织方式。
具体的符号表数据结构如下:
这里写图片描述

5.存储分配方案

原计划寄存器分配采用启发式图着色算法。采用该方法的主要目的是为了避免因引用计数法带来的全局寄存器前中后期分配不均问题。具体实现时,采用了固定寄存器分配(只使用t1,t2两个临时寄存器,随用随取)。
运行栈设计采用课本介绍的经典动态存储分配方式,图示如下:
这里写图片描述

6. 解释执行程序

本程序不采用解释执行程序,而是编译执行。

7. 四元式设计

对于整个文法而言,可能出现的有关数据的操作和对应的四元式设计如下表:

操作种类 四元式设计
int a; int,,,a
char b; char,,,b
int a[10]; int,,10,a
char b[10] char,,10,b
const int a = 5; const,int,5,a
const char b = 6; const,char,6,b
a = b; =,b,,a
a[i] = b; []=,b,i,a
a = b[i]; aassi,b,i,a//array assign
a = f();//有返回值函数调用call,f,,a
f();//无返回值函数调用 call,f,,
a 不大于 b 不大于,,a,b
a 小于 b 小于,,a,b
a >= b >=,,a,b
a > b >,,a,b
a == b ==,,a,b
a != b !=,,a,b
if(!true) jne,,,label
if(true) jmp,,,label
set label lab:,,,label
return ; ret,,,
return a; ret,,,a
scanf(a); scf,,,a
printf(“a”,b) prtf,a,b,sym
int f() func,int,,f
char f() func,char,,f
f(int a,…) para,int,,a

8. 目标代码生成方案

目标代码生成的主要任务包括:从中间代码到目标代码的转换过程中进行指令选择,目标代码地址空间的划分,目标体系结构上寄存器和内存单元的分配和指派,目标代码序列的生成等。在这一过程中,主要考虑:

1. 如何为中间代码选择语义等价且运行效率高的目标指令;
2. 为目标代码、局部变量、全局变量、静态变量、临时变量、常量等指定和划分地址空间;
3. 由于源程序和中间代码中的变量和数据结构的显示定义都将消失,取而代之的是具体的寄存器编号和内存空间地址等,因此需要准确合理的将这些寄存器和内存单元分配给不同类型和结构的变量;
4. 生成和输出目标代码序列到文件。在这一过程中,目标代码的序列和输入的中间代码序列在顺序上可以一致,也可以不一致,在保证语义不变的前提下,尽量选择更高执行效率或者更小存储空间的代码序列。

9. 优化方案

1.窥孔优化(未实现)
在采用中间代码到目标代码逐条生成的过程中,目标代码中经常会出现代码的冗余或者低效率的指令,为了消除这种指令,我们采用窥孔优化的方式处理。窥孔优化关注在目标指令的一个较短序列上,通过删除冗余代码或者替换更高效和简洁的代码来使得最终得到的目标代码是高质量的。
2.常数合并
如果在编译的时候,对于某些表达式的部分或者全部操作数的值,则此表达式就会被优化,将已知值的操作数进行可以进行的运算后再与变量进行运算。
3.常量替换
在编译之初就将所有常量替换为值,不在对常量进行取址等操作。
4.消除公共子表达式(未实现)

10. 出错处理

错误处理方案:

#define FILEOPEN_ERROR -1 //file open failed
#define ZEROSTART_ERROR 0 //
#define UNDEFFUNC_ERROR 1
#define UNDEFCHAR_ERROR 2 //undefined IDEN
#define UNMATCHSQ_ERROR 3 //unmatched singal quoto
#define UNMATCHDQ_ERROR 4 // unmatched double quoto
#define OUTOFTABINDEXX_ERROR 5 // out of table max length
#define FUNCNAMECOMPLICT_ERROR 6 // function name conplict
#define VARNAMECOMPLICT_ERROR 7 //variable name complict
#define NAMECOMPLICTGLOBAL_ERROR 8 //name can not complict with global
#define DELHEAD_IDMIS_ERROR 9 // id missed in delcare head part
#define AFTERASS_NOTIDEN_ERROR 10 // after a assign symbol should be a dientifier symbol
#define CONSTDEF_ASSMIS_ERROR 11 // in a const defination assign symbol missed
#define CONSTDEF_TYPE_ERROR 12 // const defination have only two type
#define SEMICSYMMIS_ERROR 13 // ; missed
#define VARDEF_ARRAYINDEX_ERROR 14 //an array index should be number in variable defination
#define VARDEF_TYPE_ERROR 15 // in a vardefine, identifier type error
#define MAINSYM_ERROR 16 // in a main function, name must be ”
#define LPARENSYMMIS_ERROR 17 // main ( missed
#define RPARENSYMMIS_ERROR 18 // main ) missed
#define LBPARENSYMMIS_ERROR 19 // mian {
#define RBPARENSYMMIS_ERROR 20 // main }
#define RMPARENSYMMIS_ERROR 21 // ] missed
#define AFTEROP_NUMMIS_ERROR 22 // -56 ok, -b not accepted
#define FACTOR_ERROR 23 // factor type error
#define ASSIGNSTATUS_ERROR 24 // assign status error
#define STATUS_ERROR 25 // status error
#define CONDITIONOP_ERROR 26 // condition status error
#define WHILESYMMIS_ERROR 27 // while symbol missed
#define IDSYMMIS_ERROR 28 // in a for loop, identifier missed in initial
#define FORASSIGNMIS_ERROR 29 // int a for loop initial, assign symbol must appeare
#define VARTYPE_ERROR 30 // in a for loop, loop variable should not be a const
#define FOROPMIS_ERROR 31 // in for loop step part, op(+/-) missed
#define FORDIGSYM_ERROR 32 // in for loop step part, digital missed
#define VOIDSYMMIS_ERROR 33 // in main function, void symbol missed
#define PARANUM_ERROR 34 // paramenter number error
#define UNEXCEPTRETURNVAL_ERROR 35 // void function return value
#define READARRAY_ERROR 36 // read an array name

错误处理方式:

1.递归子程序发现错误立即调用error()并传递出错信号;
2.error函数通过接收到的出错信号打印出出错信息和位置信息;
3.通过出错类型型号和位置判断应该如何进行跳读;
4.继续编译,直到文件结束。

三.操作说明

1.运行环境

运行环境为32位Win7系统,运行软件为CodeBlocks13.12版本。

2.操作步骤

打开final.cbp文件,输入目标文件所在位置(绝对位置或者相对位置均可)。

四.测试

内容略,已经过集成测试

C0文法 <加法运算符> ::= +|- <乘法运算符> ::= * |/ <关系运算符> ::= <|<=|>|>=|!=|== <字符> ::= _|a|...|z|A|...|Z <数字> ::= 0|<非零数字> <非零数字> ::= 1|...|9 <字符串> ::= "{<合法字符> }" //字符串中可以出现所有合法的可打印字符集中的字符 <程序> ::= [<常量说明部分>][<变量说明部分>]{<子函数定义部分>}<主函数> <常量说明部分> ::= const<常量定义>{,<常量定义>}; <常量定义> ::= <标识符>=<整数> <整数> ::= [+|-]<非零数字>{<数字>}|0 <标识符> ::= <字符>{<字符>|<数字>} <声明头部> ::= int <标识符> <变量说明部分> ::= <声明头部>{,<标识符>}; <子函数定义部分> ::= (<声明头部>|void <标识符>)<参数><复合语句> <复合语句> ::= ‘{’[<常量说明部分>][<变量说明部分>]<语句序列>‘}’ <参数> ::= ‘(’<参数表>‘)’ <参数表> ::= int<标识符>{,int<标识符>} | 空 <主函数> ::= (void |int) main <参数><复合语句> <表达式> ::= [+|-]<项>{<加法运算符><项>} <项> ::= <因子>{<乘法运算符><因子>} <因子> ::= <标识符>|‘(’<表达式>‘)’|<整数>|<子函数调用语句> <语句> ::= <条件语句>|<循环语句>|‘{’<语句序列>‘}’|<子函数调用语句>; |<赋值语句>; | <返回语句>;|<读语句>;|<写语句>;|<空> <赋值语句> ::= <标识符>=<表达式> <条件语句> ::= if‘(’<条件>‘)’<语句>[else<语句>] <条件> ::= <表达式><关系运算符><表达式>|<表达式> <循环语句> ::= while‘(’<条件>‘)’<语句> <子函数调用语句> ::= <标识符>‘(’<值参数表>‘)’ <值参数表> ::= <表达式>{,<表达式>}|<空> <语句序列> ::= <语句>{<语句>} <读语句> ::= scanf‘(’<标识符>‘)’ <写语句> ::= printf‘(’[<字符串>,][<表达式 >]‘)’ <返回语句> ::= return [ ‘(’<表达式>‘)’] 注:返回值为void类型的子函数不允许出现在表达式中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值