# 编译原理

::: tip 前言
**当你按下"运行"按钮，代码是怎么变成屏幕上的结果的？** 你写的每一行代码，计算机其实都"看不懂"——它只认识 0 和 1。编译器就是那个把人类语言翻译成机器语言的"翻译官"。理解编译原理，你就能理解报错信息从哪来、为什么有些语言快有些慢、以及代码优化的底层逻辑。
:::

**这篇文章会带你学什么？**

学完这章后，你将获得：

- **全局视野**：掌握从源代码到可执行程序的完整编译流水线
- **词法分析**：理解编译器如何把代码拆成一个个 Token
- **语法分析**：理解 AST（抽象语法树）的构建过程
- **AST 可视化**：直观看到代码的树形结构
- **语义分析与优化**：理解类型检查和代码优化的原理
- **优化技术实战**：掌握常量折叠、死代码消除等核心优化手段
- **执行模型**：区分编译型、解释型和 JIT 三种执行方式

| 章节 | 内容 | 核心概念 |
|-----|------|---------|
| **第 1 章** | 编译器是什么 | 翻译官类比、编译流水线 |
| **第 2 章** | 词法分析 | Token、词法规则 |
| **第 3 章** | 语法分析 | AST、语法树、优先级 |
| **第 4 章** | AST 可视化 | 交互式语法树、节点类型 |
| **第 5 章** | 语义分析与优化 | 类型检查、常量折叠、死代码消除 |
| **第 6 章** | 优化技术实战 | 函数内联、循环外提、常量传播 |
| **第 7 章** | 编译型 vs 解释型 vs JIT | 三种执行模型对比 |

---

## 0. 全景图：编译流水线概览

想象你是一个翻译官，要把一本中文小说翻译成英文。你不会一个字一个字地直译，而是：

1. **识别词语** — 把句子拆成一个个词（词法分析）
2. **理解句法** — 判断句子结构是否正确（语法分析）
3. **理解语义** — 确保意思通顺、没有矛盾（语义分析）
4. **润色优化** — 让译文更地道流畅（代码优化）
5. **输出译文** — 写出最终的英文版本（代码生成）

编译器做的事情完全一样，只不过它翻译的是编程语言。

<CompilerAnalogyDemo />

---

## 1. 编译器的六步流水线

编译器的工作可以分为六个阶段，像工厂流水线一样，每个阶段处理完交给下一个阶段。

<CompilerDemo />

::: tip 编译流水线
1. **词法分析（Lexical Analysis）**：把源代码拆成一个个 Token（单词）
2. **语法分析（Syntax Analysis）**：把 Token 组织成语法树（AST）
3. **语义分析（Semantic Analysis）**：检查类型是否正确、变量是否声明
4. **中间代码生成（IR Generation）**：生成与平台无关的中间表示
5. **代码优化（Optimization）**：让中间代码更高效
6. **代码生成（Code Generation）**：生成目标平台的机器码
:::

| 阶段 | 输入 | 输出 | 类比 |
|------|------|------|------|
| 词法分析 | 源代码字符流 | Token 流 | 把句子拆成单词 |
| 语法分析 | Token 流 | AST（语法树） | 分析句子结构 |
| 语义分析 | AST | 带类型的 AST | 检查意思是否通顺 |
| 中间代码 | 带类型的 AST | IR | 写出初稿 |
| 代码优化 | IR | 优化后的 IR | 润色删减 |
| 代码生成 | 优化后的 IR | 机器码 | 输出终稿 |

---

## 2. 词法分析：源代码的分词处理

词法分析是编译的第一步。编译器从左到右扫描源代码的每个字符，把它们组合成有意义的**Token（词法单元）**。

<LexerTokenDemo />

就像读英文句子时，你的大脑会自动把字母组合成单词一样，词法分析器把字符组合成 Token：

```
源代码: let x = 10 + 5;

Token 流:
[let]   → 关键字（语言保留字）
[x]     → 标识符（变量名）
[=]     → 运算符（赋值）
[10]    → 数字字面量
[+]     → 运算符（加法）
[5]     → 数字字面量
[;]     → 分隔符（语句结束）
```

::: tip Token 的五大类型
- **关键字**：语言保留的特殊单词，如 `let`、`if`、`return`、`function`
- **标识符**：程序员定义的名字，如变量名、函数名
- **字面量**：直接写在代码里的值，如数字 `42`、字符串 `"hello"`
- **运算符**：执行运算的符号，如 `+`、`-`、`=`、`===`
- **分隔符**：分隔代码结构的符号，如 `;`、`,`、`(`、`)`
:::

---

## 3. 语法分析：构建语法树（AST）

词法分析把代码拆成了 Token，但 Token 只是一个个孤立的"单词"。语法分析的任务是把这些 Token 按照语法规则组织成一棵**抽象语法树（Abstract Syntax Tree, AST）**——它反映了代码的结构和运算优先级。

```
表达式: 1 + 2 * 3

语法树:        为什么这样？
       +       因为 * 的优先级
      / \      高于 +，所以
     1   *     2 * 3 先结合
        / \    成为一个子树
       2   3
```

::: tip AST 的重要性
AST 是编译器的"核心数据结构"，后续的语义分析、优化、代码生成都基于它进行。现代开发工具也大量使用 AST：
- **ESLint**：解析代码为 AST，检查是否违反规则
- **Prettier**：解析为 AST 后重新格式化输出
- **Babel**：解析 AST → 转换 → 生成兼容代码
- **IDE 重构**：基于 AST 进行安全的变量重命名、函数提取
:::

| 语法结构 | Token 序列 | AST 节点 |
|---------|-----------|---------|
| 变量声明 | `let` `x` `=` `10` | VariableDeclaration → Identifier + Literal |
| 函数调用 | `add` `(` `1` `,` `2` `)` | CallExpression → Identifier + Arguments |
| 条件语句 | `if` `(` `a` `>` `b` `)` | IfStatement → BinaryExpression + Block |

---

## 4. AST 可视化：代码结构的直观呈现

上面我们用文字描述了 AST 的结构，但"看到"比"读到"更直观。下面的交互组件让你选择不同的表达式，实时观察它们的语法树长什么样。

<ASTVisualizerDemo />

通过可视化你会发现，AST 的核心规律其实很简单：

| 代码结构 | AST 根节点 | 子节点 |
|---------|-----------|-------|
| `1 + 2 * 3` | BinaryExpression (+) | 左: NumericLiteral(1)，右: BinaryExpression(*) |
| `let x = 10` | VariableDeclaration | VariableDeclarator → Identifier(x) + NumericLiteral(10) |
| `add(a, b)` | CallExpression | Identifier(add) + Arguments(a, b) |

::: tip AST 在日常开发中的应用
你可能没直接写过编译器，但你每天都在用基于 AST 的工具：
- **ESLint / Prettier**：解析代码为 AST，检查规则或重新格式化
- **Babel / SWC**：解析 AST → 转换语法 → 生成兼容代码
- **IDE 重构**：基于 AST 做安全的重命名、提取函数
- **Tree-shaking**：分析 AST 中的 import/export，删除未使用的代码
:::

---

## 5. 语义分析与代码优化

语法分析确保代码"结构正确"，但结构正确不代表"意思正确"。语义分析负责检查代码的含义是否合法，代码优化则让程序跑得更快。

<CompilationPracticeDemo />

### 5.1 语义分析与类型检查

| 检查内容 | 示例 | 结果 |
|---------|------|------|
| 类型检查 | `int x = "hello"` | ❌ 类型不匹配 |
| 作用域检查 | 使用未声明的变量 `y` | ❌ 变量不存在 |
| 类型推断 | `1 + 2.0` | ✅ 推断结果为 float |
| 参数检查 | `add(1, 2, 3)` 但函数只接受 2 个参数 | ❌ 参数数量不匹配 |

::: tip 你见过的报错，大多来自语义分析
- `TypeError: Cannot read properties of undefined` — 类型检查
- `ReferenceError: x is not defined` — 作用域检查
- `Expected 2 arguments, but got 3` — 参数检查
:::

### 5.2 代码优化：中间表示的等价变换

编译器在生成最终代码前，会对中间代码做各种优化。这些优化对程序员透明，但能显著提升性能。

| 优化技术 | 优化前 | 优化后 | 原理 |
|---------|-------|-------|------|
| 常量折叠 | `x = 10 + 5` | `x = 15` | 编译时直接算出结果 |
| 死代码消除 | `if (false) { ... }` | 直接删除 | 永远不会执行的代码 |
| 常量传播 | `x = 15; y = x * 2` | `y = 30` | 已知值直接替换 |
| 循环不变量外提 | 循环内重复计算 `len = arr.length` | 提到循环外 | 避免重复计算 |

---

## 6. 优化技术实战：编译器代码优化机制

上面我们提到了几种优化技术的名字，现在来深入看看编译器具体是怎么做的。下面的交互组件展示了 5 种最常见的编译器优化，你可以直观对比优化前后的代码差异。

<CodeOptimizationDemo />

现代编译器和 JIT 引擎（如 V8、GCC、LLVM）会自动应用数十种优化。作为开发者，你不需要手动做这些优化，但理解它们能帮你：

- **写出更容易被优化的代码**：比如用 `const` 而不是 `let`，编译器更容易做常量折叠
- **理解性能差异**：为什么小函数比大函数快？因为编译器能内联它们
- **避免"反优化"**：某些写法会阻止编译器优化，比如 `eval()` 和 `with`

| 优化技术 | 触发条件 | 性能影响 | 开发者能做什么 |
|---------|---------|---------|-------------|
| 常量折叠 | 表达式中全是常量 | 消除运行时计算 | 多用 const 声明 |
| 死代码消除 | 代码不可达或结果未使用 | 减小代码体积 | 及时清理无用代码 |
| 循环不变量外提 | 循环内有不变的计算 | 减少重复计算 | 手动提取也是好习惯 |
| 函数内联 | 小函数被频繁调用 | 消除调用开销 | 保持函数小而专注 |
| 常量传播 | 变量值在编译时可确定 | 整条计算链被消除 | 用常量代替魔法数字 |

---

## 7. 编译型 vs 解释型 vs JIT

代码写完后，有三种"翻译方式"让它运行起来。这三种方式各有优劣，直接决定了语言的性能特征和使用场景。

<CompileVsInterpretDemo />

| 维度 | 编译型 | 解释型 | JIT 即时编译 |
|------|-------|-------|------------|
| 过程 | 先全量编译成机器码，再执行 | 边读边执行，逐行翻译 | 先解释执行，热点代码再编译 |
| 运行速度 | 最快 | 最慢 | 中等（热点接近编译型） |
| 启动速度 | 慢（需要编译） | 快（直接运行） | 中等（需要预热） |
| 跨平台 | 需要重新编译 | 天然跨平台 | 跨平台 |
| 代表语言 | C, Rust, Go | Python, Ruby | JavaScript (V8), Java |

::: tip 为什么 JavaScript 这么快？
V8 引擎的 JIT 编译器会监测哪些代码被频繁执行（热点代码），然后把它们编译成高度优化的机器码。所以虽然 JavaScript 是"解释型语言"，但在 V8 中它的性能可以接近编译型语言。这也是 Node.js 能做服务端的底气。
:::

---

## 总结

编译原理不是只有编译器开发者才需要了解的知识。理解编译流程，能帮你更好地理解报错信息、选择合适的语言、写出更高效的代码。

回顾本章的关键要点：

1. **编译器是翻译官**：把人类可读的代码翻译成机器可执行的指令
2. **六步流水线**：词法分析 → 语法分析 → 语义分析 → 中间代码 → 优化 → 代码生成
3. **词法分析拆 Token**：把字符流拆成关键字、标识符、运算符等有意义的单元
4. **语法分析建 AST**：按语法规则把 Token 组织成树形结构，反映运算优先级
5. **语义分析保正确**：类型检查、作用域检查，你见过的大多数报错都来自这里
6. **编译器自动优化**：常量折叠、死代码消除、函数内联等技术让代码自动变快
7. **三种执行模型**：编译型最快、解释型最灵活、JIT 兼顾两者

## 延伸阅读

- [AST Explorer](https://astexplorer.net/) - 在线查看代码的 AST 结构
- [Crafting Interpreters](https://craftinginterpreters.com/) - 从零实现一门编程语言（免费在线书）
- [The Super Tiny Compiler](https://github.com/jamiebuilds/the-super-tiny-compiler) - 用 JavaScript 实现的超小编译器
- [V8 Blog](https://v8.dev/blog) - V8 引擎的 JIT 编译技术博客
- [LLVM 官网](https://llvm.org/) - 最流行的编译器基础设施
