From b62a08aa309c4a3bb8cb0489d696c9d0aa89404c Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Fri, 11 Sep 2026 11:30:38 +0800 Subject: [PATCH 1/5] =?UTF-8?q?layout:=20parser/scanner=20=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E5=BB=B6=E7=BB=AD=20+=20tutorial=20=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 --- layout/src/parser.rs | 6 +++-- layout/src/scanner.rs | 6 +++++ tutorial/08-leetcode/001_two-sum-hash.kv | 30 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 8c6c8594..5bb00b67 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -2072,8 +2072,10 @@ impl Parser { return; } let s = inst.writes[0].clone(); - // 路径字面量(/ 开头)是完整 key,不是成员写,勿脱糖。 - if s.starts_with('/') { + // 路径字面量(/ 开头)是完整 key,不是成员写,勿脱糖——**但含动态键段 `·*k` 的除外**: + // 那时 `·` 之后是运行期求值的段,必须脱糖成 kv·set(base, k, v) 才能取到 k 的值 + // (否则会被当成字面 key `/tmp/ts·*k` 整段写下去)。 + if s.starts_with('/') && !s.contains(&format!("{}*", keytree::MEMBER_SEP)) { return; } // struct 赋值(RHS = struct·new):浅拷 base+一层成员,lower 为 kv·cplist(struct·new→temp, dst)。 diff --git a/layout/src/scanner.rs b/layout/src/scanner.rs index 3b83f437..1439978b 100644 --- a/layout/src/scanner.rs +++ b/layout/src/scanner.rs @@ -463,6 +463,12 @@ pub fn scan(src: &str) -> Vec { while i < src.len() { // ·(U+00B7)与 .(释放给小数 key)是路径字符;· 后的坐标段 [0,1] 是成员链。 if src[i] == 0xC2 && i + 1 < src.len() && src[i + 1] == 0xB7 { + // `·*`(动态成员名)不并入路径:让 `·`/`*`/`k` 单独成 token,交成员链解析 + // 成 `kv·get/kv·set(base, k)`。`·` 后接名字的情况(`/lib/json·to` 的 + // 包·函数分隔符、`/tmp/ts·5` 的静态成员)仍按原样整段吃进路径 token。 + if i + 2 < src.len() && src[i + 2] == b'*' { + break; + } i += 2; if i < src.len() && src[i] == b'[' { while i < src.len() && src[i] != b']' { diff --git a/tutorial/08-leetcode/001_two-sum-hash.kv b/tutorial/08-leetcode/001_two-sum-hash.kv index 0ea685b4..eb2ec504 100644 --- a/tutorial/08-leetcode/001_two-sum-hash.kv +++ b/tutorial/08-leetcode/001_two-sum-hash.kv @@ -1,4 +1,5 @@ -// 001: Two Sum — O(n) hash map (h·*key 动态解引用,has 检存在) +// 001: Two Sum — O(n) hash map。two_sum 用 kv·get/kv·set; +// two_sum2 用动态成员名 `·*k` 直接读写路径成员。 // 来源: LeetCode #1 // 期望输出: // [ 0 , 1 ] @@ -25,8 +26,35 @@ rwfunc two_sum(nums:[]int64, target:int64) -> () { } } +// 同 two_sum,但用**动态成员名** `·*k` 直接读写路径成员: +// `base·*k` 取 k 的值作 memitemkey(等价 kv·get(base,k)/kv·set(base,k,v)); +// 裸 `base·k` 用的是**字面名** k,两者不可混(见 05-runtime语义/03-成员访问)。 +rwfunc two_sum2(nums:[]int64, target:int64) -> () { + n = ndarray·numel(nums) + i = 0 + /tmp/two_sum:int64·int64 = {} + while (i < n) { + x = nums[i] + need = target - x + // * 令 need 的**值**作成员键(不是字面名 need) + exists = /tmp/two_sum·*need != None + + if (exists) { + j = /tmp/two_sum·*need + k = j - 1 + println("[", k, ",", i, "]") + n -> i + } else { + v = i + 1 + /tmp/two_sum·*x = v + i + 1 -> i + } + } +} + rwfunc test() -> () { a:[]int64 = [2, 7, 11, 15] two_sum(a, 9) + two_sum2(a, 9) } From 6ecfece91cd5a5effb2d9fca4511b81fefe7ee6c Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Fri, 11 Sep 2026 12:24:35 +0800 Subject: [PATCH 2/5] =?UTF-8?q?layout/spec/tutorial:=20=E8=AF=AD=E4=B9=89?= =?UTF-8?q?=E5=BB=B6=E7=BB=AD=20+=2010-types/12-struct/14-networld=20?= =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layout lower/parser + spec 04-函数;10-types 全组(typed/string/return/ nested/tuple-key map)、12-struct(链表/树/图/reverse/CJK名/ptr-param)、 14-networld、leetcode 链表等 tutorial 同步。 Co-Authored-By: Claude Opus 4.8 --- layout/src/lower.rs | 5 ++- layout/src/parser.rs | 36 ++++++++++++------- .../03-\345\207\275\346\225\260.kv" | 2 +- tutorial/04-ndarray/sparse_literal.kv | 2 +- tutorial/05-dict/literal_as_arg.kv | 4 +-- tutorial/06-algo/map_reduce.kv | 2 +- tutorial/06-algo/word_count.kv | 2 +- tutorial/08-leetcode/002_add_two_numbers.kv | 2 +- tutorial/08-leetcode/021_merge_two_lists.kv | 2 +- tutorial/08-leetcode/083_remove_dup_list.kv | 4 +-- tutorial/08-leetcode/094_inorder_traversal.kv | 2 +- tutorial/08-leetcode/100_same_tree.kv | 2 +- tutorial/08-leetcode/104_max_tree_depth.kv | 2 +- tutorial/08-leetcode/141_linked_list_cycle.kv | 2 +- tutorial/08-leetcode/160_intersection_list.kv | 2 +- .../08-leetcode/203_remove_linked_elements.kv | 2 +- .../08-leetcode/206_reverse_linked_list.kv | 2 +- tutorial/08-leetcode/234_palindrome_list.kv | 2 +- tutorial/09-debugger/breakpoint.kv | 2 +- tutorial/09-debugger/sum_to.kv | 2 +- tutorial/10-types/01-typed-map.kv | 4 +-- tutorial/10-types/02-string-map.kv | 2 +- tutorial/10-types/03-map-return.kv | 2 +- tutorial/10-types/04-nested-type.kv | 4 +-- tutorial/10-types/05-tuple-key.kv | 6 ++-- tutorial/12-struct/03-linked-list.kv | 2 +- tutorial/12-struct/04-tree.kv | 2 +- tutorial/12-struct/05-graph.kv | 2 +- tutorial/12-struct/06-list-reverse.kv | 2 +- tutorial/12-struct/07-list-local.kv | 2 +- tutorial/12-struct/08-cjk-struct-name.kv | 4 +-- tutorial/12-struct/09-ptr-param-mutate.kv | 2 +- tutorial/14-networld/01-exec.kv | 4 +-- tutorial/14-networld/02-bash.kv | 4 +-- tutorial/14-networld/03-python.kv | 4 +-- tutorial/14-networld/04-kvlang.kv | 4 +-- tutorial/14-networld/05-capture.kv | 4 +-- tutorial/14-networld/10-capture-ext-type.kv | 4 +-- 38 files changed, 76 insertions(+), 63 deletions(-) diff --git a/layout/src/lower.rs b/layout/src/lower.rs index 30e3e9e9..f52e2b94 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -14,7 +14,10 @@ fn is_container_type(t: &str) -> bool { /// `[]` 下标校验:xv·at/xv·set 基座若为容器类型 → 报错,逼用 kv·get/kv·set/`base·key`。 fn is_container_ty(ty: &str) -> bool { - ty.contains(keytree::MEMBER_SEP) || ty.starts_with('/') + // 剥掉间接性前缀 `*`/`@` 再判:签名里 `p:*Point`、`m:*[int64]·int64` 的 `*` 是传递方式, + // 类型本体仍是值容器——不剥会把这类参数误判为"未定义容器",成员写全部被拒。 + let t = ty.trim_start_matches(['*', '@']); + t.contains(keytree::MEMBER_SEP) || t.starts_with('/') } /// 容器字面量的写目标必须带**完整 map langtype**(`{memitemkeylangtype}·{memitemvaluelangtype}`)。 diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 5bb00b67..6eb352a0 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -548,29 +548,32 @@ impl Parser { } fn check_param_types(&mut self, sig: &FuncSig) { - // 指针形参一律拒:kvlang 只有地址传递,且**由 layout 自动落到地址**——函数体里每个形参 - // 引用都被 lower 成 `*[0,±k]`(参数轴槽存的是实参地址,显式解引用才取到值)。 - // 故声明写 `p:*Point` 与 `p:Point` 完全等价,那个 `*` 是纯噪声,只会让人以为 - // 「值/地址」在签名层可选。`*T` 仍用于 struct 字段与局部标注——那里确实要区分 - // 「指向节点的引用」与节点本身。 + // 签名里的 `*` 是**作者书写的传递方式**:写 `*T` = 按地址传(帧槽存实参地址 Ptr), + // 不写 = 按值传(帧槽存值本体)。唯一硬约束:**值容器类型必须写 `*`**——map langtype + // (含 `·`)与 structref(`/` 开头)的成员落在兄弟槽 `{key}·`,单槽不是完备值,没有 + // 可拷贝的"值",只能按地址传。 for (slot, ret) in sig .params .iter() .map(|p| ("param", p)) .chain(sig.returns.iter().map(|r| ("return value", r))) { - if ptr_prefixed(&ret.ty) { + if is_value_container_ty(&ret.ty) && !ptr_prefixed(&ret.ty) { self.errors.push(Diagnostic { pos: Pos { line: 0, col: 0 }, message: format!( - "func {}: {slot} {:?}: pointer type {:?} is not allowed — parameter \ - references are already lowered to `*[0,±k]` (the axis slot holds the \ - argument address), so write {:?}", + "func {}: {slot} {:?}: 值容器类型只能按地址传递 —— 写 `{:?}`", sig.name, ret.name, - ret.ty, - ret.ty.split(['|', '·']).map(|a| a.trim_start_matches('*')) - .collect::>().join("·") + ret.ty + .split('|') + .map(|a| if !a.starts_with('*') && (a.starts_with('/') || a.contains('·')) { + format!("*{a}") + } else { + a.to_string() + }) + .collect::>() + .join("|") ), warn: false, info: false, @@ -2272,11 +2275,18 @@ fn is_fixed_dim_array(t: &str) -> bool { .all(|d| !d.trim().is_empty() && d.trim().bytes().all(|c| c.is_ascii_digit())) } -/// 类型串里是否有原子以 `*`(Ptr 前缀)起头——以 `|`(并)与 `·`(map 键值)切分后逐段看。 +/// 类型串里是否有原子以 `*`(间接性前缀)起头——以 `|`(并)与 `·`(map 键值)切分后逐段看。 fn ptr_prefixed(ty: &str) -> bool { ty.split(['|', '·']).any(|a| a.starts_with('*')) } +/// 值容器类型:成员落在兄弟槽 `{key}·` 的类型——map langtype(含 `·`)或 structref(`/` 开头)。 +/// 这类值没有可拷贝的单槽值,只能按地址传递(见 spec [[函数]])。 +fn is_value_container_ty(ty: &str) -> bool { + ty.split('|') + .any(|a| a.trim_start_matches('*').starts_with('/') || a.contains('·')) +} + fn type_error(_kind: &str) -> String { "unknown type — valid: int8/16/32/64, uint8/16/32/64, float32/64, bool, char/utf32, obj, map, index, char, any, []T, [2,3]T, [?,N]T, A|B".to_string() } diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" index c1dde32a..8a7aa076 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" @@ -91,7 +91,7 @@ error: func f: "q" aliases read param "p" (`q = p`); writing its members writes `-> (p:Point)` 里——kvlang 只有地址传递,写参 `p` 拿到的就是调用方对象的地址: ```kv -rwfunc shift(dx:int64) -> (p:Point) { p·x + dx -> p·x } +rwfunc shift(dx:int64) -> (p:*Point) { p·x + dx -> p·x } rwfunc test() -> () { a:Point = {x=1 y=2} shift(10) -> a } ``` diff --git a/tutorial/04-ndarray/sparse_literal.kv b/tutorial/04-ndarray/sparse_literal.kv index 04656eb1..fbe183d1 100644 --- a/tutorial/04-ndarray/sparse_literal.kv +++ b/tutorial/04-ndarray/sparse_literal.kv @@ -9,7 +9,7 @@ // sum = 100 // 散 key 数组作实参时 for-in 在 layout 脱糖成 compact 取值(xv·at), // 函数内改用 kv·listlen/kv·listn/kv·get 显式遍历。 -rwfunc print_sum(nums:[int64]·int64) -> (total:int64) { +rwfunc print_sum(nums:*[int64]·int64) -> (total:int64) { total = 0 kv·listlen(nums) -> n 0 -> i diff --git a/tutorial/05-dict/literal_as_arg.kv b/tutorial/05-dict/literal_as_arg.kv index e15cb6c0..46f4e7f9 100644 --- a/tutorial/05-dict/literal_as_arg.kv +++ b/tutorial/05-dict/literal_as_arg.kv @@ -4,10 +4,10 @@ // 期望输出: // 3 // hi -rwfunc count(a:[int64]·[]char/utf32) -> (n:int64) { +rwfunc count(a:*[int64]·[]char/utf32) -> (n:int64) { kv·listlen(a) -> n } -rwfunc first(a:[int64]·[]char/utf32) -> (v:[]char/utf32) { +rwfunc first(a:*[int64]·[]char/utf32) -> (v:[]char/utf32) { kv·get(a, "[0]") -> v } rwfunc test() -> () { diff --git a/tutorial/06-algo/map_reduce.kv b/tutorial/06-algo/map_reduce.kv index 73e321e1..a6f16aad 100644 --- a/tutorial/06-algo/map_reduce.kv +++ b/tutorial/06-algo/map_reduce.kv @@ -5,7 +5,7 @@ // sum of squares: 165 // 散 key 数组作实参时 for-in 在 layout 脱糖成 compact 取值(xv·at), // 函数内改用 kv·listlen/kv·listn/kv·get 显式遍历。 -rwfunc map_reduce(nums:[int64]·int64) -> (count:int64, total:int64) { +rwfunc map_reduce(nums:*[int64]·int64) -> (count:int64, total:int64) { 0 -> count total = 0 kv·listlen(nums) -> n diff --git a/tutorial/06-algo/word_count.kv b/tutorial/06-algo/word_count.kv index a96661ab..fbf1d83c 100644 --- a/tutorial/06-algo/word_count.kv +++ b/tutorial/06-algo/word_count.kv @@ -4,7 +4,7 @@ // word count = 3 // 散 key 数组作实参时 for-in 在 layout 脱糖成 compact 取值(xv·at), // 函数内改用 kv·listlen/kv·listn/kv·get 显式遍历。 -rwfunc word_count(chars:[int64]·[]char/utf32) -> (nw:int64) { +rwfunc word_count(chars:*[int64]·[]char/utf32) -> (nw:int64) { nw = 0 0 -> state kv·listlen(chars) -> n diff --git a/tutorial/08-leetcode/002_add_two_numbers.kv b/tutorial/08-leetcode/002_add_two_numbers.kv index 6b6d08dc..7f2e777a 100644 --- a/tutorial/08-leetcode/002_add_two_numbers.kv +++ b/tutorial/08-leetcode/002_add_two_numbers.kv @@ -10,7 +10,7 @@ struct Node { } lib lc { -rwfunc add(a:Node, b:Node) -> () { +rwfunc add(a:*Node, b:*Node) -> () { carry = 0 result:[int64]·int64 = { } 0 -> ri diff --git a/tutorial/08-leetcode/021_merge_two_lists.kv b/tutorial/08-leetcode/021_merge_two_lists.kv index a93651ac..6d34bf43 100644 --- a/tutorial/08-leetcode/021_merge_two_lists.kv +++ b/tutorial/08-leetcode/021_merge_two_lists.kv @@ -12,7 +12,7 @@ struct Node { next:*Node=None } -rwfunc merge(a:Node, b:Node) -> () { +rwfunc merge(a:*Node, b:*Node) -> () { pa = a pb = b while (pa != None) { diff --git a/tutorial/08-leetcode/083_remove_dup_list.kv b/tutorial/08-leetcode/083_remove_dup_list.kv index 0605ba9e..f94d33cc 100644 --- a/tutorial/08-leetcode/083_remove_dup_list.kv +++ b/tutorial/08-leetcode/083_remove_dup_list.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc dedup() -> (head:Node) { +rwfunc dedup() -> (head:*Node) { head -> cur while (cur != None) { cur·next -> nxt @@ -33,7 +33,7 @@ rwfunc dedup() -> (head:Node) { } } -rwfunc show(head:Node) -> () { +rwfunc show(head:*Node) -> () { head -> p while (p != None) { p·val -> v diff --git a/tutorial/08-leetcode/094_inorder_traversal.kv b/tutorial/08-leetcode/094_inorder_traversal.kv index 96f7edac..a558445b 100644 --- a/tutorial/08-leetcode/094_inorder_traversal.kv +++ b/tutorial/08-leetcode/094_inorder_traversal.kv @@ -10,7 +10,7 @@ struct TNode { right:*TNode=None } -rwfunc inorder(root:TNode) -> () { +rwfunc inorder(root:*TNode) -> () { // iterative stack simulation si = 0 stack:[int64]·*TNode = {} diff --git a/tutorial/08-leetcode/100_same_tree.kv b/tutorial/08-leetcode/100_same_tree.kv index 2a152d10..d9a92aa5 100644 --- a/tutorial/08-leetcode/100_same_tree.kv +++ b/tutorial/08-leetcode/100_same_tree.kv @@ -9,7 +9,7 @@ struct TNode { right:*TNode=None } -rwfunc same(a:TNode, b:TNode) -> (ok:int64) { +rwfunc same(a:*TNode, b:*TNode) -> (ok:int64) { if (a == None || b == None) { if (a == b) { 1 -> ok } else { 0 -> ok } } else { diff --git a/tutorial/08-leetcode/104_max_tree_depth.kv b/tutorial/08-leetcode/104_max_tree_depth.kv index 3f06eb89..da24daea 100644 --- a/tutorial/08-leetcode/104_max_tree_depth.kv +++ b/tutorial/08-leetcode/104_max_tree_depth.kv @@ -8,7 +8,7 @@ struct TNode { right:*TNode=None } -rwfunc max_depth(root:TNode) -> (depth:int64) { +rwfunc max_depth(root:*TNode) -> (depth:int64) { if (root == None) { 0 -> depth } else { ld = max_depth(root·left) diff --git a/tutorial/08-leetcode/141_linked_list_cycle.kv b/tutorial/08-leetcode/141_linked_list_cycle.kv index 732fc015..25259a3d 100644 --- a/tutorial/08-leetcode/141_linked_list_cycle.kv +++ b/tutorial/08-leetcode/141_linked_list_cycle.kv @@ -3,7 +3,7 @@ // 期望输出: // 1 // 0 -rwfunc has_cycle(head:Node) -> (result:int64) { +rwfunc has_cycle(head:*Node) -> (result:int64) { result = 0 head -> slow fast = head diff --git a/tutorial/08-leetcode/160_intersection_list.kv b/tutorial/08-leetcode/160_intersection_list.kv index 817e37a8..f7770bc6 100644 --- a/tutorial/08-leetcode/160_intersection_list.kv +++ b/tutorial/08-leetcode/160_intersection_list.kv @@ -8,7 +8,7 @@ struct Node { next:*Node=None } -rwfunc get_intersection(ha:Node, hb:Node) -> () { +rwfunc get_intersection(ha:*Node, hb:*Node) -> () { pa = ha pb = hb switch_a = 0 diff --git a/tutorial/08-leetcode/203_remove_linked_elements.kv b/tutorial/08-leetcode/203_remove_linked_elements.kv index 12c86d5c..f56e9910 100644 --- a/tutorial/08-leetcode/203_remove_linked_elements.kv +++ b/tutorial/08-leetcode/203_remove_linked_elements.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc remove(head:Node, val:int64) -> () { +rwfunc remove(head:*Node, val:int64) -> () { head -> p while (p != None) { v = p·val diff --git a/tutorial/08-leetcode/206_reverse_linked_list.kv b/tutorial/08-leetcode/206_reverse_linked_list.kv index a4b80f3e..b223b523 100644 --- a/tutorial/08-leetcode/206_reverse_linked_list.kv +++ b/tutorial/08-leetcode/206_reverse_linked_list.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc reverse() -> (head:Node) { +rwfunc reverse() -> (head:*Node) { prev = None head -> cur while (cur != None) { diff --git a/tutorial/08-leetcode/234_palindrome_list.kv b/tutorial/08-leetcode/234_palindrome_list.kv index 98d4419c..baaa3b5c 100644 --- a/tutorial/08-leetcode/234_palindrome_list.kv +++ b/tutorial/08-leetcode/234_palindrome_list.kv @@ -8,7 +8,7 @@ struct Node { next:*Node=None } -rwfunc is_pal(head:Node) -> () { +rwfunc is_pal(head:*Node) -> () { arr:[int64]·int64 = {} 0 -> n head -> p diff --git a/tutorial/09-debugger/breakpoint.kv b/tutorial/09-debugger/breakpoint.kv index 9236dbaa..b6e6b724 100644 --- a/tutorial/09-debugger/breakpoint.kv +++ b/tutorial/09-debugger/breakpoint.kv @@ -1,4 +1,4 @@ -rwfunc loop(n: int64) -> () { +rwfunc loop(n:int64) -> () { 1 -> i while (i <= n) { debugger() // 内联断点:执行至此暂停,等待 agent 命令 diff --git a/tutorial/09-debugger/sum_to.kv b/tutorial/09-debugger/sum_to.kv index d590cdde..43a62bc1 100644 --- a/tutorial/09-debugger/sum_to.kv +++ b/tutorial/09-debugger/sum_to.kv @@ -1,4 +1,4 @@ -rwfunc sum_to(n: int64) -> (total: int64) { +rwfunc sum_to(n:int64) -> (total:int64) { 0 -> total 1 -> i while (i <= n) { diff --git a/tutorial/10-types/01-typed-map.kv b/tutorial/10-types/01-typed-map.kv index b095ddc9..eb44f4d3 100644 --- a/tutorial/10-types/01-typed-map.kv +++ b/tutorial/10-types/01-typed-map.kv @@ -4,12 +4,12 @@ // 期望输出: // total = 30 // max = 20 -rwfunc total(m:[]char/utf8·int64) -> (s:int64) { +rwfunc total(m:*[]char/utf8·int64) -> (s:int64) { kv·get(m, "a") -> a kv·get(m, "b") -> b (a + b) -> s } -rwfunc max2(m:[]char/utf8·int64) -> (mx:int64) { +rwfunc max2(m:*[]char/utf8·int64) -> (mx:int64) { kv·get(m, "a") -> a kv·get(m, "b") -> b max(a, b) -> mx diff --git a/tutorial/10-types/02-string-map.kv b/tutorial/10-types/02-string-map.kv index 3e6e492a..977f6222 100644 --- a/tutorial/10-types/02-string-map.kv +++ b/tutorial/10-types/02-string-map.kv @@ -4,7 +4,7 @@ // en = hello // zh = 你好 // missing: true -rwfunc lookup(dict:[]char/utf8·[]char/utf32, k:[]char/utf32) -> (v:[]char/utf32) { +rwfunc lookup(dict:*[]char/utf8·[]char/utf32, k:[]char/utf32) -> (v:[]char/utf32) { kv·get(dict, k) -> v } rwfunc test() -> () { diff --git a/tutorial/10-types/03-map-return.kv b/tutorial/10-types/03-map-return.kv index 36d52204..d36b1602 100644 --- a/tutorial/10-types/03-map-return.kv +++ b/tutorial/10-types/03-map-return.kv @@ -4,7 +4,7 @@ // n = 42 // x = 7 // miss = true -rwfunc build() -> (m:[]char/utf8·int64) { +rwfunc build() -> (m:*[]char/utf8·int64) { {n=42; x=7} -> m } rwfunc test() -> () { diff --git a/tutorial/10-types/04-nested-type.kv b/tutorial/10-types/04-nested-type.kv index d756c5a9..1dd1017c 100644 --- a/tutorial/10-types/04-nested-type.kv +++ b/tutorial/10-types/04-nested-type.kv @@ -3,10 +3,10 @@ // (运行时值语义为内联标量/串,嵌套物化非本例范畴;本例证 layout 接受嵌套签名。) // 期望输出: // nested signature accepted -rwfunc grid(g:[]char/utf8·[]char/utf8·int64) -> (n:int64) { +rwfunc grid(g:*[]char/utf8·[]char/utf8·int64) -> (n:int64) { 0 -> n } -rwfunc deep(t:[]char/utf8·[]char/utf8·[]char/utf8·float64) -> (n:int64) { +rwfunc deep(t:*[]char/utf8·[]char/utf8·[]char/utf8·float64) -> (n:int64) { 0 -> n } rwfunc test() -> () { diff --git a/tutorial/10-types/05-tuple-key.kv b/tutorial/10-types/05-tuple-key.kv index a39d4eb2..4964ebcb 100644 --- a/tutorial/10-types/05-tuple-key.kv +++ b/tutorial/10-types/05-tuple-key.kv @@ -4,13 +4,13 @@ // layout 校验元组元素为标量 kind 并 round-trip 进签名 head(本例证签名接受)。 // 期望输出: // tuple-key signatures accepted -rwfunc grid_i(g:[int32,int32]·[]char/utf8) -> (n:int64) { +rwfunc grid_i(g:*[int32,int32]·[]char/utf8) -> (n:int64) { 0 -> n } -rwfunc grid_f(g:[float32,float32]·int64) -> (n:int64) { +rwfunc grid_f(g:*[float32,float32]·int64) -> (n:int64) { 0 -> n } -rwfunc mixed(g:[float32,int8,uint32]·int64) -> (n:int64) { +rwfunc mixed(g:*[float32,int8,uint32]·int64) -> (n:int64) { 0 -> n } rwfunc test() -> () { diff --git a/tutorial/12-struct/03-linked-list.kv b/tutorial/12-struct/03-linked-list.kv index 6692dc99..d0e53aa2 100644 --- a/tutorial/12-struct/03-linked-list.kv +++ b/tutorial/12-struct/03-linked-list.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc show_list(head:Node) -> () { +rwfunc show_list(head:*Node) -> () { head -> cur while (cur != None) { cur·val -> v diff --git a/tutorial/12-struct/04-tree.kv b/tutorial/12-struct/04-tree.kv index 9e8006af..ec4194f1 100644 --- a/tutorial/12-struct/04-tree.kv +++ b/tutorial/12-struct/04-tree.kv @@ -11,7 +11,7 @@ struct TNode { right:*TNode=None } -rwfunc inorder(node:TNode) -> () { +rwfunc inorder(node:*TNode) -> () { if (node != None) { node·left -> l inorder(l) diff --git a/tutorial/12-struct/05-graph.kv b/tutorial/12-struct/05-graph.kv index a9ca4791..b437f74e 100644 --- a/tutorial/12-struct/05-graph.kv +++ b/tutorial/12-struct/05-graph.kv @@ -13,7 +13,7 @@ struct GNode { seen:bool=false } -rwfunc dfs() -> (node:GNode) { +rwfunc dfs() -> (node:*GNode) { if (node != None) { node·seen -> s if (!s) { diff --git a/tutorial/12-struct/06-list-reverse.kv b/tutorial/12-struct/06-list-reverse.kv index 794482d6..c16497d0 100644 --- a/tutorial/12-struct/06-list-reverse.kv +++ b/tutorial/12-struct/06-list-reverse.kv @@ -12,7 +12,7 @@ struct Node { next:*Node=None } -rwfunc reverse_list() -> (head:Node, new_head:Node) { +rwfunc reverse_list() -> (head:*Node, new_head:*Node) { prev = None head -> cur while (cur != None) { diff --git a/tutorial/12-struct/07-list-local.kv b/tutorial/12-struct/07-list-local.kv index 74b1571f..0dff0a5c 100644 --- a/tutorial/12-struct/07-list-local.kv +++ b/tutorial/12-struct/07-list-local.kv @@ -9,7 +9,7 @@ struct Node { next:*Node=None } -rwfunc sum_list(head:Node) -> (sum:int64) { +rwfunc sum_list(head:*Node) -> (sum:int64) { head -> cur 0 -> sum while (cur != None) { diff --git a/tutorial/12-struct/08-cjk-struct-name.kv b/tutorial/12-struct/08-cjk-struct-name.kv index fdbdbdaf..12c383ba 100644 --- a/tutorial/12-struct/08-cjk-struct-name.kv +++ b/tutorial/12-struct/08-cjk-struct-name.kv @@ -9,10 +9,10 @@ struct 点 { x:int64=0 y:int64=0 } -rwfunc 求和(p:点) -> (s:int64) { +rwfunc 求和(p:*点) -> (s:int64) { p·x + p·y -> s } -rwfunc 造点(a:int64, b:int64) -> (r:点) { +rwfunc 造点(a:int64, b:int64) -> (r:*点) { a -> r·x b -> r·y } diff --git a/tutorial/12-struct/09-ptr-param-mutate.kv b/tutorial/12-struct/09-ptr-param-mutate.kv index 0c8e70d6..06f47cce 100644 --- a/tutorial/12-struct/09-ptr-param-mutate.kv +++ b/tutorial/12-struct/09-ptr-param-mutate.kv @@ -8,7 +8,7 @@ struct Point { x:int64=0 y:int64=0 } -rwfunc shift(dx:int64) -> (p:Point) { +rwfunc shift(dx:int64) -> (p:*Point) { p·x + dx -> p·x } rwfunc test() -> () { diff --git a/tutorial/14-networld/01-exec.kv b/tutorial/14-networld/01-exec.kv index 0144eedd..f022153b 100644 --- a/tutorial/14-networld/01-exec.kv +++ b/tutorial/14-networld/01-exec.kv @@ -11,8 +11,8 @@ // missing= 127 // FOO=bar123 // run_proc 封装一次命令执行:输入 args/envs,输出退出码 -rwfunc run_proc(args:[int64]·[]char/utf32, - envs:[int64]·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:*[int64]·[]char/utf32, + envs:*[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } diff --git a/tutorial/14-networld/02-bash.kv b/tutorial/14-networld/02-bash.kv index e5dcdf1f..248ecd1a 100644 --- a/tutorial/14-networld/02-bash.kv +++ b/tutorial/14-networld/02-bash.kv @@ -2,8 +2,8 @@ // 期望输出: // bash 42 // exit= 0 -rwfunc run_proc(args:[int64]·[]char/utf32, - envs:[int64]·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:*[int64]·[]char/utf32, + envs:*[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } diff --git a/tutorial/14-networld/03-python.kv b/tutorial/14-networld/03-python.kv index a806a561..96d5b1dc 100644 --- a/tutorial/14-networld/03-python.kv +++ b/tutorial/14-networld/03-python.kv @@ -2,8 +2,8 @@ // 期望输出: // python 1024 // exit= 0 -rwfunc run_proc(args:[int64]·[]char/utf32, - envs:[int64]·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:*[int64]·[]char/utf32, + envs:*[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } diff --git a/tutorial/14-networld/04-kvlang.kv b/tutorial/14-networld/04-kvlang.kv index 1f7453d1..d1193af5 100644 --- a/tutorial/14-networld/04-kvlang.kv +++ b/tutorial/14-networld/04-kvlang.kv @@ -3,8 +3,8 @@ // 期望输出: // kvlang in kvlang // exit= 0 -rwfunc run_proc(args:[int64]·[]char/utf32, - envs:[int64]·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:*[int64]·[]char/utf32, + envs:*[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } diff --git a/tutorial/14-networld/05-capture.kv b/tutorial/14-networld/05-capture.kv index 3dd3bf41..ca5785a7 100644 --- a/tutorial/14-networld/05-capture.kv +++ b/tutorial/14-networld/05-capture.kv @@ -8,8 +8,8 @@ // err= oops // run_capture 把 code/out/err 三个结果一并带回调用方; // 捕获句柄无 kindexpr 类型,写参声明为 any -rwfunc run_capture(args:[int64]·[]char/utf32, - envs:[int64]·[]char/utf32) -> (code:uint8, out:any, err:any) { +rwfunc run_capture(args:*[int64]·[]char/utf32, + envs:*[int64]·[]char/utf32) -> (code:uint8, out:any, err:any) { networld/proc·exec(args, envs) -> code, out, err } diff --git a/tutorial/14-networld/10-capture-ext-type.kv b/tutorial/14-networld/10-capture-ext-type.kv index cd4f7c64..1d02354a 100644 --- a/tutorial/14-networld/10-capture-ext-type.kv +++ b/tutorial/14-networld/10-capture-ext-type.kv @@ -5,8 +5,8 @@ // 期望输出: // hi // oops -rwfunc run_capture(args:[int64]·[]char/utf32, - envs:[int64]·[]char/utf32) -> (c:uint8, out:@[]char/utf32, +rwfunc run_capture(args:*[int64]·[]char/utf32, + envs:*[int64]·[]char/utf32) -> (c:uint8, out:@[]char/utf32, err:@[]char/utf32) { networld/proc·exec(args, envs) -> c, out, err } From 6909afd27eb1399062cee4fa61d32ce87a6af9e2 Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Fri, 11 Sep 2026 13:13:56 +0800 Subject: [PATCH 3/5] =?UTF-8?q?layout/runtime/spec/tutorial:=20=E8=AF=AD?= =?UTF-8?q?=E4=B9=89=E5=BB=B6=E7=BB=AD=20+=20value-vs-addr=20=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layout code/langtype/parser + runtime-rs rwir + runtime kvcpu + spec 系统变量;10-types value-vs-addr / any-read-param、09-debugger tutorial 对齐。 Co-Authored-By: Claude Opus 4.8 --- layout/src/code.rs | 24 +-- layout/src/langtype.rs | 8 + layout/src/parser.rs | 8 +- runtime-rs/src/rwir/mod.rs | 4 +- runtime/src/kvcpu.c | 140 +++++++++++++++--- ...73\347\273\237\345\217\230\351\207\217.kv" | 8 +- tutorial/09-debugger/chain_array.kv | 2 +- tutorial/10-types/06-any-read-param.kv | 2 + tutorial/10-types/08-value-vs-addr.kv | 26 ++++ 9 files changed, 180 insertions(+), 42 deletions(-) create mode 100644 tutorial/10-types/08-value-vs-addr.kv diff --git a/layout/src/code.rs b/layout/src/code.rs index eb95390f..b4c2820d 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -17,7 +17,7 @@ use super::ast::{ self, Expr, Func, FuncSig, Instruction, Param, RwirDecl, ScopeStmt, Stmt, StructDecl, }; use super::ffi::Kv; -use super::{builtin, ffi, keytree, kvkind, lower, parser}; +use super::{builtin, ffi, keytree, kvkind, langtype, lower, parser}; /// 创建基础目录 /lib/ 与 /vthread/(layout 前必须存在)。 pub fn init_dirs(kv: &mut Kv) -> Result<(), String> { @@ -622,30 +622,20 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { lower::specialize(fn_, &type_map); let func_dir = keytree::lib_func(pkg, &fn_.sig.name); - // pkg 下同名 func 已存在(含签名槽 [0,0])→ debug 提示即将覆盖(严格按 lib 声明判定,非路径)。 - // 覆盖是 KVLANG_LIB 引导的常态(stdlib 每次重铺),故降到 debug 级:仅 LOG_LEVEL=debug 显示。 - if !kv.get_one(&format!("{func_dir}/[0,0]")).is_empty() - && std::env::var("LOG_LEVEL").as_deref() == Ok("debug") - { - let qual = if pkg.is_empty() { - fn_.sig.name.clone() - } else { - format!("{pkg}/{}", fn_.sig.name) - }; - eprintln!("debug: func {qual} already defined — overwriting"); - } - let mut seq: Vec = Vec::new(); let mut labels: HashMap = HashMap::new(); collect_insts(&fn_.body, &mut seq, &mut labels); - // 参数名 → *[0,±k](显式解引用坐标):函数体形参引用编译期替换,删命名参数 Ptr 运行时角色。 + // 形参引用 → 帧坐标(编译期替换,运行时无命名参数角色)。带 `*` 的形参按**地址传递**(槽存实参地址,体内显式解引用 `*[0,-k]`); + // 不带 `*` 的按**值传递**(槽存值本体,体内裸坐标 `[0,-k]` 直读)。见 spec [[函数]]。 let mut param_coord: HashMap = HashMap::new(); for (i, p) in fn_.sig.params.iter().enumerate() { - param_coord.insert(p.name.clone(), format!("*[0,-{}]", i + 1)); + let d = if langtype::is_addr_param(&p.ty) { "*" } else { "" }; + param_coord.insert(p.name.clone(), format!("{d}[0,-{}]", i + 1)); } for (i, r) in fn_.sig.returns.iter().enumerate() { - param_coord.insert(r.name.clone(), format!("*[0,{}]", i + 1)); + let d = if langtype::is_addr_param(&r.ty) { "*" } else { "" }; + param_coord.insert(r.name.clone(), format!("{d}[0,{}]", i + 1)); } // 按函数覆盖(文件夹复制式合并):只 del_tree 本函数子树,不动 /lib 下其它函数。 diff --git a/layout/src/langtype.rs b/layout/src/langtype.rs index 940757d1..12667ad2 100644 --- a/layout/src/langtype.rs +++ b/layout/src/langtype.rs @@ -185,6 +185,14 @@ fn valid_atom(s: &str) -> bool { valid_shape(s) } +/// 形参是否按**地址传递**:声明带 `*`(指针)或 `@`(扩展句柄)前缀。两者都不是可拷的值本体 +/// (`*` 指别处的 key、`@` 指 kvspace 之外的位置),故槽里放间接值、体内解引用;不带前缀则按值传 +/// (槽存值本体)。见 spec [[函数]]。 +pub fn is_addr_param(ty: &str) -> bool { + let t = ty.trim_start(); + t.starts_with('*') || t.starts_with('@') +} + /// 类型表达式语法校验(装载期)。变参 `...` 是签名层 arity、不入 langtype 串,此处永不见。 pub fn valid_langtype(expr: &str) -> bool { !expr.is_empty() && expr.split('|').all(valid_atom) diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 6eb352a0..da4f55dd 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -711,9 +711,13 @@ impl Parser { } fn check_read_only_params(&mut self, func: &Func) { + // 只读只对**地址读参**(声明带 `*`/`@`)生效:它的槽是调用方对象的地址,写它就是写 + // 调用方的对象。**值读参**(不带前缀)的槽是自己的副本,体内可自由读写,不进本检查。 let mut ro = std::collections::HashSet::new(); - for n in func.sig.param_names() { - ro.insert(n); + for p in func.sig.params.iter() { + if super::langtype::is_addr_param(&p.ty) { + ro.insert(p.name.clone()); + } } if ro.is_empty() { return; diff --git a/runtime-rs/src/rwir/mod.rs b/runtime-rs/src/rwir/mod.rs index 10eced7c..05574e04 100644 --- a/runtime-rs/src/rwir/mod.rs +++ b/runtime-rs/src/rwir/mod.rs @@ -120,7 +120,7 @@ pub const MYRWIRCAPS: &[(&str, Rwir)] = &[ ( "networld/proc·exec", Rwir { - rp: &["[int64]·[]char/utf32", "[int64]·[]char/utf32"], + rp: &["*[int64]·[]char/utf32", "*[int64]·[]char/utf32"], wp: &["uint8", "[]uint8", "[]uint8"], }, ), @@ -156,7 +156,7 @@ pub const MYRWIRCAPS: &[(&str, Rwir)] = &[ "networld/fs·list", Rwir { rp: &["[]char/utf8|[]char/utf32"], - wp: &["[int64]·[]char/utf32"], + wp: &["*[int64]·[]char/utf32"], }, ), ( diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index 32c256f2..b25352d5 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -359,6 +359,47 @@ static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, return -1; } } + /* 值写参 copy-out:‥wdst 第 i 行给出第 i 个写参的调用方目标 key,把本帧 `[0,+i]` 的 + * 值复制回去(地址写参留空行,不在此列)。表不存在 = 本帧无值写参,一次读即跳过。 */ + if (d > 1) { + kvlangStrbuf_t dk; + kvlangStrbufInit(&dk); + kvlangStrbufPrintf(&dk, "%s/", fr); + kvlangStrbufPuts(&dk, RUNTIME_MEMBER_SEP "wdst"); + kvlangXvalue_t dv; + kvlangXvalueZero(&dv); + kvlangKvGetOne(kv, dk.p, &dv); + kvlangStrbufFree(&dk); + if (!kvlangXvalueNone(&dv)) { + char *table = kvlangXvalueValueString(&dv); + char *cur = table; + int i = 1; + while (cur) { + char *nl = strchr(cur, '\n'); + if (nl) + *nl = 0; + if (cur[0]) { + kvlangStrbuf_t sk; + kvlangStrbufInit(&sk); + kvlangStrbufPrintf(&sk, "%s/[0,%d]", fr, i); + kvlangXvalue_t sv; + kvlangXvalueZero(&sv); + kvlangKvGetOne(kv, sk.p, &sv); + kvlangStrbufFree(&sk); + if (!kvlangXvalueNone(&sv)) { + kvlangKvPair_t wp = {cur, sv}; + char werr[256]; + kvlangKvSet(kv, &wp, 1, werr, sizeof werr); + } + kvlangXvalueFree(&sv); + } + i++; + cur = nl ? nl + 1 : NULL; + } + free(table); + } + kvlangXvalueFree(&dv); + } char *stk = kvlangKeytreeStack(fr); char err[256]; kvlangKvDelExtIndex(kv, stk, err, sizeof err); @@ -546,12 +587,27 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangStrbufPrintf(&slot, "%s/[0,-%d]", frame_root, i + 1); if (i + 1 < inst->nr) { kvlangParam_t *arg = &inst->reads[i + 1]; - char *rk = resolve_read_path(kv, caller_fr, arg->name); bool concrete = !kvlangXvalueNone(&arg->val) && !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWIR) && !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWFUNC); char lt[256] = {0}; param_decl_type(kv, func_key, -(i + 1), lt, sizeof lt); + /* 传递方式由形参声明里的 `*` 定(见 spec [[函数]]):带 `*` 按地址(槽存实参地址 + * Ptr),不带按值(槽存实参值本体)。值传递直接落槽——字面量不必再造 ._litN 临时槽。 */ + if (lt[0] != '*') { + kvlangXvalue_t av; + kvlangXvalueZero(&av); + kvlangBuiltinResolveReadValue(kv, caller_fr, arg->name, &arg->val, &av); + if (!kvlangXvalueNone(&av)) { + pairs[np].key = kvlangStrbufDetach(&slot); + pairs[np].val = av; + np++; + } else { + kvlangStrbufFree(&slot); + } + continue; + } + char *rk = resolve_read_path(kv, caller_fr, arg->name); if (concrete) { /* 字面量无变量槽,一律写 ._litN;勿沿用 resolve_read_path 的返回值—— * 否则字面量内容(如 "https://x" 里的 //)会被当路径段,二次读回即丢。 */ @@ -576,7 +632,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, } if (rk) { kvlangXvalue_t rv; - kvlangXvalueNewPtr(&rv, lt, rk); + kvlangXvalueNewPtr(&rv, lt + 1, rk); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = rv; np++; @@ -585,24 +641,75 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, } kvlangStrbufFree(&slot); } + char *wds = NULL; /* 值写参目标表:第 i 行 = 第 i 个写参的目标 key(地址写参留空行) */ for (int i = 0; i < nw; i++) { kvlangStrbuf_t slot; kvlangStrbufInit(&slot); kvlangStrbufPrintf(&slot, "%s/[0,%d]", frame_root, i + 1); - if (i < inst->nw) { - char *wk = resolve_read_path(kv, caller_fr, inst->writes[i].name); - if (wk) { - char lt[256] = {0}; - param_decl_type(kv, func_key, i + 1, lt, sizeof lt); + char *wk = NULL; + if (i < inst->nw) + wk = resolve_read_path(kv, caller_fr, inst->writes[i].name); + char lt[256] = {0}; + param_decl_type(kv, func_key, i + 1, lt, sizeof lt); + const char *dst = NULL; + if (wk) { + if (lt[0] == '*' || lt[0] == '@') { + /* 地址写参:槽存调用方目标的地址 Ptr,体内 `*[0,+k]` 直写(现状) */ kvlangXvalue_t wv; - kvlangXvalueNewPtr(&wv, lt, wk); + kvlangXvalueNewPtr(&wv, lt + 1, wk); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = wv; np++; - free(wk); + } else { + /* 值写参:copy-in —— 把调用方该位置当前值拷进槽(累加器据此拿初值), + * 目标 key 记进 ‥wdst,返回时 copy-out 拷回。 */ + kvlangXvalue_t cur; + kvlangXvalueZero(&cur); + kvlangKvGetOne(kv, wk, &cur); + if (!kvlangXvalueNone(&cur)) { + pairs[np].key = kvlangStrbufDetach(&slot); + pairs[np].val = cur; + np++; + } else { + kvlangStrbufFree(&slot); + } + dst = wk; } + } else { + kvlangStrbufFree(&slot); } - kvlangStrbufFree(&slot); + /* 逐写参追加一行(空行为地址写参),行序即槽序 */ + { + kvlangStrbuf_t wd; + kvlangStrbufInit(&wd); + if (wds) + kvlangStrbufPuts(&wd, wds); + if (i) + kvlangStrbufPutc(&wd, '\n'); + if (dst) + kvlangStrbufPuts(&wd, dst); + free(wds); + wds = kvlangStrbufDetach(&wd); + } + free(wk); + } + if (wds) { + /* 至少有一个值写参才落表;全空表不必落(省掉返回期的一次读) */ + if (strspn(wds, "\n") != strlen(wds)) { + kvlangStrbuf_t wk; + kvlangStrbufInit(&wk); + kvlangStrbufPrintf(&wk, "%s/", frame_root); + kvlangStrbufPuts(&wk, RUNTIME_MEMBER_SEP "wdst"); + char *wks = kvlangStrbufDetach(&wk); + kvlangXvalue_t wv; + kvlangXvalueNewCharUtf8(&wv, wds); + kvlangKvPair_t wp = { wks, wv }; + char werr[256]; + kvlangKvSet(kv, &wp, 1, werr, sizeof werr); + kvlangXvalueFree(&wv); + free(wks); + } + free(wds); } /* 实参绑定写失败必须报错——绝不静默:丢的是一整个参数,症状会漂到很远的地方 * (曾表现为 fs 后端下函数读到空参数)。 */ @@ -663,15 +770,12 @@ static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, for (int bi = 0; ab && bi < al; bi++) { if (ab[bi] != 0) continue; - /* 声明的 `*T`/`@T` 只表引用性,**wire langtype 不带 `*`/`@` 前缀**(ref 归 head.ref)。 - * 剥掉再拿去构造实参 Ptr,否则 Ptr 目标类型串会多一个 `*` 而与真实值对不上 - * (曾致指针形参 validate_ptr 拒绝写入、帧槽整个参数丢失)。 */ - int s = bi + 1; - while (s < al && (ab[s] == '*' || ab[s] == '@')) - s++; - int tl = al - s; + /* 返回**原始声明串**(含 `*`/`@` 前缀):调用点据此判定传递方式——带 `*` + * 按地址(写实参地址 Ptr),不带按值(写值本体)。前缀只在构造 Ptr 时剥掉 + * (wire langtype 不含它,ref 归 head.ref)。 */ + int tl = al - (bi + 1); if (tl > 0 && tl < (int)cap) { - memcpy(lt, ab + s, (size_t)tl); + memcpy(lt, ab + bi + 1, (size_t)tl); lt[tl] = 0; } break; diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" index c68896d3..2260695e 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" @@ -32,8 +32,12 @@ lib kvlang/spec/kvspace模型/系统变量 { | `‥callpc` | 帧内执行进度(每 op 更新;块帧重入亦更新) | | `‥returnpc` | 返回地址(帧创建时固化;块帧仅首次设置,重入不覆写) | | `‥ro` | 只读参数名单,写槽检查用 | -| `‥rparam/{name}` | 读参重定向:存调用方值的绝对路径,读参从此路径直读(零拷贝) | -| `‥wparam/{name}` | 写参重定向:存调用方写目标的绝对路径,写参直写此路径 | +| `‥wdst` | **值写参的目标表**(仅当本帧有值写参时落):每行一个写参的调用方目标 key,行序即写参槽序,地址写参留空行。返回时据此 copy-out | + +**值写参的 copy-in / copy-out**(见 [[函数]]):调用点把调用方该位置的当前值**复制**进帧槽 +`[0,+k]`(累加器模式据此拿初值),并把目标 key 记进 `‥wdst`;被调方体内直接读写 `[0,+k]`;返回时 +runtime 按 `‥wdst` 把 `[0,+k]` **复制回**目标。地址写参(声明带 `*`/`@`)不走这套——槽里是调用方目 +标的地址 Ptr,体内 `*[0,+k]` 直写。 调用点如何写实参 / 结果槽、如何经指针链解析读到重定向路径,见 [[指令布局格式]]。 diff --git a/tutorial/09-debugger/chain_array.kv b/tutorial/09-debugger/chain_array.kv index c03ecf89..46604661 100644 --- a/tutorial/09-debugger/chain_array.kv +++ b/tutorial/09-debugger/chain_array.kv @@ -1,5 +1,5 @@ // 深层数组参数链路:main → f1 → f2 → f3 -// 在 f3 最深处 debugger() 暂停,可 dump kvspace 观察 ·rparam/·wparam 链 +// 在 f3 最深处 debugger() 暂停,可 dump kvspace 观察调用栈与参数槽 rwfunc f3() -> (a:int64, s:int64) { println(a) debugger() // 在此暂停 diff --git a/tutorial/10-types/06-any-read-param.kv b/tutorial/10-types/06-any-read-param.kv index 11106277..478166bc 100644 --- a/tutorial/10-types/06-any-read-param.kv +++ b/tutorial/10-types/06-any-read-param.kv @@ -1,3 +1,5 @@ +// wip —— 挂起:`any` 的传递方式待定(any 可能收到值容器,按值传没东西可拷)。 +// 待 `any` 规则敲定后去掉本行。参见 todo「any 参数传递方式」。 // 语义: any 读参接受任意类型实参——Ptr 的目标类型检查对 any 放行 // (any 是合法基类型,见 [[文法与合法性]];写参写 any 已可用)。 // 实测缺陷: any 读参一律 panic「[0,-1] is not a Ptr」——实参 Ptr 的 diff --git a/tutorial/10-types/08-value-vs-addr.kv b/tutorial/10-types/08-value-vs-addr.kv new file mode 100644 index 00000000..c7b5c996 --- /dev/null +++ b/tutorial/10-types/08-value-vs-addr.kv @@ -0,0 +1,26 @@ +// 语义: 两种传参由签名里的 `*` 决定,layout 与 runtime 完全对齐—— +// 写 `T` 按值(帧槽是调用方值的副本),写 `*T` 按地址(槽是调用方地址/句柄)。 +// 体内引用随之:值参 `[0,±k]` 直读直写,地址参 `*[0,±k]` 解引用。 +// 期望输出: +// inc r= 4 v= 3 +// bump v= 4 +// own v= 104 +rwfunc inc(x:int64) -> (r:int64) { + x = x + 1 + x -> r +} +rwfunc bump() -> (p:*int64) { + p + 1 -> p +} +rwfunc own() -> (t:int64) { + t = t + 100 +} +rwfunc test() -> () { + 3 -> v + inc(v) -> r + println("inc r=", r, " v=", v) + bump() -> v + println("bump v=", v) + own() -> v + println("own v=", v) +} From eb937b51e4ddd6be93ed3781f2f247bee872465e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 13:21:23 +0800 Subject: [PATCH 4/5] =?UTF-8?q?deps:=20=E5=8D=87=20kvspace=20=E4=B8=89?= =?UTF-8?q?=E4=BB=93=20=E2=86=92=20v0.2.17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 四仓协同 v0.2.17:map 容器 XValue::Map(MapValue) 重构(durable) + map langtype ·分隔 → INDEX storetype(kvspace 前端)。 Co-Authored-By: Claude Opus 4.8 --- deps.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deps.json b/deps.json index 5f9cd80c..3f977209 100644 --- a/deps.json +++ b/deps.json @@ -1,7 +1,7 @@ { - "kvspace": "v0.2.16", - "kvspace-c": "v0.2.16", - "kvspace-durable": "v0.2.16", + "kvspace": "v0.2.17", + "kvspace-c": "v0.2.17", + "kvspace-durable": "v0.2.17", "blockmalloc": "v0.1.4", "slotsboxmalloc": "v0.1.5" } From ee89227d18deda7b70ee70d1c24f17f0394329d8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 11 Sep 2026 13:35:02 +0800 Subject: [PATCH 5/5] =?UTF-8?q?benchmark:=20=E4=BF=AE=E5=A4=8D=20+=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20compact=20=E5=BD=A2=E5=BC=8F=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit binary_search 参数改指针传递;新增 matmul_compact / quicksort_compact 两个 compact 定长数组形式 case;run.py / README 配套修复。 Co-Authored-By: Claude Opus 4.8 --- benchmark/README.md | 2 + .../cases/binary_search/binary_search.kv | 2 +- .../cases/matmul_compact/matmul_compact.c | 32 +++++++++ .../cases/matmul_compact/matmul_compact.kv | 60 ++++++++++++++++ .../cases/matmul_compact/matmul_compact.py | 23 +++++++ .../cases/matmul_compact/matmul_compact.rs | 29 ++++++++ .../quicksort_compact/quicksort_compact.c | 44 ++++++++++++ .../quicksort_compact/quicksort_compact.kv | 69 +++++++++++++++++++ .../quicksort_compact/quicksort_compact.py | 32 +++++++++ .../quicksort_compact/quicksort_compact.rs | 35 ++++++++++ benchmark/run.py | 4 ++ 11 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 benchmark/cases/matmul_compact/matmul_compact.c create mode 100644 benchmark/cases/matmul_compact/matmul_compact.kv create mode 100644 benchmark/cases/matmul_compact/matmul_compact.py create mode 100644 benchmark/cases/matmul_compact/matmul_compact.rs create mode 100644 benchmark/cases/quicksort_compact/quicksort_compact.c create mode 100644 benchmark/cases/quicksort_compact/quicksort_compact.kv create mode 100644 benchmark/cases/quicksort_compact/quicksort_compact.py create mode 100644 benchmark/cases/quicksort_compact/quicksort_compact.rs diff --git a/benchmark/README.md b/benchmark/README.md index c90a06e7..e4684cf4 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -56,6 +56,8 @@ kvlang 是被测对象,**分别在三个 kvspace 后端上各跑一遍,占 | `k_nucleotide` | 字符串 + 哈希表 | 逐字符 `ord` 入哈希表统计碱基频次 | | `iops` | 最小寻址单元往返地板价 | 单 key 读-改-写 `a=a+1`,per-op 延迟(对齐 #204,参考基线) | | `prime_sieve` | 计算 / 控制流密集 | 嵌套 `while` + 取模,O(n²) 内层迭代(参考基线) | +| `matmul_compact` | **数组物理形态**(compact) | 同 `matmul` 算法,矩阵落单个 `[N,N]float64`(storetype=ARRAYND,元素连续打包),对照「每元素一个 key 的散 key map」 | +| `quicksort_compact` | **数组物理形态**(compact) | 同 `quicksort` 算法,待排数组与显式栈都落单个 `[N]int64`,全程在 compact 数组里直接交换 | kvlang 的性能瓶颈是「PC/帧/局部全落 KV 树、每步一次往返」的架构本质(见 kvlang#194 #204 #116), 不是某个热点函数;`iops`/`prime_sieve` 单独隔离出这条地板价,其余八例是跨语言等价算法对照。 diff --git a/benchmark/cases/binary_search/binary_search.kv b/benchmark/cases/binary_search/binary_search.kv index bba7f1a6..9fc5ba9b 100644 --- a/benchmark/cases/binary_search/binary_search.kv +++ b/benchmark/cases/binary_search/binary_search.kv @@ -2,7 +2,7 @@ // 建 arr[i]=i,对 0..N-1 逐个二分,累加命中下标;N 由 __SCALE__ 占位(勿改逻辑) // 期望输出(N=32 时): // bsearch: found = 32 sum = 496 -rwfunc bsearch(arr:[int64]·int64, n:int64, target:int64) -> (idx:int64) { +rwfunc bsearch(arr:*[int64]·int64, n:int64, target:int64) -> (idx:int64) { 0 -> lo n - 1 -> hi -1 -> idx diff --git a/benchmark/cases/matmul_compact/matmul_compact.c b/benchmark/cases/matmul_compact/matmul_compact.c new file mode 100644 index 00000000..fbb65411 --- /dev/null +++ b/benchmark/cases/matmul_compact/matmul_compact.c @@ -0,0 +1,32 @@ +#include +#include +#include + +int main(void) { + int n = atoi(getenv("BENCH_SCALE")); + double A[n * n], B[n * n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + int idx = i * n + j; + A[idx] = (double)((i + j) % 4) * 0.25 + 0.25; + B[idx] = (double)((i * 2 + j) % 4) * 0.25 + 0.25; + } + } + struct timespec t0, t1; + clock_gettime(CLOCK_MONOTONIC, &t0); + double checksum = 0.0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + double acc = 0.0; + for (int k = 0; k < n; k++) + acc += A[i * n + k] * B[k * n + j]; + checksum += acc; + } + } + clock_gettime(CLOCK_MONOTONIC, &t1); + long ns = (t1.tv_sec - t0.tv_sec) * 1000000000L + (t1.tv_nsec - t0.tv_nsec); + long out = (long)(checksum * 1000000.0); + printf("matmul_compact: check = %ld\n", out); + printf("__bench_ns: %ld\n", ns); + return 0; +} diff --git a/benchmark/cases/matmul_compact/matmul_compact.kv b/benchmark/cases/matmul_compact/matmul_compact.kv new file mode 100644 index 00000000..b53785a8 --- /dev/null +++ b/benchmark/cases/matmul_compact/matmul_compact.kv @@ -0,0 +1,60 @@ +// matrix multiplication benchmark(compact 形式)— 同 matmul 算法,矩阵落**单个 compact XValue** +// (`[N,N]float64`,storetype=ARRAYND,元素连续打包),而非散 key 的 `[int64]·float64` map。 +// 对比点:同一算法的两种 kv 物理形态——map 每次元素访问一次 KV 往返,compact 一次 xv·at/set。 +// 方阵阶 N 由 __SCALE__ 占位(O(N^3),勿改逻辑) +// 期望输出(N=6 时): +// matmul_compact: check = 82750000 +rwfunc test() -> () { + __SCALE__ -> n + A:[__SCALE__,__SCALE__]float64 = [] + B:[__SCALE__,__SCALE__]float64 = [] + 0 -> i + while (i < n) { + 0 -> j + while (j < n) { + i + j -> ta + ta % 4 -> qa + float64(qa) -> qaf + qaf × 0.25 -> pa + pa + 0.25 -> av + av -> A[i, j] + i × 2 -> i2 + i2 + j -> tb + tb % 4 -> qb + float64(qb) -> qbf + qbf × 0.25 -> pb + pb + 0.25 -> bv + bv -> B[i, j] + j + 1 -> j + } + i + 1 -> i + } + t0 = time·now() + 0.0 -> checksum + 0 -> i + while (i < n) { + 0 -> j + while (j < n) { + 0.0 -> acc + 0 -> k + while (k < n) { + A[i, k] -> av + B[k, j] -> bv + av × bv -> pr + acc + pr -> acc + k + 1 -> k + } + checksum + acc -> checksum + j + 1 -> j + } + i + 1 -> i + } + t1 = time·now() + checksum × 1000000.0 -> scaled + int64(scaled) -> out + println("matmul_compact: check =", out) + delta = time·sub(t1, t0) + ns = time/duration·as_nanos(delta) + println("__bench_ns:", ns) + println("__bench_input: N=__SCALE__") +} diff --git a/benchmark/cases/matmul_compact/matmul_compact.py b/benchmark/cases/matmul_compact/matmul_compact.py new file mode 100644 index 00000000..67c9d1d9 --- /dev/null +++ b/benchmark/cases/matmul_compact/matmul_compact.py @@ -0,0 +1,23 @@ +import os +import time + +n = int(os.environ["BENCH_SCALE"]) +A = [0.0] * (n * n) +B = [0.0] * (n * n) +for i in range(n): + for j in range(n): + idx = i * n + j + A[idx] = float((i + j) % 4) * 0.25 + 0.25 + B[idx] = float((i * 2 + j) % 4) * 0.25 + 0.25 +t0 = time.perf_counter_ns() +checksum = 0.0 +for i in range(n): + for j in range(n): + acc = 0.0 + for k in range(n): + acc += A[i * n + k] * B[k * n + j] + checksum += acc +t1 = time.perf_counter_ns() +out = int(checksum * 1000000.0) +print("matmul_compact: check =", out) +print("__bench_ns:", t1 - t0) diff --git a/benchmark/cases/matmul_compact/matmul_compact.rs b/benchmark/cases/matmul_compact/matmul_compact.rs new file mode 100644 index 00000000..c4c45d1a --- /dev/null +++ b/benchmark/cases/matmul_compact/matmul_compact.rs @@ -0,0 +1,29 @@ +use std::time::Instant; + +fn main() { + let n: usize = std::env::var("BENCH_SCALE").unwrap().parse().unwrap(); + let mut a = vec![0.0f64; n * n]; + let mut b = vec![0.0f64; n * n]; + for i in 0..n { + for j in 0..n { + let idx = i * n + j; + a[idx] = ((i + j) % 4) as f64 * 0.25 + 0.25; + b[idx] = ((i * 2 + j) % 4) as f64 * 0.25 + 0.25; + } + } + let t0 = Instant::now(); + let mut checksum = 0.0f64; + for i in 0..n { + for j in 0..n { + let mut acc = 0.0f64; + for k in 0..n { + acc += a[i * n + k] * b[k * n + j]; + } + checksum += acc; + } + } + let ns = t0.elapsed().as_nanos(); + let out = (checksum * 1000000.0) as i64; + println!("matmul_compact: check = {}", out); + println!("__bench_ns: {}", ns); +} diff --git a/benchmark/cases/quicksort_compact/quicksort_compact.c b/benchmark/cases/quicksort_compact/quicksort_compact.c new file mode 100644 index 00000000..e0e8ea18 --- /dev/null +++ b/benchmark/cases/quicksort_compact/quicksort_compact.c @@ -0,0 +1,44 @@ +#include +#include +#include + +static void swap(long *a, long i, long j) { + long t = a[i]; + a[i] = a[j]; + a[j] = t; +} + +static void qsort_r(long *a, long lo, long hi) { + if (lo < hi) { + long pivot = a[hi]; + long i = lo - 1; + for (long j = lo; j < hi; j++) { + if (a[j] <= pivot) { + i++; + swap(a, i, j); + } + } + swap(a, i + 1, hi); + long p = i + 1; + qsort_r(a, lo, p - 1); + qsort_r(a, p + 1, hi); + } +} + +int main(void) { + long n = atol(getenv("BENCH_SCALE")); + long arr[n]; + long seed = 1; + for (long i = 0; i < n; i++) { + seed = (seed * 1103515245 + 12345) % 2147483648; + arr[i] = seed % 100; + } + struct timespec t0, t1; + clock_gettime(CLOCK_MONOTONIC, &t0); + qsort_r(arr, 0, n - 1); + clock_gettime(CLOCK_MONOTONIC, &t1); + long ns = (t1.tv_sec - t0.tv_sec) * 1000000000L + (t1.tv_nsec - t0.tv_nsec); + printf("qsort_compact: a0 = %ld amid = %ld alast = %ld\n", arr[0], arr[n / 2], arr[n - 1]); + printf("__bench_ns: %ld\n", ns); + return 0; +} diff --git a/benchmark/cases/quicksort_compact/quicksort_compact.kv b/benchmark/cases/quicksort_compact/quicksort_compact.kv new file mode 100644 index 00000000..2b7649d7 --- /dev/null +++ b/benchmark/cases/quicksort_compact/quicksort_compact.kv @@ -0,0 +1,69 @@ +// quicksort benchmark(compact 形式)— 同 quicksort 算法,待排数组与显式栈都落**单个 compact +// XValue**(`[N]int64`,storetype=ARRAYND),而非散 key 的 `[int64]·int64` map。 +// 对比点:同一算法的两种 kv 物理形态。 +// 数据由 LCG 确定生成,规模 N 由 __SCALE__ 占位,run.py 按扫描点替换(勿改逻辑) +// 期望输出(N=64 时): +// qsort_compact: a0 = 1 amid = 52 alast = 99 +rwfunc test() -> () { + __SCALE__ -> n + arr:[__SCALE__]int64 = [] + 1 -> seed + 0 -> i + while (i < n) { + seed × 1103515245 -> s1 + s1 + 12345 -> s2 + s2 % 2147483648 -> seed + seed % 100 -> v + v -> arr[i] + i + 1 -> i + } + t0 = time·now() + // 显式栈也用 compact:与待排数组一样落单个 ARRAYND XValue,全程只在 compact 数组里直接交换。 + st_lo:[__SCALE__]int64 = [] + st_hi:[__SCALE__]int64 = [] + 0 -> top + 0 -> st_lo[top] + n - 1 -> st_hi[top] + top + 1 -> top + while (top > 0) { + top - 1 -> top + st_lo[top] -> lo + st_hi[top] -> hi + if (lo < hi) { + arr[hi] -> pivot + lo - 1 -> ii + lo -> jj + while (jj < hi) { + arr[jj] -> aj + if (aj <= pivot) { + ii + 1 -> ii + arr[ii] -> ai + aj -> arr[ii] + ai -> arr[jj] + } + jj + 1 -> jj + } + ii + 1 -> pp + arr[pp] -> app + pivot -> arr[pp] + app -> arr[hi] + lo -> st_lo[top] + pp - 1 -> st_hi[top] + top + 1 -> top + pp + 1 -> st_lo[top] + hi -> st_hi[top] + top + 1 -> top + } + } + t1 = time·now() + n ÷ 2 -> mid + n - 1 -> last + arr[0] -> a0 + arr[mid] -> am + arr[last] -> al + println("qsort_compact: a0 =", a0, "amid =", am, "alast =", al) + delta = time·sub(t1, t0) + ns = time/duration·as_nanos(delta) + println("__bench_ns:", ns) + println("__bench_input: N=__SCALE__,seed=1") +} diff --git a/benchmark/cases/quicksort_compact/quicksort_compact.py b/benchmark/cases/quicksort_compact/quicksort_compact.py new file mode 100644 index 00000000..401dc266 --- /dev/null +++ b/benchmark/cases/quicksort_compact/quicksort_compact.py @@ -0,0 +1,32 @@ +import os +import time +import sys + +sys.setrecursionlimit(10000) + + +def qsort(a, lo, hi): + if lo < hi: + pivot = a[hi] + i = lo - 1 + for j in range(lo, hi): + if a[j] <= pivot: + i += 1 + a[i], a[j] = a[j], a[i] + a[i + 1], a[hi] = a[hi], a[i + 1] + p = i + 1 + qsort(a, lo, p - 1) + qsort(a, p + 1, hi) + + +n = int(os.environ["BENCH_SCALE"]) +arr = [] +seed = 1 +for i in range(n): + seed = (seed * 1103515245 + 12345) % 2147483648 + arr.append(seed % 100) +t0 = time.perf_counter_ns() +qsort(arr, 0, n - 1) +t1 = time.perf_counter_ns() +print("qsort_compact: a0 =", arr[0], "amid =", arr[n // 2], "alast =", arr[n - 1]) +print("__bench_ns:", t1 - t0) diff --git a/benchmark/cases/quicksort_compact/quicksort_compact.rs b/benchmark/cases/quicksort_compact/quicksort_compact.rs new file mode 100644 index 00000000..917293a9 --- /dev/null +++ b/benchmark/cases/quicksort_compact/quicksort_compact.rs @@ -0,0 +1,35 @@ +use std::time::Instant; + +fn qsort(a: &mut [i64], lo: i64, hi: i64) { + if lo < hi { + let pivot = a[hi as usize]; + let mut i = lo - 1; + let mut j = lo; + while j < hi { + if a[j as usize] <= pivot { + i += 1; + a.swap(i as usize, j as usize); + } + j += 1; + } + a.swap((i + 1) as usize, hi as usize); + let p = i + 1; + qsort(a, lo, p - 1); + qsort(a, p + 1, hi); + } +} + +fn main() { + let n: i64 = std::env::var("BENCH_SCALE").unwrap().parse().unwrap(); + let mut arr: Vec = Vec::new(); + let mut seed: i64 = 1; + for _ in 0..n { + seed = (seed * 1103515245 + 12345) % 2147483648; + arr.push(seed % 100); + } + let t0 = Instant::now(); + qsort(&mut arr, 0, n - 1); + let ns = t0.elapsed().as_nanos(); + println!("qsort_compact: a0 = {} amid = {} alast = {}", arr[0], arr[(n / 2) as usize], arr[(n - 1) as usize]); + println!("__bench_ns: {}", ns); +} diff --git a/benchmark/run.py b/benchmark/run.py index 16d3239c..515ed2ec 100644 --- a/benchmark/run.py +++ b/benchmark/run.py @@ -61,6 +61,10 @@ "hash_table": [50, 100, 200], "matmul": [4, 6, 8], "k_nucleotide": [3, 5, 8], + # 同一算法的 compact 形态:数组落单个 ARRAYND XValue(`[N]T`),而非每元素一个 key 的散 key map。 + # 与 matmul / quicksort 同规模、同基线(native 三份与原 case 算法完全一致,仅输出标签不同)。 + "matmul_compact": [4, 6, 8], + "quicksort_compact": [32, 64, 128], } SCALE_ENV = "BENCH_SCALE" # python/rust/c 从此环境变量读规模 SCALE_TOKEN = "__SCALE__" # kvlang 源码里的规模占位符