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
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,15 @@ permissions:
jobs:
check:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
toolchain: ["1.85.0", stable]
steps:
- uses: actions/checkout@v5
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.toolchain }}
components: rustfmt, clippy
- run: make docs && make check
- run: git diff --exit-code -- docs
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "quickcoffee"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
description = "A compact CoffeeScript-inspired bytecode engine"
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand Down
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
.PHONY: fmt test examples package-metadata package qbench-check clippy api-doc docs doc-check check bench qbench
.PHONY: fmt test release-test examples package-metadata package qbench-check clippy api-doc docs doc-check check bench qbench

fmt:
cargo fmt --check

test:
cargo build --locked --bins
cargo test --locked

release-test:
cargo test --locked --release

examples:
cargo test --locked --examples

Expand Down Expand Up @@ -35,7 +39,7 @@ docs: doc-check
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

check: fmt test examples package-metadata package qbench-check clippy api-doc doc-check
check: fmt test release-test examples package-metadata package qbench-check clippy api-doc doc-check

bench:
cargo bench --locked --bench core
Expand Down
2 changes: 2 additions & 0 deletions PERFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ make qbench
cargo run --locked --release --bin qbench -- --json --iterations 100 --repeat 3
```

调试单一回归时,先用 `qbench --list` 查看确定性的内建负载名,再用 `qbench --only NAME` 只运行该负载;不指定 `--only` 始终运行完整集合,持续门禁口径不变。

`qbench` 为每个内建负载输出一行 JSON,分别记录编译、验证和执行的纳秒总耗时,并在计时循环中校验预期最终值。默认 `--repeat 1` 适合快速 CI 回归;需要正式三次 release 中位数时使用 `--repeat 3`,其结果可直接作为下文报告数据。

测量环境:Apple arm64(Darwin 25.5.0,T6000)、`rustc 1.94.0 (4a4ef493e 2026-03-02)`、`cargo bench --bench core`。基准以 release profile 运行;报告日期为 2026-08-22。
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

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

当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0109-qbench-core-suite.md](RFCs/0109-qbench-core-suite.md)。
当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0113-numeric-standard-library.md](RFCs/0113-numeric-standard-library.md)。
构建要求 Rust 1.85 或更新版本(Edition 2024);CI 同时验证 MSRV 与 stable 工具链。

```coffee
square = (x) -> x * x
Expand All @@ -27,6 +28,8 @@ cargo run -- --dump-bytecode example.qc
cargo run -- --fingerprint example.qc
cargo run --release --bin qbench -- --json --iterations 100
cargo run --release --bin qbench -- --json --iterations 100 --repeat 3
cargo run --release --bin qbench -- --list
cargo run --release --bin qbench -- --only map-spread --json --iterations 100
cargo run --locked --quiet --release --bin qbench -- --json --iterations 1 --repeat 3
cargo run --example embed
cargo run --bin qdocco -- example.qc -o example.html
Expand All @@ -43,7 +46,7 @@ cargo run --bin qbench -- --version

## 验收

`make check` 运行格式检查、全部测试(含外部嵌入 API 集成测试和 1,024 条确定性编译压力语料)、零警告 Clippy 和五份可执行手册校验;`make docs` 从文学编程源重新生成 HTML;`make bench` 运行 release 基准。项目禁止 `unsafe`。
`make check` 运行格式检查、debug 与 release 两套全部测试(含外部嵌入 API 集成测试和 1,024 条确定性编译压力语料)、零警告 Clippy 和五份可执行手册校验;`make docs` 从文学编程源重新生成 HTML;`make bench` 运行 release 基准。项目禁止 `unsafe`。

手册源在 `manuals/`,每份都是可执行的 Docco 输入。生成 HTML:

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0000-project-scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,4 @@ QuickCoffee 是一个 Rust 实现的、受 CoffeeScript 2016 启发的脚本引

本仓库中的测试即 0.1 的语义基线。对语法或运行时的新增特性必须先以 RFC 补充定义,并至少添加:成功测试、错误测试及字节码验证测试。

当前已实现的后续语义与工具 RFC 延伸至 RFC 0109;其中 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 延伸至 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 定义严格数值标准库函数,均不改变脚本语言值模型的原型无关约束。
2 changes: 1 addition & 1 deletion RFCs/0001-language-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ UTF-8 文本,以换行或分号分隔语句。缩进可形成 `if`、`unless`

RFC 0095 扩展字符串 `for` 也可使用 `by` 步长;RFC 0100 规定其支持非零有限有符号整数并以负步长倒序;RFC 0097 规定 `do (name) -> ...` 从同名外层变量转发立即调用实参。

预置函数是普通名称,不是对象原型方法:`print(value...)`、`len(value)`、`type(value)`、`range(start, end)`、`str(value)`、`keys(map)`、`values(map)`、`join(array, separator)`、`split(string, separator)`、`assert(bool, message?)`。它们的行为由 RFC 0003 的宿主接口定义。不存在 `console`、`Object.prototype`、`Array.prototype`。
预置函数是普通名称,不是对象原型方法:`print(value...)`、`len(value)`、`type(value)`、`range(start, end)`、`str(value)`、`abs(number)`、`sum(array)`、`min(array)`、`max(array)`、`keys(map)`、`values(map)`、`join(array, separator)`、`split(string, separator)`、`assert(bool, message?)`。数值聚合只接受有限数;空数组的 `sum` 为 `0`,`min`/`max` 要求非空。它们的行为由 RFC 0003 与 RFC 0113 的宿主接口定义。不存在 `console`、`Object.prototype`、`Array.prototype`。

### 后续契约修订

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0108-qtest-showcase-corpus.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
3. 使用已经采纳的 QuickCoffee 语法,覆盖至少一个可观察的核心语义;
4. 可由 `qtest tests/scripts` 递归发现并以默认 fuel 完成。

示例语料至少覆盖数值与闭包、map spread/解构、Unicode 字符串索引、筛选 comprehension,以及循环控制。语料只验证语义结果,不比较实现细节或运行时间;性能门禁仍由 RFC 0045、RFC 0081、RFC 0096 与 RFC 0107 负责。
示例语料至少覆盖数值与闭包、map spread/解构、Unicode 字符串索引、筛选 comprehension、循环控制,以及无原型标准库的组合调用。标准库样例必须覆盖 `range`、`len`、`type`、`str`、`keys`、`values`、`join`、`split` 与成功的 `assert`。语料只验证语义结果,不比较实现细节或运行时间;性能门禁仍由 RFC 0045、RFC 0081、RFC 0096 与 RFC 0107 负责。

## 验收

Expand Down
14 changes: 14 additions & 0 deletions RFCs/0110-rust-msrv-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# RFC 0110:Rust 最低版本契约

- 状态:已采纳
- 依赖:RFC 0000、RFC 0086、RFC 0087

## 契约

QuickCoffee 使用 Edition 2024,并将 Rust `1.85.0` 声明为 crate 的最低支持版本(MSRV)。`Cargo.toml` 的 `rust-version` 字段是发布元数据的一部分;新增依赖或语言特性不得无意中提高该版本。

## 验收

CI 必须对 Rust `1.85.0` 和当前 stable 各运行一次 `make docs && make check`。两套工具链都必须通过格式、全部测试、示例、crate 打包、release qbench、Clippy、rustdoc 及五份手册门禁。集成测试还必须确认 manifest 暴露 `rust-version = "1.85"`,避免文档与发布元数据分离。

MSRV 是编译器兼容性下限,不承诺旧平台的运行时性能;性能数据仍按 RFC 0096 与 RFC 0109 的 release 基准口径解释。
14 changes: 14 additions & 0 deletions RFCs/0111-release-test-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# RFC 0111:release profile 完整测试门禁

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

## 约束

`make check` 必须在 debug 与 release 两种 profile 执行完整 `cargo test --locked`。release 测试覆盖库单元测试、所有 CLI 集成测试、公开嵌入 API、五份文学手册、RFC 索引和鲁棒性语料;它不能只依赖 qbench 的少量计时负载。`cargo test --locked --examples` 仍以 debug profile 单独检查示例编译。

release qbench 继续负责优化 VM 的全套 34 个语义负载与 compile/verify/execute 计时;本 RFC 的目标是让非 benchmark 的 CLI、嵌入和错误路径也经过优化构建验证。门禁不设置机器相关的时间阈值。

## 验收

`make check` 必须包含 `cargo test --locked --release`,并在 Rust 1.85 与 stable 工具链上均成功。任何 profile 的测试失败都阻止 PR 进入 `CLEAN` 状态。
19 changes: 19 additions & 0 deletions RFCs/0112-qbench-workload-selection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# RFC 0112:qbench 负载枚举与选择

- 状态:已采纳
- 依赖:RFC 0103、RFC 0105、RFC 0109、RFC 0111

## 动机

`qbench` 默认运行完整核心负载集,以便持续门禁;调试单个回归时,重复等待全部负载会降低反馈速度。CLI 需要提供不改变默认行为的确定性枚举与单负载选择。

## 契约

1. `qbench --list` 按内建顺序逐行输出每个负载名,不输出计时记录或诊断。
2. `qbench --only NAME` 只运行精确匹配的一个负载;JSON 与文本输出格式、语义护栏、计时字段和 schema 不变。
3. 未知名称必须以退出码 2 失败,并提示使用 `--list`;`--list` 与 `--only` 同时出现也以退出码 2 失败。
4. 不指定 `--only` 时,默认完整负载集合与 RFC 0109 相同。

## 验收

`tests/cli_tools.rs` 必须验证完整枚举、单负载 JSON、未知名称和冲突参数;测试门禁先构建当前 CLI 二进制,避免 MSRV 环境回退到过期 target 产物;`make check` 继续执行完整 release qbench 门禁。
20 changes: 20 additions & 0 deletions RFCs/0113-numeric-standard-library.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# RFC 0113:严格数值标准库函数

- 状态:已采纳
- 依赖:RFC 0001、RFC 0043、RFC 0108、RFC 0111

## 动机

QuickCoffee 的标准库是普通函数,而非 JavaScript 原型方法。已有容器与字符串函数足以演示数据流,但数值脚本仍需宿主自行注册最常见的绝对值和聚合操作。内建这些函数可让 qtest 语料、嵌入宿主和性能负载共享同一严格语义。

## 契约

- `abs(number)` 接受恰好一个有限数,返回其绝对值。
- `sum(array)` 接受恰好一个数组;数组元素必须都是有限数,空数组和为 `0`。
- `min(array)` 与 `max(array)` 接受恰好一个非空数组;元素必须都是有限数,分别返回最小值或最大值。
- 参数数量、容器类型、元素类型或有限性不满足时,函数返回结构化运行时错误;不发生隐式转换,也不读取原型或成员方法。
- 返回值仍是普通 `Number`,不改变字节码验证、fuel 或宿主注册函数规则。

## 验收

`tests/scripts/stdlib.qc` 验证成功路径;核心测试验证空数组、错误参数和非有限宿主数值的错误边界;五份文学手册列出函数。`make check` 必须继续通过 debug/release 全量测试及 qdocco 门禁。
2 changes: 1 addition & 1 deletion docs/manual.classical-zh.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
<p>Cargo 包志指仓、docs.rs API、README 与许可证,使嵌者易寻其用。</p>
<p>Context::last_execution() 示所试指令与余燃料,而不露 VM 之帧。</p>
<p>-- 后之参,以常字符串数组 argv 见于文中。</p>
<p>其内府皆常函,如 print、len、type、range、str、keys、values、join、split、assert。</p>
<p>其内府皆常函,如 print、len、type、range、str、abs、sum、min、max、keys、values、join、split、assert;数聚函唯受有限数之列。</p>
<p>函式取词法之境;末常参可书 y = 2,参缺或传 nil 则于函中取其值;末有余参,则书 tail...。</p>
<p>常名之参可省其括:left, right -&gt; left + right;常值、余参与解构仍须括之。</p>
<p>return expression 惟函中可用,反其值而终此函;徒 return 得 nil,且清环行 finally。</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 @@ -14,7 +14,7 @@ QuickCoffee 者,Rust 所为字节码机也,非 JavaScript 之运行时。其

严等与数之比较,相连可书 `1 < middle() < 3`;中项惟求一遍,前较既否,后项不求。

欲试之,曰:`qcoffee -e "print(range(1, 4))"`。`--check FILE` 者,析、编、验其文而不行;`--fuel N` 者,限所行指令之数也;数尽则止而报误。内府有 `print`、`len`、`type`、`range`、`str`、`keys`、`values`、`join`、`split`、`assert`;`range(a, b)` 取自 a 至 b 前。
欲试之,曰:`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` 则书所试指令与余燃料于标准错误,程序之标准输出不改;每次惟一源码输入,执行模式相冲则报用法之误。
Expand Down
2 changes: 1 addition & 1 deletion docs/manual.devanagari-sa.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<p>yes/on true, no/off false; is/isnt strict-साम्यम् स्तः।</p>
<p>! strict-Bool not-पर्यायः अस्ति; != strict-असाम्यमेव तिष्ठति।</p>
<p>chained-comparison मध्ये मध्य-मूल्य एकवारं, पूर्व-false चेत् short-circuit भवति।</p>
<p>सामान्य-library साधारण-function रूपेण print, len, type, range, str, keys, values, join, split, assert ददाति।</p>
<p>सामान्य-library साधारण-function रूपेण print, len, type, range, str, abs, sum, min, max, keys, values, join, split, assert ददाति; संख्या-संग्रहाः केवलं finite-संख्याः गृह्णन्ति।</p>
<p>कार्यं lexical-environment गृह्णाति; y = 2 omitting अथवा nil दत्ते कार्यस्य अन्तरे default भवति; अन्तिमः rest-parameter tail... इति लिख्यते।</p>
<p>bare-name lambda left, right -&gt; left + right भवति; default, rest, pattern तु parentheses गृह्णन्ति।</p>
<p>return expression केवलं वर्तमान-कार्यं समाप्तं करोति; केवलः return nil फलति, loop शुद्धीकरोति, finally च चलयति।</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 @@ -14,7 +14,7 @@ CoffeeScript-नामानि type न परिवर्तयन्ति: `

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`, `keys`, `values`, `join`, `split`, `assert` मानक-सहायकाः; `range(a, b)` मध्ये `b` न गृह्यते।
`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-दोषं जनयति।
Expand Down
2 changes: 1 addition & 1 deletion docs/manual.en.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
<p>yes/on and no/off are Boolean aliases; is/isnt preserve strict equality.</p>
<p>! is a strict Bool alias for not; != remains strict inequality.</p>
<p>Chained strict or numeric comparisons keep the middle value once and short-circuit.</p>
<p>The standard library is ordinary functions: print, len, type, range, str, keys, values, join, split, and assert.</p>
<p>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.</p>
<p>switch/when selects one strict-equality branch without fallthrough.</p>
<p>try/catch/finally handles QuickCoffee runtime errors without JavaScript Error objects.</p>
<p>Integer ranges use [1..3] for an inclusive end and [1...3] for an exclusive end.</p>
Expand Down
Loading