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
14 changes: 14 additions & 0 deletions PERFORMANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ for character, index in 'a☕中x' by 2 then sum += index
sum
```

signed-by-iteration(20,000 次):

```coffee
sum = 0
for n, index in [1...100] by -3 then sum += n + index
sum
```

该负载验证 RFC 0100 的反向数组步进与实际下标绑定;期望值为 `3333`。它与正向 `stepped-iteration` 负载并列,避免性能报告只覆盖顺序遍历。

for-collection(10,000 次):

```coffee
Expand Down Expand Up @@ -529,6 +539,10 @@ rest 绑定会复制剩余元素到新的不可变数组,以保持宿主存储

负索引在数组上做一次长度归一化,在字符串上按 Unicode 标量计数后定位;两者均保持越界错误,不复制序列。`negative-indexing` workload(20,000 次)单次样本为:编译 75.609 ms,验证 2.750 ms,执行 33.463 ms。

## RFC 0100 有符号步长

`qbench --json` 现在包含 `signed-by-iteration`,并在每轮编译、验证和执行后检查 `3333`。该负载不把数组复制到宿主侧,反向位置由 VM 的有符号步长直接推进;运行基准时应与 `stepped-iteration` 一起比较编译、验证和执行三个阶段。

## RFC 0074 映射展开

在同一 Darwin arm64 开发机上,`cargo bench --bench core` 的 `map-spread` workload(20,000 次)单次样本为:编译 91.995 ms,验证 2.767 ms,执行 48.784 ms。映射展开为每个显式项生成单项映射,再由 `MergeMaps` 合并;后续键覆盖前值。
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/0099-bang-not-alias.md](RFCs/0099-bang-not-alias.md)。
当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0100-signed-by-iteration.md](RFCs/0100-signed-by-iteration.md)。

```coffee
square = (x) -> x * x
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 0099;其中 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 延伸至 RFC 0100;其中 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` 步长,均不改变脚本语言值模型的原型无关约束。
4 changes: 2 additions & 2 deletions RFCs/0001-language-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ UTF-8 文本,以换行或分号分隔语句。缩进可形成 `if`、`unless`

纯字面量常量折叠见 RFC 0060;折叠不改变严格运行时错误边界。多行数组与映射可按 RFC 0063 省略逗号,但调用参数仍须显式分隔;RFC 0064 另定义缩进式映射字面量;RFC 0065 增加受约束的同一逻辑行隐式调用。

`name = expression` 绑定或更新当前词法环境;顶层执行时该环境即全局环境。名称也支持严格数值前置/后置 `++`、`--`(RFC 0055)。函数调用建立子环境,因此函数内新赋值不泄漏到全局。数组与映射支持严格解构赋值;`_` 是显式忽略位。`if condition then expression else expression` 是表达式,`else` 可省略并产生 `nil`;`unless` 是条件取反的同义结构。后置 `expression if condition` 与 `expression unless condition` 在条件不满足时产生 `nil`。后缀 `value?` 仅检查是否非 nil,保持 Bool 值且不检查未绑定名称(RFC 0038)。`while condition then expression` 重复求值,`until condition then expression` 则重复至条件为真,语句位置的 `expression while condition` / `until condition` 是其后置形式(RFC 0036),`loop body` 则无限重复,三者结果均为 `nil`。数组可用严格切片 `items[start..end]`(含末端)或 `items[start...end]`(不含末端),负端点自末尾计,且端点必须为界内整数(RFC 0037)。数组或字符串可用 `for pattern in iterable [by step] [when condition] then expression` 遍历;字符串按 Unicode 标量产生单字符字符串,第二绑定为标量下标(RFC 0070)。映射可用 `for own key_pattern, value_pattern of map [when condition] then expression` 遍历;表达式也可写成后置推导 `expression for pattern in iterable` 或 `expression for own key_pattern, value_pattern of map`(RFC 0054)。绑定模式严格递归且每轮原子写入(RFC 0044),`by` 只用于数组且步长为一次求值的正整数(RFC 0029),`for` 收集每次体值为新数组,`when` 拒绝的项不收集,`break` 产生已收集前缀,`continue` 不收集当前项(RFC 0042)。`switch`/`when` 选择单一分支;`try`/`catch`/`finally` 与 `throw` 处理 QuickCoffee 运行时错误。`return expression` 仅在函数内结束当前调用并返回其值,裸 return 返回 `nil`;它清理循环并执行沿途 finally(RFC 0028)。
`name = expression` 绑定或更新当前词法环境;顶层执行时该环境即全局环境。名称也支持严格数值前置/后置 `++`、`--`(RFC 0055)。函数调用建立子环境,因此函数内新赋值不泄漏到全局。数组与映射支持严格解构赋值;`_` 是显式忽略位。`if condition then expression else expression` 是表达式,`else` 可省略并产生 `nil`;`unless` 是条件取反的同义结构。后置 `expression if condition` 与 `expression unless condition` 在条件不满足时产生 `nil`。后缀 `value?` 仅检查是否非 nil,保持 Bool 值且不检查未绑定名称(RFC 0038)。`while condition then expression` 重复求值,`until condition then expression` 则重复至条件为真,语句位置的 `expression while condition` / `until condition` 是其后置形式(RFC 0036),`loop body` 则无限重复,三者结果均为 `nil`。数组可用严格切片 `items[start..end]`(含末端)或 `items[start...end]`(不含末端),负端点自末尾计,且端点必须为界内整数(RFC 0037)。数组或字符串可用 `for pattern in iterable [by step] [when condition] then expression` 遍历;字符串按 Unicode 标量产生单字符字符串,第二绑定为标量下标(RFC 0070)。映射可用 `for own key_pattern, value_pattern of map [when condition] then expression` 遍历;表达式也可写成后置推导 `expression for pattern in iterable` 或 `expression for own key_pattern, value_pattern of map`(RFC 0054)。绑定模式严格递归且每轮原子写入(RFC 0044),`by` 只用于数组或字符串,步长一次求值且必须为非零有限整数(RFC 0029、0100),负步长从末项开始;`for` 收集每次体值为新数组,`when` 拒绝的项不收集,`break` 产生已收集前缀,`continue` 不收集当前项(RFC 0042)。`switch`/`when` 选择单一分支;`try`/`catch`/`finally` 与 `throw` 处理 QuickCoffee 运行时错误。`return expression` 仅在函数内结束当前调用并返回其值,裸 return 返回 `nil`;它清理循环并执行沿途 finally(RFC 0028)。

函数为 `(a, b) -> expression` 或无括号普通名称形式 `a, b -> expression`,以创建时的词法环境捕获自由变量;无括号形式不接受默认、rest 或解构参数(RFC 0035)。形参可写为 `name = expression` 取默认值,且默认形参必须在必选形参之后。缺省或传入 `nil` 时,默认式在被调函数内求值;最后一个形参可写成 `rest...` 以接收剩余实参数组。`=>` 作为无 `this` 运行时中的可读同义箭头;两者均不提供 JavaScript 的绑定接收者语义。`do function` 以零实参立即调用函数。没有 rest 时实参不得多于全部形参,也不得少于必选形参。递归函数通过函数自身绑定可用。默认参数细则见 RFC 0021;`for` 见 RFC 0004;无原型工厂类见 RFC 0006;缩进块见 RFC 0009。

## 标准库

RFC 0095 扩展字符串 `for` 也可使用正整数 `by` 步长;RFC 0097 规定 `do (name) -> ...` 从同名外层变量转发立即调用实参。
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`。

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0008-array-destructuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

`name, name, ... = expression` 要求右侧为数组且元素个数与名称严格相同;RFC 0071 允许数组模式最后一项写作 `tail...` 以绑定剩余数组。`_` 是显式忽略位置;其余名称在当前全局环境绑定。整个赋值表达式仍产生原数组。RFC 0023 进一步允许数组项为递归数组或映射模式。

编译器发出 `Destructure(Pattern)`,VM 在写入任一名称前检查容器种类、长度和 rest 位置,故失败不会留下部分绑定。模式默认值、映射 rest/splat 与函数参数解构中的 computed/string 键仍不支持
编译器发出 `Destructure(Pattern)`,VM 在写入任一名称前检查容器种类、长度和 rest 位置,故失败不会留下部分绑定。模式默认值见 RFC 0073,数组 rest 见 RFC 0071;映射 rest 与字面字符串键分别见 RFC 0075、RFC 0090
2 changes: 1 addition & 1 deletion RFCs/0012-map-destructuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

`{key, source: target} = expression` 要求右侧为映射并包含每个列出的键。未写冒号时键名同时为绑定名;`_` 可作为目标名明确忽略该值。

VM 在写入环境前读取并验证全部键,因此任何缺键或非映射错误都不会留下部分绑定。RFC 0023 允许映射值继续使用递归数组或映射模式;默认值、computed key 与 rest/splat 仍不支持。
VM 在写入环境前读取并验证全部键,因此任何缺键或非映射错误都不会留下部分绑定。RFC 0023 允许映射值继续使用递归数组或映射模式;模式默认值、映射 rest 与字面字符串键分别见 RFC 0073、0075、0090,computed key 仍不支持。
2 changes: 1 addition & 1 deletion RFCs/0023-nested-destructuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

赋值左侧可递归组合数组和映射模式:`[first, [middle, last]] = value`、`{point: {x, y}, labels: [_, name]} = value`。原有的 `a, b = value` 是顶层数组模式的简写;`{key, source: target} = value` 仍支持键名与绑定名不同。`_` 在任何深度均显式忽略对应值。

数组模式在每一层都要求数组和值数量精确相同;RFC 0071 为最后一项增加 `name...` rest,允许值数量不少于固定项。映射模式在每一层都要求映射包含所列的标识符键,但允许附加键。模式叶子只能是绑定名或 `_`。模式默认值、映射 rest/splat、computed/string 键与函数参数解构仍不在本 RFC 范围
数组模式在每一层都要求数组和值数量精确相同;RFC 0071 为最后一项增加 `name...` rest,允许值数量不少于固定项。映射模式在每一层都要求映射包含所列的标识符键,但允许附加键。模式叶子只能是绑定名或 `_`。模式默认值、映射 rest 与字面字符串键分别由 RFC 0073、0075、0090 扩展;computed key 仍不支持

## 字节码与原子性

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0026-destructuring-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

函数与无原型工厂类的普通形参可使用 RFC 0023 的数组/映射模式:`([left, right], {factor}) -> expression`、`class Point([x, y]) -> expression`;数组模式最后一项可按 RFC 0071 写作 `tail...`。调用时每个实参与对应模式严格匹配;数组长度不足、映射缺键或容器类型不符会使调用失败。

形参模式的绑定只写入新调用帧的词法环境,永不修改调用者。`_` 可在任何深度忽略值。默认值仅可用于命名形参,如 `factor = 2`;普通参数 rest 仍必须为最终的单个名称 `tail...`,数组模式 rest 见 RFC 0071。模式默认、映射 rest 与函数参数解构中的 computed/string 键不支持
形参模式的绑定只写入新调用帧的词法环境,永不修改调用者。`_` 可在任何深度忽略值。默认值仅可用于命名形参,如 `factor = 2`;普通参数 rest 仍必须为最终的单个名称 `tail...`,数组模式 rest 见 RFC 0071。模式默认与映射 rest 分别见 RFC 0073、0075;函数参数解构中的 computed key 仍不支持,字面字符串键见 RFC 0090

## 字节码与 API

Expand Down
8 changes: 4 additions & 4 deletions RFCs/0029-stepped-array-iteration.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@

## 语法与语义

数组循环可在可迭代表达式后、可选 `when` 前写 `by step`:`for item in items by 2 then body`。`step` 在建立迭代器时恰好求值一次,必须产生正的有限整数;否则为运行时错误。省略时步长为 `1`。该特性适用于数组及 `range` 结果;映射循环 `for own key, value of map` 不接受 `by`,是解析错误。
数组循环可在可迭代表达式后、可选 `when` 前写 `by step`:`for item in items by 2 then body`。`step` 在建立迭代器时恰好求值一次,必须产生非零的有限整数;正步长从首项前进,负步长从末项倒退(RFC 0100)。省略时步长为 `1`。该特性适用于数组及 `range` 结果;映射循环 `for own key, value of map` 不接受 `by`,是解析错误。

步长按数组下标前进:`[1..9] by 3` 依次绑定 `1`、`4`、`7`。过滤、`break`、`continue`、函数 return、fuel 与嵌套循环沿用 RFC 0004 和 RFC 0028 的规则。
步长按数组下标前进或后退:`[1..9] by 3` 依次绑定 `1`、`4`、`7`,`[1..9] by -3` 依次绑定 `9`、`6`、`3`。过滤、`break`、`continue`、函数 return、fuel 与嵌套循环沿用 RFC 0004 和 RFC 0028 的规则。

## 字节码与验证

`IterStartEnumerable` 消耗栈顶的步长和其下的数组或字符串,建立帧私有迭代器;数组按步长前进,字符串仅接受步长 1(字符串语义详见 RFC 0070)。`IterStartMap` 仍只消耗映射。验证器相应要求 enumerable 开始指令有两个栈值,并继续追踪迭代器在 `IterNext`、`IterEnd` 与 `Return` 的平衡。VM 以饱和加法更新数组位置,防止极大合法步长造成整数回绕。
`IterStartEnumerable` 消耗栈顶的步长和其下的数组或字符串,建立帧私有迭代器;数组和字符串按有符号步长前进或后退。`IterStartMap` 仍只消耗映射。验证器相应要求 enumerable 开始指令有两个栈值,并继续追踪迭代器在 `IterNext`、`IterEnd` 与 `Return` 的平衡。VM 以饱和加法或检查减法更新位置,防止极大合法步长造成整数回绕。

## 验收

测试覆盖普通步长、一次求值、零/小数/非数字拒绝、映射拒绝和已编译 chunk 验证。
测试覆盖正负步长、一次求值、零/小数/非数字拒绝、映射拒绝和已编译 chunk 验证。
4 changes: 2 additions & 2 deletions RFCs/0070-string-iteration.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ for character, index in 'a☕中' then index

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

字符串迭代也接受 `by step`。步长只求值一次,必须是正有限整数;它跳过 Unicode 标量而不是 UTF-8 字节,第二绑定仍是实际的标量下标:
字符串迭代也接受 `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 影响。
由于迭代对象可在运行时求值,`for value in dynamic by step` 在运行时按实际数组或字符串类型采用同一非零有限有符号步长。映射仍使用 `of`,不受本 RFC 影响。

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

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0073-pattern-defaults.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RFC 0073:解构模式默认值

状态:实现中
状态:已采纳

数组与映射解构模式成员可写为 `pattern = expression`。输入成员缺失或为 `nil` 时,在当前词法环境中动态求值表达式;默认值可引用此前已绑定成员。多余数组成员、错误容器类型和无默认值的缺失成员均报错,且整个匹配原子回滚。

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0074-map-spread.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RFC 0074:映射字面量展开

状态:实现中
状态:已采纳

映射字面量支持 `...expression` 展开:表达式必须求值为映射,展开项按从左到右合并,后续显式键或展开项覆盖此前同名键。映射保持原型链无关的不可变值语义;非映射展开在运行时报告错误。

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0075-map-pattern-rest.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RFC 0075:映射解构尾部模式

状态:实现中
状态:已采纳

映射解构可在末尾使用 `...name` 捕获未列出的键:`{id, ...metadata} = record`。显式字段先按普通严格模式匹配;其余键组成新的不可变映射绑定到 `name`。尾部模式必须命名且只能出现一次、位于映射模式末尾;输入必须是映射,缺失的显式字段仍报错并保持原子性。

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0076-negative-indexing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RFC 0076:序列负索引

状态:实现中
状态:已采纳

数组与字符串索引接受有限整数负值;`-1` 表示最后一个元素或 Unicode 标量,`-2` 表示倒数第二个。超出范围仍是运行时错误,不做隐式截断。字符串按 Unicode 标量而非 UTF-8 字节定位,保持 RFC 0072 的边界语义。

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0077-qtest-json-output.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RFC 0077:`qtest` JSON 输出

状态:实现中
状态:已采纳

`qtest --json FILE_OR_DIRECTORY...` 为每个测试文件输出一行 JSON:成功记录含 `ok: true` 与 `file`,失败记录含 `ok: false`、`file` 与经过 JSON 转义的 `error`。退出码仍以所有测试是否通过为准;`--stats` 继续把执行统计写到标准错误,不污染机器可读标准输出。

Expand Down
2 changes: 1 addition & 1 deletion RFCs/0078-bytecode-fingerprint.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RFC 0078:字节码内容指纹

状态:实现中
状态:已采纳

`Chunk::fingerprint()` 与 `Program::fingerprint()` 返回由已编译字节码文本表示计算的确定性 FNV-1a `u64` 指纹。相同内容的共享程序和克隆句柄指纹相同,不同字节码内容应产生不同指纹(不把指纹当作密码学哈希)。

Expand Down
Loading