diff --git a/README.md b/README.md index 4757c65..1fbbe22 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/0087-reproducible-locked-builds.md](RFCs/0087-reproducible-locked-builds.md)。 +当前实现遵循 [RFCs/0000-project-scope.md](RFCs/0000-project-scope.md) 至 [RFCs/0091-descending-ranges.md](RFCs/0091-descending-ranges.md)。 ```coffee square = (x) -> x * x diff --git a/RFCs/0014-range-literals.md b/RFCs/0014-range-literals.md index 15ac773..1dbed8e 100644 --- a/RFCs/0014-range-literals.md +++ b/RFCs/0014-range-literals.md @@ -3,7 +3,7 @@ - 状态:已采纳 - 依赖:RFC 0001、RFC 0002 -`[start..end]` 生成包含 `end` 的整数数组;`[start...end]` 生成不包含 `end` 的整数数组。两端都是先求值的表达式,且必须为有限整数。若起点不小于终点,结果是空数组;包含式的 `i64` 最大终点会报告运行时错误,避免溢出。单个区间至多生成 1,000,000 项;超限为运行时错误,以免在 fuel 检查前以一次分配耗尽宿主内存。 +`[start..end]` 生成包含 `end` 的整数数组;`[start...end]` 生成不包含 `end` 的整数数组。两端都是先求值的表达式,且必须为有限整数。方向、降序和边界保护由 RFC 0091 定义;单个区间至多生成 1,000,000 项,超限为运行时错误,以免在 fuel 检查前以一次分配耗尽宿主内存。 编译器直接发出 `MakeRange(inclusive)` 指令,而不是编译成可被用户同名绑定覆盖的 `range(start, end)` 调用。VM 以现有的数组值表示结果;因此可直接索引或供 `for` 使用。`...` 在此上下文表示排除上界,在函数参数末位才表示 rest/splat;其含义由解析上下文决定。 diff --git a/RFCs/0090-string-map-pattern-keys.md b/RFCs/0090-string-map-pattern-keys.md new file mode 100644 index 0000000..3d418ee --- /dev/null +++ b/RFCs/0090-string-map-pattern-keys.md @@ -0,0 +1,28 @@ +# RFC 0090:映射解构的字面字符串键 + +- 状态:已采纳 +- 依赖:RFC 0012、RFC 0023、RFC 0075 + +## 动机 + +QuickCoffee 的映射字面量允许任意字符串键,但映射解构过去只接受标识符键。这样无法 +解构外部数据常见的 `first-name`、`content-type` 等键,也使“映射键是字符串”与“映射 +解构键必须是标识符”产生不必要的不对称。 + +## 契约 + +映射模式的键可以是标识符或已解码的非插值字符串字面量: + +```coffee +{"first-name": first} = {"first-name": "Ada"} +``` + +字符串键按字面值查找,不执行插值、名称读取或其他表达式;动态 computed key 仍不支持。 +重命名、默认值、递归模式、末尾 map rest 和原子匹配规则保持 RFC 0012、0023、0075 不变。 +非法缺键、类型错误和模式失败仍不会留下部分绑定。 + +## 验收 + +`tests/rfc_core.rs` 覆盖连字符字符串键、单引号键、缺键错误和成功绑定;双语语法索引必须 +区分字面字符串键与未支持的 computed key。现有验证器与 VM 指令无需新增,`make check` 仍 +执行完整回归与五份文学手册。 diff --git a/RFCs/0091-descending-ranges.md b/RFCs/0091-descending-ranges.md new file mode 100644 index 0000000..a20e459 --- /dev/null +++ b/RFCs/0091-descending-ranges.md @@ -0,0 +1,28 @@ +# RFC 0091:降序整数区间 + +- 状态:已采纳 +- 依赖:RFC 0014、RFC 0029、RFC 0042 + +## 动机 + +CoffeeScript 风格整数区间不应因终点小于起点而静默变成空数组。升序和降序都常用于倒序 +遍历;当前 `MakeRange`/`range` 实现只生成升序,导致 `[4..2]` 与 `range(4, 2)` 丢失用户 +明确表达的元素。 + +## 契约 + +区间方向由起点与终点决定: + +- `[2..4]` 为 `[2, 3, 4]`,`[2...4]` 为 `[2, 3]`; +- `[4..2]` 为 `[4, 3, 2]`,`[4...2]` 为 `[4, 3]`; +- 标准库 `range(a, b)` 使用同样的方向规则,并继续排除终点。 + +边界必须是有限整数;区间长度超过既有 `MAX_RANGE_ITEMS` 仍报运行时错误。生成过程使用 +整数方向和受保护计数,不依赖 `i64` 终点加一/减一,因此 `i64` 边界不会溢出。数组 `for` +与 `by` 继续按生成后数组位置迭代,映射循环和字符串 `by` 规则不变。 + +## 验收 + +`tests/rfc_core.rs` 覆盖升降序、开闭终点、标准库 `range` 和现有非整数/超长边界错误;五份 +文学手册与中英文语法索引说明降序示例。`make check`、`make docs` 和性能语义护栏必须继续 +通过。 diff --git a/docs/manual.classical-zh.html b/docs/manual.classical-zh.html index 818692f..06b9ff0 100644 --- a/docs/manual.classical-zh.html +++ b/docs/manual.classical-zh.html @@ -38,6 +38,7 @@
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,未名之误弗隐。
diff --git a/docs/manual.classical-zh.md b/docs/manual.classical-zh.md index d641213..aaee274 100644 --- a/docs/manual.classical-zh.md +++ b/docs/manual.classical-zh.md @@ -45,7 +45,7 @@ QuickCoffee 者,Rust 所为字节码机也,非 JavaScript 之运行时。其 同一收集之法,亦可后置作 `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]`。其界必为有限整数。 +整数之区间,`[1..3]` 及其终,得 `[1, 2, 3]`;`[1...3]` 则不及,得 `[1, 2]`;逆行亦然,`[3..1]` 得 `[3, 2, 1]`。其界必为有限整数。 后缀 `value?`,惟验其非 nil:`nil?` 为 false,而 `false?` 与 `0?` 皆 true;未名之误弗隐,亦非 `left ? right` 之回退。 diff --git a/docs/manual.devanagari-sa.html b/docs/manual.devanagari-sa.html index 7986822..3c15eb1 100644 --- a/docs/manual.devanagari-sa.html +++ b/docs/manual.devanagari-sa.html @@ -39,6 +39,7 @@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 न गोप्यते।
diff --git a/docs/manual.devanagari.sa.md b/docs/manual.devanagari.sa.md index f862253..907e3ae 100644 --- a/docs/manual.devanagari.sa.md +++ b/docs/manual.devanagari.sa.md @@ -47,7 +47,7 @@ nil-सुरक्षित suffix CoffeeScript-रीत्या `record?.nam स एव 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]` भवति। सीमा finite integer भवेत्। +पूर्णाङ्क-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 अपि न। diff --git a/docs/manual.en.html b/docs/manual.en.html index 4a0ab93..35d9a6b 100644 --- a/docs/manual.en.html +++ b/docs/manual.en.html @@ -33,6 +33,7 @@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.
diff --git a/docs/manual.en.md b/docs/manual.en.md index 0d3b941..d3fd164 100644 --- a/docs/manual.en.md +++ b/docs/manual.en.md @@ -44,7 +44,7 @@ Arrays (including `range` results) can be iterated with `for item in items then 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]`). Their bounds must be finite integers. +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`. diff --git a/docs/manual.latin.html b/docs/manual.latin.html index af75beb..8390191 100644 --- a/docs/manual.latin.html +++ b/docs/manual.latin.html @@ -39,6 +39,7 @@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.
diff --git a/docs/manual.latin.md b/docs/manual.latin.md index c01220a..87ad54e 100644 --- a/docs/manual.latin.md +++ b/docs/manual.latin.md @@ -47,7 +47,7 @@ Ordo scribitur `for item in items then expressio`; ligamen pattern strictum esse 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. Fines integri finiti esse debent. +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. diff --git a/docs/manual.zh-CN.html b/docs/manual.zh-CN.html index 7f0aff0..9a41176 100644 --- a/docs/manual.zh-CN.html +++ b/docs/manual.zh-CN.html @@ -40,6 +40,7 @@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 保留。
diff --git a/docs/manual.zh-CN.md b/docs/manual.zh-CN.md index 7543f25..e024aa5 100644 --- a/docs/manual.zh-CN.md +++ b/docs/manual.zh-CN.md @@ -60,7 +60,7 @@ nil 安全后缀采用 CoffeeScript 风格写法:`record?.name`、`values?[ind 同一收集器也支持 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]`。边界必须是有限整数。 +整数区间字面量由 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`。 diff --git a/docs/syntax.en.md b/docs/syntax.en.md index 95a870c..2864ab9 100644 --- a/docs/syntax.en.md +++ b/docs/syntax.en.md @@ -6,9 +6,11 @@ Cargo package metadata links embedding users to the repository, README, license, 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, and `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`. +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. + Array and Unicode string indexing accepts negative finite integers (`items[-1]`, `'a☕中'[-2]`); out-of-range indices remain errors. -Map destructuring may capture unlisted keys with a final named rest pattern (`{id, ...metadata} = record`); explicit fields remain strict and the captured map is immutable. +Map destructuring accepts identifier or literal string keys (`{"first-name": first} = record`) and may capture unlisted keys with a final named rest pattern (`{id, ...metadata} = record`); keys remain literal (computed expressions are not evaluated), explicit fields remain strict, and the captured map is immutable. Map literals support left-to-right spread (`config = {...defaults, theme: 'dark'}`); each spread must evaluate to a map and later entries override earlier keys. @@ -42,6 +44,6 @@ Maps may also use indentation after a standalone assignment: `record =` followed Supported: decimal, hexadecimal (`0xff`), binary (`0b1010`), octal (`0o755`), and scientific-notation numbers; strings (double quotes support `#{expression}` interpolation), booleans including CoffeeScript aliases (`yes`/`on`, `no`/`off`), `nil`, arrays including integer range literals (`[1..3]` inclusive, `[1...3]` exclusive) and splats (`[head, items...]`), maps including identifier shorthand (`{name}`), indexing and strict array/string slices (`items[start..end]` inclusive, `items[start...end]` exclusive), map-only member access (`record.name`), nil-safe member/index/slice/call suffixes (`record?.name`, `record?[key]`, `items?[start..end]`, `fn?(args)`), postfix non-nil tests (`value?`), name-only existential assignment (`name ?= value`), arithmetic/comparison/logical expressions including strict-equality aliases `is`/`isnt`, short-circuit chained comparisons (`a < b < c`), and nil-specific fallback (`left ? right`), array membership (`value in array` / `value not in array`) and own-map key membership (`key of map` / `key not of map`), `if`/`unless` (including postfix conditions), `while … then …`, `until … then …`, statement-position postfix `body while/until condition`, infinite `loop …`, filtered array `for pattern in xs [by step] when condition then …`, map `for own key_pattern, value_pattern of map then …`, `switch`/`when`, `try`/`catch`/`finally`, `throw`, function-only `return`, `break`, `continue`, strict recursive assignment and parameter patterns (`([a, b], {factor}) -> …`), bare-name `x, y -> …` closures, expression-bodied `->` closures including named trailing default parameters (`y = 2`), rest parameters (`tail...`), and splat calls (`f(items...)`), and zero-argument `do function` IIFEs. A `for` expression collects body values into a new array; rejected `when` values and `continue` are omitted, while `break` returns the accumulated prefix. Array and string slice bounds are finite in-range scalar integers; negative bounds count from the sequence end and no implicit clipping occurs. `value?` is false only for nil and does not suppress an unbound-name error; `?=` is the sole form that treats an unbound name as nil and short-circuits its right side otherwise. Chains retain their middle operand once and short-circuit; aliases preserve Bool and strict QuickCoffee equality with no coercion. A soak suffix short-circuits only a nil receiver; non-nil access remains strict. Omitted or `nil` named optional arguments use the default; required parameters must come first. Defaults, rest, and patterns require parenthesized lambda parameters. Array `by step` evaluates a positive finite integer once; map iteration excludes it. `loop` is `while true` and remains fuel-limited. `return expression` exits only its current function, while bare `return` yields `nil`; it cleans active loops and runs enclosing finalizers. Calls may omit parentheses on one logical line (`f a, b`); explicit `f(a, b)` remains required across layout boundaries and for ambiguous grouping. -Identifiers use Unicode XID rules: XID start or `_` first, XID continue or `_` after, with no normalization. `#` is a line comment; non-nesting `### … ###` block comments are removed before layout. Excluded: embedded JavaScript, prototypes, `this`, `new`, inheritance, modules, regexes, unparenthesized default/rest/destructuring parameter forms, generators, async, and member/index/destructuring `?=`. Space-only indentation blocks are supported for multi-statement bodies. `class Name(args) -> expression` is supported only as a prototype-free named factory. Conditions accept only booleans and names must be declared by assignment or the host. +Identifiers use Unicode XID rules: XID start or `_` first, XID continue or `_` after, with no normalization. `#` is a line comment; non-nesting `### … ###` block comments are removed before layout. Excluded: embedded JavaScript, prototypes, `this`, `new`, inheritance, modules, regexes, computed map-pattern keys, unparenthesized default/rest/destructuring parameter forms, generators, async, and member/index/destructuring `?=`. Space-only indentation blocks are supported for multi-statement bodies. `class Name(args) -> expression` is supported only as a prototype-free named factory. Conditions accept only booleans and names must be declared by assignment or the host. Implicit calls are supported on one logical line: `print value`, `add 20, 22`, `double add 20, 22`, and `len [1, 2, 3]`. Their arguments use ordinary expression precedence; explicit parentheses remain required for calls spanning layout boundaries. diff --git a/docs/syntax.zh-CN.md b/docs/syntax.zh-CN.md index 81d8f92..8d19062 100644 --- a/docs/syntax.zh-CN.md +++ b/docs/syntax.zh-CN.md @@ -11,9 +11,11 @@ | 字面量 | 十进制、十六进制 `0xff`、二进制 `0b1010`、八进制 `0o755` 与科学计数法数字、字符串、双引号 `#{expr}` 插值、保留换行的 `"""…"""` 插值 heredoc 与 `'''…'''` 字面 heredoc、`true`/`yes`/`on`、`false`/`no`/`off`、`nil`、数组与 `[head, items...]` 展开、整数区间 `[1..3]`(含上界)/`[1...3]`(不含上界)、映射、`{name}` 简写与映射展开 `{...base, key: value}` | 正则、JS 插值、`undefined` | | 运算 | 算术、严格有符号 32 位位运算 `&`、`|`、`^`、`~`、`<<`、`>>`、`>>>` 及其名称复合赋值、名称复合赋值 `name += value`、`-=`, `*=`, `/=`, `%=`, `**=`、名称前后置更新 `++`/`--`、比较(`==`/`is`、`!=`/`isnt`,可短路成链 `a < b < c`)、`and`/`or`、`not`、仅对 `nil` 回退的 `left ? right`、后缀非 nil 测试 `value?`、仅名称的存在性赋值 `name ?= value`、数组成员 `value in array` / `value not in array`、映射自身键 `key of map` / `key not of map`、数组索引与严格切片 `a[start..end]` / `a[start...end]`、映射成员访问、nil 安全后缀 `a?.name`、`a?[i]`、`a?[start..end]`、`f?(args)` | 成员/索引/解构复合赋值、成员/索引/解构 `?=`、字符串/映射切片、隐式截断、未声明名称检查 | | 控制 | `if`/`unless`、后置条件、`while … then …`、`until … then …`、语句后置 `body while/until condition`、前置或后置列表推导 `for value[, index] in xs [by step] [when condition] then …` / `value for value in xs`、`switch`/`when`、`try`/`catch`/`finally`、`throw`、函数内 `return`、`break`、`continue` | JS Error 对象、顶层 `return`、映射 `for` 的 `by` | -| 赋值/函数 | `a, b = array`、`[a, tail...] = array`(末项 rest 可为空)、`[a, {point: [b, c]}] = array`、`{key, from: to, ...metadata} = map` 的严格递归解构、`_` 忽略位;`x, y -> expression` 无括号名称闭包、`([a, b], {factor}) -> expression` 解构形参、`(x, y = 2, rest...) -> expression`(缺省或 `nil` 取默认)、`f(items...)` 展开调用、`=>` 同义箭头、`do` 立即调用 | 无括号默认/rest/解构参数、生成器 | +| 赋值/函数 | `a, b = array`、`[a, tail...] = array`(末项 rest 可为空)、`[a, {point: [b, c]}] = array`、`{key, "first-name": first, ...metadata} = map`(标识符或字面字符串键)的严格递归解构、`_` 忽略位;`x, y -> expression` 无括号名称闭包、`([a, b], {factor}) -> expression` 解构形参、`(x, y = 2, rest...) -> expression`(缺省或 `nil` 取默认)、`f(items...)` 展开调用、`=>` 同义箭头、`do` 立即调用 | 动态 computed 映射键、无括号默认/rest/解构参数、生成器 | | OO/模块 | 无原型工厂类 `class Name(args) -> expression` | 继承、`this`、`new`、import/export | +整数区间支持升序与降序:`[2..4]` 为 `[2, 3, 4]`,`[4..2]` 为 `[4, 3, 2]`;排除上界形式相应省略终点(`[4...2]` 为 `[4, 3]`)。边界必须是有限整数,过长区间仍报错。 + 字符串索引与严格切片按 Unicode 标量边界:`'a☕中'[1]` 为 `'☕'`,`'a☕中'[1..2]` 为 `'☕中'`;负索引从末项计数(`items[-1]`),越界仍报错;字符串 `for` 仍不支持 `by`。 映射字面量可从左至右展开:`{...defaults, theme: 'dark'}`;后写显式键或展开段覆盖先写键,展开值必须为映射。映射解构可用末尾尾部模式 `{id, ...metadata}` 捕获未列键,所得映射为新的不可变值。 diff --git a/manuals/manual.classical-zh.qc b/manuals/manual.classical-zh.qc index c2b6d20..726e334 100644 --- a/manuals/manual.classical-zh.qc +++ b/manuals/manual.classical-zh.qc @@ -38,6 +38,7 @@ ## 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,未名之误弗隐。 diff --git a/manuals/manual.devanagari-sa.qc b/manuals/manual.devanagari-sa.qc index 4eff932..e358032 100644 --- a/manuals/manual.devanagari-sa.qc +++ b/manuals/manual.devanagari-sa.qc @@ -39,6 +39,7 @@ ## 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 न गोप्यते। diff --git a/manuals/manual.en.qc b/manuals/manual.en.qc index 9bef8e8..865c3ee 100644 --- a/manuals/manual.en.qc +++ b/manuals/manual.en.qc @@ -33,6 +33,7 @@ ## 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. diff --git a/manuals/manual.latin.qc b/manuals/manual.latin.qc index fe69d09..eab0ec1 100644 --- a/manuals/manual.latin.qc +++ b/manuals/manual.latin.qc @@ -39,6 +39,7 @@ ## 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. diff --git a/manuals/manual.zh-CN.qc b/manuals/manual.zh-CN.qc index 4e01b7b..207f647 100644 --- a/manuals/manual.zh-CN.qc +++ b/manuals/manual.zh-CN.qc @@ -40,6 +40,7 @@ ## 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 保留。 diff --git a/src/parser.rs b/src/parser.rs index 939e74d..8fb6965 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -323,8 +323,13 @@ impl Parser { } break; } - let key = match self.next() { - Token::Ident(key) => key, + let (key, literal_key) = match self.next() { + Token::Ident(key) => (key, false), + Token::String(key, interpolate) + if !interpolate || !key.contains("#{") => + { + (key, true) + } _ => { self.at = saved; return None; @@ -336,6 +341,9 @@ impl Parser { return None; }; self.pattern_default(pattern).ok()? + } else if literal_key { + self.at = saved; + return None; } else if key == "_" { self.pattern_default(Pattern::Ignore).ok()? } else { diff --git a/src/vm.rs b/src/vm.rs index 1b101a0..596cbbb 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1205,19 +1205,21 @@ fn numeric_range(start: f64, end: f64, inclusive: bool) -> Result