注意: COOL编译器实现是一门网络公开课,地址是https://class.coursera.org/compilers/class/index,可能要FQ
可以在里面找到所需要的开发环境(虚拟机镜像等)和相关的资料说明.如果你感兴趣,可以一同学习
和讨论.
1. 作业目标
实现parser,给了两个工具,parser生成器--Bison和操作树结构的包parser的输出是一个abstract syntax tree(AST),
我们需要做的是结合AST构造的API,再使用Bison中的semantic actions来构建这个AST。
2. 参考资料
a. Cool语言的语法结构,在The Cool Reference Manual中
b. 树结构的包的使用方法,在 Tour of Cool Support Code中
c. Bison官方文档,http://www.gnu.org/software/bison/manual/html_node/index.html#Top
3. 环境准备
copy作业文件夹:
- cd 到你的到你的workspace下
- make -f /usr/class/cs143/assignments/PA3/Makefile
4. 实际工作
修改cool.y,分为两个部分:
I. 为你引入的新的非终结符添加额外的类型声明;
II. 终结符的部分已经基本完成;
III. 需要操作符添加优先级声明;
5. 测试parser
make parser
./myparser -p good.cl //加上-p可以查看分析过程
#然后查看输出文件cool.output
使用./checkparser.sh good.cl命令来比对你实现的parser和正确parser之间
的分析结果的差异。
6. 关于输出的结构
输出的是一个AST,你的semanic actions也应该是在构造一个AST,它的根应该是program类型。
然后逐级向下构造各结点。
7. 关于错误处理
需要提供error非终结符,需要至少提供以下两种错误恢复功能:
a. 如果一个类的定义有语法错误,但这个类正确的结束定义了;而下一个类的定义是正确的,
parser能够跳过错误的类,重新从下一个正确的类开始分析。
b. 类似的,parser能够从feature中的error中恢复过来,继续分析下一个feature。
8. 注意事项
a. 关于优先级声明,只能用在表达式上。
b. let语法是二义性的语法,需要注意由它引起的shift-reduce冲突。可以利用bison中产生式的优先级声明
c. 注意注释掉所有print语句。。。
实践笔记:
- cool_tree.aps文件是各个树结点构造函数的声明文件,需要看一下
- cool_tree.cc文件包括着上面文件中函数的实现代码,而且还包括列表结点的操作
- 要注意结点构造构造函数所需要的参数,如上面的single_Cases所需要的是case类型,而真正生成case类型的函数是branch()函数,所以正确的写法应该是single_Cases(branch(id, type, expr))
- 每一个semantic action都包含两个步骤,首先,标记位置信息;然后,构造AST树结点。
- ID一定要以小写开头,TYPEID一定是大写开头
- 每一行不一定要用分号做结束。可以参考class和feature的语法
- 函数体中,出现单行expr时,不能加分号结尾。一定要加分号的话,需要用{}把expr括起来。
- 正确的reflexer, refparser等文件都在/home/compilers/cool/lib/.i686下可以找到
- 使用方法: reflexer good.cl | refparser
- 关于error处理,没有想像中复杂,只需要在expr分支下加一个error语句就可以,不用action;对应class,feature等的错误处理也可以相应进行
- 有一点需要注意,语法定义一定要使用左递归,原因不明....记住[formal, [,formal]*]定义一定要用左递归。。。。
- expr_list: expr_list ',' expr 而不能写成 expr_list: expr ',' expr_list
- 关于错误处理,单一地使用error是不足以达到恢复的效果,需要用error和其它的符号组成一个整体的错误处理语法,来达到跳过发生这行的效果
- 比如在let语法中需要加入error IN expr和error ',' expr_let_part_2这两个错误处理语法,来达到匹配整个出错的行,忽略它,进行下一行处理。
- 再如block语法中需要加入error ';'来达到匹配整个行的效果。
- 关于let语法,它是一个循环的语法,let x : int, y : int in x ==> let x :int in let y: int in x,在构造时需要把let语法分成三个部分:
- part1: let id : type [<- expr]
- part2: [,id : type[<-expr]]*
- part3: in expr
代码:
上文中提到的所有文件都在我的github里可以找到,其实也只需要看cool.y文件就可以了,所有的改动都是围绕这个文件:
https://github.com/hanks/Compiler