编译器概念入门
-
找网上关于编译器的教学方法,多数会找一个语言作为例子比如[Rust]
-
我可以了解为什么选择Rust以及Rust相关的百科介绍
-
其次就是按照教程的思路了解编译器知识点的大概,在此之前我们可以分为两部分:编译器相关概念,编译器相关算法,编译器的需求和实现知识。
-
按照:是什么,怎样样,有什么 的认知过程学习编译器。
-
编译器是做什么的?编译器获取源代码,产生一个二进制文件。我们将要学习的是比较狭义的编译器,所以会有很多计算机相关的知识概念。
-
编译器的核心行为:将源码的单词和符号分类,然后匹配出运算符号,构建运算符树(也叫表达式树),这人一步是将无序转为标准源码的过程。最后一步就是按照表达式树的格式化数据翻译成二进制数据。
-
了解整个过程的术语概念:input(char)-> lexer(词法分析器) -> token (符号) -> Parser (语法分析)-> AST 抽象语法树 -> code generator / Evaluator(汇编/二进制码生成器,或者解释执行器)
-
概念的东西比较好理解,可以快速归纳一笔带过,我们的主要目的是对其进行归纳和记忆。算法和设计思想部分才是需要我们去深入理解,并转化为个人的编程经验。
-
了解编译器的3个重要工具对象:
- Lexer 词法分析器,词法分析(lexical analysis)是计算机科学中将字符序列转换为单词(Token)序列的过程。 将文本转化为词Token
- Parser 语法分析器,也是最复杂的,将词转为语法结构。
- Code generator 代码生成器,这个比较死板。
-
代码到达了AST可以说是相当接近指令级别了,所以解析器是很重要的。
-
Lexer把文本变成词,Parser将词按照语法规则组成语句AST,计算机读取AST执行命令。
-
因此计算机也有自己的词法和语法。
-
解释器和编译器的区别在于,解释器需要按照程序到计算机里面,解释器会直接执行AST,不需输出二进制指令文件。而编译器产生的是CPU都能识别二进制文件,所以本机不需要安装额外辅助软件。
-
词法分析的任务就是,将输入的连续字符串,拆分成一个一个的单词,为后面的语法分析打下基础,他不需要做任何逻辑。
-
语法分析是将词构建出语法树,需要涉及很多逻辑处理(必要的时候会进行运算,目的是简化语法树),词法分析算法有点类似进出栈算法。
-
在一种编程语言的编译器中,词法解析器可能需要许多不同类型的标记。例如:符号,数字,标识符,字符串,操作符等。想知道要从源文件中提取怎样的标记完全取决于编程语言本身。比如Java,有访问约束符号,
赋值符号,import指令,if指令switch指令等等都是我们通过词法分析器从一大串字符串中识别出来的(空格和换行也算一种字符,没毛病) -
一门编程语言涉及到哪些关键词组以及语法组织结构是一门严谨的艺术问题,也是编译器最复杂的问题,因为只有足够的设计词法和语法,才能做到用户友好,性能高效。
-
编译器的概念比较容易懂,但是深入理解编程语言则是一件长久和鼓噪的学问。
-
语法分析器,自身定义了很多模式,通过匹配词法的token,将其关联到具体的函数定义,变量定义,数学运算定义,并构建成AST输出。
-
int a = 3 和 a: int = 3 的区别在于解析器的处理上面。解析器决定了语法的外在形式是怎样的。
-
对于编译器核心概念就是词法分析器和语法解析器的概念了,算是一个门槛比较低的科普概念。
-
词法分析器和语法分析器算法经常会内置到IDE中,通过实时编译对程序进行检错。
-
最常见的语法解析器都是自上而下的递归降解式的去匹配语法模式,而语法模式通常用 EBNF 扩展的巴科斯-诺尔形式(Backus-Naur form)来定义。
-
我们可以通过阅读EBNF去理解某一款编程语言的语法解析器的业务逻辑。这样可以更快速的了解语法解析器的大概逻辑。
-
语法文件通常看起来像是树状的,由粗略到具体。比如像expr::binop 二进制表达式操作的语法树:
Root(模式名称 expr::binop) --> LHS(左参数term)-->Value1[常量12]
Root(模式名称 expr::binop) --> Operation((词法操作符 +/- ))
Root(模式名称 expr::binop) --> RHS(右参数term)-->Value2[常量3]
这个只是加减法二进制操作的模式AST。
其中 常量和词法操作符实在 词法分析器Lexer中定义。
其他的模式 expr::binop和左右参数term则是在语法分析器Parser中定义。
输入文本 input: "12+3"
词法分析结果 tokens: [Number(12), Plus, Number(3)]
语法分析结果 ast: BinOp(
Plus,
Number(
12,
),
Number(
3,
),
)
-
在编译器原理中,前端指代 Lexer和Parser,而后端指代接受AST的 Code generator 编译器的代码生成器和 evaluator 解释器的执行器,他们都是负责接收AST转化为汇编指令,比如GCC就是后端程序。
-
而中间端IR则是指优化器,他的操作对象通常是Token或者AST。
后端只关心它接收到的 AST。这意味着可以为几种不同的前端或者语言重用相同的后端。大名鼎鼎的 GNU Compiler Collection 就属于这种情况。
- 我们常说的后端程序会把AST变成目标文件.o,这种文件会被连接器变成执行文件。而我们的汇编器也算是一种编译器,它可以把汇编码变成.o目标文件。汇编器也是一个完整的编译器,它包含了Lexer和Parser的过程。
引用: https://zhuanlan.zhihu.com/p/53336801
更多相关材料:
http://craftinginterpreters.com/ – 指导你编写一个 C 和 Java 的解释器。
https://norasandler.com/2017/11/29/Write-a-Compiler.html – 大概是对我来说最有用的 “编写编译器” 的教程了