- 使用递归下降子程序法
- 处理c0文法
- 生成pcode
- 其他若干特征
编译器
指令
make
生成sfti.exe
sfti.exe 1.c
生成对应的pcode
模拟器
指令
make simulator
处理对应的pcode
simulator.exe pcode
以上是windos下的指令,liunx下自行更改CC变量
主要分成这三部分,采用中间树表示是因为这样可以简化方便优化,并且原则上需要有一个中间表示
因为有中间表示可以方便生成针对其他原型机的代码(不只是pl0机)
分别实现虽然看起来有一些冗余,但是实际上是模块化的一种思想,便于后期的分割。
编译器构建中使用的模块将在最后一一分析。 如果有人要看源码的话
- 词法分析部分
- 中间树表示生成
- pcode生成
词法分析部分在
词法分析程序实验报告
已经提过,不再赘述
采用自己的中间树表示,并且在中间树表示这里处理所有的错误,如果有语法等的错误,将在这一层级报错。
见后表
参考了现代编译器原理一书的实现,使用栈式哈希链表(我自己起的)的数据结构完成,O1的访问时间内完成变量的存取。
关于符号表,实际上在中间树构建和生成Pcode中分贝使用了env 模块和 penv 模块作为对应的符号表条目,这样可以使得模块的功能专门化
对各个语句等的表达式类型进行检查,在构建中间结果的时候就会输出错误
支持输出,函数参数
支持调用,赋值,运算等
使用float和int 混用的表达式将被认为是float 类型(1+2.0) 隐式类型转换还体现在其他一些地方,这里不叙述了。
使用教材 pxx 的pcode 并不能完全表示c0语法的特征,所以我针对指令集进行了一点微小的修改
并且对运行模型进行了一定程度的修改,下面就我使用的指令给与说明。
b 栈基址
t 栈顶指针1
S 栈区
ip 当前指令指针
- LOD x,y
将层次差x,offset 为y的变量载入栈顶 t=t+1 - STO x,y
将栈顶的值存入层次差x,offset为y 的变量中 t=t-1 - MKS x,y
栈标记指令, y是新栈帧中分配给参数的空间量 完成的动作如下 S(t+1)=b //动态连设置 S(t+2)=b //静态链的暂时设置 b=t t=t+4+y //分配相应的参数和栈帧空间 - CAL x,y 调用指令,层次差x,起始地址y S(b+3)=ip+1 S(b+2)=SL SL 的获取方式是不断沿着静态链上x次 寻直到找到调用的函数 ip=y
- INT x,y
将栈顶增加y t=t+y - JMP x,y
无条件跳转 ip=y - JPC x,y
有条件跳转 t=t-1 if S(t-1) == 0 ip = y - RED x,y
等待输入,并将输入填入层次差为x offset 为y 的变量 - WRT x,y
输出栈顶数字 t=t-1 - WRTS x,y
输出栈顶的字符 t=t-1 - WRTF x,y 输出栈顶的浮点数
- FLT x,y 浮点数转换 if y==1 将栈顶浮点转换为整形 if y==0 将栈顶整形转换为浮点
- OPRF x,y 同下,操作数为浮点数
- OPR x,y
根据y执行不同的运算操作,如下
case y==0 :
执行返回指令,将栈顶元素作为返回值返回
S(b)=S(t-1)
t=b+1
ip=S(b+3)
b=S(b+1)
case y==2 :
执行add 操作
S(t-2)=S(t-2)+S(t-1)
t=t-1
case y==3 :
执行minus操作
S(t-2)=S(t-2)-S(t-1)
t=t-1
case y==4 :
执行乘法操作
S(t-2)=S(t-2)*S(t-1)
t=t-1
case y==5 :
执行除法操作
S(t-2)=S(t-2)/S(t-1)
t=t-1
case y==8 :
等于 同下
case y==9 :
不等于
if S(t-2)==S(t-1)
S(t-2)=0
else
S(t-2)=1
case y==10:
小于
ifS(t-2) < S(t-1)
S(t-2)=1
else
S(t-1)=0
case y=11
大于 同上
case y=12
大于等于
case y=13
小于等于
因为pl0树上的pcode是不够的,并且模型也不够表示,所以这里对活动记录,栈帧表示进行了些许修改。
修改后的活动记录,每一个栈帧自下而上分别是 返回值,动态链,静态链, 返回地址 因为要进行函数参数传递的缘故,参考后面的pascal-s 添加了mks 指令,用在cal指令前设置一个栈帧,其中就包括了参数的分配,具体的步骤见上
code
- 函数参数传递类型不符合 定义处
- 函数参数传递类型不符合 使用处
- 函数参数过少
- 函数参数过多
- 这里期望有一个;
- 这里应该是一个int
- 这里应该是一个string
- 这里因该是一个float
- 这里应该是一个赋值=
- 变量在定义时不可以初始化
- 不明类型(只允许int string float)
- 变量定义时的不明符号
- 不合法的返回类型
- 不合法的主函数返回类型
- 这里因该是(
- 这里应该是)
- 函数参数中的未知类型
- 这里应该是, 或者)
- 函数}结束符丢失
- 函数间非法的分隔符
- 语句序列{丢失
- 未知语句类型
- 语句(丢失
- 语句 )丢失
- 未知的变量被引用
- 未知的因子类型
- 函数返回类型出错
- 未知变量被调用(只有函数可以被调用)
- 不是一个合理的左值
- 赋值语句的类型不一致
注: 在出现比较微小的错误后程序仍然可以正常编译,但是此时不保证程序的正确性。 此时程序不可被解释执行(需要先删除错误信息)
负责中间树生成,表示程序中的各个语义结构
负责错误处理,将程序结构组织成链表
词法处理程序
中间树生成阶段的条目
pcode生成中的条目
pcode生成中的栈帧管理极简
语法分析生成中间树的主要代码
符号表的数据结构
哈希表
中间树生成pcode的主要代码
内存分配等常用函数
pl0 模拟器魔改
提供了 1.c -5.c 是正确的样例程序
6.c 62.c 分别是int 和 float 版本的阶乘程序
7.c -12.c 是错误程序,错误信息提供在错误信息处理.docx
中,以供查看
没有GUI 编译器要什么GUI
感谢《现代编译原理(C语言描述)》 ,部分数据结构和思想借鉴此书。 代码量接近4k行,也耗费了不少时间。因为《现代编译原理(C语言描述)》中的编译器叫tiger 表示纪念,这个叫做sfti。