编译器到底在干嘛?从字符流到机器码,顺手拆一下 V 编译器的流水线

很多人把"编译原理"当成劝退课。但把它拆成一条流水线后,你会发现它只做一件事:把源代码一层层降维,翻译成等价的另一种程序 。 本文用一句 x = 1 + 2 串完整个流程,最后对照 V 编译器的真实目录,看看这套理论是怎么落地的。

一、先记住一句话

编译的本质是 分阶段降级抽象:把给人看的高级文本,一步步变成给机器执行的低级指令。

每个阶段只操心一件事,做完就把结果交给下一棒。这样既降低了单次思考的复杂度,也让中间产物可以复用------N 种语言的前端可以共用同一个后端,一个前端也能对接多个后端(x86 / ARM / WASM)。

二、六个阶段:让 x = 1 + 2 走一遍

阶段 输入 → 输出 干什么
词法分析 字符流 → Token 流 扫掉空格和注释,识别出 x=1+2
语法分析 Token 流 → AST 按语法规则搭树;括号不匹配、缺分号在这里报错
语义分析 AST → 带类型的 AST x 声明过没、类型对不对、1+2 能不能赋给 x
中间表示 / 优化 AST → IR 常量折叠成 x = 3、删掉没用到的函数、内联
代码生成 IR → 目标代码 生成机器码、C 代码、字节码......
汇编链接 目标代码 → 可执行文件 地址重定位、链接库

三、每一步具体长什么样

1. 词法分析(Lexical Analysis)

编译器拿到的第一手材料只是一串字符。词法分析器(Scanner)把它切成有含义的最小单元 Token,顺便扔掉空格、换行和注释:

text 复制代码
x = 1 + 2
   ↓
IDENT(x)   ASSIGN(=)   INT(1)   PLUS(+)   INT(2)

2. 语法分析(Parsing)

Token 是平的,程序是嵌套的。语法分析器(Parser)按语法规则把 Token 组装成一棵树------AST(抽象语法树)。这棵树直接表达了"谁先算、谁包着谁":

text 复制代码
Assign
├── target: x
└── value: Binary
            ├── left:  1
            ├── op:    +
            └── right: 2

注意:括号、分号这类纯语法问题在这一层就暴露了,和"类型对不对"完全解耦。

3. 语义分析(Semantic Analysis)

语法对了不代表意思对。这一层负责:变量有没有声明(查符号表)、类型是否匹配、1 + 2 的结果类型是什么、重载该选哪一个。产出是带类型标注的 AST

4. 中间表示与优化(IR + Optimization)

AST 仍然是语言相关的,换成更贴近机器的 IR(中间表示)才好做优化:常量折叠、死代码消除、循环不变量外提、函数内联......这些优化与源语言、目标平台都无关,所以能写一次到处复用。

5-6. 代码生成与链接

最后把 IR 降级成目标代码,再交给汇编器和链接器变成可执行文件。这里最硬的活是指令选择寄存器分配

四、对照 V 编译器:理论怎么落地

V 语言的编译由 v.builder 编排,入口在 cmd/v/v.v,流水线非常标准:

text 复制代码
v.scanner → v.parser → v.checker → v.transformer → v.markused → v.gen.c
阶段 模块 职责
词法 vlib/v/scanner/ 字符 → Token
语法 vlib/v/parser/ Token → AST(AST 定义在 vlib/v/ast/
语义 vlib/v/checker/ 类型检查、符号解析
优化 vlib/v/transformer/ 常量折叠、表达式简化
裁剪 vlib/v/markused/ 死代码消除,只保留真正用到的函数
后端 vlib/v/gen/c/ 生成 C 源码

V 的独门选择:转译到 C

V 不做传统意义上的后端(指令选择、寄存器分配都不做),而是把 AST 翻译成 C 代码,再交给系统里的 gcc / clang / tcc 去汇编和链接。

好处很直接:

  • 编译极快:重活外包给 C 编译器
  • 可移植性白嫖:C 能跑的地方 V 就能跑,生态直接复用
  • 调试直观 :加 -keepc 就能看到人类可读的 C 代码

代价也很实在:

  • 语义受 C 约束,某些语言特性不好表达
  • 必须依赖外部 C 编译器

想亲眼看这条流水线?在本地 V 仓库里两条命令就有答案:

bash 复制代码
./v -keepc -o ./vnew cmd/v    # 构建编译器,保留生成的 C
./vnew -keepc run file.v      # 看你的代码变成了什么 C

除了 C,V 还有 gen/js/gen/native/(直接产出机器码 ELF / Mach-O)、gen/wasm/,但 C 后端最成熟。

五、一句话总结

编译 = 分阶段降级抽象。 前端理解人写了什么,中端把它改得更好,后端把它翻译成机器能跑的东西。搞懂这条流水线,再去看任何一个编译器的源码,你都能立刻定位到"我现在在哪一层"。

相关推荐
sickworm陈浩7 天前
日常修改,3秒生效:腾讯音乐 Android 秒编方案 Jugg 开源
android·编译原理·编译器
DLite11 天前
开源一个静态类型语言——NLang
c++·编译器·nlang
_瑞17 天前
APM_函数调用栈展开
ios·编译原理
_瑞18 天前
读懂 iOS 的线程调用栈
ios·编译原理·汇编语言
Patrick_Wilson1 个月前
sccache 用在 Rust 上为什么常「不省编译」:原理、限制与 Windows 接入
ci/cd·rust·编译器
ZZH_AI项目交付1 个月前
Apple Silicon 模拟器遇到旧版 MLKit:一次完整的依赖排查
ios·app·编译器
whi2 个月前
V 编译器 v3 ownership 模式:编译与使用指南
后端·编译器
DogDaoDao2 个月前
【GitHub】 LLVM Project 深度解析:现代编译器基础设施的基石
java·c++·python·程序员·github·编译器·llvm
2601_951645743 个月前
C语言环境搭建指南
c语言·编译器·开发环境·helloworld·集成开发环境