Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ clippy:
cargo clippy --locked --all-targets -- -D warnings

api-doc:
RUSTDOCFLAGS="-D warnings" cargo doc --locked --no-deps
RUSTDOCFLAGS="-D warnings -D missing_docs" cargo doc --locked --no-deps

doc-check:
cargo run --locked --quiet --bin qdocco -- --check manuals/manual.zh-CN.qc
Expand Down
10 changes: 10 additions & 0 deletions PERFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ for n in [1...100] by 3 then sum = sum + n
sum
```

stepped-string-iteration(20,000 次):

```coffee
sum = 0
for character, index in 'a☕中x' by 2 then sum += index
sum
```

for-collection(10,000 次):

```coffee
Expand Down Expand Up @@ -250,6 +258,7 @@ sum
| closures-and-ranges | 50.954 ms | 196,255 programs/s | 368.589 ms | 27,130 programs/s |
| bare-lambda | 49.829 ms | 200,686 programs/s | 367.051 ms | 27,244 programs/s |
| stepped-iteration | 31.895 ms | 313,529 programs/s | 123.921 ms | 80,697 programs/s |
| stepped-string-iteration | 69.630 ms | 287,232 programs/s | 49.288 ms | 405,780 programs/s |
| for-collection | 36.532 ms | 273,733 programs/s | 310.488 ms | 32,207 programs/s |
| postfix-comprehension | 43.229 ms | 231,326 programs/s | 542.345 ms | 18,438 programs/s |
| for-pattern-bindings | 57.605 ms | 173,596 programs/s | 946.521 ms | 10,565 programs/s |
Expand Down Expand Up @@ -460,6 +469,7 @@ rest 绑定会复制剩余元素到新的不可变数组,以保持宿主存储
| closures-and-ranges | 50.707 / 52.681 / 50.954 | 386.383 / 368.550 / 368.589 |
| bare-lambda | 49.267 / 51.139 / 49.829 | 367.051 / 366.815 / 367.863 |
| stepped-iteration | 31.774 / 32.983 / 31.895 | 123.424 / 123.921 / 125.822 |
| stepped-string-iteration | 69.630 / 68.868 / 72.102 | 49.175 / 50.030 / 49.288 |
| for-collection | 35.892 / 37.276 / 36.532 | 310.488 / 310.415 / 320.711 |
| postfix-comprehension | 42.830 / 45.106 / 43.229 | 542.345 / 541.648 / 555.071 |
| for-pattern-bindings | 57.142 / 59.312 / 57.605 | 943.416 / 946.521 / 965.377 |
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

QuickCoffee 是一台以 Rust 编写、受 CoffeeScript 启发的字节码脚本引擎。它保留紧凑、可读的表达式语法,却不兼容 JavaScript:没有原型链、`this`、`eval` 或嵌入 JavaScript。

当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0091-descending-ranges.md](RFCs/0091-descending-ranges.md)。
当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0095-string-iteration-by-step.md](RFCs/0095-string-iteration-by-step.md)。

```coffee
square = (x) -> x * x
Expand Down
11 changes: 9 additions & 2 deletions RFCs/0070-string-iteration.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,19 @@ for character, index in 'a☕中' then index

第二绑定是从零开始的 Unicode 标量下标,而不是 UTF-8 字节偏移。每轮模式匹配成功后才写入绑定;字符串为空时产生空数组。后置推导和语句位置的丢弃循环同样适用。

`by` 只属于数组迭代。由于迭代对象可在运行时求值,`for value in dynamic by step` 在运行时遇到字符串时报告错误,而不是静默改变步长语义。映射仍使用 `of`,不受本 RFC 影响。
字符串迭代也接受 `by step`。步长只求值一次,必须是正有限整数;它跳过 Unicode 标量而不是 UTF-8 字节,第二绑定仍是实际的标量下标:

```coffee
for character, index in 'a☕中x' by 2 then [character, index]
# => [[a, 0], [中, 2]]
```

由于迭代对象可在运行时求值,`for value in dynamic by step` 在运行时按实际数组或字符串类型采用同一正整数步长。映射仍使用 `of`,不受本 RFC 影响。

非数组、非字符串的 `in` 迭代对象仍是运行时错误;字符串的 `of` 迭代仍是映射类型错误。该功能不暴露 JavaScript 的 UTF-16 code unit、迭代器对象或原型链。

## 字节码与验收

编译器发出统一的 `IterStartEnumerable`,消耗迭代对象与步长;VM 根据运行时值选择数组或 Unicode 字符串迭代。验证器按原数组迭代路径检查两个栈值与一个迭代器状态,`IterNext` 的模式数量和控制流规则不变。

验收至少包括 ASCII 与非 ASCII 标量、动态字符串、可选下标、过滤、空字符串、`by` 错误、非字符串/数组错误、嵌套控制流,以及生成字节码的验证。
验收至少包括 ASCII 与非 ASCII 标量、动态字符串、可选下标、过滤、空字符串、非法步长错误、非字符串/数组错误、嵌套控制流,以及生成字节码的验证。
23 changes: 23 additions & 0 deletions RFCs/0092-public-api-documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# RFC 0092:公开 Rust API 文档门禁

- 状态:已采纳
- 依赖:RFC 0002、RFC 0043、RFC 0046、RFC 0089

## 动机

QuickCoffee 的 crate 既是 CLI 的实现,也是宿主系统的嵌入 API。若公开的 `Value`、`Context`、
`Program`、`Chunk` 或错误类型没有 rustdoc,docs.rs 不能提供可靠的集成入口;普通编译通过
并不能证明发布 API 可发现、可维护。

## 契约

所有公开类型、字段、枚举变体、方法和顶层函数必须有简短 rustdoc。低层 `Instruction`、
`Pattern`、`Constant` 的变体允许使用统一的枚举级说明,因为它们是已验证字节码的机械标签,
但公开 `Chunk` 字段、宿主值访问器、错误分类和 `Engine`/`Context`/`Program` 操作必须说明
所有权、验证与执行语义。文档不得承诺原型链、JavaScript `undefined` 或隐藏的可变状态。

## 验收

Makefile 的 `api-doc` 使用 `RUSTDOCFLAGS="-D warnings -D missing_docs" cargo doc --locked
--no-deps`;CI 的 `make check` 因而把缺失文档视为失败。外部 `tests/embedding_api.rs` 继续
证明文档所描述的公开入口可从 crate 外部调用,其他运行时行为不变。
26 changes: 26 additions & 0 deletions RFCs/0093-value-kind-inspection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# RFC 0093:宿主值类型标签

- 状态:已采纳
- 依赖:RFC 0002、RFC 0041、RFC 0089、RFC 0092

## 动机

嵌入方经常需要在调用 `Value` 访问器前判断值类型。逐个尝试 `as_number`、`as_array` 等
方法既冗长,也会诱使宿主直接匹配公开 enum 的内部 `Rc` 容器。需要一个不泄漏存储实现、
可在 match 中使用的稳定类型标签。

## 契约

公开 `ValueKind::{Nil, Bool, Number, String, Array, Map, Function}`,并提供:

- `Value::kind()`:只读返回对应标签,不执行脚本、不克隆容器;
- `Value::is_nil()`:仅对 `nil` 返回 true,false/0/空容器均返回 false。

标签与 QuickCoffee 运行时类型一一对应;新增类型必须显式扩展 `ValueKind`。现有 `as_*`
访问器、构造器、Display 语义和字节码指纹不变。

## 验收

`tests/embedding_api.rs` 从 crate 外部检查全部相关标签与 `nil` 行为;严格 rustdoc 门禁必须
包含新类型和方法。五语嵌入说明可继续使用 `Value::kind()` 做宿主分流,`make check` 保持
通过。
22 changes: 22 additions & 0 deletions RFCs/0094-qdocco-final-true-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# RFC 0094:qdocco 文学源最终值门禁

- 状态:已采纳
- 依赖:RFC 0003、RFC 0005、RFC 0083

## 动机

`qdocco --check` 是文学编程源和用户手册的可执行验收入口。RFC 0005 规定每份手册最后
必须得到布尔 `true`,但旧实现只判断源程序没有读取、解析或运行错误,导致返回数字、字符串
或 `nil` 的文档错误地通过检查。

## 契约

`qdocco --check FILE` 在读取、编译、验证和执行成功后,还必须要求最终值严格为
`Value::Bool(true)`。其他值以非零退出码(1)失败,并在标准错误说明实际值与期望 `true`。
普通 HTML/Markdown 生成模式仍可展示任意最终值,便于文档工具调试;该门禁只属于显式
`--check`。qdocco 不执行 Markdown、HTML 或 JavaScript 内容。

## 验收

CLI 集成测试覆盖最终值 `true` 的通过、数字最终值的拒绝及既有 HTML/Markdown 输出。五份
文学手册继续由 `qdocco --check` 验证,`make check` 与生成物一致性必须通过。
23 changes: 23 additions & 0 deletions RFCs/0095-string-iteration-by-step.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# RFC 0095:字符串 Unicode 标量步进迭代

- 状态:已采纳
- 依赖:RFC 0070、RFC 0042、RFC 0044

## 动机

RFC 0070 已定义字符串按 Unicode 标量迭代,但 `by` 仍被拒绝。数组与字符串共享 `for` 收集、过滤、模式绑定和可选下标语义;拒绝字符串步长使同一语法在运行时类型切换时不一致,也迫使文档保留一个不必要的例外。

## 契约

`for value in string by step then body` 按 Unicode 标量位置 `0, step, 2*step, ...` 取值。每个值是单一 Unicode 标量组成的 QuickCoffee 字符串,不暴露 UTF-8 字节或 JavaScript UTF-16 code unit。第二绑定得到实际的标量下标,而非迭代轮数:

```coffee
for character, index in 'a☕中x' by 2 then [character, index]
# => [[a, 0], [中, 2]]
```

步长表达式只求值一次,必须是正的有限整数;数组和字符串共享该检查。动态 `in` 迭代对象在运行时决定采用数组或字符串的步进路径;`of` 映射迭代不接受 `by`。空字符串、过滤、后置推导、`break`、`continue` 与严格递归模式保持 RFC 0070 语义。

## 实现与验收

`IterationKind::String` 保存步长,并以饱和加法推进 Unicode 标量位置;字节码指令格式与验证规则不变。验收覆盖 ASCII 与多字节 Unicode、动态字符串、实际标量下标、动态步长、空输入、过滤、嵌套控制流及既有非法步长/映射错误。
24 changes: 24 additions & 0 deletions RFCs/0096-stepped-string-benchmark.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# RFC 0096:字符串步进迭代性能基准覆盖

- 状态:已采纳
- 依赖:RFC 0045、RFC 0081、RFC 0095

## 动机

RFC 0095 扩展了字符串 `for ... by` 的执行路径,但若只保留功能测试,字符串 Unicode 标量步进可能在 VM 优化或回归中变慢而不被发现。项目既要求可执行语义护栏,也要求性能报告能按工作负载追踪编译、验证和执行成本。

## 契约

`cargo bench --bench core` 必须包含 `stepped-string-iteration` 工作负载;`qbench --json` 也必须输出同名记录。负载使用 ASCII 与多字节 Unicode、实际标量下标和 `by 2`,最终值严格为 `2`:

```coffee
sum = 0
for character, index in 'a☕中x' by 2 then sum += index
sum # => 2
```

两条基准路径都必须先编译/验证并执行语义检查,再计时;结果不得包含标准输出、文件 I/O 或调试构建。性能报告记录机器、工具链、命令、迭代数和至少三次 release 样本的中位数,不把单次读数当成跨机器比较。

## 验收

`cargo bench --locked --bench core` 的新工作负载通过最终值护栏;`cargo run --locked --release --bin qbench -- --json --iterations 1` 输出一个 `stepped-string-iteration` JSON 记录且 `expected` 为 `2`;`make check` 与性能报告中的基线数据保持一致。
6 changes: 6 additions & 0 deletions benches/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ fn main() {
iterations: 20_000,
expected: "3",
},
Workload {
name: "stepped-string-iteration",
source: "sum = 0\nfor character, index in 'a☕中x' by 2 then sum += index\nsum",
iterations: 20_000,
expected: "2",
},
Workload {
name: "string-escapes",
source: "message = \"A\\x42\\u{43}\"\nlen(message) + (if message == 'ABC' then 1 else 0)",
Expand Down
3 changes: 2 additions & 1 deletion docs/manual.classical-zh.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
<p>qcoffee --interactive(或 -i)者,逐行共用一 Context;:help 示命,:quit 出之。</p>
<p>qcoffee --interactive --stats 惟非空行之行而行或运行时有误者,书指令与余燃料一条;析验之误不更书。</p>
<p>'a☕中'[1] 即 '☕','a☕中'[1..2]' 得 '☕中';字符串索引循 Unicode 标量。</p>
<p>for character, index in 'a☕中' then index,得 [0, 1, 2];字符串循 Unicode 标量,弗受 by。</p>
<p>for character, index in 'a☕中' then index,得 [0, 1, 2];字符串循 Unicode 标量,亦受正整数 by。</p>
<p>[head, tail...] = [1, 2, 3],tail 得 [2, 3];数组之 rest 必居末。</p>
<p>qtest --fuel N 者,为各可行文别限其指令之数。</p>
<p>qtest --stats 更书各篇所试指令与余燃料于标准错误,而 ok 之出不改。</p>
Expand All @@ -29,6 +29,7 @@
<p>qdocco --markdown 出说明、围栏 QuickCoffee 代码及终值为可阅 Markdown 文。</p>
<p>嵌者可于两行之间呼 Context::set_fuel;Context::fuel 示每行之限,而全局不失。</p>
<p>cargo run --example embed 可验最小 Rust 宿主,设全局、立原生回调而行 QuickCoffee。</p>
<p>宿主可用 Value::kind() 别其类,Value::is_nil() 验 nil,不窥其内容器。</p>
<p>Cargo 包志指仓、docs.rs API、README 与许可证,使嵌者易寻其用。</p>
<p>Context::last_execution() 示所试指令与余燃料,而不露 VM 之帧。</p>
<p>-- 后之参,以常字符串数组 argv 见于文中。</p>
Expand Down
2 changes: 1 addition & 1 deletion docs/manual.classical-zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@ QuickCoffee 者,Rust 所为字节码机也,非 JavaScript 之运行时。其

一逻辑行中,函调用可略括,如 `implicit_answer = implicit_add 20, 22`;遇比较或布局之界,仍宜明括。

`qtest --json` 各试篇出定 JSON,`qtest --tap` 出 TAP 13;`qcoffee --fingerprint FILE` 不行其文而出定式字节码键;`qbench --json` 记编、验、行之时,皆有语义护栏;`qdocco --markdown` 出可阅文学编程 Markdown。嵌者可呼 `Context::set_fuel` 改复用境之限,并行 `cargo run --example embed` 观宿主全例。
`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` 观宿主全例。
3 changes: 2 additions & 1 deletion docs/manual.devanagari-sa.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<p>qcoffee --interactive (वा -i) एकं Context पङ्क्ति-क्रमेण धारयति; :help दर्शयति, :quit निर्गच्छति।</p>
<p>qcoffee --interactive --stats केवलं कार्यितायै वा runtime-दोषयुक्तायै non-empty पङ्क्त्यै instruction तथा fuel लेखं लिखति; parse अथवा verify-दोषे नूतनं लेखं न लिखति।</p>
<p>'a☕中'[1] '☕' अस्ति, 'a☕中'[1..2] '☕中' अस्ति; string-index Unicode-scalar-अनुसारी अस्ति।</p>
<p>for character, index in 'a☕中' then index Unicode-scalar-अङ्कान् [0, 1, 2] ददाति; string-iteration मध्ये by नास्ति।</p>
<p>for character, index in 'a☕中' then index Unicode-scalar-अङ्कान् [0, 1, 2] ददाति; string-iteration मध्ये धनात्मक by-क्रमः अस्ति।</p>
<p>[head, tail...] = [1, 2, 3] tail-नाम्नि [2, 3] बध्नाति; array-pattern rest अन्तिमः भवति।</p>
<p>qtest --fuel N प्रत्येक executable-document पृथक् instruction-budget ददाति।</p>
<p>qtest --stats प्रत्येकस्य documentस्य instruction-संख्या तथा अवशिष्ट-fuel standard error मध्ये लिखति, ok-निर्गमं न परिवर्तयति।</p>
Expand All @@ -25,6 +25,7 @@
<p>qdocco --markdown टिप्पणीन्, सीमितं QuickCoffee-कोडं, अन्तिम-मूल्यं च पठनीय Markdown-फलके लिखति।</p>
<p>अन्तःस्थापकः चालनयोर्मध्ये Context::set_fuel आह्वयितुं शक्नोति; Context::fuel वर्तमान-सीमां दर्शयति, वैश्विक-मूल्यानि न नाशयति।</p>
<p>`cargo run --example embed` लघुं Rust-आश्रयं संयोजयति, वैश्विकं स्थापयति, native-callback योजयति, QuickCoffee च चालयति।</p>
<p>Host `Value::kind()` द्वारा प्रकारं विभजति, `Value::is_nil()` द्वारा nil परीक्षते, आन्तरिक-container न पश्यति।</p>
<p>Cargo-वस्तु-विवरणानि अन्तःस्थापकान् repository, docs.rs-API, README, licence च प्रति नयन्ति।</p>
<p>Context::last_execution() instruction-संख्या तथा अवशिष्ट-fuel दर्शयति, VM-frame न प्रकाशयति।</p>
<p>-- पश्चात् argumentाः साधारण-string-array argv रूपेण दीयन्ते।</p>
Expand Down
2 changes: 1 addition & 1 deletion docs/manual.devanagari.sa.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,4 @@ host-त्रुटिः संरचिता: `error.kind()` `ErrorKind::Par

एकस्यां 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 द्वारा पुनःप्रयुक्त-सन्दर्भस्य सीमा परिवर्तयितुं शक्नोति, तथा `cargo run --example embed` पूर्णं host-उदाहरणं चालयति।
`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-उदाहरणं चालयति।
Loading