From 3c96cf6bed065976ba53147b08f9ed25b4c2c311 Mon Sep 17 00:00:00 2001 From: array2d-gitter Date: Thu, 10 Sep 2026 14:10:12 +0800 Subject: [PATCH 1/3] =?UTF-8?q?runtime:=20=E6=8C=87=E9=92=88=E6=AF=94?= =?UTF-8?q?=E8=BE=83=E6=8C=89=20target=20=E4=B8=B2=E5=88=A4=E8=BA=AB?= =?UTF-8?q?=E4=BB=BD=EF=BC=88=E4=BB=85=20=3D=3D/!=3D=EF=BC=89+=20kvlangbri?= =?UTF-8?q?ef/ptr=20spec=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ptr 只支持 ==/!= 身份比较(target 串 strcmp,空指针 target="",另一方须 Ptr 或 char 哨兵);配套 layout/langtype/collection/kv builtins、kvlangbrief、 类型系统 16-ptr spec、inorder_traversal tutorial。 Co-Authored-By: Claude Opus 4.8 --- layout/src/code.rs | 4 + layout/src/langtype.rs | 7 + runtime/src/builtin.c | 16 + runtime/src/builtin_collection.c | 7 +- runtime/src/builtin_kv.c | 19 +- stdlib/kvlang/kvlangbrief.kv | 318 ++++++++++++------ .../16-ptr.kv" | 22 +- tutorial/08-leetcode/094_inorder_traversal.kv | 14 +- 8 files changed, 279 insertions(+), 128 deletions(-) diff --git a/layout/src/code.rs b/layout/src/code.rs index d3f91da0..27fa829b 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -715,6 +715,10 @@ pub fn write_struct_decl(kv: &mut Kv, decl: &StructDecl) { /// 字段默认值 XValue:head kind = 字段类型,body = 默认字面量(未给则零值)。 /// 标量+char 直接编码;带 dims / structref 仅记录类型(空 body),嵌套 struct 待定。 fn field_default(ty: &str, default: Option<&Expr>) -> Vec { + // *T 指针字段:默认空指针(ref=1、langtype=目标 kindexpr、body 空)。 + if let Some(target) = ty.strip_prefix('*') { + return ffi::new_ptr(target, ""); + } let (dims, base) = kvkind::parse_langtype(ty); let s = default.map(|e| e.val.clone()).unwrap_or_default(); if base.starts_with("char/") { diff --git a/layout/src/langtype.rs b/layout/src/langtype.rs index 3f63a449..a548e3b1 100644 --- a/layout/src/langtype.rs +++ b/layout/src/langtype.rs @@ -159,7 +159,11 @@ fn valid_key(s: &str) -> bool { } /// atom = shape | mapexpr | structref;mapexpr = key "·" type;structref = "/" path。 +/// 最前 `*` 是 ref 前缀(Ptr 存储位置),校验剥离后剩余部分(见 [[类型表达式文法]])。 fn valid_atom(s: &str) -> bool { + if let Some(rest) = s.strip_prefix('*') { + return !rest.is_empty() && valid_atom(rest); + } if s.starts_with('/') { return valid_structref(s); } @@ -185,6 +189,9 @@ pub fn expand_struct_refs(s: &str) -> String { } fn expand_atom(s: &str) -> String { + if let Some(rest) = s.strip_prefix('*') { + return format!("*{}", expand_atom(rest)); + } if s.starts_with('/') { return s.to_string(); } diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index 3b0e393e..5be0b344 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -366,6 +366,22 @@ static int kvlangBuiltinCmp(kvlangFrame_t *f, cmp_op op) { } else if (a.id == KVLANG_LT_BOOL && b.id == KVLANG_LT_BOOL) { bool av = kvlangScalarI64(a) != 0, bv = kvlangScalarI64(b) != 0; r = op == CMP_EQ ? av == bv : op == CMP_NEQ ? av != bv : op == CMP_LT ? av < bv : op == CMP_GT ? av > bv : op == CMP_LE ? av <= bv : av >= bv; + } else if (kvlangXvalueIsPtr(&in[0]) || kvlangXvalueIsPtr(&in[1])) { + // 指针比较恒判身份(target 串)。空指针 target="";另一方须是 Ptr 或 char 哨兵。 + if (op != CMP_EQ && op != CMP_NEQ) { + kvlangBuiltinSetErr(f, "TypeError: cannot order ptr with %s", kvlangXvalueKind(&in[0])); kvlangBuiltinFreeInputs(in, n); return -1; + } + if (!kvlangXvalueIsPtr(&in[0]) && !kvlangLtIsChar(a.id)) { + kvlangBuiltinSetErr(f, "TypeError: cannot compare %s with ptr", kvlangXvalueKind(&in[0])); kvlangBuiltinFreeInputs(in, n); return -1; + } + if (!kvlangXvalueIsPtr(&in[1]) && !kvlangLtIsChar(b.id)) { + kvlangBuiltinSetErr(f, "TypeError: cannot compare %s with ptr", kvlangXvalueKind(&in[1])); kvlangBuiltinFreeInputs(in, n); return -1; + } + char *as = kvlangXvalueIsPtr(&in[0]) ? kvlangXvaluePtrTarget(&in[0]) : kvlangXvalueValueString(&in[0]); + char *bs = kvlangXvalueIsPtr(&in[1]) ? kvlangXvaluePtrTarget(&in[1]) : kvlangXvalueValueString(&in[1]); + int c = strcmp(as, bs); + r = op == CMP_EQ ? c == 0 : c != 0; + free(as); free(bs); } else { kvlangBuiltinSetErr(f, "TypeError: cannot compare %s with %s", kvlangXvalueKind(&in[0]), kvlangXvalueKind(&in[1])); kvlangBuiltinFreeInputs(in, n); return -1; } diff --git a/runtime/src/builtin_collection.c b/runtime/src/builtin_collection.c index 4c2a1876..7862e219 100644 --- a/runtime/src/builtin_collection.c +++ b/runtime/src/builtin_collection.c @@ -568,7 +568,12 @@ int kvlangBuiltinStructNew(kvlangFrame_t *f) { const char *vk = kvlangXvalueKind(&in[i + 1]); kvspaceHead_t vh; kvlangXvalueHead(&in[i + 1], &vh); kvlangLangtype vkx; kvlangLangtypeParse(vh.langtype, &vkx); - if (ftype[0] && !kvlangLangtypeMatch(ftype, vk, vkx.ndim, vkx.dims)) { + // *T 指针字段:字段类型剥离前导 * 后与值的 langtype 比对,并校验值 ref=1。 + const char *fx = (ftype[0] == '*') ? ftype + 1 : ftype; + bool type_ok = ftype[0] ? kvlangLangtypeMatch(fx, vk, vkx.ndim, vkx.dims) : true; + if (ftype[0] == '*' && vh.ref != KVSPACE_REF_PTR) + type_ok = false; + if (ftype[0] && !type_ok) { rc = kvlangBuiltinSetErr(f, "TypeError: field %s: expected %s, got %s", fname, ftype, vk[0] ? vk : "None"); free(ftype); free(fname); break; } diff --git a/runtime/src/builtin_kv.c b/runtime/src/builtin_kv.c index cb20750f..2432dcc0 100644 --- a/runtime/src/builtin_kv.c +++ b/runtime/src/builtin_kv.c @@ -115,8 +115,23 @@ int kvlangBuiltinKvAbs(kvlangFrame_t *f) { kvlangXvalue_t in[1]; int n = kvlangBuiltinReadInputs(f, in, 1); char *p = n >= 1 ? resolve_path_arg(f, 0, in) : NULL; if (!p) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.abs requires a key"); } - kvlangXvalue_t r; kvlangXvalueNewCharUtf32(&r, p); - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); + /* 产出 Ptr(ref=1):langtype = 目标 kindexpr、body = 目标绝对路径。 + * &x ≡ kv.abs(x):取址返回指向 x 所在节点的软链接(单跳同型),不再产 char 路径串。 */ + kvlangXvalue_t tg; kvlangXvalueZero(&tg); + kvlangKvGetOne(f->kv, p, &tg); + char lt[256] = {0}; + if (!kvlangXvalueNone(&tg)) { + kvspaceHead_t h; + if (kvlangXvalueHead(&tg, &h) == 0 && h.langtype[0]) { + size_t llen = strlen((const char *)h.langtype); + if (llen > sizeof lt - 1) + llen = sizeof lt - 1; + memcpy(lt, h.langtype, llen); + } + } + kvlangXvalue_t r; kvlangXvalueNewPtr(&r, lt, p); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); kvlangXvalueFree(&tg); free(p); kvlangBuiltinFreeInputs(in, n); return rc; } diff --git a/stdlib/kvlang/kvlangbrief.kv b/stdlib/kvlang/kvlangbrief.kv index 0ca6d7e1..152c88c7 100644 --- a/stdlib/kvlang/kvlangbrief.kv +++ b/stdlib/kvlang/kvlangbrief.kv @@ -1,146 +1,250 @@ // 欢迎加入kvspace世界 -// kvlang 语法速览 —— 也是 KV 树里的数据。 +// kvlang 语法速览 —— 也是 KV 树里的数据,也是 LLM 生成 kvlang 代码时的权威提示。 // 本文件在 kvlang stdlib,runtime-rs 启动时 layout&run:顶层写语句包进 kvlang·init, -// 执行后把整份速览作为字符串落在 /lib/kvlang/kvlangbrief。 -// 之后 byteseek 的 llm·call 生成 kv 代码时,把这份速览拼进系统提示。 +// 执行后把整份速览作为字符串落在 /lib/kvlang/kvlangbrief,供 byteseek 的 llm·call 拼接进 system prompt。 lib kvlang { - r###"# kvlang 速览 + r###"# kvlang 速览(LLM 代码生成版) -kvlang 是一门全新语言,语法以下面示例为准,不要套用其它语言直觉。 +你是 kvlang 代码生成器:把用户需求翻译成一段 kvlang 程序。你只输出一个顶层函数体 +`rwfunc main() -> () { ... }`(不要 `main()` 调用、不要 `lib` 块、不要 markdown 围栏), +最终结果用 `println(...)` 打印。下面语法以示例为准,不要套用其它语言直觉。 -## 注释与字符串(对齐 Rust) -- 注释:`//` 行注释、`/* … */` 块注释(可嵌套)。**没有 `#` 注释**。 -- 字符串三形式: - - `"…"` 转义串(`\n \t \" \\` 等生效,可直接跨行)。 - - `r"…"` 原始串(零转义,内容不含 `"`)。 - - `r#"…"#` / `r##"…"##` 原始串带井号栏,内容可含 `"`;井号越多可嵌套越深。**没有 `"""` 三引号、没有反引号串**。 +## 0. 十条铁律(最影响正确率,先背下来) + +1. 乘用 `×`、除用 `÷`。`*` 不是乘(是取址/指针位),`/` 不是除(是路径/注释)。 +2. 没有 `int`/`float`/`char` 简写,也没有 `object`:数值写 `int64`/`float64` 等定宽;字符串恒 `[]char/utf32`;异构记录用 `{k=v}` 或 `struct`。 +3. 赋值两个方向:右写 `表达式 -> 槽`、左写 `槽 = 表达式`;`=` 不是表达式,判断相等用 `==`。 +4. 每条指令一行最清晰;`;` 与换行等价(`a = 1; b = 2` 合法)。 +5. 注释是 `//` 行注释、`/* */` 块注释;没有 `#` 注释。 +6. 字符串只有 `"…"`(转义)和 `r"…"` / `r#"…"#`(原始);没有 `"""` 三引号、没有反引号。 +7. 写参初值是 `None` 不是 0:累加前先 `0 -> acc`,否则 `None + x` 报错。 +8. 数组要带 `[]` 前缀:`a:[]int64 = [1,2,3]`;写成 `a:int64 = [..]` 会报错。空容器必须标类型(`d:[]char/utf32·int64 = {}`)。 +9. `+` 只允许同类:字符+字符=拼接,数值+数值=相加;`"标签" + 数字` 会 TypeError。带标签打印用 `println("answer =", n)` 多参数(自动空格)。 +10. 函数没有返回值,结果通过写参传出:`rwfunc f(x:int64) -> (r:int64) { x + 1 -> r }`;调用 `f(3) -> y`。 + +## 1. 骨架与常见任务 -## 程序结构 -文件入口约定是 `rwfunc test() -> () { … }`——`kvlang xxx.kv` 会自动运行 `test`,**不要**再手写 `test()` 调用。其它函数(含习惯叫的 `main`)必须被 `test` 直接或间接调用才会执行。 ```kv -rwfunc test() -> () { - total = 0 - 1 -> i // -> 写右边这个槽 - while (i <= 5) { total = total + i; i + 1 -> i } - println(total) // 15 +rwfunc main() -> () { + 3 + 4 -> s + println(s) // 7 } ``` -## 赋值三形式与调用 -- `x = e` / `x = e`:写左边的槽;`e -> x`:写右边的槽。`=` 不是表达式,不能嵌进条件里——条件相等判断一律用 `==`。 -- 调用只能通过写参映射拿结果,没有返回值:`f(a,b) -> r`;丢弃 `-> _`;多个 `-> x, y`。 -- 写槽必须是位置:裸名(帧局部)、`/绝对/路径`(全局)、`base·name`(成员)。字面量不能当写槽。 - -## rwfunc 读参/写参 -- 读参 `(a:int64)` 只读,函数体内不能把读参放进写槽(含 `a[i] = v`)。 -- 写参 `-> (acc:int64)` 体内可读可写——累加器、要被修改的数组都声明为写参。 -- **写参初值是 None,不是 0**:累加前必须先显式 `0 -> acc`,否则首次 `acc + x` 是 `None + x` 直接失败。 ```kv -lib mylib { rwfunc add(A:int64, B:int64) -> (C:int64) { A + B -> C } } -rwfunc sum(arr:[]int64) -> (acc:int64) { - 0 -> acc; 0 -> i - while (i < ndarray·numel(arr)) { xv·at(arr,i) -> e; acc + e -> acc; i + 1 -> i } -} -rwfunc test() -> () { - mylib·add(3,4) -> s; println(s) // 7 - a:[]int64 = [1,2,3,4,5]; sum(a) -> t; println(t) // 15 +rwfunc main() -> () { + // 字符串 + s = "hello" + string·len(s) -> n + println(n) // 5 + + // 数组求和 + a:[]int64 = [1,2,3,4] + 0 -> acc + 0 -> i + while (i < ndarray·numel(a)) { + xv·at(a, i) -> e + acc + e -> acc + i + 1 -> i + } + println(acc) // 10 + + // map 读写 + d:[]char/utf32·int64 = {} + d·a = 10 + d·b = 20 + kv·get(d, "a") -> x + println(x) // 10 } ``` -## 数值类型(只有定宽类型) -`int8/16/32/64 uint8/16/32/64 float32/64` 及 `char/utf8` 等;**没有 `int`/`float`**(会被拒)。 -构造器兼转换:`float32(3)`、`int8(300)`(窄化按补码回绕,float→int 向零截断)。变量声明 `x:int64 = 42`。 +## 2. 类型与构造器 + +数值定宽:`int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64`。 +其它:`bool`、`char/utf8` `char/utf32` `char/ascii`、`stringkeymap`、`index`、`struct`、`time`、`duration`、`any`。 -## 数组:两种存储形态(`[...]` compact / `{...}` stringkeymap 散 key) -计算机意义的数组(含多维)有两种物理形态,由字面量括号选择: -- **compact**(`[...]`):定长同类型元素连续打包进**单个 XValue**(head kind=元素类型),支持 `xv·at`/`ndarray·numel`/下标随机访问;多维 `[256,256]uint8` 即 ndarray/tensor。 -- **stringkeymap**(`{...}`):每元素落 `base·i` 独立 key(head kind=`stringkeymap`),变长/可增长,**字符串数组、可追加数组走这里**,用 `for-in` 遍历(`w:[]char/utf8 = {"foo","bar"}`)。 -```kv -a:[]int64 = [7,2,9,4] // ✅ compact:`[]` 前缀才是数组,裸 kind 是标量;写成 a:int64=[...] 会报错 -ndarray·numel(a) -> n // 4 -xv·at(a,2) -> e // 9(0 起) -xv·set(a,1,99) -> a // 改元素:a=[7,99,9,4] -``` -遍历/聚合用 `while + ndarray·numel + xv·at`。求最大值: ```kv -xv·at(a,0) -> hi; 1 -> i -while (i < ndarray·numel(a)) { xv·at(a,i) -> e; if (e > hi) { e -> hi }; i + 1 -> i } // hi=9 +float64(3) -> f // 3.0 +int64(3.9) -> i // 3(向零截断) +int64("42") -> n // 42 +bool(1) -> b // true +char/utf32(x) -> s // 字符串编码转换(如 char/utf8 → char/utf32) ``` -## 容器:stringkeymap / object(关键:`[]` 是数组专用,容器成员绝不用 `[]`) -空容器字面量 `{}` **必须带类型标注**,裸 `d = {}` 会被拒。两种容器: -- **stringkeymap**(一等 map 类型):kindexpr 用 `·` 分隔 `key·value`,`[]char/utf8·int64` = Go `map[string]int64`。 - key 侧恒 `[]char/` 字符串键,或 `[标量,…]` 元组键(如 `[int64]`、`[int32,int32]`,物理以字符串格式落 key);value 可递归嵌套(`[]char/utf8·[]char/utf8·int64`)。 -- **object**:异构命名成员(类 struct/record)。 +`string·formatint(n, base)` / `string·formatuint(n, base)` 整数按进制转字符串; +`string·parseint(s, base)` / `string·parseuint(s, base)` 字符串转整数(base 2..36)。 + +## 3. 运算符 + +算术:`+ - × ÷ %`。`÷` 两整数为整除(`7÷2`=3),任一侧浮点则浮点除。 +一元:`-x` `!b` `√x`(sqrt) `&x`(取址,得到指向 x 的软链接/指针)。 +比较:`== != < > <= >=`(`≠ ≤ ≥` 是等价字形)。 +逻辑/位:`&& || !`、`& | ^ << >>`。 +内建:`pow(x,y)` `sqrt(x)` `exp(x)` `log(x)` `abs(x)` `sign(x)` `neg(x)` `min(a,b,…)` `max(a,b,…)`。 +注意:`min`/`max` 是**标量变参**,不是数组;对数组求最值用 while 循环。 +数值保宽:同宽运算同宽(溢出回绕);混宽提升到更宽(`int32+int64 → int64`;混入浮点走浮点)。 + +## 4. 字符串(恒 `[]char/utf32`) + ```kv -d:[]char/utf8·int64 = {} // 空 map 必须标类型 -d·a = 10; d·b = 20 // 成员写(命名成员在 d·a) -kv·get(d, "a") -> x // 取值;缺失返回 None -k = "a"; d·*k -> v // 动态键:读 d·a -kv·set(d, k, 99) -> _ // 动态键写 -m:[int64]·int64 = {} // 整型键 map(key 落字符串) -kv·set(m, 0, 7) -> _; kv·get(m, 0) -> e -rec:object = {} // 异构命名成员 -rec·name = "kv"; rec·ver = 1 +s = "hello" +string·len(s) -> n // 5 +string·char(s, 1) -> c // "e"(单字符字符串) +string·ord(s, 0) -> o // 104(码点) +string·cmp("a","b") -> c // 相等返回 0 +string·find(s, "ll") -> i // 2;找不到 -1 +string·slice(s, 0, 2) -> p // "he" +s + "!" -> t // 拼接(同类才可) +s[1] -> x // "e";越界返回 "" +s[1] = "X" // 替换单字符,s 变 "hXllo" ``` -**容器成员一律走 `·` / `kv·get` / `kv·set`,绝不用 `[]`**——`[]` 只索引 compact array(见「数组」),对容器用 `[]` 会 layout 报错。 + +## 5. 数组(compact `[..]` 定长同型) + ```kv -/n1:object = {} // 跨函数共享的数据放绝对路径 -/n1·val = 1; /n1·next = "/n2" -"/n1" -> p; p·val -> v // ✅ 引号=路径串(指针)·member 解引用;不加引号 /n1 读的是值非路径 +a:[]int64 = [7,2,9,4] +ndarray·numel(a) -> n // 4(元素数) +ndarray·dim(a) -> d // 1(维数) +ndarray·shape(a) -> sh // 各轴长度 +xv·at(a, 2) -> e // 9(越界报错) +xv·set(a, 1, 99) -> b // 改元素,返回新数组 [7,99,9,4] +a[0] -> head // 7 +xv·langtype(a) -> lt // "[4]int64" +xv·bodylen(a) -> bl // 32(body 字节数) ``` -## 扩展世界:`@` 前缀 kindexpr -`@` 开头的 kindexpr 标记「元信息 key」——其真实 xvalue **不属于 kvspace 元存储**(kvspace 世界),而在 kvspace 的扩展世界。 -文件系统里的文件、GPU 里的 tensordata 都**不直接存进 kvspace**,kvspace 只存一条元信息(如大小、设备、外部路径),实体读写走扩展存储。 +遍历用 `while + ndarray·numel + xv·at`。多维 `[2,3]int64` 也是 compact ndarray。 +字符串数组 / 变长集合用 `{}` stringkeymap 形态(见下),不要用 `[]`。 +`array·append/slice/fill/scatter/compact` 当前行为未稳定,数组任务优先用 compact + 下标/`xv·*`。 + +## 6. 容器:stringkeymap / map / struct(成员一律用 `·`,绝不用 `[]`) -## 控制流(只能在 rwfunc 体内) -`while (c) {…}`、`if (c) {…} else {…}`。条件可为复合表达式。数组遍历用 `while` 配 `ndarray·numel`/`xv·at`(见上),不要用 for-in。 +```kv +d:[]char/utf32·int64 = {} // 空 stringkeymap 必须标类型 +d·a = 10 // 静态成员写 +d·b = 20 +kv·get(d, "a") -> x // 读(缺失返回 None) +k = "a" +d·*k -> v // 动态键读 +kv·set(d, "c", 30) -> _ // 动态键写 + +m = map() // stringkeymap +m·k = 5 + +m2:[int64]·int64 = {} // 整型键 map(键落字符串) +kv·set(m2, 0, 7) -> _ + +r = {name="kv", ver=1} // struct 字面量(异构记录;没有 object) +r·name -> n +5 -> r·ver // struct 成员可写 +``` -## 运算符(注意乘除号) -算术 `+ - × ÷ %`:**乘用 `×`、除用 `÷`,不要用 `*` `/`**(`/` 只用于路径,`*` 保留)。 -`÷`:两整数为整除(`7÷2`=3),任一为浮点则浮点除。比较 `== != < > <= >=`,逻辑 `&& || !`。 +命名 struct(需要字段默认值 / 复用类型时): -## IO 与字符串 -`print` / `println` 可用(扩展 rwir)。字符串用 `+` 拼接: ```kv -s = "hello" -string·len(s) -> n // 5 -string·char(s,1) -> c // 'e'(读第 i 个字符) -string·slice(s,0,2) -> p // "he" -string·find(s,"ll") -> i // 2(找不到返回 -1) -// 替换第 i 个字符:用 slice 拼接,不要用 string·char(s,i)=x -string·slice(s,0,1) + "a" -> t; string·slice(s,2,5) -> u; t + u -> r // "hallo" +struct Point { x:int64=0 y:int64=0 } +rwfunc main() -> () { + p:Point = {x=3 y=4} + println(p·x, p·y) // 3 4 +} ``` -**`+` 只在同类别内成立(数值 vs 字符不可混)**:两边都是字符=拼接,两边都是数值=相加;数值与字符跨类别会 TypeError。 -数值类别内允许跨位宽混算:`int32 + int64`、`float32 + int64` 均成立,结果隐式提升到更宽的类型(`int32+int64 → int64`;混入浮点则走浮点)。 -`"标签" + 数字` 会 TypeError(`expected numeric, got char/utf32`)。 -打印带标签的数字,用 `println` 多参数(自动空格分隔),**不要**拼字符串: +跨函数共享的数据放绝对路径:`/n1·val = 1`;取槽的地址用 `&x`,按 `p·member` 解引用。 + +## 7. 控制流(只能在 rwfunc 体内) + ```kv -n = 42 -println("answer =", n) // answer = 42(✅ 两个参数) -// "answer = " + n -> s; println(s) // ❌ TypeError:char + int +if (c) { ... } else if (c2) { ... } else { ... } +while (c) { ... } +for (e in arr) { ... } // 遍历 compact 数组 +break / continue +return // 无返回值 ``` -## 自举:代码即数据(生成/校验/入库/运行) -kv 代码是 KV 树里的数据,可运行时自产自运行: +map 遍历用 `while + kv·listlen + kv·listn`(for-in 对 map 不可靠)。 + +## 8. 函数(lib 内定义;跨库调用走全路径) + ```kv -kvlanglayout·vet(src) -> v // 校验源码:合法 "ok",否则错误信息 -kvlanglayout·format(src) -> f // 规范化源码(parse → 格式化文本) -kvlanglayout·layout(src) -> e // 源码入库 /lib:成功空串,失败 "error: …" -kvlanglayout·dump("/lib") -> d // /lib 子树导出为可运行源码(含 // 槽位注释) -vthread·call("pkg·func") // 在当前 vthread 直接调用已入库函数到结束再续跑(裸名 = /lib/<名>) -vthread·create("pkg·func") -> vid // 新开独立子 vthread(只创建不运行),返回 vid 句柄 -vthread·run(vid) // 驱动某 vid(由 create 得到)到结束——参数是 vid,不是函数名 +lib mylib { + rwfunc add(a:int64, b:int64) -> (c:int64) { a + b -> c } +} +rwfunc main() -> () { + mylib·add(3,4) -> s + println(s) // 7 +} ``` -## 调试 / 暂停 +读参只读(不能放进写槽);写参可读可写(初值 None,先置初值);变参 `x:int64...` 仅限末位读参。 + +## 9. 完整内建/rwir 速查(runtime-rs) + +### 输出/输入(term) +- `print(x,…)` 无空格无换行;`println(x,…)` 空格分隔并换行;`cerr(x,…)` 同 println 但写 stderr。 +- `printf(fmt,…)` C 风格格式化,支持 `%d %i %u %o %x %X %f %e %g %c %s %`;不自动换行(换行写 `\n`)。 +- `input(prompt) -> line` 读一行 stdin。 + +### JSON +- `json·to(path) -> str`:path 处值转 JSON 文本(字符串会加引号转义,常用于拼 JSON 请求体)。 +- `json·from(str) -> root`:JSON 文本解析写入 root 子树(覆盖语义)。 + +### 网络 +- `http·call(method, header, url, body) -> resp`;`http·get(url)`/`post(url,body)`/`put(url,body)`/`del(url)`。 + +### 自举(kvlanglayout) +- `kvlanglayout·vet(src) -> "ok"/err`:只校验,不写库。 +- `kvlanglayout·format(src) -> 规范化源码`。 +- `kvlanglayout·layout(src) -> entry`:源码 layout 入库,返回入口。 +- `kvlanglayout·dump("/lib") -> 源码`:/lib 子树导出为可运行源码。 + +### 外部进程/文件系统(networld) +- `networld/proc·exec(args, envs) -> code[, out, err]`:args/envs 是 `{...}` 数组字面量,args 首元素=可执行文件;out/err 是 `@[]uint8` 捕获句柄(绑定即捕获)。 +- `networld/fs·size(p)->n`、`·read(p,start,len)->raw`、`·write(p,bytes)->n`、`·append(p,bytes)->n`、`·list(p)->names`、`·del(p)->code`、`·mkdir(p)->code`、`·exists(p)->b`。 +- 字符串↔字节:`xv·reinterpret("s","[]uint8")` 或 `xv·reinterpret(raw,"[]char/utf8")`。 + ```kv -debugger() // 内联断点:执行到此暂停(status 置 paused) -vthread·setstatus("paused") // 等价:设 vthread 状态(paused 即停住) +noenv = map() +a = {"sh", "-c", "echo hi; echo oops 1>&2"} +networld/proc·exec(a, noenv) -> code, out, err +println(code, out, err) +networld/fs·write("/tmp/x.txt", xv·reinterpret("hi", "[]uint8")) -> n ``` -暂停后由外部把 `/vthread/{vid}/·status` 改回 `running` 续跑。 + +### KV 树(kv·*) +- `kv·get(path)`、`kv·set(path,val)`、`kv·del(path)`、`kv·deltree(path)`。 +- `kv·list("/dir/")`、`kv·listlen("/dir/")`、`kv·listn("/dir/",i)`(列目录 path 要带尾 `/`)。 +- `kv·abs(x)`(= `&x`,取址)、`kv·cp(src,dst)`、`kv·cpdir(src,dst)`、`kv·cplist(src,dst)`、`kv·has(path)`。 + +### 字符串(string·*) +- `len / char / ord / cmp / find / slice / concat / formatint / formatuint / parseint / parseuint`。 + +### 数组(ndarray·* / xv·*) +- `ndarray·numel / dim / shape`;`xv·at / set / reshape / reinterpret / langtype / bodylen`。 + +### 数学 +- 运算符见上;`pow / sqrt / exp / log / abs / sign / neg / min / max`;常量 `/lib/math·Pi` `/lib/math·E` `/lib/math·Tau`。 + +### 时间/时长/随机 +- `time·now()`、`time·sub/add/before/after`;`time/duration·nanos/millis/seconds/minutes/hours(...)`、`time/duration·as_nanos/...`。 +- `random·intn(n)`、`random·uint64()`、`random·int63()`。 + +### 线程/调试 +- `vthread·create(funckey)->vid`、`vthread·run(vid)`、`vthread·call(funckey)`、`vthread·sleep(dur)`、`vthread·setstatus("paused")`。 +- `debugger()` 内联断点(非调试模式 no-op)。 + +## 10. 常见错误对照表 + +| 想写 | 错 | 对 | +|------|-----|-----| +| 乘法 | `a * b` | `a × b` | +| 除法 | `a / b` | `a ÷ b` | +| 整数类型 | `int` | `int64` | +| 三引号串 | `"""..."""` | `r#"..."#` | +| 井号注释 | `# x` | `// x` | +| 异构记录 | `object` | `{k=v}` 或 `struct` | +| 数组类型 | `a:int64=[..]` | `a:[]int64=[..]` | +| 写参累加 | 直接 `acc + x -> acc` | 先 `0 -> acc` | +| 标签+数字 | `"x=" + 1` | `println("x=", 1)` | +| 返回结果 | `return 42` | 写参传出 | "### -> /lib/kvlang/kvlangbrief } - diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" index c1e52684..250e1472 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" @@ -3,18 +3,22 @@ lib kvlang/spec/类型系统/ptr { r####"# ptr(软链接) **ptr** 不是种类,而是 head 的 `ref=1`(存储位置维):ptr 值的 head storetype/langtype 描述**目标的 -完整形态**,body 存**目标 key 路径**。源码类型标注最前可写 `*` 前缀(如 `*int64`)表 ref,layout 剥 -离后落成 head `ref` 独立字节;wire 层 langtype 串不含前缀(见 [[总体方案]]);赋值(Set)时据 head -的 storetype/langtype 做单跳类型检查。 +完整形态**,body 存**目标 key 路径**。源码类型标注最前可写 `*` 前缀(如 `*int64`)表 ref,`*` 后可 +跟任意 langtype 基(kind 或 structref,如 `*Node`、`[int64]·*Node`);layout 剥离后落成 head `ref` +独立字节;wire 层 langtype 串不含前缀(见 [[总体方案]]);赋值(Set)时据 head 的 storetype/langtype +做单跳类型检查。 -跨函数共享的数据放绝对路径,指针变量存其路径字符串: +跨函数共享的数据放绝对路径,指针变量/字段存其路径字符串: ```kv -/n1 = Node{} // Node 为 struct 原型(含 val:int64、next:[]char/utf8 字段) -/n1·val = 1 -/n1·next = "/n2" // next 存目标路径(指针) -"/n1" -> p // 引号 = 路径串(指针) -p·val -> v // 按值解引用:读 /n1·val +struct Node { + val:int64=0 + next:*Node="" // 指针字段:ref=1 + langtype=/lib/Node,body 存目标路径 +} +/n1 = Node{val=1} // 绝对路径建节点 +/n2 = Node{val=2 next=/n1} // next 存 /n1 绝对路径(指针) +/n1 -> p // p 为指针变量(存路径串) +p·val -> v // 按值解引用:读 /n1·val ``` 局部变量/容器成员取指针用一元前缀 `&`(`&x ≡ kv·abs(x)`,返回绝对路径串),与 `*` ref 标注互补—— diff --git a/tutorial/08-leetcode/094_inorder_traversal.kv b/tutorial/08-leetcode/094_inorder_traversal.kv index 3e691ca7..56ae9f08 100644 --- a/tutorial/08-leetcode/094_inorder_traversal.kv +++ b/tutorial/08-leetcode/094_inorder_traversal.kv @@ -30,13 +30,9 @@ rwfunc inorder(root:[]char/utf32) -> () { } rwfunc test() -> () { - t:[int64]·TNode = {} - kv·abs(t) -> tb - tb + "·2" -> t1 - tb + "·3" -> t2 - t·1:TNode = {val=1 left=t1 right=t2} - t·2:TNode = {val=2} - t·3:TNode = {val=3} - tb + "·1" -> root + n2:TNode = {val=2} + n3:TNode = {val=3} + n1:TNode = {val=1 left=&n2 right=&n3} + &n1 -> root inorder(root) -} +} \ No newline at end of file From 76164902d605d5f14ff052768863de33ef2cf77f Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Thu, 10 Sep 2026 19:51:29 +0800 Subject: [PATCH 2/3] =?UTF-8?q?layout/runtime:=20link=20=E4=B8=8D=E9=9A=90?= =?UTF-8?q?=E5=BC=8F=E7=A9=BF=E9=80=8F=EF=BC=8C=E6=8C=87=E9=92=88=E8=A7=A3?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E6=98=BE=E5=BC=8F=E5=8C=96=20+=20=E9=93=BE?= =?UTF-8?q?=E8=A1=A8=20tutorial=20=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layout code.rs/kvkind/langtype/parser + runtime builtin/kv/kvcpu 配合 后端 set/del 整键不穿透;spec 16-ptr/09-调用extindex/CABI 与 leetcode 链表、struct 链表 tutorial 同步。 Co-Authored-By: Claude Opus 4.8 --- layout/src/code.rs | 122 +++++++++++------- layout/src/kvkind.rs | 21 +++ layout/src/langtype.rs | 5 +- layout/src/parser.rs | 5 + runtime/src/builtin.c | 64 ++++----- runtime/src/builtin_kv.c | 20 ++- runtime/src/kv.c | 9 +- runtime/src/kvcpu.c | 75 ++++++----- .../04-CABI.kv" | 5 + ...03\345\261\200\346\240\274\345\274\217.kv" | 9 +- .../09-\350\260\203\347\224\250extindex.kv" | 34 +++-- ...13\350\241\250\350\276\276\345\274\217.kv" | 2 +- .../16-ptr.kv" | 26 +++- ...16\347\255\276\345\220\215\350\241\214.kv" | 14 +- .../03-\345\207\275\346\225\260.kv" | 4 +- ...75\344\270\216\345\270\203\345\261\200.kv" | 10 +- ...70\203\345\261\200\344\270\216def-rwir.kv" | 8 +- ...75\346\225\260\350\260\203\347\224\250.kv" | 32 ++--- ...00\345\217\221\350\247\204\350\214\203.kv" | 4 +- ...5-\350\241\250\350\276\276\345\274\217.kv" | 5 +- tutorial/08-leetcode/002_add_two_numbers.kv | 4 +- tutorial/08-leetcode/021_merge_two_lists.kv | 4 +- tutorial/08-leetcode/083_remove_dup_list.kv | 6 +- tutorial/08-leetcode/094_inorder_traversal.kv | 8 +- tutorial/08-leetcode/100_same_tree.kv | 20 +-- tutorial/08-leetcode/104_max_tree_depth.kv | 17 +-- tutorial/08-leetcode/141_linked_list_cycle.kv | 4 +- tutorial/08-leetcode/160_intersection_list.kv | 4 +- .../08-leetcode/203_remove_linked_elements.kv | 4 +- .../08-leetcode/206_reverse_linked_list.kv | 4 +- tutorial/08-leetcode/234_palindrome_list.kv | 4 +- tutorial/12-struct/03-linked-list.kv | 2 +- tutorial/12-struct/06-list-reverse.kv | 2 +- tutorial/12-struct/07-list-local.kv | 2 +- 34 files changed, 315 insertions(+), 244 deletions(-) diff --git a/layout/src/code.rs b/layout/src/code.rs index 27fa829b..a1cadbad 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -2,18 +2,20 @@ //! //! 存储约定: //! /lib/·/[0,0] 布局后签名锚点(kind=rwfunc,body=计数头 [nr,nw,dyn]) -//! /lib/·/ 命名参数→slot 指针(langtype=该参类型, ref=1, body=坐标) +//! /lib/·.[0,±k] 参数定义键(langtype=def langtype, body=名字\x00类型) //! /lib/·/[i,j] 编译后指令(kind=rwir),i 从 1 开始 //! /lib/·/‥labels/ label → irseq //! /lib/·.src 源码副本(仅 write_func 保留写入,dump 不再依赖) //! //! WriteBody: DFS-number insts (incl. ScopeStmt), emit [i,j], rewrite goto/br labels to irseq. -//! dump: 反向——严格读 /lib/ 子树重建 AST(签名读命名参数 Ptr、体读线性槽+‥labels), +//! dump: 反向——严格读 /lib/ 子树重建 AST(签名读参数定义键 .[0,±k]、体读线性槽+‥labels), //! 不读 .src、不依赖签名行 [0,x] 静态槽。 use std::collections::HashMap; -use super::ast::{self, Expr, Func, FuncSig, Instruction, Param, RwirDecl, ScopeStmt, Stmt, StructDecl}; +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}; @@ -387,7 +389,7 @@ fn reconstruct(kv: &mut Kv, dir: &str, name: &str) -> String { .full_text() } -/// 从命名参数 Ptr 键重建签名:类型取 Ptr langtype,读/写与序取 body 坐标 [0,±k]。 +/// 从参数定义键 base.[0,±k](点后缀)重建签名:body=名字\x00类型串。 fn reconstruct_sig(kv: &mut Kv, dir: &str, name: &str, nr: i32, nw: i32, dynamic: bool) -> FuncSig { let blank = || Param { name: String::new(), @@ -395,26 +397,24 @@ fn reconstruct_sig(kv: &mut Kv, dir: &str, name: &str, nr: i32, nw: i32, dynamic }; let mut params: Vec = (0..nr).map(|_| blank()).collect(); let mut returns: Vec = (0..nw).map(|_| blank()).collect(); - for c in kv.list(dir, false, true) { - if c.starts_with('[') || c.ends_with('/') || c.starts_with(keytree::RUNTIME_MEMBER_SEP) { - continue; // 指令槽 / labels 目录 / 运行时保留字段 - } - let data = kv.get_one(&format!("{dir}{c}")); - if !kvkind::is_ptr(&data) { - continue; + let base = dir.trim_end_matches('/'); + for k in 1..=nr { + if let Some((pname, pty)) = + kvkind::def_param_parts(&kv.get_one(&format!("{base}.[0,-{k}]"))) + { + params[(k - 1) as usize] = Param { + name: pname, + ty: pty, + }; } - let ty = kvkind::langtype(&data); - let (is_read, k) = match parse_slot_coord(&kvkind::ptr_target(&data)) { - Some(v) => v, - None => continue, - }; - let slot = Param { - name: c.clone(), - ty, - }; - let dst = if is_read { &mut params } else { &mut returns }; - if k >= 1 && (k as usize) <= dst.len() { - dst[k as usize - 1] = slot; + } + for k in 1..=nw { + if let Some((rname, rty)) = kvkind::def_param_parts(&kv.get_one(&format!("{base}.[0,{k}]"))) + { + returns[(k - 1) as usize] = Param { + name: rname, + ty: rty, + }; } } if dynamic { @@ -431,23 +431,21 @@ fn reconstruct_sig(kv: &mut Kv, dir: &str, name: &str, nr: i32, nw: i32, dynamic } } -/// 解析槽坐标 `[0,-1]`/`[0,1]` → (是否读参, |k|)。 -fn parse_slot_coord(s: &str) -> Option<(bool, i32)> { - let inner = s.strip_prefix('[')?.strip_suffix(']')?; - let col = inner.split(',').nth(1)?.trim(); - let n: i32 = col.parse().ok()?; - Some((n < 0, n.abs())) -} - /// ‥labels/ 子树 → (irseq, label),按 irseq 升序(体切块用)。 fn read_labels(kv: &mut Kv, dir: &str) -> Vec<(i32, String)> { - let ldir = format!("{dir}{}{}/", keytree::RUNTIME_MEMBER_SEP, keytree::SEG_LABELS); + let ldir = format!( + "{dir}{}{}/", + keytree::RUNTIME_MEMBER_SEP, + keytree::SEG_LABELS + ); let mut out: Vec<(i32, String)> = kv .list(&ldir, false, true) .into_iter() .filter(|c| !c.ends_with('/')) .filter_map(|c| { - let irseq: i32 = kvkind::plain(&kv.get_one(&format!("{ldir}{c}"))).parse().ok()?; + let irseq: i32 = kvkind::plain(&kv.get_one(&format!("{ldir}{c}"))) + .parse() + .ok()?; Some((irseq, c)) }) .collect(); @@ -566,10 +564,7 @@ fn build_inst(inst: &RawInst, by_irseq: &HashMap) -> Instruction { "=" => inst.reads.first().map(operand_expr), "goto" => Some(ast::call( "goto", - inst.reads - .iter() - .map(|o| label_leaf(o, by_irseq)) - .collect(), + inst.reads.iter().map(|o| label_leaf(o, by_irseq)).collect(), )), "br" => { let mut args = Vec::with_capacity(inst.reads.len()); @@ -631,6 +626,15 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { let mut labels: HashMap = HashMap::new(); collect_insts(&fn_.body, &mut seq, &mut labels); + // 参数名 → *[0,±k](显式解引用坐标):函数体形参引用编译期替换,删命名参数 Ptr 运行时角色。 + 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)); + } + for (i, r) in fn_.sig.returns.iter().enumerate() { + param_coord.insert(r.name.clone(), format!("*[0,{}]", i + 1)); + } + // 按函数覆盖(文件夹复制式合并):只 del_tree 本函数子树,不动 /lib 下其它函数。 // 禁止整库删除——layoutcode 必须可增量:多次 layout 各自覆盖其函数,不误删先前的函数。 let _ = kv.del_tree(&func_dir); @@ -649,26 +653,33 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { keytree::lib_src(pkg, &fn_.sig.name), ffi::new_char_byte(fn_.full_text().as_bytes()), )); - // 命名参数键 funcDir/:Ptr,body=帧坐标 [0,±k](k<0 读参、k>0 写参), - // target_langtype=该参类型——类型随参数名承载。func dir 根不落 [0,±k] 静态槽: - // 那些坐标是 runtime call 期写入的帧本地实参地址,若 layout 提前落在 func dir 根, - // 建帧 extindex 时会把它们当只读扩展节点,令 call 期同坐标绑定触发 ext-write 保护。 + // 参数定义键 funcDir.[0,±k](点后缀,与坐标斜杠键 /[0,±k] 区分):langtype=def langtype, + // body=名字\x00类型串。坐标斜杠键 [0,±k] 留给 runtime call 期写实参地址 Ptr;点后缀键 + // 不同名、不触发 extindex 写保护。函数体形参引用已替换为 *[0,±k] 显式解引用。 for (i, p) in fn_.sig.params.iter().enumerate() { pairs.push(( - format!("{func_dir}/{}", p.name), - ffi::new_ptr(¶m_types[i], &format!("[0,-{}]", i + 1)), + format!("{func_dir}.[0,-{}]", i + 1), + kvkind::new_def_param(&p.name, ¶m_types[i]), )); } for (i, r) in fn_.sig.returns.iter().enumerate() { pairs.push(( - format!("{func_dir}/{}", r.name), - ffi::new_ptr(¶m_types[nr as usize + i], &format!("[0,{}]", i + 1)), + format!("{func_dir}.[0,{}]", i + 1), + kvkind::new_def_param(&r.name, ¶m_types[nr as usize + i]), )); } let _ = kv.set(&pairs); for (i, inst) in seq.iter().enumerate() { - write_linear_inst(kv, &func_dir, (i as i32) + 1, inst, &labels, &mut type_map); + write_linear_inst( + kv, + &func_dir, + (i as i32) + 1, + inst, + &labels, + &mut type_map, + ¶m_coord, + ); } if !labels.is_empty() { let _ = kv.mkindex(&keytree::lib_labels_dir(pkg, &fn_.sig.name)); @@ -757,8 +768,10 @@ pub fn write_rwir_decl(kv: &mut Kv, decl: &RwirDecl) { let param_types = decl.sig.langtype_list(); let base = keytree::rwir(&opcode); // 路由头:仅计数头(无参数载荷);各参数类型落 [0,x] 签名行槽(def langtype)。 - let mut pairs: Vec<(String, Vec)> = - vec![(base.clone(), kvkind::new_defrwir(nr, nw, decl.sig.dynamic()))]; + let mut pairs: Vec<(String, Vec)> = vec![( + base.clone(), + kvkind::new_defrwir(nr, nw, decl.sig.dynamic()), + )]; for i in 0..nr as usize { pairs.push(( format!("{base}/[0,-{}]", i + 1), @@ -808,6 +821,7 @@ fn write_linear_inst( s: &Instruction, labels: &HashMap, type_map: &mut HashMap, + params: &HashMap, ) { for (j, w) in s.writes.iter().enumerate() { if j < s.write_types.len() && !s.write_types[j].is_empty() { @@ -845,13 +859,21 @@ fn write_linear_inst( pairs.push((format!("{prefix}/[{n},0]"), opcode_value(&opcode))); } for (j, r) in reads.iter().enumerate() { + let rv = params + .get(r.as_str()) + .map(String::as_str) + .unwrap_or(r.as_str()); pairs.push(( format!("{prefix}/[{n},-{}]", j + 1), - slot_value(r, target_char), + slot_value(rv, target_char), )); } for (j, w) in s.writes.iter().enumerate() { - pairs.push((format!("{prefix}/[{n},{}]", j + 1), slot_value(w, ""))); + let wv = params + .get(w.as_str()) + .map(String::as_str) + .unwrap_or(w.as_str()); + pairs.push((format!("{prefix}/[{n},{}]", j + 1), slot_value(wv, ""))); } if !pairs.is_empty() { let _ = kv.set(&pairs); diff --git a/layout/src/kvkind.rs b/layout/src/kvkind.rs index bad048b7..a6b4f66f 100644 --- a/layout/src/kvkind.rs +++ b/layout/src/kvkind.rs @@ -260,6 +260,27 @@ pub fn new_def_langtype(langtype: &str) -> Vec { ffi::tlv_encode(KIND_DEF_LANGTYPE, langtype.as_bytes(), 1) } +/// rwfunc 参数定义键(点后缀 .[0,-k]):body=名字\x00类型串,langtype=def langtype。 +pub fn new_def_param(name: &str, langtype: &str) -> Vec { + let mut body = name.as_bytes().to_vec(); + body.push(0); + body.extend_from_slice(langtype.as_bytes()); + ffi::tlv_encode(KIND_DEF_LANGTYPE, &body, 1) +} + +/// 解析参数定义键 body(名字\x00类型串)→ (名字, 类型)。 +pub fn def_param_parts(data: &[u8]) -> Option<(String, String)> { + if data.is_empty() { + return None; + } + let h = ffi::decode_head(data); + let b = body(data, &h); + let nul = b.iter().position(|&x| x == 0)?; + let name = String::from_utf8_lossy(&b[..nul]).into_owned(); + let ty = String::from_utf8_lossy(&b[nul + 1..]).into_owned(); + Some((name, ty)) +} + // ── struct 原型(对齐 runtime kvlangBuiltinMemindex)───────────────── // // /lib/Name kind=struct,body="name:langtype\n..."(字段声明类型,供实例化类型校验) diff --git a/layout/src/langtype.rs b/layout/src/langtype.rs index a548e3b1..de7c6071 100644 --- a/layout/src/langtype.rs +++ b/layout/src/langtype.rs @@ -182,10 +182,7 @@ pub fn valid_langtype(expr: &str) -> bool { /// `/lib/`,使 kv 源可写 `x:Node` / `[int64]·Node`,runtime 恒收到完整 `/lib/…` 路径。 /// 已 `/` 开头或 known kind 原样返回。mapexpr 只对 value 递归展开(key 恒 `[…]` 非 struct)。 pub fn expand_struct_refs(s: &str) -> String { - s.split('|') - .map(expand_atom) - .collect::>() - .join("|") + s.split('|').map(expand_atom).collect::>().join("|") } fn expand_atom(s: &str) -> String { diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 9cfd3b15..25628417 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -1240,9 +1240,14 @@ impl Parser { return self.parse_pratt(UNARY_PREC); } // 一元前缀 & = 取址:&x ≡ kv·abs(x)(中缀 & 仍为按位与,走 pratt 中缀路径)。 + // & 对成员链 kv·get(base, segs...) → kv·abs(base, segs...):取成员路径地址,非读值取址。 if symbol::lookup(&t.value).word == "bitand" { let arg = self.parse_pratt(UNARY_PREC)?; let op = format!("kv{}abs", keytree::MEMBER_SEP); + let get = format!("kv{}get", keytree::MEMBER_SEP); + if arg.op == get && arg.args.len() >= 2 { + return Some(ast::call(&op, arg.args)); + } return Some(ast::call(&op, vec![arg])); } let arg = self.parse_pratt(UNARY_PREC)?; diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index 5be0b344..07c53b63 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -74,20 +74,25 @@ void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const } if (!name || !name[0]) return; if (name[0] == '/') { kvlangKvGetOne(kv, name, out); return; } + if (name[0] == '*') { + /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ 解引用到实参值。 */ + char *stk = kvlangKeytreeStack(frame_root); + kvlangXvalue_t pv; kvlangXvalueZero(&pv); + kvlangKvGetMember(kv, stk, name + 1, &pv); + if (kvlangXvalueIsPtr(&pv)) { + char *target = kvlangXvaluePtrTarget(&pv); + kvlangKvGetOne(kv, target, out); + free(target); + } + kvlangXvalueFree(&pv); free(stk); + return; + } char *stk = kvlangKeytreeStack(frame_root); kvlangXvalue_t pv; kvlangXvalueZero(&pv); kvlangKvGetMember(kv, stk, name, &pv); if (kvlangXvalueIsPtr(&pv)) { - char *target = kvlangXvaluePtrTarget(&pv); - kvlangXvalue_t av; kvlangXvalueZero(&av); - kvlangKvGetMember(kv, stk, target, &av); - free(target); - if (!kvlangXvalueNone(&av)) { - char *path = kvlangXvalueValueString(&av); - kvlangKvGetOne(kv, path, out); - free(path); - } - kvlangXvalueFree(&av); + /* 数据 Ptr(&x):地址值本体,读值不解引用。成员访问另走 kv·get 的 member_path。 */ + *out = pv; pv.data = NULL; pv.len = 0; } else if (!kvlangXvalueNone(&pv)) { *out = pv; pv.data = NULL; pv.len = 0; } @@ -108,36 +113,18 @@ char *kvlangBuiltinResolveReadKey(kvlangKv_t *kv, const char *frame_root, const char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, const char *name) { if (name[0] == '/') return strdup(name); char *stk = kvlangKeytreeStack(frame_root); - kvlangXvalue_t pv; kvlangXvalueZero(&pv); - kvlangKvGetMember(kv, stk, name, &pv); - char *result = NULL; - if (kvlangXvalueIsPtr(&pv)) { - char *target = kvlangXvaluePtrTarget(&pv); - kvlangXvalue_t av; kvlangXvalueZero(&av); - kvlangKvGetMember(kv, stk, target, &av); - free(target); - if (!kvlangXvalueNone(&av)) { - /* 只追显式指针(ptr, ref==1)链;av 是 handle_call 已 resolve 好的 - * 最终写目标路径(普通 char),直接取字符串,勿再按值读下一跳—— - * 否则会把已写数据(char)误当路径再追,导致二次调用写回旧值。 */ - kvlangXvalue_t v = av; av.data = NULL; av.len = 0; - while (kvlangXvalueIsPtr(&v)) { - char *np = kvlangXvaluePtrTarget(&v); - kvlangXvalue_t nxt; kvlangXvalueZero(&nxt); - kvlangKvGetOne(kv, np, &nxt); - free(np); - kvlangXvalueFree(&v); - v = nxt; - } - if (!kvlangXvalueNone(&v)) result = kvlangXvalueValueString(&v); - kvlangXvalueFree(&v); - } - kvlangXvalueFree(&av); + const char *seg = name; + if (name[0] == '*') { + /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ target=写槽路径。 */ + kvlangXvalue_t pv; kvlangXvalueZero(&pv); + kvlangKvGetMember(kv, stk, name + 1, &pv); + char *result = kvlangXvalueIsPtr(&pv) ? kvlangXvaluePtrTarget(&pv) : NULL; + kvlangXvalueFree(&pv); + if (result) { free(stk); return result; } + seg = name + 1; } - kvlangXvalueFree(&pv); - if (result) { free(stk); return result; } kvlangStrbuf_t o; kvlangStrbufInit(&o); - kvlangStrbufPuts(&o, stk); kvlangStrbufPuts(&o, name); + kvlangStrbufPuts(&o, stk); kvlangStrbufPuts(&o, seg); free(stk); return kvlangStrbufDetach(&o); } @@ -796,4 +783,3 @@ int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, k /* ── vthread 控制 / debugger ─────────────────────────────────────── */ - diff --git a/runtime/src/builtin_kv.c b/runtime/src/builtin_kv.c index 2432dcc0..5385b8b7 100644 --- a/runtime/src/builtin_kv.c +++ b/runtime/src/builtin_kv.c @@ -32,9 +32,17 @@ static bool base_is_container(const kvlangXvalue_t *base) { static char *member_path(kvlangFrame_t *f, const kvlangXvalue_t *in, int n) { const kvlangXvalue_t *base = &in[0]; - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *bp = base_is_container(base) ? kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[0].name) : kvlangXvalueValueString(base); - free(fr); + char *bp = NULL; + if (kvlangXvalueIsPtr(base)) { + /* 数据 Ptr(&x)作成员 base:直接取其目标路径,逐段下钻。 */ + bp = kvlangXvaluePtrTarget(base); + } else if (base_is_container(base)) { + char *fr = kvlangKeytreeFrameRoot(f->pc); + bp = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[0].name); + free(fr); + } else { + bp = kvlangXvalueValueString(base); + } /* 成员链:base 之后逐段拼 key(变参),每段可为静态字面量或动态键(运行时值)。 */ for (int i = 1; i < n; i++) { char *kk = kvlangKvKey(&in[i]); @@ -112,8 +120,10 @@ int kvlangBuiltinKvCpTree(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cpd int kvlangBuiltinKvCpList(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cplist", kvlangKvCpList); } int kvlangBuiltinKvAbs(kvlangFrame_t *f) { - kvlangXvalue_t in[1]; int n = kvlangBuiltinReadInputs(f, in, 1); - char *p = n >= 1 ? resolve_path_arg(f, 0, in) : NULL; + kvlangXvalue_t in[MAX_PARAMS]; int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); + char *p = NULL; + if (n >= 2) p = member_path(f, in, n); + else if (n >= 1) p = resolve_path_arg(f, 0, in); if (!p) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.abs requires a key"); } /* 产出 Ptr(ref=1):langtype = 目标 kindexpr、body = 目标绝对路径。 * &x ≡ kv.abs(x):取址返回指向 x 所在节点的软链接(单跳同型),不再产 char 路径串。 */ diff --git a/runtime/src/kv.c b/runtime/src/kv.c index 20844892..c9843783 100644 --- a/runtime/src/kv.c +++ b/runtime/src/kv.c @@ -37,8 +37,9 @@ int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out) { return 0; } -/* Frame member: dir 直连 name 组键,借用读(resolve=1 穿透 link,全路径 Get(resolve=0) 不穿透 [d] 帧) - * → out 借 kvspace 指针(borrowed=1)。空值 → out len=0。 */ +/* Frame member: dir 直连 name 组键,借用读(resolve=0:拿 Ptr 本体,不穿透 link—— + * 解引用由 runtime 显式按 target 形态判别,见 ResolveReadValue/ResolveWriteSlot); + * 空值 → out len=0。 */ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXvalue_t *out) { kvlangXvalueZero(out); if (!name || !name[0]) @@ -50,7 +51,7 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv key[dl + nl] = 0; uint8_t *d; uint32_t len; - if (kvspaceGet(k->h, key, 1, &d, &len) == 0 && d && len > 0) { + if (kvspaceGet(k->h, key, 0, &d, &len) == 0 && d && len > 0) { out->data = d; out->len = len; out->borrowed = 1; @@ -107,7 +108,7 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui uint32_t body_len = h.body_len < 0 ? 0 : (uint32_t)h.body_len; const uint8_t *body = v->data + h.body_offset; uint8_t *dst = NULL; - if (kvspaceWriteInPlace(k->h, pairs[i].key, 1, body_len, &dst, err, err_cap) != 0) { + if (kvspaceWriteInPlace(k->h, pairs[i].key, 0, body_len, &dst, err, err_cap) != 0) { if (kvspaceWriteNewPlace(k->h, pairs[i].key, h.ref, h.storetype, h.ro, h.vid, (const char *)h.langtype, body_len, &dst, err, err_cap) != 0) { rc = -1; break; diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index e1a1c10c..8ba0687a 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -251,40 +251,20 @@ static char *frame_slot_key(const char *frame_root, const char *slot) { } /* 实参名 → 其存储键(写入被调帧 [0,-k]/[0,k])。字面量返回 NULL。 - * 命名参数经 Ptr 指到本帧 [0,±k],槽内是上层 handle_call 已 resolve 好的最终键路径; - * 之后只追显式 Ptr(ref==1)链,勿把 char 值当路径再追——否则字符串实参的内容会被 - * 误当键(穿两层调用即变 None)。写侧 kvlangBuiltinResolveWriteSlot 同此纪律(#124)。 */ + * *[0,±k] 显式解引用:读本帧 [0,±k](ref=1 Ptr,body=实参地址),取 target 即实参存储键。 + * 普通名/绝对路径走 frame_slot_key(绝对路径直通、·成员返回 NULL)。 */ static char *resolve_read_path(kvlangKv_t *kv, const char *frame_root, const char *name) { if (is_literal(name)) return NULL; - char *stk = kvlangKeytreeStack(frame_root); - kvlangXvalue_t v; kvlangXvalueZero(&v); - kvlangKvGetMember(kv, stk, name, &v); - char *result = NULL; - if (kvlangXvalueIsPtr(&v)) { - char *target = kvlangXvaluePtrTarget(&v); - kvlangXvalue_t nv; kvlangXvalueZero(&nv); - kvlangKvGetMember(kv, stk, target, &nv); - if (kvlangXvalueNone(&nv) || !kvlangXvalueIsCharKind(kvlangXvalueKind(&nv))) { - result = frame_slot_key(frame_root, target); - } else { - char *path = kvlangXvalueValueString(&nv); - for (;;) { - kvlangXvalue_t hop; kvlangXvalueZero(&hop); - kvlangKvGetOne(kv, path, &hop); - if (!kvlangXvalueIsPtr(&hop)) { kvlangXvalueFree(&hop); result = path; break; } - char *p2 = kvlangXvaluePtrTarget(&hop); - kvlangXvalueFree(&hop); - free(path); - path = p2; - } - } - kvlangXvalueFree(&nv); - free(target); - } else { - result = frame_slot_key(frame_root, name); + if (name[0] == '*') { + char *stk = kvlangKeytreeStack(frame_root); + kvlangXvalue_t pv; kvlangXvalueZero(&pv); + kvlangKvGetMember(kv, stk, name + 1, &pv); + char *result = kvlangXvalueIsPtr(&pv) ? kvlangXvaluePtrTarget(&pv) : NULL; + kvlangXvalueFree(&pv); + free(stk); + return result; } - kvlangXvalueFree(&v); free(stk); - return result; + return frame_slot_key(frame_root, name); } /* return:弹出当前帧 [d]。d==1 → 顶层结束(*out_next=NULL);否则 *out_next=‥returnpc。 @@ -442,6 +422,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) 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}; if (concrete) { /* 字面量无变量槽,一律写 ._litN;勿沿用 resolve_read_path 的返回值—— * 否则字面量内容(如 "https://x" 里的 //)会被当路径段,二次读回即丢。 */ @@ -451,15 +432,25 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) rk = kvlangStrbufDetach(&lk); /* 写字面量到 rk(拷贝,避免 double-free) */ kvspaceHead_t ah; kvspaceDecodeHead(arg->val.data, arg->val.len, &ah); + if (ah.langtype[0]) snprintf(lt, sizeof lt, "%s", ah.langtype); int32_t abl; const uint8_t *ab = kvlangXvalueBody(&arg->val, &ah, &abl); kvlangLangtype akx; kvlangLangtypeParse(ah.langtype, &akx); pairs[np].key = strdup(rk); kvspaceTlvEncode(kvlangXvalueKind(&arg->val), ab, (uint32_t)abl, akx.dims, akx.ndim, &pairs[np].val.data, &pairs[np].val.len); np++; + } else if (rk) { + kvlangXvalue_t hv; kvlangXvalueZero(&hv); + kvlangKvGetOne(kv, rk, &hv); + if (!kvlangXvalueNone(&hv)) { + kvspaceHead_t ah; + if (kvlangXvalueHead(&hv, &ah) == 0 && ah.langtype[0]) + snprintf(lt, sizeof lt, "%s", ah.langtype); + } + kvlangXvalueFree(&hv); } if (rk) { - kvlangXvalue_t rv; kvlangXvalueNewCharUtf8(&rv, rk); + kvlangXvalue_t rv; kvlangXvalueNewPtr(&rv, lt, rk); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = rv; np++; @@ -474,7 +465,25 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) if (i < inst->nw) { char *wk = resolve_read_path(kv, caller_fr, inst->writes[i].name); if (wk) { - kvlangXvalue_t wv; kvlangXvalueNewCharUtf8(&wv, wk); + char lt[256] = {0}; + kvlangStrbuf_t pk; kvlangStrbufInit(&pk); + kvlangStrbufPrintf(&pk, "%s.[0,%d]", func_key, i + 1); + kvlangXvalue_t dv; kvlangXvalueZero(&dv); + kvlangKvGetOne(kv, pk.p, &dv); + kvlangStrbufFree(&pk); + if (!kvlangXvalueNone(&dv)) { + kvspaceHead_t ah; + if (kvlangXvalueHead(&dv, &ah) == 0) { + int32_t al; const uint8_t *ab = kvlangXvalueBody(&dv, &ah, &al); + for (int bi = 0; ab && bi < al; bi++) if (ab[bi] == 0) { + int tl = al - bi - 1; + if (tl > 0 && tl < (int)sizeof lt) { memcpy(lt, ab + bi + 1, tl); lt[tl] = 0; } + break; + } + } + } + kvlangXvalueFree(&dv); + kvlangXvalue_t wv; kvlangXvalueNewPtr(&wv, lt, wk); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = wv; np++; diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/04-CABI.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/04-CABI.kv" index a54dcbbd..948e5b68 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/04-CABI.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/04-CABI.kv" @@ -33,6 +33,11 @@ camelCase)——kvspace 层前缀 `kvspace`(`kvspaceGet` / `kvspaceWriteInPl 读写各带 `resolve` 参数:`resolve=1` 时透明穿透 ref=1 指针(link);`kvspaceListLen`/`kvspaceListAt` 另有 `expand_ext` 参数控制是否展开 extindex 子项。head 线格式与 langtype 编码见 [[键系统与数组访 问]]。 + +**写侧不穿透整键(normative)**:`kvspaceWriteInPlace` / `kvspaceWriteNewPlace` / `kvspaceDel` / +`kvspaceDelTree` 对**最终组件**不解析 link——写/删 Ptr 键只作用该键本体,不重定向到 target +(`Del`/`DelTree` 为 POSIX rm 式:删指针本身,不删被指向者)。解引用由 runtime 显式 `*` 掌控(见 +[[ptr]])。否则「写实参地址 Ptr 到帧槽」会被重定向到 target,导致丢参 / 写坏被指向的值。 ## kvspace C ABI 导出符号全集(nm -D 实测,31) ``` diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" index 32bc1169..f3f86653 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" @@ -26,11 +26,10 @@ lib kvlang/spec/kvspace模型/指令布局格式 { 签名**),body = `[nr:u16 LE][nw:u16 LE][dynamic:u8]`(读参个数 `nr`、写参个数 `nw`、是否变参 `dynamic`)。 -rwfunc 的各参数类型**不**落 `[0,x]` 静态槽,而随**命名参数键**承载:`/lib/{pkg}·{name}/{param}` 是一 -个 -Ptr,body 为该参数的槽坐标串(如 `x` → `"[0,-1]"`、返回值 `R` → `"[0,1]"`;读参 `[0,-j]`、写参 -`[0,+j]`),且 **Ptr 的 langtype 即该参数完整类型串**。rwfunc 按名在帧内解析、不在意参数顺序(仅区分 -读/写参),故 func 目录根**不**落 `[0,x]` 子槽。 +rwfunc 的各参数类型**不**落 `/[0,x]` 静态槽,而随**参数定义键**承载:`/lib/{pkg}·{name}.[0,-j]` +(读参)/ `.[0,+j]`(写参)——点后缀、与坐标斜杠键 `/[0,±j]` 不同名——langtype `def langtype`, +body = 参数名 + NUL + 该参数完整类型串。参数顺序由键中的 `±j` 显式记录(命名键不保序),故 func 目录 +根**不**落 `/[0,x]` 子槽。 `def rwir` 则相反:主槽 langtype = `def rwir`(裸种类名)、body 同计数格式,各参数类型落**位置化**签 名 diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" index 2966fc85..dab5f598 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" @@ -5,29 +5,35 @@ lib kvlang/spec/kvspace模型/调用extindex { 调用一个函数时,运行时**不**拷贝指令,而是为本次调用建帧子树,令帧根成为指向 `/lib/{pkg}·{name}/` 指 令树的 extindex 叠加层,再把实参 / 结果地址写进帧根下的 `[0,±j]` 槽: -- `/lib/{pkg}·{name}/` 下(rwfunc)只有命名参数键(`x` → body `"[0,-1]"`、langtype 为该参类型的 - Ptr),**无** `[0,±j]` 静态坐标槽。运行时向 `frameRoot/[0,±j]` 写实参地址 Ptr 时,该坐标在只读 - extindex 目标(函数树)中并不存在——extindex 写保护正因**该名在扩展层缺席而放行**帧本地写,实参内容 - 零拷贝。(反之若 layout 预落 `[0,±j]` 静态槽,写保护会因扩展层已有同名只读节点而拒写、丢参——故 - rwfunc 目录根不落这些坐标。) +- `/lib/{pkg}·{name}.[0,±j]`(rwfunc 的**参数定义键**,点后缀、与坐标斜杠键 `/[0,±j]` 不同名): + langtype=`def langtype`,body=该参数名字 + NUL + 类型串。此键是**签名元数据**(dump / 类型检查 + 用),**不参与运行时形参解析**;函数体指令里的形参引用已在 layout 编译期替换为帧坐标的显式解引用 + `*[0,-1]`。目录根**无** `/[0,±j]` 静态坐标槽——运行时向 `frameRoot/[0,±j]` 写实参地址 Ptr 时, + 该坐标在只读 extindex 目标(函数树)中并不存在,extindex 写保护正因**该名在扩展层缺席而放行**帧本 + 地写,实参内容零拷贝。(反之若 layout 预落 `/[0,±j]` 静态槽,写保护会因扩展层已有同名只读节点而拒 + 写、丢参——故 rwfunc 目录根不落这些坐标。) - 所有帧共享 `/lib` 下同一份指令树,零拷贝;帧根下叠加各自的实参槽与局部变量。 崩溃恢复由此自然成立:PC 是路径字符串、帧根落 KV、返回点落 KV——进程重启后可从 kvspace 续跑。 -## 参数解析:指针链 +## 参数解析:一层引用(显式解引用) -指令读到 `rwir:x` 时,`x` 是相对当前帧的名字。运行时经指针链解析到实参真实位置: +函数体指令里的形参引用,layout 编译期把参数名替换为**对帧槽坐标的显式解引用** `*[0,-1]`。帧槽 +`frameRoot/[0,-1]` 由调用点写入实参地址 Ptr(ref=1,langtype=参数类型,body=实参地址),`*` 解引用 +一次即得实参真实值: ``` -1. 读 frameRoot/x → "[0,-1]" 名字 → 槽坐标 -2. 读 frameRoot/[0,-1] → 实参 key 路径 槽 → 实参地址 -3. 读该路径 → 值 解引用 +1. frameRoot/[0,-1] → 实参 key 路径 槽 → 实参地址(调用点写入的帧本地 Ptr) +2. *[0,-1] → 值 显式解引用一次 ``` -第 1 跳的 `frameRoot/x` 由 `/lib` 指令树经 extindex 透出(layout 写入、只读;其 Ptr 的 langtype 记参 -数类型,运行期解析只取 body 坐标),第 2 跳的 `frameRoot/[0,-1]` 由运行时在调用点写入。读参地址可指 -向 -调用方值以实现零拷贝重定向(见 [[系统变量]] 的 `‥rparam` / `‥wparam`)。 +读写对称:读参 `*[0,-k]` 读到实参值;写参 `*[0,+k]` 指回调用者写槽,子帧写参直写调用方目标位置。读参 +地址可指向调用方值以实现零拷贝重定向(见 [[系统变量]] 的 `‥rparam` / `‥wparam`)。形参类型随帧槽的 +Ptr langtype 承载,不在解析链上额外查参数定义键。 + +写侧关键约束:kvspace 对 Set/Del **不穿透整键**(Ptr 键的写/删只作用该键本身,不追 target)——自动 +穿透会把「写实参地址 Ptr 到帧槽」重定向到 target,导致丢参 / 写坏被指向的值。解引用一律由 runtime 显 +式 `*` 掌控(见 [[ptr]])。 ## init 与标签 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" index 67c528ab..bc74544f 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" @@ -12,7 +12,7 @@ lib kvlang/spec/类型系统/langtype签名类型表达式 { | 型 | 出现位置 | 角色 | 表达力 | |----|---------|------|--------| -| **def langtype** | def rwir 签名行 `[0,x]` 槽(storetype=`def langtype`);rwfunc 则随命名参数键 `{name}` 的 Ptr langtype 承载同一模式(见 [[storetype_def族与签名行]]) | **模式**:描述一个参数「可接受的一类值」 | 完整——并集 `A\|B`、轴量词 `.`/`?`/`*`/`+`、`any`、mapexpr(见 [[匹配语义与变参]])。参数变参 `...` 是签名层 arity、**不在** langtype 内(见 [[函数]]) | +| **def langtype** | def rwir 签名行 `[0,x]` 槽(storetype=`def langtype`);rwfunc 则随参数定义键 `.[0,±j]`(body=名字+NUL+类型串)承载同一模式(见 [[storetype_def族与签名行]]) | **模式**:描述一个参数「可接受的一类值」 | 完整——并集 `A\|B`、轴量词 `.`/`?`/`*`/`+`、`any`、mapexpr(见 [[匹配语义与变参]])。参数变参 `...` 是签名层 arity、**不在** langtype 内(见 [[函数]]) | | **value langtype** | 一个具体 XValue 的 head langtype(见 [[langtype语义类型]]) | **实例**:描述这个值**是什么** | 具体固定——单一种类 + 确定 `[dims]`(每维具体整数),不含并集与任何轴量词 | def langtype 是「值域」,value langtype 是值域里「一个点」;类型检查即判定后者是否落进前者(匹配语义 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" index 250e1472..0b79b1ba 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" @@ -1,6 +1,6 @@ // 欢迎加入kvspace世界 lib kvlang/spec/类型系统/ptr { - r####"# ptr(软链接) + r####"# ptr(指针) **ptr** 不是种类,而是 head 的 `ref=1`(存储位置维):ptr 值的 head storetype/langtype 描述**目标的 完整形态**,body 存**目标 key 路径**。源码类型标注最前可写 `*` 前缀(如 `*int64`)表 ref,`*` 后可 @@ -8,7 +8,17 @@ lib kvlang/spec/类型系统/ptr { 独立字节;wire 层 langtype 串不含前缀(见 [[总体方案]]);赋值(Set)时据 head 的 storetype/langtype 做单跳类型检查。 -跨函数共享的数据放绝对路径,指针变量/字段存其路径字符串: +指针是**值**:读一个 `*T` 变量得到**地址值本身**(ref=1 Ptr,不解引用),解引用是**显式**的——用一元 +前缀 `*`。三者与 C 对齐: + +| 形态 | 语义 | +|------|------| +| `&x` | 取地址 → ref=1 Ptr(`&x ≡ kv·abs(x)`,返回绝对路径串) | +| `p`(`*T` 变量读值) | 得地址值本身(不穿透) | +| `*p` | 解引用 → 读 body 目标地址的值 | + +`&` 对**裸名** `&x` 取帧槽地址(`≡ kv·abs(x)`)、对**成员链** `&t·2` 取成员路径地址(`≡ +kv·abs(t, "2")`),均产 ref=1 Ptr、body 为目标 key 路径。 ```kv struct Node { @@ -17,11 +27,15 @@ struct Node { } /n1 = Node{val=1} // 绝对路径建节点 /n2 = Node{val=2 next=/n1} // next 存 /n1 绝对路径(指针) -/n1 -> p // p 为指针变量(存路径串) -p·val -> v // 按值解引用:读 /n1·val +/n2 -> p // p 为指针变量(存路径串) +*p -> q // q = 解引用 p = n2 的实例值 +p·val -> v // 成员访问经指针自动解引用:读 /n1·val ``` -局部变量/容器成员取指针用一元前缀 `&`(`&x ≡ kv·abs(x)`,返回绝对路径串),与 `*` ref 标注互补—— -`&` 造路径、`*` 标注目标形态(见 [[逻辑位运算与优先级]] 取址运算符)。 +成员访问 `p·field` 经指针**自动解引用**(对齐 Go/Rust 的 `p.field`);需要「指针指向的整值」(如指针 +指向标量)时用显式 `*p`。二者不冲突:`*p` 取整值、`p·field` 取成员。 + +嵌套指针(`**T`)不在本模型内:wire 层 langtype 串不含 `*`、ref 仅单字节,无法表达层数。链表/树/取址 +皆一层指针,已覆盖;C 里 `**` 的「out 参数传地址」由 kvlang 写参(`-> (r:T)`)取代,无需 `**`。 "#### -> /lib/kvlang/spec/类型系统/ptr } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" index 100ff7ea..369e65a4 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" @@ -22,12 +22,14 @@ rwfunc / def rwir 的**签名(各参数类型)不写进 langtype 串**,也 **`def langtype`** xvalue,body 存该参数完整 langtype 串(一参一槽),**纯净、不含变参 `...`**—— 变 参是 arity,只落主槽 `dynamic` 字节。跨 runtime 队列按位置收参,故须有序签名。 -3. **rwfunc——命名参数键 `{name}` 承载**:func 目录根**不**落 `[0,x]` 槽;每个参数类型随命名参数键 - `funcDir/{name}` 的 Ptr 承载——该 Ptr body 为槽坐标(读参 `[0,-j]`、写参 `[0,+j]`),**其 langtype - 即该参数完整类型串**。rwfunc 按名在帧内解析,不在意参数顺序、仅区分读/写参。 +3. **rwfunc——参数定义键 `funcDir.[0,±j]` 承载**:func 目录根**不**落 `/` 坐标槽;每个参数随点后缀 + 键 `funcDir.[0,-j]`(读参)/ `funcDir.[0,+j]`(写参)承载——langtype `def langtype`,body = 参数 + 名 + NUL + 该参数完整类型串。点后缀与坐标斜杠键 `/[0,±j]` 不同名,故运行期向帧根 + `frameRoot/[0,±j]` 写实参地址 Ptr 时不会撞上只读静态节点;参数顺序由键中的 `±j` 显式记录(命名 + 键不保序),rwfunc 按位置取参、只区分读/写。 于是 rwfunc/def rwir 主槽 langtype 都是裸种类名 `rwfunc` / `def rwir` 本身,**非「签名 langtype -串」**。def rwir 的签名散在 `[0,x]` 槽、rwfunc 的散在 `{name}` Ptr;实现指令行(若有)在 +串」**。def rwir 的签名散在 `[0,x]` 槽、rwfunc 的散在 `.[0,±j]` 键;实现指令行(若有)在 `[s0>0,·]`。 ### def 族:定义节点的 langtype(带空格书写) @@ -39,7 +41,7 @@ rwfunc / def rwir 的**签名(各参数类型)不写进 langtype 串**,也 |----------|------|------| | `def rwir` | `/lib/` | 一条 rwir 的路由头/签名定义(原 `defrwir`,废连写) | | `def struct` | `/lib/` | 一个 struct 原型的定义节点(字段类型定义在其子槽) | -| `def langtype` | def rwir 签名行 `[0,x]` 槽 | 一个参数的类型定义(body=该参数 langtype 串;rwfunc 不用此槽,类型随 `{name}` Ptr 的 langtype 承载) | +| `def langtype` | def rwir 签名行 `[0,x]` 槽 / rwfunc 参数定义键 `.[0,±j]` | 一个参数的类型定义(def rwir body=该参数 langtype 串;rwfunc body=参数名+NUL+langtype 串) | `def` 与 base 间的空格是**规范强制**:连写形式 `defrwir`/`defstruct`/`deflangtype` 一律废除。 @@ -50,7 +52,7 @@ KV 树中的目录路径(尾 `/`)本身是 xvalue,storetype ∈ {index, ex | 路径 | langtype | storetype | 成员 | |------|----------|-----------|------| | `/lib/` | `lib` | index | `/lib/` 各包目录(`/` 子路径) | -| `/lib/pkg·funca/` | `rwfunc` | index(静态函数定义目录) | 命名参数键 `{name}`(Ptr,langtype=参类型)+ 实现指令行 `[s0>0,·]` | +| `/lib/pkg·funca/` | `rwfunc` | index(静态函数定义目录) | 参数定义键 `.[0,±j]`(def langtype,body=名字+NUL+类型)+ 实现指令行 `[s0>0,·]` | | `/lib/` | `def rwir` | index(路由头,只有签名行) | 签名行 `[0,x]`(def langtype),无实现指令行 | | `/vthread/1/` | `rwfunc` | extindex(运行栈,可扩展) | `/vthread/1/<坐标>` 栈帧/指令 | 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 141fcdb6..fb20d557 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" @@ -41,8 +41,8 @@ error: func f: return value "r" has no type annotation 末读参**可**在参数上尾缀 `...` 声明变参(吸收 0..N 个同型实参,如 `A:int64|float64...`)。`...` 是** 签名层 arity 构造,不是 type_expr 的一部分**(轴量词见 [[langtype签名类型表达式]]):layout 解析时把 它从参数上**剥离**,给该函数主槽置 `dynamic=1`(见 [[指令布局格式]]),而参数的**纯净** langtype(不含 -`...`)由承载点存放——def rwir 落 `[0,x]` 子槽、rwfunc 随 `{name}` Ptr 的 langtype。故 -`LangtypeValid`/`LangtypeMatch` 永不见 `...`。 +`...`)由承载点存放——def rwir 落 `[0,x]` 子槽、rwfunc 随 `{name}` Ptr 的 langtype(此键为签名元数 +据,不参与运行时形参解析,见 [[调用extindex]])。故 `LangtypeValid`/`LangtypeMatch` 永不见 `...`。 - 变参**必须**是最后一个读参;非末位读参带 `...`:装载期错误。 - 写参(返回值)**不得**变参:装载期错误。 diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" index cf1f8aaa..080b8002 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" @@ -16,11 +16,13 @@ lib kvlang/spec/layout语义/函数写槽与布局 { rwfunc 布进 `/lib/·/`: 1. `[0,0]`——签名行锚点,langtype `rwfunc`,body `[nr:u16 LE][nw:u16 LE][dynamic:u8]`(读参数/写参数/ - 是否变参),仅此计数头。rwfunc **不**在 func 目录根落 `[0,±k]` 参数类型槽——各参类型改由命名参数 - 键承载(见下)。 + 是否变参),仅此计数头。rwfunc **不**在 func 目录根落 `[0,±k]` 参数类型槽。 2. `/lib/·.src`——源码副本(kind `char`)。 -3. 每个命名参数——`funcDir/` 存 Ptr,body 为其帧坐标串(读参 `[0,-j]`、写参 `[0,+j]`), - **Ptr 的 langtype 即该参数类型**(target_langtype)——类型随参数名承载,非固定 `char`。 +3. 每个参数定义键——`funcDir.[0,-j]`(读参)/ `funcDir.[0,+j]`(写参),点后缀、与坐标斜杠键 + `/[0,±j]` 不同名;langtype=`def langtype`,body = 参数名 + NUL + 该参类型串。此键是**签名元数 + 据**(dump / 类型检查 / 调用期类型检查用),**不参与运行时形参解析**——函数体指令里的形参引用 + 已在编译期替换为帧坐标的显式解引用 `*[0,-j]`/`*[0,+j]`,运行期直接读帧槽解引用到实参值(见 + [[调用extindex]])。 4. 指令体从 `[1,0]` 起(`[0,0]` 为签名行锚点)。 func 目录根之所以**不**预落 `[0,±k]`:这些坐标是 runtime 调用期写入的帧本地实参地址;若 layout 提前 diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" index 1ba91902..b0529b7d 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/12-rwfunc\345\270\203\345\261\200\344\270\216def-rwir.kv" @@ -9,12 +9,14 @@ lib kvlang/spec/layout语义/rwfunc布局与def_rwir { - `[0,0]`——rwfunc 签名锚点,body 仅 `[nr][nw][dynamic]` 计数头;func 目录根**不**落 `[0,±k]` 参数 类型槽。 - `.src`(ASCII 点后缀,`/lib/·.src`)——源码副本,kind `char`。 -- 每个命名参数——`funcDir/` 存 Ptr,body 为槽坐标串(读参 `[0,-j]`、写参 `[0,+j]`),**Ptr - langtype = 该参数类型**(类型随参数名承载)。 +- 每个参数定义键——`funcDir.[0,-j]`(读参)/ `funcDir.[0,+j]`(写参),点后缀、与坐标斜杠键 + `/[0,±j]` 不同名;langtype=`def langtype`,body = 参数名 + NUL + 该参类型串。此键为签名元数据 + (dump / 类型检查用),不参与运行时形参解析——函数体指令里的形参引用已编译期替换为帧坐标的显式解 + 引用 `*[0,-j]`/`*[0,+j]`,运行期直接读帧槽解引用到实参值(见 [[调用extindex]])。 - `[i,j]`(i≥1)——线性指令(见 [[控制流]])。 - `‥labels/