Ziglings 笔记 72: 编译期解析器 (Inline While)
我,编译器
这是 Ziglings 语法基础部分的最后一关(练习 72)。我们以一个极其硬核的概念结束:在编译期写一个解释器。
在普通语言中,解析字符串通常发生在运行时,消耗 CPU 周期。但在 Zig 中,我们可以利用 comptime 和 inline while,在编译阶段就“吃掉”字符串,吐出纯粹的机器指令。
挑战:字符串变机器码
我们有一串指令:"+3 *5 -2 *2"。
我们需要计算它的结果。
要求:在生成的最终程序中,不能包含这个字符串,也不能包含解析它的循环。程序运行起来必须像是直接写了 value += 3; value *= 5... 一样快。
解决方案
我们需要使用 inline while 来遍历字符串,步长为 3(因为格式是 “符号 数字 空格”):
const print = @import("std").debug.print;
pub fn main() void {
const instructions = "+3 *5 -2 *2";
var value: u32 = 0;
// 编译期索引
comptime var i = 0;
// 核心逻辑:inline while
// 编译器会执行这个循环,直到 i >= instructions.len
// 每次迭代步进 3 个字符
inline while (i < instructions.len) : (i += 3) {
// 1. 解析数字 (编译期计算)
// '3' - '0' = 3
const digit = instructions[i + 1] - '0';
// 2. 解析操作符并生成代码 (编译期展开)
switch (instructions[i]) {
'+' => value += digit,
'-' => value -= digit,
'*' => value *= digit,
else => unreachable,
}
// 注意:instructions 字符串和变量 i 在编译完成后就消失了
}
// 最终生成的代码逻辑等价于:
// value += 3;
// value *= 5;
// value -= 2;
// value *= 2;
// 初始 0 + 3 = 3 -> 3 * 5 = 15 -> 15 - 2 = 13 -> 13 * 2 = 26
print("{}\n", .{value});
}
核心知识点总结
1. 循环展开 (Loop Unrolling)
inline while 极其强大。在这个例子中,它根据字符串长度自动展开了代码。
如果 instructions 有 1000 个操作,编译器就会生成 1000 行对应的汇编指令。这消除了运行时的循环跳转开销(Branch Prediction Overhead)。
2. 嵌入式领域的应用
这种技术在嵌入式开发中非常有用。例如,你可以把复杂的寄存器配置写在一个易读的字符串或 JSON 文件中,然后用 Zig 在编译期把它解析成一连串高效的内存写入指令。
3. 完结撒花 🎉
至此,我们已经完成了 Ziglings 中关于语言特性的所有核心练习! 我们从 Hello World 开始,一路经过了:
- 数组与切片
- 指针与内存
- 结构体与联合体
- 错误处理与可选项
- Comptime 元编程 (最终 Boss)
你现在已经具备了阅读和编写 Zig 代码的核心能力。
后续预告:虽然基础语法学完了,但 Zig 的标准库(Standard Library)是一座巨大的宝藏。接下来的旅程,我们将离开语法练习,通过实际构建项目来探索 std.fs (文件), std.http (网络), std.Thread (并发) 以及著名的 Zig 构建系统。