From c5296d43b0254ab00bf71ef7d7520deb16170ef6 Mon Sep 17 00:00:00 2001 From: tiye Date: Sun, 23 Aug 2026 15:03:15 +0800 Subject: [PATCH 1/3] feat: add chainable embedding context setup --- Makefile | 5 + PERFORMANCE.md | 28 +++ README.md | 3 +- RFCs/0000-project-scope.md | 2 +- RFCs/0114-context-builder-api.md | 19 ++ RFCs/0115-qtest-selection.md | 19 ++ RFCs/0116-stdlib-benchmark-coverage.md | 18 ++ RFCs/0117-qcoffee-json-output.md | 20 ++ benches/core.rs | 24 +++ docs/manual.classical-zh.html | 7 +- docs/manual.classical-zh.md | 236 ++++++++++++-------- docs/manual.devanagari-sa.html | 7 +- docs/manual.devanagari-sa.md | 147 +++++++++++++ docs/manual.devanagari.sa.md | 96 --------- docs/manual.en.html | 7 +- docs/manual.en.md | 270 +++++++++++++---------- docs/manual.latin.html | 7 +- docs/manual.latin.md | 237 ++++++++++++-------- docs/manual.zh-CN.html | 7 +- docs/manual.zh-CN.md | 288 +++++++++++++------------ docs/syntax.en.md | 2 +- docs/syntax.zh-CN.md | 2 +- examples/embed.rs | 23 +- manuals/manual.classical-zh.qc | 5 +- manuals/manual.devanagari-sa.qc | 5 +- manuals/manual.en.qc | 5 +- manuals/manual.latin.qc | 5 +- manuals/manual.zh-CN.qc | 5 +- src/bin/qbench.rs | 20 ++ src/bin/qtest.rs | 39 +++- src/main.rs | 133 ++++++++++-- src/vm.rs | 18 ++ tests/cli_tools.rs | 116 ++++++++++ tests/embedding_api.rs | 28 ++- 34 files changed, 1266 insertions(+), 587 deletions(-) create mode 100644 RFCs/0114-context-builder-api.md create mode 100644 RFCs/0115-qtest-selection.md create mode 100644 RFCs/0116-stdlib-benchmark-coverage.md create mode 100644 RFCs/0117-qcoffee-json-output.md create mode 100644 docs/manual.devanagari-sa.md delete mode 100644 docs/manual.devanagari.sa.md diff --git a/Makefile b/Makefile index 171b98e..7f2f7c9 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,11 @@ docs: doc-check cargo run --locked --quiet --bin qdocco -- manuals/manual.en.qc -o docs/manual.en.html cargo run --locked --quiet --bin qdocco -- manuals/manual.latin.qc -o docs/manual.latin.html cargo run --locked --quiet --bin qdocco -- manuals/manual.devanagari-sa.qc -o docs/manual.devanagari-sa.html + cargo run --locked --quiet --bin qdocco -- --markdown manuals/manual.zh-CN.qc -o docs/manual.zh-CN.md + cargo run --locked --quiet --bin qdocco -- --markdown manuals/manual.classical-zh.qc -o docs/manual.classical-zh.md + cargo run --locked --quiet --bin qdocco -- --markdown manuals/manual.en.qc -o docs/manual.en.md + cargo run --locked --quiet --bin qdocco -- --markdown manuals/manual.latin.qc -o docs/manual.latin.md + cargo run --locked --quiet --bin qdocco -- --markdown manuals/manual.devanagari-sa.qc -o docs/manual.devanagari-sa.md check: fmt test release-test examples package-metadata package qbench-check clippy api-doc doc-check diff --git a/PERFORMANCE.md b/PERFORMANCE.md index fc0d4bc..befecde 100644 --- a/PERFORMANCE.md +++ b/PERFORMANCE.md @@ -543,6 +543,34 @@ rest 绑定会复制剩余元素到新的不可变数组,以保持宿主存储 ## 已知性能边界 +## RFC 0113/0116 数值标准库 + +标准库数值路径由四个负载覆盖:`stdlib-abs` 测量单值绝对值,`stdlib-sum` 测量小数组聚合,`stdlib-min-max` 测量严格最小/最大值,`stdlib-range-sum` 测量 `range` 与 `sum` 的组合。四者同时存在于 `qbench --json` 与 `cargo bench --bench core`,并检查最终值 `42`、`10`、`4`、`4950`。 + +复现机器可读记录: + +```sh +cargo run --locked --release --bin qbench -- --json --only stdlib-sum --iterations 100 --repeat 3 +``` + +复现完整 release 负载与持续门禁: + +```sh +make qbench-check +make bench +``` + +这些负载只用于同一实现、同一环境的回归跟踪;报告不设跨机器硬时间阈值,比较时应记录 `rustc -Vv`、机器、操作系统、迭代次数和重复次数。 + +本轮 Apple arm64 release 基准样本(`cargo bench --locked --bench core`,单位 ms;仅作仓库内回归锚点): + +| 工作负载 | 编译 | 验证 | 执行 | +|---|---:|---:|---:| +| stdlib-abs(20,000 次) | 23.700 | 1.428 | 32.712 | +| stdlib-sum(20,000 次) | 34.830 | 1.457 | 33.823 | +| stdlib-min-max(20,000 次) | 56.280 | 1.849 | 37.775 | +| stdlib-range-sum(10,000 次) | 17.852 | 0.896 | 30.155 | + ## RFC 0076 负索引 负索引在数组上做一次长度归一化,在字符串上按 Unicode 标量计数后定位;两者均保持越界错误,不复制序列。`negative-indexing` workload(20,000 次)单次样本为:编译 75.609 ms,验证 2.750 ms,执行 33.463 ms。 diff --git a/README.md b/README.md index 7e6ccc2..7afca86 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ QuickCoffee 是一台以 Rust 编写、受 CoffeeScript 启发的字节码脚本引擎。它保留紧凑、可读的表达式语法,却不兼容 JavaScript:没有原型链、`this`、`eval` 或嵌入 JavaScript。 -当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0113-numeric-standard-library.md](RFCs/0113-numeric-standard-library.md)。 +当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0117-qcoffee-json-output.md](RFCs/0117-qcoffee-json-output.md)。 构建要求 Rust 1.85 或更新版本(Edition 2024);CI 同时验证 MSRV 与 stable 工具链。 ```coffee @@ -26,6 +26,7 @@ cargo run -- example.qc -- first second cargo run -- --check example.qc cargo run -- --dump-bytecode example.qc cargo run -- --fingerprint example.qc +cargo run -- --json -e "{answer: 42}" cargo run --release --bin qbench -- --json --iterations 100 cargo run --release --bin qbench -- --json --iterations 100 --repeat 3 cargo run --release --bin qbench -- --list diff --git a/RFCs/0000-project-scope.md b/RFCs/0000-project-scope.md index bdbba12..2e0c29a 100644 --- a/RFCs/0000-project-scope.md +++ b/RFCs/0000-project-scope.md @@ -20,4 +20,4 @@ QuickCoffee 是一个 Rust 实现的、受 CoffeeScript 2016 启发的脚本引 本仓库中的测试即 0.1 的语义基线。对语法或运行时的新增特性必须先以 RFC 补充定义,并至少添加:成功测试、错误测试及字节码验证测试。 -当前已实现的后续语义与工具 RFC 延伸至 RFC 0113;其中 RFC 0077 定义 JSON 输出、RFC 0079 定义 TAP 输出、RFC 0080 定义 CLI 字节码指纹、RFC 0081 定义可机器读取的基准输出、RFC 0082 规范化指纹编码、RFC 0083 定义 Markdown 文学编程产物、RFC 0084 定义嵌入上下文 fuel 控制、RFC 0085 定义可执行 Rust 嵌入示例、RFC 0086 定义 crate 发布元数据、RFC 0094 定义 qdocco 最终值门禁、RFC 0095 定义字符串步进迭代、RFC 0096 定义其性能基准、RFC 0097 定义 `do` 参数转发、RFC 0098 定义 RFC 索引门禁、RFC 0099 定义 `!` 否定别名、RFC 0100 定义有符号 `by` 步长、RFC 0101 定义 qdocco 原子输出、RFC 0102 定义 qtest 规范文件去重、RFC 0103 定义 qbench schema 版本、RFC 0104 定义 qdocco 块注释代码保留、RFC 0105 定义 qbench 重复采样中位数、RFC 0106 定义 crate 发布包验收门禁、RFC 0107 定义 release qbench 持续门禁、RFC 0108 定义 qtest 可执行示例语料、RFC 0109 定义 qbench 核心负载全套护栏、RFC 0110 定义 Rust MSRV 契约、RFC 0111 定义 release profile 完整测试门禁、RFC 0112 定义 qbench 负载枚举与选择、RFC 0113 定义严格数值标准库函数,均不改变脚本语言值模型的原型无关约束。 +当前已实现的后续语义与工具 RFC 延伸至 RFC 0117;其中 RFC 0077 定义 JSON 输出、RFC 0079 定义 TAP 输出、RFC 0080 定义 CLI 字节码指纹、RFC 0081 定义可机器读取的基准输出、RFC 0082 规范化指纹编码、RFC 0083 定义 Markdown 文学编程产物、RFC 0084 定义嵌入上下文 fuel 控制、RFC 0085 定义可执行 Rust 嵌入示例、RFC 0086 定义 crate 发布元数据、RFC 0094 定义 qdocco 最终值门禁、RFC 0095 定义字符串步进迭代、RFC 0096 定义其性能基准、RFC 0097 定义 `do` 参数转发、RFC 0098 定义 RFC 索引门禁、RFC 0099 定义 `!` 否定别名、RFC 0100 定义有符号 `by` 步长、RFC 0101 定义 qdocco 原子输出、RFC 0102 定义 qtest 规范文件去重、RFC 0103 定义 qbench schema 版本、RFC 0104 定义 qdocco 块注释代码保留、RFC 0105 定义 qbench 重复采样中位数、RFC 0106 定义 crate 发布包验收门禁、RFC 0107 定义 release qbench 持续门禁、RFC 0108 定义 qtest 可执行示例语料、RFC 0109 定义 qbench 核心负载全套护栏、RFC 0110 定义 Rust MSRV 契约、RFC 0111 定义 release profile 完整测试门禁、RFC 0112 定义 qbench 负载枚举与选择、RFC 0113 定义严格数值标准库函数、RFC 0114 定义链式嵌入上下文配置、RFC 0115 定义 qtest 语料筛选与枚举、RFC 0116 定义数值标准库性能负载覆盖、RFC 0117 定义 qcoffee 单结果 JSON 输出,均不改变脚本语言值模型的原型无关约束。 diff --git a/RFCs/0114-context-builder-api.md b/RFCs/0114-context-builder-api.md new file mode 100644 index 0000000..f03a618 --- /dev/null +++ b/RFCs/0114-context-builder-api.md @@ -0,0 +1,19 @@ +# RFC 0114:链式嵌入上下文配置 + +- 状态:已采纳 +- 依赖:RFC 0041、RFC 0043、RFC 0084、RFC 0085、RFC 0089 + +## 动机 + +宿主可以用 `Context::set_global` 与 `Context::add_native` 配置全局值和回调,但每次配置都需要可变借用。嵌入示例和小型宿主程序更适合声明式、可链式的初始化,同时不能破坏已有的可变 API。 + +## 契约 + +1. `Context::with_global(name, value)` 消费并返回上下文,语义等同于先调用 `set_global`。 +2. `Context::with_native(name, callback)` 消费并返回上下文,语义等同于先调用 `add_native`;回调仍返回结构化 `Result`。 +3. 两个 builder 方法可与 `with_fuel` 任意顺序链式组合;全局值、原生函数、fuel 和后续执行语义与现有 API 完全相同。 +4. 既有 `set_global`、`add_native`、`fuel` 和 `run_program` API 保持兼容;不暴露环境、原型链或 JavaScript 对象。 + +## 验收 + +`tests/embedding_api.rs` 必须以链式配置执行共享 Program 并读取宿主全局;`examples/embed.rs` 使用链式 API;`make check` 必须继续通过完整 debug/release、文档和打包门禁。 diff --git a/RFCs/0115-qtest-selection.md b/RFCs/0115-qtest-selection.md new file mode 100644 index 0000000..dd0308d --- /dev/null +++ b/RFCs/0115-qtest-selection.md @@ -0,0 +1,19 @@ +# RFC 0115:qtest 语料筛选与枚举 + +- 状态:已采纳 +- 依赖:RFC 0068、RFC 0077、RFC 0079、RFC 0102、RFC 0111 + +## 动机 + +qtest 递归执行目录中的全部 `.qc` 文件,适合持续门禁;调试大型语料库时,宿主需要先确定会执行哪些文件,再只运行匹配路径的测试。筛选必须不改变默认排序、去重、fuel 和结果格式。 + +## 契约 + +1. `qtest --filter TEXT PATH...` 保留规范化路径中包含 `TEXT` 的测试文件,匹配大小写敏感且不使用 glob;未指定时运行完整集合。 +2. `qtest --list [--filter TEXT] PATH...` 按最终确定性顺序逐行输出文件路径,不执行源码;不能与 `--json`、`--tap` 或 `--stats` 同用。 +3. 筛选后无文件以退出码 2 失败;`--filter` 缺少或为空参数也以退出码 2 失败。 +4. JSON、TAP、普通输出、fuel、符号链接去重和错误退出码在筛选结果集内保持既有契约。 + +## 验收 + +`tests/cli_tools.rs` 必须覆盖单文件筛选、列表枚举、无匹配和参数冲突;`make check` 继续执行完整 qtest 语料,不能把筛选误当作默认门禁。 diff --git a/RFCs/0116-stdlib-benchmark-coverage.md b/RFCs/0116-stdlib-benchmark-coverage.md new file mode 100644 index 0000000..0460ad5 --- /dev/null +++ b/RFCs/0116-stdlib-benchmark-coverage.md @@ -0,0 +1,18 @@ +# RFC 0116:数值标准库性能负载覆盖 + +- 状态:已采纳 +- 依赖:RFC 0045、RFC 0096、RFC 0109、RFC 0113 + +## 动机 + +RFC 0113 新增了严格数值标准库函数,但既有 benchmark 只覆盖语言运算和容器路径。若不把标准库调用纳入同一编译、验证、执行计时口径,性能报告无法发现其回归,也无法比较宿主回调与 VM 内建函数的实际成本。 + +## 契约 + +1. `qbench` 和 `cargo bench --bench core` 都必须包含同名的 `stdlib-abs`、`stdlib-sum`、`stdlib-min-max`、`stdlib-range-sum` 负载。 +2. 每个负载在编译、验证和执行阶段都检查 RFC 0113 的最终值;qbench JSON schema、默认完整集合和 `--only` 选择语义不变。 +3. 标准 benchmark 继续记录重复迭代吞吐,不设置跨机器的硬时间阈值;性能报告必须说明样本口径和环境。 + +## 验收 + +`tests/cli_tools.rs` 必须枚举并验证四个机器可读负载名;`make qbench-check` 和 `make bench` 必须执行全套;`PERFORMANCE.md` 必须列出新增负载及复现实验命令。 diff --git a/RFCs/0117-qcoffee-json-output.md b/RFCs/0117-qcoffee-json-output.md new file mode 100644 index 0000000..fdee566 --- /dev/null +++ b/RFCs/0117-qcoffee-json-output.md @@ -0,0 +1,20 @@ +# RFC 0117:`qcoffee` 单结果 JSON 输出 + +- 状态:已采纳 +- 依赖:RFC 0002、RFC 0047、RFC 0062、RFC 0077 + +## 动机 + +`qtest` 与 `qbench` 已有稳定的机器输出,而 `qcoffee` 执行模式仍把值和错误写成面向人的文本。CI、编辑器和嵌入宿主若直接消费 CLI,必须自行解析展示文本,既不能可靠区分 `nil` 与字符串,也无法稳定取得错误类别和源码行。 + +## 契约 + +1. `qcoffee --json -e SOURCE`、`qcoffee --json FILE` 和 `qcoffee --json -` 各输出恰好一行 JSON;成功时形如 `{"ok":true,"value":VALUE}`,`nil` 映射为 JSON `null`。 +2. QuickCoffee 的 Bool、有限 Number、String、Array、Map 递归映射为对应 JSON 值;函数是 `{"$quickcoffee":"function"}`,以保留其为不可序列化宿主值的类型信息。Map 键按确定性字典序输出,字符串和控制字符采用 JSON 转义。 +3. 编译或执行失败时退出码仍为 `1`,标准输出形如 `{"ok":false,"kind":KIND,"message":TEXT,"line":N}`;`line` 无来源时为 `null`。读取文件失败使用 `stage:"read"` 与 `kind:"io"`。错误模式不向标准错误重复输出详情。 +4. `--json` 只适用于单次执行,不得与 `--interactive`、`--check`、`--dump-bytecode` 或 `--fingerprint` 合用;`--stats` 仍可使用且只写标准错误。 +5. JSON 协议不改变普通输出、退出码、fuel 或 QuickCoffee 值模型;它不引入 JavaScript `undefined`、原型或隐式转换。 + +## 验收 + +`tests/cli_tools.rs` 必须覆盖复合值、`nil`、解析错误、fuel 运行时错误和 JSON/普通模式隔离;五份可执行手册与中英文语法索引说明该选项。`make check` 必须继续通过。 diff --git a/benches/core.rs b/benches/core.rs index fffd85f..ebe4b14 100644 --- a/benches/core.rs +++ b/benches/core.rs @@ -17,6 +17,30 @@ fn main() { iterations: 20_000, expected: "100", }, + Workload { + name: "stdlib-abs", + source: "abs(-42)", + iterations: 20_000, + expected: "42", + }, + Workload { + name: "stdlib-sum", + source: "sum([1, 2, 3, 4])", + iterations: 20_000, + expected: "10", + }, + Workload { + name: "stdlib-min-max", + source: "min([3, 1, 2]) + max([3, 1, 2])", + iterations: 20_000, + expected: "4", + }, + Workload { + name: "stdlib-range-sum", + source: "sum(range(1, 100))", + iterations: 10_000, + expected: "4950", + }, Workload { name: "postfix-loops", source: "sum = 0\ni = 0\ni = i + 1 while i < 100\nsum + i", diff --git a/docs/manual.classical-zh.html b/docs/manual.classical-zh.html index b23cfc1..dd3f022 100644 --- a/docs/manual.classical-zh.html +++ b/docs/manual.classical-zh.html @@ -22,6 +22,8 @@

qtest --stats 更书各篇所试指令与余燃料于标准错误,而 ok 之出不改。

qtest --json 每篇出一行 JSON,便于 CI 取用;--stats 仍书于标准错误。

qtest --tap 出 TAP 13 及定次之记录;--json 与 --tap 不可并用。

+

qtest --filter TEXT 依路径择篇;qtest --list 但列所择之篇而不行其文。

+

qcoffee --json 一行以 JSON 载其值或错状,俾 CI 与宿主取用。

宿主之误,有 ErrorKind::Parse、Verify、Runtime 三类,且可别取其详;error.position() 或示从一始之源码行。

Engine::compile_program 创时验之;Context::run_program 屡行则复用不可变已验字节码。

Program::fingerprint 出确定 u64 码键,便宿主缓存,而不改执行。

@@ -29,7 +31,7 @@

qbench --json 每负载出一计时录,皆有语义护栏;--iterations 定其试数。

指纹以定式编码字节码,不取 Rust 调试辞,故工具链改其辞而缓存键不改。

qdocco --markdown 出说明、围栏 QuickCoffee 代码及终值为可阅 Markdown 文。

-

嵌者可于两行之间呼 Context::set_fuel;Context::fuel 示每行之限,而全局不失。

+

嵌者可于两行之间呼 Context::set_fuel;Context::fuel 示每行之限,而全局不失;with_global、with_native 可相次而呼以置宿主。

cargo run --example embed 可验最小 Rust 宿主,设全局、立原生回调而行 QuickCoffee。

宿主可用 Value::kind() 别其类,Value::is_nil() 验 nil,不窥其内容器。

Cargo 包志指仓、docs.rs API、README 与许可证,使嵌者易寻其用。

@@ -62,7 +64,8 @@

数组之环可书 by step;步惟求一遍,须非零有限整数,负者自末起,映射环弗用之。

数组之环亦可系从一始之下标:for value, index in items then value + index。

后置之推导亦循严收集:value * 2 for value in items,或括以 [value * 2 for value in items]。

-

Code

甲 = 6
+

Code


+甲 = 6
 倍 = (x) -> x * 2
 shorthand = 'yes'
 [first, {point: [x, y]}] = [0, {point: [20, 22]}]
diff --git a/docs/manual.classical-zh.md b/docs/manual.classical-zh.md
index d96ff9f..41f61b8 100644
--- a/docs/manual.classical-zh.md
+++ b/docs/manual.classical-zh.md
@@ -1,92 +1,144 @@
-# QuickCoffee 用法(宋代官话古文体)
-
-QuickCoffee 者,Rust 所为字节码机也,非 JavaScript 之运行时。其文先析,次编,复验,而后行之。故无原型之链,无 `this`,无 `eval`,亦禁嵌 JavaScript。
-
-三引号之 heredoc,保其换行:`"""…"""` 可插 `#{expression}`,`'''…'''` 则字面也;不削其缩进,不闭则为词误。
-
-`#` 注一行;`### … ###` 注一段,弗相嵌。其文先于布局与析法而略之;不闭则为词误。
-
-名从 Unicode XID 之法:首用 XID start 或 `_`,续用 XID continue 或 `_`;合附之记可续名,而机不正其 Unicode。
-
-常字符串可解控制字符之转义,亦可用 `\\xNN`、`\\uNNNN`、`\\u{...}` 表 Unicode;转义非法或非标量者,析法报误。
-
-又从 CoffeeScript 之便称:`yes`、`on` 同 `true`;`no`、`off` 同 `false`;`is`、`isnt` 同严等之 `==`、`!=`,不易其类。
-
-严等与数之比较,相连可书 `1 < middle() < 3`;中项惟求一遍,前较既否,后项不求。
-
-欲试之,曰:`qcoffee -e "print(range(1, 4))"`。`--check FILE` 者,析、编、验其文而不行;`--fuel N` 者,限所行指令之数也;数尽则止而报误。内府有 `print`、`len`、`type`、`range`、`str`、`abs`、`sum`、`min`、`max`、`keys`、`values`、`join`、`split`、`assert`;数聚函唯受有限数之列;`range(a, b)` 取自 a 至 b 前。
-
-`qcoffee -` 自标准输入读其文,便于管道;`qcoffee --dump-bytecode -` 则析其指令而不行之。
-`qcoffee --stats` 则书所试指令与余燃料于标准错误,程序之标准输出不改;每次惟一源码输入,执行模式相冲则报用法之误。
-
-`qcoffee --interactive`(或 `-i`)逐行共用一 Context;`:help` 示其命,`:quit`/`:exit` 出之。管道输入不见提示。
-交互而加 `--stats`,每一非空行亦书所试指令与余燃料于标准错误。`for character, index in 'a☕中' then index` 得 `[0, 1, 2]`;字符串循 Unicode 标量,不受 `by`。`[head, tail...] = [1, 2, 3]`,tail 得 `[2, 3]`;数组之 rest 必居末。
-
-`--` 后之参,以常字符串数组 `argv` 见:`qcoffee program.qc -- first second`,则 `len(argv)` 为 `2`。不暴宿主之进程与环境对象。
-
-函式书作 `(x) -> expression`,或省其括而曰 `left, right -> left + right`,取其所生之词法境;常值、余参与解构之参仍须括之。末之参可定其常值,如 `(head, sep = '-') -> expression`。参缺,或明传 `nil`,则于被调函中求其常值,故可引先参与所取之境;必参当先于定值之参。若末参为余参,则书 `(head, tail...) -> expression`,余实参合为数组。欲为可行之文,曰:`qdocco FILE -o FILE.html`;欲试诸例,曰:`qtest FILE...`,各篇终值皆须 `true`。
-
-函中可曰 `return expression`,即反其值而终此函;徒曰 `return`,则反 `nil`。不越内函。其在环中,则清环之机;其经 `try`、`catch`,则由内而外行 `finally`。若 `finally` 中复曰 return,则夺前所欲反之值。欲因条件而反,当书 `if condition then return value`。
-
-函参亦可层叠解构:`([left, right], {factor}) -> (left + right) * factor`。调用之参,必先合其式;常值惟名参可设,余参惟末一名。
-
-映射字面量中,`{name}` 即 `{name: name}` 之省文;若键为字符串,犹须明书其值。
-
-赋值之式,可层叠数组与映射:`[first, {point: [x, y]}] = [1, {point: [20, 22]}]`。数组各层必等其数,映射必有其所列键;机先验其全式而后易名,故深处有误亦不半易。
-
-数组项与调用实参末之 `...` 展其数组:`[1, values..., 4]` 拼其元素,`fn(values...)` 逐个传参。所展必为数组。
-
-欲安于空值,则书 `record?.name`、`values?[index]`、`fn?(args)`:受者为 `nil`,即得 `nil`,索引与参亦不求;非空则仍循常法,映射缺键犹报误。
-
-`qtest --fuel N FILE...` 则各篇别限其指令,故一篇受限之环,不耗他篇之数。
-`qtest --stats` 更书各篇所试指令与余燃料于标准错误,而 `ok` 之出不改。
-
-遍数组,则曰 `for item in items then expression`;其所系可为严式,如 `for [left, right] in pairs then left + right`,一项之诸名必待全合而后易。若欲间取,则置 `by step`,如 `for item in [1..9] by 3 then expression`,负步则自末项反行。其体诸值聚为新数组;`when` 所拒者不聚,`break` 则反已聚之先段。其步惟求一遍,且须非零有限整数;映射之遍不得用之。`break` 止其内环,`continue` 逾其一轮;while、until、loop 之值恒为 `nil`。
-
-同一收集之法,亦可后置作 `value * 2 for value in items`,或括之为 `[value * 2 for value in items]`。方括惟为推导之界,不更生一层数组;`by`、`when`、映射、诸式、`break`、`continue` 皆循前式。
-
-整数之区间,`[1..3]` 及其终,得 `[1, 2, 3]`;`[1...3]` 则不及,得 `[1, 2]`;逆行亦然,`[3..1]` 得 `[3, 2, 1]`。其界必为有限整数。
-
-后缀 `value?`,惟验其非 nil:`nil?` 为 false,而 `false?` 与 `0?` 皆 true;未名之误弗隐,亦非 `left ? right` 之回退。
-
-`name ?= value` 者,名未系或值为 nil,乃求 value 而书之;既非 nil,则右不行。惟名可用,成员、索引、解构皆弗许;寻常读未名,犹为误。
-
-名亦可前后置增减:`next = ++counter` 得新值,`previous = counter--` 先得旧值再减一;惟普通名称可用。
-
-算术亦有整除 `a // b` 与取模 `a %% b`;如 `-7 // 5` 得 `-2`,`-7 %% 5` 得 `3`,寻常 `%` 仍随被除数取符号。
-
-位运算皆守有符号三十二位之数:`&`、`|`、`^`、`~`、`<<`、`>>`、`>>>`;移位之数限于零至三十一,复合式惟名可用。
-
-行末若有显式运算符,次行可承其表达式;承行之缩进惟饰文,不改布局之块。
-
-常引号之文亦可跨行;换行合为一空格,行末反斜杠则去之。
-
-如 `(1 + 2 * 3) == 7` 之纯字面算术,编时折为所验常量。
-
-数组之截,书 `items[start..end]` 以包括终,`items[start...end]` 以不及终;二端左至右各求一遍,皆须界内有限整数。负数自末计,`-1` 末项也;惟数组可截,弗暗截短。受者 nil,则 `items?[start..end]` 得 nil,端不求焉。
-
-`left ? right` 者,空值之回退也:左为 `nil`,乃求右;`false`、零、空串与空器皆不以为空。
-
-`value in array` 验数组之成员,`value not in array` 反其验;`key of map` 唯验映射己有之字符串键,`key not of map` 反其验。映射无原型键可寻。
-
-`until condition then body` 者,反环也:反复至布尔条件真而止;`break`、`continue`、缩进与 fuel 之法,皆同 `while`。
-
-语句之位,亦可后书 `n = n + 1 while n < 3`,等前书之环,而每轮尽行其赋;`until` 同此。解构赋亦可为体,然不可嵌为寻常子式。
-
-`loop body` 者,恒行如 `while true`;以 `break` 出之,犹受 fuel 之限。
-
-`for` 之可迭者与 `then` 间置 `when condition`,可筛其环;不合者不行其体:`for n in [1..5] when n > 2 then print(n)`。
-
-嵌入方遇误,可由 `error.kind()` 别 `ErrorKind::Parse`、`Verify`、`Runtime`;`error.message()` 得其详,`error.position()` 或得从一始之源码行,不必析展示之文。
-
-欲屡行已编之文,可用 `Engine::compile_program` 编验一次得共享 `Program`,以 `Context::run_program` 行之;复制其柄,不复制字节码,亦不重验。
-
-`Context::last_execution()` 可取最近行止之 `ExecutionStats`,载所试指令数 `instructions` 及余燃料 `fuel_remaining`;编验之误,不易前录。
-
-数组映射跨行时,逗号可省;调用之参与寻常括中之式,仍须明分其隔。
-
-赋值独行而后缩进,亦可成映射;其键值层叠而无原型,寻常赋值续行不误为之。
-
-一逻辑行中,函调用可略括,如 `implicit_answer = implicit_add 20, 22`;遇比较或布局之界,仍宜明括。
-
-`qtest --json` 各试篇出定 JSON,`qtest --tap` 出 TAP 13;`qcoffee --fingerprint FILE` 不行其文而出定式字节码键;`qbench --json` 记编、验、行之时,皆有语义护栏;`qdocco --markdown` 出可阅文学编程 Markdown。嵌者可呼 `Context::set_fuel` 改复用境之限,以 `Value::kind()`、`Value::is_nil()` 别值之类,并行 `cargo run --example embed` 观宿主全例。
+# QuickCoffee document
+
+## Notes
+
+QuickCoffee 用法
+映射可展其项,后书之键胜前书:{...defaults, theme: 'dark'}。
+映射解构末可用 ...metadata 收未列之键,所得映射不变。
+数列与 Unicode 字符负索引,-一取其末。
+此机先析其文,编为字节码,验而后行。非 JavaScript 也,故无原型之链、this、eval 与内嵌之文。
+# 注一行;### … ### 注一段,弗相嵌,先于布局析法略之。
+名从 Unicode XID 之法,合附之记可续名,而机不正其 Unicode。
+yes、on 同 true;no、off 同 false;is、isnt 则严等也。
+! 同 not,严反 Bool;!= 仍严不等。
+严等与数较可相连,中项惟求一遍,前否则后不求。
+qcoffee - 者,自标准输入读其文也。
+qcoffee --stats 则书所试指令与余燃料于标准错误,程序之标准输出不改;每次惟一源码输入,执行模式相冲则报用法之误。
+qcoffee --check FILE 者,析编验其文而不行也。
+qcoffee --interactive(或 -i)者,逐行共用一 Context;:help 示命,:quit 出之。
+qcoffee --interactive --stats 惟非空行之行而行或运行时有误者,书指令与余燃料一条;析验之误不更书。
+'a☕中'[1] 即 '☕','a☕中'[1..2]' 得 '☕中';字符串索引循 Unicode 标量。
+for character, index in 'a☕中' then index,得 [0, 1, 2];字符串循 Unicode 标量,by 可正可负,负者自末起。
+do (name, other) -> ... 即调用之,转外层同名之值;do -> ... 仍零参。
+[head, tail...] = [1, 2, 3],tail 得 [2, 3];数组之 rest 必居末。
+qtest --fuel N 者,为各可行文别限其指令之数。
+qtest --stats 更书各篇所试指令与余燃料于标准错误,而 ok 之出不改。
+qtest --json 每篇出一行 JSON,便于 CI 取用;--stats 仍书于标准错误。
+qtest --tap 出 TAP 13 及定次之记录;--json 与 --tap 不可并用。
+qtest --filter TEXT 依路径择篇;qtest --list 但列所择之篇而不行其文。
+qcoffee --json 一行以 JSON 载其值或错状,俾 CI 与宿主取用。
+宿主之误,有 ErrorKind::Parse、Verify、Runtime 三类,且可别取其详;error.position() 或示从一始之源码行。
+Engine::compile_program 创时验之;Context::run_program 屡行则复用不可变已验字节码。
+Program::fingerprint 出确定 u64 码键,便宿主缓存,而不改执行。
+qcoffee --fingerprint FILE 出十六位小写字节码键,先验之而不行其文。
+qbench --json 每负载出一计时录,皆有语义护栏;--iterations 定其试数。
+指纹以定式编码字节码,不取 Rust 调试辞,故工具链改其辞而缓存键不改。
+qdocco --markdown 出说明、围栏 QuickCoffee 代码及终值为可阅 Markdown 文。
+嵌者可于两行之间呼 Context::set_fuel;Context::fuel 示每行之限,而全局不失;with_global、with_native 可相次而呼以置宿主。
+cargo run --example embed 可验最小 Rust 宿主,设全局、立原生回调而行 QuickCoffee。
+宿主可用 Value::kind() 别其类,Value::is_nil() 验 nil,不窥其内容器。
+Cargo 包志指仓、docs.rs API、README 与许可证,使嵌者易寻其用。
+Context::last_execution() 示所试指令与余燃料,而不露 VM 之帧。
+-- 后之参,以常字符串数组 argv 见于文中。
+其内府皆常函,如 print、len、type、range、str、abs、sum、min、max、keys、values、join、split、assert;数聚函唯受有限数之列。
+函式取词法之境;末常参可书 y = 2,参缺或传 nil 则于函中取其值;末有余参,则书 tail...。
+常名之参可省其括:left, right -> left + right;常值、余参与解构仍须括之。
+return expression 惟函中可用,反其值而终此函;徒 return 得 nil,且清环行 finally。
+函参可层叠数组映射之式;常值与余参仍惟名可书。
+整数之区间,`[1..3]` 及其终,`[1...3]` 则不及。
+区间亦可逆行,`[3..1]` 得 `[3, 2, 1]`,`[3...1]` 得 `[3, 2]`。
+数组之截,a[start..end] 及终,a[start...end] 不及终;端须界内有限整数,负自末计,受者 nil 则安截不求端。
+空值回退,书 left ? right;惟 nil 发之,false 与零不易。
+后缀 value? 惟验非 nil;nil? 为 false,false? 与 0? 皆 true,未名之误弗隐。
+name ?= value,名未系或 nil 乃书之;既非 nil,右弗行,成员索引解构弗可。
+名亦可前后置增减:next = ++counter 得新值,previous = counter-- 先得旧值;惟名可用。
+算术亦有整除 // 与取模 %%:-7 // 5 得 -2,-7 %% 5 得 3。
+value in array 验数组之有无;key of map 唯验映射己有之字符串键。
+value not in array、key not of map,皆反其严验,不涉原型。
+映射字面量之 {name},即 {name: name} 之省文。
+赋值之式,可层叠数组与映射;机先验其全式,后易诸名。
+数组与调用之 items...,展其数组,非假 JavaScript apply。
+安空值之缀,书 a?.name、a?[i]、f?(args);惟受者 nil 则短之。
+until condition then body 者,至其布尔条件真而止。
+语句可后书 while/until,反复其全赋或解构,弗嵌寻常子式。
+loop body 者,如 while true 恒行;以 break 出之,犹受 fuel 限。
+for 为收集之式:轮体诸值成新数组,when 与 continue 弗收,break 存既得之首。
+for 所系可为严式:for [left, right] in pairs,每项全合乃易名。
+数组之环可书 by step;步惟求一遍,须非零有限整数,负者自末起,映射环弗用之。
+数组之环亦可系从一始之下标:for value, index in items then value + index。
+后置之推导亦循严收集:value * 2 for value in items,或括以 [value * 2 for value in items]。
+
+## Code
+
+````quickcoffee
+
+甲 = 6
+倍 = (x) -> x * 2
+shorthand = 'yes'
+[first, {point: [x, y]}] = [0, {point: [20, 22]}]
+scale = ([left, right], {factor}) -> (left + right) * factor
+倍(甲) == 12 and "其数为 #{倍(甲)}" == '其数为 12' and yes is on and no is off and 1 < 2 < 3 and x + y == 42 and scale([20, 1], {factor: 2}) == 42 and ((首, y = 2) -> 首 + y)(40) == 42 and ((首, 余...) -> 首 + len(余))(40, 1, 2) == 42 and ((items) -> for n in items then if n == 42 then return n)([1, 42]) == 42 and ((-> try return 1 catch error then 2 finally 0)()) == 1 and len([1..3]) == 3 and len([1...3]) == 2 and (nil ? 42) == 42 and (false ? 42) == false and nil?.missing == nil and 2 in [1, 2] and 'name' of {name: 1} and {shorthand}.shorthand == 'yes' and len([1, [2, 3]..., 4]) == 4
+步和 = 0
+for n in [1..9] by 3 then 步和 = 步和 + n
+步和 == 12
+len(for [left, right] in [[20, 22], [1, 2]] then left + right) == 2
+后置之倍 = value * 2 for value in [1..3]
+后置之倍 == [2, 4, 6]
+计数 = 2
+前增 = ++计数
+后减 = 计数--
+[前增, 后减, 计数] == [3, 3, 3]
+[-7 // 5, -7 %% 5] == [-2, 3]
+[5 & 3, 5 | 2, 5 ^ 1, ~1, 1 << 3, -8 >> 2, -1 >>> 1] == [1, 7, 4, -2, 8, -2, 2147483647]
+continued = 1 +
+  2 * 3
+continued == 7
+message = "hello
+  world"
+message == 'hello world'
+escaped = "A\\x42\\u{43}"
+escaped == 'ABC'
+folded = (1 + 2 * 3) == 7
+folded
+values = [
+  1
+  2
+]
+values == [1, 2]
+record = {
+  first: 20
+  second: 22
+}
+record.first + record.second == 42
+indented_record =
+  first: 20
+  nested:
+    second: 22
+indented_record.nested.second == 22
+implicit_add = (left, right) -> left + right
+implicit_answer = implicit_add 20, 22
+implicit_answer == 42
+3 not in [1, 2] and '缺' not of {在: 1}
+循环数 = 0
+loop
+  循环数 = 循环数 + 1
+  break if 循环数 == 3
+循环数 == 3
+裸加 = left, right -> left + right
+裸加(20, 22) == 42
+后置数 = 0
+后置数 = 后置数 + 1 while 后置数 < 3
+后置数 == 3
+切片数 = [0..4][1..3]
+len(切片数) == 3 and 切片数[0] == 1 and [0..4][-3...-1][0] == 2
+nil? == false and false? == true and 0? == true
+默认数 ?= 42
+默认数 == 42
+### 此段有无效 ` 文,机不求之
+###
+42 == 42
+````
+
+## Final value
+
+`true`
diff --git a/docs/manual.devanagari-sa.html b/docs/manual.devanagari-sa.html
index 03b3978..2a5432c 100644
--- a/docs/manual.devanagari-sa.html
+++ b/docs/manual.devanagari-sa.html
@@ -17,6 +17,8 @@
 

qtest --stats प्रत्येकस्य documentस्य instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति, ok-निर्गमं न परिवर्तयति।

qtest --json प्रत्येकस्य लेखस्य स्थिरं JSON फलम् एकस्मिन् पङ्क्तौ लिखति; --stats stderr मध्ये एव।

qtest --tap TAP 13 तथा नियत-सङ्ख्याङ्कितानि फलानि लिखति; --json च --tap च परस्परं निषिद्धे।

+

qtest --filter TEXT मार्ग-साम्येन परीक्षां चिनोति; qtest --list चयनित-पत्राणि केवलं गणयति, न चालयति।

+

qcoffee --json एकस्मिन् प्रयोगे JSON-मूल्यं वा संरचितं दोषं एकया पङ्क्त्या ददाति, CI-होष्ट्रयोः उपयोगाय।

host-error ErrorKind::Parse, Verify, Runtime तथा प्रदर्शनात् स्वतन्त्रं विवरणं ददाति; error.position() कदाचित् एकतः गणितां स्रोत-पङ्क्तिं ददाति।

Engine::compile_program एकवारं verify करोति; Context::run_program पुनःचालने अपरिवर्तनीय-सत्यापित-bytecode पुनरुपयुङ्क्ते।

Program::fingerprint होस्ट-सञ्चयाय नियतं u64 बीजं ददाति, निष्पादनं न परिवर्तयति।

@@ -24,7 +26,7 @@

qbench --json प्रत्येक-सुरक्षित-भारस्य एकं काल-मापन-फलम् लिखति; --iterations नमूना-सङ्ख्यां नियच्छति।

बीजाङ्काः Rust-debug-रूपं विना स्पष्ट-नियत-bytecode-संकेतेन निर्मीयन्ते, अतः साधन-रूपपरिवर्तनं सञ्चय-कुञ्जीं न परिवर्तयति।

qdocco --markdown टिप्पणीन्, सीमितं QuickCoffee-कोडं, अन्तिम-मूल्यं च पठनीय Markdown-फलके लिखति।

-

अन्तःस्थापकः चालनयोर्मध्ये Context::set_fuel आह्वयितुं शक्नोति; Context::fuel वर्तमान-सीमां दर्शयति, वैश्विक-मूल्यानि न नाशयति।

+

अन्तःस्थापकः चालनयोर्मध्ये Context::set_fuel आह्वयितुं शक्नोति; Context::fuel वर्तमान-सीमां दर्शयति, वैश्विक-मूल्यानि न नाशयति; with_global तथा with_native क्रमिक-संयोजनाय स्तः।

`cargo run --example embed` लघुं Rust-आश्रयं संयोजयति, वैश्विकं स्थापयति, native-callback योजयति, QuickCoffee च चालयति।

Host `Value::kind()` द्वारा प्रकारं विभजति, `Value::is_nil()` द्वारा nil परीक्षते, आन्तरिक-container न पश्यति।

Cargo-वस्तु-विवरणानि अन्तःस्थापकान् repository, docs.rs-API, README, licence च प्रति नयन्ति।

@@ -63,7 +65,8 @@

array-for by step उपयुज्यते; non-zero finite integer step एकवारं मूल्यते, negative क्रमः अन्तिम-पदात् आरभते, map तु न।

array-for शून्यात् गणितं index अपि बध्नाति: for value, index in items then value + index।

postfix-comprehension समानं strict-collection वहति: value * 2 for value in items, अथवा [value * 2 for value in items]।

-

Code

base = 40
+

Code


+base = 40
 add = (x) -> x + base
 shorthand = 'yes'
 [first, {point: [x, y]}] = [0, {point: [20, 22]}]
diff --git a/docs/manual.devanagari-sa.md b/docs/manual.devanagari-sa.md
new file mode 100644
index 0000000..9873e24
--- /dev/null
+++ b/docs/manual.devanagari-sa.md
@@ -0,0 +1,147 @@
+# QuickCoffee document
+
+## Notes
+
+QuickCoffee मार्गदर्शिका
+मानचित्र-विस्तारः पश्चात् लिखिता कुञ्जी पूर्वलिखितां जयति।
+मानचित्र-विन्यासे ...metadata अनुक्तानि कुञ्जीनि गृह्णाति।
+ऋण-सूचकाङ्केन क्रमस्य अन्तिमं पदं लभ्यते।
+स्रोतः पठ्यते, सत्यापित-bytecode मध्ये संकल्यते, fuel-सीमया चालयते।
+qcoffee - मानक-input तः QuickCoffee-program पठति।
+qcoffee --stats instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति, stdout अपरिवर्तितं स्थापयति; एकमेव source ग्राह्यः, विरोधि execution-mode तु usage-दोषं जनयति।
+qcoffee --check FILE स्रोतं verify करोति, न चालयति।
+qcoffee --interactive (वा -i) एकं Context पङ्क्ति-क्रमेण धारयति; :help दर्शयति, :quit निर्गच्छति।
+qcoffee --interactive --stats केवलं कार्यितायै वा runtime-दोषयुक्तायै non-empty पङ्क्त्यै instruction तथा fuel लेखं लिखति; parse अथवा verify-दोषे नूतनं लेखं न लिखति।
+'a☕中'[1] '☕' अस्ति, 'a☕中'[1..2] '☕中' अस्ति; string-index Unicode-scalar-अनुसारी अस्ति।
+for character, index in 'a☕中' then index Unicode-scalar-अङ्कान् [0, 1, 2] ददाति; string-iteration मध्ये शून्य-वर्जित signed by-क्रमः अस्ति।
+do (name, other) -> ... तत्क्षणं आह्वयति, बहिः समाननाम-मूल्यानि ददाति; do -> ... निरवयवम् अस्ति।
+[head, tail...] = [1, 2, 3] tail-नाम्नि [2, 3] बध्नाति; array-pattern rest अन्तिमः भवति।
+qtest --fuel N प्रत्येक executable-document पृथक् instruction-budget ददाति।
+qtest --stats प्रत्येकस्य documentस्य instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति, ok-निर्गमं न परिवर्तयति।
+qtest --json प्रत्येकस्य लेखस्य स्थिरं JSON फलम् एकस्मिन् पङ्क्तौ लिखति; --stats stderr मध्ये एव।
+qtest --tap TAP 13 तथा नियत-सङ्ख्याङ्कितानि फलानि लिखति; --json च --tap च परस्परं निषिद्धे।
+qtest --filter TEXT मार्ग-साम्येन परीक्षां चिनोति; qtest --list चयनित-पत्राणि केवलं गणयति, न चालयति।
+qcoffee --json एकस्मिन् प्रयोगे JSON-मूल्यं वा संरचितं दोषं एकया पङ्क्त्या ददाति, CI-होष्ट्रयोः उपयोगाय।
+host-error ErrorKind::Parse, Verify, Runtime तथा प्रदर्शनात् स्वतन्त्रं विवरणं ददाति; error.position() कदाचित् एकतः गणितां स्रोत-पङ्क्तिं ददाति।
+Engine::compile_program एकवारं verify करोति; Context::run_program पुनःचालने अपरिवर्तनीय-सत्यापित-bytecode पुनरुपयुङ्क्ते।
+Program::fingerprint होस्ट-सञ्चयाय नियतं u64 बीजं ददाति, निष्पादनं न परिवर्तयति।
+qcoffee --fingerprint FILE सत्यापित-bytecode-कुञ्जीं षोडश लघु-षोडशाधारीय-अङ्कैः दर्शयति, लेखं न चालयति।
+qbench --json प्रत्येक-सुरक्षित-भारस्य एकं काल-मापन-फलम् लिखति; --iterations नमूना-सङ्ख्यां नियच्छति।
+बीजाङ्काः Rust-debug-रूपं विना स्पष्ट-नियत-bytecode-संकेतेन निर्मीयन्ते, अतः साधन-रूपपरिवर्तनं सञ्चय-कुञ्जीं न परिवर्तयति।
+qdocco --markdown टिप्पणीन्, सीमितं QuickCoffee-कोडं, अन्तिम-मूल्यं च पठनीय Markdown-फलके लिखति।
+अन्तःस्थापकः चालनयोर्मध्ये Context::set_fuel आह्वयितुं शक्नोति; Context::fuel वर्तमान-सीमां दर्शयति, वैश्विक-मूल्यानि न नाशयति; with_global तथा with_native क्रमिक-संयोजनाय स्तः।
+`cargo run --example embed` लघुं Rust-आश्रयं संयोजयति, वैश्विकं स्थापयति, native-callback योजयति, QuickCoffee च चालयति।
+Host `Value::kind()` द्वारा प्रकारं विभजति, `Value::is_nil()` द्वारा nil परीक्षते, आन्तरिक-container न पश्यति।
+Cargo-वस्तु-विवरणानि अन्तःस्थापकान् repository, docs.rs-API, README, licence च प्रति नयन्ति।
+Context::last_execution() instruction-संख्या तथा अवशिष्ट-fuel दर्शयति, VM-frame न प्रकाशयति।
+-- पश्चात् argumentाः साधारण-string-array argv रूपेण दीयन्ते।
+JavaScript नास्ति: prototype-chain, this, eval, अन्तःस्थ-JavaScript च न सन्ति।
+# line-comment अस्ति; ### … ### non-nesting block-comment layout तथा parse पूर्वं त्यज्यते।
+Unicode XID-नामानि संयोजक-चिह्नानि गृह्णन्ति, अतः स्थित इत्यादि नाम executable अस्ति।
+yes/on true, no/off false; is/isnt strict-साम्यम् स्तः।
+! strict-Bool not-पर्यायः अस्ति; != strict-असाम्यमेव तिष्ठति।
+chained-comparison मध्ये मध्य-मूल्य एकवारं, पूर्व-false चेत् short-circuit भवति।
+सामान्य-library साधारण-function रूपेण print, len, type, range, str, abs, sum, min, max, keys, values, join, split, assert ददाति; संख्या-संग्रहाः केवलं finite-संख्याः गृह्णन्ति।
+कार्यं lexical-environment गृह्णाति; y = 2 omitting अथवा nil दत्ते कार्यस्य अन्तरे default भवति; अन्तिमः rest-parameter tail... इति लिख्यते।
+bare-name lambda left, right -> left + right भवति; default, rest, pattern तु parentheses गृह्णन्ति।
+return expression केवलं वर्तमान-कार्यं समाप्तं करोति; केवलः return nil फलति, loop शुद्धीकरोति, finally च चलयति।
+parameter strict array/map-pattern गृह्णाति; default तथा rest केवलं name भवतः।
+पूर्णाङ्क-range `[1..3]` अन्तं गृह्णाति; `[1...3]` अन्तं न गृह्णाति।
+Range अधोमुखोऽपि भवति: `[3..1]` `[3, 2, 1]` ददाति, `[3...1]` `[3, 2]` ददाति।
+array-slice a[start..end] अन्तं गृह्णाति, a[start...end] अन्तं न गृह्णाति; सीमा finite integer array-अन्तर्गतौ, negative अन्तात्, nil-safe slice nil-receiver मध्ये सीमौ न मूल्यते।
+nil-विशेष-fallback left ? right इति; false तथा zero न परिवर्तेते।
+postfix value? non-nil एव परीक्षते: nil? false, false? तथा 0? true; unbound-name-error न गोप्यते।
+name ?= value unbound अथवा nil नाम्नि एव लिखति; non-nil right-side त्यजति, member/index/pattern न।
+नाम्नि strict prefix/postfix update अपि स्तः: next = ++counter नूतनं, previous = counter-- पूर्व-मूल्यं ददाति; केवलं name मान्यः।
+arithmetic मध्ये floor-division // तथा dividend-dependent modulo %% अपि स्तः: -7 // 5 = -2, -7 %% 5 = 3।
+value in array array-सदस्यं परीक्षते; key of map map-स्वकीय-string-key परीक्षते।
+value not in array तथा key not of map तयोः strict निषेधौ स्तः, prototype विना।
+map-literal मध्ये {name} इति {name: name} संक्षेपः अस्ति।
+assignment-pattern array-map nested भवति; VM सर्वं परीक्ष्य पश्चात् एव binding परिवर्तयति।
+array तथा call मध्ये items... array-विस्तारः, JavaScript apply विना।
+nil-सुरक्षित suffix a?.name, a?[i], f?(args) केवलम् nil-receiver मध्ये short-circuit करोति।
+until condition then body पुनः पुनः, यावत् Boolean-condition सत्यम् भवति।
+वाक्य-स्थाने postfix while/until पूर्ण-assignment अथवा strict-destructuring पुनरावर्तयति, सामान्य-subexpression न।
+loop body अनन्तः while true; break निर्गमं करोति, fuel सीमा तिष्ठति।
+for-expression शरीर-मूल्यानि नूतने array मध्ये सञ्चिनोति; when तथा continue त्यजतः, break सञ्चित-पूर्वभागं रक्षति।
+for-binding strict-pattern भवति: for [left, right] in pairs प्रत्येक-pair पूर्णतया बध्नाति।
+array-for by step उपयुज्यते; non-zero finite integer step एकवारं मूल्यते, negative क्रमः अन्तिम-पदात् आरभते, map तु न।
+array-for शून्यात् गणितं index अपि बध्नाति: for value, index in items then value + index।
+postfix-comprehension समानं strict-collection वहति: value * 2 for value in items, अथवा [value * 2 for value in items]।
+
+## Code
+
+````quickcoffee
+
+base = 40
+add = (x) -> x + base
+shorthand = 'yes'
+[first, {point: [x, y]}] = [0, {point: [20, 22]}]
+scale = ([left, right], {factor}) -> (left + right) * factor
+add(2) == 42 and "फलम् #{add(2)}" == 'फलम् 42' and yes is on and no is off and 1 < 2 < 3 and x + y == 42 and scale([20, 1], {factor: 2}) == 42 and ((mukha, y = 2) -> mukha + y)(40) == 42 and ((mukha, puchcha...) -> mukha + len(puchcha))(40, 1, 2) == 42 and ((items) -> for n in items then if n == 42 then return n)([1, 42]) == 42 and ((-> try return 1 catch error then 2 finally 0)()) == 1 and len([1..3]) == 3 and len([1...3]) == 2 and (nil ? 42) == 42 and (false ? 42) == false and nil?.missing == nil and 2 in [1, 2] and 'name' of {name: 1} and {shorthand}.shorthand == 'yes' and len([1, [2, 3]..., 4]) == 4
+पदयोग = 0
+for n in [1..9] by 3 then पदयोग = पदयोग + n
+पदयोग == 12
+len(for [left, right] in [[20, 22], [1, 2]] then left + right) == 2
+postfix_doubles = value * 2 for value in [1..3]
+postfix_doubles == [2, 4, 6]
+counter_update = 2
+prefix_update = ++counter_update
+postfix_update = counter_update--
+[prefix_update, postfix_update, counter_update] == [3, 3, 3]
+[-7 // 5, -7 %% 5] == [-2, 3]
+[5 & 3, 5 | 2, 5 ^ 1, ~1, 1 << 3, -8 >> 2, -1 >>> 1] == [1, 7, 4, -2, 8, -2, 2147483647]
+continued = 1 +
+  2 * 3
+continued == 7
+message = "hello
+  world"
+message == 'hello world'
+escaped = "A\\x42\\u{43}"
+escaped == 'ABC'
+folded = (1 + 2 * 3) == 7
+folded
+values = [
+  1
+  2
+]
+values == [1, 2]
+record = {
+  first: 20
+  second: 22
+}
+record.first + record.second == 42
+indented_record =
+  first: 20
+  nested:
+    second: 22
+indented_record.nested.second == 22
+implicit_add = (left, right) -> left + right
+implicit_answer = implicit_add 20, 22
+implicit_answer == 42
+स्थित = 40
+स्थित + 2 == 42
+3 not in [1, 2] and 'missing' not of {present: 1}
+loop_count = 0
+loop
+  loop_count = loop_count + 1
+  break if loop_count == 3
+loop_count == 3
+bare_add = left, right -> left + right
+bare_add(20, 22) == 42
+postfix_count = 0
+postfix_count = postfix_count + 1 while postfix_count < 3
+postfix_count == 3
+slice_values = [0..4][1..3]
+len(slice_values) == 3 and slice_values[0] == 1 and [0..4][-3...-1][0] == 2
+nil? == false and false? == true and 0? == true
+default_value ?= 42
+default_value == 42
+### invalid ` source अत्र उपेक्षितः
+###
+42 == 42
+````
+
+## Final value
+
+`true`
diff --git a/docs/manual.devanagari.sa.md b/docs/manual.devanagari.sa.md
deleted file mode 100644
index 65d5dc9..0000000
--- a/docs/manual.devanagari.sa.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# QuickCoffee मार्गदर्शिका (देवनागरी, संस्कृत)
-
-`QuickCoffee` Rust-निर्मितः bytecode-यन्त्रः अस्ति, JavaScript-runtime न। स्रोतः पठ्यते, संकल्यते, परीक्ष्यते, ततः चालयते। prototype-chain, `this`, `eval`, अन्तःस्थ-JavaScript च न सन्ति।
-
-triple-quote heredoc newline रक्षति: `"""…"""` `#{expression}` interpolates, `'''…'''` literal भवति; indentation न छिद्यते, unclosed delimiter lexical-error भवति।
-
-`#` एक-पङ्क्ति-comment आरभते। `### … ###` अनन्तर्निहित block-comment अस्ति; layout तथा parse पूर्वं त्यज्यते, अयुक्त-closure lexical-error भवति।
-
-नामानि Unicode XID नियमं अनुसरन्ति: प्रथमं XID start अथवा `_`, पश्चात् XID continue अथवा `_`। संयोजक-चिह्नानि नाम निरन्तरयन्ति; engine Unicode-normalization न करोति।
-
-Quoted-string सामान्य-control-escape तथा `\\xNN`, `\\uNNNN`, `\\u{...}` Unicode-escape गृह्णाति; अवैध अथवा non-scalar escape parse-error जनयति।
-
-CoffeeScript-नामानि type न परिवर्तयन्ति: `yes`/`on` = `true`, `no`/`off` = `false`, `is`/`isnt` strict `==`/`!=` स्तः।
-
-strict अथवा numeric-comparison श्रृङ्खला भवति: `1 < middle() < 3` मध्ये middle एकवारं मूल्यते, पूर्वं false चेत् परं न मूल्यते।
-
-`qcoffee -e "print(range(1, 4))"` प्रयुञ्जीत। `qcoffee --check FILE` स्रोतं compile-verify करोति, न चालयति; `--fuel N` निर्देश-संख्यां सीमयति। `print`, `len`, `type`, `range`, `str`, `abs`, `sum`, `min`, `max`, `keys`, `values`, `join`, `split`, `assert` मानक-सहायकाः; संख्या-संग्रहाः केवलं finite-संख्याः गृह्णन्ति; `range(a, b)` मध्ये `b` न गृह्यते।
-
-`qcoffee -` मानक-input तः स्रोतः पठति; `qcoffee --dump-bytecode -` तमेव स्रोतं चालनं विना विच्छिनत्ति।
-`qcoffee --stats` instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति, stdout अपरिवर्तितं स्थापयति; एकमेव source ग्राह्यः, विरोधि execution-mode तु usage-दोषं जनयति।
-
-`qcoffee --interactive` (वा `-i`) पङ्क्तिषु एकं Context धारयति; `:help` आदेशान् दर्शयति, `:quit`/`:exit` सत्रं समापयतः। pipe-input मध्ये prompt न भवति।
-`for character, index in 'a☕中' then index` `[0, 1, 2]` ददाति; string Unicode-scalar-क्रमेण चलति, `by` न स्वीकरोति।
-[head, tail...] = [1, 2, 3] tail-नाम्नि [2, 3] बध्नाति; array-pattern rest अन्तिमः भवति।
-`--stats` सहिते प्रत्येक non-empty पङ्क्तिः instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति।
-
-`--` पश्चात् argumentाः साधारण-string-array `argv` रूपेण दीयन्ते: `qcoffee program.qc -- first second` मध्ये `len(argv)` `2` भवति। host-process अथवा environment-object न प्रकाश्यते।
-
-कार्यं `(x) -> expression` अथवा bare-name `left, right -> left + right` इति लिख्यते; lexical-environment गृह्णाति। default, rest, pattern तु parentheses अपेक्षन्ते। अन्तिम-सामान्य-parameter default सहितः भवितुं शक्नोति, यथा `(head, separator = '-') -> expression`; argument अभावे अथवा `nil` दत्ते default-expression कार्यस्य अन्तरे मूल्यते, अतः पूर्व-parameter तथा captured-environment पश्यति। आवश्यक-parameter default-parameter पूर्वं स्थापनीयः। अन्तिमः rest-parameter `(head, tail...) -> expression` शेषान् argumentान् array मध्ये बध्नाति। `qdocco FILE -o FILE.html` साहित्य-दस्तावेजं जनयति; `qtest FILE...` सफलं भवति यदा सर्वेषां अन्तिम-मूल्यं `true` भवति।
-
-`return expression` केवलं कार्यस्य अन्तरे मान्यः, तत् कार्यं शीघ्रं समाप्तं करोति; केवलः `return` `nil` फलति। अन्तःस्थ-कार्यं न अतिक्रामति। सक्रिय-loop शुद्धीकरोति तथा अन्तःतः बहिः `finally` चलयति; `finally` मध्ये return पूर्वफलम् परिवर्तयति। सशर्त-return `if condition then return value` इति लिख्यते।
-
-parameter strict-pattern अपि भवति: `([left, right], {factor}) -> (left + right) * factor`। प्रत्येक argument pattern अनुरूपः भवेत्; default केवलं name-parameter, rest केवलं अन्तिम-name भवति।
-
-map-literal मध्ये `{name}` इति `{name: name}` संक्षिप्त-रूपम्; string-key स्पष्ट-मूल्यम् अपेक्षते।
-
-assignment-pattern array तथा map मध्ये nested भवितुं शक्नोति: `[first, {point: [x, y]}] = [1, {point: [20, 22]}]`। array प्रत्येकस्तरे exact-length अपेक्षते, map निर्दिष्ट-key अपेक्षते; VM सर्व-pattern परीक्ष्य पश्चात् एव binding परिवर्तयति।
-
-array-item अथवा call-argument पश्चात् `...` array-विस्तारं करोति: `[1, values..., 4]` तत्त्वानि योजयति, `fn(values...)` पृथक् argument ददाति। विस्तृत-वस्तु array भवेत्।
-
-nil-सुरक्षित suffix CoffeeScript-रीत्या `record?.name`, `values?[index]`, `fn?(args)` इति। receiver `nil` चेत् फलम् `nil` भवति, index अथवा argument न मूल्यते; non-nil receiver सामान्य-strict नियमम् अनुसरति।
-
-`qtest --fuel N FILE...` प्रत्येक-document पृथक् instruction-budget ददाति; एकस्य सीमित-loop अन्यस्य budget न क्षिणोति।
-`qtest --stats` प्रत्येकस्य documentस्य instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति, `ok`-निर्गमं न परिवर्तयति।
-
-क्रमः `for item in items then expression` इति लिख्यते; binding strict-pattern अपि भवति, यथा `for [left, right] in pairs then left + right`, तथा प्रत्येक-item-स्य सर्व-binding पूर्ण-match पश्चात् एव परिवर्तते। body-मूल्यानि नूतन-array मध्ये संगृह्णाति, `when`-अस्वीकृतानि न संगृह्णाति, `break` संगृहीत-prefix ददाति। `by step`, यथा `for item in [1..9] by 3 then expression`, एकवारं-मूल्यितं non-zero finite integer पदं ददाति; negative क्रमः अन्तिम-पदात् आरभते। map-क्रमे `by` नास्ति; `break` तथा `continue` अन्तःस्थितं क्रमं नियच्छतः; while, until, loop nil फलन्ति।
-
-स एव collector CoffeeScript-postfix-comprehension अपि स्वीकरोति: `value * 2 for value in items`, अथवा `[value * 2 for value in items]`। brackets केवलं comprehension-सीमा, अतिरिक्त nested-array न; `by`, `when`, map, pattern, `break`, `continue` prefix-रूपस्य नियमैः चलन्ति।
-
-पूर्णाङ्क-range `[1..3]` अन्तं गृह्णाति, अतः `[1, 2, 3]` भवति; `[1...3]` अन्तं न गृह्णाति, अतः `[1, 2]` भवति; अधोमुख-range `[3..1]` `[3, 2, 1]` भवति। सीमा finite integer भवेत्।
-
-postfix `value?` केवलं non-nil परीक्षते: `nil?` false, `false?` तथा `0?` true; unbound-name-error न गोपयति, `left ? right` fallback अपि न।
-
-`name ?= value` केवलं name unbound अथवा nil चेत् value मूल्ययित्वा बध्नाति; non-nil चेत् right side न चलति। केवलं name, member/index/destructuring न; साधारण unbound-name-read error एव।
-
-नाम्नि strict numeric update अपि अस्ति: `next = ++counter` नूतनं मूल्यं ददाति, `previous = counter--` decrement पूर्वं पुरातनं मूल्यं ददाति। केवलं साधारण-name मान्यः।
-
-CoffeeScript-arithmetic मध्ये floor-division `a // b` तथा modulo `a %% b` अपि स्तः; `-7 // 5` `-2`, `-7 %% 5` `3` भवति, सामान्य `%` तु dividend-संकेतं रक्षति।
-
-बिट्-क्रियाः कठोरैः signed 32-bit अङ्कैः भवन्ति: `&`, `|`, `^`, `~`, `<<`, `>>`, `>>>`; स्थानान्तरण-सङ्ख्या 0 तः 31 पर्यन्तं, संयुक्तरूपाणि केवलं नाम्नि।
-
-पङ्क्तेः अन्ते स्पष्टः operatorः चेत् अभिव्यक्तिः अग्रिम-पङ्क्तौ निरन्तरं भवति; निरन्तर-पङ्क्तेः indentation केवलं विन्यासः, layout न परिवर्तयति।
-
-सामान्य-उद्धृत-पाठः पङ्क्त्यन्तरं गन्तुं शक्नोति; नूतन-पङ्क्तिः एकं space भवति, अन्त्यः backslash तु तां निवारयति।
-
-`(1 + 2 * 3) == 7` इव शुद्धं literal-अङ्कगणितं compilation-काले परीक्षित-constant रूपेण सङ्कुच्यते।
-
-array-slice `items[start..end]` अन्तं गृह्णाति, `items[start...end]` अन्तं न गृह्णाति। सीमौ वामतः दक्षिणं एकवारं मूल्येते, finite integer तथा array-सीमा अन्तर्गतौ भवेताम्। negative-index अन्तात् गणयति, `-1` अन्तिमः; array एव छेद्यः, implicit truncation न। receiver nil चेत् `items?[start..end]` nil फलति, सीमौ न मूल्येते।
-
-`left ? right` nil-विशेष-fallback अस्ति: `left` nil भवति चेत् एव `right` मूल्यते। `false`, zero, रिक्त-string, रिक्त-array च रक्षिताः भवन्ति।
-
-`value in array` QuickCoffee-साम्येन array-सदस्यं परीक्षते, `value not in array` तस्य निषेधः। `key of map` map-स्वकीय-string-key परीक्षते, `key not of map` तस्य निषेधः; map मध्ये prototype-key न सन्ति।
-
-`until condition then body` प्रतिलोम-loop अस्ति: यावत् Boolean-condition सत्यम् न भवति तावत् पुनरावर्तते; `break`, `continue`, indentation, fuel नियमाः `while` इव भवन्ति।
-
-वाक्य-स्थाने `n = n + 1 while n < 3` postfix-loop अस्ति, prefix while इव सम्पूर्ण-assignment पुनरावर्तयति; `until` अपि तथा। strict-destructuring body भवति, सामान्य-subexpression अन्तरे न।
-
-`loop body` अनन्तः `while true`-रूपः अस्ति; `break` तं समाप्तं करोति, fuel-सीमा तिष्ठति।
-
-`for`-iterable तथा `then` मध्ये `when condition` स्थापनेन filter भवति: `for n in [1..5] when n > 2 then print(n)` अस्वीकृत-मूल्येषु body न चलति।
-
-host-त्रुटिः संरचिता: `error.kind()` `ErrorKind::Parse`/`Verify`/`Runtime` ददाति, `error.message()` प्रदर्शन-पाठं न विश्लेष्य विवरणं ददाति, `error.position()` कदाचित् एकतः गणितं स्रोत-पङ्क्तिं ददाति।
-
-पुनःचालनाय `Engine::compile_program` एकवारं compile तथा verify कृत्वा साझा `Program` निर्माति, `Context::run_program` तं चालयति; handle-स्य प्रतिलिपिः bytecode न प्रतिलिपयति, पुनः verification अपि न करोति।
-
-`Context::last_execution()` अन्तिम-सफलतायाः वा runtime-विफलतायाः `ExecutionStats` ददाति; `instructions` तथा `fuel_remaining` तत्र स्तः, compilation अथवा verification-दोषः पूर्वलेखं न परिवर्तयति।
-
-> 注:此文件按“天成文”近似“天城文(Devanagari)”的解释提供;若所指为其他语言或文字,可替换为经审订译本。
-
-बहु-पङ्क्ति array तथा map मध्ये comma त्यक्तुं शक्यते; call-argument तथा सामान्य parenthesis मध्ये स्पष्ट-विभागः आवश्यकः।
-
-एकाकी assignment (`record =`) अनन्तरं indentation द्वारा map लिखितुं शक्यते; nested `key: value` prototype-विहीनं भवति, सामान्य continuation न विपर्यस्यते।
-
-एकस्यां logical-line मध्ये call-parenthesis विना अपि शक्यते: `implicit_answer = implicit_add 20, 22`; comparison अथवा layout-boundary मध्ये explicit parenthesis प्रयोजनीया।
-
-`qtest --json` प्रत्येक-परीक्षा-पत्राय स्थिरं JSON लिखति, `qtest --tap` TAP 13 ददाति; `qcoffee --fingerprint FILE` लेखं न चालयित्वा नियत-bytecode-कुञ्जीं दर्शयति; `qbench --json` semantic-रक्षणेन compile, verify, execute कालं मापयति; `qdocco --markdown` समीक्षायै literate Markdown जनयति। अन्तःस्थापकः Context::set_fuel द्वारा पुनःप्रयुक्त-सन्दर्भस्य सीमा परिवर्तयितुं शक्नोति, Value::kind() तथा Value::is_nil() द्वारा प्रकारं परीक्षते, तथा `cargo run --example embed` पूर्णं host-उदाहरणं चालयति।
diff --git a/docs/manual.en.html b/docs/manual.en.html
index cb419cf..89f794a 100644
--- a/docs/manual.en.html
+++ b/docs/manual.en.html
@@ -13,6 +13,8 @@
 

qtest --stats writes each file's instruction count and remaining fuel to stderr without changing its ok output.

qtest --json writes one stable JSON result per file for CI consumers; --stats remains on stderr.

qtest --tap writes TAP version 13 records with deterministic numbering; --json and --tap are mutually exclusive.

+

qtest --filter TEXT selects matching paths, while qtest --list enumerates selected files without executing them.

+

qcoffee --json emits one JSON value or structured error for a single execution, suitable for CI and hosts.

Rust embedding errors expose ErrorKind::Parse, Verify, or Runtime plus a display-independent message; host callbacks may return Error::runtime("message"), and error.position() may give a one-based source line.

Engine::compile_program verifies once; Context::run_program reuses the immutable verified bytecode for repeated embedding calls.

Program::fingerprint provides a deterministic u64 bytecode cache key without changing execution.

@@ -20,7 +22,7 @@

qbench --json emits one timing record per guarded workload; --iterations controls sample count.

Fingerprints use explicit canonical bytecode encoding, not Rust debug formatting, so cache keys survive toolchain display changes.

qdocco --markdown writes Notes, fenced QuickCoffee code, and the final value as a reviewable Markdown artifact.

-

Embedders may call Context::set_fuel between runs; Context::fuel reports the current per-run budget without clearing globals.

+

Embedders may call Context::set_fuel between runs; Context::fuel reports the current per-run budget without clearing globals, while with_global and with_native provide chainable setup.

cargo run --example embed compiles a minimal Rust host that sets a global, registers a native callback, and evaluates QuickCoffee.

A host can branch on Value::kind() and use Value::is_nil() without inspecting internal containers.

Cargo package metadata points embedding users to the repository, docs.rs API, README, and license.

@@ -66,7 +68,8 @@

Arithmetic also has floor division // and dividend-dependent modulo %%: -7 // 5 is -2, while -7 %% 5 is 3.

return expression exits only its current function; bare return yields nil, cleans loops, and runs enclosing finally blocks.

Parameters may use strict nested array/map patterns; defaults and rest stay name-only.

-

Code

base = 20
+

Code


+base = 20
 add = (x) ->
   result = x + base
   result
diff --git a/docs/manual.en.md b/docs/manual.en.md
index e19b5e9..d568d16 100644
--- a/docs/manual.en.md
+++ b/docs/manual.en.md
@@ -1,118 +1,154 @@
-# QuickCoffee manual
-
-QuickCoffee is a Rust bytecode engine, not a JavaScript runtime. Source is parsed, compiled, verified, then run. There are no prototypes, `this`, `eval`, or embedded JavaScript.
-
-Triple-quoted heredocs preserve newlines: `"""…"""` interpolates `#{expression}`, while `'''…'''` stays literal. Their content is not indentation-trimmed; an unclosed delimiter is a lexical error.
-
-`#` starts a line comment. A non-nesting `### … ###` block comment is removed before layout and parsing; an unclosed delimiter is a lexical error.
-
-Identifiers use Unicode XID rules: XID start or `_` first, then XID continue or `_`. Combining marks may therefore continue a name; the engine does not normalize Unicode.
-
-Quoted strings decode common control escapes plus `\\xNN`, `\\uNNNN`, and `\\u{...}` Unicode escapes. Invalid escapes and non-scalar Unicode values are parse errors.
-
-CoffeeScript-style spellings are available without changing runtime types: `yes`/`on` mean `true`, `no`/`off` mean `false`, and `is`/`isnt` mean strict `==`/`!=`.
-
-Adjacent strict or numeric comparisons may chain: `1 < middle() < 3` evaluates `middle()` once and stops before later operands when an earlier comparison is false.
-
-Run `qcoffee -e "print(range(1, 4))"`, `qcoffee --fuel 10000 program.qc`, `qcoffee --check program.qc`, or `qcoffee --dump-bytecode program.qc`. `--check` parses, compiles, and verifies without executing. Fuel limits executed instructions; exhaustion is a safe error. The small standard library contains `print`, `len`, `type`, end-exclusive `range(a, b)`, `str`, `abs`, `sum`, `min`, `max`, `keys`, `values`, `join`, `split`, and `assert`; numeric aggregators accept strict finite-number arrays.
-
-`qcoffee -` reads source from standard input, which is convenient in pipelines; `qcoffee --dump-bytecode -` disassembles that same input instead of executing it.
-`qcoffee --stats` writes instruction and remaining-fuel counters to stderr while preserving program stdout; qcoffee accepts one source input and rejects conflicting execution modes.
-
-`qcoffee --interactive` (or `-i`) keeps one Context across input lines; `:help` lists commands and `:quit`/`:exit` leave the session. Piped input receives no prompts.
-With `--stats`, each non-empty interactive line that executes or reaches a runtime error writes its instruction and remaining-fuel counters to stderr; parse and verify errors write no fresh record.
-`for character, index in 'a☕中' then index` yields `[0, 1, 2]`; strings iterate Unicode scalars and reject `by`.
+# QuickCoffee document
+
+## Notes
+
+QuickCoffee manual
+Source is parsed, compiled to verified bytecode, and executed with a fuel budget.
+qcoffee - reads a QuickCoffee program from standard input.
+qcoffee --stats writes instruction and remaining-fuel counters to stderr while preserving program stdout; qcoffee accepts one source input and rejects conflicting execution modes.
+qcoffee --check FILE parses, compiles, and verifies without executing FILE.
+qcoffee --interactive (or -i) keeps one Context for a line-oriented session; :help and :quit are built-in commands.
+qcoffee --interactive --stats writes one instruction/fuel record for each non-empty line that executes or reaches a runtime error; parse and verify errors write none.
+for character, index in 'a☕中' then index yields [0, 1, 2]; strings iterate Unicode scalars and accept non-zero signed by steps.
+do (name, other) -> ... immediately calls and forwards same-named outer values; do -> ... remains zero-argument.
 [head, tail...] = [1, 2, 3] binds tail to [2, 3]; array-pattern rest must be final.
-
-Arguments after `--` are exposed as the ordinary string array `argv`: `qcoffee program.qc -- first second` makes `len(argv)` evaluate to `2`. No host process or environment object is exposed.
-
-Functions use `(x) -> expression` or bare names such as `left, right -> left + right`, and capture their lexical environment. Defaults, rest, and patterns still require parentheses. Calls may omit parentheses on one logical line, while explicit parentheses remain available for unambiguous grouping. A trailing parameter may have a default, as in `(head, separator = '-') -> expression`; an omitted or explicit `nil` argument evaluates that default inside the callee, so it may use earlier parameters and lexical captures. Required parameters must precede defaults. A final rest parameter (`(head, tail...) -> expression`) accepts remaining values, bound as an array. Maps are indexed by strings: `{name: 'coffee'}['name']`.
-
-`return expression` is valid only in a function and immediately exits that function; bare `return` yields `nil`. It never crosses a nested function. It cleans an active loop and runs enclosing `finally` blocks from inner to outer; a `return` in `finally` replaces the pending result. Write `if condition then return value` for a conditional return.
-
-Parameters may also use strict recursive patterns: `([left, right], {factor}) -> (left + right) * factor`. Each argument must match its pattern before the function starts. Defaults remain name-only and rest remains a final name.
-
-Within a map literal, `{name}` is shorthand for `{name: name}`; string keys still require an explicit value, as in `{'name': value}`.
-
-Assignment patterns can nest arrays and maps: `[first, {point: [x, y]}] = [1, {point: [20, 22]}]`. Arrays match their exact length; maps require their listed identifier keys. The VM validates the full pattern before changing any binding, so a deep mismatch is atomic.
-
-An array or call item with trailing `...` expands an array: `[1, values..., 4]` concatenates its elements, while `fn(values...)` passes them as individual arguments. A splat must be an array; it never invokes a JavaScript-style `apply` method.
-
-Nil-safe suffixes use CoffeeScript-style soak syntax: `record?.name`, `values?[index]`, and `fn?(args)`. If the receiver is `nil`, the suffix returns `nil` and does not evaluate an index or argument. A non-nil receiver follows ordinary strict access rules, so a missing map member still reports an error.
-
-Arrays (including `range` results) can be iterated with `for item in items then expression`, or with one-time-evaluated non-zero integer stepping: `for item in [1..9] by 3 then expression`; a negative step starts at the last item. A second binding receives the zero-based position, as in `for item, index in items then item + index`; with `by`, it receives the actual stepped position. The binding is a strict recursive pattern, so `for [left, right] in pairs then left + right` and `for {point: {x, y}} in values then x + y` are valid; all bindings for an item change atomically. A `for` expression collects body values into a new array; rejected `when` items are omitted and `break` returns the collected prefix. `break` and `continue` affect the innermost loop. `while`/`until`/`loop` evaluate to `nil`; map iteration excludes `by`.
-
-The same collector has CoffeeScript's postfix comprehension form: `value * 2 for value in items`, or `[value * 2 for value in items]`. The brackets delimit the comprehension and do not create an extra nested array; `by`, `when`, map iteration, patterns, `break`, and `continue` retain their prefix-form semantics.
-
-Integer range literals are arrays built directly by the bytecode VM: `[1..3]` includes its end (`[1, 2, 3]`), while `[1...3]` excludes it (`[1, 2]`); descending forms work too, so `[3..1]` is `[3, 2, 1]`. Their bounds must be finite integers.
-
-Array slices use `items[start..end]` for an inclusive end and `items[start...end]` for an exclusive end: `[0..4][1..3]` is `[1, 2, 3]`. Bounds evaluate once from left to right and must be finite in-range integers; negative bounds count from the end, so `-1` is the last item. Slices are arrays only and never clip implicitly. `items?[start..end]` returns `nil` without evaluating bounds when its receiver is `nil`.
-
-`left ? right` is a nil-specific fallback: it evaluates `right` only when `left` is `nil`. Unlike a truthiness default, it preserves `false`, `0`, empty strings, and empty containers.
-
-The postfix `value?` tests only for non-nil: `nil?` is `false`, while `false?` and `0?` are `true`. It does not hide an unbound-name error and is distinct from the `left ? right` fallback.
-
-`name ?= value` evaluates and stores `value` only when the name is unbound or currently nil; a non-nil value skips the right side. Names also support strict arithmetic compound assignment such as `total += amount` and `power **= 2`. These forms are name-only, never member/index/destructuring assignment, and an ordinary unbound-name read remains an error.
-
-Names also support strict numeric updates: `next = ++counter` yields the new value, while `previous = counter--` yields the old value before decrementing. Updates are name-only and reject members, indexes, and destructuring.
-
-CoffeeScript arithmetic also provides floor division `a // b` and dividend-dependent modulo `a %% b`; for example, `-7 // 5` is `-2` and `-7 %% 5` is `3`. Ordinary `%` remains the signed remainder.
-
-Bitwise operators use strict signed 32-bit numbers: `&`, `|`, `^`, `~`, `<<`, `>>`, and `>>>`; shifts accept counts from 0 through 31, and compound forms are name-only.
-
-An explicit operator at a physical line end continues the expression on the next line; continuation indentation is layout-neutral until the expression ends.
-
-Ordinary quoted strings may span lines: a newline joins as one space, while a trailing backslash removes the newline.
-
-Pure literal arithmetic such as `(1 + 2 * 3) == 7` is folded into verified bytecode constants.
-
-`value in array` checks array membership with QuickCoffee equality, while `value not in array` negates it. `key of map` checks an own string key in a map, while `key not of map` negates it; maps have no prototype keys to inspect.
-
-`until condition then body` is the inverse loop form: it repeats until its Boolean condition becomes true, using the same `break`, `continue`, indentation, and fuel rules as `while`.
-
-At statement position, `n = n + 1 while n < 3` is a postfix loop equivalent to the prefix while and repeats the whole assignment; `until` works likewise. Strict destructuring may also be its body. A postfix loop cannot be nested inside an ordinary subexpression.
-
-`loop body` is the infinite `while true` form. Exit it with `break`; it remains fuel-limited. For example: `n = 0; loop then if n == 3 then break else n = n + 1`.
-
-Put `when condition` between a `for` iterable and `then` to filter a loop without running the body for rejected bindings: `for n in [1..5] when n > 2 then print(n)`.
-
-A prototype-free data factory uses `class Point(x, y = 0) -> {x: x, y: y}` and follows the same default-parameter rules as a function. Calling it returns an ordinary map, so `Point(3).x` reads a member; there is no `this`, `new`, or inheritance.
-
-Double quotes interpolate QuickCoffee expressions: `"answer=#{add(21)}"`. Single quotes do not, and interpolation never runs JavaScript.
-
-Use `switch value` with indented `when pattern` branches for strict-equality selection. Exactly one branch is selected; there is no fallthrough.
-
-Exceptions use `try`, `catch error`, optional `finally`, and `throw value`. A catch receives a stable error string rather than a JavaScript Error object; function returns also run applicable finalizers.
-
-For Rust embedding, create `Context`, optionally call `with_fuel`, register a host callback with `add_native`, then call `eval`; callbacks can return `Error::runtime("message")` and the script may catch it. For repeated execution, compile once with `Engine::compile_program` (which verifies once) and pass the shared `Program` to `run_program`; cloning that handle does not copy bytecode or repeat verification. `Value::from`, `Value::string`, `Value::array`, and `Value::map` construct host values without exposing VM reference-counting internals; `Value::kind()` and `Value::is_nil()` provide stable type checks.
-
-`Context::last_execution()` returns public `ExecutionStats` (`instructions` and `fuel_remaining`) for the latest successful or runtime-failed execution; compile and verification errors leave the previous record unchanged.
-
-`cx.get_global("host_values")` reads a script or host global without executing code and returns `None` for an unknown name. It returns a public `Value` clone only, never an environment or call frame.
-
-Embedding errors are structured: `error.kind()` returns `ErrorKind::Parse`, `ErrorKind::Verify`, or `ErrorKind::Runtime`, `error.message()` returns its detail, and `error.position()` may provide a one-based source line. Hosts need not parse display text; `Display` remains suitable for CLI output and QuickCoffee `catch` strings.
-
-```rust
-let mut cx = quickcoffee::Context::new().with_fuel(100_000);
-cx.set_global(
-    "host_values",
-    quickcoffee::Value::array(vec![
-        quickcoffee::Value::from(40_i64),
-        quickcoffee::Value::from(2_i64),
-    ]),
-);
-let value = cx.eval("host_values[0] + host_values[1]")?;
-```
-
-`qdocco FILE -o FILE.html` verifies and renders executable documentation; `qtest FILE_OR_DIRECTORY...` recursively discovers `.qc` files and passes only when every final value is `true`.
-
-`qtest --fuel N FILE_OR_DIRECTORY...` gives every discovered test file its own instruction budget, so a deliberately bounded loop cannot consume the budget of another test.
-`qtest --stats` additionally writes each file's instruction count and remaining fuel to stderr without changing its `ok` output.
-
-Multiline arrays and maps may omit commas at line boundaries; calls and ordinary parentheses still require explicit separators.
-
-An indented map may follow a standalone assignment (`record =`); nested `key: value` entries become a prototype-free map without changing ordinary assignment continuations.
-
-Calls may omit parentheses on one logical line: `implicit_answer = implicit_add 20, 22`; explicit parentheses remain available for unambiguous comparisons and layout boundaries.
-
-`qtest --json` emits one stable JSON record per test file, while `qtest --tap` emits TAP 13 records. `qcoffee --fingerprint FILE` prints a canonical verified-bytecode cache key without executing the file. `qbench --json` reports guarded compile, verify, and execute timings; `qdocco --markdown` writes reviewable literate Markdown. Embedding hosts can adjust a reused context with `Context::set_fuel` and run the complete host example with `cargo run --example embed`.
+qtest --fuel N gives each executable documentation file its own instruction budget.
+qtest --stats writes each file's instruction count and remaining fuel to stderr without changing its ok output.
+qtest --json writes one stable JSON result per file for CI consumers; --stats remains on stderr.
+qtest --tap writes TAP version 13 records with deterministic numbering; --json and --tap are mutually exclusive.
+qtest --filter TEXT selects matching paths, while qtest --list enumerates selected files without executing them.
+qcoffee --json emits one JSON value or structured error for a single execution, suitable for CI and hosts.
+Rust embedding errors expose ErrorKind::Parse, Verify, or Runtime plus a display-independent message; host callbacks may return Error::runtime("message"), and error.position() may give a one-based source line.
+Engine::compile_program verifies once; Context::run_program reuses the immutable verified bytecode for repeated embedding calls.
+Program::fingerprint provides a deterministic u64 bytecode cache key without changing execution.
+qcoffee --fingerprint FILE prints the same verified bytecode key as 16 lowercase hexadecimal digits without running the file.
+qbench --json emits one timing record per guarded workload; --iterations controls sample count.
+Fingerprints use explicit canonical bytecode encoding, not Rust debug formatting, so cache keys survive toolchain display changes.
+qdocco --markdown writes Notes, fenced QuickCoffee code, and the final value as a reviewable Markdown artifact.
+Embedders may call Context::set_fuel between runs; Context::fuel reports the current per-run budget without clearing globals, while with_global and with_native provide chainable setup.
+cargo run --example embed compiles a minimal Rust host that sets a global, registers a native callback, and evaluates QuickCoffee.
+A host can branch on Value::kind() and use Value::is_nil() without inspecting internal containers.
+Cargo package metadata points embedding users to the repository, docs.rs API, README, and license.
+Context::last_execution() exposes instruction and remaining-fuel counters without VM frames.
+Arguments after -- are exposed as the ordinary string array argv.
+This is not JavaScript: prototypes, this, eval, and embedded JavaScript do not exist.
+# is a line comment; ### … ### is a non-nesting block comment removed before layout and parsing.
+Identifiers use Unicode XID rules; combining marks may continue a name and no normalization occurs.
+yes/on and no/off are Boolean aliases; is/isnt preserve strict equality.
+! is a strict Bool alias for not; != remains strict inequality.
+Chained strict or numeric comparisons keep the middle value once and short-circuit.
+The standard library is ordinary functions: print, len, type, range, str, abs, sum, min, max, keys, values, join, split, and assert. Numeric aggregators accept strict finite-number arrays.
+switch/when selects one strict-equality branch without fallthrough.
+try/catch/finally handles QuickCoffee runtime errors without JavaScript Error objects.
+Integer ranges use [1..3] for an inclusive end and [1...3] for an exclusive end.
+Ranges may descend too: [3..1] yields [3, 2, 1], while [3...1] yields [3, 2].
+Triple-quoted heredocs preserve newlines: """...""" interpolates and '''...''' remains literal.
+Array slices use a[start..end] for an inclusive end and a[start...end] for an exclusive end; bounds are finite in-range integers, negatives count from the end, and a nil-safe slice skips bounds on nil.
+Nil-specific fallback is written as left ? right; false and zero are kept unchanged.
+Postfix value? tests only non-nil: nil? is false, false? and 0? are true, and an unbound name remains an error.
+name ?= value writes only for an unbound or nil name; a non-nil name short-circuits its right side, and members, indexes, and patterns are excluded.
+value in array checks array membership; key of map checks only map-owned string keys.
+value not in array and key not of map negate those same strict checks without prototype keys.
+In a map literal, {name} abbreviates {name: name}.
+Map literals support checked left-to-right spread: {...defaults, theme: 'dark'}; later keys win.
+Map patterns may end with ...metadata to capture unlisted keys immutably.
+Arrays and Unicode strings accept negative indices: items[-1] is the final item.
+Assignment patterns may nest arrays and maps; validation is atomic before bindings change.
+In arrays and calls, items... expands an array without JavaScript apply.
+Nil-safe soak suffixes record?.name, values?[i], and fn?(args) short-circuit only a nil receiver.
+until condition then body repeats until its Boolean condition becomes true.
+At statement position, postfix while/until repeats a whole assignment or strict destructuring, not an ordinary subexpression.
+loop body is infinite while true; break exits it and fuel still bounds it.
+A for expression collects body values; when and continue omit values, and break keeps the collected prefix.
+for bindings may use strict patterns: for [left, right] in pairs binds each pair atomically.
+An array for loop may use by step; the non-zero finite integer step is evaluated once, negative steps start at the last item, and maps exclude it.
+Array for may bind a zero-based index too: for value, index in items then value + index.
+Postfix comprehensions use the same strict collector: value * 2 for value in items, or [value * 2 for value in items].
+Functions capture lexical scope; y = 2 defaults when omitted or nil, and a final rest parameter is tail....
+Plain names may omit lambda parentheses: left, right -> left + right; defaults, rest, and patterns retain parentheses.
+Names support strict arithmetic compound assignment: total += amount and power **= 2; members and indexes do not.
+Names also support strict prefix/postfix updates: next = ++counter yields the new value, while previous = counter-- yields the old value.
+Arithmetic also has floor division // and dividend-dependent modulo %%: -7 // 5 is -2, while -7 %% 5 is 3.
+return expression exits only its current function; bare return yields nil, cleans loops, and runs enclosing finally blocks.
+Parameters may use strict nested array/map patterns; defaults and rest stay name-only.
+
+## Code
+
+````quickcoffee
+
+base = 20
+add = (x) ->
+  result = x + base
+  result
+shorthand = 'yes'
+[first, {point: [x, y]}] = [0, {point: [20, 22]}]
+scale = ([left, right], {factor}) -> (left + right) * factor
+add(22) == 42 and "answer=#{add(22)}" == 'answer=42' and yes is on and no is off and 1 < 2 < 3 and x + y == 42 and scale([20, 1], {factor: 2}) == 42 and ((head, y = 2) -> head + y)(40) == 42 and ((head, tail...) -> head + len(tail))(40, 1, 2) == 42 and ((items) -> for n in items then if n == 42 then return n)([1, 42]) == 42 and ((-> try return 1 catch error then 2 finally 0)()) == 1 and len([1..3]) == 3 and len([1...3]) == 2 and (nil ? 42) == 42 and (false ? 42) == false and nil?.missing == nil and 2 in [1, 2] and 'name' of {name: 1} and {shorthand}.shorthand == 'yes' and len([1, [2, 3]..., 4]) == 4
+try throw 'manual' catch error then error == 'runtime error: thrown: manual'
+by_sum = 0
+for n in [1..9] by 3 then by_sum = by_sum + n
+by_sum == 12
+len(for [left, right] in [[20, 22], [1, 2]] then left + right) == 2
+postfix_doubles = value * 2 for value in [1..3]
+postfix_doubles == [2, 4, 6]
+counter = 2
+prefix_update = ++counter
+postfix_update = counter--
+[prefix_update, postfix_update, counter] == [3, 3, 3]
+[-7 // 5, -7 %% 5] == [-2, 3]
+[5 & 3, 5 | 2, 5 ^ 1, ~1, 1 << 3, -8 >> 2, -1 >>> 1] == [1, 7, 4, -2, 8, -2, 2147483647]
+continued = 1 +
+  2 * 3
+continued == 7
+message = "hello
+  world"
+message == 'hello world'
+escaped = "A\\x42\\u{43}"
+escaped == 'ABC'
+folded = (1 + 2 * 3) == 7
+folded
+values = [
+  1
+  2
+]
+values == [1, 2]
+record = {
+  first: 20
+  second: 22
+}
+record.first + record.second == 42
+indented_record =
+  first: 20
+  nested:
+    second: 22
+indented_record.nested.second == 22
+implicit_add = (left, right) -> left + right
+implicit_answer = implicit_add 20, 22
+implicit_answer == 42
+3 not in [1, 2] and 'missing' not of {present: 1}
+loop_count = 0
+loop
+  loop_count = loop_count + 1
+  break if loop_count == 3
+loop_count == 3
+bare_add = left, right -> left + right
+bare_add(20, 22) == 42
+postfix_count = 0
+postfix_count = postfix_count + 1 while postfix_count < 3
+postfix_count == 3
+slice_values = [0..4][1..3]
+len(slice_values) == 3 and slice_values[0] == 1 and [0..4][-3...-1][0] == 2
+nil? == false and false? == true and 0? == true
+default_value ?= 42
+default_value == 42
+heredoc = """answer #{add(22)}
+next"""
+heredoc == 'answer 42\nnext'
+### invalid ` source is safely ignored here
+###
+42 == 42
+````
+
+## Final value
+
+`true`
diff --git a/docs/manual.latin.html b/docs/manual.latin.html
index 47c45cf..fa4def7 100644
--- a/docs/manual.latin.html
+++ b/docs/manual.latin.html
@@ -17,6 +17,8 @@
 

qtest --stats numeros instructionum et alimenti reliqui cuiusque documenti ad errorem ordinarium scribit, sine mutatione exitus ok.

qtest --json unam lineam JSON pro unoquoque documento scribit ad usum CI; --stats in stderr manet.

qtest --tap versiones TAP 13 et numeros certos scribit; --json et --tap simul prohibentur.

+

qtest --filter TEXT itinera congruentia eligit; qtest --list tantum documenta electa enumerat sine exsecutione.

+

qcoffee --json unam lineam JSON valoris vel erroris structi reddit, aptam CI hospitibusque.

Errores hospitis ErrorKind::Parse, Verify, Runtime habent atque detail sine textu ostenso praebent; error.position() lineam fontis a uno numeratam interdum dat.

Engine::compile_program semel verificat; Context::run_program bytecode immutabile verificatum ad iteratum cursum reutitur.

Program::fingerprint clavem u64 determinatam praebet ad memoriam hospitis sine mutatione exsecutionis.

@@ -24,7 +26,7 @@

qbench --json unam mensurae lineam pro unoquoque onere custodito emittit; --iterations numerum exemplorum regit.

Claves codicem bytecode explicite et canonice signant, non formam Rust debug; ideo mutatio instrumenti claves non mutat.

qdocco --markdown notas, codicem QuickCoffee clausum, et valorem ultimum in documento Markdown scribit.

-

Hospes inter cursus `Context::set_fuel` vocare potest; `Context::fuel` budgetum ostendit sine globalibus deletis.

+

Hospes inter cursus `Context::set_fuel` vocare potest; `Context::fuel` budgetum ostendit sine globalibus deletis; `with_global` et `with_native` configurationem concatenatam praebent.

`cargo run --example embed` hospitem Rust minimum compilat: globale ponit, callback nativum addit, et QuickCoffee currit.

Hospes `Value::kind()` ad genus discernendum et `Value::is_nil()` ad nil probandum utitur, sine interioribus vasorum.

Notitiae Cargoe hospites ad repositorium, API docs.rs, README et licentiam ducunt.

@@ -63,7 +65,8 @@

Ordo seriei by step uti potest; gradus integer finitus positivus semel aestimatur, maps eum excludunt.

Iteratio seriei etiam indicem a zero numeratum ligare potest: for value, index in items then value + index.

Comprehensio postfix eandem collectionem strictam servat: value * 2 for value in items, vel [value * 2 for value in items].

-

Code

numerus = 7
+

Code


+numerus = 7
 quadratum = (x) -> x * x
 shorthand = 'yes'
 [first, {point: [x, y]}] = [0, {point: [20, 22]}]
diff --git a/docs/manual.latin.md b/docs/manual.latin.md
index 8ca21df..a11b98c 100644
--- a/docs/manual.latin.md
+++ b/docs/manual.latin.md
@@ -1,94 +1,145 @@
-# Manuale QuickCoffee (Latine)
-
-QuickCoffee est machina bytecodicis Rustiana, non tempus JavaScript. Fons legitur, compilatur, verificatur, deinde currit. Nulla catena prototyporum, `this`, `eval`, nec JavaScript inclusum est.
-
-Heredoc trium signorum lineas servat: `"""…"""` `#{expression}` interpolat, `'''…'''` litteralis manet; indentatio non tollitur, delimiter non clausus error lexicalis est.
-
-`#` commentarium unius lineae incipit. `### … ###` commentarium non-nidificatum est, ante layout et analysin remotum; delimiter non clausus error lexicalis est.
-
-Nomina regulas Unicode XID sequuntur: XID start vel `_` primum, XID continue vel `_` postea. Signa combinantia nomen continuare possunt; machina Unicode non normalizat.
-
-Chorda citata evadit notas communes et formas Unicode `\\xNN`, `\\uNNNN`, `\\u{...}`; evaditiones invalidae vel valores non scalarii errorem analysi pariunt.
-
-Voces CoffeeScript sine mutatione generum valent: `yes`/`on` sunt `true`, `no`/`off` sunt `false`, atque `is`/`isnt` sunt stricta `==`/`!=`.
-
-Comparationes strictae vel numericae conecti possunt: `1 < middle() < 3` medium semel aestimat et, priore falso, posteriora non aestimat.
-
-Utere `qcoffee -e "print(range(1, 4))"`; `qcoffee --check FILE` fontem compilat atque verificat sine cursu; `--fuel N` numerum instructionum finit. Bibliotheca parva habet `print`, `len`, `type`, `range(a, b)`, `str`, `abs`, `sum`, `min`, `max`, `keys`, `values`, `join`, `split`, et `assert`; aggregationes numericae solum series numerorum finitorum accipiunt; finis in `range` exclusus est.
-
-`qcoffee -` fontem ex initio normali legit; `qcoffee --dump-bytecode -` illum fontem sine cursu explicat.
-`qcoffee --stats` numeros instructionum et alimenti reliqui ad errorem ordinarium scribit, dum exitus programmatis intactus manet; unus tantum fons admittitur et modi contrarii errorem usus reddunt.
-
-`qcoffee --interactive` (vel `-i`) unum Context inter lineas servat; `:help` imperia ostendit, `:quit`/`:exit` sessionem finiunt. Input per fistulam promptum non accipit.
-`for character, index in 'a☕中' then index` indices `[0, 1, 2]` reddit; stringae per scalas Unicode iterantur et `by` non admittunt.
+# QuickCoffee document
+
+## Notes
+
+Manuale QuickCoffee
+Tabulae mappae segmenta expandere possunt; claves posteriores priores superant.
+In forma mappae ...metadata claves omissas immutabiliter capit.
+Indices negativi in seriebus et textu Unicode extremum elementum petunt.
+Fons legitur, in bytecodicem verificatum compilatur, et cum limite fuel currit.
+qcoffee - programma QuickCoffee ex initio normali legit.
+qcoffee --stats numeros instructionum et alimenti reliqui ad errorem ordinarium scribit, dum exitus programmatis intactus manet; unus tantum fons admittitur et modi contrarii errorem usus reddunt.
+qcoffee --check FILE fontem verificat sine cursu.
+qcoffee --interactive (vel -i) unum Context per lineas servat; :help docet, :quit exit.
+qcoffee --interactive --stats unam instructionum et alimenti reliqui notam lineae non vacuae exsecutæ vel errorem currendi ferenti scribit; errores analysi vel verificationis nihil scribunt.
+'a☕中'[1] est '☕', et 'a☕中'[1..2] est '☕中'; indices stringarum scalas Unicode sequuntur.
+for character, index in 'a☕中' then index indices Unicode scalarum [0, 1, 2] reddit; iteratio stringarum gradus nonnullos signatos by accipit.
+do (name, other) -> ... statim vocat et valores externos eiusdem nominis tradit; do -> ... sine argumentis manet.
 [head, tail...] = [1, 2, 3] tail ad [2, 3] ligat; rest in forma array postremum esse debet.
-Cum `--stats`, unaquaeque linea non vacua numeros instructionum et alimenti reliqui ad errorem ordinarium scribit.
-
-Argumenta post `--` ut series chordarum ordinaria `argv` praebentur: in `qcoffee program.qc -- first second`, `len(argv)` est `2`. Nulla res processus aut ambitus hospitis exponitur.
-
-Functio scribitur `(x) -> expressio` vel nominibus nudis, ut `sinister, dexter -> sinister + dexter`, ambitum lexicalem capit. Default, rest, et pattern parentheses poscunt. Parameter extremus valorem praedefinitum habere potest, ut `(caput, separator = '-') -> expressio`; argumento omisso vel `nil` dato, valor intra functionem aestimatur atque parametros priores ambitumque captum videre potest. Parametri necessari ante praedefinitos sunt. Ultimus rest, ut `(caput, cauda...) -> expressio`, reliqua argumenta in serie ligat. Ad documentum faciendum: `qdocco FILE -o FILE.html`. Ad probationes: `qtest FILE...`; omnis valor ultimus `true` esse debet.
-
-`return expressio` solum intra functionem valet eamque statim finit; nudum `return` dat `nil`. Functionem inclusam non transit. Iterationem activam purgat et `finally` circumstantia ab intimo ad externum peragit; return in `finally` eventum priorem superat. Return condicionale scribitur `if conditio then return valor`.
-
-Parametri etiam patterna stricta habere possunt: `([left, right], {factor}) -> (left + right) * factor`. Quodque argumentum pattern convenire debet; praedefinitio solum nomini et rest solum nomini ultimo datur.
-
-In littera map, `{name}` est forma brevis `{name: name}`; claves chordarum valorem explicitum poscunt.
-
-Patterna assignmentis series et maps includere possunt: `[first, {point: [x, y]}] = [1, {point: [20, 22]}]`. Series longitudinem exactam poscunt, maps claves nominatas; VM totum pattern ante mutationem ligaminum verificat.
-
-`...` post item seriei aut argumentum vocationis seriem expandit: `[1, values..., 4]` elementa coniungit et `fn(values...)` singula argumenta tradit. Res expansa series esse debet.
-
-Suffixa tuta nil more CoffeeScript scribuntur: `record?.name`, `values?[index]`, `fn?(args)`. Si recipiens est `nil`, eventus est `nil` nec index aut argumenta aestimantur; recipiens non-nil regulas strictas ordinarias sequitur.
-
-`qtest --fuel N FILE...` singulis documentis budget instructionum separatum dat; una iteratio finita alterius budget non consumit.
-`qtest --stats` etiam numeros instructionum et alimenti reliqui cuiusque documenti ad errorem ordinarium scribit, sine mutatione exitus `ok`.
-
-Ordo scribitur `for item in items then expressio`; ligamen pattern strictum esse potest, ut `for [left, right] in pairs then left + right`, et omnes ligamina cuiusque item solum post integram congruentiam mutantur. Valores corporis in seriem novam colligit, valores `when` reiecti non colliguntur, et `break` praefixum collectum reddit. `by step`, ut `for item in [1..9] by 3 then expressio`, gradum semel aestimatum et integrum finitum positivum dat. Maps `by` non accipiunt; `break` et `continue` ordinem intimum regunt; while, until, loop nil dant.
-
-Eadem collectio formam postfixam CoffeeScript habet: `value * 2 for value in items`, vel `[value * 2 for value in items]`. Bracteae solum terminum comprehensionis indicant nec seriem interiorem addunt; `by`, `when`, maps, patterna, `break`, et `continue` regulas formae praefixae servant.
-
-Spatium integrorum `[1..3]` finem includit atque `[1, 2, 3]` facit; `[1...3]` finem excludit atque `[1, 2]` facit; descendens `[3..1]` `[3, 2, 1]` facit. Fines integri finiti esse debent.
-
-Suffixum `value?` tantum non-nil probat: `nil?` false est, `false?` et `0?` true sunt; errorem nominis non ligati non celat neque recessus `left ? right` est.
-
-`name ?= value` value aestimat et ligat tantum si nomen non ligatum aut nil est; valore non-nil dextram omittit. Solum nomen admittitur, non membrum, index, aut destructio; lectio ordinaria nominis non ligati error manet.
-
-Nomina etiam strictum incrementum et decrementum habent: `next = ++counter` novum valorem reddit, `previous = counter--` priorem reddit ante decrementum. Tantum nomina simplicia admittuntur.
-
-Arithmetica CoffeeScript etiam divisionem inferiorem `a // b` et modulum `a %% b` praebet; `-7 // 5` est `-2`, `-7 %% 5` est `3`, dum `%` reliquum signum dividendi servat.
-
-Operationes bitwise numeris strictis signatis 32-bit utuntur: `&`, `|`, `^`, `~`, `<<`, `>>`, `>>>`; numerus translationis a 0 ad 31 tantum admittitur, formae compositae nomen solum accipiunt.
-
-Operator explicitus in fine lineae expressionem in linea sequenti continuat; indentatio continuationis ordinem clausularum non mutat.
-
-Textus inter notas simplices vel duplices lineas transire potest; novum-linea fit spatium unum, backslash finalis autem eam tollit.
-
-Arithmetica pura litteralis, ut `(1 + 2 * 3) == 7`, tempore compilationis in constantes verificatas redigitur.
-
-Sectio seriei `items[start..end]` finem includit, `items[start...end]` excludit; termini sinistro ad dextrum semel aestimantur atque integri finiti intra limites esse debent. Numerus negativus ab extremo numeratur, `-1` ultimum est; sola series secari potest, nec truncatio tacita fit. Recipiente nil, `items?[start..end]` nil dat nec terminos aestimat.
-
-`left ? right` recessus nil-specialis est: `right` tantum aestimatur si `left` est `nil`. `false`, zero, chorda vacua, et series vacua servantur.
-
-`value in array` membrum seriei aequalitate QuickCoffee probat, et `value not in array` contrarium. `key of map` clavem propriam map probat, et `key not of map` contrarium; map claves prototyporum non habet.
-
-`until condition then body` forma inversa ordinis est: repetit donec conditio Boolean vera sit; regulae `break`, `continue`, indentationis et fuel eae sunt ac `while`.
-
-In loco sententiae, `n = n + 1 while n < 3` est ordo postfixus aequalis while praefixo et totam assignationem repetit; `until` similiter. Destructio stricta corpus esse potest, non autem subexpressio ordinaria.
-
-`loop body` est forma infinita `while true`; `break` eam finit, atque limite fuel manet.
-
-`when condition` inter iterabile `for` et `then` positum iterationem filtrat: `for n in [1..5] when n > 2 then print(n)` corpus pro valoribus reiectis non currit.
-
-Hospes errorem structum accipit: `error.kind()` dat `ErrorKind::Parse`, `Verify`, aut `Runtime`; `error.message()` detail sine analysi textus ostensi dat, et `error.position()` lineam fontis a uno numeratam interdum dat.
-
-Ad iterandum programmatum compilatum, `Engine::compile_program` semel compilat et verificat, `Program` commune dat, et `Context::run_program` illud currit; clavis eius sine copia bytecodicis vel repetita verificatione clonatur.
-
-`Context::last_execution()` reddit `ExecutionStats` publicas de ultimo cursu prospero vel errore temporis, cum `instructions` et `fuel_remaining`; errores compilationis vel verificationis memoriam priorem servant.
-
-In seriebus et mapis per plures lineas, commata omitti possunt; argumenta functionum et parenteses ordinariae separationem apertam servant.
-
-Post assignationem solam (`record =`) mapa per indentationem scribi potest; claves interiores sine prototypo fiunt, nec continuatio ordinaria confunditur.
-
-In una linea logica, functio sine parenthesibus vocari potest: `implicit_answer = implicit_add 20, 22`; apud comparationes vel limites ordinis parenthesibus uti licet.
-
-`qtest --json` unam rem stabilem pro quoque testium documento scribit, `qtest --tap` TAP 13 reddit; `qcoffee --fingerprint FILE` clavem bytecodicis canonice verificati sine exsecutione ostendit; `qbench --json` tempora compilationis, verificationis et cursus cum custodia semantica metitur; `qdocco --markdown` documentum literarium ad recensionem scribit. Hospes `Context::set_fuel` inter cursus mutare potest, `Value::kind()` et `Value::is_nil()` ad genus probandum adhibet, et `cargo run --example embed` exemplum integrum currere.
+qtest --fuel N cuique documento exsecutabili budget instructionum proprium dat.
+qtest --stats numeros instructionum et alimenti reliqui cuiusque documenti ad errorem ordinarium scribit, sine mutatione exitus ok.
+qtest --json unam lineam JSON pro unoquoque documento scribit ad usum CI; --stats in stderr manet.
+qtest --tap versiones TAP 13 et numeros certos scribit; --json et --tap simul prohibentur.
+qtest --filter TEXT itinera congruentia eligit; qtest --list tantum documenta electa enumerat sine exsecutione.
+qcoffee --json unam lineam JSON valoris vel erroris structi reddit, aptam CI hospitibusque.
+Errores hospitis ErrorKind::Parse, Verify, Runtime habent atque detail sine textu ostenso praebent; error.position() lineam fontis a uno numeratam interdum dat.
+Engine::compile_program semel verificat; Context::run_program bytecode immutabile verificatum ad iteratum cursum reutitur.
+Program::fingerprint clavem u64 determinatam praebet ad memoriam hospitis sine mutatione exsecutionis.
+qcoffee --fingerprint FILE eandem clavem hexadecimali parvis litteris sedecim signorum ostendit, sine documento exsecuto.
+qbench --json unam mensurae lineam pro unoquoque onere custodito emittit; --iterations numerum exemplorum regit.
+Claves codicem bytecode explicite et canonice signant, non formam Rust debug; ideo mutatio instrumenti claves non mutat.
+qdocco --markdown notas, codicem QuickCoffee clausum, et valorem ultimum in documento Markdown scribit.
+Hospes inter cursus `Context::set_fuel` vocare potest; `Context::fuel` budgetum ostendit sine globalibus deletis; `with_global` et `with_native` configurationem concatenatam praebent.
+`cargo run --example embed` hospitem Rust minimum compilat: globale ponit, callback nativum addit, et QuickCoffee currit.
+Hospes `Value::kind()` ad genus discernendum et `Value::is_nil()` ad nil probandum utitur, sine interioribus vasorum.
+Notitiae Cargoe hospites ad repositorium, API docs.rs, README et licentiam ducunt.
+Context::last_execution() numeros instructionum et alimenti reliqui ostendit, sine tabulis VM.
+Argumenta post -- ut series chordarum ordinaria argv praebentur.
+JavaScript non est: catena prototyporum, this, eval, atque JavaScript inclusum desunt.
+# commentarium lineae est; ### … ### commentarium non-nidificatum ante layout et analysin removetur.
+Nomina Unicode XID sequuntur; signa combinantia ea continuant, sine normalizatione.
+yes/on sunt true, no/off false; is/isnt aequalitatem strictam servant.
+! est negatio Bool stricta sicut not; != inaequalitas stricta manet.
+Comparationes conectae medium semel servant atque priore falso breviant.
+Bibliotheca communis functiones ordinarias habet: print, len, type, range, str, abs, sum, min, max, keys, values, join, split, assert; aggregationes numericae solum series numerorum finitorum accipiunt.
+Functiones ambitum lexicalem capiunt; y = 2 omissus vel nil intra functionem adhibetur; rest ultimus scribitur tail....
+Nomina nuda parentheses omittere possunt: sinister, dexter -> sinister + dexter; default, rest, pattern eas servant.
+return expressio functionem praesentem finit; nudum return nil dat, iterata purgat, et finally circumstantia peragit.
+Parametri patterna stricta seriei/map habere possunt; default et rest nomina manent.
+Spatium integrorum `[1..3]` finem includit; `[1...3]` finem excludit.
+Spatia descendere quoque possunt: `[3..1]` `[3, 2, 1]` reddit, `[3...1]` `[3, 2]` reddit.
+Sectio seriei a[start..end] finem includit, a[start...end] excludit; termini integri finiti intra limites sunt, negativi ab extremo numerantur, et sectio nil-tuta terminos nil recipiente omittit.
+Recessus nil-specialis left ? right scribitur; false et zero servantur.
+Suffixum value? non-nil tantum probat: nil? false est, false? et 0? true sunt, nomen non ligatum errorem manet.
+name ?= value tantum nomen non ligatum aut nil scribit; non-nil dextram omittit, membrum, index, destructio excluduntur.
+Nomina etiam incrementum/decrementum strictum praefixum et postfixum habent: next = ++counter novum, previous = counter-- vetus valorem reddit.
+Arithmetica etiam divisionem inferiorem // et modulum dependentem %% habet: -7 // 5 est -2, -7 %% 5 est 3.
+value in array membrum seriei probat; key of map clavem propriam map probat.
+value not in array et key not of map easdem probationes strictas negant, sine prototypis.
+In littera map, {name} pro {name: name} breviter scribitur.
+Patterna assignmentis series atque maps includere possunt; VM totum ante ligamina mutanda verificat.
+In seriebus et vocationibus, items... seriem expandit sine JavaScript apply.
+Suffixa nil-tuta a?.name, a?[i], f?(args) tantum recipiens nil breviant.
+until condition then body repetit donec conditio Boolean vera sit.
+In loco sententiae, postfix while/until totam assignationem aut destructionem strictam repetit, non subexpressionem.
+loop body est while true infinitum; break exit, fuel autem limitem manet.
+Expressio for valores corporis colligit; when et continue omittunt, break praefixum collectum servat.
+Ligamen for pattern strictum esse potest: for [left, right] in pairs singula par atomice ligat.
+Ordo seriei by step uti potest; gradus integer finitus positivus semel aestimatur, maps eum excludunt.
+Iteratio seriei etiam indicem a zero numeratum ligare potest: for value, index in items then value + index.
+Comprehensio postfix eandem collectionem strictam servat: value * 2 for value in items, vel [value * 2 for value in items].
+
+## Code
+
+````quickcoffee
+
+numerus = 7
+quadratum = (x) -> x * x
+shorthand = 'yes'
+[first, {point: [x, y]}] = [0, {point: [20, 22]}]
+scale = ([left, right], {factor}) -> (left + right) * factor
+quadratum(numerus) == 49 and "numerus=#{quadratum(numerus)}" == 'numerus=49' and yes is on and no is off and 1 < 2 < 3 and x + y == 42 and scale([20, 1], {factor: 2}) == 42 and ((caput, y = 2) -> caput + y)(40) == 42 and ((caput, cauda...) -> caput + len(cauda))(40, 1, 2) == 42 and ((items) -> for n in items then if n == 42 then return n)([1, 42]) == 42 and ((-> try return 1 catch error then 2 finally 0)()) == 1 and len([1..3]) == 3 and len([1...3]) == 2 and (nil ? 42) == 42 and (false ? 42) == false and nil?.missing == nil and 2 in [1, 2] and 'name' of {name: 1} and {shorthand}.shorthand == 'yes' and len([1, [2, 3]..., 4]) == 4
+summa_gradus = 0
+for n in [1..9] by 3 then summa_gradus = summa_gradus + n
+summa_gradus == 12
+len(for [left, right] in [[20, 22], [1, 2]] then left + right) == 2
+postfixum_duplum = value * 2 for value in [1..3]
+postfixum_duplum == [2, 4, 6]
+numerus_mut = 2
+praefixum_mut = ++numerus_mut
+postfixum_mut = numerus_mut--
+[praefixum_mut, postfixum_mut, numerus_mut] == [3, 3, 3]
+[-7 // 5, -7 %% 5] == [-2, 3]
+[5 & 3, 5 | 2, 5 ^ 1, ~1, 1 << 3, -8 >> 2, -1 >>> 1] == [1, 7, 4, -2, 8, -2, 2147483647]
+continued = 1 +
+  2 * 3
+continued == 7
+message = "hello
+  world"
+message == 'hello world'
+escaped = "A\\x42\\u{43}"
+escaped == 'ABC'
+folded = (1 + 2 * 3) == 7
+folded
+values = [
+  1
+  2
+]
+values == [1, 2]
+record = {
+  first: 20
+  second: 22
+}
+record.first + record.second == 42
+indented_record =
+  first: 20
+  nested:
+    second: 22
+indented_record.nested.second == 22
+implicit_add = (left, right) -> left + right
+implicit_answer = implicit_add 20, 22
+implicit_answer == 42
+3 not in [1, 2] and 'absens' not of {praesens: 1}
+numerus_circuli = 0
+loop
+  numerus_circuli = numerus_circuli + 1
+  break if numerus_circuli == 3
+numerus_circuli == 3
+additio_nuda = sinister, dexter -> sinister + dexter
+additio_nuda(20, 22) == 42
+numerus_postfixus = 0
+numerus_postfixus = numerus_postfixus + 1 while numerus_postfixus < 3
+numerus_postfixus == 3
+sectio = [0..4][1..3]
+len(sectio) == 3 and sectio[0] == 1 and [0..4][-3...-1][0] == 2
+nil? == false and false? == true and 0? == true
+valor_defectus ?= 42
+valor_defectus == 42
+### fons invalidus ` hic ignoratur
+###
+42 == 42
+````
+
+## Final value
+
+`true`
diff --git a/docs/manual.zh-CN.html b/docs/manual.zh-CN.html
index 3ad467a..d28d66a 100644
--- a/docs/manual.zh-CN.html
+++ b/docs/manual.zh-CN.html
@@ -14,6 +14,8 @@
 

qtest --stats 将每份文档的指令数与剩余燃料写入标准错误,不改变 ok 输出。

qtest --json 为每份文档输出一行稳定 JSON,便于 CI;--stats 仍写标准错误。

qtest --tap 输出 TAP 13 及确定编号的记录;--json 与 --tap 互斥。

+

qtest --filter TEXT 按路径筛选;qtest --list 只列出筛选后的文件而不执行。

+

qcoffee --json 单次执行输出一行 JSON 值或结构化错误,便于 CI 与宿主消费。

Rust 嵌入错误有 ErrorKind::Parse、Verify、Runtime 与不依赖展示文本的详情;宿主回调可返回 Error::runtime("message"),error.position() 可给出从 1 开始的源码行。

Engine::compile_program 创建时验证一次;Context::run_program 重复执行时复用不可变的已验证字节码。

Program::fingerprint 提供确定性的 u64 字节码缓存键,不改变执行语义。

@@ -21,7 +23,7 @@

qbench --json 为每个带语义护栏的负载输出一条计时记录;--iterations 设置样本次数。

指纹使用显式规范化字节码编码,不依赖 Rust 调试格式,故工具链显示变化不会改缓存键。

qdocco --markdown 将说明、围栏 QuickCoffee 代码与最终值写成可审阅的 Markdown 产物。

-

嵌入方可在运行之间调用 Context::set_fuel;Context::fuel 返回当前每轮预算,且不清除全局值。

+

嵌入方可在运行之间调用 Context::set_fuel;Context::fuel 返回当前每轮预算,且不清除全局值;with_global 与 with_native 可链式配置宿主。

cargo run --example embed 可编译最小 Rust 宿主:设置全局、注册原生回调并执行 QuickCoffee。

宿主可用 Value::kind() 分流类型,用 Value::is_nil() 判断 nil,无须检查内部容器。

Cargo 包元数据指向仓库、docs.rs API、README 与许可证,便于嵌入方发现项目。

@@ -66,7 +68,8 @@

数组循环可写 by step;步长只求一次,须为非零有限整数,负步从末项起,映射循环不用 by。

数组循环亦可绑定从零开始的下标:for value, index in items then value + index。

后置推导沿用严格收集:value * 2 for value in items,亦可写作 [value * 2 for value in items]。

-

Code

base = 21
+

Code


+base = 21
 double = (x) -> x * 2
 shorthand = 'yes'
 [first, {point: [x, y]}] = [0, {point: [20, 22]}]
diff --git a/docs/manual.zh-CN.md b/docs/manual.zh-CN.md
index 9eacbfa..ce9a80d 100644
--- a/docs/manual.zh-CN.md
+++ b/docs/manual.zh-CN.md
@@ -1,140 +1,150 @@
-# QuickCoffee 用户手册(简体中文)
-
-QuickCoffee 是一个 Rust 字节码引擎,不是 JavaScript 运行时。源码被解析、编译、验证后才执行;不存在原型链、`this`、`eval` 或内嵌 JavaScript。
-
-`#` 开始行注释。非嵌套块注释用 `### … ###` 包围,内容在布局与解析前被忽略;未闭合会报告词法错误。
-
-标识符使用 Unicode XID 规则:首字符为 XID start 或 `_`,后续可为 XID continue 或 `_`,因此组合附标可出现在名称中;引擎不做 Unicode 规范化。
-
-普通字符串支持常用控制字符转义以及 `\\xNN`、`\\uNNNN`、`\\u{...}` Unicode 转义;非法转义和非 Unicode 标量值会报告解析错误。
-
-可使用 CoffeeScript 风格别名而不改变运行时类型:`yes`/`on` 等同 `true`,`no`/`off` 等同 `false`,`is`/`isnt` 等同严格的 `==`/`!=`。
-
-相邻的严格或数值比较可写成链:`1 < middle() < 3` 只会计算一次 `middle()`;较早比较为假时,不会计算后续操作数。
-
-## 起步
-
-```sh
-qcoffee -e "print(range(1, 4))"
-qcoffee --fuel 10000 program.qc
-qcoffee --check program.qc
-qcoffee --dump-bytecode program.qc
-```
-
-`qcoffee -` 从标准输入读取源码,便于接入管道;`qcoffee --check FILE`(FILE 可为 `-`)只解析、编译和验证而不执行;`qcoffee --dump-bytecode -` 则反汇编标准输入而不执行。
-`qcoffee --stats` 将指令数与剩余 fuel 写入标准错误,同时保持程序标准输出不变;qcoffee 每次只接受一个源码输入,冲突执行模式会报用法错误。
-
-`qcoffee --interactive`(或 `-i`)在输入行之间保持同一 Context;`:help` 列出命令,`:quit`/`:exit` 退出会话。管道输入不输出提示符。
-交互模式加 `--stats` 时,仅实际执行或运行时失败的非空输入行把指令数与剩余 fuel 写入标准错误;解析、验证错误不生成新记录。
-`'a☕中'[1]` 为 `'☕'`,`'a☕中'[1..2]` 为 `'☕中'`;字符串索引按 Unicode 标量。
-`for character, index in 'a☕中' then index` 得到 `[0, 1, 2]`;字符串按 Unicode 标量遍历,不接受 `by`。
-`[head, tail...] = [1, 2, 3]` 将 tail 绑定为 `[2, 3]`;数组模式 rest 必须居末。
-
-`--` 之后的参数以普通字符串数组 `argv` 提供:`qcoffee program.qc -- first second` 中 `len(argv)` 为 `2`。引擎不会暴露宿主进程或环境对象。
-
-`--fuel` 是每次执行的指令上限,耗尽会安全失败。标准库包括 `print`、`len`、`type`、`range`、`str`、`abs`、`sum`、`min`、`max`、`keys`、`values`、`join`、`split` 与 `assert`;数值聚合只接受严格有限数数组,`range(a, b)` 生成 `[a, b)`。
-
-## 语法示例
-
-```coffee
-factor = 6
+# QuickCoffee document
+
+## Notes
+
+QuickCoffee 用户手册
+QuickCoffee 先将源码解析并编译为经验证的字节码,随后由带 fuel 限制的 VM 执行。
+qcoffee - 可从标准输入读取 QuickCoffee 程序。
+qcoffee --stats 将指令数与剩余燃料写入标准错误,同时保持程序标准输出不变;qcoffee 每次只接受一个源码输入,冲突执行模式会报用法错误。
+qcoffee --check FILE 只解析、编译并验证 FILE,不执行它。
+qcoffee --interactive(或 -i)逐行复用同一 Context;:help 显示命令,:quit 退出。
+qcoffee --interactive --stats 仅为实际执行或运行时失败的非空输入行输出指令/燃料统计;解析、验证错误不输出新记录。
+'a☕中'[1] 为 '☕','a☕中'[1..2] 为 '☕中';字符串索引按 Unicode 标量。
+for character, index in 'a☕中' then index 得 [0, 1, 2];字符串按 Unicode 标量遍历,by 可用非零有符号整数,负步从末项起。
+do (name, other) -> ... 即刻调用,并按名转发外层值;do -> ... 仍为零参。
+[head, tail...] = [1, 2, 3] 将 tail 绑定为 [2, 3];数组模式 rest 必须居末。
+qtest --fuel N 为每份可执行文档设置独立指令预算。
+qtest --stats 将每份文档的指令数与剩余燃料写入标准错误,不改变 ok 输出。
+qtest --json 为每份文档输出一行稳定 JSON,便于 CI;--stats 仍写标准错误。
+qtest --tap 输出 TAP 13 及确定编号的记录;--json 与 --tap 互斥。
+qtest --filter TEXT 按路径筛选;qtest --list 只列出筛选后的文件而不执行。
+qcoffee --json 单次执行输出一行 JSON 值或结构化错误,便于 CI 与宿主消费。
+Rust 嵌入错误有 ErrorKind::Parse、Verify、Runtime 与不依赖展示文本的详情;宿主回调可返回 Error::runtime("message"),error.position() 可给出从 1 开始的源码行。
+Engine::compile_program 创建时验证一次;Context::run_program 重复执行时复用不可变的已验证字节码。
+Program::fingerprint 提供确定性的 u64 字节码缓存键,不改变执行语义。
+qcoffee --fingerprint FILE 以 16 位小写十六进制输出同一已验证字节码键,且不执行文件。
+qbench --json 为每个带语义护栏的负载输出一条计时记录;--iterations 设置样本次数。
+指纹使用显式规范化字节码编码,不依赖 Rust 调试格式,故工具链显示变化不会改缓存键。
+qdocco --markdown 将说明、围栏 QuickCoffee 代码与最终值写成可审阅的 Markdown 产物。
+嵌入方可在运行之间调用 Context::set_fuel;Context::fuel 返回当前每轮预算,且不清除全局值;with_global 与 with_native 可链式配置宿主。
+cargo run --example embed 可编译最小 Rust 宿主:设置全局、注册原生回调并执行 QuickCoffee。
+宿主可用 Value::kind() 分流类型,用 Value::is_nil() 判断 nil,无须检查内部容器。
+Cargo 包元数据指向仓库、docs.rs API、README 与许可证,便于嵌入方发现项目。
+Context::last_execution() 提供指令数与剩余燃料统计,不暴露 VM 帧。
+-- 后的参数以普通字符串数组 argv 暴露给程序。
+它不是 JavaScript:没有原型链、this、eval 或内嵌 JavaScript。
+# 是行注释;### … ### 是不嵌套块注释,内容在布局和解析前忽略。
+标识符遵循 Unicode XID:组合附标可作续字符,且引擎不做 Unicode 规范化。
+yes/on 与 no/off 是布尔别名;is/isnt 保持严格相等。
+! 是严格 Bool 的 not 别名;!= 仍为严格不等。
+严格或数值比较可成链,保留中间值且前段失败会短路。
+标准库皆为普通函数:print、len、type、range、str、abs、sum、min、max、keys、values、join、split 与 assert;数值聚合只收严格有限数数组。
+映射字面量可从左至右展开:{...defaults, theme: 'dark'};后写键覆盖先写键。
+映射解构末尾可用 ...metadata 捕获未列键,所得映射不可变。
+数组与 Unicode 字符串支持负索引,-1 取末项。
+函数捕获词法环境;末尾参数可写作 y = 2,缺省或 nil 时在函数内取默认值;末位 rest 参数写作 tail...。
+普通名称参数可省略括号:left, right -> left + right;默认、rest 与解构参数仍须括号。
+return expression 只在函数内结束当前调用;裸 return 得 nil,并清理循环且执行沿途 finally。
+形参可使用严格嵌套数组/映射模式,默认值和 rest 仍只用于名称。
+整数区间 `[1..3]` 包含上界,`[1...3]` 不包含上界。
+区间也可降序:`[3..1]` 得到 `[3, 2, 1]`,`[3...1]` 得到 `[3, 2]`。
+三引号 heredoc 保留换行:"""...""" 插值,'''...''' 为字面量。
+数组切片 a[start..end] 包含末端,a[start...end] 不包含末端;端点须为界内有限整数,负数自末计,nil 安全切片在接收者 nil 时不求端点。
+空值回退写作 left ? right,仅 nil 触发,false 与 0 保留。
+后缀 value? 仅检验非 nil;nil? 为 false,false? 与 0? 为 true,且不隐藏未绑定名称错误。
+name ?= value 只在名称未绑定或为 nil 时求值并写入;已有非 nil 值时右侧短路,成员、索引与解构不适用。
+value in array 检查数组成员;key of map 只检查映射自身的字符串键。
+value not in array 与 key not of map 分别是否定的严格数组成员和映射自有键检查。
+映射字面量中的 {name} 是 {name: name} 的简写。
+赋值模式可嵌套数组和映射,VM 先完整验证,失败不留下部分绑定。
+名称可作严格算术复合赋值:total += amount、power **= 2;成员与索引不可作之。
+名称亦支持严格前后置更新:next = ++counter 得新值,previous = counter-- 先得旧值。
+算术亦有整除 // 与向下取模 %%:-7 // 5 为 -2,-7 %% 5 为 3。
+数组和调用中的 items... 会展开数组,不使用 JavaScript apply。
+nil 安全后缀 a?.name、a?[i]、f?(args) 只在接收者为 nil 时短路。
+until condition then body 重复执行,直至布尔条件为真。
+语句位置可后置 while/until:重复整个赋值或严格解构,不能嵌入普通子表达式。
+loop body 是 while true 的无限形式,以 break 退出,仍受 fuel 限制。
+for 是收集表达式:每轮体值成新数组,when 与 continue 不收集,break 保留既得前缀。
+for 绑定可用严格模式:for [left, right] in pairs 会原子地绑定每个 pair。
+数组循环可写 by step;步长只求一次,须为非零有限整数,负步从末项起,映射循环不用 by。
+数组循环亦可绑定从零开始的下标:for value, index in items then value + index。
+后置推导沿用严格收集:value * 2 for value in items,亦可写作 [value * 2 for value in items]。
+
+## Code
+
+````quickcoffee
+
+base = 21
 double = (x) -> x * 2
-if double(factor) == 12 then print('ok') else print('bad')
-```
-
-函数捕获创建处的词法环境。普通名称形参可省略括号,如 `left, right -> left + right`;默认值、rest 和解构形参仍必须使用括号。尾部形参可设默认值,如 `(head, separator = '-') -> expression`;缺省或显式传入 `nil` 时,默认表达式在被调函数内计算,因此可引用较早形参与闭包变量。必选形参必须在默认形参之前。末位可变参数写作 `(head, tail...) -> expression`,其余实参会以数组绑定给 `tail`。映射键使用字符串索引:`{name: 'coffee'}['name']`。
-
-`return expression` 只可用于函数体,立即结束当前函数;裸 `return` 返回 `nil`。它不会跨越嵌套函数。位于循环时会清理循环状态;穿过 `try` 或 `catch` 时会由内向外执行 `finally`。`finally` 中的 return 会覆盖先前的返回值。条件返回请写作 `if condition then return value`。
-
-形参也可使用严格递归模式:`([left, right], {factor}) -> (left + right) * factor`。函数开始前,每个实参必须匹配对应模式;默认值仍只允许命名形参,rest 仍只能是末位名称。
-
-映射字面量中的 `{name}` 是 `{name: name}` 的简写;字符串键仍必须显式给出值,如 `{'name': value}`。
-
-赋值模式可嵌套数组与映射:`[first, {point: [x, y]}] = [1, {point: [20, 22]}]`。数组每层都要求长度精确相同,映射要求列出的标识符键存在。VM 会先验证整个模式再写任何绑定,故深层不匹配同样是原子的。
-
-数组项或调用实参末尾的 `...` 会展开数组:`[1, values..., 4]` 拼入其元素,`fn(values...)` 将它们逐个传参。展开目标必须是数组,不会调用 JavaScript 风格的 `apply` 方法。
-
-nil 安全后缀采用 CoffeeScript 风格写法:`record?.name`、`values?[index]` 与 `fn?(args)`。接收者为 `nil` 时结果为 `nil`,索引或实参也不会求值;接收者非 `nil` 时沿用普通访问的严格规则,因此映射缺键仍会报错。
-
-数组循环写作 `for item in range(1, 4) then print(item)`;可在数组之后写 `by step`,如 `for item in [1..9] by 3 then print(item)` 或用负步长从末项反向遍历。第二个绑定可取得从零开始的实际下标,如 `for item, index in items then item + index`,步进时仍取数组位置。绑定位置可用严格递归模式,例如 `for [left, right] in pairs then left + right` 或 `for {point: {x, y}} in values then x + y`;每个项的全部绑定只会在模式完整匹配后写入。`for` 是收集表达式:每次循环体值组成新数组,`when` 拒绝的项不收集,`break` 返回已收集前缀。步长只求值一次,且必须是非零的有限整数。`break` 和 `continue` 控制最内层循环;`while`/`until`/`loop` 的结果仍为 `nil`;映射循环不支持 `by`。
-
-同一收集器也支持 CoffeeScript 风格后置推导:`value * 2 for value in items`,或写作 `[value * 2 for value in items]`。方括号只是推导界标,不产生额外嵌套数组;`by`、`when`、映射、模式、`break`、`continue` 仍遵循前置形式。
-
-整数区间字面量由 VM 的专用字节码直接构造:`[1..3]` 包含上界,结果为 `[1, 2, 3]`;`[1...3]` 不包含上界,结果为 `[1, 2]`;降序同样支持,`[3..1]` 为 `[3, 2, 1]`。边界必须是有限整数。
-
-数组切片写作 `items[start..end]`(含末端)或 `items[start...end]`(不含末端),例如 `[0..4][1..3]` 为 `[1, 2, 3]`。端点从左到右各求值一次,必须是界内有限整数;负数从末尾计,`-1` 是最后一项。切片只用于数组且不隐式截断。nil 安全形式 `items?[start..end]` 在接收者为 `nil` 时不求端点而产生 `nil`。
-
-`left ? right` 是仅针对 `nil` 的回退:只有左值为 `nil` 才会求值右侧。它不会把 `false`、`0`、空字符串或空容器视为空值。
-
-后缀 `value?` 只检测值是否不是 `nil`:`nil?` 为 `false`,而 `false?`、`0?` 都为 `true`。它不隐藏未绑定名称错误,也不同于 `left ? right` 的回退。
-
-`name ?= value` 仅在名称还未绑定或当前为 `nil` 时求值并写入 `value`;已有非 nil 值时右侧不会执行。名称还支持严格算术复合赋值,如 `total += amount`、`power **= 2`。这些形式只适用于名称,不能用于成员、索引或解构;普通未绑定名称读取仍是错误。
-
-名称还支持严格数值前后置更新:`next = ++counter` 产生新值,`previous = counter--` 先产生旧值再减一。更新只接受名称,成员、索引和解构形式均拒绝。
-
-CoffeeScript 算术还提供整除 `a // b` 与向下取模 `a %% b`;例如 `-7 // 5` 为 `-2`,`-7 %% 5` 为 `3`。普通 `%` 仍是随被除数取符号的余数。
-
-位运算采用严格有符号 32 位数:`&`、`|`、`^`、`~`、`<<`、`>>`、`>>>`;移位计数限于 0 至 31,复合形式只接受名称。
-
-物理行末的显式运算符可使表达式续至下一行;续行期间的缩进只作排版,不改变布局块。
-
-普通引号字符串可以跨行;换行合为一个空格,行末反斜杠则去除换行。
-
-诸如 `(1 + 2 * 3) == 7` 的纯字面量算术会在编译时折叠为经验证的常量。
-
-`value in array` 按 QuickCoffee 相等性检查数组成员,`value not in array` 取其相反值。`key of map` 只检查映射自身的字符串键,`key not of map` 取其相反值;映射没有可查询的原型键。
-
-`until condition then body` 是反向循环形式:它重复执行直到布尔条件为真,`break`、`continue`、缩进和 fuel 规则均与 `while` 相同。
-
-语句位置还可写后置循环:`n = n + 1 while n < 3` 与前置 while 等价,重复整个赋值;`until` 同理。严格解构也可作为体。后置循环不能嵌入普通子表达式。
-
-`loop body` 是无限的 `while true` 形式;使用 `break` 退出,仍受 fuel 限制。例如 `n = 0; loop then if n == 3 then break else n = n + 1`。
-
-在 `for` 的可迭代对象与 `then` 之间放置 `when condition` 可过滤循环,不为被拒绝的绑定执行循环体:`for n in [1..5] when n > 2 then print(n)`。
-
-无原型的数据工厂写作 `class Point(x, y = 0) -> {x: x, y: y}`,其默认参数规则与函数相同;调用后得到普通映射,可用 `Point(3).x` 读取成员。没有 `this`、`new` 或继承。
-
-双引号可插入 QuickCoffee 表达式:`"答案 #{double(21)}"`。单引号没有插值;其中不会运行任何 JavaScript。
-
-多分支表达式使用 `switch value` 与缩进的 `when pattern`;只会选择一个严格相等分支,且没有贯穿。
-
-异常使用 `try`、`catch error`、可选 `finally` 与 `throw value`。catch 得到稳定的错误字符串,而非 JavaScript Error 对象;函数 return 也会经过适用的 finally。
-
-## 嵌入 Rust
-
-```rust
-let mut cx = quickcoffee::Context::new().with_fuel(100_000);
-cx.set_global(
-    "host_values",
-    quickcoffee::Value::array(vec![
-        quickcoffee::Value::from(40_i64),
-        quickcoffee::Value::from(2_i64),
-    ]),
-);
-let value = cx.eval("host_values[0] + host_values[1]")?;
-```
-
-`Value::from`、`Value::string`、`Value::array` 与 `Value::map` 可以直接构造宿主值,无需接触 VM 的引用计数内部表示。宿主回调可返回 `Error::runtime("message")`,脚本可用 `catch` 捕获。
-
-若需重复执行,使用 `Engine::compile_program` 编译并验证一次,再将共享 `Program` 传给 `run_program`;克隆该句柄不会复制字节码或重复验证。
-
-`Context::last_execution()` 可读最近一次成功或运行时失败的 `ExecutionStats`,其中有执行指令数 `instructions` 与余下燃料 `fuel_remaining`;编译或验证错误不会改写上一条记录。
-
-`cx.get_global("host_values")` 可在不执行脚本的情况下读取脚本或宿主设置的全局值;未知名称返回 `None`。它只返回公开 `Value` 的副本,不泄漏环境或调用帧。
-
-嵌入错误具有结构:`error.kind()` 返回 `ErrorKind::Parse`、`ErrorKind::Verify` 或 `ErrorKind::Runtime`,`error.message()` 返回详情,`error.position()` 可返回从 1 开始的源码行号。宿主无需解析展示文本;`Display` 输出仍适合 CLI 与 QuickCoffee 的 `catch` 错误字符串。
-
-## 文档与测试
-
-`qdocco demo.qc -o demo.html` 生成并校验可执行文档;用 `qdocco --check demo.qc` 只校验。`qtest cases` 会递归运行目录中的 `.qc` 文件,要求每个脚本的最后值严格为 `true`。
-
-`qtest --fuel N cases` 会为每个发现的测试文件分别设置指令预算,因此一个受限循环不会耗尽其他测试的预算。
-`qtest --stats` 还会把每个文件的指令数与剩余 fuel 写入标准错误,不改变 `ok` 输出。
-
-多行数组和映射可按行省略逗号;调用参数与普通括号内表达式仍须显式分隔。
-
-单独赋值行(`record =`)后可缩进书写映射;嵌套的 `key: value` 条目会成为无原型映射,普通赋值续行不受影响。
-
-同一逻辑行的调用可省略括号:`implicit_answer = implicit_add 20, 22`;比较或跨布局边界时仍可使用显式括号。
-
-`qtest --json` 为每个测试文件输出稳定 JSON,`qtest --tap` 输出 TAP 13;`qcoffee --fingerprint FILE` 在不执行文件时输出规范化字节码缓存键;`qbench --json` 输出带语义护栏的编译、验证、执行计时;`qdocco --markdown` 生成可审阅的文学编程 Markdown。嵌入方可用 `Context::set_fuel` 调整复用上下文,用 `Value::kind()` 与 `Value::is_nil()` 做稳定类型判断,并运行 `cargo run --example embed` 查看完整宿主示例。
+shorthand = 'yes'
+[first, {point: [x, y]}] = [0, {point: [20, 22]}]
+scale = ([left, right], {factor}) -> (left + right) * factor
+double(base) == 42 and "答案 #{double(base)}" == '答案 42' and yes is on and no is off and 1 < 2 < 3 and x + y == 42 and scale([20, 1], {factor: 2}) == 42 and ((head, y = 2) -> head + y)(40) == 42 and ((head, tail...) -> head + len(tail))(40, 1, 2) == 42 and ((items) -> for n in items then if n == 42 then return n)([1, 42]) == 42 and ((-> try return 1 catch error then 2 finally 0)()) == 1 and len([1..3]) == 3 and len([1...3]) == 2 and (nil ? 42) == 42 and (false ? 42) == false and nil?.missing == nil and 2 in [1, 2] and 'name' of {name: 1} and {shorthand}.shorthand == 'yes' and len([1, [2, 3]..., 4]) == 4
+步和 = 0
+for n in [1..9] by 3 then 步和 = 步和 + n
+步和 == 12
+len(for [left, right] in [[20, 22], [1, 2]] then left + right) == 2
+后置倍数 = value * 2 for value in [1..3]
+后置倍数 == [2, 4, 6]
+计数器 = 2
+前置更新 = ++计数器
+后置更新 = 计数器--
+[前置更新, 后置更新, 计数器] == [3, 3, 3]
+[-7 // 5, -7 %% 5] == [-2, 3]
+[5 & 3, 5 | 2, 5 ^ 1, ~1, 1 << 3, -8 >> 2, -1 >>> 1] == [1, 7, 4, -2, 8, -2, 2147483647]
+continued = 1 +
+  2 * 3
+continued == 7
+message = "hello
+  world"
+message == 'hello world'
+escaped = "A\\x42\\u{43}"
+escaped == 'ABC'
+folded = (1 + 2 * 3) == 7
+folded
+values = [
+  1
+  2
+]
+values == [1, 2]
+record = {
+  first: 20
+  second: 22
+}
+record.first + record.second == 42
+indented_record =
+  first: 20
+  nested:
+    second: 22
+indented_record.nested.second == 22
+implicit_add = (left, right) -> left + right
+implicit_answer = implicit_add 20, 22
+implicit_answer == 42
+3 not in [1, 2] and 'missing' not of {present: 1}
+循环数 = 0
+loop
+  循环数 = 循环数 + 1
+  break if 循环数 == 3
+循环数 == 3
+裸加 = left, right -> left + right
+裸加(20, 22) == 42
+后置数 = 0
+后置数 = 后置数 + 1 while 后置数 < 3
+后置数 == 3
+切片数 = [0..4][1..3]
+len(切片数) == 3 and 切片数[0] == 1 and [0..4][-3...-1][0] == 2
+nil? == false and false? == true and 0? == true
+默认数 ?= 42
+默认数 == 42
+多行 = """答案 #{double(base)}
+次行"""
+多行 == '答案 42\n次行'
+### 这段含无效 ` 源文,却不会参与执行
+###
+42 == 42
+````
+
+## Final value
+
+`true`
diff --git a/docs/syntax.en.md b/docs/syntax.en.md
index 175de78..8390ba4 100644
--- a/docs/syntax.en.md
+++ b/docs/syntax.en.md
@@ -4,7 +4,7 @@ Embedding callers may use `Program::fingerprint()` as a deterministic bytecode c
 
 Cargo package metadata links embedding users to the repository, README, license, and docs.rs API.
 
-The bundled `qtest --json` runner emits one stable JSON result per file for CI and host integration; `qtest --tap` emits deterministic TAP 13 records. `qcoffee --fingerprint FILE` prints a stable 16-digit hexadecimal key for verified bytecode without executing it; the key uses canonical encoding rather than Rust debug text. `qbench --json` emits guarded compile/verify/execute timing records; `qbench --list` enumerates workloads and `qbench --only NAME` runs one selected workload, while the default remains the complete suite. `qdocco --markdown` renders literate notes, fenced source, and final values for review. Embedders can adjust a reused context with `Context::set_fuel` and inspect `Context::fuel`; `cargo run --example embed` is a compiled host integration example. Execution statistics remain on stderr with `--stats`.
+The bundled `qtest --json` runner emits one stable JSON result per file for CI and host integration; `qtest --tap` emits deterministic TAP 13 records; `qtest --filter TEXT` selects matching paths and `qtest --list` enumerates the selected files without executing them. `qcoffee --json` emits one stable JSON value or structured error for a single execution, while `qcoffee --fingerprint FILE` prints a stable 16-digit hexadecimal key for verified bytecode without executing it; the key uses canonical encoding rather than Rust debug text. `qbench --json` emits guarded compile/verify/execute timing records; `qbench --list` enumerates workloads and `qbench --only NAME` runs one selected workload, while the default remains the complete suite. `qdocco --markdown` renders literate notes, fenced source, and final values for review. Embedders can adjust a reused context with `Context::set_fuel` and inspect `Context::fuel`, or chain `Context::with_global` and `Context::with_native`; `cargo run --example embed` is a compiled host integration example. Execution statistics remain on stderr with `--stats`.
 
 Integer ranges are ascending or descending: `[2..4]` is `[2, 3, 4]`, `[4..2]` is `[4, 3, 2]`, and exclusive forms omit the end (`[4...2]` is `[4, 3]`). Bounds must be finite integers and oversized ranges are rejected.
 
diff --git a/docs/syntax.zh-CN.md b/docs/syntax.zh-CN.md
index a8803e9..af2fdfb 100644
--- a/docs/syntax.zh-CN.md
+++ b/docs/syntax.zh-CN.md
@@ -2,7 +2,7 @@
 
 嵌入宿主可用 `Program::fingerprint()` 作为确定性字节码缓存键;该指纹不改变验证与执行语义。
 
-内建 `qtest --json` 每个文件输出一行稳定 JSON,供 CI 与宿主系统使用;`qtest --tap` 输出确定性的 TAP 13 记录;`qcoffee --fingerprint FILE` 在不执行脚本时输出已验证字节码的稳定 16 位十六进制键,指纹使用规范化编码而非 Rust 调试文本;`qbench --json` 输出带语义护栏的编译、验证、执行计时记录,`qbench --list` 枚举负载而 `qbench --only NAME` 可只运行一个负载;`qdocco --markdown` 生成说明、围栏源码和最终值供审阅;嵌入方可用 `Context::set_fuel` 调整复用上下文的预算并用 `Context::fuel` 读取,`cargo run --example embed` 提供可编译宿主示例;`--stats` 的执行统计仍写入标准错误。
+内建 `qtest --json` 每个文件输出一行稳定 JSON,供 CI 与宿主系统使用;`qtest --tap` 输出确定性的 TAP 13 记录;`qtest --filter TEXT` 按路径筛选,`qtest --list` 只枚举最终文件而不执行;`qcoffee --json` 单次执行输出一行稳定 JSON 值或结构化错误,`qcoffee --fingerprint FILE` 在不执行脚本时输出已验证字节码的稳定 16 位十六进制键,指纹使用规范化编码而非 Rust 调试文本;`qbench --json` 输出带语义护栏的编译、验证、执行计时记录,`qbench --list` 枚举负载而 `qbench --only NAME` 可只运行一个负载;`qdocco --markdown` 生成说明、围栏源码和最终值供审阅;嵌入方可用 `Context::set_fuel` 调整复用上下文的预算并用 `Context::fuel` 读取,也可链式调用 `Context::with_global` 与 `Context::with_native`,`cargo run --example embed` 提供可编译宿主示例;`--stats` 的执行统计仍写入标准错误。
 
 这是 RFC 0001 的中文索引;未列出的 CoffeeScript 2016 特性不是“隐式兼容”,而是明确不支持。Cargo 包元数据提供仓库、README、许可证和 docs.rs API 链接。
 
diff --git a/examples/embed.rs b/examples/embed.rs
index c5d65ba..75a7526 100644
--- a/examples/embed.rs
+++ b/examples/embed.rs
@@ -1,17 +1,18 @@
 use quickcoffee::{Context, Error, Value};
 
 fn main() -> Result<(), Error> {
-    let mut context = Context::new().with_fuel(100_000);
-    context.set_global("factor", Value::from(2_i64));
-    context.add_native("host_add", |args| {
-        if args.len() != 2 {
-            return Err(Error::runtime("host_add expects two numbers"));
-        }
-        let (Some(left), Some(right)) = (args[0].as_number(), args[1].as_number()) else {
-            return Err(Error::runtime("host_add expects two numbers"));
-        };
-        Ok(Value::from(left + right))
-    });
+    let mut context = Context::new()
+        .with_fuel(100_000)
+        .with_global("factor", Value::from(2_i64))
+        .with_native("host_add", |args| {
+            if args.len() != 2 {
+                return Err(Error::runtime("host_add expects two numbers"));
+            }
+            let (Some(left), Some(right)) = (args[0].as_number(), args[1].as_number()) else {
+                return Err(Error::runtime("host_add expects two numbers"));
+            };
+            Ok(Value::from(left + right))
+        });
     let value = context.eval("host_add(20, 22) * factor")?;
     println!("{value}");
     Ok(())
diff --git a/manuals/manual.classical-zh.qc b/manuals/manual.classical-zh.qc
index 81ca9f2..a20eb5b 100644
--- a/manuals/manual.classical-zh.qc
+++ b/manuals/manual.classical-zh.qc
@@ -22,6 +22,9 @@
 ## qtest --stats 更书各篇所试指令与余燃料于标准错误,而 ok 之出不改。
 ## qtest --json 每篇出一行 JSON,便于 CI 取用;--stats 仍书于标准错误。
 ## qtest --tap 出 TAP 13 及定次之记录;--json 与 --tap 不可并用。
+## qtest --filter TEXT 依路径择篇;qtest --list 但列所择之篇而不行其文。
+
+## qcoffee --json 一行以 JSON 载其值或错状,俾 CI 与宿主取用。
 ## 宿主之误,有 ErrorKind::Parse、Verify、Runtime 三类,且可别取其详;error.position() 或示从一始之源码行。
 ## Engine::compile_program 创时验之;Context::run_program 屡行则复用不可变已验字节码。
 ## Program::fingerprint 出确定 u64 码键,便宿主缓存,而不改执行。
@@ -29,7 +32,7 @@
 ## qbench --json 每负载出一计时录,皆有语义护栏;--iterations 定其试数。
 ## 指纹以定式编码字节码,不取 Rust 调试辞,故工具链改其辞而缓存键不改。
 ## qdocco --markdown 出说明、围栏 QuickCoffee 代码及终值为可阅 Markdown 文。
-## 嵌者可于两行之间呼 Context::set_fuel;Context::fuel 示每行之限,而全局不失。
+## 嵌者可于两行之间呼 Context::set_fuel;Context::fuel 示每行之限,而全局不失;with_global、with_native 可相次而呼以置宿主。
 ## cargo run --example embed 可验最小 Rust 宿主,设全局、立原生回调而行 QuickCoffee。
 ## 宿主可用 Value::kind() 别其类,Value::is_nil() 验 nil,不窥其内容器。
 ## Cargo 包志指仓、docs.rs API、README 与许可证,使嵌者易寻其用。
diff --git a/manuals/manual.devanagari-sa.qc b/manuals/manual.devanagari-sa.qc
index c8a1fdd..893c252 100644
--- a/manuals/manual.devanagari-sa.qc
+++ b/manuals/manual.devanagari-sa.qc
@@ -17,6 +17,9 @@
 ## qtest --stats प्रत्येकस्य documentस्य instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति, ok-निर्गमं न परिवर्तयति।
 ## qtest --json प्रत्येकस्य लेखस्य स्थिरं JSON फलम् एकस्मिन् पङ्क्तौ लिखति; --stats stderr मध्ये एव।
 ## qtest --tap TAP 13 तथा नियत-सङ्ख्याङ्कितानि फलानि लिखति; --json च --tap च परस्परं निषिद्धे।
+## qtest --filter TEXT मार्ग-साम्येन परीक्षां चिनोति; qtest --list चयनित-पत्राणि केवलं गणयति, न चालयति।
+
+## qcoffee --json एकस्मिन् प्रयोगे JSON-मूल्यं वा संरचितं दोषं एकया पङ्क्त्या ददाति, CI-होष्ट्रयोः उपयोगाय।
 ## host-error ErrorKind::Parse, Verify, Runtime तथा प्रदर्शनात् स्वतन्त्रं विवरणं ददाति; error.position() कदाचित् एकतः गणितां स्रोत-पङ्क्तिं ददाति।
 ## Engine::compile_program एकवारं verify करोति; Context::run_program पुनःचालने अपरिवर्तनीय-सत्यापित-bytecode पुनरुपयुङ्क्ते।
 ## Program::fingerprint होस्ट-सञ्चयाय नियतं u64 बीजं ददाति, निष्पादनं न परिवर्तयति।
@@ -24,7 +27,7 @@
 ## qbench --json प्रत्येक-सुरक्षित-भारस्य एकं काल-मापन-फलम् लिखति; --iterations नमूना-सङ्ख्यां नियच्छति।
 ## बीजाङ्काः Rust-debug-रूपं विना स्पष्ट-नियत-bytecode-संकेतेन निर्मीयन्ते, अतः साधन-रूपपरिवर्तनं सञ्चय-कुञ्जीं न परिवर्तयति।
 ## qdocco --markdown टिप्पणीन्, सीमितं QuickCoffee-कोडं, अन्तिम-मूल्यं च पठनीय Markdown-फलके लिखति।
-## अन्तःस्थापकः चालनयोर्मध्ये Context::set_fuel आह्वयितुं शक्नोति; Context::fuel वर्तमान-सीमां दर्शयति, वैश्विक-मूल्यानि न नाशयति।
+## अन्तःस्थापकः चालनयोर्मध्ये Context::set_fuel आह्वयितुं शक्नोति; Context::fuel वर्तमान-सीमां दर्शयति, वैश्विक-मूल्यानि न नाशयति; with_global तथा with_native क्रमिक-संयोजनाय स्तः।
 ## `cargo run --example embed` लघुं Rust-आश्रयं संयोजयति, वैश्विकं स्थापयति, native-callback योजयति, QuickCoffee च चालयति।
 ## Host `Value::kind()` द्वारा प्रकारं विभजति, `Value::is_nil()` द्वारा nil परीक्षते, आन्तरिक-container न पश्यति।
 ## Cargo-वस्तु-विवरणानि अन्तःस्थापकान् repository, docs.rs-API, README, licence च प्रति नयन्ति।
diff --git a/manuals/manual.en.qc b/manuals/manual.en.qc
index 2168acd..c5d0b4a 100644
--- a/manuals/manual.en.qc
+++ b/manuals/manual.en.qc
@@ -13,6 +13,9 @@
 ## qtest --stats writes each file's instruction count and remaining fuel to stderr without changing its ok output.
 ## qtest --json writes one stable JSON result per file for CI consumers; --stats remains on stderr.
 ## qtest --tap writes TAP version 13 records with deterministic numbering; --json and --tap are mutually exclusive.
+## qtest --filter TEXT selects matching paths, while qtest --list enumerates selected files without executing them.
+
+## qcoffee --json emits one JSON value or structured error for a single execution, suitable for CI and hosts.
 ## Rust embedding errors expose ErrorKind::Parse, Verify, or Runtime plus a display-independent message; host callbacks may return Error::runtime("message"), and error.position() may give a one-based source line.
 ## Engine::compile_program verifies once; Context::run_program reuses the immutable verified bytecode for repeated embedding calls.
 ## Program::fingerprint provides a deterministic u64 bytecode cache key without changing execution.
@@ -20,7 +23,7 @@
 ## qbench --json emits one timing record per guarded workload; --iterations controls sample count.
 ## Fingerprints use explicit canonical bytecode encoding, not Rust debug formatting, so cache keys survive toolchain display changes.
 ## qdocco --markdown writes Notes, fenced QuickCoffee code, and the final value as a reviewable Markdown artifact.
-## Embedders may call Context::set_fuel between runs; Context::fuel reports the current per-run budget without clearing globals.
+## Embedders may call Context::set_fuel between runs; Context::fuel reports the current per-run budget without clearing globals, while with_global and with_native provide chainable setup.
 ## cargo run --example embed compiles a minimal Rust host that sets a global, registers a native callback, and evaluates QuickCoffee.
 ## A host can branch on Value::kind() and use Value::is_nil() without inspecting internal containers.
 ## Cargo package metadata points embedding users to the repository, docs.rs API, README, and license.
diff --git a/manuals/manual.latin.qc b/manuals/manual.latin.qc
index 2db586c..f57177d 100644
--- a/manuals/manual.latin.qc
+++ b/manuals/manual.latin.qc
@@ -17,6 +17,9 @@
 ## qtest --stats numeros instructionum et alimenti reliqui cuiusque documenti ad errorem ordinarium scribit, sine mutatione exitus ok.
 ## qtest --json unam lineam JSON pro unoquoque documento scribit ad usum CI; --stats in stderr manet.
 ## qtest --tap versiones TAP 13 et numeros certos scribit; --json et --tap simul prohibentur.
+## qtest --filter TEXT itinera congruentia eligit; qtest --list tantum documenta electa enumerat sine exsecutione.
+
+## qcoffee --json unam lineam JSON valoris vel erroris structi reddit, aptam CI hospitibusque.
 ## Errores hospitis ErrorKind::Parse, Verify, Runtime habent atque detail sine textu ostenso praebent; error.position() lineam fontis a uno numeratam interdum dat.
 ## Engine::compile_program semel verificat; Context::run_program bytecode immutabile verificatum ad iteratum cursum reutitur.
 ## Program::fingerprint clavem u64 determinatam praebet ad memoriam hospitis sine mutatione exsecutionis.
@@ -24,7 +27,7 @@
 ## qbench --json unam mensurae lineam pro unoquoque onere custodito emittit; --iterations numerum exemplorum regit.
 ## Claves codicem bytecode explicite et canonice signant, non formam Rust debug; ideo mutatio instrumenti claves non mutat.
 ## qdocco --markdown notas, codicem QuickCoffee clausum, et valorem ultimum in documento Markdown scribit.
-## Hospes inter cursus `Context::set_fuel` vocare potest; `Context::fuel` budgetum ostendit sine globalibus deletis.
+## Hospes inter cursus `Context::set_fuel` vocare potest; `Context::fuel` budgetum ostendit sine globalibus deletis; `with_global` et `with_native` configurationem concatenatam praebent.
 ## `cargo run --example embed` hospitem Rust minimum compilat: globale ponit, callback nativum addit, et QuickCoffee currit.
 ## Hospes `Value::kind()` ad genus discernendum et `Value::is_nil()` ad nil probandum utitur, sine interioribus vasorum.
 ## Notitiae Cargoe hospites ad repositorium, API docs.rs, README et licentiam ducunt.
diff --git a/manuals/manual.zh-CN.qc b/manuals/manual.zh-CN.qc
index fa535e5..5c08bea 100644
--- a/manuals/manual.zh-CN.qc
+++ b/manuals/manual.zh-CN.qc
@@ -14,6 +14,9 @@
 ## qtest --stats 将每份文档的指令数与剩余燃料写入标准错误,不改变 ok 输出。
 ## qtest --json 为每份文档输出一行稳定 JSON,便于 CI;--stats 仍写标准错误。
 ## qtest --tap 输出 TAP 13 及确定编号的记录;--json 与 --tap 互斥。
+## qtest --filter TEXT 按路径筛选;qtest --list 只列出筛选后的文件而不执行。
+
+## qcoffee --json 单次执行输出一行 JSON 值或结构化错误,便于 CI 与宿主消费。
 ## Rust 嵌入错误有 ErrorKind::Parse、Verify、Runtime 与不依赖展示文本的详情;宿主回调可返回 Error::runtime("message"),error.position() 可给出从 1 开始的源码行。
 ## Engine::compile_program 创建时验证一次;Context::run_program 重复执行时复用不可变的已验证字节码。
 ## Program::fingerprint 提供确定性的 u64 字节码缓存键,不改变执行语义。
@@ -21,7 +24,7 @@
 ## qbench --json 为每个带语义护栏的负载输出一条计时记录;--iterations 设置样本次数。
 ## 指纹使用显式规范化字节码编码,不依赖 Rust 调试格式,故工具链显示变化不会改缓存键。
 ## qdocco --markdown 将说明、围栏 QuickCoffee 代码与最终值写成可审阅的 Markdown 产物。
-## 嵌入方可在运行之间调用 Context::set_fuel;Context::fuel 返回当前每轮预算,且不清除全局值。
+## 嵌入方可在运行之间调用 Context::set_fuel;Context::fuel 返回当前每轮预算,且不清除全局值;with_global 与 with_native 可链式配置宿主。
 ## cargo run --example embed 可编译最小 Rust 宿主:设置全局、注册原生回调并执行 QuickCoffee。
 ## 宿主可用 Value::kind() 分流类型,用 Value::is_nil() 判断 nil,无须检查内部容器。
 ## Cargo 包元数据指向仓库、docs.rs API、README 与许可证,便于嵌入方发现项目。
diff --git a/src/bin/qbench.rs b/src/bin/qbench.rs
index eeae37c..19396f9 100644
--- a/src/bin/qbench.rs
+++ b/src/bin/qbench.rs
@@ -17,6 +17,26 @@ const WORKLOADS: &[Workload] = &[
         source: "sum = 0\ni = 0\nwhile i < 100 then i = i + 1\nsum + i",
         expected: "100",
     },
+    Workload {
+        name: "stdlib-abs",
+        source: "abs(-42)",
+        expected: "42",
+    },
+    Workload {
+        name: "stdlib-sum",
+        source: "sum([1, 2, 3, 4])",
+        expected: "10",
+    },
+    Workload {
+        name: "stdlib-min-max",
+        source: "min([3, 1, 2]) + max([3, 1, 2])",
+        expected: "4",
+    },
+    Workload {
+        name: "stdlib-range-sum",
+        source: "sum(range(1, 100))",
+        expected: "4950",
+    },
     Workload {
         name: "closures-and-ranges",
         source: "base = 1\nadd = (n) -> n + base\nsum = 0\nfor n in [1...50] then sum = sum + add(n)\nsum",
diff --git a/src/bin/qtest.rs b/src/bin/qtest.rs
index 13c9ba5..0161371 100644
--- a/src/bin/qtest.rs
+++ b/src/bin/qtest.rs
@@ -43,7 +43,7 @@ fn collect(
 }
 fn usage() {
     eprintln!(
-        "Usage: qtest [--fuel N] [--stats] [--json|--tap] FILE_OR_DIRECTORY...\n       qtest --version"
+        "Usage: qtest [--fuel N] [--stats] [--json|--tap] [--filter TEXT] FILE_OR_DIRECTORY...\n       qtest --list [--filter TEXT] FILE_OR_DIRECTORY...\n       qtest --version"
     );
 }
 fn json_escape(value: &str) -> String {
@@ -76,6 +76,8 @@ fn main() -> ExitCode {
     let mut stats = false;
     let mut json = false;
     let mut tap = false;
+    let mut filter = None;
+    let mut list = false;
     let mut inputs: Vec = vec![];
     let mut args = env::args().skip(1);
     while let Some(arg) = args.next() {
@@ -98,6 +100,14 @@ fn main() -> ExitCode {
             "--stats" => stats = true,
             "--json" => json = true,
             "--tap" => tap = true,
+            "--list" => list = true,
+            "--filter" => match args.next() {
+                Some(value) if !value.is_empty() => filter = Some(value),
+                _ => {
+                    eprintln!("--filter requires non-empty text");
+                    return ExitCode::from(2);
+                }
+            },
             value if !value.starts_with('-') => inputs.push(value.to_string()),
             _ => {
                 usage();
@@ -134,15 +144,38 @@ fn main() -> ExitCode {
     }
     files.sort();
     files.dedup();
+    let filtered = filter.is_some();
+    if let Some(filter) = filter.as_deref() {
+        files.retain(|path| {
+            fs::canonicalize(path)
+                .map(|canonical| canonical.to_string_lossy().contains(filter))
+                .unwrap_or(false)
+        });
+    }
     if files.is_empty() {
+        let message = if filtered {
+            "no matching .qc test files found"
+        } else {
+            "no .qc test files found"
+        };
         if tap {
             println!("TAP version 13");
-            println!("Bail out! no .qc test files found");
+            println!("Bail out! {message}");
         } else {
-            eprintln!("no .qc test files found");
+            eprintln!("{message}");
         }
         return ExitCode::from(2);
     }
+    if list {
+        if json || tap || stats {
+            eprintln!("--list cannot be combined with --json, --tap, or --stats");
+            return ExitCode::from(2);
+        }
+        for path in files {
+            println!("{}", path.display());
+        }
+        return ExitCode::SUCCESS;
+    }
     if tap {
         println!("TAP version 13");
     }
diff --git a/src/main.rs b/src/main.rs
index f948842..80d59d8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,6 @@
 //! Command-line entry point for the `qcoffee` interpreter.
 
-use quickcoffee::{Context, Engine, Value};
+use quickcoffee::{Context, Engine, Error, Value};
 use std::{
     env, fs,
     io::{self, BufRead, IsTerminal, Write},
@@ -9,7 +9,7 @@ use std::{
 
 fn usage() {
     eprintln!(
-        "Usage: qcoffee [--fuel N] [--stats] [-i | -e SOURCE | --check FILE | --dump-bytecode FILE | --fingerprint FILE | FILE | -] [-- ARG...]\n       qcoffee --interactive\n       qcoffee --version"
+        "Usage: qcoffee [--fuel N] [--stats] [--json] [-i | -e SOURCE | --check FILE | --dump-bytecode FILE | --fingerprint FILE | FILE | -] [-- ARG...]\n       qcoffee --interactive\n       qcoffee --version"
     );
 }
 fn read_source(path: &str) -> Result {
@@ -19,6 +19,77 @@ fn read_source(path: &str) -> Result {
         fs::read_to_string(path).map_err(|error| format!("read error: {error}"))
     }
 }
+fn json_escape(value: &str) -> String {
+    let mut out = String::with_capacity(value.len() + 2);
+    for character in value.chars() {
+        match character {
+            '"' => out.push_str("\\\""),
+            '\\' => out.push_str("\\\\"),
+            '\n' => out.push_str("\\n"),
+            '\r' => out.push_str("\\r"),
+            '\t' => out.push_str("\\t"),
+            character if character.is_control() => {
+                out.push_str(&format!("\\u{:04x}", character as u32));
+            }
+            character => out.push(character),
+        }
+    }
+    out
+}
+fn json_value(value: &Value) -> String {
+    match value.kind() {
+        quickcoffee::ValueKind::Nil => "null".to_owned(),
+        quickcoffee::ValueKind::Bool => value.as_bool().unwrap().to_string(),
+        quickcoffee::ValueKind::Number => {
+            let number = value.as_number().unwrap();
+            if number.is_finite() {
+                number.to_string()
+            } else {
+                "null".to_owned()
+            }
+        }
+        quickcoffee::ValueKind::String => {
+            format!("\"{}\"", json_escape(value.as_str().unwrap()))
+        }
+        quickcoffee::ValueKind::Array => format!(
+            "[{}]",
+            value
+                .as_array()
+                .unwrap()
+                .iter()
+                .map(json_value)
+                .collect::>()
+                .join(",")
+        ),
+        quickcoffee::ValueKind::Map => format!(
+            "{{{}}}",
+            value
+                .as_map()
+                .unwrap()
+                .iter()
+                .map(|(key, value)| { format!("\"{}\":{}", json_escape(key), json_value(value)) })
+                .collect::>()
+                .join(",")
+        ),
+        quickcoffee::ValueKind::Function => "{\"$quickcoffee\":\"function\"}".to_owned(),
+    }
+}
+fn json_error(error: &Error) -> String {
+    let line = error
+        .position()
+        .map_or_else(|| "null".to_owned(), |position| position.line.to_string());
+    format!(
+        "{{\"ok\":false,\"kind\":\"{}\",\"message\":\"{}\",\"line\":{line}}}",
+        error.kind(),
+        json_escape(error.message())
+    )
+}
+fn json_io_error(stage: &str, message: &str) -> String {
+    format!(
+        "{{\"ok\":false,\"stage\":\"{stage}\",\"kind\":\"io\",\"message\":\"{}\",\"line\":null}}",
+        json_escape(message)
+    )
+}
 fn repl(fuel: u64, script_args: Vec, stats: bool) -> ExitCode {
     let stdin = io::stdin();
     let show_prompt = stdin.is_terminal() && io::stdout().is_terminal();
@@ -90,6 +161,7 @@ fn main() -> ExitCode {
     let mut fingerprint = false;
     let mut check = false;
     let mut stats = false;
+    let mut json = false;
     let mut interactive = false;
     let mut script_args = vec![];
     while let Some(arg) = args.next() {
@@ -115,6 +187,7 @@ fn main() -> ExitCode {
                 }
             },
             "--stats" => stats = true,
+            "--json" => json = true,
             "-e" => match args.next() {
                 Some(s) if source.is_none() => source = Some(s),
                 Some(_) => {
@@ -127,9 +200,9 @@ fn main() -> ExitCode {
                 }
             },
             "--dump-bytecode" => {
-                if source.is_some() || dump || check || fingerprint || stats {
+                if source.is_some() || dump || check || fingerprint || stats || json {
                     eprintln!(
-                        "--check, --dump-bytecode, --fingerprint, and --stats are execution-mode alternatives"
+                        "--check, --dump-bytecode, --fingerprint, --json, and --stats are execution-mode alternatives"
                     );
                     return ExitCode::from(2);
                 }
@@ -154,9 +227,9 @@ fn main() -> ExitCode {
                 }
             }
             "--check" => {
-                if source.is_some() || dump || check || fingerprint || stats {
+                if source.is_some() || dump || check || fingerprint || stats || json {
                     eprintln!(
-                        "--check, --dump-bytecode, --fingerprint, and --stats are execution-mode alternatives"
+                        "--check, --dump-bytecode, --fingerprint, --json, and --stats are execution-mode alternatives"
                     );
                     return ExitCode::from(2);
                 }
@@ -165,7 +238,11 @@ fn main() -> ExitCode {
                     Some(path) => match read_source(&path) {
                         Ok(text) => source = Some(text),
                         Err(error) => {
-                            eprintln!("{error}");
+                            if json {
+                                println!("{}", json_io_error("read", &error));
+                            } else {
+                                eprintln!("{error}");
+                            }
                             return ExitCode::from(1);
                         }
                     },
@@ -176,9 +253,9 @@ fn main() -> ExitCode {
                 }
             }
             "--fingerprint" => {
-                if source.is_some() || dump || check || fingerprint || stats {
+                if source.is_some() || dump || check || fingerprint || stats || json {
                     eprintln!(
-                        "--check, --dump-bytecode, --fingerprint, and --stats are execution-mode alternatives"
+                        "--check, --dump-bytecode, --fingerprint, --json, and --stats are execution-mode alternatives"
                     );
                     return ExitCode::from(2);
                 }
@@ -205,14 +282,22 @@ fn main() -> ExitCode {
             "-" if source.is_none() => match read_source("-") {
                 Ok(text) => source = Some(text),
                 Err(error) => {
-                    eprintln!("{error}");
+                    if json {
+                        println!("{}", json_io_error("read", &error));
+                    } else {
+                        eprintln!("{error}");
+                    }
                     return ExitCode::from(1);
                 }
             },
             path if !path.starts_with('-') && source.is_none() => match read_source(path) {
                 Ok(text) => source = Some(text),
                 Err(error) => {
-                    eprintln!("{error}");
+                    if json {
+                        println!("{}", json_io_error("read", &error));
+                    } else {
+                        eprintln!("{error}");
+                    }
                     return ExitCode::from(1);
                 }
             },
@@ -222,10 +307,16 @@ fn main() -> ExitCode {
             }
         }
     }
+    if json && (dump || check || fingerprint) {
+        eprintln!(
+            "--check, --dump-bytecode, --fingerprint, and --json are execution-mode alternatives"
+        );
+        return ExitCode::from(2);
+    }
     if interactive {
-        if source.is_some() || check || dump || fingerprint {
+        if source.is_some() || check || dump || fingerprint || json {
             eprintln!(
-                "--interactive cannot be combined with a source, --check, --dump-bytecode, or --fingerprint"
+                "--interactive cannot be combined with a source, --check, --dump-bytecode, --fingerprint, or --json"
             );
             return ExitCode::from(2);
         }
@@ -245,7 +336,11 @@ fn main() -> ExitCode {
     let chunk = match engine.compile(&source) {
         Ok(c) => c,
         Err(e) => {
-            eprintln!("{e}");
+            if json {
+                println!("{}", json_error(&e));
+            } else {
+                eprintln!("{e}");
+            }
             return ExitCode::from(1);
         }
     };
@@ -275,13 +370,19 @@ fn main() -> ExitCode {
     }
     match result {
         Ok(value) => {
-            if !matches!(value, quickcoffee::Value::Nil) {
+            if json {
+                println!("{{\"ok\":true,\"value\":{}}}", json_value(&value));
+            } else if !matches!(value, quickcoffee::Value::Nil) {
                 println!("{value}")
             }
             ExitCode::SUCCESS
         }
         Err(e) => {
-            eprintln!("{e}");
+            if json {
+                println!("{}", json_error(&e));
+            } else {
+                eprintln!("{e}");
+            }
             ExitCode::from(1)
         }
     }
diff --git a/src/vm.rs b/src/vm.rs
index 8a21abe..c1e140e 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -445,6 +445,14 @@ impl Context {
     pub fn set_global(&mut self, name: impl Into, value: Value) {
         self.global.borrow_mut().values.insert(name.into(), value);
     }
+    /// Returns this context after installing an immutable global value.
+    ///
+    /// This builder-style form is equivalent to [`Context::set_global`] and
+    /// is convenient when configuring an embedding context inline.
+    pub fn with_global(mut self, name: impl Into, value: Value) -> Self {
+        self.set_global(name, value);
+        self
+    }
     /// Reads a global value without exposing the VM environment or running code.
     pub fn get_global(&self, name: &str) -> Option {
         lookup(&self.global, name)
@@ -461,6 +469,16 @@ impl Context {
             })),
         );
     }
+    /// Returns this context after registering a host callback as a global.
+    ///
+    /// This builder-style form is equivalent to [`Context::add_native`].
+    pub fn with_native(mut self, name: impl Into, f: F) -> Self
+    where
+        F: Fn(&[Value]) -> Result + 'static,
+    {
+        self.add_native(name, f);
+        self
+    }
     /// Compiles, verifies, and executes source in this context.
     pub fn eval(&mut self, source: &str) -> Result {
         let program = self.engine.compile_program(source)?;
diff --git a/tests/cli_tools.rs b/tests/cli_tools.rs
index 0d8b5df..4d86811 100644
--- a/tests/cli_tools.rs
+++ b/tests/cli_tools.rs
@@ -185,6 +185,42 @@ fn qtest_reports_success_and_failure() {
             "qtest skipped {fixture}"
         );
     }
+    let filtered = Command::new(bin("qtest"))
+        .args(["--filter", "stdlib", "tests/scripts"])
+        .output()
+        .unwrap();
+    assert!(filtered.status.success());
+    assert_eq!(String::from_utf8_lossy(&filtered.stdout).lines().count(), 1);
+    assert!(String::from_utf8_lossy(&filtered.stdout).contains("stdlib.qc"));
+    let single_file = Command::new(bin("qtest"))
+        .args(["--filter", "arithmetic.qc", "tests/scripts/arithmetic.qc"])
+        .output()
+        .unwrap();
+    assert!(single_file.status.success());
+    assert_eq!(
+        String::from_utf8_lossy(&single_file.stdout).lines().count(),
+        1
+    );
+    assert!(String::from_utf8_lossy(&single_file.stdout).contains("arithmetic.qc"));
+    let listed = Command::new(bin("qtest"))
+        .args(["--list", "--filter", "stdlib", "tests/scripts"])
+        .output()
+        .unwrap();
+    assert!(listed.status.success());
+    assert_eq!(
+        String::from_utf8_lossy(&listed.stdout),
+        "tests/scripts/stdlib.qc\n"
+    );
+    let missing_filter = Command::new(bin("qtest"))
+        .args(["--filter", "does-not-exist", "tests/scripts"])
+        .output()
+        .unwrap();
+    assert_eq!(missing_filter.status.code(), Some(2));
+    let list_conflict = Command::new(bin("qtest"))
+        .args(["--list", "--json", "tests/scripts"])
+        .output()
+        .unwrap();
+    assert_eq!(list_conflict.status.code(), Some(2));
     let bad = Command::new(bin("qtest"))
         .arg("tests/fixtures/failure.qc")
         .output()
@@ -470,6 +506,82 @@ fn qcoffee_evaluation_fuel_and_disassembly_match_the_cli_contract() {
     let _ = fs::remove_file(temp);
 }
 
+#[test]
+fn qcoffee_json_reports_values_and_structured_errors() {
+    let value = Command::new(bin("qcoffee"))
+        .args(["--json", "-e", "{answer: 42, ok: true}"])
+        .output()
+        .unwrap();
+    assert!(value.status.success());
+    assert_eq!(
+        String::from_utf8_lossy(&value.stdout),
+        "{\"ok\":true,\"value\":{\"answer\":42,\"ok\":true}}\n"
+    );
+    assert!(value.stderr.is_empty());
+
+    let function = Command::new(bin("qcoffee"))
+        .args(["--json", "-e", "(x) -> x"])
+        .output()
+        .unwrap();
+    assert!(function.status.success());
+    assert_eq!(
+        String::from_utf8_lossy(&function.stdout),
+        "{\"ok\":true,\"value\":{\"$quickcoffee\":\"function\"}}\n"
+    );
+
+    let nil = Command::new(bin("qcoffee"))
+        .args(["--json", "-e", "nil"])
+        .output()
+        .unwrap();
+    assert!(nil.status.success());
+    assert_eq!(
+        String::from_utf8_lossy(&nil.stdout),
+        "{\"ok\":true,\"value\":null}\n"
+    );
+
+    let parse_error = Command::new(bin("qcoffee"))
+        .args(["--json", "-e", "@"])
+        .output()
+        .unwrap();
+    assert!(!parse_error.status.success());
+    assert_eq!(
+        String::from_utf8_lossy(&parse_error.stdout),
+        "{\"ok\":false,\"kind\":\"parse\",\"message\":\"unexpected character '@'\",\"line\":1}\n"
+    );
+    assert!(parse_error.stderr.is_empty());
+
+    let runtime_error = Command::new(bin("qcoffee"))
+        .args(["--json", "--fuel", "10", "-e", "while true then 1"])
+        .output()
+        .unwrap();
+    assert!(!runtime_error.status.success());
+    let runtime_stdout = String::from_utf8_lossy(&runtime_error.stdout);
+    assert!(runtime_stdout.starts_with("{\"ok\":false,\"kind\":\"runtime\""));
+    assert!(runtime_stdout.contains("fuel exhausted"));
+    assert!(runtime_stdout.ends_with("\"line\":null}\n"));
+
+    let missing = Command::new(bin("qcoffee"))
+        .args(["--json", "qcoffee-file-that-does-not-exist.qc"])
+        .output()
+        .unwrap();
+    assert!(!missing.status.success());
+    let missing_stdout = String::from_utf8_lossy(&missing.stdout);
+    assert!(
+        missing_stdout.starts_with(
+            "{\"ok\":false,\"stage\":\"read\",\"kind\":\"io\",\"message\":\"read error:"
+        )
+    );
+    assert!(missing_stdout.ends_with("\",\"line\":null}\n"));
+    assert!(missing.stderr.is_empty());
+
+    let reverse_conflict = Command::new(bin("qcoffee"))
+        .args(["--check", "tests/scripts/arithmetic.qc", "--json"])
+        .output()
+        .unwrap();
+    assert_eq!(reverse_conflict.status.code(), Some(2));
+    assert!(String::from_utf8_lossy(&reverse_conflict.stderr).contains("--json"));
+}
+
 #[test]
 fn qcoffee_fingerprint_is_stable_non_executing_and_mutually_exclusive() {
     let temp = std::env::temp_dir().join(format!("qcoffee-fingerprint-{}.qc", std::process::id()));
@@ -524,6 +636,10 @@ fn qbench_json_is_guarded_and_machine_readable() {
     let lines: Vec<_> = stdout.lines().collect();
     let expected_names = [
         "loop-core",
+        "stdlib-abs",
+        "stdlib-sum",
+        "stdlib-min-max",
+        "stdlib-range-sum",
         "closures-and-ranges",
         "map-spread",
         "negative-indexing",
diff --git a/tests/embedding_api.rs b/tests/embedding_api.rs
index 0416134..e54440b 100644
--- a/tests/embedding_api.rs
+++ b/tests/embedding_api.rs
@@ -12,7 +12,10 @@ fn public_embedding_surface_runs_shared_programs_with_host_state() {
     let mut context = Context::new();
     context.set_global("factor", Value::from(2_i64));
     context.add_native("host", |args| {
-        let (Some(left), Some(right)) = (args[0].as_number(), args[1].as_number()) else {
+        let (Some(left), Some(right)) = (
+            args.first().and_then(Value::as_number),
+            args.get(1).and_then(Value::as_number),
+        ) else {
             return Err(Error::runtime("host expects two numbers"));
         };
         Ok(Value::from(left + right))
@@ -27,6 +30,29 @@ fn public_embedding_surface_runs_shared_programs_with_host_state() {
     assert!(context.get_global("missing").is_none());
 }
 
+#[test]
+fn builder_embedding_surface_chains_host_configuration() {
+    let program = Engine::new()
+        .compile_program("host(20, 22) * factor")
+        .unwrap();
+    let mut context = Context::new()
+        .with_global("factor", Value::from(2_i64))
+        .with_native("host", |args| {
+            let (Some(left), Some(right)) = (
+                args.first().and_then(Value::as_number),
+                args.get(1).and_then(Value::as_number),
+            ) else {
+                return Err(Error::runtime("host expects two numbers"));
+            };
+            Ok(Value::from(left + right))
+        });
+    assert_eq!(
+        context.run_program(&program).unwrap().as_number(),
+        Some(84.)
+    );
+    assert_eq!(context.get_global("factor").unwrap().as_number(), Some(2.));
+}
+
 #[test]
 fn public_values_and_native_errors_are_structured() {
     let mut context = Context::new();

From 2128895c246adb2cd255c69fca04a6f01e68cfa3 Mon Sep 17 00:00:00 2001
From: tiye 
Date: Sun, 23 Aug 2026 15:21:38 +0800
Subject: [PATCH 2/3] fix: permit generated docs during package verification

---
 Makefile                               | 2 +-
 RFCs/0106-package-verification-gate.md | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 7f2f7c9..4abd5ab 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ package-metadata:
 	cargo metadata --locked --no-deps --format-version 1 >/dev/null
 
 package:
-	cargo publish --dry-run --locked
+	cargo publish --dry-run --locked --allow-dirty
 
 clippy:
 	cargo clippy --locked --all-targets -- -D warnings
diff --git a/RFCs/0106-package-verification-gate.md b/RFCs/0106-package-verification-gate.md
index bf96b56..fbc0a00 100644
--- a/RFCs/0106-package-verification-gate.md
+++ b/RFCs/0106-package-verification-gate.md
@@ -3,6 +3,6 @@
 - 状态:已采纳
 - 依赖:RFC 0086、RFC 0087
 
-成熟发布验收除 `cargo metadata --locked` 外,还必须运行 `cargo publish --dry-run --locked`。Cargo 将按发布清单生成 crate、校验发布元数据,并在隔离的包目录中构建它;因此缺失源文件、示例、文档、RFC 或锁文件不一致会在 CI 中暴露,而不是等到真正发布时才发现。
+成熟发布验收除 `cargo metadata --locked` 外,还必须运行 `cargo publish --dry-run --locked --allow-dirty`。Cargo 将按发布清单生成 crate、校验发布元数据,并在隔离的包目录中构建它;因此缺失源文件、示例、文档、RFC 或锁文件不一致会在 CI 中暴露,而不是等到真正发布时才发现。
 
-`make check` 包含该门禁,CI 使用干净工作树运行同一命令。`--dry-run` 会执行 registry 上传前的验证与构建,但不会上传 crate、修改版本或更改远程状态;生成物只位于 Cargo 的临时目录。
+`make check` 包含该门禁。`--dry-run` 会执行 registry 上传前的验证与构建,但不会上传 crate、修改版本或更改远程状态;`--allow-dirty` 允许此前 `make docs` 生成待检查的文档,CI 随后以 `git diff --exit-code -- docs` 拒绝未提交的文档变更。生成物只位于 Cargo 的临时目录。

From a95d40bd0f32c29dd201fe2db96d4918f0f2c66c Mon Sep 17 00:00:00 2001
From: tiye 
Date: Sun, 23 Aug 2026 15:39:45 +0800
Subject: [PATCH 3/3] docs: regenerate literate manual markdown

---
 docs/manual.classical-zh.md  | 1 +
 docs/manual.devanagari-sa.md | 1 +
 docs/manual.en.md            | 1 +
 docs/manual.latin.md         | 1 +
 docs/manual.zh-CN.md         | 2 ++
 5 files changed, 6 insertions(+)

diff --git a/docs/manual.classical-zh.md b/docs/manual.classical-zh.md
index 41f61b8..0dfc04d 100644
--- a/docs/manual.classical-zh.md
+++ b/docs/manual.classical-zh.md
@@ -3,6 +3,7 @@
 ## Notes
 
 QuickCoffee 用法
+
 映射可展其项,后书之键胜前书:{...defaults, theme: 'dark'}。
 映射解构末可用 ...metadata 收未列之键,所得映射不变。
 数列与 Unicode 字符负索引,-一取其末。
diff --git a/docs/manual.devanagari-sa.md b/docs/manual.devanagari-sa.md
index 9873e24..4ce5167 100644
--- a/docs/manual.devanagari-sa.md
+++ b/docs/manual.devanagari-sa.md
@@ -3,6 +3,7 @@
 ## Notes
 
 QuickCoffee मार्गदर्शिका
+
 मानचित्र-विस्तारः पश्चात् लिखिता कुञ्जी पूर्वलिखितां जयति।
 मानचित्र-विन्यासे ...metadata अनुक्तानि कुञ्जीनि गृह्णाति।
 ऋण-सूचकाङ्केन क्रमस्य अन्तिमं पदं लभ्यते।
diff --git a/docs/manual.en.md b/docs/manual.en.md
index d568d16..c49f699 100644
--- a/docs/manual.en.md
+++ b/docs/manual.en.md
@@ -3,6 +3,7 @@
 ## Notes
 
 QuickCoffee manual
+
 Source is parsed, compiled to verified bytecode, and executed with a fuel budget.
 qcoffee - reads a QuickCoffee program from standard input.
 qcoffee --stats writes instruction and remaining-fuel counters to stderr while preserving program stdout; qcoffee accepts one source input and rejects conflicting execution modes.
diff --git a/docs/manual.latin.md b/docs/manual.latin.md
index a11b98c..afda2ca 100644
--- a/docs/manual.latin.md
+++ b/docs/manual.latin.md
@@ -3,6 +3,7 @@
 ## Notes
 
 Manuale QuickCoffee
+
 Tabulae mappae segmenta expandere possunt; claves posteriores priores superant.
 In forma mappae ...metadata claves omissas immutabiliter capit.
 Indices negativi in seriebus et textu Unicode extremum elementum petunt.
diff --git a/docs/manual.zh-CN.md b/docs/manual.zh-CN.md
index ce9a80d..737ffe6 100644
--- a/docs/manual.zh-CN.md
+++ b/docs/manual.zh-CN.md
@@ -3,6 +3,7 @@
 ## Notes
 
 QuickCoffee 用户手册
+
 QuickCoffee 先将源码解析并编译为经验证的字节码,随后由带 fuel 限制的 VM 执行。
 qcoffee - 可从标准输入读取 QuickCoffee 程序。
 qcoffee --stats 将指令数与剩余燃料写入标准错误,同时保持程序标准输出不变;qcoffee 每次只接受一个源码输入,冲突执行模式会报用法错误。
@@ -42,6 +43,7 @@ yes/on 与 no/off 是布尔别名;is/isnt 保持严格相等。
 映射字面量可从左至右展开:{...defaults, theme: 'dark'};后写键覆盖先写键。
 映射解构末尾可用 ...metadata 捕获未列键,所得映射不可变。
 数组与 Unicode 字符串支持负索引,-1 取末项。
+
 函数捕获词法环境;末尾参数可写作 y = 2,缺省或 nil 时在函数内取默认值;末位 rest 参数写作 tail...。
 普通名称参数可省略括号:left, right -> left + right;默认、rest 与解构参数仍须括号。
 return expression 只在函数内结束当前调用;裸 return 得 nil,并清理循环且执行沿途 finally。