1. 编译的全过程

原文地址:https://blog.csdn.net/weixin_53009585/article/details/129236682

1.1. 概述

编译就是将源代码变成目标代码的过程。

compile-process.png

1.2. 词法分析(Lexical Analysis)

词法分析将输入的字符串以单词符号的形式进行输出。

lexical-rules.jpeg

lexical-token-type.jpeg

程序里的单词叫 Token,Token 的类型包括关键字、标识符、字面量、操作符等。

词法分析就是将字符串转换成 Token 序列的过程。

1.3. 语法分析(Syntactic Analysis)

语法分析阶段将 Token 序列转换成体现语法规则的、树状数据结构,即抽象语法树 AST。AST 反应程序的语法结构,大多数编译器和解释器都使用 AST 作为源代码的内部表示。

1.4. 语义分析

语义分析阶段的任务是理解语义,语句要做什么。比如 + 执行加法、= 执行赋值、for 结构实现循环、if 结构实现判断。

语义分析阶段将执行上下文分析,包括引用消解、类型分析与检查等。

比如上面的 C 代码,经过语义分析获得的信息(引用消解信息、类型信息),可以在 AST 上进行标注,形成“带有标注的语法树”,使编译器能更好地理解程序的语义。

ast-with-annotation.png

这些上下文信息也将被存入“符号表”结构中,便于各阶段查询上下文信息。

符号表是有层次的结构:只需逐级向上查找就能找到变量、函数等信息(作用域、类型等)。

symbol-table-example.png

接下来可以解释执行,实现一门解释型语言。

提示

编译型语言需要生成目标代码,而解释型语言只需要解释器执行语义即可。

在语法分析后得到 AST,语义分析后得到“带标注的 AST”和符号表后,可以深度优先遍历 AST,并且边遍历边执行节点的语义规则。整个遍历过程就是执行代码的过程。

比如执行下面的语义:

1.5. 中间代码生成

在编译前端完成后,编译器可以直接解释执行,也可以生成目标代码。对于不同的 CPU 架构,还需要生成不同的汇编代码,如果对每种汇编代码都做优化,那么很繁琐。所以增加一个环节 - 生成中间代码 IR,然后统一优化 IR,再将 IR 转换成目标代码。

IR 有两个用途:

1.6. 代码优化(Optimization)

1.6.1. 为什么需要进行代码优化?
  1. 源语言和目标语言有差异

  2. 程序员编写的代码不是最优的,编译器帮助纠正

1.6.2. 优化器分类

optimization-catagory.png

基于 IR 编写优化算法的好处是可以将大部分优化算法写成与具体 CPU 架构无关的形式,从而降低编译器适配不同 CPU 的工作量。并且 LLVM 之类的工具可以使多种语言的前端生成相同的 IR,这样可以复用中/后端的程序。

1.7. 目标代码生成

目标代码生成就是生成虚拟机执行的字节码,或者操作系统执行的汇编代码。

目标代码生成的过程就是将 IR 逐个翻译成想要的汇编代码。目标代码生成阶段的任务包括:

目标代码生成后,整个编译过程完成。

compile-process-2.png


2. Go AST

原文地址:https://zhuanlan.zhihu.com/p/380421057

2.1. 概述

Go 官方提供如下包,实现 Go 代码的语法树分析:

抽象语法树由节点(Node)构成,从源代码中(go/ast/ast.go)可以看出,Go AST 主要由三种节点构成:分别是表达式和类型节点(Expressions and type nodes)、语句节点(Statement nodes)和声明节点(Declaration nodes)。所有节点都包含标识其在源代码中开头和结尾位置的信息。

从代码中可以看到,所有 AST Node 都实现 ast.Node 接口,它返回 Node 的位置信息。

另外,还有 3 个主要接口继承 ast.Node

go/ast 中定义的全部节点类型如下图所示:

go-ast-nodes.png

2.2. 代码示例

2.2.1. 示例 1 - 查看 AST

执行:

可以看到:

2.2.2. 示例 2 - 遍历 AST

Inspect 以深度优先的方式遍历 AST:以调用 f(node) 开始;node 必须不为 nil。如果 f 返回 true,那么 Inspect 为该节点的每个非 nil 孩子递归地调用 f,后面跟着一个 f(nil) 调用。

上述代码中的 AST 的大体结构如下:

ast-example.jpg


3. 进阶示例

3.1. 为接口生成默认实现


4. go generate 命令

go generate 是 Go 语言提供的命令,用于自动化生成代码。通过在代码中添加特定的注释,可以触发 go generate 命令执行预定义的代码生成操作。这样可以简化重复性的任务,比如生成接口的实现、生成模拟数据、生成文档等。

go generate 的使用步骤如下:

  1. 在源代码中添加 //go:generate 注释,指定要执行的命令。该注释必须在独立的行上

  2. 运行 go generate 命令,它将扫描代码中的 //go:generate 注释,并且执行其中指定的命令

以下是展示如何使用 go generate 的简单示例:

注意事项: