COOL Compiler-Part1

COOL Compiler 是 Stanford CS143:Compilers 的课程项目,这学期学编译原理,于是就做了一下。每个 PA 都写了一点笔记和踩坑记录,大概分个两三次更完。

开始之前

这门课的官方材料好像每隔几年就会换一个地方,导致找起来十分的困难(之前做的 xv6 就很棒,多少年的东西都放在同一个网站上),现在这个时间点(2023年3月)的官网应该是在 edx 上面,需要注册收费之类的才能获取相关材料。不过这门课历史悠久,基本上从2000年左右就有了,自然是被大家复制传播到了各种地方。这个 repo 里面的 starter code 是从 github 上别人的仓库里面的分发包(student-dist.tar.gz)解压出来的,我对比了几个仓库的代码内容都是一样的,那么做这个版本的应该问题不大。不过和课程网站上的内容还是有一些不同的,这就没有办法了,大家也拿不到 Stanford 的内部版本。

PA1

第一个实验是用 COOL 写一个简单的程序,程序的功能是解释一个简单的 Stack Machine 语言,会输入一些序列然后按照文档的说明进行操作。

推荐的实现方式是,定义一个 StackCommand 类,然后为每一条命令实现一个子类用来执行具体的操作。这里还给了一个工具类 atoi.cl,里面实现了 Stringint 的转换。不需要实现错误处理,假设给出的序列都是合法的。不过本人才疏学浅,就写了几个 if 就写完了,压根没写这么复杂。

首先我们需要一个 List 作为栈,这个可以从 examples/list.cl 里面抄过来,然后把数据类型改成 String 就可以了。然后就是处理输入输出,定义一个输入变量,一个大循环判断是不是 input = "x",里面一个大 if-else-then 判断输入的命令,如果是 d 就调用前面抄过来的 print_list(需要修改一下格式和类型);如果是 e 需要再来一个 if-else-then 来处理求值操作;其他的就直接塞进栈里面,这里不做错误处理,假设都是合法的。具体到 e 命令里面,pop 并判断栈顶,如果是 +pop 两次,调用 a2i 做运算然后再 i2a 变回字符串 push 到栈上;如果是 s,依然需要 pop 两次并保存 pop 出来的东西,然后逆序 push 进去就完成了交换操作。

主要的困难在于 COOL 的智障语法,其他的倒是没啥难度。

  • 最智障的是它的 expr。绝大部分的东西都是个表达式,两个大括号也是表达式 { [[expr; ]]+},两个大括号中间的表达式后面必须跟分号(其他的不需要),if - fi 后面都必须跟分号我也是绷不住了。然后它的函数定义后面跟着那两个大括号是个语法符号(摆设)而不是 expr,这种就很不直觉,让我困惑了半天然后翻它的文法声明才发现这个事情。feature::=ID( [ formal [[, formal]]∗ ] ) : TYPE { expr },也就是说,只能写一个表达式,不然就得再加一个单独的大括号然后里面写多个分号隔开的表达式。
  • 其次是它的 if-then-else 居然不能没有 else!这导致我不得不像个傻子一样定义一个 dummy(): Object {0};,然后放在根本不需要的 else 子句里面。
  • 还有它的 while 循环居然没有 break ?!
  • let 语法也是究极奇葩,我是不能理解为什么要设计成这个样子。想定义一个局部变量都得加一层嵌套,这个嵌套多得让我觉得我 tm 是在写 scheme
  • 它的 case 也是奇葩,居然是用来做动态类型匹配的,匹配的是类型而不是值,和正常的 switch-case 完全不一样,倒是有点像 rust 里面的那种感觉。

测试的话,虽然 handout 里面说是直接 make test 然后对比输出,不过我也没看到它哪里有所谓的 reference implementation,自己看了看测例觉着没啥问题就行了。

摸摸猫猫
Built with Hugo
主题 StackJimmy 设计