第 12 章拆解 — 手搓一门语言:zcalc 解释器
手搓一门语言:zcalc 解释器
这一章讲什么: 编程语言是怎么被造出来的——不是原理课,是手工课。 先写一页 EBNF 文法当蓝图,然后三段流水线:词法器(字节 → token)、 解析器(token → 语法树)、求值器(树 → 值),最后包一个 REPL。 书的定位说得很准:这是系统编程书的合格收官,因为它把前十一章的每门手艺 (流处理、显式内存、哈希表、递归)都用在一道题上1。
主线例子:书上自带的三角形程序,从源码字符一路走到返回值
Large,每个阶段都有具体中间产物可对账。
先交代来源(不影响阅读): 原书第 13 章「Unlock Your Exclusive Benefits」是 Packt 的购书福利解锁说明(凭发票到 packtpub.com/unlock 兑换免费内容),不含技术内容,本篇仅作登记,不拆解2。
1. 顶层全景:三段流水线 + 四个源文件
源码字节流 ──lexer.zig──▶ token 序列 ──parser.zig──▶ AST ──evaluator.zig──▶ 运行时值
"let a = 10;" .Let .Ident a Stmt[0]=Let{a,10} env{a:10}
.Assign .Int 10 → Void
.SemiColon → ReturnWrapper
→ 40
repl.zig = 三段合并 + 交互外壳(文件模式批跑 / 无参数进 REPL)
图说:lexer/parser/evaluator 三个文件各自自包含、各带 main()——刻意的冗余,
让每个阶段可以独立编译、独立调试、独立验证;repl.zig 才是最终成品。[^3]2. 文法先行的道理:EBNF 当蓝图
造语言第一步不是写代码,是写文法——用 EBNF(扩展巴科斯-瑙尔范式)精确描述什么字符串合法。记法当场学会:= 左边是规则名,右边是定义;| 分隔候选('true' | 'false' 二选一);括号分组;? 表示可选(0 或 1 次);* 零次或多次;引号里的是终结符(源码里真实出现的字面字符),其余是规则名3。
zcalc 的文法里有两处设计值得点名:
- 语句与表达式严格分家:语句(let/赋值/return/if)是控制流的骨架,执行动作、改环境,但不产生值;表达式是计算引擎,必须能归约成一个原始值4。语言里
let声明和=赋值分开不是啰嗦——环境因此能明确区分「引入新标识符」和「改已有状态」,未定义变量这类运行时错误才抓得住5。 - if 的分支强制花括号:文法层面消灭了臭名昭著的悬垂 else(dangling-else)歧义,作用域边界对解析器和程序员都刚性清晰6。
分层表达式天然编码优先级:Equality → Relational → Addition → Multiplication → Primary 一层层下降,乘除比加减绑得紧,比较最低;树的形状本身就是运算顺序,求值器永远不用做任何优先级排序7。括号在 Primary 层,给程序员手动推翻内置优先级的机会8。
3. 词法器:两个字节的游标
程序一开始只是无差别的字节流。词法(lexing) 把原始字符分组成有意义的词(token)——空格丢弃、注释跳过、数字和关键字识别,给下一阶段一个干净可预期的词汇序列9。
Lexer 的状态只有三个:输入切片、position(正看的字符)、read_position(下一个字符),外加缓存当前字符的 ch。双指针是词法器的心脏:消费当前字符的同时能偷看下一个——区分 =(Assign)和 ==(Eq)全靠这一眼10。哨兵设计:ch 用 0 表示输入结束,干净映射到 .Eof token,readChar() 里零特判11。
nextToken() 的主循环:skipWhitespace() 后按 ch 分派;双字符运算符先 peekChar() 再决定;标识符/数字读出切片(窗口直指原输入缓冲,词法器零堆分配)后提前返回——因为辅助函数已把游标停在了词素外第一个字符,不需要尾部 readChar()12。关键字识别抽成纯函数 lookupIdent():四次等值比较,全不中就是用户标识符——主分派因此永远不用懂关键字13。
字符串是唯一的分配点:读到 \ 就吞掉下一字节做转义翻译(\n→换行、" 和 \ 按字面),反斜杠后的字节永远作为转义的一部分被消费,所以转义的反斜杠能正确保护后面的引号,不干扰终止符搜索;EOF 先到时回报 error.UnterminatedString,而不是把到 EOF 为止的一切静默当字符串14。
实测(math.small):四行源码出 22 个 token,从 .Let "let" 到 .Eof "" 一览无余——给语言加新语法时,这个输出就是解析器将收到的输入,是最好的诊断工具15。
4. 解析器:递归下降与那棵树的形状
token 序列是平的,平就没有层级——它表达不了「第三行的乘法必须先于加法算」16。解析就是把文法强加到平序列上,建起抽象语法树(AST);递归下降解析器把每条文法规则直接翻译成一个函数调用,读文法就是在读解析器的执行流17。
两个类型撑起全部节点:Stmt(let/assign/return/expression/if 五变体)与 Expr(整数字面、字符串、标识符、Binary 四变体)18。Binary 的两个孩子必须是堆上指针——Zig 的标记联合不能直接内嵌递归类型,编译器算不出大小;allocator.create(Expr) 两次,孩子只以 *Expr 被知晓,递归尺寸依赖就此斩断19。所有 AST 内存来自进程 arena,退出一次释放——所以全章没有 deinit;REPL 整个会话共用一个 arena 不复位,内存随会话增长,学习可接受,长命解释器不行20。
解析器持两个 token 槽:cur 和 peek(词法器双指针的同款设计),init 里连调两次 nextToken() 预装21。分派里最有味道的是 .Ident 分支:一次 look-ahead——peek 是 .Assign 就是赋值语句,否则是表达式语句;「这是消解该歧义所需的最小上下文,无需回溯」22。parseLetStatement 进三步(expectPeek 到标识符、过 =、落到值表达式的第一个 token),可选分号用条件 peek 而非强制——对省略更宽容23。parseBlock 与 parseProgram 结构相同,只差终止条件(.RBrace 对 .Eof),自然可组合24。
表达式层的模式四条函数一个模子:parseAddition 先取 parseMultiplication() 当左操作数,while peek 是 +/- 就循环——左结合由此而来:左操作数在返回前累积了 successive 右端;优先级由调用链保证(Equality 最低、Primary 最高),等树建完,高优先级操作已坐得更深、被求值器先算25。
实测 math.small:四语句,第三条打印成 Binary{...}——树的形状记录了 a*2 与 b*4 是两个独立乘法、它们的结果才是加法的两个操作数。这个由解析器的优先级分层烙进树形的结构事实,正是求值器需要的:它从上到下走一遍,正确的运算顺序从节点所在的深度自然涌现26。
5. 求值器:环境、真值表与 ReturnWrapper
运行时值四变体外加一个秘密成员:Integer/String/Boolean/Void,以及 ReturnWrapper——不是用户可见类型,是控制流信号:遇到 return,把值装箱,沿每一次递归调用向上传播,直到最外层循环拆箱交出最终结果27。程序的内存是包着 StringHashMap 的 Environment,分配器存在环境内部,ReturnWrapper 的堆分配和字符串拼接都从这同一个 arena 取,不必给每个求值函数单传分配器28。
eval() 按语句变体分派:let/assign 都是「求值表达式 → 存环境」,唯一差别是映射键(解析期已验证);return 装箱;if 查 isTruthy() 后委托给 consequence 或可选的 alternative29。evalBlock 一遇 ReturnWrapper 立即短路——这就是 return 能退出整个外层块而不只是当前语句的机制30。真值规则:布尔直查、整数非零为真、其余一切(含字符串与 Void)默认真——简单约定,条件求值可预期31。
表达式求值里,Identifier 查不到就打印 Runtime Error: Identifier not found 并返回错误——运行时错误,不是解析期32。Binary 全在这一层(dispatch):双整数全运算符可用,@divTrunc 截断除法;字符串唯一接受的运算符是 +(拼接),其余组合一律 error.TypeMismatch33。
书对求值器局限的坦白是全章最实用的一页,三个坑都摆明:
- 布尔不能当操作数:
true == false甚至(1 < 2) == (3 < 4)都是类型错误,不是你以为的 true——比较产生布尔,但求值器没有任何分支接受布尔当操作数34。 - 字符串不对称:
"foo" + "bar"成功得 foobar,"foo" == "foo"反而 TypeMismatch(没有字符串比较分支)35。 10 / 0不报错,直接 panic——截断除法不查零除数。记住任意「运算符 × 操作数」组合只有三种结局:算出值、TypeMismatch、除零硬崩36。
6. 主线例子:三角形程序走全程
书上用一段脚本串起三阶段,这里把它走到底(中间产物均可对账)37:
源码: let base = 10; let height = 5;
let area = (base * height) / 2;
if (area > 20) { return "Large"; } else { return "Small"; }
第 1 阶段 lexer: 逐字符 → .Let .Ident(base) .Assign .Int(10) .SemiColon …
空白全丢,关键字与标识符分家
第 2 阶段 parser: 三句 Let + 一棵 If 树;
括号让乘法在递归中自成一树,除法节点以它为左孩子——
(base*height)/2 的形状由文法烙定,不是求值器排出来的
第 3 阶段 evaluator:
env ← {base:10, height:5}
area: 查 base 得 10 ×2 前先看树——左子树 a*b? 不对,本例是
(10×5)=50,50/2=25 → env ← {…, area:25}
if: 25 > 20 为真 → 进 consequence,忽略 alternative
return "Large" → 装箱 ReturnWrapper → 逐层上冒 → 拆箱 → 输出 Large图说:第二个例子 math.small(let c = a * 2 + b * 4; return c;)得 40——左子树 a*2=20,右子树 b*4=20,根 + 合成 40,返回值装箱再拆箱打印38。
7. REPL:把三段粘成一个工具
三段各自可跑之后,缺的是胶水:repl.zig 合并三阶段为单一编译单元,按启动时有无文件参数选模式——有参数批跑脚本,无参数进交互,零配置开关39。两个构件:printVal() 按值变体格式化输出,ReturnWrapper 递归解包——无论 return 套多深,打印的永远是拆箱后的内容40;readLine() 逐字节读到换行/EOF,完全空的输入返回 null 表示会话结束41。
REPL 模式的关键设计:一个环境贯穿整个会话——第一行声明的变量对之后每一行都可见,这才是交互感的来源42。文件模式解析失败打 Parsing Error 干净退出,不抛栈43。
7.1 REPL 的两种模式与退出语义
repl.zig 一个文件两种人格:带文件名参数走批处理(读文件、解析、逐句求值,遇 ReturnWrapper 打印拆箱值立即退出——与批处理求值器语义完全对齐)44;无参数进交互(打印提示符、读一行、trim 后查退出命令)45。readLine 逐字节读,完全空的输入返回 null 表示会话结束;REPL 用 arena,行与行之间不复位——变量跨行存活(环境贯穿会话)正是交互感的来源,代价是内存随会话增长46。
7.2 printVal 的递归解包
printVal() 按值变体格式化:整数、带引号字符串、布尔各一行,Void 静默;ReturnWrapper 递归解包内层值——无论 return 套了多少层,打印的永远是最终内容47。
8. 作者的判断与证据
- 判断: 树遍历求值(tree-walking)是第一种解释器的正确选择——递归访问 AST 节点、以调用栈为隐式求值栈,最简单最易读;生产语言通常再进一步:把 AST 编译成字节码、跑在带显式值栈的虚拟机上——分离编译期与执行期、开启树遍历做不了或做不好的优化、对计算密集程序快得多48。这是路线图陈述,两种都未在本章实现字节机。
- 判断: 分段自包含(冗余的 lexer/parser/evaluator)让每阶段可独立验证,「隔离正是它们好造、好想的原因」49。证据:三个阶段各有可跑的诊断输出。
- 诚实清单: 布尔操作数、字符串比较、除零三个坑全部明码标在正文而非脚注343536;REPL 的 arena 不复位导致内存随会话增长,明说不适合长命解释器20。
9. 边界与局限
- zcalc 无函数、无复合数据结构、无浮点;return 暗示了「带值退出上下文」的架构,但函数定义不在文法里50。
- 错误处理分两期:解析期(语法错)与运行期(未定义变量/类型不匹配),没有静态类型检查——
10 + "x"要到执行才炸。 - 整数字面只有连续数字,无负数字面(
-是一元还是二元都没处理,文法里没有一元运算)。 - 字符串转义只此一家
\",\n在字符串里是两个字面字节\和n——扩展留给读者,readString()是唯一要改的地方51。 - 解释器无性能数字(连基准都没有);树遍历对计算密集程序慢是路线图里的断言,本章未量化。
10. 可带走的
- 先写文法再写代码:EBNF 里每条规则 = 解析器里一个函数,读文法就是读执行流。
?可选、*重复、|候选、引号终结符——四符号读懂任何 EBNF。- 语句(不产值、控流)与表达式(归约成值)分家,环境才能抓未定义变量。
- 词法器双指针:消费当前、偷看下一;
ch=0哨兵免 EOF 特判。 - token 的 literal 是指进原缓冲的窗口——词法器零分配。
- 递归类型进联合必须堆指针:
create两次斩断尺寸依赖。 - 优先级不用算:分层下降函数链把优先级烙进树的深度;
while累积左操作数 = 左结合。 - 表达式语句 vs 赋值:一次 look-ahead 是消歧所需的最小上下文。
- return 用包装类型当控制流信号,遇箱即短路——块级退出就这么简单。
- 每阶段独立可跑是解释器开发的正确节奏:lexer 看 token、parser 看树形、evaluator 看结果。
- REPL 的「活」来自跨行持久的环境。
11. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 收官定位 | Chapter 12: Developing a Domain-Specific Language | text/138-ch12-chapter-12-developing-a-domain-specific-language.txt:35(搜「not a departure」) |
| EBNF 记法 | The EBNF grammar of zcalc | text/139-fm-the-ebnf-grammar-of-zcalc.txt:62(搜「straightforward」) |
| 语句表达式分家 | The EBNF grammar of zcalc | text/139-fm-the-ebnf-grammar-of-zcalc.txt:238(搜「separation」) |
| 悬垂 else 的消灭 | The EBNF grammar of zcalc | text/139-fm-the-ebnf-grammar-of-zcalc.txt:138(搜「dangling-else」) |
| 分层编码优先级 | The EBNF grammar of zcalc | text/139-fm-the-ebnf-grammar-of-zcalc.txt:260(搜「stratified」) |
| 三文件冗余设计 | The EBNF grammar of zcalc | text/139-fm-the-ebnf-grammar-of-zcalc.txt:427(搜「isolation」) |
| 词法器的角色 | Lexing: breaking strings into tokens | text/140-fm-lexing-breaking-strings-into-tokens.txt:25(搜「specialized filter」) |
| 双指针心脏 | Lexing: breaking strings into tokens | text/140-fm-lexing-breaking-strings-into-tokens.txt:82(搜「two-pointer」) |
| 字符串转义 | Lexing: breaking strings into tokens | text/140-fm-lexing-breaking-strings-into-tokens.txt:250(搜「protects」) |
| token 流诊断 | Lexing: breaking strings into tokens | text/140-fm-lexing-breaking-strings-into-tokens.txt:452(搜「diagnostic」) |
| 平序列无层级 | Lexing: breaking strings into tokens | text/140-fm-lexing-breaking-strings-into-tokens.txt:504(搜「no hierarchy」) |
| 递归下降=文法翻译 | Parsing: abstract syntax trees (ASTs) and recursion | text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:23(搜「function calls」) |
| 递归类型堆指针 | Parsing: abstract syntax trees (ASTs) and recursion | text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:96(搜「cannot directly embed」) |
| arena 与无 deinit | Parsing: abstract syntax trees (ASTs) and recursion | text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:146(搜「single allocator」) |
| look-ahead 消歧 | Parsing: abstract syntax trees (ASTs) and recursion | text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:280(搜「minimum context」) |
| 左结合的 while | Parsing: abstract syntax trees (ASTs) and recursion | text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:416(搜「left-associativity」) |
| 树形即运算顺序 | Parsing: abstract syntax trees (ASTs) and recursion | text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:530(搜「baked」) |
| ReturnWrapper 控制流 | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:25(搜「control-flow」) |
| evalBlock 短路 | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:152(搜「short-circuits」) |
| 真值规则 | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:175(搜「truth」) |
| 布尔操作数之坑 | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:329(搜「surprising gap」) |
| 除零 panic | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:385(搜「panics」) |
| 树遍历 vs 字节码 | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:399(搜「tree-walking」) |
| math.small 得 40 | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:425(搜「40」) |
| REPL 持久环境 | Evaluation: tree-walking execution | text/142-fm-evaluation-tree-walking-execution.txt:660(搜「persisting」) |
| 第 13 章=营销页 | Chapter 13: Unlock Your Exclusive Benefits | text/143-ch13-chapter-13-unlock-your-exclusive-benefits.txt:5(搜「exclusive benefits」) |
本篇拆解依据原书第 12 章(含各小节);第 13 章为营销页,仅登记。每条出处可用 node scripts/book-verify.mjs systems-programming-with-zig 回核。
Footnotes
-
出处:「Chapter 12: Developing a Domain-Specific Language」第 35 段(
text/138-ch12-chapter-12-developing-a-domain-specific-language.txt:35,搜「not a departure」)。 ↩ -
出处:「Chapter 13: Unlock Your Exclusive Benefits」第 5 段(
text/143-ch13-chapter-13-unlock-your-exclusive-benefits.txt:5,搜「exclusive benefits」)。该章为 Packt 购书福利解锁指引(凭发票访问 packtpub.com/unlock),无技术内容,如实登记、不作拆解。 ↩ -
出处:「The EBNF grammar of zcalc」第 62 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:62,搜「straightforward」)。 ↩ -
出处:「The EBNF grammar of zcalc」第 238 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:238,搜「separation」)。 ↩ -
出处:「The EBNF grammar of zcalc」第 126 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:126,搜「crucial distinction」)。 ↩ -
出处:「The EBNF grammar of zcalc」第 138 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:138,搜「dangling-else」)。 ↩ -
出处:「The EBNF grammar of zcalc」第 260 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:260,搜「stratified」)与第 264 段(text/139-fm-the-ebnf-grammar-of-zcalc.txt:264,搜「physical shape」)。 ↩ -
出处:「The EBNF grammar of zcalc」第 210 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:210,搜「manually override」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 9 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:9,搜「lexing」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 82 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:82,搜「two-pointer」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 131 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:131,搜「sentinel」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 189 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:189,搜「window」)与第 422 段(text/140-fm-lexing-breaking-strings-into-tokens.txt:422,搜「does not belong」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 294 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:294,搜「pure function」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 250 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:250,搜「protects」)与第 288 段(text/140-fm-lexing-breaking-strings-into-tokens.txt:288,搜「UnterminatedString」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 452 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:452,搜「diagnostic」)。 ↩ -
出处:「Lexing: breaking strings into tokens」第 504 段(
text/140-fm-lexing-breaking-strings-into-tokens.txt:504,搜「no hierarchy」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 23 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:23,搜「function calls」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 47 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:47,搜「five variants」)与第 73 段(text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:73,搜「Binary node」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 96 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:96,搜「cannot directly embed」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 146 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:146,搜「single allocator」)与第 160 段(text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:160,搜「grows」)。 ↩ ↩2 -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 184 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:184,搜「look-ahead」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 280 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:280,搜「minimum context」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 299 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:299,搜「advances three times」)与第 342 段(text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:342,搜「forgiving」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 390 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:390,搜「termination condition」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 416 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:416,搜「left-associativity」)与第 404 段(text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:404,搜「precedence level」)。 ↩ -
出处:「Parsing: abstract syntax trees (ASTs) and recursion」第 530 段(
text/141-fm-parsing-abstract-syntax-trees-asts-and-recursion.txt:530,搜「baked」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 25 段(
text/142-fm-evaluation-tree-walking-execution.txt:25,搜「control-flow」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 63 段(
text/142-fm-evaluation-tree-walking-execution.txt:63,搜「same arena」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 115 段(
text/142-fm-evaluation-tree-walking-execution.txt:115,搜「isTruthy」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 152 段(
text/142-fm-evaluation-tree-walking-execution.txt:152,搜「short-circuits」)与第 167 段(text/142-fm-evaluation-tree-walking-execution.txt:167,搜「entire enclosing block」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 193 段(
text/142-fm-evaluation-tree-walking-execution.txt:193,搜「truthy by default」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 257 段(
text/142-fm-evaluation-tree-walking-execution.txt:257,搜「surfaces」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 317 段(
text/142-fm-evaluation-tree-walking-execution.txt:317,搜「far more limited」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 329 段(
text/142-fm-evaluation-tree-walking-execution.txt:329,搜「surprising gap」)。 ↩ ↩2 -
出处:「Evaluation: tree-walking execution」第 345 段(
text/142-fm-evaluation-tree-walking-execution.txt:345,搜「asymmetry」)。 ↩ ↩2 -
出处:「Evaluation: tree-walking execution」第 385 段(
text/142-fm-evaluation-tree-walking-execution.txt:385,搜「panics」)。 ↩ ↩2 -
出处:「The EBNF grammar of zcalc」第 276 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:276,搜「base」)与第 397 段(text/139-fm-the-ebnf-grammar-of-zcalc.txt:397,搜「bubble」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 425 段(
text/142-fm-evaluation-tree-walking-execution.txt:425,搜「40」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 553 段(
text/142-fm-evaluation-tree-walking-execution.txt:553,搜「without any configuration flags」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 604 段(
text/142-fm-evaluation-tree-walking-execution.txt:604,搜「recurses」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 642 段(
text/142-fm-evaluation-tree-walking-execution.txt:642,搜「null」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 660 段(
text/142-fm-evaluation-tree-walking-execution.txt:660,搜「persisting」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 683 段(
text/142-fm-evaluation-tree-walking-execution.txt:683,搜「Parsing Error」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 672 段(
text/142-fm-evaluation-tree-walking-execution.txt:672,搜「FILE MODE」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 698 段(
text/142-fm-evaluation-tree-walking-execution.txt:698,搜「REPL MODE」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 660 段(
text/142-fm-evaluation-tree-walking-execution.txt:660,搜「interactive quality」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 604 段(
text/142-fm-evaluation-tree-walking-execution.txt:604,搜「recurses」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 399 段(
text/142-fm-evaluation-tree-walking-execution.txt:399,搜「tree-walking」)与第 405 段(text/142-fm-evaluation-tree-walking-execution.txt:405,搜「bytecode」)。 ↩ -
出处:「Evaluation: tree-walking execution」第 531 段(
text/142-fm-evaluation-tree-walking-execution.txt:531,搜「isolation」)。 ↩ -
出处:「The EBNF grammar of zcalc」第 226 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:226,搜「lacks」)。 ↩ -
出处:「The EBNF grammar of zcalc」第 176 段(
text/139-fm-the-ebnf-grammar-of-zcalc.txt:176,搜「literal bytes」)。 ↩