MP1.2 Cool Lexer
本次实验是完成 Cool 语言的词法分析部分. 主要内容为: 参考 Cool 语言的 Manual, 写出 Cool 的 各类token 的正规表达式, 用 flex 的词法描述文件表达, 并且增加错误处理. 完成这次实验后, 你会得到一个 lexer
的可执行文件, 它读入一个 Cool 程序, 输出一个 Token 序列;此外, lexer
对错误的程序也应该有较好的处理. 这个 lexer
将在 MP1.3 中使用到.
下面介绍本次实验的要求, 并提供一些 flex 相关的资料.
1. 文件说明
在做 MP1.1 中,已经要求你把 cool-support
, reference-binaries
和 src
复制到了你的 mp1
目录下. 如果你上次确实交了作业, 现在你的目录结构应该是这个样子:
PBXXXXXXX : git 根目录
├── README-git
└── mp1/
├── answer.txt : 上一次的作业
├── cool-support
│ ├── include
│ └── src
├── reference-binaries
└── src
├── Makefile
├── bison_test_bad.cl
├── bison_test_good.cl
├── cool.flex
├── cool.y
└── flex_test.cl
在 mp1/src/
目录下, 执行
$ make lexer
会编译生成 lexer
可执行文件, 第一次执行这个命令时会将 cool-support 里的文
件也链接到该目录下.
实验涉及到的就是现在目录下的这些文件:
├── Makefile
├── bison_test_bad.cl : 在mp1.3 中使用
├── bison_test_good.cl : 在mp1.3 中使用
├── cool-lex.cc : flex 根据 cool.flex 生成的词法分析器源码
├── cool.flex : 你要完成的的 flex 文件
├── cool.y : 在mp1.3 中使用
├── flex_test.cl : 一个 cool 程序, 用于测试
├── handle_flags.cc -> ../cool-support/src/handle_flags.cc
| : lexer 命令行选项的处理
├── lexer
├── lextest.cc -> ../cool-support/src/lextest.cc
| : main 函数在这里
├── stringtab.cc -> ../cool-support/src/stringtab.cc
| : 需要用到的函数, 参见 stringtab.h
├── utilities.cc -> ../cool-support/src/utilities.cc
└ : 需要用到的函数, 参见 utilities.h
你需要修改的文件为
cool.flex
一个 flex 词法描述文件的框架. 编译生成的 lexer 只是把输入原样输出. 为了完成实验, 你需要添加Cool 的词法元素的正规表达式和一些辅助函数, 辅助函数请直接放在这个文件里.flex_test.cl
包含一些 cool 语言的测试输入. 仅通过这个测试是不够的. 你应该充分测试你的lexer
.我们的会用我们的测试例子来测试你的程序. 你不用提交这个文件.
lexer
的运行方式为:
$ ./lexer flex_test.cl
在handle_flags.cc
里了解更多命令行选项. 其他文件请勿修改.
2. 对词法分析器的要求
你应该根据 Cool manual : CoolAid 中的 Section 10 的 Figure 1 (P16) 来完成词法规则的定义. 返回的 Token 的类型定义在 cool-support/include/cool-parse.h
.(涉及到 flex 与 bison 的合作, 请利用后面的资源了解这个过程).
a. 基本要求
完成关键词的分析.
完成行号的记录, 变量
curr_lineno
用来记录源码的行号(已经定义).程序中一个词法单位(lexeme) 往往出现多次, 比如一个变量名. 为了节省时间和空间, 编译器会把这些词法单元用一个 string table 保存. 在 cool-support 里面提供了一个 string table 的实现. 有三种 String Table: 字符串的、整型常量的、标识符的. 请填好这三个 String Table.
此外,不用检查整型常量是否合适. 特殊的 Identifier (如
Object
,Int
,self
等)暂时不用特殊处理, 以后再说.
b. 错误处理与错误恢复
所有的错误都会传给以后实验中的 parser
. 遇到错误时, 返回 ERROR
这个 token , 并且给 yylval.error_msg
赋值一个代表错误说明的字符串.
下面是本实验要处理的错误. 这些情况返回 ERROR
token, 下面说的返回字符串
是指通过变量yylval.error_msg
返回.
遇到无效字符(不可能是任何 token 的开头)时, 返回包含这个字符的字符串, 并从下个字符开始继续分析.
字符串过长, 或者包含了无效字符,则报错 "String constant too long" / "String contains null character". 在这个字符串的结束处开始继续分析, 不要为这个字符串生成错误记号之前生成一个string token.
如果一个字符串包含非转义的换行, 则报错 "Unterminated string constant", 并从下一行开始恢复词法分析 -- 这里假设程序员忘记加 close-quote (即右引号). 在生成错误记号前不产生一个 string token.
如果在字符串/注释中遇到了 EOF, 则报错 "EOF in string constant/comment". 不要为这个注释生成 token.
在注释之外的地方遇到了
*)
, 报错 "Unmatched *)", 而不是生成*
)
两个 token.
实验的难点在于对 string 和 comment 的词法分析, 建议放到最后实现, 需要使用到 Start Condition 的功能.
建议先理解代码, 了解 lexer 是怎么运行的, 利用好现有代码, 结合 A Tour of the Cool Support Code 先理解 cool-support 里面的代码再开始写. 不要反复造轮子, 可以使用utilities.h
里面的函数.
3. 提交要求
你提交的文件目录应该是这个样子:
PBXXXXXXXX : git 根目录
├── README-git
└── mp1/
├── answer.txt
├── cool-support
├── reference-binaries
└── src
├── READEME(.md) *********: 说明文档
├── Makefile
├── bison_test_bad.cl
├── bison_test_good.cl
├── cool.flex *********: 你的主要工作在这
├── cool.y
└── flex_test.cl
在 README
中说明你实现了哪些功能, 并尽可能给出自己实现的说明文档.
在 cool.flex
里面记得多写点注释.
提交前先运行
$ make realclean
4. 资料
flex 和 bison
2015 年编译课使用的实验教程, 包含一个 表达式语言 的例子. 见 flex and bison 2015
动物书: flex and bison(英文版) 或(中文版), 书的代码在这里