pa2
分析
全局字符指针yytext中提供与该匹配相对应的文本(称为token),并在全局int变量yyleng中提供长度。
然后执行与匹配模式(pattern)相对应的操作(action),然后扫描剩余的输入寻找下一个匹配。
%x 定义的区域可以在规则部分 用 BEGIN() 进入。
进入后 正则匹配 对呀 <> 前缀的环境下进行
1 | %x STRING |
定义我们自己设置的函数。
1 | int comment_depth = 0; |
上面的定义 定义在 %{ xxxx %}. xxx里面
然后是定义扫描的 作用域名字
1 | %x COMMENT |
为了保证 代码 安装类型去进行 扫描 并判断基础语法的闭合等是否正确
定义不同类型的 正则匹配逻辑
这里定义 匹配到后应该返回什么
1 | WHITESPACE [\ \f\r\t\v] |
扫描的正则匹配
- 需要判断() 前后是否闭合
- 当然匹配到 前括号 于是开始扫描匹配 括号内到能容 括号个数用
comment_depth
变量来统计 - 匹配内容时还要统计 是否换行等用
curr_lineno
变量来记录 - 如果匹配到的
)
后comment_depth
== 0 这时代码正常 否者 存储错误
- 然后匹配运算符,类型名字,
class, if ,else
等 - 染匹配
string
的数据并保存,且判断""
是否闭合
pa3
分析
bison 语法中 结构和 flex 类似 实现代码的逻辑结构
语法结构
1 | %{ |
Pa3 我们需要编辑 cool.y 实现对 语法 结构的分析, 并且保证语法的逻辑正确,先乘除法后加减法。
利用的 LR(1) 的语法分析结构
expr: id | id '+' id;
如这样去匹配 +号
%token 用于标记语法解析中用到的基本语素 总结字符
规则部分写自己定义规则
%left 左推导 后定义的优先级更高 所以我们定义 加减乘除的时候要 先定义加减 后定义乘除
1 | %left '+' '-' |
实现对应的语义分析
Cool.y 里面的内容和函数实现,要根据
cool-tree.cc
来找到对应的函数。来进行语义的分析,里面保存的是对应运算的函数。
通过编译原理的 语义分析公式 对cool 语法进行分析。
例如:
语义分析 两数相加
在 cool-tree.cc
中 这样定义功能
1 | Expression plus(Expression e1, Expression e2) |
我们在cool.y
中就要写为. 匹配 $1 $3 作为参数 传给 plus 函数。然后返回给 $$
1 | expr '+' expr |
注意⚠️
除了我们需要的功能,我们自己还要写 不同类语义分析对应的 error
还要去 匹配到 ';'
保存语法的完整性
pa4
分析
Pa4。 ps:分析的github 师傅的代码
分析cool 的语法
分析和处理 cool 语句的语法
- 给每个类定义一个 作用域,利用结构体保存。
ClassTable
- 在作用域中,要保证变量没有被重复定义。
- 保证一个变量或者函数的使用,是在前面的代码中定义过。不能使用没有定义过的变量。
- 赋值中,保证赋值对象和赋值内容,是同一种type。
- 在继承树中,继承类中的每个类都是存在的。
实现语义的整理
在识别一个变量名时,会先便利本身的继承树里面更具,变量的名字去找是否这个变量名是否已经存在。
对于类的保存,采用的是 类似于 libc里面的smallbins 的结构,这样不但能记录下最基础的类的数量,也能更具这个数据结构去记录下这个类的继承类之间的关系。从而保证类的实现的完整性。
对应不同类型的变量,我们利用不同的,
Class_
类型的tableclass
来存储Object_class
IO_class
Int_class
Bool_class
Str_class
让后将定义了的 class 类型add
到对应的作用域的class table
里面对于不同类型的 变量,自身就有固定的类函数,所以在初始化类的时候就要将对应的函数和变量添加进去 如:
Str_class
下的val
str_field
length() : Int
concat(arg: Str) : Str
substr(arg: Int, arg2: Int): Str
然后又根据不同的类型(方法,变量,参数个数)分类添加到对应的类中。如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28// The class Str has a number of slots and operations:
// val the length of the string
// str_field the string itself
// length() : Int returns length of the string
// concat(arg: Str) : Str performs string concatenation
// substr(arg: Int, arg2: Int): Str substring selection
//
Class_ Str_class =
class_(Str,
Object,
append_Features(
append_Features(
append_Features(
append_Features(
single_Features(attr(val, Int, no_expr())),
single_Features(attr(str_field, prim_slot, no_expr()))),
single_Features(method(length, nil_Formals(), Int, no_expr()))),
single_Features(method(concat,
single_Formals(formal(arg, Str)),
Str,
no_expr()))),
single_Features(method(substr,
append_Formals(single_Formals(formal(arg, Int)),
single_Formals(formal(arg2, Int))),
Str,
no_expr()))),
filename);且每个类都要定义对应的 error 错误的函数,用于保存,语义分析时的错误。
在类的定义时,我们用到的是双向链表,保证类类型的强制转换的正确,
父类->子类。子类-/>父类。
一个类中的函数定义好后,如果有参数,在调用时,要去判断传入的参数的类型,是否对应。
一个变量的定义,我们要保存它对应的值,和保存它定义的 type ,当进行复制操作等时,要去验证 等号两边的值是否类型相同。
pa5
分析
P5 实现 将我们语义分析后的代码结构,功能等转化为,电脑能够识别等汇编语言,从而实现cool的编译,实现在电脑上运行(汇编语言)。 用到 mips32 汇编 学习地址
代码文件功能
cgen.h
定义代码转换时用到的 函数,全局参数 一共五个 class 类型分别保存CgenClassTable
用于符号类型(symbol)CgenNode
用于类类型的继承等(class)BoolConst
bool类型用到的函数。cool-tree.handcode.h
宏定义头文件,定义最基础的,会用到的函数,类类型,全局函数,yylineno全局变量等。cgen.cc
关键代码文件。用于实现cool 语言转化为汇编指令,(机器码)二进制文件,从而能在电脑环境下运行。emit.h
寄存器名称和地址以字符串形式传递。可以通过这个文件查看对应的寄存器的名字等Emit.h
寄存器关系 链接
1 | /////////////////////////////////////////////////////////////////////// |
代码分析
我们的 string
, ing
, bool
这两个常数类型,被保存在自定义的表中 stringtable
inttable
表中,而bool类型只有两个值不会生成表
将此代码
和代码生成器树的类列表
传递给“CgenClassTable”
的构造函数。
1 | void program_class::cgen(ostream &os) |
在cage.cc 文件中已经定义好了基础的 函数。
定义 push 栈,当有寄存器被push 进栈时。栈向低地址放心增长 emit_addiu(SP,SP,-4,str);
(-4大小)
1 | static void emit_push(char *reg, ostream& str) |
将int的整数值保存在寄存器,将寄存器的值复制给int整数部分
1 | // Fetch the integer value in an Int object. |
我们要启动 .data
段,.text
段,并且声明他们的全局名称,并将初始引用记录下来。
初始化stringtable
和 inttable
(初始 “”,0)然后将扫描得到的 string int bool 分别保存到对应的table
1 | void CgenClassTable::code_constants() |
对我们定义的类的 基础属性进行一个记录。名字,类型。以此记录下来。
让我们去记录 这个类 拥有的方法,方法名字,方法属性等,(先扫描记录父类中拥有的方法函数,然后记录下自身定义的心的方法函数)要注意是否 override
还要记录起大小偏移。
对应类里面根据名字生成 新的 树 symboletable
1 | void CgenClassTable::code_dispatchTab() |
对于每个 类的基本属性,在每个表中,用名字作为下标记录,不同属性的表都会用 name作为下标来保存值。
cgenNode_table[name] = node;
attr_list[name]= std::vector<Symbol>();
等。
转换class method 的时候
- 循环遍历 nds 表 排除
if (name == Object || name == IO || name == Str) continue;
- 当得到一个对应的方法等时候。会将对应的参数放入栈中。然后会call 这个方法类,在方法执行完返回时,会 pop出栈,恢复栈平衡
1 | for (int i = fs->first(); fs->more(i); i = fs->next(i)) { |
然后初始化 CgenClassTable
1 | CgenClassTable::CgenClassTable(Classes classes, ostream& s) : nds(NULL) , str(s) |
定义最基础的类 Object类
IO类
然后定义 int类
bool类
str类
1 | Object类没有父类。 其方法是 |
然后重新生成类的 继承树, 设置继承树中类的 父类,和子类关系。将合法类添加到 类列表和符号表 。
最后 实现不同类型的 code函数,编译不同的函数功能。