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] |
move | move 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,label | if(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文件,输入目标文件所在位置(绝对位置或者相对位置均可)。
四.测试
内容略,已经过集成测试