代码结构如下
- build
xcw_main.l.yy.cpp
由lex根据xcw_scanner.l
生成的文件xcw_parser.tab.c/xcw_parser.tab.h
由yacc根据xcw_parser.y
生成的文件complier
通过g++得到的可执行文件
- source
xcw_scanner.l
词法分析器xcw_parser.y
语法分析器
- Makefile
- tests
用来传递参数。
核心代码如下,用IF_ptr_int
表示传上来的是否为常量,分别用ptr_int
和ptr_str
保存结果
struct Ptr_num{
int ptr_int;
string ptr_str;
int IF_ptr_int;
Ptr_num(int p_int){
ptr_int = p_int;
IF_ptr_int = 1;
}
Ptr_num(string p_str){
ptr_str = p_str;
IF_ptr_int = 0;
}
Ptr_num(){}
};
对于每一个IDENT(包括常量、变量、数组、函数),用一个IDENT_scope
用于保存,其中包含了各种IDENT的基本信息。在每次词法分析器遇到IDENT时,对需要的变量进行初始化
string IDENT_name;
string IDENT_num; // 变量的值可变,因此用string存储
int IDENT_const_num; // const常量直接用INT型数字表示其内容
int Array_size;
vector<Ptr_num>* IDENT_array; // 指向数组头部的指针
vector<int>* IDENT_dim_array; // 用于存储数组的维度
int IDENT_func_param_num; //参数的个数
int IDENT_deep;
bool IDENT_if_const;
string IR_name; // 在Eeyore中的变量名
bool IDENT_if_array; //是否为数组变量
bool IDENT_if_func; //是否为函数变量
bool IDENT_if_ret_int; //返回是否为INT
用一个vector
模拟符号表的结构
vector<IDENT_scope> Scope;
函数find_define
,输入一个IDENT,在符号表中找出这个IDENT的定义,返回指针
实现思路就是从后往前进行寻找,遇到第一个匹配的直接返回。
IDENT_scope* find_define(string name){
int i = Scope.size() - 1;
if(i == -1)
return nullptr;
while(i >= 0){
if(name == Scope[i].IDENT_name){
return & Scope[i];
}
i--;
}
return nullptr;
}
只要保证每个Block结束后,都把这个Block中的定义的局部变量删除,这样find_define
能找到正确的定义。具体操作是在Block->LCURLY BlockItems RCURLY
结束后,增加如下代码
int i = Scope.size() - 1;
while(Scope[i].IDENT_deep == DEEP && i >= 0){ //需要深度一致
Scope.pop_back();
i--;
}
DEEP --; //还原
DEEP
:表示当前所处的深度Func_VarDecl\Func_Other
:用于保证先输出变量定义,每次都把需要打印的语句根据是否为定义语句,分别放入这两个vector<string>
中。
以比较复杂的语句VarDef->IDENT ArrayDef ASSIGN LCURLY ArrayInit RCURLY
为例
即类似于 int b[4][2] = {1,2,{3},{5},7,8}
ArrayDef
中,先把方括号中表示数组维度的参数放入全局变量Array_dim
中,并获取当前的数组大小n
- 初始化一个
IDENT_scope
变量,将IDENT_if_array
设置为1,Array_size
设置为n,并将Array_dim
中的元素存到这个数组的Ident_dim_array
中 - 最后将数组压入到符号表中
- 变量
Array_dest
表示当前大括号控制的范围 - 变量
Array_loc
表示当前读取到的位置 - 利用数组总的size和每个维度的size,得到每个大括号的控制范围,将
Array_loc
到目标范围之间的元素都赋值成0
主要是语句LVal->IDENT ArrayLVals
Array_LVal_dim
存储了各个数组具体的维度,Array_dim
存储了原始的维度- 然后根据原始数组的维度,计算每次的中间变量
- 当数组变量不在左侧作为被赋值的对象时,需要用一个临时变量进行存储
- 变量
Cond_Array_Flag\R_Array_Flag\BRAC_Array_Flag
分别表示是否在条件语句、是否在右侧、是否在数组的中括号中
- 变量
- 对于每个IF、while语句,用至少5个label表示其中的位置
- l0表示条件为错的位置
- l1表示条件为真的位置
- l2表示整个语句块的下一个语句
- l3表示当前条件语句的位置
- 用变量
label_l_num_st
表示总的条件语句数l_num + label_l_num_st + 2
表示下一个条件语句的位置
- AND语句
- 如果为真,直接跳到下一个条件语句
- 如果为假,跳到l0
- OR语句
- 如果为真,跳到l1
- 如果为假,跳到下一个条件语句