从零开始的编译器生涯
近日一屑高二学生无聊动手写起了编译器….这是他的珍贵作战记录 0x01 理论基础 我摊牌,我没有看任何编译原理相关的书籍,因此这篇文章并不能作为严格的参考资料,甚至很多地方可能是错误的。 编译器,编译器,就是把高级语言的代码编译成另一种形式(class,asm,二进制,IR),而他在编译成另一种形式之前大概需要过这么个流程: Lex -> Parse -> Compile 接下来逐步讲解这个过程。 Lexer 就是分词器,输入用户提供的代码接着把他分成 tokens,也就是 tokenstream。 你肯定看不懂上面那句话的意思,让我们来点实例: a.value 里的那个 ArrayList 就是一个 token stream,str 是被解析的代码。不难发现,语句被 Lexer 按顺序进行了分类以及数据的分割,如 a 被识别为了一个 Identify (标记)。 因此也可以归纳出来 Token 大致的代码长啥样: @AllArgsConstructor @Getter public class Token { private int line; private Type type; private String content; } Parser Lexer 从源码中提取出 token stream 后将会交给 Parser 处理,它负责对 token stream 进行解析,生成一个 AST (Abstract Syntax Tree),也就是 抽象语法树。 这张图直观的描述了这一过程,你可以看到它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节。 接着,AST 将会丢给代码生成器用于生成代码,但是一般会先对 AST 进行优化,例如 常量折叠 Static Analyzing 但在这之前,我们还有一些问题要解决。 其实这玩意我是和 Parser 写一块的 试想一下,如果有这样一行代码: ...