yfaming on Nostr: 今天看完了 Crafting Interpreters 第 16 章,Scanning on Demand。 Scanning 在 ...
今天看完了 Crafting Interpreters 第 16 章,Scanning on Demand。
Scanning 在 jlox 里已经详细讲过,而且本身算简单,因此这一章新东西不多。这里只记录一下与 jlox 的不同之处。
clox 的 TokenType enum 增加了 `TOKEN_ERROR`。scan 时遇到错误,就返回它。
clox 的 scanner 采用 lazy 方式,每次调用 `scanToken()` 函数时,返回一个 token。这样,可避免在 C 中管理资源的负担。而且 parser 只需要 lookahead 1 个 token,本就不必保留所有 token。
另外,对于 literal (字面量)的值,clox 在 scanning 阶段没有记录。而 jlox 记录在 `Token.literal` 字段里。
clox 在识别 identifier 和 keyword 时,采用了 Trie 的思路,效率更高。
所有的 keyword 都是合法的 identifier,scan 时需要区分出来。在 clox 和 jlox 中,都是先识别 identifier,然后看它是不是 keyword。如果是 keyword 就返回 keyword,否则就返回 identifier。这是一个「将 keyword 从 identifier 中挑出来」的过程。
jlox 将所有的 keyword 放到一个 HashMap 里,识别过程很简单。
clox 则使用了 trie 数据结构,更高效。它尽可能减少了对字符(串)的访问次数。
由于 keyword 数量有限(16 个),组成的 trie 也非常小。clox 直接用嵌套的 switch 语句来实现。代码看起来笨拙,但效率很高。作者提到 v8 也使用了这样的做法。
Trie 的核心思路是,尽可能减少对字符的访问。比如,如果 identifier 第一个字符是 d,则它不可能是任何 keyword。只比较一个字符就足够了。只有当 identifier 是 keyword 时,才需要比较所有字符。而反观 jlox,检查 identifier 是否在 keyword hash map 里,需要计算 identifier 的 hash code。计算时就得访问 identifier 的所有字符。
接下来,开始 Crafting Interpreters 第二部分,用 C 实现的 Lox 字节码解释器 clox。
在第一部分,用 Java 实现的 tree-walk interpreter jlox 中,我们完整实现了 Lox 解释器,包括 scanner、parser、resolver、interpreter 等等。
但是,第一部分的解释器利用了 Java 语言的许多特性。
比如,Lox 的值,全部用 Java 的 Object 类型表示。
Lox 里的 return,用 Java 的异常来实现。
而 Java 自带 GC,所以 Lox 中对象的生命周期,我们也完全没考虑过。
在第二部分,我们将使用 C 语言实现解释器。C 比 Java 更加 low-level,它不支持 OOP,不支持异常和 GC。
因此,这几个问题,我们就需要在 C 里面手动处理了。
而且,第二部分我们实现的是字节码解释器。我们需要定义字节码,并在 parsing 将 ast 编译为字节码。
用不同语言实现同一门语言,但采用略有不同的技术,可以加深我们的理解,并学习 compiler/interpreter 领域的不同主题。
Published at
2026-08-18 04:02:08 UTCEvent JSON
{
"id": "3d60dd470a2a462f61eded8ba816feaae6bf4e4be6782ca05cb236600e634e13",
"pubkey": "908fbc3babc322eea473a0ab1e6bf6b3adcf899288ac97b2fa79f34ad408d4e4",
"created_at": 1787025728,
"kind": 1,
"tags": [
[
"q",
"1aea59b1f714db6bebf6019f30c55e36795d9bbba666682c752163c90db1acff",
"wss://lang.relays.land/zh",
"908fbc3babc322eea473a0ab1e6bf6b3adcf899288ac97b2fa79f34ad408d4e4"
]
],
"content": "今天看完了 Crafting Interpreters 第 16 章,Scanning on Demand。\nScanning 在 jlox 里已经详细讲过,而且本身算简单,因此这一章新东西不多。这里只记录一下与 jlox 的不同之处。\n\nclox 的 TokenType enum 增加了 `TOKEN_ERROR`。scan 时遇到错误,就返回它。\n\nclox 的 scanner 采用 lazy 方式,每次调用 `scanToken()` 函数时,返回一个 token。这样,可避免在 C 中管理资源的负担。而且 parser 只需要 lookahead 1 个 token,本就不必保留所有 token。\n\n另外,对于 literal (字面量)的值,clox 在 scanning 阶段没有记录。而 jlox 记录在 `Token.literal` 字段里。\n\nclox 在识别 identifier 和 keyword 时,采用了 Trie 的思路,效率更高。\n\n所有的 keyword 都是合法的 identifier,scan 时需要区分出来。在 clox 和 jlox 中,都是先识别 identifier,然后看它是不是 keyword。如果是 keyword 就返回 keyword,否则就返回 identifier。这是一个「将 keyword 从 identifier 中挑出来」的过程。\n\njlox 将所有的 keyword 放到一个 HashMap 里,识别过程很简单。\nclox 则使用了 trie 数据结构,更高效。它尽可能减少了对字符(串)的访问次数。\n\n由于 keyword 数量有限(16 个),组成的 trie 也非常小。clox 直接用嵌套的 switch 语句来实现。代码看起来笨拙,但效率很高。作者提到 v8 也使用了这样的做法。\n\nTrie 的核心思路是,尽可能减少对字符的访问。比如,如果 identifier 第一个字符是 d,则它不可能是任何 keyword。只比较一个字符就足够了。只有当 identifier 是 keyword 时,才需要比较所有字符。而反观 jlox,检查 identifier 是否在 keyword hash map 里,需要计算 identifier 的 hash code。计算时就得访问 identifier 的所有字符。\n\nnostr:nevent1qvzqqqqqqypzpyy0hsa6hseza6j88g9tre4ldvade7ye9z9vj7e0570nft2q348yqyvhwumn8ghj7mrpdenjuun9d3shjuewd3skuep00f5qqgq6afvmrac5md47haspnucv2h3k09wehwaxve5zcafpv0ysmvdvlue2w2a0",
"sig": "d762111432a7552fd527002116ff3f78484bc73ebbea60c510fa105b20af5269dcfedcbaa60f7672db39477a4ba9e9ad816972b4ca018bb1f713e0ef76f8a893"
}