上 构建Lua解释器Part7:构建完整的语法分析器

以下内容转载自%E6%9E%84%E5%BB%BAlua%E8%A7%A3%E9%87%8A%E5%99%/
在上一章里 , 我完成了词法分析器的设计与实现的论述 , 接下来我要继续论述语法分析器的设计与实现 。限于篇幅 , 我将会把语法分析器分为两个部分来论述 , 本章为上部 , 下一章为下部 。本章将会重新复习编译器的构造 , 并且论述编译流程 , 以及lua的所涉及的编译相关的内容 , 最后通过阐述虚拟机相关指令的实现 , 作为结尾 。本章所涉及的代码 , 全部在工程里 , 欢迎star 。另外 , 如果你喜欢我写的文章 , 喜欢讨论技术 , 欢迎加入我创建的群:
编译器结构
早在Part5的时候 , 我就论述过编译器的构造了 , 现在重新回顾一下 , 按照编译器标准的划分方式 , 编译器可以分为前端、优化器和后端 。编译器前端主要的工作 , 就是将源码经过语法分析 , 语义分析后 , 生成抽象语法树AST(Tree) , 然后将AST本身 , 或者转化为另一种形式的中间表示( 简称IR) , 交给优化器进行优化 , 然后再交给编译器后端去生成机器码 。
图1
编译器的本质 , 就是将一种语言 , 翻译成另一种语言 , 在Part5中 , 我也较为详细地论述了编译器和解释器的区别 , 这里简单回顾一下 。编译器是将源码编译成对应平台的机器码 , 生成可执行文件或者动态库 , 尔后存储在磁盘里 。当我们要执行这些代码的时候 , 需要创建一个进程 , 然后加载这些目标代码才能执行 。而解释器则是可以直接加载源码 , 然后在解释器内部编译并且立刻执行 。
Lua是标准的解释器 , Lua代码被加载后 , 可以直接被编译 , 且立即执行 。本章要讨论的内容则是Lua解释器内部的编译流程 。Lua的编译工作 , 主要是由模块来完成 。
编译流程
按照《and Tools》的定义 , 编译流程大概要经历以下几个步骤:
图2
实际上 , 图2中的词法分析器( )、语法分析器( )、语义分析器( )和中间代码生成器( Code )属于编译器前端 , 而代码生成器(Code )和平台依赖代码优化器(- Code )则属于编译器后端 。
实际上 , 编译过程中 , 出现错误 , 也不一定是要中断编译流程 , 有些编译器会尝试修复错误[2] , 在lua中 , 遇到编译问题 , 则是终止编译流程 。此外 , IR也并非一定是三址代码 , 也可以是AST[3] 。
Lua 的主要工作
如图2所示的编译流程中 , 词法分析器我已经在上一章详细论述过了 。由于Lua实现了完整的虚拟机 , 有自己的虚拟机指令 , 因此它的编译流程只需要将Lua代码编译成虚拟机指令即可 , Lua解释器没有内置JIT , 因此不会将Lua代码编译成目标机器码 , 所有的虚拟机指令 , 均是由c语言编写 , 本质上来说 , Lua的内置编译器只是一个前端编译器 , 而Lua虚拟机的虚拟机指令 , 则可以视为是一种IR , 这种IR将在虚拟机里被识别并运行 。
Lua的模块 , 则是执行了语法分析和语义分析 , 最后将Lua代码编译成虚拟机指令 , 这些虚拟机指令最后会交给虚拟机执行 。
图3