编译器可视化

编译器识别字符片段,把词元组织成层级,遍历树,再生成机器可执行的程序。本组三课依次展示源码到 AST、AST 到字节码、栈虚拟机执行字节码。后两课共享 1 + 2 * 3,第一课用 12 + 3 * 4,让多位数的词法处理可见。

最后的虚拟机不是 CPU 或虚拟化管理器,而是语言虚拟机:取操作码,修改栈,再继续。源码中的 + 进入树结构,ADD 则执行两次弹出和一次压入。若生成代码时把运算符放在操作数前,运行就会栈下溢。

错误也有阶段归属。12 + @ 4 无法形成完整词元序列;12 + * 4 可以分词,却在 primary() 解析失败;先执行 ADD 的字节码格式可读,但运行时缺少操作数。

课程比较

课程核心问题变化失败点
词法与语法分析优先级从哪里来字符片段成为平面词元,语法调用建立层级@ 使词法失败,操作数位置的 * 使语法失败
AST → 代码为何 1 + 2 * 3 先写 MUL后序遍历,Fold 先折叠常量名称阻止上层折叠,不妨碍另一独立子树
虚拟机指令为何必须按该顺序操作码修改栈,热循环触发编译运算符优先导致下溢,冷循环不触发 JIT

解析器输出与代码生成输入使用同样的运算符层级。代码生成右侧的 IR 是正在写出的程序,虚拟机右侧的 Stack 是程序运行时使用的数据栈,两者不可混读。

建议学习顺序

先在词法与语法分析中运行 12 + 3 * 4,看 1、2 如何留在同一个词素内。再跟踪调用:expr 等待 +,term 先完成 *。切换括号场景,解析器代码不变,Add 成为内部节点,Mul 成为根。最后分别尝试词法、语法错误,区分“能分词”和“语法有效”。

AST → 代码默认以 Naive 处理 1 + 2 * 3,MUL 写出时 + 仍在等待。保持树不变切到 Fold,五条指令变为 LOAD_CONST 7。再用 x + 2 * 3:乘法仍可折叠,加法因未知名称不能折叠。

虚拟机默认用解释器运行同一表达式。三次压入后,MUL 计算 2 * 3 → 6,ADD 再计算 1 + 6,说明代码生成为什么使用后序。随后加载 ADD first,实际触发 Underflow。

理解栈纪律后再看可选的 JIT。倒计时先解释两轮,达到热度后编译,指令逐条取出的显示停止。冷循环的热度低于阈值,不会编译。

观察重点

  • 区分源码顺序、词元顺序、树层级和指令顺序。源码先出现 +,树先嵌套 *,指令先执行 MUL。
  • 合法词元不一定出现在合法位置。12 + * 4 中 STAR 可被识别,却不能开始 primary()。
  • 看栈高度,不只看顶值。默认峰值为 3,对应首个运算符执行前等待的三个叶子。
  • 空栈本身不是错误。初始化可以为空,只有运算符请求缺失操作数才下溢。
  • JIT 后剩余计算仍会执行。一个原生步骤表示不再逐条取解释器指令,不代表 O(1) 工作或直接跳过计算。

全部编译器可视化课程

改变表达式或执行引擎,逐步对照 Python 代码。词法步骤故意不点亮解析器代码面板,字符到词元的证据显示在画布上。