CC's

Back

Ark Compiler#

一个简单的编译器,本科时几个人合作写的,起的名字比较好玩。

实现了 parser、lexer、AST、符号表,以及在此之上的简单类型检查、结构体、重载、指针等高阶特性,虽然比较简单但功能相对完整。不过其实应该算一个编译器前端,能够将 Pascal 语言编译成一种三地址中间码。

例:一个 gcd 算法(最大公约数),输入两个整数,输出它们的最大公约数。涉及到指针、函数、递归、重载等必要特性。

program example(input,output);
var x,y:integer;
function gcd(a,b:integer):integer;
    begin 
        if b=0 then gcd:=a
        else gcd:=gcd(b, a mod b)
    end;
begin
    read(x, y);
    write(gcd(x, y))
end.
pascal

编译结果:本来是想接入 LLVM 作为后端,但是因为时间关系没有做到,暂且输出一种 C 的三地址中间码。更复杂例子请查看附录

设计#

Lexer#

词法分析器使用了 Flex 生成的有限自动机,将源代码转为 token 流,并检查词法错误。

Parser#

虽然有考虑过用 LR(1) 文法的分析器,但是在参考 Rust 实现及 LLVM 官方教程后,考虑到 LR(1) 功能还是过于僵硬、不易调试、错误恢复困难等问题,最终权衡利弊下还是使用了手写的递归下降分析器。

Parser 会将 token 流转为我们内部的 AST,并检查语法错误。

AST#

我负责实现了 AST、符号表与后续的中间代码生成。

AST 中包含了一些节点,有

  • AST:基类
  • BlockAST:代码块。由于 Pascal 的代码块不返回值,所以单独对待。
  • ExprAST:带有返回值的表达式。
  • TypeDeclAST:类型声明表达式的基类。
  • BasicTypeAST:基本类型,如 int。
  • TypeDefAST:自定义类型。
  • PointerTypeDeclAST:定义指针类型。
  • ArrayTypeDeclAST:定义数组类型。
  • NumberExprAST:数字常量。
  • StringExprAST:字符串常量。
  • CharExprAST:字符常量。
  • VariableExprAST:变量引用。
  • UnaryExprAST:一元运算符。
  • BinaryExprAST:二元运算符。
  • ReturnAST:返回语句。
  • CallExprAST:函数调用。
  • VariableDeclAST:变量声明。
  • ForStatementAST:for 语句。
  • WhileStatementAST:while 语句。
  • IfStatementAST:if 语句。
  • FunctionSignatureAST:函数签名,不包含函数体。
  • FunctionAST:函数定义。
  • StructDeclAST:结构体定义。
  • GlobalAST:全局 AST。

符号表与描述符#

编译器实现了符号表,实现了作用域等必要功能。

描述符包括

  • SymbolDescriptor:基类
  • TypeDescriptor:类型描述符
  • PointerTypeDescriptor:指针类型描述符
  • ArrayTypeDescriptor:数组类型描述符
  • VariableDescriptor:变量描述符
  • StructDescriptor:结构体描述符
  • FunctionDescriptor:函数描述符

中间代码生成#

代码生成主要由 Dispatcher 在 AST 上遍历完成。AST 定义了 dispatcher 的遍历顺序,并由 dispatcher 负责生成代码。这种设计使得未来可以相对方便地替换 dispatcher 从而对接其他后端,比如 LLVM(虽然最后还是没时间换了)。生成过程中将检查语义错误。

dispatcher 将代码插入至 Collector,并按照中间代码的结构顺序作最后编排,编译为中间代码。后续由 gcc 编译为目标程序。

以及一点可视化#

这个纯属课程作业的附带需求。由小组另一位兄弟完成:https://gr.yiren.lu/

开源#

代码于 github 公开:https://github.com/Harmaney/ark_compiler

团体名字和项目名字只是因为有趣,没有特别的意思。

不过代码质量写得没那么好,建议只编译后玩玩 files 文件下附带的 examples。


附录:更多例子#

以下附带二维数组、0-1 背包、快速排序三个复杂样例。其他更多样例可查看开源地址。

二维数组#

源代码#

program Hello;
var a:array[1..10,10..20] of integer;
begin
    a[1,14]:=2;
    a[2,12]:=3;
    a[10,20]:=a[1,14]+a[2,12];
    write(a[10,20]);
end.
pascal

中间代码#

0-1 背包#

源代码#

中间代码#

快速排序#

源代码#

中间代码#

Ark Compiler
https://astro-pure.js.org/blog/project-ark-compiler
Author Cheng Chen
Published at 2021年10月28日
Comment seems to stuck. Try to refresh?✨