From 91190761b5b829f286c32b35e7a9c854809ab77f Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Tue, 15 Sep 2026 09:39:19 +0800 Subject: [PATCH 1/5] =?UTF-8?q?stdlib:=20=E6=96=B0=E5=A2=9E=20kvlang/refle?= =?UTF-8?q?ct=EF=BC=9Bnetworld=20=E6=8B=86=E5=88=86=E5=B9=B6=E4=B8=8B?= =?UTF-8?q?=E6=B2=89=20edit/shell/python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把「agent 工具」这类能力从上层项目下沉到语言 stdlib: - **kvlang/reflect**(新):kvlang 自省。fnlist(name) 枚举 /lib/ 下的 能力名;分类看 xvalue 的 langtype(三轴正交 ref/storetype/langtype), 不靠名字猜——用 xv·parselangtype 拿基名(剥 [dims] 与 "def ")。 - **networld/edit**(新):文件改写语义(read 行号窗口视图 / write 覆盖写…), 与 fs 分工——fs 管字节与只读检索,edit 管改写。 - **networld/shell、networld/python**(新):跑 shell / python3 取 stdout, 实现在 networld/proc·exec 之上。 - **networld/fs**:改写的部分移出(→ edit),只留字节与只读检索。 - string.kv / kvlangbrief:配套补充。 Part of #170 Co-Authored-By: Claude Code --- stdlib/kvlang/kvlangbrief.kv | 2 + stdlib/kvlang/reflect.kv | 54 ++++++++++++ stdlib/networld/edit.kv | 165 +++++++++++++++++++++++++++++++++++ stdlib/networld/fs.kv | 41 --------- stdlib/networld/python.kv | 13 +++ stdlib/networld/shell.kv | 13 +++ stdlib/string.kv | 27 ++++++ 7 files changed, 274 insertions(+), 41 deletions(-) create mode 100644 stdlib/kvlang/reflect.kv create mode 100644 stdlib/networld/edit.kv create mode 100644 stdlib/networld/python.kv create mode 100644 stdlib/networld/shell.kv diff --git a/stdlib/kvlang/kvlangbrief.kv b/stdlib/kvlang/kvlangbrief.kv index 7b8f7787..ae86c15f 100644 --- a/stdlib/kvlang/kvlangbrief.kv +++ b/stdlib/kvlang/kvlangbrief.kv @@ -224,6 +224,8 @@ networld/fs·write("/tmp/x.txt", xv·reinterpret("hi", "[]uint8")) -> n ### 数组(ndarray·* / xv·*) - `ndarray·numel / dim / shape`;`xv·at / set / reshape / reinterpret / langtype / bodylen`。 +- `xv·parselangtype(v) -> (kind[, ndim])`:解析 langtype 基名(剥 `[dims]` 与 `def ` 前缀), + 如 `def langtype`→`langtype`、`def rwir`→`rwir`、`[508]char/utf8`→`char/utf8`;用于按类型分类 KV 成员。 ### 数学 - 运算符见上;`pow / sqrt / exp / log / abs / sign / neg / min / max`;常量 `/lib/math·Pi` `/lib/math·E` `/lib/math·Tau`。 diff --git a/stdlib/kvlang/reflect.kv b/stdlib/kvlang/reflect.kv new file mode 100644 index 00000000..7713f519 --- /dev/null +++ b/stdlib/kvlang/reflect.kv @@ -0,0 +1,54 @@ +// 欢迎加入kvspace世界 +// lib kvlang/reflect —— kvlang 自省(rwfunc,kv 源码)。 +// fnlist(name) -> listing:枚举 /lib/ 下的能力名,逗号连接。 +// 分类看 xvalue 的 langtype(三轴正交:ref / storetype / langtype),不看名字猜—— +// 用 `xv·parselangtype(v) -> kind` 拿基名(剥掉 [dims] 与 "def " 前缀): +// langtype 形参 / 返回槽(def langtype)—— 不是能力,跳过 +// rwir rwir 路由头(def rwir)—— 能力,名字原样 +// rwfunc 函数签名槽 —— 能力 +// 其他数据 常量等普通 key —— 能力,名字原样 +// 每个 rwfunc 在容器里有三条成员:`.[0,±k]`(形参)、`.src`(源码副本)、 +// `/`(函数目录条目)。前者按 langtype 跳过,中者是源码副本跳过,后者去尾斜杠即函数名。 +// 用途:把一棵 lib 子树的「有什么能力」压成一行文本,供 LLM 生成能力记忆 / 工具清单。 +// 命名沿用 Go/Rust 的 reflect(自省);不占用 `lib kvlang`——那是 kvlangbrief 与 +// kvlang·vet/·layout/·format/·dump 四个 def rwir 路由头的命名空间。 + +lib kvlang/reflect { + rwfunc fnlist(name:[]char/utf32) -> (listing:[]char/utf32) { + "" -> listing + "/lib/" + name + "·" -> mdir + kvspace·listlen(mdir) -> n + i = 0 + while (i < n) { + kvspace·listn(mdir, i) -> key + char/utf32(key) -> key + string·concat(mdir, key) -> kp + kvspace·get(kp) -> v + xv·parselangtype(v) -> k + if (k != "langtype") { + string·find(key, ".src") -> src + if (src < 0) { + string·len(key) -> klen + string·char(key, klen - 1) -> tail + dup = false + if (tail == "/") { + klen - 1 -> cut + string·slice(key, 0, cut) -> cap + } else { + key -> cap + // 多返回值函数会把返回容器也存成同名裸成员:同名函数签名是 + // rwfunc 就说明 "…/" 条目已代表它,裸条目跳过(否则重复)。 + string·concat(kp, "/[0,0]") -> sigp + kvspace·get(sigp) -> sig + xv·parselangtype(sig) -> ks + if (ks == "rwfunc") { dup = true } + } + if (!dup) { + if (listing == "") { cap -> listing } else { listing + ", " + cap -> listing } + } + } + } + i + 1 -> i + } + } +} diff --git a/stdlib/networld/edit.kv b/stdlib/networld/edit.kv new file mode 100644 index 00000000..d9d0cedf --- /dev/null +++ b/stdlib/networld/edit.kv @@ -0,0 +1,165 @@ +// 欢迎加入kvspace世界 +// lib networld/edit —— 文件编辑语义(rwfunc,kv 源码)。 +// 与 networld/fs 的分工:fs 管字节与只读检索(size/read/write/readtext/writetext/match/ +// glob/scan/grep),edit 管「改写」这一层语义—— +// read 行号窗口视图(给 agent 看,不是全文 dump) +// write 覆盖写 +// replace 单处精确替换:old 必须恰命中 1 处,绝不「尽力而为」地改第一处 +// multi 事务式多处替换:全部成功才落盘 +// commit 原子落盘(写

.tmp → fs·rename 覆盖 p → 回读校验);write/replace/multi 都走它 +// 返回码 code:0 成功 / 1 未找到 / 2 歧义(命中多处)/ 3 文件不可读 / 4 写失败 / +// 5 回读不一致 / 6 两数组长度不等 / 7 非 UTF-8(拒绝改写二进制文件)。 +// 文案由调用方(harness)决定,本库不出话术。 +// 文本约定同 fs:文件按 UTF-8 解码为 []char/utf32 后操作,落盘再编码回 UTF-8。 + +lib networld/edit { + // read(p, from, lines) -> (view, total, sz):行号窗口视图。 + // 从第 from 行(1 起)起取 lines 行,每行前缀 "N: "; + // from < 1 视为 1,lines <= 0 视为到末尾。sz = 字节数(-1 = 不可读); + // 超过 8MB 不返回内容:view 空、total = -1(改用 fs·read 分片读); + // 非 UTF-8 文件同样不返回:view 空、total = -2。 + rwfunc read(p:[]char/utf32, from:int64, lines:int64) -> ( + view:[]char/utf32, total:int64, sz:int64) { + networld/fs·readtext(p) -> (t, sz) + "" -> view + total = 0 + if (sz >= 0) { + networld/fs·isutf8(p) -> u8ok + if (u8ok == false) { + total = -2 + } else { + if (sz > 8388608) { + total = -1 + } else { + parts:[int64]·[]char/utf32 = {} + string·lines(t) -> (parts, total) + lo = from + if (lo < 1) { lo = 1 } + hi = total + if (lines > 0 && lo + lines - 1 < total) { hi = lo + lines - 1 } + i = lo + while (i <= hi) { + kvspace·listn(parts, i - 1) -> kk + kvspace·get(parts, kk) -> ln + string·formatint(i, 10) -> no + string·concat(view, no) -> view + string·concat(view, ": ") -> view + string·concat(view, ln) -> view + string·concat(view, "\n") -> view + i + 1 -> i + } + } + } + } + } + + // commit(p, t) -> code:**原子落盘** —— 写 `

.tmp` → rename 覆盖 p → 回读校验。 + // 4 写失败 / 5 回读不一致 / 0 成功;失败时 p 保持原样(不留半截文件)。 + rwfunc commit(p:[]char/utf32, t:[]char/utf32) -> (code:int64) { + code = 4 + string·concat(p, ".tmp") -> tp + networld/fs·writetext(tp, t) -> wn + if (wn >= 0) { + networld/fs·rename(tp, p) -> rc + if (rc == 0) { + networld/fs·readtext(p) -> (back, _) + if (string·cmp(back, t) == 0) { code = 0 } else { code = 5 } + } + } + } + + // write(p, t) -> (n, code):UTF-8 覆盖写(原子:临时文件 + rename)+ 回读校验。 + // n = 写入字节数(失败 -1) + rwfunc write(p:[]char/utf32, t:[]char/utf32) -> (n:int64, code:int64) { + n = 0 + networld/edit·commit(p, t) -> code + if (code == 0) { string·len(t) -> n } + } + + // replace(p, old, new, dry) -> (done, hit, code):单处精确替换。 + // dry = 1 只预演(不落盘);old 命中数必须恰为 1,否则一个字节都不动。 + rwfunc replace(p:[]char/utf32, old:[]char/utf32, new:[]char/utf32, + dry:int64) -> (done:int64, hit:int64, code:int64) { + done = 0 + hit = 0 + networld/fs·isutf8(p) -> u8ok + if (u8ok == false) { + code = 7 + } else { + networld/fs·readtext(p) -> (t, sz) + if (sz < 0) { + code = 3 + } else { + string·count(t, old) -> hit + if (hit == 0) { + code = 1 + } else { + if (hit > 1) { + code = 2 + } else { + string·replace(t, old, new) -> (t2, _) + if (dry == 1) { + done = 1 + code = 0 + } else { + networld/edit·commit(p, t2) -> code + if (code == 0) { done = 1 } + } + } + } + } + } + } + + // multi(p, olds, news, dry) -> (done, bad, code):事务式多处替换。 + // 逐条精确替换(每条 old 须恰命中 1 处),全部成功才落盘; + // bad = 失败下标(-1 = 无失败),dry = 1 只预演。 + rwfunc multi(p:[]char/utf32, olds:*[int64]·[]char/utf32, + news:*[int64]·[]char/utf32, dry:int64) -> ( + done:int64, bad:int64, code:int64) { + kvspace·listlen(olds) -> cnt + kvspace·listlen(news) -> m + done = 0 + bad = -1 + networld/fs·isutf8(p) -> u8ok + if (u8ok == false) { + code = 7 + } else { + if (cnt != m) { + code = 6 + } else { + networld/fs·readtext(p) -> (t, sz) + if (sz < 0) { + code = 3 + } else { + code = 0 + i = 0 + while (i < cnt) { + kvspace·listn(olds, i) -> ko + kvspace·listn(news, i) -> kn + kvspace·get(olds, ko) -> o + kvspace·get(news, kn) -> w + string·count(t, o) -> c + if (c == 0) { + bad = i + code = 1 + break + } + if (c > 1) { + bad = i + code = 2 + break + } + string·replace(t, o, w) -> (t2, _) + t = t2 + done = done + 1 + i = i + 1 + } + if (bad < 0 && dry != 1) { + networld/edit·commit(p, t) -> code + } + } + } + } + } +} diff --git a/stdlib/networld/fs.kv b/stdlib/networld/fs.kv index 3320d692..ed6fc659 100644 --- a/stdlib/networld/fs.kv +++ b/stdlib/networld/fs.kv @@ -6,7 +6,6 @@ // match 名字段 shell 通配(* 任意串、? 单字符) // glob 按通配展开路径 // grep 逐行查找(文件或目录下匹配 glob 的成员) -// multi 事务式多处编辑(逐条精确替换,全部成功才落盘) // 文本约定:文件按 UTF-8 解码为 []char/utf32 后操作(只有定宽的 char/utf32 可 // 索引 / 切片);落盘再编码回 UTF-8。字节级 I/O 仍走 native rwir。 @@ -161,48 +160,8 @@ lib networld/fs { string·concat(hits, h) -> hits } } - i = i + 1 - } - } - } - // multi(path, olds, news) -> (done, bad):事务式多处编辑。按序对文件文本做 - // 逐条精确替换,每条 old 须恰命中 1 处;全部成功才落盘。 - // bad = -1 全部成功,done = 条数(文件已落盘) - // bad >= 0 该下标编辑失败(未命中或多处命中),文件不变 - // bad = -2 文件不可读 bad = -3 olds / news 长度不等 - rwfunc multi(path:[]char/utf32, olds:*[int64]·[]char/utf32, - news:*[int64]·[]char/utf32) -> (done:int64, bad:int64) { - kvspace·listlen(olds) -> cnt - kvspace·listlen(news) -> m - done = 0 - bad = -1 - if (cnt != m) { - bad = -3 - } else { - networld/fs·readtext(path) -> (t, sz) - if (sz < 0) { - bad = -2 - } else { - i = 0 - while (i < cnt) { - kvspace·listn(olds, i) -> ko - kvspace·listn(news, i) -> kn - kvspace·get(olds, ko) -> o - kvspace·get(news, kn) -> w - string·count(t, o) -> c - if (c != 1) { - bad = i - break - } - string·replace(t, o, w) -> (t2, _) - t = t2 - done = done + 1 i = i + 1 } - if (bad < 0) { - networld/fs·writetext(path, t) -> wn - } } } - } } diff --git a/stdlib/networld/python.kv b/stdlib/networld/python.kv new file mode 100644 index 00000000..cd8076d1 --- /dev/null +++ b/stdlib/networld/python.kv @@ -0,0 +1,13 @@ +// 欢迎加入kvspace世界 +// lib networld/python —— 跑一段 python3 代码取 stdout(rwfunc,kv 源码)。 +// run(code) -> out:等价 `python3 -c code`,返回 stdout 文本(stderr 一并捕获但不返回)。 +// 实现在 networld/proc·exec 之上;要退出码或 stderr 就直接用 proc·exec。 + +lib networld/python { + rwfunc run(code:[]char/utf32) -> (out:[]char/utf32) { + noenv:[int64]·[]char/utf32 = {} + a:[int64]·[]char/utf32 = {"python3", "-c", code} + networld/proc·exec(a, noenv) -> ret, o, e + o -> out + } +} diff --git a/stdlib/networld/shell.kv b/stdlib/networld/shell.kv new file mode 100644 index 00000000..ed28826a --- /dev/null +++ b/stdlib/networld/shell.kv @@ -0,0 +1,13 @@ +// 欢迎加入kvspace世界 +// lib networld/shell —— 跑一段 shell 命令取 stdout(rwfunc,kv 源码)。 +// run(cmd) -> out:等价 `bash -c cmd`,返回 stdout 文本(stderr 一并捕获但不返回)。 +// 实现在 networld/proc·exec 之上;要退出码或 stderr 就直接用 proc·exec。 + +lib networld/shell { + rwfunc run(cmd:[]char/utf32) -> (out:[]char/utf32) { + noenv:[int64]·[]char/utf32 = {} + a:[int64]·[]char/utf32 = {"bash", "-c", cmd} + networld/proc·exec(a, noenv) -> code, o, e + o -> out + } +} diff --git a/stdlib/string.kv b/stdlib/string.kv index 7300519a..3b46055d 100644 --- a/stdlib/string.kv +++ b/stdlib/string.kv @@ -565,4 +565,31 @@ lib string { i = i + 1 } } + // lang(s) -> lg:语言子标签(BCP-47 短码)—— 按文字系统判: + // 假名 > 谚文 > 汉字 > 拉丁;都不命中(数字 / 标点 / 空串)= "other"。 + // 命名对齐主流:HTML `lang`、ICU `uloc_getLanguage`、Go `x/text/language`、 + // JS `Intl.Locale.language`(不叫 script——与「脚本」撞词,且 script ≠ 语言)。 + rwfunc lang(s:[]char/utf32) -> (lg:[]char/utf32) { + han = false + kana = false + hangul = false + latin = false + n = string·len(s) + i = 0 + while (i < n) { + string·char(s, i) -> c + string·ord(c) -> o + if (o >= 12352) { if (o <= 12543) { kana = true } } // 平/片假名 + if (o >= 19968) { if (o <= 40959) { han = true } } // 汉字 + if (o >= 44032) { if (o <= 55215) { hangul = true } } // 谚文 + if (o >= 65) { if (o <= 90) { latin = true } } // A-Z + if (o >= 97) { if (o <= 122) { latin = true } } // a-z + i = i + 1 + } + "other" -> lg + if (latin) { "en" -> lg } + if (han) { "zh" -> lg } + if (hangul) { "ko" -> lg } + if (kana) { "ja" -> lg } + } } From 3c02020606d79719b267e08624b6025416944f46 Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Tue, 15 Sep 2026 09:39:20 +0800 Subject: [PATCH 2/5] =?UTF-8?q?runtime:=20xv=20=E5=A4=9A=E5=86=99=E5=8F=82?= =?UTF-8?q?=E6=A7=BD=EF=BC=9Bnetworld/fs=20=E8=A1=A5=20rename=20/=20isutf8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - rwir_xvalue.c:新增 xv_write_slot——写第 i 个写参槽(原 kvlangBuiltinWriteResult 只写 writes[0]),支撑多输出。 - runtime-rs:注册 networld/fs·rename、networld/fs·isutf8 两个 rwir。 - rwir_string / rwir_vthread / myrwircaps / rwir_internal.h:配套调整。 Part of #170 Co-Authored-By: Claude Code --- runtime-rs/src/rwir/mod.rs | 23 ++++++++++++ runtime-rs/src/rwir/networld/fs.rs | 20 ++++++++++ runtime/src/myrwircaps.c | 1 + runtime/src/rwir_internal.h | 1 + runtime/src/rwir_string.c | 10 ++--- runtime/src/rwir_vthread.c | 16 +++++++- runtime/src/rwir_xvalue.c | 59 ++++++++++++++++++++++++++++++ 7 files changed, 123 insertions(+), 7 deletions(-) diff --git a/runtime-rs/src/rwir/mod.rs b/runtime-rs/src/rwir/mod.rs index 0c24de01..01ec5cda 100644 --- a/runtime-rs/src/rwir/mod.rs +++ b/runtime-rs/src/rwir/mod.rs @@ -145,6 +145,20 @@ pub const MYRWIRCAPS: &[(&str, Rwir)] = &[ wp: &["int64"], }, ), + ( + "networld/fs·rename", + Rwir { + rp: &["[]char/utf8|[]char/utf32", "[]char/utf8|[]char/utf32"], + wp: &["int64"], + }, + ), + ( + "networld/fs·isutf8", + Rwir { + rp: &["[]char/utf8|[]char/utf32"], + wp: &["bool"], + }, + ), ( "networld/fs·append", Rwir { @@ -190,6 +204,11 @@ pub fn rwirmap() -> &'static HashMap<&'static str, &'static Rwir> { pub fn register(eng: &Engine) { for (op, r) in MYRWIRCAPS { + // 一致性闸门:登记了签名却漏了 is_inproc 的 op,调用会被当外部 rwir 排队等别人兑现 + // (表现为调用挂起,不是报错)——这里一次性喊出来。 + if !is_inproc(op) { + eprintln!("[rwir] {op} 在 MYRWIRCAPS 但不在 is_inproc:调用会挂起,请补登记"); + } let rp: Vec = r.rp.iter().map(|s| cs(s)).collect(); let wp: Vec = r.wp.iter().map(|s| cs(s)).collect(); let rpp: Vec<*const c_char> = rp.iter().map(|c| c.as_ptr()).collect(); @@ -228,10 +247,12 @@ pub fn is_inproc(op: &str) -> bool { | "networld/fs·read" | "networld/fs·write" | "networld/fs·append" + | "networld/fs·rename" | "networld/fs·list" | "networld/fs·del" | "networld/fs·mkdir" | "networld/fs·exists" + | "networld/fs·isutf8" ) } @@ -254,10 +275,12 @@ pub fn dispatch(eng: &Engine, op: &str, pc: &str) { "networld/fs·read" => networld::fs::read(eng, pc), "networld/fs·write" => networld::fs::write(eng, pc), "networld/fs·append" => networld::fs::append(eng, pc), + "networld/fs·rename" => networld::fs::rename(eng, pc), "networld/fs·list" => networld::fs::list(eng, pc), "networld/fs·del" => networld::fs::del(eng, pc), "networld/fs·mkdir" => networld::fs::mkdir(eng, pc), "networld/fs·exists" => networld::fs::exists(eng, pc), + "networld/fs·isutf8" => networld::fs::isutf8(eng, pc), "kvlang·vet" => { let out = kvlanglayout::vet(eng, &eng.read0(pc)); eng.set_kv(&eng.write0(pc), &out); diff --git a/runtime-rs/src/rwir/networld/fs.rs b/runtime-rs/src/rwir/networld/fs.rs index 4e68df03..0508912b 100644 --- a/runtime-rs/src/rwir/networld/fs.rs +++ b/runtime-rs/src/rwir/networld/fs.rs @@ -52,6 +52,15 @@ pub fn append(eng: &Engine, pc: &str) { eng.set_tlv_encoded(&eng.write0(pc), "int64", &n.to_le_bytes(), &[]); } +/// networld/fs·rename(src, dst) -> code:改名/移动(POSIX rename 语义,同文件系统内原子); +/// 0 成功 / -1 失败。配合 write 临时文件 + rename 可得「要么全有要么全无」的原子落盘。 +pub fn rename(eng: &Engine, pc: &str) { + let src = eng.read0(pc); + let dst = eng.read_at(pc, 1); + let code = std::fs::rename(&src, &dst).map(|_| 0i64).unwrap_or(-1); + eng.set_tlv_encoded(&eng.write0(pc), "int64", &code.to_le_bytes(), &[]); +} + /// networld/fs·list(p) -> names:列目录 p 的成员名(名字序)。p 必须是目录,否则空列表。 pub fn list(eng: &Engine, pc: &str) { let path = eng.read0(pc); @@ -96,6 +105,17 @@ pub fn exists(eng: &Engine, pc: &str) { eng.set_tlv_encoded(&eng.write0(pc), "bool", &[b as u8], &[]); } +/// networld/fs·isutf8(p) -> b:p 的内容是否是合法 UTF-8(缺失/不可读 = false)。 +/// 文本级改写(networld/edit)在落盘前用它把二进制文件挡在外面——解码成 []char/utf32 +/// 再写回是有损的,非法字节会被替换字符吃掉。 +pub fn isutf8(eng: &Engine, pc: &str) { + let path = eng.read0(pc); + let b = std::fs::read(&path) + .map(|bytes| std::str::from_utf8(&bytes).is_ok()) + .unwrap_or(false); + eng.set_tlv_encoded(&eng.write0(pc), "bool", &[b as u8], &[]); +} + fn read_slice(path: &str, start: i64, len: i64) -> Vec { let Ok(mut f) = std::fs::File::open(path) else { return Vec::new(); diff --git a/runtime/src/myrwircaps.c b/runtime/src/myrwircaps.c index 1f86ef14..4af96b3c 100644 --- a/runtime/src/myrwircaps.c +++ b/runtime/src/myrwircaps.c @@ -144,6 +144,7 @@ static const struct { const char *op; kvlangBuiltinFn fn; } myrwircaps[] = { {"xv·at", kvlangBuiltinXvAt}, {"xv·set", kvlangBuiltinXvSet}, {"xv·reshape", kvlangBuiltinXvReshape}, {"xv·reinterpret", kvlangBuiltinXvReinterpret}, {"xv·langtype", kvlangBuiltinXvLangtype}, {"xv·bodylen", kvlangBuiltinXvBodylen}, + {"xv·parselangtype", kvlangBuiltinXvParselangtype}, {"string·set", kvlangBuiltinStringSet}, {"string·char", kvlangBuiltinStringChar}, {"string·ord", kvlangBuiltinStringOrd}, {"string·cmp", kvlangBuiltinStringCmp}, {"string·find", kvlangBuiltinStringFind}, {"string·len", kvlangBuiltinStringLen}, {"string·slice", kvlangBuiltinStringSlice}, {"string·concat", kvlangBuiltinStringConcat}, diff --git a/runtime/src/rwir_internal.h b/runtime/src/rwir_internal.h index 356ff004..ba529ac8 100644 --- a/runtime/src/rwir_internal.h +++ b/runtime/src/rwir_internal.h @@ -57,6 +57,7 @@ int kvlangBuiltinArray(kvlangFrame_t *f), kvlangBuiltinXvReinterpret(kvlangFrame_t *f), kvlangBuiltinXvLangtype(kvlangFrame_t *f), kvlangBuiltinXvBodylen(kvlangFrame_t *f), + kvlangBuiltinXvParselangtype(kvlangFrame_t *f), kvlangBuiltinScatter(kvlangFrame_t *f), kvlangBuiltinCompact(kvlangFrame_t *f), kvlangBuiltinAppend(kvlangFrame_t *f), kvlangBuiltinSlice(kvlangFrame_t *f), diff --git a/runtime/src/rwir_string.c b/runtime/src/rwir_string.c index 863698cd..877a67f8 100644 --- a/runtime/src/rwir_string.c +++ b/runtime/src/rwir_string.c @@ -91,7 +91,7 @@ int kvlangBuiltinStringSet(kvlangFrame_t *f) { int kvlangBuiltinStringChar(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); if (n < 2) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.char requires string and index"); } - if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); } + { const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } } int idx = (int)kvlangScalarI64(kvlangXvalueScalar(&in[1])); int rn; uint32_t *r = string_runes(&in[0], &rn); int rc; @@ -104,7 +104,7 @@ int kvlangBuiltinStringChar(kvlangFrame_t *f) { int kvlangBuiltinStringOrd(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); if (n < 1) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.ord requires a string"); } - if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); } + { const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } } int rn; uint32_t *r = string_runes(&in[0], &rn); kvlangXvalue_t e; kvlangXvalueNewInt64(&e, rn == 0 ? -1 : (int64_t)r[0]); int rc = kvlangBuiltinWriteResult(f, &e); kvlangXvalueFree(&e); free(r); kvlangBuiltinFreeInputs(in, n); @@ -124,7 +124,7 @@ int kvlangBuiltinStringCmp(kvlangFrame_t *f) { int kvlangBuiltinStringFind(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); if (n < 2) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.find requires two strings"); } - if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); } + { const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } } int hn; uint32_t *hay = string_runes(&in[0], &hn); int nn; uint32_t *needle = string_runes(&in[1], &nn); int64_t r = -1; @@ -139,7 +139,7 @@ int kvlangBuiltinStringLen(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); int len = 0; if (n > 0) { - if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); } + { const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } } if (kvlangXvalueKindIs(&in[0], KVSPACE_KIND_CHAR)) len = kvlangXvalueArrayLen(&in[0]); else { uint32_t *r = string_runes(&in[0], &len); free(r); } } @@ -151,7 +151,7 @@ int kvlangBuiltinStringLen(kvlangFrame_t *f) { int kvlangBuiltinStringSlice(kvlangFrame_t *f) { kvlangXvalue_t in[3]; int n = kvlangBuiltinReadInputs(f, in, 3); if (n < 3) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.slice requires string, start, end"); } - if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); } + { const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } } int lo = (int)kvlangScalarI64(kvlangXvalueScalar(&in[1])), hi = (int)kvlangScalarI64(kvlangXvalueScalar(&in[2])); int rn; uint32_t *r = string_runes(&in[0], &rn); if (lo < 0 || hi > rn || lo > hi) { free(r); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "IndexError: at: slice index out of bounds (lo=%d hi=%d char count=%d)", lo, hi, rn); } diff --git a/runtime/src/rwir_vthread.c b/runtime/src/rwir_vthread.c index 65d30c9f..6aa12afb 100644 --- a/runtime/src/rwir_vthread.c +++ b/runtime/src/rwir_vthread.c @@ -34,12 +34,24 @@ int kvlangBuiltinVthreadRun(kvlangFrame_t *f) { kvlangBuiltinFreeInputs(in, n); char *subpc = NULL, *status = NULL; kvlangVthreadGet(f->kv, vid, &subpc, &status); - free(status); free(vid); + free(status); if (!subpc || !subpc[0]) { free(subpc); kvlangBuiltinNextPc(f); return 0; } char *subout = NULL; int rc = kvlangKvcpuExecuteMode(f->kv, subpc, KVMODE_RETURN, &subout); free(subpc); - if (rc < 0) { free(subout); return kvlangBuiltinSetErr(f, "RuntimeError: vthread.run failed"); } + if (rc < 0) { + /* 子 vthread 自己失败(status=error)是**子**的终态:监督者要能活着检视 + * /vthread//‥status 与 ‥error/msg(一个子任务失败不该杀掉整条会话)。 + * 只有驱动本身失败才冒泡给父。 */ + char *sp = NULL, *st = NULL; + kvlangVthreadGet(f->kv, vid, &sp, &st); + free(sp); + int child_failed = st && strcmp(st, "error") == 0; + free(st); free(vid); free(subout); + if (child_failed) { kvlangBuiltinNextPc(f); return 0; } + return kvlangBuiltinSetErr(f, "RuntimeError: vthread.run failed"); + } + free(vid); if (rc == 1) { *f->yield_pc = subout; return 0; } kvlangBuiltinNextPc(f); return 0; diff --git a/runtime/src/rwir_xvalue.c b/runtime/src/rwir_xvalue.c index 17deb257..be9d3665 100644 --- a/runtime/src/rwir_xvalue.c +++ b/runtime/src/rwir_xvalue.c @@ -368,3 +368,62 @@ int kvlangBuiltinXvBodylen(kvlangFrame_t *f) { kvlangXvalueFree(&r); return rc; } + +/* 写第 i 个写参槽(多输出用;kvlangBuiltinWriteResult 只写 writes[0])。 */ +static void xv_write_slot(kvlangFrame_t *f, int i, const kvlangXvalue_t *v) { + char *owned = NULL; + const char *fr = f->frame_root; + if (!fr) { + owned = kvlangKeytreeFrameRoot(f->pc); + fr = owned; + } + char *key = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[i].name); + kvlangKvPair_t pair = {key, *v}; + char err[256]; + kvlangKvSet(f->kv, &pair, 1, err, sizeof err); + free(key); + free(owned); +} + +/* xv·parselangtype(v) -> (kind[, ndim]):解析 v 的 langtype(三轴正交的第三轴)。 + * kind —— 剥掉前导 [dims] 与 "def " 前缀后的基名:char / char/utf8 / float64 / + * rwfunc / rwir / langtype / stringkeymap / index … + * ndim —— 维数(可省:只绑一个写参时不出) + * 用途:按类型给 KV 成员分类(如 def langtype = 形参槽、def rwir = 路由头), + * 调用方不必对 langtype 串做字符串手术。 */ +int kvlangBuiltinXvParselangtype(kvlangFrame_t *f) { + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: xv.parselangtype requires a write param (-> kind[, ndim])"); + kvspaceHead_t h; + const char *lt = ""; + if (xv_head1(f, &h) == 0) + lt = (const char *)h.langtype; + kvlangLangtype p; + kvlangLangtypeParse((const uint8_t *)lt, &p); + const char *k = p.kind ? p.kind : ""; + int32_t kl = p.kind_len; + if (kl >= 4 && strncmp(k, "def ", 4) == 0) { + k += 4; + kl -= 4; + } + char buf[256]; + if (kl < 0) + kl = 0; + if ((size_t)kl >= sizeof buf) + kl = (int32_t)sizeof buf - 1; + memcpy(buf, k, (size_t)kl); + buf[kl] = 0; + kvlangXvalue_t vk; + kvlangXvalueNewCharUtf8(&vk, buf); + xv_write_slot(f, 0, &vk); + kvlangXvalueFree(&vk); + if (f->inst->nw > 1) { + kvlangXvalue_t vn; + kvlangXvalueNewInt64(&vn, p.ndim); + xv_write_slot(f, 1, &vn); + kvlangXvalueFree(&vn); + } + kvlangBuiltinNextPc(f); + return 0; +} From 2843623fd51596b5618b85c92a95232dffa57231 Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Tue, 15 Sep 2026 09:39:20 +0800 Subject: [PATCH 3/5] =?UTF-8?q?tutorial:=20=E8=A1=A5=20reflect=20/=20edit?= =?UTF-8?q?=20/=20shell=20/=20python=20=E7=94=A8=E4=BE=8B=EF=BC=9Bwalk=5Fl?= =?UTF-8?q?ib=20=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 13-stdlib/kvlang/(reflect)、13-stdlib/string/08-lang、 14-networld/{12-edit,13-shell,14-python} - 07-lib/walk_lib、14-networld/11-fs-text 跟随调整 Part of #170 Co-Authored-By: Claude Code --- tutorial/07-lib/walk_lib.kv | 41 +++++++++++--- tutorial/13-stdlib/kvlang/01-reflect.kv | 26 +++++++++ tutorial/13-stdlib/string/08-lang.kv | 18 +++++++ tutorial/14-networld/11-fs-text.kv | 20 +------ tutorial/14-networld/12-edit.kv | 72 +++++++++++++++++++++++++ tutorial/14-networld/13-shell.kv | 10 ++++ tutorial/14-networld/14-python.kv | 10 ++++ 7 files changed, 173 insertions(+), 24 deletions(-) create mode 100644 tutorial/13-stdlib/kvlang/01-reflect.kv create mode 100644 tutorial/13-stdlib/string/08-lang.kv create mode 100644 tutorial/14-networld/12-edit.kv create mode 100644 tutorial/14-networld/13-shell.kv create mode 100644 tutorial/14-networld/14-python.kv diff --git a/tutorial/07-lib/walk_lib.kv b/tutorial/07-lib/walk_lib.kv index 479a5326..7dda192a 100644 --- a/tutorial/07-lib/walk_lib.kv +++ b/tutorial/07-lib/walk_lib.kv @@ -1,5 +1,8 @@ -// 递归打印 /lib 代码树:kvspace·listlen/kvspace·listn 逐层列举 + 自递归 -// 目录名以 "/" 结尾则下钻;kvspace·listn 返回 utf8,显式 char/utf32 转码后才能喂给 string·find(utf32) +// 递归遍历代码树 /lib:逐层列举成员名 + 其值的 langtype(目录为 index)。 +// 目录判定不看 list 的尾斜杠(三后端不统一):<名>/ 是结构目录, +// <名>· 是成员前缀目录(pkg 成员容器,如 /lib/string· 装 string·upper…)。 +// 两种拼写都试,先 / 后 ·。kvspace·listn 返回 utf8,须显式转 char/utf32 +// 才能喂给 string·find(utf32)。 // 期望输出: // /lib/ // print @@ -10,15 +13,41 @@ rwfunc walk(path:[]char/utf32, indent:[]char/utf32) -> () { kvspace·listlen(path) -> n 0 -> i + "" -> last while (i < n) { kvspace·listn(path, i) -> nm8 char/utf32(nm8) -> nm - println(indent, nm) + path + nm -> full + "" -> child string·find(nm, "/") -> sl if (sl >= 0) { - path + nm -> child - indent + " " -> ind2 - walk(child, ind2) + full -> child + } else { + full + "/" -> d1 + kvspace·listlen(d1) -> c1 + if (c1 > 0) { + d1 -> child + } else { + full + "·" -> d2 + kvspace·listlen(d2) -> c2 + if (c2 > 0) { + d2 -> child + } + } + } + kvspace·get(full) -> v + if (v == None) { + println(indent, nm) + } else { + xv·langtype(v) -> lt + char/utf32(lt) -> lts + println(indent, nm, lts) + } + // 孪生条目去重:fs 会同时给出 X 与 X/(都指向同一目录),只走一次。 + string·cmp(child, last) != 0 -> fresh + if (string·len(child) > 0 && fresh) { + walk(child, indent + " ") + child -> last } i + 1 -> i } diff --git a/tutorial/13-stdlib/kvlang/01-reflect.kv b/tutorial/13-stdlib/kvlang/01-reflect.kv new file mode 100644 index 00000000..e9e10132 --- /dev/null +++ b/tutorial/13-stdlib/kvlang/01-reflect.kv @@ -0,0 +1,26 @@ +// kvlang/reflect:自省 —— 枚举一棵 lib 子树的能力名 +// fnlist(name) -> listing:/lib/· 下的能力名,逗号连接 +// 分类看 xvalue 的 langtype(三轴正交:ref / storetype / langtype),不看名字猜: +// def langtype(形参 / 返回槽)跳过;def rwir(路由头)与普通数据(常量)保留 +// 期望输出: +// math= E, Pi, Tau +// kvlang= dump, format, layout, vet +// has-allinset= true +// has-param= false +// string-nonempty= true +// missing-len= 0 +rwfunc test() -> () { + kvlang/reflect·fnlist("math") -> m + println("math=", m) + kvlang/reflect·fnlist("kvlang") -> k + println("kvlang=", k) + kvlang/reflect·fnlist("string") -> s + string·contains(s, "allinset") -> a + println("has-allinset=", a) + string·contains(s, "[0,") -> p + println("has-param=", p) + string·len(s) -> sl + println("string-nonempty=", sl > 0) + kvlang/reflect·fnlist("no_such_lib_xyz") -> e + println("missing-len=", string·len(e)) +} diff --git a/tutorial/13-stdlib/string/08-lang.kv b/tutorial/13-stdlib/string/08-lang.kv new file mode 100644 index 00000000..24bff01d --- /dev/null +++ b/tutorial/13-stdlib/string/08-lang.kv @@ -0,0 +1,18 @@ +// string·lang:语言子标签(BCP-47 短码)—— 按文字系统判:假名 > 谚文 > 汉字 > 拉丁 +// 期望输出: +// zh= zh +// en= en +// ja= ja +// ko= ko +// mix= zh +// num= other +// empty= other +rwfunc test() -> () { + println("zh=", string·lang("读一下文件")) + println("en=", string·lang("read this file")) + println("ja=", string·lang("これはテスト")) + println("ko=", string·lang("테스트")) + println("mix=", string·lang("hello 世界")) + println("num=", string·lang("123 !!")) + println("empty=", string·lang("")) +} diff --git a/tutorial/14-networld/11-fs-text.kv b/tutorial/14-networld/11-fs-text.kv index a18fa608..8185348a 100644 --- a/tutorial/14-networld/11-fs-text.kv +++ b/tutorial/14-networld/11-fs-text.kv @@ -1,7 +1,7 @@ -// networld/fs 文本级 rwfunc:readtext / writetext / match / glob / grep / multi +// networld/fs 文本级 rwfunc:readtext / writetext / match / glob / grep // 文件 ↔ 文本按 UTF-8 往返(字节级 read/write 仍是 native rwir) // match:名字段通配(* 任意串、? 单字符) glob:按通配展开路径(\n 连接) -// grep:文件或目录下匹配 glob 的成员逐行查找 multi:事务式多处编辑 +// grep:文件或目录下匹配 glob 的成员逐行查找 // 期望输出: // size= 12 // text= hello world @@ -9,10 +9,6 @@ // match= true false // glob= /tmp/kvlang_fs_text/a.txt // grep= /tmp/kvlang_fs_text/a.txt:1:hello world -// multi= 1 -1 -// after= HELLO world -// bad= 0 -// keep= HELLO world rwfunc test() -> () { d = "/tmp/kvlang_fs_text" a = "/tmp/kvlang_fs_text/a.txt" @@ -37,16 +33,4 @@ rwfunc test() -> () { println("glob=", g) networld/fs·grep("world", d, "*.txt") -> hits println("grep=", hits) - olds:[int64]·[]char/utf32 = {"hello"} - news:[int64]·[]char/utf32 = {"HELLO"} - networld/fs·multi(a, olds, news) -> (done, bad) - println("multi=", done, bad) - networld/fs·readtext(a) -> (t2, sz2) - println("after=", t2) - old2:[int64]·[]char/utf32 = {"L"} - new2:[int64]·[]char/utf32 = {"X"} - networld/fs·multi(a, old2, new2) -> (d2, bad2) - println("bad=", bad2) - networld/fs·readtext(a) -> (t3, sz3) - println("keep=", t3) } diff --git a/tutorial/14-networld/12-edit.kv b/tutorial/14-networld/12-edit.kv new file mode 100644 index 00000000..2912c474 --- /dev/null +++ b/tutorial/14-networld/12-edit.kv @@ -0,0 +1,72 @@ +// networld/edit:文件编辑语义 —— 行号窗口读 / 精确替换(唯一命中)/ 事务式多处替换 +// read 行号窗口视图(from 行起 lines 行,lines <= 0 = 到末尾) +// write 覆盖写 + 回读校验 +// replace 单处精确替换:old 须恰命中 1 处,否则一个字节都不动(dry = 1 只预演) +// multi 事务式多处替换:全部成功才落盘 +// code 0 成功 / 1 未找到 / 2 歧义 / 3 不可读 / 4 写失败 / 5 回读不一致 / 6 长度不等 +// 期望输出: +// window= 2: beta +// 3: alpha +// (total,sz)=( 4 , 23 ) +// dry= ( 1 , 1 , 0 ) +// unchanged= alpha +// ambiguous= ( 0 , 2 , 2 ) +// missing= ( 0 , 0 , 1 ) +// ok= ( 1 , 1 , 0 ) +// after= alpha +// multi= ( 2 , -1 , 0 ) +// rolled= ( 1 , 1 , 2 ) +// keep= alpha +// lenbad= ( 0 , -1 , 6 ) +// final= x +// tmpleft= 0 +// bin-total= -2 +// bin-code= 7 +rwfunc test() -> () { + d = "/tmp/kvlang_edit" + p = "/tmp/kvlang_edit/t.txt" + noenv:[int64]·[]char/utf32 = {} + clean:[int64]·[]char/utf32 = {"rm", "-rf", "/tmp/kvlang_edit"} + networld/proc·exec(clean, noenv) -> rc + networld/fs·mkdir(d) -> mc + networld/fs·writetext(p, "alpha\nbeta\nalpha\ngamma\n") -> w0 + networld/edit·read(p, 2, 2) -> (v1, t1, s1) + println("window=", v1) + println("(total,sz)=(", t1, ",", s1, ")") + networld/edit·replace(p, "beta", "BETA", 1) -> (d1, h1, c1) + println("dry= (", d1, ",", h1, ",", c1, ")") + networld/fs·readtext(p) -> (unchanged, _) + println("unchanged=", unchanged) + networld/edit·replace(p, "alpha", "A", 0) -> (d2, h2, c2) + println("ambiguous= (", d2, ",", h2, ",", c2, ")") + networld/edit·replace(p, "zzz", "A", 0) -> (d3, h3, c3) + println("missing= (", d3, ",", h3, ",", c3, ")") + networld/edit·replace(p, "beta", "BETA", 0) -> (d4, h4, c4) + println("ok= (", d4, ",", h4, ",", c4, ")") + networld/fs·readtext(p) -> (after, _) + println("after=", after) + olds:[int64]·[]char/utf32 = {"BETA", "gamma"} + news:[int64]·[]char/utf32 = {"B", "G"} + networld/edit·multi(p, olds, news, 0) -> (md, mb, mc2) + println("multi= (", md, ",", mb, ",", mc2, ")") + olds2:[int64]·[]char/utf32 = {"B", "alpha"} + news2:[int64]·[]char/utf32 = {"C", "D"} + networld/edit·multi(p, olds2, news2, 0) -> (rd, rb, rc2) + println("rolled= (", rd, ",", rb, ",", rc2, ")") + networld/fs·readtext(p) -> (keep, _) + println("keep=", keep) + olds3:[int64]·[]char/utf32 = {"B"} + news3:[int64]·[]char/utf32 = {"C", "D"} + networld/edit·multi(p, olds3, news3, 0) -> (ld, lb, lc) + println("lenbad= (", ld, ",", lb, ",", lc, ")") + networld/edit·write(p, "x\n") -> (wn, wc) + networld/fs·readtext(p) -> (final, _) + println("final=", final) + networld/fs·glob("/tmp/kvlang_edit/*.tmp") -> left + println("tmpleft=", string·len(left)) + networld/shell·run("printf '\\377\\376' > /tmp/kvlang_edit/bin.dat") -> binw + networld/edit·read("/tmp/kvlang_edit/bin.dat", 1, 0) -> (bv, bt, bs) + println("bin-total=", bt) + networld/edit·replace("/tmp/kvlang_edit/bin.dat", "x", "y", 0) -> (bd, bh, bc) + println("bin-code=", bc) +} diff --git a/tutorial/14-networld/13-shell.kv b/tutorial/14-networld/13-shell.kv new file mode 100644 index 00000000..159dbda5 --- /dev/null +++ b/tutorial/14-networld/13-shell.kv @@ -0,0 +1,10 @@ +// networld/shell·run(cmd) -> out:bash -c 的 stdout 文本(stderr 不返回) +// 期望输出: +// out= hello shell +// vars= 42 +rwfunc test() -> () { + networld/shell·run("echo hello shell") -> o + println("out=", o) + networld/shell·run("x=42; echo $x") -> v + println("vars=", v) +} diff --git a/tutorial/14-networld/14-python.kv b/tutorial/14-networld/14-python.kv new file mode 100644 index 00000000..0d1d0768 --- /dev/null +++ b/tutorial/14-networld/14-python.kv @@ -0,0 +1,10 @@ +// networld/python·run(code) -> out:python3 -c 的 stdout 文本(stderr 不返回) +// 期望输出: +// out= 42 +// str= abc +rwfunc test() -> () { + networld/python·run("print(6*7)") -> o + println("out=", o) + networld/python·run("print('a'+'bc')") -> s + println("str=", s) +} From ba45277ea5a6a8d81af4016a3f22807b9272f7dc Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Tue, 15 Sep 2026 10:04:20 +0800 Subject: [PATCH 4/5] =?UTF-8?q?tutorial(reflect):=20=E6=9C=9F=E6=9C=9B?= =?UTF-8?q?=E8=BE=93=E5=87=BA=E8=A1=A5=20init?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fnlist 是客观自省:只跳过 def langtype(形参 / 返回槽),def rwir、普通 常量与 init 入口都保留。原期望漏了 init(每个 lib 都有),导致 shm/fs 两个 job 各 FAIL:1。本地复跑 PASS。 Co-Authored-By: Claude Code --- tutorial/13-stdlib/kvlang/01-reflect.kv | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tutorial/13-stdlib/kvlang/01-reflect.kv b/tutorial/13-stdlib/kvlang/01-reflect.kv index e9e10132..2732aaa2 100644 --- a/tutorial/13-stdlib/kvlang/01-reflect.kv +++ b/tutorial/13-stdlib/kvlang/01-reflect.kv @@ -1,10 +1,11 @@ // kvlang/reflect:自省 —— 枚举一棵 lib 子树的能力名 // fnlist(name) -> listing:/lib/· 下的能力名,逗号连接 // 分类看 xvalue 的 langtype(三轴正交:ref / storetype / langtype),不看名字猜: -// def langtype(形参 / 返回槽)跳过;def rwir(路由头)与普通数据(常量)保留 +// def langtype(形参 / 返回槽)跳过;def rwir(路由头)、普通数据(常量) +// 与 init 入口都保留 —— fnlist 是客观自省,是否过滤由调用方决定 // 期望输出: -// math= E, Pi, Tau -// kvlang= dump, format, layout, vet +// math= E, Pi, Tau, init +// kvlang= dump, format, init, layout, vet // has-allinset= true // has-param= false // string-nonempty= true From f8489e1aff4dbe2939bdb08c7777755c43a8ec2d Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Tue, 15 Sep 2026 13:22:18 +0800 Subject: [PATCH 5/5] =?UTF-8?q?kvspace=C2=B7members=EF=BC=9A=E5=88=97?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E5=B9=B6=E6=8A=98=E5=8F=A0=E5=90=8C=E5=90=8D?= =?UTF-8?q?=E5=AD=AA=E7=94=9F=E6=9D=A1=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 值与同名目录可以共存(kvspace/tutorial-durable/10-dir-file-coexist.sh), 此时列举会同时给出裸名 X 与目录名 X/(或 X·)—— 它们是两个实体, **后端不去重**(fs 发两条、redis 视场景而定,无统一规则)。 故在 stdlib 收口:新增 kvspace·members(path) -> (names, n),裸名后面 紧跟同名 X/(X·)时只保留带目录标记的那条(名字取 X/)。需要「这层 有哪些名字」的自省/遍历类调用方一律用它: - reflect·fnlist 改走 members(不再自己猜孪生) - walk_lib 同上(移除本地的去重猜测) - 01-reflect 期望随之调整 Co-Authored-By: Claude Code --- stdlib/kv.kv | 55 ++++++++++++++++++++ stdlib/kvlang/reflect.kv | 23 ++++----- tutorial/07-lib/walk_lib.kv | 69 ++++++++++++++----------- tutorial/13-stdlib/kvlang/01-reflect.kv | 4 +- 4 files changed, 108 insertions(+), 43 deletions(-) diff --git a/stdlib/kv.kv b/stdlib/kv.kv index b80f2033..642f059a 100644 --- a/stdlib/kv.kv +++ b/stdlib/kv.kv @@ -25,4 +25,59 @@ lib kvspace { kvspace·set(path, val) } } + // members(path) -> (names, n):列目录成员,并**折叠同名孪生条目**。 + // 值与同名目录可以共存(见 kvspace/tutorial-durable/10-dir-file-coexist.sh),此时 + // 列举会同时给出裸名 X 与目录名 X/(或 X·)——它们是两个实体,**后端不去重**(fs 发两 + // 条、redis 视场景而定,无统一规则)。需要「这层有哪些名字」的自省/遍历类调用方一律用 + // 本函数:裸名后面紧跟同名 X/(或 X·)时只保留带目录标记的那条,名字取 `X/`。 + // 调用方先声明空容器:`m:[int64]·[]char/utf32 = {}` + `kvspace·members(p) -> (m, n)`。 + rwfunc members(path:[]char/utf32) -> ( + names:*[int64]·[]char/utf32, n:int64) { + kvspace·listlen(path) -> total + n = 0 + i = 0 + while (i < total) { + kvspace·listn(path, i) -> m8 + char/utf32(m8) -> m + skip = false + string·len(m) -> ml + if (ml > 0) { + string·char(m, ml - 1) -> tail + if (tail != "/" && tail != "·") { + // 名字带 `.[…]`/`.src` 的兄弟把裸名与 `<名>/` 隔开 + // (列举按 cmp_coord 排序:fields, fields.[0,-1], …, fields/), + // 所以要向后扫完**同前缀**的条目,不能只看下一条。 + j = i + 1 + while (j < total) { + kvspace·listn(path, j) -> n8 + char/utf32(n8) -> nx + string·len(nx) -> xl + if (xl < ml) { + break + } + string·slice(nx, 0, ml) -> head + if (string·cmp(head, m) != 0) { + break + } + m + "/" -> t1 + m + "·" -> t2 + if (string·cmp(nx, t1) == 0) { + skip = true + break + } + if (string·cmp(nx, t2) == 0) { + skip = true + break + } + j = j + 1 + } + } + } + if (!skip) { + array·append(names, m) -> names + n = n + 1 + } + i = i + 1 + } + } } diff --git a/stdlib/kvlang/reflect.kv b/stdlib/kvlang/reflect.kv index 7713f519..5ccad0cc 100644 --- a/stdlib/kvlang/reflect.kv +++ b/stdlib/kvlang/reflect.kv @@ -9,6 +9,8 @@ // 其他数据 常量等普通 key —— 能力,名字原样 // 每个 rwfunc 在容器里有三条成员:`.[0,±k]`(形参)、`.src`(源码副本)、 // `/`(函数目录条目)。前者按 langtype 跳过,中者是源码副本跳过,后者去尾斜杠即函数名。 +// 成员一律经 `kvspace·members` 取:值与同名目录共存时后端会同时给出裸名与目录名(两个实体, +// 后端不去重),members 把它折成一条,这里就不必再猜「裸名是不是目录的孪生」。 // 用途:把一棵 lib 子树的「有什么能力」压成一行文本,供 LLM 生成能力记忆 / 工具清单。 // 命名沿用 Go/Rust 的 reflect(自省);不占用 `lib kvlang`——那是 kvlangbrief 与 // kvlang·vet/·layout/·format/·dump 四个 def rwir 路由头的命名空间。 @@ -17,11 +19,13 @@ lib kvlang/reflect { rwfunc fnlist(name:[]char/utf32) -> (listing:[]char/utf32) { "" -> listing "/lib/" + name + "·" -> mdir - kvspace·listlen(mdir) -> n + ms:[int64]·[]char/utf32 = {} + kvspace·members(mdir) -> (ms, n) i = 0 while (i < n) { - kvspace·listn(mdir, i) -> key - char/utf32(key) -> key + kvspace·listn(ms, i) -> mk + kvspace·get(ms, mk) -> k8 + char/utf32(k8) -> key string·concat(mdir, key) -> kp kvspace·get(kp) -> v xv·parselangtype(v) -> k @@ -30,21 +34,16 @@ lib kvlang/reflect { if (src < 0) { string·len(key) -> klen string·char(key, klen - 1) -> tail - dup = false if (tail == "/") { klen - 1 -> cut string·slice(key, 0, cut) -> cap } else { key -> cap - // 多返回值函数会把返回容器也存成同名裸成员:同名函数签名是 - // rwfunc 就说明 "…/" 条目已代表它,裸条目跳过(否则重复)。 - string·concat(kp, "/[0,0]") -> sigp - kvspace·get(sigp) -> sig - xv·parselangtype(sig) -> ks - if (ks == "rwfunc") { dup = true } } - if (!dup) { - if (listing == "") { cap -> listing } else { listing + ", " + cap -> listing } + if (listing == "") { + cap -> listing + } else { + listing + ", " + cap -> listing } } } diff --git a/tutorial/07-lib/walk_lib.kv b/tutorial/07-lib/walk_lib.kv index 7dda192a..f3f4d5b1 100644 --- a/tutorial/07-lib/walk_lib.kv +++ b/tutorial/07-lib/walk_lib.kv @@ -1,21 +1,29 @@ -// 递归遍历代码树 /lib:逐层列举成员名 + 其值的 langtype(目录为 index)。 -// 目录判定不看 list 的尾斜杠(三后端不统一):<名>/ 是结构目录, -// <名>· 是成员前缀目录(pkg 成员容器,如 /lib/string· 装 string·upper…)。 -// 两种拼写都试,先 / 后 ·。kvspace·listn 返回 utf8,须显式转 char/utf32 -// 才能喂给 string·find(utf32)。 +// 递归遍历一棵 lib 子树:列举成员名 + 叶子的 langtype(目录只列名),并下钻一层。 +// 粒度按实测定:本 pod 上单次 kvspace 操作约 17ms(redis RTT 主导),所以整棵 /lib +// (含 84 章规范树)或 string 包全下钻(3k+ 叶子)都是分钟级。这里: +// 1) 列 string 包自身成员(38 个函数的签名键 / 源码 / 目录条目,192 条); +// 2) 只挑一个函数目录(count/)下钻,演示递归 + 指令槽与标签的 langtype。 +// 成员经 `kvspace·members` 取:值与同名目录共存时后端会同时给出裸名与目录名(两个实体, +// 后端不去重),members 折成一条,遍历侧不必各自去重(见 lib kvspace 注释)。 +// 目录判定不看 list 的尾斜杠(三后端不统一):<名>/ 是结构目录,<名>· 是成员前缀目录。 +// 两种拼写都试,先 / 后 ·。kvspace·listn 返回 utf8,须显式转 char/utf32 才能索引(utf32)。 // 期望输出: -// /lib/ -// print -// println -// kvspace -// string -// math -rwfunc walk(path:[]char/utf32, indent:[]char/utf32) -> () { - kvspace·listlen(path) -> n +// /lib/string· +// upper +// upper.src +// count/ +// [0,-1] +// /lib/string·count/ +// [0,0] rwfunc +// ‥labels/ +// _do_9 +rwfunc walk(path:[]char/utf32, indent:[]char/utf32, down:int64) -> () { + ms:[int64]·[]char/utf32 = {} + kvspace·members(path) -> (ms, n) 0 -> i - "" -> last while (i < n) { - kvspace·listn(path, i) -> nm8 + kvspace·listn(ms, i) -> mk + kvspace·get(ms, mk) -> nm8 char/utf32(nm8) -> nm path + nm -> full "" -> child @@ -35,24 +43,27 @@ rwfunc walk(path:[]char/utf32, indent:[]char/utf32) -> () { } } } - kvspace·get(full) -> v - if (v == None) { - println(indent, nm) + if (child == "") { + kvspace·get(full) -> v + if (v == None) { + println(indent, nm) + } else { + xv·langtype(v) -> lt + char/utf32(lt) -> lts + println(indent, nm, lts) + } } else { - xv·langtype(v) -> lt - char/utf32(lt) -> lts - println(indent, nm, lts) - } - // 孪生条目去重:fs 会同时给出 X 与 X/(都指向同一目录),只走一次。 - string·cmp(child, last) != 0 -> fresh - if (string·len(child) > 0 && fresh) { - walk(child, indent + " ") - child -> last + println(indent, nm) + if (down > 0) { + walk(child, indent + " ", down - 1) + } } i + 1 -> i } } rwfunc test() -> () { - println("/lib/") - walk("/lib/", " ") + println("/lib/string·") + walk("/lib/string·", " ", 0) + println("/lib/string·count/") + walk("/lib/string·count/", " ", 1) } diff --git a/tutorial/13-stdlib/kvlang/01-reflect.kv b/tutorial/13-stdlib/kvlang/01-reflect.kv index 2732aaa2..1fa26d80 100644 --- a/tutorial/13-stdlib/kvlang/01-reflect.kv +++ b/tutorial/13-stdlib/kvlang/01-reflect.kv @@ -4,8 +4,8 @@ // def langtype(形参 / 返回槽)跳过;def rwir(路由头)、普通数据(常量) // 与 init 入口都保留 —— fnlist 是客观自省,是否过滤由调用方决定 // 期望输出: -// math= E, Pi, Tau, init -// kvlang= dump, format, init, layout, vet +// math= E, Pi, Tau +// kvlang= dump, format, layout, vet // has-allinset= true // has-param= false // string-nonempty= true