cbc 启动和调试初步
最近在看《自制编译器》一书,上面介绍了怎么样实现一个编译器。但是,内容是抽取代码的部分进行讲解的。对我我这样基础比较薄弱的人来说,读起来,可谓管中窥豹,只见一斑。看了许久了,一直无法深入下去,只能停留在前几章的内容。之后的内容,基本上像是在看天书了,这样下去,花再多的时间也是徒劳的。所以,我想到了来运行cbc的代码,经过一番折腾,现在的成果是可以调试输出cbc的AST,即对任意的cbc源代码进行Parser,然后将生成的 AST 打印出来。
前置条件
- 安装 JDK 8
- 安装 JavaCC 7
- 安装 eclipse
在继续阅读下面内容前,确保这些东西都已经准备好了!
项目目录
我这里使用的 IDE 是 eclipse,使用其它 IDE 应该是一样的。首先,创建一个工程文件夹用来存放cbc代码,这里我创建一个 cbc 文件夹,然后在其中建立一个src目录。这个目录是 IDE 的规范,它是用来存放源码的。然后将 cbc 文件内容复制进src目录,使用 eclipse 作为工程打开即可。eclipse 会自动创建工程需要的其它东西,但是这不是我们需要关心的重点。
// 项目目录结构
-src
\---net
\---loveruby
\---cflat
+---asm
+---ast
+---compiler
+---entity
+---exception
+---ir
+---parser
+---sysdep
| \---x86
+---type
\---utils
生成 Parser 代码
cbc 的代码是不包含 Parser 代码的,它只有一个 Parser.jj
文件。这是一个 JavaCC(基于Java的解析器生成器兼扫描器生成器) 文件,它是一个 Parser Generator。所以,我们需要自己来根据 Parser.jj
来生成扫描器和解析器代码。这就需要我们前面安装的JavaCC了。我自己在 eclipse 中安装了 JavaCC 插件,所以可以直接右键选择 Compile with javacc
生成。
但是,我不推荐你这样做!这个插件安装其实挺麻烦的(而且插件的版本很老),不像 vscode 那样简单,所以我推荐大家直接使用命令行来操作。使用命令行来生成,非常简单,只有一句代码:
javacc Parser.jj
因为我已经生成好了,就不想再操作一遍了,给大家看一个 eclipse 插件生成的输出,使用命令行的输出基本是一样的。不过这里可以看到该插件是版本是 5.0 了,我下载的 JavaCC 是最新版的 7.0。
JavaCC 生成的文件,开头都会提示,不要去编辑它。但是生成的文件再 eclipse 里面会报错,因为有一个类没有被正确导入,Parser.java 文件,需要手动导入一个类:import net.loveruby.cflat.entity.Parameter;
如果你报错找不到 Parameter,可以直接将这行代码加入 Parser.java
文件中。
初次运行
这是测试的输入文件 hello.cb:
int main(int argc, char **argv) {
printf("Hello, World!\n");
return 0;
}
配置工作:
cbc 项目的主类是 Compiler,我们需要时需要指定相应的参数,这里使用下面这种方法来配置,我现在只使用了两个参数,第一个参数指定了源文件,第二个参数是输出源文件的 AST 结构。
运行:
运行结果:
注意:cbc 是 Linux 中 C 语言的规范,所以它的main函数是带参数的。如果使用不带参数的main函数,会无法解析通过,它会报下图所示的错误。
解决导入路径问题
完成上面,基本上已经算是大功告成了,可以启动cbc代码进行 Parser。这里现在只能进行 Parser,后续的我还不清楚。不过现在还有一点问题,那就是无法在源文件中包含导包代码,例如对如下文件进行 Parse:
import stdio;
int main(int argc, char **argv) {
printf("Hello, World!\n");
return 0;
}
这是因为默认的导包路径无法找到需要导入的包的问题。经过分析(如下图所示),默认的导包路径是在当前路径下面,所以这里解决办法就有两种了:
- 将需要导入的包放入工程目录中;
- 修改默认的导包路径。
这里我选择第二种方式,这样对工程没有侵入性。解决办法是复制 cbc 目录中的 import 包到 cbc 工程所在的目录下。
然后修改代码如下:
这里将默认的导包路径由当前目录下,修改为了上一层目录下的 import
文件夹下了。
再次运行:
这样就可以正常通过 Parse 了,不过输出基本没什么变化,毕竟现在只是编译的解析过程。
说明
通过一番折腾,总算是走了一小步了。因为编译的前期是平台无关的,所以这里可以在 Windows 上面进行操作,不用去搞虚拟机了。而且,通过IDE阅读代码,也比通过看书本上的部分代码好多了。不过,接下来的内容,感觉很难看懂了,所以我打算暂且放一放了。