From 9fe7a1f467bead76a9fe70a32d80c43b313298ba Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Thu, 10 Sep 2026 22:22:23 +0800 Subject: [PATCH 1/5] =?UTF-8?q?extensions:=20=E8=AF=AD=E6=B3=95=E9=AB=98?= =?UTF-8?q?=E4=BA=AE=20keyword=20=E7=BB=84=E5=8A=A0=E5=85=A5=20in=EF=BC=88?= =?UTF-8?q?+=20=E5=AF=B9=E9=BD=90=20origin=20=E7=A7=BB=E9=99=A4=20object?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for x in y 的 in 补为控制关键字;type 组同步 origin 已删的 object kind。 Co-Authored-By: Claude Opus 4.8 --- extensions/kvlang/syntaxes/kvlang.tmLanguage.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/kvlang/syntaxes/kvlang.tmLanguage.json b/extensions/kvlang/syntaxes/kvlang.tmLanguage.json index 55435b3c..2e221c12 100644 --- a/extensions/kvlang/syntaxes/kvlang.tmLanguage.json +++ b/extensions/kvlang/syntaxes/kvlang.tmLanguage.json @@ -46,7 +46,7 @@ }, "keyword": { "name": "keyword.control.kvlang", - "match": "\\b(rwfunc|rwir|lib|struct|if|else|for|while|break|continue|return)\\b" + "match": "\\b(rwfunc|rwir|lib|struct|if|else|for|in|while|break|continue|return)\\b" }, "block_label": { "name": "entity.name.label.kvlang", From 42dc68a07c71ad002ca209fc27f48b098abc9ade Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Thu, 10 Sep 2026 22:25:33 +0800 Subject: [PATCH 2/5] =?UTF-8?q?runtime/tutorial:=20link/=E6=8C=87=E9=92=88?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E5=BB=B6=E7=BB=AD=20+=20=E5=90=84=20tutorial?= =?UTF-8?q?=20=E6=98=BE=E5=BC=8F=E8=A7=A3=E5=BC=95=E7=94=A8=E5=AF=B9?= =?UTF-8?q?=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit runtime builtin/builtin_kv/kv/kvcpu + runtime_internal.h 配合 link 不隐式 穿透;01-basics/03-control/04-ndarray/06-algo/12-struct/13-stdlib/ 14-networld/15-vthread 各 tutorial 同步显式 * 解引用与新语义。 Co-Authored-By: Claude Opus 4.8 --- runtime/src/builtin.c | 42 ++++++++++++++++-------- runtime/src/builtin_kv.c | 39 +++++++++++++++++++++- runtime/src/kv.c | 5 ++- runtime/src/kvcpu.c | 18 ++++------- runtime/src/runtime_internal.h | 2 ++ tutorial/01-basics/kv_mkindex_cap.kv | 10 ++++-- tutorial/01-basics/kv_tree.kv | 17 ++++++++-- tutorial/01-basics/strings.kv | 10 +++++- tutorial/03-control/for.kv | 12 ++++--- tutorial/04-ndarray/continuous.kv | 15 +++++---- tutorial/04-ndarray/fill_init.kv | 15 +++++---- tutorial/04-ndarray/for_compact.kv | 8 +++-- tutorial/04-ndarray/sparse_literal.kv | 17 ++++++++-- tutorial/06-algo/map_reduce.kv | 45 ++++++++++++++++---------- tutorial/06-algo/word_count.kv | 45 ++++++++++++++++---------- tutorial/12-struct/03-linked-list.kv | 17 ++++++---- tutorial/12-struct/06-list-reverse.kv | 23 ++++++++----- tutorial/12-struct/07-list-local.kv | 20 +++++++----- tutorial/13-stdlib/duration/max_min.kv | 17 ++++++---- tutorial/13-stdlib/time/elapsed_ns.kv | 9 ++++-- tutorial/13-stdlib/time/since.kv | 9 ++++-- tutorial/14-networld/01-exec.kv | 14 +++++--- tutorial/14-networld/02-bash.kv | 7 +++- tutorial/14-networld/03-python.kv | 7 +++- tutorial/14-networld/04-kvlang.kv | 7 +++- tutorial/14-networld/05-capture.kv | 9 +++++- tutorial/14-networld/06-fs.kv | 11 +++++-- tutorial/14-networld/07-fs-write.kv | 24 +++++++++++--- tutorial/14-networld/08-fs-dir.kv | 27 ++++++++++------ tutorial/14-networld/09-fs-exists.kv | 11 +++++-- tutorial/15-vthread/02-crashrecover.kv | 11 +++++-- tutorial/15-vthread/04-sleep.kv | 9 ++++-- 32 files changed, 378 insertions(+), 154 deletions(-) diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index 3fcdfde3..ead19717 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -75,15 +75,19 @@ 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)→ 解引用到实参值。 */ + /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ 解引用到实参值。 + * 同 ResolveWriteSlot:帧槽必有 Ptr,读不到即传参链路已坏 → panic, + * 绝不静默留 None(那会让形参读成空值,把真因藏到几层之外)。 */ 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); + if (!kvlangXvalueIsPtr(&pv)) { + fprintf(stderr, "panic: %s%s is not a Ptr — frame slot missing, param passing broken\n", stk, name + 1); + abort(); } + char *target = kvlangXvaluePtrTarget(&pv); + kvlangKvGetOne(kv, target, out); + free(target); kvlangXvalueFree(&pv); free(stk); return; } @@ -113,18 +117,23 @@ 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); - const char *seg = name; if (name[0] == '*') { - /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ target=写槽路径。 */ + /* 显式解引用:*[0,±k] → 读 frame_root/[0,±k](ref=1 Ptr)→ target=写槽路径。 + * `*[0,±k]` 是 layout 编译期生成的形参引用,帧槽必有调用点写入的 Ptr;读不到 + * 即传参链路已坏(不变量违反,非用户错误)→ panic,绝不回退成字面路径—— + * 回退会把「写形参」静默变成「写一个叫 *[0,±k] 的键」,掩盖真因。 */ 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; + if (!kvlangXvalueIsPtr(&pv)) { + fprintf(stderr, "panic: %s%s is not a Ptr — frame slot missing, param passing broken\n", stk, name + 1); + abort(); + } + char *target = kvlangXvaluePtrTarget(&pv); + kvlangXvalueFree(&pv); free(stk); + return target; } kvlangStrbuf_t o; kvlangStrbufInit(&o); - kvlangStrbufPuts(&o, stk); kvlangStrbufPuts(&o, seg); + kvlangStrbufPuts(&o, stk); kvlangStrbufPuts(&o, name); free(stk); return kvlangStrbufDetach(&o); } @@ -675,7 +684,14 @@ int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, k kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangBuiltinResolveReadValue(kv, fr, inst->reads[0].name, &inst->reads[0].val, &v); for (int i = 0; i < inst->nw; i++) { - char *key = kvlangBuiltinResolveWriteSlot(kv, fr, inst->writes[i].name); + const char *slot = inst->writes[i].name; + const char *dot = strstr(slot, MEMBER_SEP); + if (dot && dot != slot) { + char *b = strndup(slot, (size_t)(dot - slot)); + kvlangBuiltinEnsureMemberBase(kv, fr, b); + free(b); + } + char *key = kvlangBuiltinResolveWriteSlot(kv, fr, slot); kvlangKvPair_t pair = { key, v }; char err[256]; kvlangKvSet(kv, &pair, 1, err, sizeof err); diff --git a/runtime/src/builtin_kv.c b/runtime/src/builtin_kv.c index 5385b8b7..bf018a3c 100644 --- a/runtime/src/builtin_kv.c +++ b/runtime/src/builtin_kv.c @@ -63,10 +63,47 @@ int kvlangBuiltinKvGet(kvlangFrame_t *f) { free(key); kvlangBuiltinFreeInputs(in, n); return rc; } +/* 成员写的 base 若是未初始化的局部容器变量(`m·k = v` 而 m 尚无值),先落空 stringkeymap + * 值。建容器是**语言层语义**(layout 已把 m 推断为 stringkeymap),故在 runtime 做—— + * 存储层只认字节,`/lib/pkg·fn` 那类 `·` 它无从分辨,交给存储层猜必然要靠特判兜底。 + * base 在 `/lib` 下时不介入——`/lib` 是 layout/runtime 的结构域(见 [[结构域]]),那里的 + * `·` 是包·函数命名分隔符而非成员写,由 layout 全权写入。 */ +static void ensure_local_container(kvlangFrame_t *f, const char *name) { + char *fr = kvlangKeytreeFrameRoot(f->pc); + kvlangBuiltinEnsureMemberBase(f->kv, fr, name); + free(fr); +} + + +/* 成员写的 base(容器名,裸名或绝对路径)尚无值 → 先落空 stringkeymap 值。 + * 建容器是**语言层语义**,故在 runtime 做——存储层只认字节,`/lib/pkg·fn` 那类 `·` 它 + * 无从分辨,交给它猜必然要靠特判兜底。 + * base 在 `/lib` 下时跳过:`/lib` 是 layout/runtime 的结构域(见 [[结构域]]),那里的 `·` + * 是包·函数命名分隔符而非成员写,由 layout 全权写入。 */ +void kvlangBuiltinEnsureMemberBase(kvlangKv_t *kv, const char *frame_root, const char *base) { + if (!base || !base[0] || strncmp(base, "/lib", 4) == 0) return; + char *path = kvlangBuiltinResolveWriteSlot(kv, frame_root, base); + kvlangXvalue_t cur; kvlangXvalueZero(&cur); + kvlangKvGetOne(kv, path, &cur); + if (kvlangXvalueNone(&cur)) { + kvlangXvalue_t empty; kvlangXvalueNewTlv(&empty, KVSPACE_KIND_MAP, NULL, 0, 1); + kvlangKvPair_t p = { path, empty }; + char err[256]; + kvlangKvSet(kv, &p, 1, err, sizeof err); + kvlangXvalueFree(&empty); + } + kvlangXvalueFree(&cur); + free(path); +} + int kvlangBuiltinKvSet(kvlangFrame_t *f) { kvlangXvalue_t in[MAX_PARAMS]; int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); char *key; kvlangXvalue_t *val; - if (f->inst->nr >= 3) { key = member_path(f, in, n - 1); val = &in[n - 1]; } + if (f->inst->nr >= 3) { + const char *base = f->inst->reads[0].name; + if (strncmp(base, "/lib", 4) != 0) ensure_local_container(f, base); + key = member_path(f, in, n - 1); val = &in[n - 1]; + } else { key = n >= 1 ? path_arg(f, 0, in) : NULL; val = &in[1]; } if (!key || (f->inst->nr < 3 && n < 2)) { free(key); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.set requires path and value"); } kvlangKvPair_t p = { key, *val }; diff --git a/runtime/src/kv.c b/runtime/src/kv.c index c9843783..de2df63e 100644 --- a/runtime/src/kv.c +++ b/runtime/src/kv.c @@ -103,7 +103,10 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui continue; } kvspaceHead_t h; - if (kvspaceDecodeHead(v->data, v->len, &h) != 0 || !h.langtype[0]) + /* head 解码失败才跳过:langtype 为空**不是**跳过理由——空 langtype 的 Ptr 仍是 + * 指针(如跨帧传一个值为 None 的容器实参时,runtime 推不出类型)。None 由上面 + * 的 len==0 分支处理,二者不是一回事。曾因这里静默跳过,致 fs 后端帧槽丢参数。 */ + if (kvspaceDecodeHead(v->data, v->len, &h) != 0) continue; uint32_t body_len = h.body_len < 0 ? 0 : (uint32_t)h.body_len; const uint8_t *body = v->data + h.body_offset; diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index 8ba0687a..08ea92de 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -432,22 +432,12 @@ 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; kvlangXvalueNewPtr(&rv, lt, rk); @@ -492,8 +482,14 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) } kvlangStrbufFree(&slot); } - if (np > 0) kvlangKvSet(kv, pairs, np, err, sizeof err); + /* 实参绑定写失败必须报错——绝不静默:丢的是一整个参数,症状会漂到很远的地方 + * (曾表现为 fs 后端下函数读到空参数)。 */ + int wrc = np > 0 ? kvlangKvSet(kv, pairs, np, err, sizeof err) : 0; for (int i = 0; i < np; i++) { free(pairs[i].key); kvlangXvalueFree(&pairs[i].val); } + if (wrc != 0) { + kvlangVthreadSetError(kv, vtid, pc, err); + goto fail; + } free(caller_fr); free(stack_fr); kvlangStrbufFree(&npc); kvlangStrbufFree(&retpc); kvlangStrbufFree(&callpc); kvlangStrbufFree(&seglib); diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 3c79e7a8..58e3216a 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -338,6 +338,8 @@ int kvlangCtlBr(kvlangFrame_t *f); void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const char *name, const kvlangXvalue_t *val, kvlangXvalue_t *out); char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, const char *name); +/* 成员写的 base 尚无值 → 落空 stringkeymap 值(`/lib` 下跳过,见 builtin_kv.c)。 */ +void kvlangBuiltinEnsureMemberBase(kvlangKv_t *kv, const char *frame_root, const char *base); char *kvlangBuiltinResolveReadKey(kvlangKv_t *kv, const char *frame_root, const char *name, const kvlangXvalue_t *val); bool kvlangBuiltinTryParseNumber(const char *s, kvlangXvalue_t *out); /* 成功 out 接管 */ void kvlangDisplay(const kvlangXvalue_t *v, char **out); /* malloc,对齐 Go Display */ diff --git a/tutorial/01-basics/kv_mkindex_cap.kv b/tutorial/01-basics/kv_mkindex_cap.kv index 3c23e8e2..05245f80 100644 --- a/tutorial/01-basics/kv_mkindex_cap.kv +++ b/tutorial/01-basics/kv_mkindex_cap.kv @@ -1,11 +1,17 @@ // 期望输出: // n= 3 + +// 列出 prefix 下的子键并计数 +rwfunc count_children(prefix:[]char/utf32) -> (n:int64) { + kv·list(prefix) -> names + ndarray·numel(names) -> n +} + rwfunc test() -> () { kv·mkindex("/tmp/kvtc/", 8) kv·set("/tmp/kvtc/a", 1) kv·set("/tmp/kvtc/bb", 2) kv·set("/tmp/kvtc/ccc", 3) - kv·list("/tmp/kvtc/") -> names - ndarray·numel(names) -> n + count_children("/tmp/kvtc/") -> n println("n=", n) } diff --git a/tutorial/01-basics/kv_tree.kv b/tutorial/01-basics/kv_tree.kv index b54f35af..e1cc0d4a 100644 --- a/tutorial/01-basics/kv_tree.kv +++ b/tutorial/01-basics/kv_tree.kv @@ -5,19 +5,30 @@ // n= 2 // numel= 3 dim= 1 // shape0= 3 + +// path 下是否存在字段 key +rwfunc has_field(path:[]char/utf32, key:[]char/utf32) -> (ok:bool) { + kv·get(path, key) != None -> ok +} + +// 列出 prefix 下的子键并计数 +rwfunc count_children(prefix:[]char/utf32) -> (n:int64) { + kv·list(prefix) -> names + ndarray·numel(names) -> n +} + rwfunc test() -> () { kv·set("/tmp/kvt·data", 42) kv·get("/tmp/kvt·data") -> v println("v=", v) - kv·get("/tmp/kvt", "data") != None -> h + has_field("/tmp/kvt", "data") -> h println("has_data=", h) kv·get("/tmp/kvt", "data") -> x println("x=", x) kv·mkindex("/tmp/kvt/") kv·set("/tmp/kvt/a", 1) kv·set("/tmp/kvt/b", 2) - kv·list("/tmp/kvt/") -> names - ndarray·numel(names) -> n + count_children("/tmp/kvt/") -> n println("n=", n) ndarray·numel([10,20,30]) -> m ndarray·dim([10,20,30]) -> d diff --git a/tutorial/01-basics/strings.kv b/tutorial/01-basics/strings.kv index 9610e3cc..a5fdc498 100644 --- a/tutorial/01-basics/strings.kv +++ b/tutorial/01-basics/strings.kv @@ -14,10 +14,18 @@ // -1 // l // 108 + +// 替换第 i 个字符,返回新串(值语义:实参不被修改) +rwfunc replace_at(s:[]char/utf32, i:int64, + ch:[]char/utf32) -> (r:[]char/utf32) { + s -> r + r[i] = ch +} + rwfunc test() -> () { s = "hello" println(s[1]) - s[0] = "H" + replace_at(s, 0, "H") -> s println(s) t = "kv" + "lang" diff --git a/tutorial/03-control/for.kv b/tutorial/03-control/for.kv index a45fb70d..b5c38dfe 100644 --- a/tutorial/03-control/for.kv +++ b/tutorial/03-control/for.kv @@ -1,9 +1,15 @@ // cels_to_fahr: 摄氏转华氏温度表(K&R C §1.2) -// 算法: for 遍历器 + 浮点运算 +// 算法: for 遍历器驱动 fahr_to_cels 逐项换算 // 来源: C 语言 K&R §1.2 "The For Statement" // 期望输出: // 0 F = -17.77777777777778 C // 300 F = 148.88888888888889 C +rwfunc fahr_to_cels(fahr:int64) -> (cels:float64) { + factor = 5.0 ÷ 9.0 + fahr - 32 -> diff + factor × diff -> cels +} + rwfunc test() -> () { data·0 = 0 data·1 = 20 @@ -22,9 +28,7 @@ rwfunc test() -> () { 280 -> data·14 data·15 = 300 for (fahr in data) { - factor = 5.0 ÷ 9.0 - fahr - 32 -> diff - cels = factor × diff + cels = fahr_to_cels(fahr) println(fahr, "F =", cels, "C") } } diff --git a/tutorial/04-ndarray/continuous.kv b/tutorial/04-ndarray/continuous.kv index fd2dbfce..b256890c 100644 --- a/tutorial/04-ndarray/continuous.kv +++ b/tutorial/04-ndarray/continuous.kv @@ -5,6 +5,13 @@ // [0] = 10 // after set: 10 99 30 // sum = 179 +rwfunc sum(a:[]int64) -> (total:int64) { + total = 0 + for (x in a) { + total = total + x + } +} + rwfunc test() -> () { a:[]int64 = [10, 20, 30, 40] ndarray·numel(a) -> n @@ -16,12 +23,6 @@ rwfunc test() -> () { a[1] -> x1 x2 = a[2] println("after set:", x0, x1, x2) - total = 0 - 0 -> i - while (i < n) { - x = a[i] - total = total + x - i + 1 -> i - } + sum(a) -> total println("sum =", total) } diff --git a/tutorial/04-ndarray/fill_init.kv b/tutorial/04-ndarray/fill_init.kv index 573a2815..05eaf8c4 100644 --- a/tutorial/04-ndarray/fill_init.kv +++ b/tutorial/04-ndarray/fill_init.kv @@ -5,6 +5,13 @@ // a0 = 0 // b12 = 5 // sum = 30 +rwfunc sum6(a:[6]int32) -> (total:int64) { + total = 0 + for (x in a) { + total = total + x + } +} + rwfunc test() -> () { a:[4]int32 = [] ndarray·numel(a) -> na @@ -15,12 +22,6 @@ rwfunc test() -> () { v12 = b[1,2] println("b12 =", v12) c:[6]int32 = [5...] - total = 0 - 0 -> i - while (i < 6) { - x = c[i] - total = total + x - i + 1 -> i - } + sum6(c) -> total println("sum =", total) } diff --git a/tutorial/04-ndarray/for_compact.kv b/tutorial/04-ndarray/for_compact.kv index b81b5684..064f705a 100644 --- a/tutorial/04-ndarray/for_compact.kv +++ b/tutorial/04-ndarray/for_compact.kv @@ -5,12 +5,16 @@ // 20 // 30 // sum = 60 -rwfunc test() -> () { - a:[]int64 = [10, 20, 30] +rwfunc print_sum(a:[]int64) -> (total:int64) { total = 0 for (x in a) { println(x) total = total + x } +} + +rwfunc test() -> () { + a:[]int64 = [10, 20, 30] + print_sum(a) -> total println("sum =", total) } diff --git a/tutorial/04-ndarray/sparse_literal.kv b/tutorial/04-ndarray/sparse_literal.kv index b5dbfdc7..b93cb243 100644 --- a/tutorial/04-ndarray/sparse_literal.kv +++ b/tutorial/04-ndarray/sparse_literal.kv @@ -7,12 +7,23 @@ // 30 // 40 // sum = 100 -rwfunc test() -> () { - nums = {10, 20, 30, 40} +// 散 key 数组作实参时 for-in 在 layout 脱糖成 compact 取值(xv·at), +// 函数内改用 kv·listlen/kv·listn/kv·get 显式遍历。 +rwfunc print_sum(nums:[int64]·int64) -> (total:int64) { total = 0 - for (x in nums) { + kv·listlen(nums) -> n + 0 -> i + while (i < n) { + kv·listn(nums, i) -> k + kv·get(nums, k) -> x println(x) total = total + x + i = i + 1 } +} + +rwfunc test() -> () { + nums = {10, 20, 30, 40} + print_sum(nums) -> total println("sum =", total) } diff --git a/tutorial/06-algo/map_reduce.kv b/tutorial/06-algo/map_reduce.kv index 55718616..e2fb9508 100644 --- a/tutorial/06-algo/map_reduce.kv +++ b/tutorial/06-algo/map_reduce.kv @@ -1,8 +1,34 @@ // map_reduce: filter + map + reduce(MDN JS Array) -// 算法: for 遍历器筛选奇数 + 累加平方 +// 算法: 遍历筛选奇数 + 累加平方 // 期望输出: // odd squares count: 5 // sum of squares: 165 +// 散 key 数组作实参时 for-in 在 layout 脱糖成 compact 取值(xv·at), +// 函数内改用 kv·listlen/kv·listn/kv·get 显式遍历。 +rwfunc map_reduce(nums:[int64]·int64) -> (count:int64, total:int64) { + 0 -> count + total = 0 + kv·listlen(nums) -> n + 0 -> i + while (i < n) { + kv·listn(nums, i) -> k + kv·get(nums, k) -> nv + rem = nv % 2 + rem == 1 -> is_odd + + if (is_odd) { + println(" keep:", nv) + sq = nv × nv + sq_m = nv × nv + total = total + sq + count + 1 -> count + } else { + println(" skip:", nv) + } + i = i + 1 + } +} + rwfunc test() -> () { nums·0 = 1 3 -> nums·1 @@ -14,22 +40,7 @@ rwfunc test() -> () { 6 -> nums·7 nums·8 = 8 nums·9 = 10 - 0 -> count - total = 0 - for (n in nums) { - rem = n % 2 - rem == 1 -> is_odd - - if (is_odd) { - println(" keep:", n) - sq = n × n - sq_m = n × n - total = total + sq - count + 1 -> count - } else { - println(" skip:", n) - } - } + map_reduce(nums) -> (count, total) println("odd squares count:", count) println("sum of squares:", total) /last_sum = total diff --git a/tutorial/06-algo/word_count.kv b/tutorial/06-algo/word_count.kv index 6ff954e3..216008f3 100644 --- a/tutorial/06-algo/word_count.kv +++ b/tutorial/06-algo/word_count.kv @@ -1,7 +1,33 @@ // word_count: 单词计数状态机(K&R C §1.5.4) -// 算法: for 遍历 + IN/OUT 状态机 +// 算法: 遍历 + IN/OUT 状态机 // 期望输出: // word count = 3 +// 散 key 数组作实参时 for-in 在 layout 脱糖成 compact 取值(xv·at), +// 函数内改用 kv·listlen/kv·listn/kv·get 显式遍历。 +rwfunc word_count(chars:[int64]·[]char/utf32) -> (nw:int64) { + nw = 0 + 0 -> state + kv·listlen(chars) -> n + 0 -> i + while (i < n) { + kv·listn(chars, i) -> k + kv·get(chars, k) -> c + blank = c == " " + + if (blank) { + state = 0 + } else { + state == 0 -> was_out + + if (was_out) { + state = 1 + nw = nw + 1 + } + } + i = i + 1 + } +} + rwfunc test() -> () { chars·0 = "h" "e" -> chars·1 @@ -21,22 +47,7 @@ rwfunc test() -> () { chars·15 = "a" "n" -> chars·16 chars·17 = "g" - nw = 0 - 0 -> state - for (c in chars) { - blank = c == " " - - if (blank) { - state = 0 - } else { - state == 0 -> was_out - - if (was_out) { - state = 1 - nw = nw + 1 - } - } - } + word_count(chars) -> nw println("word count =", nw) nw -> /last_wc } diff --git a/tutorial/12-struct/03-linked-list.kv b/tutorial/12-struct/03-linked-list.kv index c34a6a5b..d0e53aa2 100644 --- a/tutorial/12-struct/03-linked-list.kv +++ b/tutorial/12-struct/03-linked-list.kv @@ -1,6 +1,7 @@ // linked-list: 局部 struct 节点变量 + & 取址串成链表 // 语义: 每个节点是帧内 struct 局部变量;next 存下一节点地址(&node ≡ kv·abs(node)); -// None=尾;cur·field 按 next 路径解引用遍历。尾节点先建,&node 恒指向已存在节点。 +// None=尾;show_list 接收 *Node 头指针,按 next 路径解引用逐节点打印。 +// 尾节点先建,&node 恒指向已存在节点。 // 期望输出: // 10 // 20 @@ -10,14 +11,18 @@ struct Node { next:*Node=None } -rwfunc test() -> () { - n3:Node = {val=30} - n2:Node = {val=20 next=&n3} - n1:Node = {val=10 next=&n2} - cur = &n1 +rwfunc show_list(head:*Node) -> () { + head -> cur while (cur != None) { cur·val -> v println(v) cur·next -> cur } } + +rwfunc test() -> () { + n3:Node = {val=30} + n2:Node = {val=20 next=&n3} + n1:Node = {val=10 next=&n2} + show_list(&n1) +} diff --git a/tutorial/12-struct/06-list-reverse.kv b/tutorial/12-struct/06-list-reverse.kv index dc18f4fb..4423595b 100644 --- a/tutorial/12-struct/06-list-reverse.kv +++ b/tutorial/12-struct/06-list-reverse.kv @@ -1,7 +1,8 @@ // list-reverse: C 风格 struct 指针变量,切换链表指向(原地反转) // 语义: cur/prev/nxt 都是指针变量(绝对路径串),等价 C 的 Node*; -// cur·next 即 C 的 cur->next。反转 = 逐节点把 next 指回前驱。 -// 链表: 10→20→30 反转后从新头 prev 遍历得 30→20→10 +// cur·next 即 C 的 cur->next。reverse_list 接收头指针,逐节点把 +// next 指回前驱并返回新头;test 从新头遍历打印。 +// 链表: 10→20→30 反转后从新头遍历得 30→20→10 // 期望输出: // 30 // 20 @@ -11,19 +12,25 @@ struct Node { next:*Node=None } -rwfunc test() -> () { - n3:Node = {val=30} - n2:Node = {val=20 next=&n3} - n1:Node = {val=10 next=&n2} +rwfunc reverse_list(head:*Node) -> (new_head:*Node) { prev = None - cur = &n1 + head -> cur while (cur != None) { cur·next -> nxt prev -> cur·next cur -> prev nxt -> cur } - cur = prev + prev -> new_head +} + +rwfunc test() -> () { + n3:Node = {val=30} + n2:Node = {val=20 next=&n3} + n1:Node = {val=10 next=&n2} + &n1 -> h + reverse_list(h) -> head + head -> cur while (cur != None) { cur·val -> v println(v) diff --git a/tutorial/12-struct/07-list-local.kv b/tutorial/12-struct/07-list-local.kv index d59345a2..0dff0a5c 100644 --- a/tutorial/12-struct/07-list-local.kv +++ b/tutorial/12-struct/07-list-local.kv @@ -1,6 +1,6 @@ // list-local: 局部 struct 节点变量 + & 建链,遍历求和 // 语义: 节点是帧内 struct 局部变量,next 存下一节点地址(&node ≡ kv·abs(node)); -// None=尾;尾节点先建,&node 恒指向已存在节点。遍历累加各节点 val。 +// None=尾;sum_list 接收 *Node 头指针,遍历累加各节点 val。 // 链表: 10→20→30 // 期望输出: // 60 @@ -9,16 +9,20 @@ struct Node { next:*Node=None } -rwfunc test() -> () { - n3:Node = {val=30} - n2:Node = {val=20 next=&n3} - n1:Node = {val=10 next=&n2} - cur = &n1 - sum = 0 +rwfunc sum_list(head:*Node) -> (sum:int64) { + head -> cur + 0 -> sum while (cur != None) { cur·val -> v sum + v -> sum cur·next -> cur } - println(sum) +} + +rwfunc test() -> () { + n3:Node = {val=30} + n2:Node = {val=20 next=&n3} + n1:Node = {val=10 next=&n2} + sum_list(&n1) -> s + println(s) } diff --git a/tutorial/13-stdlib/duration/max_min.kv b/tutorial/13-stdlib/duration/max_min.kv index e55c9ce0..2c9df24d 100644 --- a/tutorial/13-stdlib/duration/max_min.kv +++ b/tutorial/13-stdlib/duration/max_min.kv @@ -2,15 +2,20 @@ // 期望输出: // max: 3 // min: 1 +// 取两 duration 的极值,并以小时为单位返回(大者、小者) +rwfunc extremes(d1:duration, d2:duration) -> (hi:int64, lo:int64) { + time/duration·max(d1, d2) -> mx + time/duration·min(d1, d2) -> mn + time/duration·as_hours(mx) -> hi + time/duration·as_hours(mn) -> lo +} + rwfunc test() -> () { time/duration·hours(1) -> h1 time/duration·hours(3) -> h3 - time/duration·max(h1, h3) -> mx - time/duration·as_hours(mx) -> mx_h + extremes(h1, h3) -> (mx, mn) print("max: ") - println(mx_h) - time/duration·min(h1, h3) -> mn - time/duration·as_hours(mn) -> mn_h + println(mx) print("min: ") - println(mn_h) + println(mn) } diff --git a/tutorial/13-stdlib/time/elapsed_ns.kv b/tutorial/13-stdlib/time/elapsed_ns.kv index 928c9fcf..ef0ba19a 100644 --- a/tutorial/13-stdlib/time/elapsed_ns.kv +++ b/tutorial/13-stdlib/time/elapsed_ns.kv @@ -1,10 +1,15 @@ // time·elapsed_ns:从指定时刻到现在的纳秒数(确定 >= 0) // 期望输出: // elapsed >= 0: true -rwfunc test() -> () { - time·now() -> t0 +// 自 t0 至今经过的纳秒数非负 +rwfunc elapsed_nonneg(t0:time) -> (ok:bool) { time·elapsed_ns(t0) -> ns ns >= 0 -> ok +} + +rwfunc test() -> () { + time·now() -> t0 + elapsed_nonneg(t0) -> ok print("elapsed >= 0: ") println(ok) } diff --git a/tutorial/13-stdlib/time/since.kv b/tutorial/13-stdlib/time/since.kv index 3902e368..39f100ca 100644 --- a/tutorial/13-stdlib/time/since.kv +++ b/tutorial/13-stdlib/time/since.kv @@ -1,11 +1,16 @@ // time·since:从指定时刻到现在的时长(确定 >= 0) // 期望输出: // since >= 0: true -rwfunc test() -> () { - time·now() -> t0 +// 自 t0 至今经过的时长非负 +rwfunc since_nonneg(t0:time) -> (ok:bool) { time·since(t0) -> dur time/duration·as_nanos(dur) -> ns ns >= 0 -> ok +} + +rwfunc test() -> () { + time·now() -> t0 + since_nonneg(t0) -> ok print("since >= 0: ") println(ok) } diff --git a/tutorial/14-networld/01-exec.kv b/tutorial/14-networld/01-exec.kv index 63aef4dd..626d6306 100644 --- a/tutorial/14-networld/01-exec.kv +++ b/tutorial/14-networld/01-exec.kv @@ -8,19 +8,25 @@ // seven= 7 // missing= 127 // FOO=bar123 +// run_proc 封装一次命令执行:输入 args/envs,输出退出码 +rwfunc run_proc(args:[]char/utf8·[]char/utf32, + envs:[]char/utf8·[]char/utf32) -> (code:uint8) { + networld/proc·exec(args, envs) -> code +} + rwfunc test() -> () { noenv = map() echoargs = {"sh", "-c", "echo hello from child"} - networld/proc·exec(echoargs, noenv) -> ok + run_proc(echoargs, noenv) -> ok println("ok=", ok) failargs = {"sh", "-c", "exit 7"} - networld/proc·exec(failargs, noenv) -> seven + run_proc(failargs, noenv) -> seven println("seven=", seven) badargs = {"/no/such/program"} - networld/proc·exec(badargs, noenv) -> missing + run_proc(badargs, noenv) -> missing println("missing=", missing) envargs = {"sh", "-c", "echo FOO=$FOO"} envs = {"FOO=bar123"} - networld/proc·exec(envargs, envs) -> withenv + run_proc(envargs, envs) -> withenv println("withenv=", withenv) } diff --git a/tutorial/14-networld/02-bash.kv b/tutorial/14-networld/02-bash.kv index 0b0f2757..970834fc 100644 --- a/tutorial/14-networld/02-bash.kv +++ b/tutorial/14-networld/02-bash.kv @@ -2,9 +2,14 @@ // 期望输出: // bash 42 // exit= 0 +rwfunc run_proc(args:[]char/utf8·[]char/utf32, + envs:[]char/utf8·[]char/utf32) -> (code:uint8) { + networld/proc·exec(args, envs) -> code +} + rwfunc test() -> () { noenv = map() args = {"bash", "-c", "echo bash $((6*7))"} - networld/proc·exec(args, noenv) -> code + run_proc(args, noenv) -> code println("exit=", code) } diff --git a/tutorial/14-networld/03-python.kv b/tutorial/14-networld/03-python.kv index d1007a23..7452773b 100644 --- a/tutorial/14-networld/03-python.kv +++ b/tutorial/14-networld/03-python.kv @@ -2,9 +2,14 @@ // 期望输出: // python 1024 // exit= 0 +rwfunc run_proc(args:[]char/utf8·[]char/utf32, + envs:[]char/utf8·[]char/utf32) -> (code:uint8) { + networld/proc·exec(args, envs) -> code +} + rwfunc test() -> () { noenv = map() args = {"python3", "-c", "print('python', 2**10)"} - networld/proc·exec(args, noenv) -> code + run_proc(args, noenv) -> code println("exit=", code) } diff --git a/tutorial/14-networld/04-kvlang.kv b/tutorial/14-networld/04-kvlang.kv index dbd66318..06e8f1b2 100644 --- a/tutorial/14-networld/04-kvlang.kv +++ b/tutorial/14-networld/04-kvlang.kv @@ -3,9 +3,14 @@ // 期望输出: // kvlang in kvlang // exit= 0 +rwfunc run_proc(args:[]char/utf8·[]char/utf32, + envs:[]char/utf8·[]char/utf32) -> (code:uint8) { + networld/proc·exec(args, envs) -> code +} + rwfunc test() -> () { noenv = map() args = {"kvlang", "-c", "print(\"kvlang in kvlang\")"} - networld/proc·exec(args, noenv) -> code + run_proc(args, noenv) -> code println("exit=", code) } diff --git a/tutorial/14-networld/05-capture.kv b/tutorial/14-networld/05-capture.kv index f5404f94..47ee5d07 100644 --- a/tutorial/14-networld/05-capture.kv +++ b/tutorial/14-networld/05-capture.kv @@ -6,10 +6,17 @@ // code= 0 // out= hi // err= oops +// run_capture 把 code/out/err 三个结果一并带回调用方; +// 捕获句柄无 kindexpr 类型,写参声明为 any +rwfunc run_capture(args:[]char/utf8·[]char/utf32, + envs:[]char/utf8·[]char/utf32) -> (code:uint8, out:any, err:any) { + networld/proc·exec(args, envs) -> code, out, err +} + rwfunc test() -> () { noenv = map() a = {"sh", "-c", "echo hi; echo oops 1>&2"} - networld/proc·exec(a, noenv) -> code, out, err + run_capture(a, noenv) -> (code, out, err) println("code=", code) println("out=", out) println("err=", err) diff --git a/tutorial/14-networld/06-fs.kv b/tutorial/14-networld/06-fs.kv index f8654655..0a527ed8 100644 --- a/tutorial/14-networld/06-fs.kv +++ b/tutorial/14-networld/06-fs.kv @@ -5,14 +5,19 @@ // 期望输出: // size= 12 // text= hello kvlang +// read_text 读取整个文件的字节并整体重解释为 utf8 字符串,同时带回字节大小 +rwfunc read_text(p:[]char/utf32) -> (s:[]char/utf8, n:int64) { + networld/fs·size(p) -> n + networld/fs·read(p, 0, n) -> raw + xv·reinterpret(raw, "[]char/utf8") -> s +} + rwfunc test() -> () { p = "/tmp/kvlangfs_demo.txt" noenv = map() w = {"sh", "-c", "printf 'hello kvlang' > /tmp/kvlangfs_demo.txt"} networld/proc·exec(w, noenv) -> wc - networld/fs·size(p) -> n + read_text(p) -> (s, n) println("size=", n) - networld/fs·read(p, 0, n) -> raw - xv·reinterpret(raw, "[]char/utf8") -> s println("text=", s) } diff --git a/tutorial/14-networld/07-fs-write.kv b/tutorial/14-networld/07-fs-write.kv index 74f73886..3e6ba003 100644 --- a/tutorial/14-networld/07-fs-write.kv +++ b/tutorial/14-networld/07-fs-write.kv @@ -7,15 +7,29 @@ // append= 24 // size= 44 // text= hello world +// write_text/append_text 把字符串整体重解释为 []uint8 后写入磁盘,返回字节数 +rwfunc write_text(p:[]char/utf32, s:[]char/utf32) -> (n:int64) { + networld/fs·write(p, xv·reinterpret(s, "[]uint8")) -> n +} + +rwfunc append_text(p:[]char/utf32, s:[]char/utf32) -> (n:int64) { + networld/fs·append(p, xv·reinterpret(s, "[]uint8")) -> n +} + +// read_text 读回整个文件并还原为 []char/utf32 字符串,同时带回字节大小 +rwfunc read_text(p:[]char/utf32) -> (s:[]char/utf32, n:int64) { + networld/fs·size(p) -> n + networld/fs·read(p, 0, n) -> raw + xv·reinterpret(raw, "[]char/utf32") -> s +} + rwfunc test() -> () { p = "/tmp/kvlang_fs_write.txt" - networld/fs·write(p, xv·reinterpret("hello", "[]uint8")) -> wn + write_text(p, "hello") -> wn println("write=", wn) - networld/fs·append(p, xv·reinterpret(" world", "[]uint8")) -> an + append_text(p, " world") -> an println("append=", an) - networld/fs·size(p) -> sz + read_text(p) -> (txt, sz) println("size=", sz) - networld/fs·read(p, 0, sz) -> raw - xv·reinterpret(raw, "[]char/utf32") -> txt println("text=", txt) } diff --git a/tutorial/14-networld/08-fs-dir.kv b/tutorial/14-networld/08-fs-dir.kv index a9824451..325739be 100644 --- a/tutorial/14-networld/08-fs-dir.kv +++ b/tutorial/14-networld/08-fs-dir.kv @@ -8,14 +8,8 @@ // name= b.txt // delfile= 0 // deldir= 0 -rwfunc test() -> () { - d = "/tmp/kvlang_fs_dir" - noenv = map() - clean = {"rm", "-rf", "/tmp/kvlang_fs_dir"} - networld/proc·exec(clean, noenv) -> rc - networld/fs·mkdir(d) -> mc - networld/fs·write("/tmp/kvlang_fs_dir/a.txt", xv·reinterpret("A", "[]uint8")) -> w1 - networld/fs·write("/tmp/kvlang_fs_dir/b.txt", xv·reinterpret("B", "[]uint8")) -> w2 +// print_dir 列目录成员(stringkeymap 两步遍历)并打印成员数与逐个名字 +rwfunc print_dir(d:[]char/utf32) -> () { networld/fs·list(d) -> names kv·listlen(names) -> nc println("count=", nc) @@ -26,8 +20,21 @@ rwfunc test() -> () { println("name=", nm) i = i + 1 } - networld/fs·del("/tmp/kvlang_fs_dir/a.txt") -> d1 - networld/fs·del("/tmp/kvlang_fs_dir/b.txt") -> d2 +} + +rwfunc test() -> () { + d = "/tmp/kvlang_fs_dir" + da = "/tmp/kvlang_fs_dir/a.txt" + db = "/tmp/kvlang_fs_dir/b.txt" + noenv = map() + clean = {"rm", "-rf", "/tmp/kvlang_fs_dir"} + networld/proc·exec(clean, noenv) -> rc + networld/fs·mkdir(d) -> mc + networld/fs·write(da, xv·reinterpret("A", "[]uint8")) -> w1 + networld/fs·write(db, xv·reinterpret("B", "[]uint8")) -> w2 + print_dir(d) + networld/fs·del(da) -> d1 + networld/fs·del(db) -> d2 networld/fs·del(d) -> dd println("delfile=", d1) println("deldir=", dd) diff --git a/tutorial/14-networld/09-fs-exists.kv b/tutorial/14-networld/09-fs-exists.kv index 1122db65..b02836b4 100644 --- a/tutorial/14-networld/09-fs-exists.kv +++ b/tutorial/14-networld/09-fs-exists.kv @@ -4,17 +4,22 @@ // before= false // after_write= true // after_del= false +// file_exists 封装存在性判定:输入路径,输出 bool +rwfunc file_exists(p:[]char/utf32) -> (b:bool) { + networld/fs·exists(p) -> b +} + rwfunc test() -> () { p = "/tmp/kvlang_fs_exists.txt" noenv = map() rm = {"rm", "-f", "/tmp/kvlang_fs_exists.txt"} networld/proc·exec(rm, noenv) -> rc - networld/fs·exists(p) -> b0 + file_exists(p) -> b0 println("before=", b0) networld/fs·write(p, xv·reinterpret("x", "[]uint8")) -> w - networld/fs·exists(p) -> b1 + file_exists(p) -> b1 println("after_write=", b1) networld/fs·del(p) -> dc - networld/fs·exists(p) -> b2 + file_exists(p) -> b2 println("after_del=", b2) } diff --git a/tutorial/15-vthread/02-crashrecover.kv b/tutorial/15-vthread/02-crashrecover.kv index 51dcb347..95dc35b5 100644 --- a/tutorial/15-vthread/02-crashrecover.kv +++ b/tutorial/15-vthread/02-crashrecover.kv @@ -9,9 +9,11 @@ // step 4 // step 5 // complete final= 5 -rwfunc test() -> () { +// run_steps 跑 limit 个带检查点的步骤:每步先落盘 step 再打印、 +// sleep 出中断窗口,返回完成步数 +rwfunc run_steps(limit:int64) -> (final:int64) { 0 -> i - while (i < 5) { + while (i < limit) { i + 1 -> i kv·set("/crashtest/step", i) println("step", i) @@ -19,6 +21,11 @@ rwfunc test() -> () { a = {"sleep", "0.2"} networld/proc·exec(a, noenv) -> code } + i -> final +} + +rwfunc test() -> () { + run_steps(5) -> i kv·set("/crashtest/done", 1) println("complete final=", i) } diff --git a/tutorial/15-vthread/04-sleep.kv b/tutorial/15-vthread/04-sleep.kv index f3805268..2e9fe748 100644 --- a/tutorial/15-vthread/04-sleep.kv +++ b/tutorial/15-vthread/04-sleep.kv @@ -3,13 +3,18 @@ // 单进程模型下即阻塞驱动线程;与 vthread·setstatus("wait") 的挂起-恢复不同 // 期望输出: // slept >= 10ms: true -rwfunc test() -> () { - time/duration·millis(10) -> d +// sleep_at_least 睡 ms 毫秒,并核对实际耗时是否达到请求值,返回 bool +rwfunc sleep_at_least(ms:int64) -> (ok:bool) { + time/duration·millis(ms) -> d time·now() -> t0 vthread·sleep(d) time·elapsed_ns(t0) -> ns time/duration·as_nanos(d) -> want ns >= want -> ok +} + +rwfunc test() -> () { + sleep_at_least(10) -> ok print("slept >= 10ms: ") println(ok) } From fb083437b9d952e49bfa84b1ca9241bde3066151 Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Thu, 10 Sep 2026 23:38:30 +0800 Subject: [PATCH 3/5] =?UTF-8?q?layout/runtime/spec/tutorial:=20=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E8=A7=84=E8=8C=83=E9=87=8D=E5=91=BD=E5=90=8D=20map=20?= =?UTF-8?q?=E5=AE=B9=E5=99=A8=20+=20=E8=AF=AD=E4=B9=89=E5=BB=B6=E7=BB=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spec 03 种类系列(标量时间容器/代码与特殊值/文法合法性/成员访问/总体方案/ 已敲定)与 14-容器→14-map容器 重命名、runtime 语义-05 对齐;layout code/lower/parser + runtime builtin/kv/kvcpu 配合;一批 leetcode/ndarray/ dict/algo tutorial 同步。 Co-Authored-By: Claude Opus 4.8 --- layout/src/code.rs | 2 + layout/src/lower.rs | 117 ++++++++++++++++-- layout/src/parser.rs | 26 ++-- runtime/src/builtin.c | 11 +- runtime/src/builtin_kv.c | 43 +++---- runtime/src/kvcpu.c | 45 ++++--- runtime/src/runtime_internal.h | 3 +- ...66\351\227\264\345\256\271\345\231\250.kv" | 4 +- ...16\347\211\271\346\256\212\345\200\274.kv" | 2 +- ...16\345\220\210\346\263\225\346\200\247.kv" | 38 +++++- .../14-map\345\256\271\345\231\250.kv" | 75 +++++++++++ .../14-\345\256\271\345\231\250.kv" | 52 -------- ...20\345\221\230\350\256\277\351\227\256.kv" | 21 +++- ...73\344\275\223\346\226\271\346\241\210.kv" | 2 +- ...5-\345\267\262\346\225\262\345\256\232.kv" | 2 +- ...20\345\221\230\350\256\277\351\227\256.kv" | 12 +- ...76\345\274\217\346\226\207\346\263\225.kv" | 8 +- tutorial/03-control/for.kv | 1 + tutorial/04-ndarray/geo_coord.kv | 1 + tutorial/04-ndarray/string_array.kv | 5 +- tutorial/05-dict/kv_has_string_key.kv | 1 + tutorial/06-algo/map_reduce.kv | 1 + tutorial/06-algo/word_count.kv | 1 + tutorial/08-leetcode/020_valid_parentheses.kv | 1 + tutorial/08-leetcode/038_count_and_say.kv | 14 ++- tutorial/08-leetcode/056_merge_intervals.kv | 27 ++-- tutorial/08-leetcode/062_unique_paths.kv | 7 +- tutorial/08-leetcode/118_pascal_triangle.kv | 8 +- tutorial/08-leetcode/217_contains_dup_hash.kv | 2 +- 29 files changed, 362 insertions(+), 170 deletions(-) create mode 100644 "stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" delete mode 100644 "stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" diff --git a/layout/src/code.rs b/layout/src/code.rs index f2adda7e..236018c3 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -33,6 +33,7 @@ pub fn compile(kv: &mut Kv, src: &str) -> Result, String> { let (file, mut diags) = parser::parse_code(src)?; for f in subscript_check_funcs(&file) { diags.extend(lower::check_container_subscript(&f)); + diags.extend(lower::check_map_defined(&f)); } for d in &diags { eprintln!("{}", d.string()); @@ -140,6 +141,7 @@ pub fn vet(src: &str) -> Result<(), String> { let (file, mut diags) = parser::parse_code(src)?; for f in subscript_check_funcs(&file) { diags.extend(lower::check_container_subscript(&f)); + diags.extend(lower::check_map_defined(&f)); } for d in &diags { eprintln!("{}", d.string()); diff --git a/layout/src/lower.rs b/layout/src/lower.rs index 72874ce0..69a2e4c4 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -13,6 +13,105 @@ fn is_container_type(t: &str) -> bool { } /// `[]` 下标校验:xv·at/xv·set 基座若为容器类型 → 报错,逼用 kv·get/kv·set/`base·key`。 +fn is_container_ty(ty: &str) -> bool { + ty.contains(keytree::MEMBER_SEP) || ty.starts_with('/') +} + +/// 容器成员写的前置条件:base 必须**已声明且类型明确为容器**。 +/// 禁止未定义类型的 map —— `m·k = v` 而 m 无显式声明即 error,不做任何自动推断。 +pub fn check_map_defined(fn_: &Func) -> Vec { + let mut defined: HashSet = HashSet::new(); + for p in fn_.sig.params.iter().chain(fn_.sig.returns.iter()) { + if is_container_ty(&p.ty) { + defined.insert(p.name.clone()); + } + } + collect_container_decls(&fn_.body, &mut defined); + let mut diags = Vec::new(); + check_map_body(&fn_.body, &defined, &mut diags); + diags +} + +/// 收集「带容器类型标注的写槽」声明(`m:T = ...`,T 为 map/structref)。 +fn collect_container_decls(body: &[Stmt], out: &mut HashSet) { + for st in body { + match st { + Stmt::Instruction(s) => { + // 带容器类型标注的写槽(`m:T = ...`)即容器声明;被赋过值的名字一律算已定义 + // (指针别名 `n = node` 后 `n·field` 是合法成员访问,不是未定义 map)。 + for w in &s.writes { + out.insert(w.clone()); + } + } + Stmt::Scope(s) => collect_container_decls(&s.body, out), + Stmt::If(s) => { + collect_container_decls(&s.then_, out); + collect_container_decls(&s.else_, out); + } + Stmt::While(s) => collect_container_decls(&s.body, out), + Stmt::For(s) => collect_container_decls(&s.body, out), + _ => {} + } + } +} + +fn check_map_body(body: &[Stmt], defined: &HashSet, diags: &mut Vec) { + for st in body { + match st { + Stmt::Instruction(s) => check_map_inst(s, defined, diags), + Stmt::Scope(s) => check_map_body(&s.body, defined, diags), + Stmt::If(s) => { + if let Some(c) = &s.cond { + check_map_inst(c, defined, diags); + } + check_map_body(&s.then_, defined, diags); + check_map_body(&s.else_, defined, diags); + } + Stmt::While(s) => { + if let Some(c) = &s.cond { + check_map_inst(c, defined, diags); + } + check_map_body(&s.body, defined, diags); + } + Stmt::For(s) => check_map_body(&s.body, defined, diags), + _ => {} + } + } +} + +fn check_map_base(base: &str, defined: &HashSet, pos: Pos, diags: &mut Vec) { + if base.is_empty() || base.starts_with("/lib") || defined.contains(base) { + return; + } + diags.push(Diagnostic { + pos, + warn: false, + info: false, + message: format!( + "member write on undefined container {base:?} — declare it first with an explicit type, e.g. `{base}:[]char/utf8·int64 = {{}}`" + ), + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); +} + +fn check_map_inst(s: &Instruction, defined: &HashSet, diags: &mut Vec) { + for w in &s.writes { + if let Some(i) = w.find(keytree::MEMBER_SEP) { + if i > 0 { + check_map_base(&w[..i], defined, Pos { line: 0, col: 0 }, diags); + } + } + } + // kv·set(base, key, val) 成员形 + if let Some(e) = &s.expr { + if e.op == "kv·set" && e.args.len() >= 3 && !e.args[0].val.contains('/') { + check_map_base(&e.args[0].val, defined, Pos { line: 0, col: 0 }, diags); + } + } +} + pub fn check_container_subscript(fn_: &Func) -> Vec { let tm = infer_types(fn_); let mut diags = Vec::new(); @@ -378,10 +477,14 @@ fn lower_for_with_cont( let cond_slot = lg.tmp(); let len_slot = lg.tmp(); let key_slot = lg.tmp(); + // 容器源(stringkeymap/struct)用 kv·listlen/kv·listn/kv·get 遍历,compact 数组源用 + // ndarray·numel/xv·at(见 spec 控制流脱糖)。判据看**声明的类型**:map 的 kindexpr 恒含 + // `key·value` 的那个 `·`(`[int64]·int64`、`[]char/utf8·int64`),compact 数组不含。 let is_obj = s.iter.op == "obj" || s.iter.op == "map" || (s.iter.is_leaf() - && matches!(tm.get(&s.iter.val), Some(t) if t == "[]stringkeymap" || t == "stringkeymap")); + && matches!(tm.get(&s.iter.val), + Some(t) if t == "stringkeymap" || t.contains(keytree::MEMBER_SEP))); // 迭代源:裸标识符直接原地遍历;表达式(如数组字面量)先物化到临时槽。 let mut init_body = Vec::new(); @@ -750,11 +853,6 @@ fn infer_inst(inst: &Instruction, tm: &mut HashMap) { Some(e) => e, None => return, }; - // kv.set 成员形(3 读:base, key, val)是 void 无写槽,但 base 仍须推断为 stringkeymap(供 for-in / 成员访问) - if e.op == "kv·set" && e.args.len() >= 3 && !e.args[0].val.contains('/') { - tm.entry(e.args[0].val.clone()) - .or_insert_with(|| "stringkeymap".to_string()); - } if inst.writes.is_empty() { return; } @@ -840,11 +938,8 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap= 3 { - tm.entry(reads[0].clone()) - .or_insert_with(|| "stringkeymap".to_string()); - } + // 成员写的 base 类型**不在此推断**:map 必须显式声明准确类型 + // (`m:[]char/utf8·int64 = {}`),由 check_map_defined 静态拦截未定义者。 return String::new(); } match opcode { diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 54a96202..72ac3501 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -1220,30 +1220,26 @@ impl Parser { chain_segs.push(ast::str_lit(&field)); continue; } - // strkeymap 坐标访问 m·[i,j]:坐标段是单个成员名 "[i,j]"(字符串键)。 + // 元组/坐标 key:`m·[i,j]` 的成员名**取源码原样文本**(`[39.90,116.40]` 就是 + // `[39.90,116.40]`,不重建、不规范化——key 是独立 langtype 的字面表示, + // 改写会让写入与读取的 key 对不上)。逐 token 取原文拼接。 if self.peek().kind == Kind::LBrack { self.advance(); - let mut idxs = Vec::new(); + let mut parts: Vec = vec!["[".to_string()]; while self.peek().kind != Kind::RBrack && self.peek().kind != Kind::EOF { - if self.eat(Kind::Comma) { - continue; - } - if let Some(idx) = self.parse_pratt(0) { - idxs.push(idx); + let t = self.advance(); + if t.kind == Kind::Comma { + parts.push(",".to_string()); + } else if t.kind != Kind::Newline && t.kind != Kind::Comment { + parts.push(t.value.clone()); } } self.expect(Kind::RBrack); - let coord = format!( - "[{}]", - idxs.iter() - .map(|e| e.to_string()) - .collect::>() - .join(",") - ); + parts.push("]".to_string()); if chain_base.is_none() { chain_base = Some(left.clone()); } - chain_segs.push(ast::str_lit(&coord)); + chain_segs.push(ast::str_lit(&parts.concat())); continue; } } diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index ead19717..0a126450 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -687,9 +687,18 @@ int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, k const char *slot = inst->writes[i].name; const char *dot = strstr(slot, MEMBER_SEP); if (dot && dot != slot) { + /* 成员写前置条件:memhead 必须已存在(同 kv·set,见 builtin_kv.c)。 */ char *b = strndup(slot, (size_t)(dot - slot)); - kvlangBuiltinEnsureMemberBase(kv, fr, b); + int ok = kvlangBuiltinCheckMemhead(kv, fr, b); + char msg[320]; + if (ok != 0) + snprintf(msg, sizeof msg, "TypeError: memhead %s does not exist — declare the container first (e.g. `%s:T = {}`)", b, b); free(b); + if (ok != 0) { + kvlangXvalueFree(&v); free(fr); + kvlangVthreadSetError(kv, vtid, pc, msg); + return -1; + } } char *key = kvlangBuiltinResolveWriteSlot(kv, fr, slot); kvlangKvPair_t pair = { key, v }; diff --git a/runtime/src/builtin_kv.c b/runtime/src/builtin_kv.c index bf018a3c..8fda6e98 100644 --- a/runtime/src/builtin_kv.c +++ b/runtime/src/builtin_kv.c @@ -63,37 +63,20 @@ int kvlangBuiltinKvGet(kvlangFrame_t *f) { free(key); kvlangBuiltinFreeInputs(in, n); return rc; } -/* 成员写的 base 若是未初始化的局部容器变量(`m·k = v` 而 m 尚无值),先落空 stringkeymap - * 值。建容器是**语言层语义**(layout 已把 m 推断为 stringkeymap),故在 runtime 做—— - * 存储层只认字节,`/lib/pkg·fn` 那类 `·` 它无从分辨,交给存储层猜必然要靠特判兜底。 - * base 在 `/lib` 下时不介入——`/lib` 是 layout/runtime 的结构域(见 [[结构域]]),那里的 - * `·` 是包·函数命名分隔符而非成员写,由 layout 全权写入。 */ -static void ensure_local_container(kvlangFrame_t *f, const char *name) { - char *fr = kvlangKeytreeFrameRoot(f->pc); - kvlangBuiltinEnsureMemberBase(f->kv, fr, name); - free(fr); -} - - -/* 成员写的 base(容器名,裸名或绝对路径)尚无值 → 先落空 stringkeymap 值。 - * 建容器是**语言层语义**,故在 runtime 做——存储层只认字节,`/lib/pkg·fn` 那类 `·` 它 - * 无从分辨,交给它猜必然要靠特判兜底。 - * base 在 `/lib` 下时跳过:`/lib` 是 layout/runtime 的结构域(见 [[结构域]]),那里的 `·` - * 是包·函数命名分隔符而非成员写,由 layout 全权写入。 */ -void kvlangBuiltinEnsureMemberBase(kvlangKv_t *kv, const char *frame_root, const char *base) { - if (!base || !base[0] || strncmp(base, "/lib", 4) == 0) return; +/* 成员写(memitem)的前置条件:memhead(容器值 base)必须已存在。不存在即拒绝写入—— + * 容器由用户显式建(`m:T = {}`),不隐式补:隐式补会掩盖「写了一个不存在的容器」这一 + * 错误,与「禁止写入未声明的 key」一致(对齐 strict 语义,见 [[容器]])。 + * `/lib` 下跳过:那是 layout/runtime 的结构域,`·` 是包·函数命名分隔符而非成员写。 + * 返回 0 通过;-1 拒绝(调用方置 TypeError)。 */ +int kvlangBuiltinCheckMemhead(kvlangKv_t *kv, const char *frame_root, const char *base) { + if (!base || !base[0] || strncmp(base, "/lib", 4) == 0) return 0; char *path = kvlangBuiltinResolveWriteSlot(kv, frame_root, base); kvlangXvalue_t cur; kvlangXvalueZero(&cur); kvlangKvGetOne(kv, path, &cur); - if (kvlangXvalueNone(&cur)) { - kvlangXvalue_t empty; kvlangXvalueNewTlv(&empty, KVSPACE_KIND_MAP, NULL, 0, 1); - kvlangKvPair_t p = { path, empty }; - char err[256]; - kvlangKvSet(kv, &p, 1, err, sizeof err); - kvlangXvalueFree(&empty); - } + int ok = !kvlangXvalueNone(&cur); kvlangXvalueFree(&cur); free(path); + return ok ? 0 : -1; } int kvlangBuiltinKvSet(kvlangFrame_t *f) { @@ -101,7 +84,13 @@ int kvlangBuiltinKvSet(kvlangFrame_t *f) { char *key; kvlangXvalue_t *val; if (f->inst->nr >= 3) { const char *base = f->inst->reads[0].name; - if (strncmp(base, "/lib", 4) != 0) ensure_local_container(f, base); + char *fr = kvlangKeytreeFrameRoot(f->pc); + int ok = kvlangBuiltinCheckMemhead(f->kv, fr, base); + free(fr); + if (ok != 0) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "TypeError: memhead %s does not exist — declare the container first (e.g. `%s:T = {}`)", base, base); + } key = member_path(f, in, n - 1); val = &in[n - 1]; } else { key = n >= 1 ? path_arg(f, 0, in) : NULL; val = &in[1]; } diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index 08ea92de..647a7dc9 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -299,6 +299,8 @@ static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, char return 0; } +static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, char *lt, size_t cap); + /* HandleCall:创建子帧。返回 EntryPC(frameRoot),失败 NULL */ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) { kvlangStrbuf_t vtid_b; kvlangStrbufInit(&vtid_b); @@ -423,6 +425,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) char *rk = resolve_read_path(kv, caller_fr, arg->name); bool concrete = !kvlangXvalueNone(&arg->val) && !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWIR) && !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWFUNC); char lt[256] = {0}; + param_decl_type(kv, func_key, -(i + 1), lt, sizeof lt); if (concrete) { /* 字面量无变量槽,一律写 ._litN;勿沿用 resolve_read_path 的返回值—— * 否则字面量内容(如 "https://x" 里的 //)会被当路径段,二次读回即丢。 */ @@ -456,23 +459,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) char *wk = resolve_read_path(kv, caller_fr, inst->writes[i].name); if (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); + param_decl_type(kv, func_key, i + 1, lt, sizeof lt); kvlangXvalue_t wv; kvlangXvalueNewPtr(&wv, lt, wk); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = wv; @@ -504,6 +491,30 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) return NULL; } +/* 帧槽实参 Ptr 的类型来自**形参声明**:读参数定义键 funcDir.[0,±x] 的 body + * (名字\x00类型串),取出类型串。绝不从实参值的 head 反推——实参可以是 None + * (如递归传空子树),那时无值可取,Ptr 会因空 langtype 构造失败而整个参数丢失。 */ +static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, char *lt, size_t cap) { + kvlangStrbuf_t pk; kvlangStrbufInit(&pk); + kvlangStrbufPrintf(&pk, "%s.[0,%d]", func_key, x); + 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) continue; + int tl = al - bi - 1; + if (tl > 0 && tl < (int)cap) { memcpy(lt, ab + bi + 1, (size_t)tl); lt[tl] = 0; } + break; + } + } + } + kvlangXvalueFree(&dv); +} + int kvlangCtlCall(kvlangFrame_t *f) { char *sub = handle_call(f->kv, f->pc, f->inst); if (!sub) return -1; diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 58e3216a..37493e86 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -339,7 +339,8 @@ void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const const kvlangXvalue_t *val, kvlangXvalue_t *out); char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, const char *name); /* 成员写的 base 尚无值 → 落空 stringkeymap 值(`/lib` 下跳过,见 builtin_kv.c)。 */ -void kvlangBuiltinEnsureMemberBase(kvlangKv_t *kv, const char *frame_root, const char *base); +/* 成员写(memitem)前置条件:memhead(base 容器值)必须已存在;缺则返回 -1 拒绝写入。 */ +int kvlangBuiltinCheckMemhead(kvlangKv_t *kv, const char *frame_root, const char *base); char *kvlangBuiltinResolveReadKey(kvlangKv_t *kv, const char *frame_root, const char *name, const kvlangXvalue_t *val); bool kvlangBuiltinTryParseNumber(const char *s, kvlangXvalue_t *out); /* 成功 out 接管 */ void kvlangDisplay(const kvlangXvalue_t *v, char **out); /* malloc,对齐 Go Display */ diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" index a53785c6..c0ca255d 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" @@ -56,11 +56,11 @@ kvlang 的数字种类**必须携带精确位宽**: | 种类 | 说明 | |------|------| | `stringkeymap` | map 容器,langtype 为 `key·value`(如 `[]char/utf8·int64`) | -| `struct` | 结构原型实例的种类(见 [[容器]] 与结构声明) | +| `struct` | 结构原型实例的种类(见 [[struct]]) | | `index` | **storetype**:索引型节点(map/struct 值容器的 `{key}·` memindex、lib/rwfunc 目录节点);物理=m×n 对齐字符串数组,dims=[len,cap,M](M align8) | | `extindex` | **storetype**:可扩展索引型节点(vthread 运行栈等,cap 可增长);与 `ref`/`@ext` 无关,extindex 是 storetype 不是 ref=2 | -容器的物理组成(值 + memindex)见 [[容器]]。 +容器的物理组成(值 + memindex)见 [[map容器]](struct 同此物理模型)。 ### 时间种类 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" index 2a9b28dc..9329262f 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" @@ -23,7 +23,7 @@ lib kvlang/spec/类型系统/种类_代码与特殊值 { - **None** 不是种类:它是 `storetype=NONE` 的空结构体 XValue。任何有名变量都有地址、都有 XValue;未 赋值即 None。None 参与算术、比较、分支条件、类型构造一律 TypeError(见下文与 [[执行模型]])。 - **ptr(指针)** 不是种类:它是 head 的 `ref=1`(存储位置维),head 的 storetype/langtype 描述目 - 标的完整形态,body 存目标 key 路径(见 [[容器]])。**空指针就是 None**(无值),不设"空 Ptr"、也 + 标的完整形态,body 存目标 key 路径(见 [[map容器]])。**空指针就是 None**(无值),不设"空 Ptr"、也 不用 `""` 冒充(见 [[ptr]])。 > ptr(`ref=1`)是 head 的 ref 字段(存储位置维);kvlang **源码**类型标注最前仍可书写 `*`/`@` 前缀 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/07-\346\226\207\346\263\225\344\270\216\345\220\210\346\263\225\346\200\247.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/07-\346\226\207\346\263\225\344\270\216\345\220\210\346\263\225\346\200\247.kv" index 32e58d1e..c7e4cc5a 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/07-\346\226\207\346\263\225\344\270\216\345\220\210\346\263\225\346\200\247.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/07-\346\226\207\346\263\225\344\270\216\345\220\210\346\263\225\346\200\247.kv" @@ -21,12 +21,38 @@ lib kvlang/spec/类型系统/文法与合法性 { 空 `[]` **等价** `[.]`(恰一维、任意长)——字符串标注 `[]char/<编码>` 即此形。一个 dims 内**至多一 个**变元量词(`?`/`*`/`+`),其余须为定长元(整数或 `.`)——保证匹配 O(n) 确定、无回溯(见 [[匹配语 义与变参]])。 -- **mapexpr** = `key·value`,`value` 递归为完整类型表达式(故 map 可无限嵌套)。 -- **key** = `[]char/<编码>`(字符串键)或 `[scalar,…]`(标量元组键);`scalar` 限 `bool` 与十个定宽 - 数字种类。 +- **mapexpr** = `memitemkeylangtype·memitemvaluelangtype`——`·`(U+00B7 中点)连接**两条各自独立的 + langtype**:左侧是 item **键**类型、右侧是 item **值/body** 类型。值类型递归为完整类型表达式(故 + map 可无限嵌套)。map 声明形如 `memheadname:memitemkeylangtype·memitemvaluelangtype={}`,术语与形态 + 详见 [[map容器]]。 +- **memitemkey**(键类型,三选一,各自独立可校验): + - **裸标量** `scalar`——如 `int64`、`float64`、`bool`;`scalar` 限 `bool` 与十个定宽数字种类。 + - **字符串键** `[]char/<编码>`——等价 Go `map[string]V`。 + - **标量元组键** `[scalar,…]`——括号内是**种类名序列**(如 `[float64,float64]`),元素须全为标量。 + +### 访问键逐字铁律:禁止 `[]` 包裹、禁止自动改键 + +成员的访问键(落 kvspace 的中点键、runtime 解释的成员键)**就是 memitemkey 值按其 langtype 的字面量本 +身**,kvspace 与 kvlangruntime **均不得**改写、归一化或自动加壳: + +| memitemkey | 合法访问 | 禁止(自动包裹/改键) | +|------------|---------|----------------------| +| `int64` | `b·200` | `b·[200]`(裸标量禁包成 1 元元组) | +| `[float64,float64]` | `a·[342.124,23.2345]` | 塌缩为 `[2]float64` 后再改键 | +| `[]char/utf8` | `m·a` / `kv·set(m,"a",v)` | 加维度或改编码 | + +即 `a:[float64,float64]·int32={}` 的真实存储/访问路径必须是 `a·[342.124,23.2345]`; +`b:int64·int64={}` 必须是 `b·200`(**不是** `b·[200]`)。裸标量键与 1 元元组键 `[int64]`(访问键 +`b·[200]`)是**不同** memitemkey,不得互相转换。 + +### 易混淆:`[float64,float64]`(元组键)≠ `[2]float64`(数组形状) + +括号内**放什么**决定构造:**放种类名**(`[float64,float64]`)是 memitemkey 的**标量元组键**;**放整数 ++ 尾随种类**(`[2]float64`)是 shape 的 `dims`——1 维、长度 2 的 float64 数组(见 [[数组形态]])。二者 +语义差别巨大(定长异构位置元组 vs 一维定长向量),layout 与 runtime **不得**将元组键与数组形状互相塌 +缩或推断转换。 - **any** = 通配,匹配任意种类;是**唯一**简写。 -- **structref** = 以 `/` 起始的原型路径(如 `/lib/Point`),作为 atom 标注结构类型(见 [[容器]] 的 - struct)。 +- **structref** = 以 `/` 起始的原型路径(如 `/lib/Point`),作为 atom 标注结构类型(见 [[struct]])。 > 轴量词 `.`/`?`/`*`/`+` 与正则一致,**只描述单个值的形状**。而**参数变参**(一个参数位吸收 0..N 个 实参,旧写法末读参尾缀 `...`)是**签名**层的 arity 构造,**不属于类型表达式**,见 [[函数]];它落成 @@ -39,7 +65,7 @@ lib kvlang/spec/类型系统/文法与合法性 { |------|------| | `int` `float` `char` | 家族简写,非种类名 | | `int64*` | 后缀 `*` 位置非法;`*`/`@` 前缀**仅**可书写在源码类型标注最前,layout 剥离落 `ref` 字节,wire langtype 不含前缀 | -| `char/utf8·int64` | map 键未加方括号 | +| `char/utf8·int64` | `char/*` 只能作 `[]char/*` 字符串键,不可裸作键(裸键仅限标量 `scalar`,如 `int64·int64` 合法) | | `[]int32·int64` | map 字符串键的编码非 `char/*` | | `[.]·int64` | map 键含维度 | | `[foo,int32]·int64` | 元组键含非标量元素 | diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" new file mode 100644 index 00000000..075088b4 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" @@ -0,0 +1,75 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/map容器 { + r####"# map 容器 + +**map**(种类 `stringkeymap`)是 kvlang 的一等键-值容器。异构命名成员容器 **struct** 是另一种容器语义 +类型,见 [[struct]](本卷不再涉及);kvlang **没有** `dict` 种类,也没有 `object`/`scope` 种类(见 +[[总体方案]])。 + +## 标准 map 定义 + +一个 map 的 kvlang 代码形式: + +``` +memheadname : memitemkeylangtype · memitemvaluelangtype = {} +``` + +| 位置 | 名称 | 含义 | +|------|------|------| +| `:` 之前 | **memheadname** | map 的名字(头名,命名整族成员) | +| `:` 与 `·` 之间 | **memitemkeylangtype** | item **键**的 langtype(独立 langtype) | +| `·` 之后 | **memitemvaluelangtype** | item **值/body** 的 langtype(独立 langtype,可递归嵌套) | + +`·`(U+00B7 中点)连接 item 的键类型与值类型两条**各自独立**的 langtype。**通过 item 的 key 访问 item +的 body**:写 `memheadname·` 即取到该 item 的 body(memitemvalue)。 + +**memitemkey** 是 key-eligible langtype,三选一:**裸标量**(`int64`)、**字符串键** +`[]char/<编码>`、**标量元组键** `[scalar,…]`。memitemkey 逐字落键——访问键就是 memitemkey 值按其 +langtype 的**字面量本身**,kvspace 与 kvlangruntime 均**不得**加壳、归一化或改键(逐字铁律与 +`[float64,float64]`(元组键)≠ `[2]float64`(数组形状)的区分见 [[文法与合法性]])。 + +## 例子 + +```kv +m:[]char/utf8·int64 = {} // 字符串键 map;空字面量须带类型标注 +m·a = 10 // 命名成员写 +kv·set(m, "b", 20) // 动态键写 +kv·get(m, "a") -> x // 动态键读;缺失返回 None + +b:int64·int64 = {} // 裸标量键 map +b·200 = 7 // 访问键就是 200,禁止写成 b·[200] + +a:[float64,float64]·int32 = {} // 标量元组键 map([float64,float64] ≠ [2]float64) +a·[342.124,23.2345] = 1 // 访问键就是元组字面量本身 +``` + +memitemvalue 可递归嵌套(`[]char/utf8·[]char/utf8·int64` 即 `map[string]map[string]int64`)。 + +## 锚例 + +- 字面量与访问:`tutorial/10-types/01-typed-map.kv`、`tutorial/05-dict/literal.kv`。 +- 动态键、缺失返 None:`tutorial/10-types/02-string-map.kv`、`05-dict/kv_has_string_key.kv`。 +- 嵌套 map 与元组键签名:`tutorial/10-types/04-nested-type.kv`、`05-tuple-key.kv`。 + +## 容器物理结构 + +map 在 kvspace 中分两部分落库,`·` 是值容器唯一的 memindex 标记(注:`/lib/{pkg}·{func}` 中的 `·` 是 +包·函数命名分隔符,与 memindex 无关,详见 [[总体方案]]): + +- **容器值**:落在裸 base 键(memheadname),storetype=index,body 空;langtype 为 map langtype(如 + `[]char/utf8·int64`)。 +- **成员(item)**:`base·memitemkey` 是独立子 key,各自持该 item 的 body(memitemvalue)。 +- **memindex**:落在 `base·` 键(尾中点),storetype=index,其 body 是成员键索引矩阵,是成员枚举的权 + 威来源。 + +memindex 的线格式为 dims = `[len, cap, M]` 的矩阵:`len` = 有效成员数、`cap` = 预留行数 +(`cap ≥ len`,满则翻倍扩容)、`M` = 成员键 UTF-8 字节最大长向上 8 对齐后的**行宽**;body 为 +`cap × M` 字节,前 `len` 行是有序成员键(NUL 补齐),其余行全 NUL。容量内增删成员 body 长恒定、就地覆 +写。`M` 是行宽(行 stride),`cap` 是可翻倍的容量。(此物理模型 struct 同样适用。) + +## 空 map 字面量须带类型标注 + +`{}` 空 map 字面量**必须**在赋值目标处带类型标注(如 `x:[]char/utf8·int64 = {}`);无标注时 layout 报 +错——空字面量本身无法推断 memitemkey/memitemvalue 类型。 +"#### -> /lib/kvlang/spec/类型系统/map容器 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" deleted file mode 100644 index 9f005982..00000000 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" +++ /dev/null @@ -1,52 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/spec/类型系统/容器 { - r####"# 容器 - -kvlang 的复合命名数据只有两种容器语义类型:**stringkeymap**(键-值映射)与 **struct**(异构命名成 -员)。kvlang **没有** `dict` 种类,也没有 `object` 或 `scope` 种类(见 [[总体方案]])。容器物理落 -storetype=index,成员经 `{key}·` memindex 兄弟槽枚举(与 lib/rwfunc 目录节点用 `/` 子路径不同)。此 -外 **ptr**(指针)是 ref=1 的值形态,指向另一 key。 - -## 锚例 - -- stringkeymap 字面量与访问:`tutorial/10-types/01-typed-map.kv`、`tutorial/05-dict/literal.kv`。 -- stringkeymap 与动态键、缺失返 None:`tutorial/10-types/02-string-map.kv`、 - `05-dict/kv_has_string_key.kv`。 -- 嵌套 map 与元组键签名:`tutorial/10-types/04-nested-type.kv`、`05-tuple-key.kv`。 -- struct 声明/实例化/默认值/多类型字段:`tutorial/12-struct/00-point.kv`、`01-fields.kv`。 -- 指针解引用、局部结构体、链表/树/图:`tutorial/12-struct/03-linked-list.kv`~`07-list-local.kv`。 - -## 容器结构 - -容器在 kvspace 中分两部分落库,`·`(U+00B7 中点)是值容器唯一的 memindex 标记(注: -`/lib/{pkg}·{func}` 中的 `·` 是包·函数命名分隔符,与 memindex 无关,详见 [[总体方案]]): - -- **容器值**:落在裸 base 键,storetype=index,body 空;langtype 为 map langtype(如 - `[]char/utf8·int64`)或 struct 原型路径(如 `/lib/Point`)。 -- **成员**:`base·name` 是独立子 key,各自持成员值。 -- **memindex**:落在 `base·` 键(尾中点),storetype=index,其 body 是成员名索引矩阵,是成员枚举的权 - 威来源。 - -memindex 的线格式为 dims = `[len, cap, M]` 的矩阵:`len` = 有效成员数、`cap` = 预留行数 -(`cap ≥ len`,满则翻倍扩容)、`M` = 成员名 UTF-8 字节最大长向上 8 对齐后的**行宽**;body 为 -`cap × M` 字节,前 `len` 行是有序成员名(NUL 补齐),其余行全 NUL。容量内增删成员 body 长恒定、就地 -覆写。`M` 是行宽(行 stride),`cap` 是可翻倍的容量。 - -## stringkeymap - -**stringkeymap** 是一等 map 类型,langtype 为 `key·value`(见 [[langtype签名类型表达式]]): -`[]char/utf8·int64` 即 `map[string]int64`,value 可递归嵌套(`[]char/utf8·[]char/utf8·int64`)。 - -```kv -m:[]char/utf8·int64 = {} // 空 map 同样须带类型标注 -m·a = 10 // 命名成员写 -kv·set(m, "b", 20) // 动态键写 -kv·get(m, "a") -> x // 动态键读;缺失返回 None -``` - -## 空容器字面量须带类型标注 - -`{}` 空 stringkeymap 字面量**必须**在赋值目标处带类型标注(如 `x:[]char/utf8·int64 = {}`);无标注时 -layout 报错——空字面量本身无法推断 key/value 类型。 -"#### -> /lib/kvlang/spec/类型系统/容器 -} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" index 13094e53..1c33bf4f 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" @@ -4,12 +4,23 @@ lib kvlang/spec/类型系统/成员访问 { | 语法 | 语义 | |------|------| -| `base·name` | 静态键成员读/写 | -| `base·*k` | 动态键:变量 `k` 的值作成员名(用于局部容器) | -| `kv·get(base, k)` / `kv·set(base, k, v)` | 动态字符串键读/写 | +| `base·name` | 静态字符串键成员读/写 | +| `base·200` | 裸标量 memitemkey:访问键即标量字面量本身(禁 `base·[200]`) | +| `base·[342.124,23.2345]` | 标量元组 memitemkey:访问键即元组字面量本身 | +| `base·*k` | 动态键:`*` 令 runtime-c 解引用/求值 `k`,其值作 memitemkey | +| `kv·get(base, k)` / `kv·set(base, k, v)` | 动态键读/写 | -容器成员一律走 `·`(或 `kv·get`/`kv·set`),**不得**用 `[]`;`[]` 只索引 compact 数组(见 [[数组形 -态]])。 +**指针作 key 必须 `*` 开头**:当作为 memitemkey 的操作数是指针(ref=1 Ptr)时,**必须**写 `base·*k`, +由 runtime-c 沿 Ptr 解引用取被指向值作键;省略 `*` 非法(不自动解引用)。这是**显式** deref、非自动改 +键(运行期语义见 [[成员访问]](runtime 卷))。 + +`base` 即 map 的 memheadname(或 struct 实例名)。访问键**逐字即 memitemkey 值**:kvspace 与 +kvlangruntime 均不得归一化、改写或自动加壳(详见 [[文法与合法性]] 的逐字铁律,术语见 [[map容器]])。裸 +标量键 `base·200` 与 1 元元组键 `base·[200]` 是**不同** memitemkey,不得互转。 + +容器成员一律走 `·`(或 `kv·get`/`kv·set`)作**索引**,**不得**用 `[]` 索引运算符;`[]` 只索引 compact +数组(见 [[数组形态]])。注意:元组键 `base·[…]` 里的 `[…]` 是 **memitemkey 值字面量**、不是索引运算 +符——它整体是 `·` 右侧的那一个键。 ### `base·name` 的 base 解析:按指针优先、按名回退 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/18-\346\200\273\344\275\223\346\226\271\346\241\210.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/18-\346\200\273\344\275\223\346\226\271\346\241\210.kv" index caa80cc2..4f2db490 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/18-\346\200\273\344\275\223\346\226\271\346\241\210.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/18-\346\200\273\344\275\223\346\226\271\346\241\210.kv" @@ -3,7 +3,7 @@ lib kvlang/spec/类型系统/总体方案 { r####"# 类型系统总体方案:xvalue head 三正交维 > **本卷为新方案,待敲定。** 敲定后据此重构 [[种类与定宽类型]]、[[langtype签名类型表达式]]、[[数组形 - 态]]、[[容器]]。 + 态]]、[[map容器]]、[[struct]]。 每个 XValue 落入 kvspace 时,其类别由 head 三个**正交**字段表达,彻底取代旧的单一 `kind` 分类与 langtype 首字符 `*`/`@` 前缀。 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" index 06fb8383..388da1e1 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" @@ -35,7 +35,7 @@ lib kvlang/spec/类型系统/已敲定 { ## 仍待敲定 (本卷所有类型分歧已敲定。剩余仅实现层细节——如 index/extindex m×n 数组各字段精确偏移、签名行 `[0,x]` -坐标与 [[指令布局格式]] 二元坐标的具体编码、extindex 扩容策略——随 01-04 重构与 [[容器]] memindex 线 +坐标与 [[指令布局格式]] 二元坐标的具体编码、extindex 扩容策略——随 01-04 重构与 [[map容器]] memindex 线 格式对齐后落定,不属本卷规范分歧。) "#### -> /lib/kvlang/spec/类型系统/已敲定 } diff --git "a/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" "b/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" index 0682ffc8..dc67a2c7 100644 --- "a/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" +++ "b/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" @@ -39,10 +39,16 @@ kvlang 用两个符号访问复合数据,语义严格分离,runtime **不得 该规则让"局部结构体"与"指针成员访问"共用一套语法:结构体的 `base` 非 Ptr(走按名),指针变量持 Ptr(按 其 target)。跨函数共享的数据**应**放绝对路径(`/n1`),使不同帧的指针都解引用到同一 key。 -## 动态成员名 `·*` +## 动态成员名 `·*`:解引用操作数取 memitemkey -`base·*k` 用 `k` 的**值**作为成员名:读 `m·*k` 即以局部变量 `k` 当前的字符串值定位 `m·`。`·*` -用于局部容器变量的动态键访问;等价的显式形式为 `kv·get(m, k)` / `kv·set(m, k, v)`。 +`base·*k` 用 `k` 的**值**作为成员键:`·*` 令 runtime-c **解引用/求值**操作数 `k`,以其值定位 +`base·`(等价显式形式 `kv·get(base, k)` / `kv·set(base, k, v)`)。裸 `base·k`(无 `*`)用字面 +名 `k`;`base·*k` 才取 `k` 的值。 + +**指针作 memitemkey 必须 `*` 开头**:当作为 key 的操作数是**指针**(ref=1 Ptr)时,**必须**写 +`base·*k`,由 runtime-c 沿 Ptr 解引用到被指向值、再以该值作 memitemkey;对指针操作数省略 `*` 非法(不 +会自动解引用)。解引用得到的值随后**逐字**作键——`*` 是**显式** deref,与逐字铁律([[文法与合法性]], +禁止**自动**改键)不冲突:改写由源码 `*` 显式授权,非 runtime 擅自所为。 ## 指针:完整 langtype + 单跳类型检查 diff --git "a/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" "b/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" index 0b87198d..2e3f9ae0 100644 --- "a/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" +++ "b/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" @@ -10,10 +10,12 @@ type_expr = [ ref_prefix ] atom { "|" atom } (* ref 前缀可选;layout ref_prefix = "*" | "@" (* * → ref=ptr(指针);@ → ref=@ext(扩展世界) *) atom = shape | mapexpr | structref shape = [ dims ] ( any | kind ) -mapexpr = key "·" type_expr (* stringkeymap;value 可递归嵌套 *) +mapexpr = memitemkey "·" memitemvalue (* stringkeymap;`·` 连接两条独立 langtype:左=item 键类型、右=item 值类型。声明形如 memheadname:memitemkey·memitemvalue={},见 [[map容器]] *) +memitemvalue = type_expr (* item 值/body 类型,递归为完整 type_expr,故 map 可无限嵌套 *) +memitemkey = scalar (* 裸标量键:访问键即该标量字面量本身,如 b·200;禁止包成 [200](见 [[文法与合法性]]) *) + | "[]" char_enc (* 字符串键,等价 Go map[string]V *) + | "[" scalar { "," scalar } "]" (* 标量元组键:括号内是种类名序列,访问键即括号元组字面量本身,如 a·[342.124,23.2345] *) structref = path (* struct 原型路径,如 /lib/Point *) -key = "[]" char_enc (* 字符串键,等价 Go map[string]V *) - | "[" scalar { "," scalar } "]" (* 标量元组键,物理以字符串格式落 key *) char_enc = "char/utf8" | "char/utf32" | "char/ascii" scalar = "bool" | "int8" | "int16" | "int32" | "int64" diff --git a/tutorial/03-control/for.kv b/tutorial/03-control/for.kv index b5c38dfe..67c05032 100644 --- a/tutorial/03-control/for.kv +++ b/tutorial/03-control/for.kv @@ -11,6 +11,7 @@ rwfunc fahr_to_cels(fahr:int64) -> (cels:float64) { } rwfunc test() -> () { + data:[int64]·int64 = {} data·0 = 0 data·1 = 20 40 -> data·2 diff --git a/tutorial/04-ndarray/geo_coord.kv b/tutorial/04-ndarray/geo_coord.kv index 7303b10c..20960a94 100644 --- a/tutorial/04-ndarray/geo_coord.kv +++ b/tutorial/04-ndarray/geo_coord.kv @@ -4,6 +4,7 @@ // [39.9,116.4] -> Beijing // [31.23,121.47] -> Shanghai rwfunc test() -> () { + geo:[float64,float64]·[]char/utf32 = {} geo·[39.9,116.4] = "Beijing" geo·[31.23,121.47] = "Shanghai" geo·[39.9,116.4] -> bj diff --git a/tutorial/04-ndarray/string_array.kv b/tutorial/04-ndarray/string_array.kv index a2fbeb94..47a46cff 100644 --- a/tutorial/04-ndarray/string_array.kv +++ b/tutorial/04-ndarray/string_array.kv @@ -9,12 +9,13 @@ // x // y rwfunc test() -> () { - names = {"apple", "banana", "cherry"} + names:[int64]·[]char/utf32 = {"apple", "banana", "cherry"} for (s in names) { println(s) } println("-- inline --") - for (s in {"x", "y"}) { + inline_names:[int64]·[]char/utf32 = {"x", "y"} + for (s in inline_names) { println(s) } } diff --git a/tutorial/05-dict/kv_has_string_key.kv b/tutorial/05-dict/kv_has_string_key.kv index 0db0e6f9..eb4e7253 100644 --- a/tutorial/05-dict/kv_has_string_key.kv +++ b/tutorial/05-dict/kv_has_string_key.kv @@ -1,6 +1,7 @@ // 期望输出 // true rwfunc test() -> () { + /tmp/kvhas_test:[]char/utf8·int64 = {} /tmp/kvhas_test·data = 42 kv·get(/tmp/kvhas_test, "data") != None -> ok println(ok) diff --git a/tutorial/06-algo/map_reduce.kv b/tutorial/06-algo/map_reduce.kv index e2fb9508..73e321e1 100644 --- a/tutorial/06-algo/map_reduce.kv +++ b/tutorial/06-algo/map_reduce.kv @@ -30,6 +30,7 @@ rwfunc map_reduce(nums:[int64]·int64) -> (count:int64, total:int64) { } rwfunc test() -> () { + nums:[int64]·int64 = {} nums·0 = 1 3 -> nums·1 nums·2 = 5 diff --git a/tutorial/06-algo/word_count.kv b/tutorial/06-algo/word_count.kv index 216008f3..a96661ab 100644 --- a/tutorial/06-algo/word_count.kv +++ b/tutorial/06-algo/word_count.kv @@ -29,6 +29,7 @@ rwfunc word_count(chars:[int64]·[]char/utf32) -> (nw:int64) { } rwfunc test() -> () { + chars:[int64]·[]char/utf32 = {} chars·0 = "h" "e" -> chars·1 chars·2 = "l" diff --git a/tutorial/08-leetcode/020_valid_parentheses.kv b/tutorial/08-leetcode/020_valid_parentheses.kv index a736a742..90f6fb86 100644 --- a/tutorial/08-leetcode/020_valid_parentheses.kv +++ b/tutorial/08-leetcode/020_valid_parentheses.kv @@ -13,6 +13,7 @@ rwfunc is_valid(s:[]char/utf32) -> (ok:int64) { c = s[i] if (c == "(") { si + 1 -> si + /tmp/vp:[int64]·int64 = {} _ = kv·set("/tmp/vp", si, 1) } if (c == "[") { diff --git a/tutorial/08-leetcode/038_count_and_say.kv b/tutorial/08-leetcode/038_count_and_say.kv index 39462fa4..593c4eb5 100644 --- a/tutorial/08-leetcode/038_count_and_say.kv +++ b/tutorial/08-leetcode/038_count_and_say.kv @@ -8,19 +8,20 @@ // 1211 // 111221 rwfunc next_seq(src_len:int64) -> (dst_len:int64) { + /tmp/s2:[int64]·int64 = {} i = 0 di = 0 while (i < src_len) { - /tmp/s[i] -> cur + kv·get("/tmp/s", i) -> cur count = 1 j = i + 1 while (j < src_len) { - /tmp/s[j] -> v + kv·get("/tmp/s", j) -> v if (v == cur) { count + 1 -> count ; j + 1 -> j } else { src_len -> j } } - _ = kv·set("/tmp/s2", di, count) - _ = kv·set("/tmp/s2", di+1, cur) + _ = kv·set("/tmp/s2", di, count) + _ = kv·set("/tmp/s2", di+1, cur) di + 2 -> di i + count -> i } @@ -30,8 +31,8 @@ rwfunc next_seq(src_len:int64) -> (dst_len:int64) { rwfunc print_seq_len(seq_len:int64) -> () { i = 0 while (i < seq_len) { - cnt = /tmp/s[i] - val = /tmp/s[i+1] + kv·get("/tmp/s", i) -> cnt + kv·get("/tmp/s", i+1) -> val k = 0 while (k < cnt) { print(val) @@ -52,6 +53,7 @@ rwfunc copy_from_s2_to_s(s2_len:int64) -> () { } rwfunc count_and_say(N:int64) -> () { + /tmp/s:[int64]·int64 = {} _ = kv·set("/tmp/s", 0, 1) // count=1 of digit 1 _ = kv·set("/tmp/s", 1, 1) cur_len = 2 diff --git a/tutorial/08-leetcode/056_merge_intervals.kv b/tutorial/08-leetcode/056_merge_intervals.kv index d2c76cc4..73a03571 100644 --- a/tutorial/08-leetcode/056_merge_intervals.kv +++ b/tutorial/08-leetcode/056_merge_intervals.kv @@ -10,12 +10,17 @@ rwfunc bubble_sort(n:int64) -> () { while (i < n - 1) { j = 0 while (j < n - 1 - i) { - /tmp/mi[j][0] -> a - /tmp/mi[j+1][0] -> b + kv·get("/tmp/mi", j) -> _t + _t[0] -> a + kv·get("/tmp/mi", j+1) -> _t + _t[0] -> b if (a > b) { // swap start - /tmp/mi[j][1] -> a1 - /tmp/mi[j+1][1] -> b1 + kv·get("/tmp/mi", j) -> _t + _t[1] -> a1 + kv·get("/tmp/mi", j+1) -> _t + _t[1] -> b1 + /tmp/mi:[int64]·[2]int64 = {} _ = kv·set("/tmp/mi", j, [b, b1]) _ = kv·set("/tmp/mi", j+1, [a, a1]) } @@ -28,17 +33,19 @@ rwfunc bubble_sort(n:int64) -> () { rwfunc merge(n:int64) -> (m:int64) { bubble_sort(n) ki = 0 - /tmp/mi[0] -> cur + /tmp/mi:[int64]·[2]int64 = {} + kv·get("/tmp/mi", 0) -> cur idx = 1 while (idx < n) { - /tmp/mi[idx] -> nxt + kv·get("/tmp/mi", idx) -> nxt cur[1] -> cur_end nxt[0] -> nxt_start nxt[1] -> nxt_end if (cur_end >= nxt_start) { if (nxt_end > cur_end) { + /tmp/mr:[int64]·[2]int64 = {} _ = kv·set("/tmp/mr", ki, [cur[0], nxt_end]) - /tmp/mr[ki] -> cur + kv·get("/tmp/mr", ki) -> cur } } else { _ = kv·set("/tmp/mr", ki, cur) @@ -53,15 +60,17 @@ rwfunc merge(n:int64) -> (m:int64) { rwfunc test() -> () { // init intervals in tmp storage + /tmp/mi:[int64]·[2]int64 = {} _ = kv·set("/tmp/mi", 0, [1, 3]) _ = kv·set("/tmp/mi", 1, [2, 6]) _ = kv·set("/tmp/mi", 2, [8, 10]) _ = kv·set("/tmp/mi", 3, [15, 18]) + /tmp/mr:[int64]·[2]int64 = {} cnt = merge(4) i = 0 while (i < cnt) { - /tmp/mr[i] -> iv + kv·get("/tmp/mr", i) -> iv print("[", iv[0], ",", iv[1], "]"); println() i + 1 -> i } -} +} \ No newline at end of file diff --git a/tutorial/08-leetcode/062_unique_paths.kv b/tutorial/08-leetcode/062_unique_paths.kv index 4462c6f0..2aea2688 100644 --- a/tutorial/08-leetcode/062_unique_paths.kv +++ b/tutorial/08-leetcode/062_unique_paths.kv @@ -4,6 +4,7 @@ // 28 // 3 rwfunc dp(m:int64, n:int64) -> (result:int64) { + /tmp/up:[int64]·int64 = {} i = 0 while (i < n) { _ = kv·set("/tmp/up", i, 1) @@ -13,14 +14,14 @@ rwfunc dp(m:int64, n:int64) -> (result:int64) { while (j < m) { k = 1 while (k < n) { - /tmp/up[k] -> v - /tmp/up[k-1] -> prev + kv·get("/tmp/up", k) -> v + kv·get("/tmp/up", k-1) -> prev _ = kv·set("/tmp/up", k, prev + v) k + 1 -> k } j + 1 -> j } - /tmp/up[n-1] -> result + kv·get("/tmp/up", n-1) -> result } rwfunc test() -> () { diff --git a/tutorial/08-leetcode/118_pascal_triangle.kv b/tutorial/08-leetcode/118_pascal_triangle.kv index 9e81747c..a71867d6 100644 --- a/tutorial/08-leetcode/118_pascal_triangle.kv +++ b/tutorial/08-leetcode/118_pascal_triangle.kv @@ -18,7 +18,9 @@ // 4 // 1 rwfunc generate(num_rows:int64) -> () { + /tmp/cur:[int64]·int64 = {} // store prev row in dict + /tmp/pt:[int64]·int64 = {} _ = kv·set("/tmp/pt", 0, 1) println(1) if (num_rows == 1) { } @@ -28,8 +30,8 @@ rwfunc generate(num_rows:int64) -> () { _ = kv·set("/tmp/cur", 0, 1) i = 1 while (i < r) { - /tmp/pt[i-1] -> a - /tmp/pt[i] -> b + kv·get("/tmp/pt", i-1) -> a + kv·get("/tmp/pt", i) -> b v = a + b println(v) _ = kv·set("/tmp/cur", i, v) @@ -40,7 +42,7 @@ rwfunc generate(num_rows:int64) -> () { // copy cur to pt k = 0 while (k <= r) { - /tmp/cur[k] -> val + kv·get("/tmp/cur", k) -> val _ = kv·set("/tmp/pt", k, val) k + 1 -> k } diff --git a/tutorial/08-leetcode/217_contains_dup_hash.kv b/tutorial/08-leetcode/217_contains_dup_hash.kv index 07886b9f..4b2e7a8b 100644 --- a/tutorial/08-leetcode/217_contains_dup_hash.kv +++ b/tutorial/08-leetcode/217_contains_dup_hash.kv @@ -31,8 +31,8 @@ rwfunc has_dup(a:[]int64) -> () { rwfunc no_dup() -> () { a:[]int64 = [1, 2, 3, 4] - ndarray·numel(a) -> n seen:[]char/utf8·int64 = {} + ndarray·numel(a) -> n found = false i = 0 while (i < n) { From 5a3ee1d669a832c86922b2da580558ce3c5f8aea Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Fri, 11 Sep 2026 09:27:11 +0800 Subject: [PATCH 4/5] =?UTF-8?q?layout/runtime(-rs)/spec/tutorial=20?= =?UTF-8?q?=E8=AF=AD=E4=B9=89=E5=BB=B6=E7=BB=AD=20+=20v0.2.16=20benchmark?= =?UTF-8?q?=20=E5=BF=AB=E7=85=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layout code/kvkind/lower/parser + runtime C(builtin_collection/kv/kvcpu/ rwir/rwirext/xvalue) + runtime-rs(engine/rwir/networld-fs) 配合;spec 11-stringkeymap形态/14-map容器/06-控制流脱糖/06-runtime开发规范/附录表达式 同步;ndarray/networld/vthread/leetcode tutorial 对齐;含新 benchmark CSV。 Co-Authored-By: Claude Opus 4.8 --- .../results-v0.2.16-20260911T011308Z.csv | 31 + layout/src/code.rs | 55 +- layout/src/kvkind.rs | 45 +- layout/src/lower.rs | 111 +- layout/src/parser.rs | 51 +- runtime-rs/src/engine.rs | 6 +- runtime-rs/src/rwir/mod.rs | 4 +- runtime-rs/src/rwir/networld/fs.rs | 2 +- runtime/.clang-format | 2 + runtime/src/builtin_collection.c | 984 +++++++++++++----- runtime/src/builtin_internal.h | 91 +- runtime/src/builtin_kv.c | 392 +++++-- runtime/src/kvcpu.c | 726 +++++++++---- runtime/src/runtime_internal.h | 524 ++++++---- runtime/src/rwir.c | 54 +- runtime/src/rwirext.c | 448 ++++---- runtime/src/xvalue.c | 212 +++- ...1-stringkeymap\345\275\242\346\200\201.kv" | 9 +- .../14-map\345\256\271\345\231\250.kv" | 11 +- ...66\346\265\201\350\204\261\347\263\226.kv" | 2 +- ...00\345\217\221\350\247\204\350\214\203.kv" | 2 +- ...5-\350\241\250\350\276\276\345\274\217.kv" | 2 +- tutorial/01-basics/kv_mkindex_cap.kv | 2 +- tutorial/01-basics/kv_tree.kv | 4 +- tutorial/04-ndarray/map_coord.kv | 2 +- tutorial/04-ndarray/separated.kv | 6 +- tutorial/04-ndarray/sparse_literal.kv | 2 +- tutorial/08-leetcode/056_merge_intervals.kv | 17 +- tutorial/14-networld/01-exec.kv | 16 +- tutorial/14-networld/02-bash.kv | 8 +- tutorial/14-networld/03-python.kv | 8 +- tutorial/14-networld/04-kvlang.kv | 8 +- tutorial/14-networld/05-capture.kv | 8 +- tutorial/14-networld/06-fs.kv | 4 +- tutorial/14-networld/08-fs-dir.kv | 8 +- tutorial/14-networld/09-fs-exists.kv | 4 +- tutorial/15-vthread/02-crashrecover.kv | 4 +- 37 files changed, 2651 insertions(+), 1214 deletions(-) create mode 100644 benchmark/results/results-v0.2.16-20260911T011308Z.csv diff --git a/benchmark/results/results-v0.2.16-20260911T011308Z.csv b/benchmark/results/results-v0.2.16-20260911T011308Z.csv new file mode 100644 index 00000000..b944cbb3 --- /dev/null +++ b/benchmark/results/results-v0.2.16-20260911T011308Z.csv @@ -0,0 +1,31 @@ +timestamp,version,commit,cpu,case,input,samples,kvlang_shm_ns,kvlang_fs_ns,kvlang_redis_ns,python_ns,rust_ns,c_ns,python_ver,rust_ver,c_ver,native_opt,valid +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,binary_search,N=16,3,7099193,223205369,716253222,14349,717,512,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,binary_search,N=32,3,15275688,501195152,1656970081,24010,1380,1027,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,binary_search,N=64,3,33652224,1138764433,3784256743,45918,2840,2184,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,binary_trees,depth=4,3,5282216,278127932,739482394,14223,2474,5803,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,binary_trees,depth=5,3,9883753,637170827,1483571136,22263,3132,6512,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,binary_trees,depth=6,3,19741714,1660609241,2939132713,40068,4444,8371,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,fib,n=8,3,7732204,164578012,583684588,10730,303,299,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,fib,n=9,3,12536490,261690481,876688373,12828,388,423,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,fib,n=10,3,20571007,434462225,1448044410,15200,498,588,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,hash_table,N=50,3,4790641,223979622,690915351,33785,5123,327,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,hash_table,N=100,3,9478869,549312608,1397201286,61674,8287,444,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,hash_table,N=200,3,19748149,1459993787,2769276840,112690,15610,770,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,iops,N=500,3,7751474,379145374,1598561541,23470,497,287,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,iops,N=1000,3,14960984,753124227,3111577142,45404,794,451,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,iops,N=2000,3,29944181,1474923033,6268901390,88209,1474,776,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,k_nucleotide,rep=3,3,10877880,425792743,1593215560,39363,3352,197,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,k_nucleotide,rep=5,3,21262879,700458624,2680194117,63228,4896,283,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,k_nucleotide,rep=8,3,41275509,1098307648,4196984458,98657,7428,400,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,matmul,N=4,3,3368303,273707259,668428398,20272,358,225,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,matmul,N=6,3,9842886,952503423,2119539868,52564,434,370,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,matmul,N=8,3,22320955,2733835542,4897455300,116622,724,557,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,nqueens,N=4,3,5350128,165642072,372506635,14109,325,258,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,nqueens,N=5,3,16144084,511662662,1179281110,23232,577,565,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,nqueens,N=6,3,45942985,1566409608,3445230882,50474,1501,1520,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,prime_sieve,N=50,3,12260942,504422820,2129346527,40230,15878,17671,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,prime_sieve,N=75,3,23484188,1016440944,4342725066,62068,18500,19593,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,prime_sieve,N=100,3,34097331,1488108259,6456957991,82141,21762,21318,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,quicksort,"N=32,seed=1",3,8070433,504594995,1478560168,18553,1096,1015,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,quicksort,"N=64,seed=1",3,17755186,1294659882,3412003366,33848,2294,2208,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True +2026-09-11T01:13:08Z,v0.2.16-9-gfb08343-dirty,fb08343,Intel(R) Xeon(R) Platinum 8336C CPU @ 2.30GHz,quicksort,"N=128,seed=1",3,37873522,3499084466,7528516277,66947,5173,4588,3.12.7,1.97.1,13.3.0,gcc -O1 / rustc opt-level=1 / cpython,True diff --git a/layout/src/code.rs b/layout/src/code.rs index 236018c3..eb95390f 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -34,6 +34,7 @@ pub fn compile(kv: &mut Kv, src: &str) -> Result, String> { for f in subscript_check_funcs(&file) { diags.extend(lower::check_container_subscript(&f)); diags.extend(lower::check_map_defined(&f)); + diags.extend(lower::check_container_typed(&f)); } for d in &diags { eprintln!("{}", d.string()); @@ -142,6 +143,7 @@ pub fn vet(src: &str) -> Result<(), String> { for f in subscript_check_funcs(&file) { diags.extend(lower::check_container_subscript(&f)); diags.extend(lower::check_map_defined(&f)); + diags.extend(lower::check_container_typed(&f)); } for d in &diags { eprintln!("{}", d.string()); @@ -460,6 +462,7 @@ struct RawInst { opcode: String, reads: Vec, writes: Vec, + write_types: Vec, } /// 操作数:引用(变量/opcode/路径)、字符串字面量、其它字面量(数值/bool)。 @@ -490,19 +493,23 @@ fn read_insts(kv: &mut Kv, dir: &str) -> Vec { j += 1; } let mut writes = Vec::new(); + let mut wtypes = Vec::new(); let mut j = 1; loop { let d = kv.get_one(&format!("{dir}[{n},{j}]")); if d.is_empty() { break; } - writes.push(kvkind::rwir_sig(&d)); + let (w, ty) = kvkind::write_slot_name(&d); + writes.push(w); + wtypes.push(ty); j += 1; } out.push(RawInst { opcode, reads, writes, + write_types: wtypes, }); n += 1; } @@ -594,14 +601,18 @@ fn build_inst(inst: &RawInst, by_irseq: &HashMap) -> Instruction { v[0] = ty; v } else { - Vec::new() + let mut v = inst.write_types.clone(); + if v.iter().all(String::is_empty) { + v = Vec::new(); + } + v }; Instruction { comments: Vec::new(), expr, writes: inst.writes.clone(), write_types, - arrow_left: is_fill, + arrow_left: is_fill || inst.write_types.iter().any(|t| !t.is_empty()), } } @@ -645,6 +656,15 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { let nr = fn_.sig.num_reads(); let nw = fn_.sig.num_writes(); let param_types: Vec = fn_.sig.langtype_list(); + // 形参/写参的**声明类型**:容器字面量写给带类型标注的形参时(`{…} -> m`,m:[]char/utf8·int64), + // 写槽须承载该 map langtype——否则 runtime 无从得知容器值 langtype(签名即声明处,见 [[map容器]])。 + let mut param_langtype: HashMap = HashMap::new(); + for (i, p) in fn_.sig.params.iter().enumerate() { + param_langtype.insert(p.name.clone(), param_types[i].clone()); + } + for (i, r) in fn_.sig.returns.iter().enumerate() { + param_langtype.insert(r.name.clone(), param_types[nr as usize + i].clone()); + } let mut pairs: Vec<(String, Vec)> = Vec::new(); pairs.push(( @@ -681,6 +701,7 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { &labels, &mut type_map, ¶m_coord, + ¶m_langtype, ); } if !labels.is_empty() { @@ -826,6 +847,7 @@ fn write_linear_inst( labels: &HashMap, type_map: &mut HashMap, params: &HashMap, + param_langtype: &HashMap, ) { for (j, w) in s.writes.iter().enumerate() { if j < s.write_types.len() && !s.write_types[j].is_empty() { @@ -873,11 +895,19 @@ fn write_linear_inst( )); } for (j, w) in s.writes.iter().enumerate() { - let wv = params - .get(w.as_str()) + let orig = w.as_str(); + let wv = params.get(orig).map(String::as_str).unwrap_or(orig); + let ty = s + .write_types + .get(j) .map(String::as_str) - .unwrap_or(w.as_str()); - pairs.push((format!("{prefix}/[{n},{}]", j + 1), slot_value(wv, ""))); + .filter(|t| !t.is_empty()) + .or_else(|| param_langtype.get(orig).map(String::as_str)) + .unwrap_or(""); + pairs.push(( + format!("{prefix}/[{n},{}]", j + 1), + write_slot_value(wv, ty), + )); } if !pairs.is_empty() { let _ = kv.set(&pairs); @@ -909,6 +939,17 @@ fn opcode_value(opcode: &str) -> Vec { } } +/// 写槽值:写目标带 **map langtype** 标注时(`x:{keylt}·{valt} = {}`),槽的 langtype 即该 map +/// langtype、body 即变量名——runtime 据此把容器值按 [[map容器]] 落成「langtype=map langtype、 +/// storetype=index、body 空」,成员索引落兄弟槽 `x·`。其余写槽仍是 rwir 引用(body 带计数头)。 +/// 参数替换过的写槽(rwfunc 写参轴)不承载类型:声明类型已落 `/lib/.[0,k]` 参数定义键。 +fn write_slot_value(name: &str, ty: &str) -> Vec { + if ty.contains(keytree::MEMBER_SEP) { + return ffi::tlv_encode(ty, name.as_bytes(), 1); + } + slot_value(name, "") +} + /// 将字面量/引用字符串编码为 XValue TLV(rwir 槽值)。 fn slot_value(val: &str, target_char: &str) -> Vec { if !is_literal(val) { diff --git a/layout/src/kvkind.rs b/layout/src/kvkind.rs index a6b4f66f..2bef422b 100644 --- a/layout/src/kvkind.rs +++ b/layout/src/kvkind.rs @@ -39,17 +39,32 @@ pub fn head(data: &[u8]) -> ffi::kvspaceHead_t { } /// 解析 langtype 内容 → (dims, base kind)。langtype 无前缀(ref/ptr 归 head.ref)。 +/// **map langtype 无形状段**:`{keylt}·{valt}` 里 `·` 之前的方括号是键类型(`[int64]`、 +/// `[float64,float64]`),不是维度——与 `[2]float64`(数组形状)截然不同,故整串即基 kind。 +/// 非 map 串也只把**纯数字/空/?**的方括号当形状(见 [[map容器]])。 pub fn parse_langtype(kx: &str) -> (Vec, String) { + if kx.contains(super::keytree::MEMBER_SEP) { + return (Vec::new(), kx.to_string()); + } if kx.starts_with('[') { match kx.find(']') { - Some(end) => ( - kx[1..end] - .split(',') - .filter(|d| !d.is_empty()) - .map(|d| d.parse().unwrap_or(0)) - .collect(), - kx[end + 1..].to_string(), - ), + Some(end) => { + let inner = &kx[1..end]; + if inner.split(',').all(|d| { + let d = d.trim(); + d.is_empty() || d == "?" || d.parse::().is_ok() + }) { + return ( + inner + .split(',') + .filter(|d| !d.is_empty()) + .map(|d| d.parse().unwrap_or(0)) + .collect(), + kx[end + 1..].to_string(), + ); + } + (Vec::new(), kx.to_string()) + } None => (Vec::new(), kx.to_string()), } } else { @@ -354,6 +369,20 @@ pub fn plain(data: &[u8]) -> String { } /// rwir 族槽值的载荷串(opcode / 引用名):body 去 5 字节计数头后的字节。 +/// 写槽值 → (名字, 声明类型):写目标带 map langtype 标注(`x:{keylt}·{valt} = {}`)时,槽的 +/// langtype 即该 map langtype、body 即变量名(见 [[map容器]]);其余写槽走 rwir 引用载荷 +/// (跳过 5B 计数头)。dump 逆向时据此还原 `-> x:`,使 round-trip 不丢容器类型。 +pub fn write_slot_name(data: &[u8]) -> (String, String) { + if data.is_empty() { + return (String::new(), String::new()); + } + let k = kind(data); + if k.contains(super::keytree::MEMBER_SEP) { + return (value_string(data), k); + } + (rwir_sig(data), String::new()) +} + pub fn rwir_sig(data: &[u8]) -> String { if data.is_empty() { return String::new(); diff --git a/layout/src/lower.rs b/layout/src/lower.rs index 69a2e4c4..30e3e9e9 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -17,6 +17,84 @@ fn is_container_ty(ty: &str) -> bool { ty.contains(keytree::MEMBER_SEP) || ty.starts_with('/') } +/// 容器字面量的写目标必须带**完整 map langtype**(`{memitemkeylangtype}·{memitemvaluelangtype}`)。 +/// 容器值的 langtype 就是它(见 [[map容器]])——kvspace 里没有「langtype=种类名 stringkeymap 的值」; +/// runtime 也绝不据此退化兜底。类型来源二选一:写槽上的 `x:T = {…}` 标注,或签名里参数/返回的声明类型。 +/// 二者皆无即 layout 报错,逼代码作者补标注(`{}` 尤其无法推断:空字面量不含任何成员)。 +pub fn check_container_typed(fn_: &Func) -> Vec { + let mut diags = Vec::new(); + check_container_body(&fn_.body, fn_, &mut diags); + diags +} + +fn check_container_body(body: &[Stmt], fn_: &Func, diags: &mut Vec) { + for st in body { + match st { + Stmt::Instruction(s) => check_container_inst(s, fn_, diags), + Stmt::Scope(s) => check_container_body(&s.body, fn_, diags), + Stmt::If(s) => { + if let Some(c) = &s.cond { + check_container_inst(c, fn_, diags); + } + check_container_body(&s.then_, fn_, diags); + check_container_body(&s.else_, fn_, diags); + } + Stmt::While(s) => { + if let Some(c) = &s.cond { + check_container_inst(c, fn_, diags); + } + check_container_body(&s.body, fn_, diags); + } + Stmt::For(s) => check_container_body(&s.body, fn_, diags), + _ => {} + } + } +} + +fn check_container_inst(s: &Instruction, fn_: &Func, diags: &mut Vec) { + let Some(e) = &s.expr else { return }; + if e.op != "obj" && e.op != "map" { + return; + } + let Some(target) = s.writes.first() else { + return; + }; + let ann = s.write_types.first().map(String::as_str).unwrap_or(""); + // structref(`/lib/Name`)字面量是 struct 实例,不是 map 容器,另有 struct·new 路径。 + if ann.starts_with('/') { + return; + } + let declared = if ann.is_empty() { + fn_.sig + .params + .iter() + .chain(fn_.sig.returns.iter()) + .find(|p| &p.name == target) + .map(|p| p.ty.as_str()) + .unwrap_or("") + } else { + ann + }; + let msg = if declared.is_empty() { + format!("container literal `{{…}}` on {target:?} needs a map langtype, e.g. `{target}:[]char/utf8·int64 = {{}}`") + } else if !super::langtype::valid_langtype(declared) { + format!("invalid langtype {declared:?} on container literal target {target:?}") + } else if !declared.contains(keytree::MEMBER_SEP) { + format!("container type {declared:?} is not a map langtype; expect `{{memitemkeylangtype}}·{{memitemvaluelangtype}}`") + } else { + return; + }; + diags.push(Diagnostic { + pos: Pos { line: 0, col: 0 }, + warn: false, + info: false, + message: msg, + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); +} + /// 容器成员写的前置条件:base 必须**已声明且类型明确为容器**。 /// 禁止未定义类型的 map —— `m·k = v` 而 m 无显式声明即 error,不做任何自动推断。 pub fn check_map_defined(fn_: &Func) -> Vec { @@ -298,7 +376,7 @@ fn lower_body( if s.writes.len() == 1 { if let Some(e) = &s.expr { if e.op == "map" { - preamble.extend(expand_sparse(&s.writes[0], e, lg)); + preamble.extend(expand_sparse(&s.writes[0], e, &s.write_types, lg)); continue; } } @@ -492,7 +570,9 @@ fn lower_for_with_cont( s.iter.val.clone() } else if s.iter.op == "map" { let slot = lg.tmp(); - init_body.extend(expand_sparse(&slot, &s.iter, lg)); + // for-in 源是裸字面量:无写目标可标注,就地推 map langtype(元素类型必须唯一且可推)。 + let wty = infer_sparse_type(&s.iter, tm); + init_body.extend(expand_sparse(&slot, &s.iter, &wty, lg)); slot } else { let slot = lg.tmp(); @@ -685,14 +765,37 @@ fn goto_label(label: &str) -> Stmt { }) } +/// 容器字面量的 map langtype 推断(仅用于无写目标可标注的场合,如 for-in 源): +/// 位置型 `{v0,v1,…}` 键为 1 元坐标 `[int64]`,命名型 `{k=v}` 键为字符串 `[]char/utf8`; +/// 值类型取首个元素的推断类型。推不出(元素类型未知)则空 → runtime 直接 fatal 暴露。 +fn infer_sparse_type(e: &Expr, tm: &HashMap) -> Vec { + let (keylt, val) = if e.op == "map" { + ("[int64]", e.args.first()) + } else { + ("[]char/utf8", e.args.get(1)) + }; + // 成员值类型:char 元素在容器里按字符串数组存,故 `char/*` 上提为 `[]char/*`。 + let vt = match val.map(|a| slot_type(&a.val, tm)).unwrap_or_default() { + t if t.starts_with("char/") => format!("[]{t}"), + t => t, + }; + if vt.is_empty() { + Vec::new() + } else { + vec![format!("{keylt}{}{vt}", keytree::MEMBER_SEP)] + } +} + /// 散 key 数组字面量(parser 产出的 "map")→ 单条 `map(v0, v1, ...) -> base`, /// 运行时 map builtin 创建 map 根(kind=map)+ 子键 `base/[i]`(相对 key,方括号索引串)。 -fn expand_sparse(base: &str, e: &Expr, _lg: &mut LabelGen) -> Vec { +fn expand_sparse(base: &str, e: &Expr, wtypes: &[String], _lg: &mut LabelGen) -> Vec { let inst = Instruction { comments: Vec::new(), expr: Some(ast::call("map", e.args.clone())), writes: vec![base.to_string()], - write_types: Vec::new(), + // 写类型**必须原样带过来**:容器字面量的 map langtype 就靠它落到写槽 + // (`nums:[int64]·int64 = {…}`;见 [[map容器]])。曾在此清空,runtime 无从得知容器类型。 + write_types: wtypes.to_vec(), arrow_left: true, }; vec![Stmt::Instruction(inst)] diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 72ac3501..d356b399 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -786,7 +786,11 @@ impl Parser { comments: Vec::new(), })] } - _ => self.parse_inst().into_iter().map(Stmt::Instruction).collect(), + _ => self + .parse_inst() + .into_iter() + .map(Stmt::Instruction) + .collect(), } } @@ -907,11 +911,9 @@ impl Parser { /// 校验散 key 字面量 `{...}` 的出现位置。`top_legal` 表示当前上下文允许顶层出现 /// (赋值右值 / for-in 源);无论如何,其元素内部都不得再嵌套散 key 字面量。 - // 空容器字面量 `{}` 不含任何类型信息,必须由写目标显式标注 langtype: - // 禁 `d = {}`,须 `d:[]char/utf8·int64 = {}`(非空 `{a=…}` 可由成员推断,放行)。 /// `{}` 对应两种 langtype:写类型是 structref(`/lib/Name`)→ `struct·new(path, k, v, …)`; - /// 否则(stringkeymap / mapexpr)保留 `obj`(runtime 构 stringkeymap)。裸无类型 `{}` 由 - /// check_empty_container_typed 报错。desugar 已把成员/下标写目标改成 kv·set,故此处只命中简单局部。 + /// 否则(容器字面量)保留 `obj`(runtime 构 map 容器)。容器字面量必须有 map langtype, + /// 由 lower::check_container_typed 统一把关(那里能看见签名里的参数/返回类型)。 fn dispatch_obj_by_type(&mut self, inst: &mut Instruction) { if inst.expr.as_ref().map(|e| e.op.as_str()) != Some("obj") { return; @@ -929,44 +931,6 @@ impl Parser { )); } - fn check_empty_container_typed(&mut self, inst: &Instruction) { - let Some(e) = &inst.expr else { return }; - if e.op != "obj" || !e.args.is_empty() { - return; - } - if inst.writes.len() != 1 { - return; - } - // 仅约束简单局部变量目标;成员/下标目标(无类型标注语法)放行。 - let target = &inst.writes[0]; - if target.contains(keytree::MEMBER_SEP) || target.contains('[') { - return; - } - let ty = inst.write_types.first().map(String::as_str).unwrap_or(""); - let t = self.peek(); - if ty.is_empty() { - self.errors.push(Diagnostic { - pos: t.pos, - warn: false, - info: false, - message: "empty container literal {} needs a type annotation on its target, e.g. `d:[]char/utf8·int64 = {}`".to_string(), - source: String::new(), - src_file: String::new(), - src_name: String::new(), - }); - } else if !super::langtype::valid_langtype(ty) { - self.errors.push(Diagnostic { - pos: t.pos, - warn: false, - info: false, - message: format!("invalid langtype {ty:?} on empty container literal target"), - source: String::new(), - src_file: String::new(), - src_name: String::new(), - }); - } - } - fn check_sparse_usage(&mut self, e: &Expr, top_legal: bool) { let top_is_sparse = e.op == "map"; let bad = if top_is_sparse && top_legal { @@ -1087,7 +1051,6 @@ impl Parser { for k in 0..out.len() { let i = std::mem::take(&mut out[k]); self.check_write_type_match(&i); - self.check_empty_container_typed(&i); // 散 key 字面量 `{...}` 仅允许作赋值右值(单一写目标);其余位置报错。 let top_legal = i.writes.len() == 1; if let Some(e) = &i.expr { diff --git a/runtime-rs/src/engine.rs b/runtime-rs/src/engine.rs index e6329599..611c274e 100644 --- a/runtime-rs/src/engine.rs +++ b/runtime-rs/src/engine.rs @@ -245,13 +245,13 @@ impl Engine { } } - /// 写一个 stringkeymap 容器(对齐 C kv·list 表示,供 fs·list 等返回可 for-in 的字符串列表): - /// dst = 容器标记:kind=stringkeymap,body 空,dims=[n] + /// 写一个字符串列表容器(供 fs·list 等返回可 for-in 的字符串列表,对齐 C kv·list 表示): + /// dst = 容器值:langtype=`[int64]·[]char/utf32`(见 [[map容器]]),body 空,dims=[n] /// dst· = memindex:kind=index,body=[4B count LE]["[0]\n[1]\n..."](成员坐标名唯一权威) /// dst·[i] = 各成员字符串(char/utf32) pub fn set_str_list(&self, dst: &str, items: &[String]) { let n = items.len(); - self.set_tlv_encoded(dst, "stringkeymap", &[], &[n as i32]); + self.set_tlv_encoded(dst, "[int64]·[]char/utf32", &[], &[n as i32]); let mut body = (n as u32).to_le_bytes().to_vec(); for i in 0..n { if i > 0 { diff --git a/runtime-rs/src/rwir/mod.rs b/runtime-rs/src/rwir/mod.rs index 6a2ec157..10eced7c 100644 --- a/runtime-rs/src/rwir/mod.rs +++ b/runtime-rs/src/rwir/mod.rs @@ -120,7 +120,7 @@ pub const MYRWIRCAPS: &[(&str, Rwir)] = &[ ( "networld/proc·exec", Rwir { - rp: &["[]stringkeymap", "[]stringkeymap"], + rp: &["[int64]·[]char/utf32", "[int64]·[]char/utf32"], wp: &["uint8", "[]uint8", "[]uint8"], }, ), @@ -156,7 +156,7 @@ pub const MYRWIRCAPS: &[(&str, Rwir)] = &[ "networld/fs·list", Rwir { rp: &["[]char/utf8|[]char/utf32"], - wp: &["[]stringkeymap"], + wp: &["[int64]·[]char/utf32"], }, ), ( diff --git a/runtime-rs/src/rwir/networld/fs.rs b/runtime-rs/src/rwir/networld/fs.rs index 362eef0a..4e68df03 100644 --- a/runtime-rs/src/rwir/networld/fs.rs +++ b/runtime-rs/src/rwir/networld/fs.rs @@ -3,7 +3,7 @@ //! networld/fs·read(p, start, offset) -> raw 从 start 起读 offset 字节,返回 []uint8 //! networld/fs·write(p, data) -> n 把 data([]uint8) 覆盖写入 p(创建/截断),返回写入字节数(失败 -1) //! networld/fs·append(p, data) -> n 把 data([]uint8) 追加到 p 末尾,返回写入字节数(失败 -1) -//! networld/fs·list(p) -> names 列目录 p 的成员名([]stringkeymap,名字序);p 必须是目录 +//! networld/fs·list(p) -> names 列目录 p 的成员名([int64]·[]char/utf32,名字序);p 必须是目录 //! networld/fs·del(p) -> code 删除 p,0 成功/-1 失败;p 必须是文件或空目录(非空目录失败) //! networld/fs·mkdir(p) -> code 创建目录 p(含缺失父级,幂等),0 成功/-1 失败 //! networld/fs·exists(p) -> b p 是否存在(bool) diff --git a/runtime/.clang-format b/runtime/.clang-format index 9b3aa8b7..d9b45c65 100644 --- a/runtime/.clang-format +++ b/runtime/.clang-format @@ -1 +1,3 @@ BasedOnStyle: LLVM +IndentWidth: 4 +ReflowComments: false diff --git a/runtime/src/builtin_collection.c b/runtime/src/builtin_collection.c index 601e8168..c44a743b 100644 --- a/runtime/src/builtin_collection.c +++ b/runtime/src/builtin_collection.c @@ -2,40 +2,55 @@ #include "builtin_internal.h" -static void pack_typed_array(const char *kind, const kvlangXvalue_t *elems, int n, kvlangXvalue_t *out) { +static void pack_typed_array(const char *kind, const kvlangXvalue_t *elems, + int n, kvlangXvalue_t *out) { int sz = kvlangXvalueElemSize(kind); uint8_t *raw = malloc((size_t)sz * (n > 0 ? n : 1)); for (int i = 0; i < n; i++) { - const uint8_t *b; int32_t blen; - kvspaceHead_t h; kvspaceDecodeHead(elems[i].data, elems[i].len, &h); - b = elems[i].data + h.body_offset; blen = h.body_len; + const uint8_t *b; + int32_t blen; + kvspaceHead_t h; + kvspaceDecodeHead(elems[i].data, elems[i].len, &h); + b = elems[i].data + h.body_offset; + blen = h.body_len; int c = blen < sz ? blen : sz; memcpy(raw + i * sz, b, (size_t)c); - for (int j = c; j < sz; j++) raw[i * sz + j] = 0; + for (int j = c; j < sz; j++) + raw[i * sz + j] = 0; } kvlangXvalueNewTlv(out, kind, raw, (uint32_t)(sz * n), n); free(raw); } static int separated_len(kvlangKv_t *kv, const char *base) { - for (int i = 0; ; i++) { - kvlangStrbuf_t k; kvlangStrbufInit(&k); - kvlangStrbufPuts(&k, base); kvlangStrbufPuts(&k, MEMBER_SEP); + for (int i = 0;; i++) { + kvlangStrbuf_t k; + kvlangStrbufInit(&k); + kvlangStrbufPuts(&k, base); + kvlangStrbufPuts(&k, MEMBER_SEP); kvlangStrbufPrintf(&k, "[%d]", i); - kvlangXvalue_t v; kvlangXvalueZero(&v); + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangKvGetOne(kv, k.p, &v); bool none = kvlangXvalueNone(&v); - kvlangXvalueFree(&v); kvlangStrbufFree(&k); - if (none) return i; + kvlangXvalueFree(&v); + kvlangStrbufFree(&k); + if (none) + return i; } } /* 坐标段 key:base·[s0,s1,...]。1 维即 base·[s0]。 */ -char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, int ncoord) { - kvlangStrbuf_t b; kvlangStrbufInit(&b); - kvlangStrbufPuts(&b, base); kvlangStrbufPuts(&b, MEMBER_SEP); kvlangStrbufPutc(&b, '['); +char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, + int ncoord) { + kvlangStrbuf_t b; + kvlangStrbufInit(&b); + kvlangStrbufPuts(&b, base); + kvlangStrbufPuts(&b, MEMBER_SEP); + kvlangStrbufPutc(&b, '['); for (int i = 0; i < ncoord; i++) { - if (i) kvlangStrbufPutc(&b, ','); + if (i) + kvlangStrbufPutc(&b, ','); kvlangStrbufPrintf(&b, "%lld", (long long)coords[i]); } kvlangStrbufPutc(&b, ']'); @@ -43,53 +58,108 @@ char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, int ncoor } /* kvlangBuiltinMemindex(p·):kind=index,body=[4B count LE][name\n...],成员列表唯一权威。 */ -void kvlangBuiltinMemindex(kvlangXvalue_t *out, const char *const *names, int n) { - kvlangStrbuf_t body; kvlangStrbufInit(&body); - char count[4] = { (char)(n & 0xFF), (char)((n >> 8) & 0xFF), (char)((n >> 16) & 0xFF), (char)((n >> 24) & 0xFF) }; +void kvlangBuiltinMemindex(kvlangXvalue_t *out, const char *const *names, + int n) { + kvlangStrbuf_t body; + kvlangStrbufInit(&body); + char count[4] = {(char)(n & 0xFF), (char)((n >> 8) & 0xFF), + (char)((n >> 16) & 0xFF), (char)((n >> 24) & 0xFF)}; kvlangStrbufPutn(&body, count, 4); for (int i = 0; i < n; i++) { - if (i) kvlangStrbufPutc(&body, '\n'); + if (i) + kvlangStrbufPutc(&body, '\n'); kvlangStrbufPuts(&body, names[i]); } - kvlangXvalueNewTlv(out, KVSPACE_KIND_INDEX, (const uint8_t *)body.p, (uint32_t)body.len, 1); + kvlangXvalueNewTlv(out, KVSPACE_KIND_INDEX, (const uint8_t *)body.p, + (uint32_t)body.len, 1); kvlangStrbufFree(&body); } -/* stringkeymap 容器值(p):body 空,dims 落 head。 */ -void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const int32_t *dims, int ndim) { - kvlangXvalueNewTlvDims(out, KVSPACE_KIND_MAP, (const uint8_t *)"", 0, dims, ndim); +/* map 容器值(p):body 空、storetype=index,langtype 为 map langtype(见 [[map容器]])。 + * langtype 恒非空——layout 强制容器字面量写目标带 map langtype(缺则 layout 报错)。 */ +void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const char *langtype, + const int32_t *dims, int ndim) { + kvlangXvalueNewTlvDims(out, langtype, (const uint8_t *)"", 0, dims, ndim); +} + +/* 写槽 `w` 的声明容器类型:layout 的 write_slot_value 把 map langtype 落进写槽的 langtype + * (见 code.rs)。缺类型即 layout 漏检——直接 fatal,不退化兜底。调用方负责 free。 */ +static char *declared_map_langtype(kvlangFrame_t *f, int w) { + if (w >= f->inst->nw) { + fprintf(stderr, + "panic: container literal write slot %d missing (nw=%d)\n", w, + f->inst->nw); + abort(); + } + char buf[256]; + kvlangXvalueLangtype(&f->inst->writes[w].val, buf, sizeof buf); + if (!strstr(buf, MEMBER_SEP)) { + fprintf(stderr, + "panic: container literal target %s has no map langtype " + "(layout must reject)\n", + f->inst->writes[w].name); + abort(); + } + return strdup(buf); } static void ensure_scattered(kvlangFrame_t *f, const char *base) { - kvlangXvalue_t arr; kvlangXvalueZero(&arr); + kvlangXvalue_t arr; + kvlangXvalueZero(&arr); kvlangKvGetOne(f->kv, base, &arr); - if (kvlangXvalueNone(&arr) || kvlangXvalueElemSize(kvlangXvalueKind(&arr)) <= 0) { kvlangXvalueFree(&arr); return; } + if (kvlangXvalueNone(&arr) || + kvlangXvalueElemSize(kvlangXvalueKind(&arr)) <= 0) { + kvlangXvalueFree(&arr); + return; + } int n = kvlangXvalueArrayLen(&arr); for (int i = 0; i < n; i++) { - int64_t c[1] = { i }; + int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(base, c, 1); - kvlangXvalue_t e; kvlangBuiltinXvalueAt(&arr, i, &e); - kvlangKvPair_t p = { k, e }; - char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - kvlangXvalueFree(&e); free(k); + kvlangXvalue_t e; + kvlangBuiltinXvalueAt(&arr, i, &e); + kvlangKvPair_t p = {k, e}; + char err[256]; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + kvlangXvalueFree(&e); + free(k); } kvlangXvalueFree(&arr); - char err[256]; kvlangKvDel(f->kv, base, err, sizeof err); + char err[256]; + kvlangKvDel(f->kv, base, err, sizeof err); } int kvlangBuiltinArray(kvlangFrame_t *f) { - kvlangXvalue_t in[64]; int n = kvlangBuiltinReadInputs(f, in, 64); - if (f->inst->nw == 0 || n == 0) { kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; } + kvlangXvalue_t in[64]; + int n = kvlangBuiltinReadInputs(f, in, 64); + if (f->inst->nw == 0 || n == 0) { + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; + } const char *kind = kvlangXvalueKind(&in[0]); - if (kvlangXvalueElemSize(kind) <= 0) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "array: unsupported element kind %s", kind); } - for (int i = 1; i < n; i++) if (strcmp(kvlangXvalueKind(&in[i]), kind) != 0) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "array: mixed kinds %s and %s", kind, kvlangXvalueKind(&in[i])); } - kvlangXvalue_t arr; pack_typed_array(kind, in, n, &arr); + if (kvlangXvalueElemSize(kind) <= 0) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "array: unsupported element kind %s", + kind); + } + for (int i = 1; i < n; i++) + if (strcmp(kvlangXvalueKind(&in[i]), kind) != 0) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "array: mixed kinds %s and %s", kind, + kvlangXvalueKind(&in[i])); + } + kvlangXvalue_t arr; + pack_typed_array(kind, in, n, &arr); char *fr = kvlangKeytreeFrameRoot(f->pc); - char *key = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + char *key = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); free(fr); - kvlangKvPair_t p = { key, arr }; - char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - free(key); kvlangXvalueFree(&arr); + kvlangKvPair_t p = {key, arr}; + char err[256]; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + free(key); + kvlangXvalueFree(&arr); kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; @@ -98,77 +168,149 @@ int kvlangBuiltinArray(kvlangFrame_t *f) { /* array·fill(langtype[, value]) -> a:按 langtype 的定长维度一次成型 compact 数组, * 每元素填 value(缺省全零)。用于大数组初始化,无需逐元素赋值。 */ int kvlangBuiltinArrayFill(kvlangFrame_t *f) { - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: array.fill requires a write param (-> a)"); - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); - if (n < 1) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: array.fill requires a langtype"); } + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: array.fill requires a write param (-> a)"); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); + if (n < 1) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, + "TypeError: array.fill requires a langtype"); + } char *ke = kvlangXvalueValueString(&in[0]); - kvlangLangtype kx; kvlangLangtypeParse((const uint8_t *)ke, &kx); + kvlangLangtype kx; + kvlangLangtypeParse((const uint8_t *)ke, &kx); int sz = kvlangXvalueElemSize(kx.kind); if (sz <= 0 || sz > 8 || kx.ndim < 1 || kx.array_len < 0) { - int rc = kvlangBuiltinSetErr(f, "TypeError: array.fill: not a fixed-length array type %s", ke); - free(ke); kvlangBuiltinFreeInputs(in, n); return rc; + int rc = kvlangBuiltinSetErr( + f, "TypeError: array.fill: not a fixed-length array type %s", ke); + free(ke); + kvlangBuiltinFreeInputs(in, n); + return rc; } - uint8_t elem[8] = { 0 }; + uint8_t elem[8] = {0}; if (n >= 2 && !kvlangXvalueNone(&in[1])) { kvlangScalar_t s = kvlangXvalueScalar(&in[1]); int tid = kvlangLangTypeId(kx.kind, kx.kind_len); - if (tid == KVLANG_LT_FLOAT32) { float v = (float)kvlangScalarF64(s); memcpy(elem, &v, 4); } - else if (tid == KVLANG_LT_FLOAT64) { double v = kvlangScalarF64(s); memcpy(elem, &v, 8); } - else { int64_t v = kvlangScalarI64(s); memcpy(elem, &v, (size_t)sz); } + if (tid == KVLANG_LT_FLOAT32) { + float v = (float)kvlangScalarF64(s); + memcpy(elem, &v, 4); + } else if (tid == KVLANG_LT_FLOAT64) { + double v = kvlangScalarF64(s); + memcpy(elem, &v, 8); + } else { + int64_t v = kvlangScalarI64(s); + memcpy(elem, &v, (size_t)sz); + } } size_t total = (size_t)sz * (size_t)kx.array_len; uint8_t *raw = malloc(total > 0 ? total : 1); - for (int i = 0; i < kx.array_len; i++) memcpy(raw + (size_t)i * sz, elem, (size_t)sz); - kvlangXvalue_t arr; kvlangXvalueNewTlvDims(&arr, kx.kind, raw, (uint32_t)total, kx.dims, kx.ndim); + for (int i = 0; i < kx.array_len; i++) + memcpy(raw + (size_t)i * sz, elem, (size_t)sz); + kvlangXvalue_t arr; + kvlangXvalueNewTlvDims(&arr, kx.kind, raw, (uint32_t)total, kx.dims, + kx.ndim); free(raw); int rc = kvlangBuiltinWriteResult(f, &arr); - kvlangXvalueFree(&arr); free(ke); kvlangBuiltinFreeInputs(in, n); + kvlangXvalueFree(&arr); + free(ke); + kvlangBuiltinFreeInputs(in, n); return rc; } -static int xv_head1(kvlangFrame_t *f, kvspaceHead_t *h); /* GetHead-only 单读参 head,定义见下 */ +static int xv_head1(kvlangFrame_t *f, + kvspaceHead_t *h); /* GetHead-only 单读参 head,定义见下 */ + +/* ndarray 形状算子只认带形状段的 langtype。map 容器(langtype=`{keylt}·{valt}`)无形状段—— + * 成员数在 memindex(`p·`)而非容器值,用 ndarray·numel 取是范畴错误,直接报错逼用 kv·listlen。 */ +static int ndarray_shape_guard(kvlangFrame_t *f, const char *op, + const kvspaceHead_t *h) { + kvlangLangtype kx; + kvlangLangtypeParse(h->langtype, &kx); + if (kx.ndim > 0) + return 0; + if (kvlangKindIsMap(kx.kind)) + return kvlangBuiltinSetErr(f, + "TypeError: %s: container %s has no shape; " + "use kv·listlen for member count", + op, kx.kind); + return 0; +} int kvlangBuiltinNdarrayNumel(kvlangFrame_t *f) { - kvspaceHead_t h; int64_t n_el = 0; - if (xv_head1(f, &h) == 0) { kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); n_el = kx.array_len; } - kvlangXvalue_t r; kvlangXvalueNewInt64(&r, n_el); - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); + kvspaceHead_t h; + int64_t n_el = 0; + if (xv_head1(f, &h) == 0) { + int g = ndarray_shape_guard(f, "ndarray·numel", &h); + if (g) + return g; + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); + n_el = kx.array_len; + } + kvlangXvalue_t r; + kvlangXvalueNewInt64(&r, n_el); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); return rc; } int kvlangBuiltinNdarrayDim(kvlangFrame_t *f) { - kvspaceHead_t h; int64_t ndim = 0; - if (xv_head1(f, &h) == 0) { kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); ndim = kx.ndim; } - kvlangXvalue_t r; kvlangXvalueNewInt64(&r, ndim); - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); + kvspaceHead_t h; + int64_t ndim = 0; + if (xv_head1(f, &h) == 0) { + int g = ndarray_shape_guard(f, "ndarray·dim", &h); + if (g) + return g; + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); + ndim = kx.ndim; + } + kvlangXvalue_t r; + kvlangXvalueNewInt64(&r, ndim); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); return rc; } int kvlangBuiltinNdarrayShape(kvlangFrame_t *f) { - int32_t dims[8]; int32_t ndim = 0; + int32_t dims[8]; + int32_t ndim = 0; kvspaceHead_t h; if (xv_head1(f, &h) == 0) { - kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); + int g = ndarray_shape_guard(f, "ndarray·shape", &h); + if (g) + return g; + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); ndim = kx.ndim; - for (int i = 0; i < ndim && i < 8; i++) dims[i] = kx.dims[i]; + for (int i = 0; i < ndim && i < 8; i++) + dims[i] = kx.dims[i]; } - uint8_t raw[64]; uint32_t raw_len = 0; + uint8_t raw[64]; + uint32_t raw_len = 0; for (int i = 0; i < ndim; i++) { int64_t d = dims[i]; - for (int j = 0; j < 8; j++) raw[i * 8 + j] = (d >> (j * 8)) & 0xFF; + for (int j = 0; j < 8; j++) + raw[i * 8 + j] = (d >> (j * 8)) & 0xFF; raw_len += 8; } - int32_t sd[1] = { ndim }; - kvlangXvalue_t r; kvlangXvalueNewTlvDims(&r, KVSPACE_KIND_INT64, raw, raw_len, sd, 1); - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); + int32_t sd[1] = {ndim}; + kvlangXvalue_t r; + kvlangXvalueNewTlvDims(&r, KVSPACE_KIND_INT64, raw, raw_len, sd, 1); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); return rc; } /* 计算多维下标的 row-major 扁平索引,越界返回 -1。 */ -static int64_t flat_index(const kvlangLangtype *kx, const int64_t *idx, int nidx) { +static int64_t flat_index(const kvlangLangtype *kx, const int64_t *idx, + int nidx) { int64_t flat = 0; for (int i = 0; i < nidx; i++) { - if (idx[i] < 0 || idx[i] >= kx->dims[i]) return -1; + if (idx[i] < 0 || idx[i] >= kx->dims[i]) + return -1; flat = flat * kx->dims[i] + idx[i]; } return flat; @@ -176,24 +318,35 @@ static int64_t flat_index(const kvlangLangtype *kx, const int64_t *idx, int nidx /* base kind(kx.kind 为非 NUL 终止子串)拷成 NUL 终止串,取元素字节大小;空 kind → 0。 */ static int xv_elem_size(const kvlangLangtype *kx) { - if (!kx->kind || kx->kind_len <= 0) return 0; - char kb[64]; int kl = kx->kind_len < 63 ? kx->kind_len : 63; - memcpy(kb, kx->kind, (size_t)kl); kb[kl] = 0; + if (!kx->kind || kx->kind_len <= 0) + return 0; + char kb[64]; + int kl = kx->kind_len < 63 ? kx->kind_len : 63; + memcpy(kb, kx->kind, (size_t)kl); + kb[kl] = 0; return kvlangXvalueElemSize(kb); } /* 读参 ri 的 head:变量走 GetHead 只读前缀(不借 body,*key=malloc'd 键),字面量数组借整块 * 解 head(*key=NULL,*borrow 持整块,调用方 kvlangXvalueFree)。返回 0 成功、非 0 空/不存在。 */ -static int xv_read_head(kvlangFrame_t *f, const char *fr, int ri, kvspaceHead_t *h, - char **key, kvlangXvalue_t *borrow) { - kvlangXvalueZero(borrow); *key = NULL; - char *k = kvlangBuiltinResolveReadKey(f->kv, fr, f->inst->reads[ri].name, &f->inst->reads[ri].val); +static int xv_read_head(kvlangFrame_t *f, const char *fr, int ri, + kvspaceHead_t *h, char **key, kvlangXvalue_t *borrow) { + kvlangXvalueZero(borrow); + *key = NULL; + char *k = kvlangBuiltinResolveReadKey(f->kv, fr, f->inst->reads[ri].name, + &f->inst->reads[ri].val); if (k) { - if (kvlangKvGetHead(f->kv, k, h) != 0) { free(k); return -1; } - *key = k; return 0; + if (kvlangKvGetHead(f->kv, k, h) != 0) { + free(k); + return -1; + } + *key = k; + return 0; } - kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[ri].name, &f->inst->reads[ri].val, borrow); - if (kvlangXvalueNone(borrow)) return -1; + kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[ri].name, + &f->inst->reads[ri].val, borrow); + if (kvlangXvalueNone(borrow)) + return -1; return kvspaceDecodeHead(borrow->data, borrow->len, h) == 0 ? 0 : -1; } @@ -201,17 +354,22 @@ static int xv_read_head(kvlangFrame_t *f, const char *fr, int ri, kvspaceHead_t * 返回 0 并填 *h;空/不存在返回 -1(调用方给默认值)。 */ static int xv_head1(kvlangFrame_t *f, kvspaceHead_t *h) { char *fr = kvlangKeytreeFrameRoot(f->pc); - char *key; kvlangXvalue_t borrow; + char *key; + kvlangXvalue_t borrow; int rc = xv_read_head(f, fr, 0, h, &key, &borrow); - free(key); kvlangXvalueFree(&borrow); free(fr); + free(key); + kvlangXvalueFree(&borrow); + free(fr); return rc; } /* 读 first..first+nidx-1 的标量下标到 idx[]。 */ -static void xv_read_indices(kvlangFrame_t *f, const char *fr, int first, int nidx, int64_t *idx) { +static void xv_read_indices(kvlangFrame_t *f, const char *fr, int first, + int nidx, int64_t *idx) { for (int i = 0; i < nidx && i < X_MAX_NDIM; i++) { kvlangXvalue_t iv; - kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[first + i].name, &f->inst->reads[first + i].val, &iv); + kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[first + i].name, + &f->inst->reads[first + i].val, &iv); idx[i] = kvlangScalarI64(kvlangXvalueScalar(&iv)); kvlangXvalueFree(&iv); } @@ -220,30 +378,64 @@ static void xv_read_indices(kvlangFrame_t *f, const char *fr, int first, int nid /* 分片读单元素:GetHead 定位 + GetPart 只借该元素的 [off, off+sz) 字节,不借整块。 */ int kvlangBuiltinXvAt(kvlangFrame_t *f) { int nidx = f->inst->nr - 1; - if (nidx < 1) return kvlangBuiltinSetErr(f, "TypeError: xv.at requires array and indices"); + if (nidx < 1) + return kvlangBuiltinSetErr( + f, "TypeError: xv.at requires array and indices"); char *fr = kvlangKeytreeFrameRoot(f->pc); - kvspaceHead_t h; char *key; kvlangXvalue_t arr; - if (xv_read_head(f, fr, 0, &h, &key, &arr) != 0) { free(fr); return kvlangBuiltinSetErr(f, "TypeError: xv.at requires a compact array"); } - kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); + kvspaceHead_t h; + char *key; + kvlangXvalue_t arr; + if (xv_read_head(f, fr, 0, &h, &key, &arr) != 0) { + free(fr); + return kvlangBuiltinSetErr(f, + "TypeError: xv.at requires a compact array"); + } + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); int sz = xv_elem_size(&kx); - if (sz <= 0 || kx.ndim == 0) { free(key); kvlangXvalueFree(&arr); free(fr); return kvlangBuiltinSetErr(f, "TypeError: xv.at requires a compact array, got %s", h.langtype); } - if (nidx != kx.ndim) { free(key); kvlangXvalueFree(&arr); free(fr); return kvlangBuiltinSetErr(f, "IndexError: xv.at: %d-dim array needs %d indices, got %d", kx.ndim, kx.ndim, nidx); } - int64_t idx[X_MAX_NDIM]; xv_read_indices(f, fr, 1, nidx, idx); + if (sz <= 0 || kx.ndim == 0) { + free(key); + kvlangXvalueFree(&arr); + free(fr); + return kvlangBuiltinSetErr( + f, "TypeError: xv.at requires a compact array, got %s", h.langtype); + } + if (nidx != kx.ndim) { + free(key); + kvlangXvalueFree(&arr); + free(fr); + return kvlangBuiltinSetErr( + f, "IndexError: xv.at: %d-dim array needs %d indices, got %d", + kx.ndim, kx.ndim, nidx); + } + int64_t idx[X_MAX_NDIM]; + xv_read_indices(f, fr, 1, nidx, idx); free(fr); int64_t flat = flat_index(&kx, idx, nidx); - if (flat < 0) { free(key); kvlangXvalueFree(&arr); return kvlangBuiltinSetErr(f, "IndexError: xv.at: index out of bounds"); } - char kb[64]; int kl = kx.kind_len < 63 ? kx.kind_len : 63; memcpy(kb, kx.kind, (size_t)kl); kb[kl] = 0; + if (flat < 0) { + free(key); + kvlangXvalueFree(&arr); + return kvlangBuiltinSetErr(f, "IndexError: xv.at: index out of bounds"); + } + char kb[64]; + int kl = kx.kind_len < 63 ? kx.kind_len : 63; + memcpy(kb, kx.kind, (size_t)kl); + kb[kl] = 0; kvlangXvalue_t e; if (key) { - kvlangXvalue_t part; kvlangKvGetPart(f->kv, key, (uint32_t)(h.body_offset + flat * sz), (uint32_t)sz, &part); + kvlangXvalue_t part; + kvlangKvGetPart(f->kv, key, (uint32_t)(h.body_offset + flat * sz), + (uint32_t)sz, &part); kvlangXvalueNewTlv(&e, kb, part.data, part.len, 1); - kvlangXvalueFree(&part); free(key); + kvlangXvalueFree(&part); + free(key); } else { const uint8_t *body = arr.data + h.body_offset; kvlangXvalueNewTlv(&e, kb, body + flat * sz, (uint32_t)sz, 1); } kvlangXvalueFree(&arr); - int rc = kvlangBuiltinWriteResult(f, &e); kvlangXvalueFree(&e); + int rc = kvlangBuiltinWriteResult(f, &e); + kvlangXvalueFree(&e); return rc; } @@ -251,77 +443,148 @@ int kvlangBuiltinXvAt(kvlangFrame_t *f) { * 不重建整块);源≠目标 / 字面量 / 缺失 → 回退借整块、拷贝、改元素、整体重建。 */ int kvlangBuiltinXvSet(kvlangFrame_t *f) { int nidx = f->inst->nr - 2; - if (nidx < 1) return kvlangBuiltinSetErr(f, "TypeError: xv.set requires array, indices, value"); - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: xv.set requires a write param (-> a)"); + if (nidx < 1) + return kvlangBuiltinSetErr( + f, "TypeError: xv.set requires array, indices, value"); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: xv.set requires a write param (-> a)"); char *fr = kvlangKeytreeFrameRoot(f->pc); - char *rk = kvlangBuiltinResolveReadKey(f->kv, fr, f->inst->reads[0].name, &f->inst->reads[0].val); - char *wk = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); - int64_t idx[X_MAX_NDIM]; xv_read_indices(f, fr, 1, nidx, idx); - kvlangXvalue_t vv; kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[nidx + 1].name, &f->inst->reads[nidx + 1].val, &vv); - kvspaceHead_t vh; kvspaceDecodeHead(vv.data, vv.len, &vh); + char *rk = kvlangBuiltinResolveReadKey(f->kv, fr, f->inst->reads[0].name, + &f->inst->reads[0].val); + char *wk = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + int64_t idx[X_MAX_NDIM]; + xv_read_indices(f, fr, 1, nidx, idx); + kvlangXvalue_t vv; + kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[nidx + 1].name, + &f->inst->reads[nidx + 1].val, &vv); + kvspaceHead_t vh; + kvspaceDecodeHead(vv.data, vv.len, &vh); const uint8_t *vb = vv.data + vh.body_offset; kvspaceHead_t h; - if (rk && wk && strcmp(rk, wk) == 0 && kvlangKvGetHead(f->kv, wk, &h) == 0) { - kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); + if (rk && wk && strcmp(rk, wk) == 0 && + kvlangKvGetHead(f->kv, wk, &h) == 0) { + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); int sz = xv_elem_size(&kx); - int64_t flat = (sz > 0 && kx.ndim && nidx == kx.ndim) ? flat_index(&kx, idx, nidx) : -1; - const char *emsg = sz <= 0 || kx.ndim == 0 ? "TypeError: xv.set requires a compact array" - : nidx != kx.ndim ? "IndexError: xv.set: dim/index count mismatch" - : flat < 0 ? "IndexError: xv.set: index out of bounds" : NULL; + int64_t flat = (sz > 0 && kx.ndim && nidx == kx.ndim) + ? flat_index(&kx, idx, nidx) + : -1; + const char *emsg = + sz <= 0 || kx.ndim == 0 + ? "TypeError: xv.set requires a compact array" + : nidx != kx.ndim ? "IndexError: xv.set: dim/index count mismatch" + : flat < 0 ? "IndexError: xv.set: index out of bounds" + : NULL; int rc = 0; - if (emsg) rc = kvlangBuiltinSetErr(f, "%s", emsg); + if (emsg) + rc = kvlangBuiltinSetErr(f, "%s", emsg); else { int c = vh.body_len < sz ? vh.body_len : sz; - char err[256]; kvlangKvSetPart(f->kv, wk, (uint32_t)(h.body_offset + flat * sz), vb, (uint32_t)c, err, sizeof err); + char err[256]; + kvlangKvSetPart(f->kv, wk, (uint32_t)(h.body_offset + flat * sz), + vb, (uint32_t)c, err, sizeof err); kvlangBuiltinNextPc(f); } - free(rk); free(wk); free(fr); kvlangXvalueFree(&vv); + free(rk); + free(wk); + free(fr); + kvlangXvalueFree(&vv); return rc; } - free(rk); free(wk); + free(rk); + free(wk); - kvlangXvalue_t arr; kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[0].name, &f->inst->reads[0].val, &arr); + kvlangXvalue_t arr; + kvlangBuiltinResolveReadValue(f->kv, fr, f->inst->reads[0].name, + &f->inst->reads[0].val, &arr); free(fr); const char *k = kvlangXvalueKind(&arr); int sz = kvlangXvalueElemSize(k); - kvspaceHead_t ah; kvspaceDecodeHead(arr.data, arr.len, &ah); - kvlangLangtype kx; kvlangLangtypeParse(ah.langtype, &kx); - const char *emsg = sz <= 0 || kx.ndim == 0 ? "TypeError: xv.set requires a compact array" - : nidx != kx.ndim ? "IndexError: xv.set: dim/index count mismatch" : NULL; + kvspaceHead_t ah; + kvspaceDecodeHead(arr.data, arr.len, &ah); + kvlangLangtype kx; + kvlangLangtypeParse(ah.langtype, &kx); + const char *emsg = + sz <= 0 || kx.ndim == 0 ? "TypeError: xv.set requires a compact array" + : nidx != kx.ndim ? "IndexError: xv.set: dim/index count mismatch" + : NULL; int64_t flat = emsg ? -1 : flat_index(&kx, idx, nidx); - if (!emsg && flat < 0) emsg = "IndexError: xv.set: index out of bounds"; - if (emsg) { kvlangXvalueFree(&arr); kvlangXvalueFree(&vv); return kvlangBuiltinSetErr(f, "%s", emsg); } + if (!emsg && flat < 0) + emsg = "IndexError: xv.set: index out of bounds"; + if (emsg) { + kvlangXvalueFree(&arr); + kvlangXvalueFree(&vv); + return kvlangBuiltinSetErr(f, "%s", emsg); + } uint8_t *nb = malloc((size_t)ah.body_len); memcpy(nb, arr.data + ah.body_offset, (size_t)ah.body_len); int c = vh.body_len < sz ? vh.body_len : sz; memcpy(nb + flat * sz, vb, (size_t)c); - kvlangXvalue_t nv; kvlangXvalueNewTlvDims(&nv, k, nb, (uint32_t)ah.body_len, kx.dims, kx.ndim); + kvlangXvalue_t nv; + kvlangXvalueNewTlvDims(&nv, k, nb, (uint32_t)ah.body_len, kx.dims, kx.ndim); int rc = kvlangBuiltinWriteResult(f, &nv); - kvlangXvalueFree(&nv); free(nb); kvlangXvalueFree(&arr); kvlangXvalueFree(&vv); + kvlangXvalueFree(&nv); + free(nb); + kvlangXvalueFree(&arr); + kvlangXvalueFree(&vv); return rc; } int kvlangBuiltinXvReshape(kvlangFrame_t *f) { int ndims = f->inst->nr - 1; - if (ndims < 1) return kvlangBuiltinSetErr(f, "TypeError: xv.reshape requires array and >=1 dims"); - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: xv.reshape requires a write param (-> a)"); - kvlangXvalue_t in[MAX_PARAMS]; int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); + if (ndims < 1) + return kvlangBuiltinSetErr( + f, "TypeError: xv.reshape requires array and >=1 dims"); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: xv.reshape requires a write param (-> a)"); + kvlangXvalue_t in[MAX_PARAMS]; + int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); const char *k = kvlangXvalueKind(&in[0]); - if (kvlangXvalueElemSize(k) <= 0) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: xv.reshape requires a compact array, got %s", k); } - kvspaceHead_t h; kvspaceDecodeHead(in[0].data, in[0].len, &h); - kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); - if (kx.ndim < 1) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: xv.reshape requires a compact array, got scalar %s", k); } - if (ndims > X_MAX_NDIM) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "IndexError: xv.reshape: at most %d dims, got %d", X_MAX_NDIM, ndims); } - int32_t dims[X_MAX_NDIM]; int64_t numel = 1; + if (kvlangXvalueElemSize(k) <= 0) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "TypeError: xv.reshape requires a compact array, got %s", k); + } + kvspaceHead_t h; + kvspaceDecodeHead(in[0].data, in[0].len, &h); + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); + if (kx.ndim < 1) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "TypeError: xv.reshape requires a compact array, got scalar %s", + k); + } + if (ndims > X_MAX_NDIM) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "IndexError: xv.reshape: at most %d dims, got %d", X_MAX_NDIM, + ndims); + } + int32_t dims[X_MAX_NDIM]; + int64_t numel = 1; for (int i = 0; i < ndims; i++) { dims[i] = (int32_t)kvlangScalarI64(kvlangXvalueScalar(&in[i + 1])); - if (dims[i] < 0) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "IndexError: xv.reshape: negative dim %d", dims[i]); } + if (dims[i] < 0) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "IndexError: xv.reshape: negative dim %d", dims[i]); + } numel *= dims[i]; } - if (numel != kx.array_len) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "IndexError: xv.reshape: cannot reshape %d elements into %lld", kx.array_len, (long long)numel); } + if (numel != kx.array_len) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "IndexError: xv.reshape: cannot reshape %d elements into %lld", + kx.array_len, (long long)numel); + } const uint8_t *body = in[0].data + h.body_offset; - kvlangXvalue_t nv; kvlangXvalueNewTlvDims(&nv, k, body, (uint32_t)h.body_len, dims, ndims); + kvlangXvalue_t nv; + kvlangXvalueNewTlvDims(&nv, k, body, (uint32_t)h.body_len, dims, ndims); int rc = kvlangBuiltinWriteResult(f, &nv); kvlangXvalueFree(&nv); kvlangBuiltinFreeInputs(in, n); @@ -330,12 +593,19 @@ int kvlangBuiltinXvReshape(kvlangFrame_t *f) { /* xv·reinterpret(arr, langtype) -> a:body 字节原样,整个 langtype 换成传入的(kind+dims 一起),不做校验。 */ int kvlangBuiltinXvReinterpret(kvlangFrame_t *f) { - if (f->inst->nr < 2) return kvlangBuiltinSetErr(f, "TypeError: xv.reinterpret requires array and langtype"); - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: xv.reinterpret requires a write param (-> a)"); - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + if (f->inst->nr < 2) + return kvlangBuiltinSetErr( + f, "TypeError: xv.reinterpret requires array and langtype"); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: xv.reinterpret requires a write param (-> a)"); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); char *ke = kvlangXvalueValueString(&in[1]); - kvlangLangtype nkx; kvlangLangtypeParse((const uint8_t *)ke, &nkx); - kvspaceHead_t h; kvspaceDecodeHead(in[0].data, in[0].len, &h); + kvlangLangtype nkx; + kvlangLangtypeParse((const uint8_t *)ke, &nkx); + kvspaceHead_t h; + kvspaceDecodeHead(in[0].data, in[0].len, &h); const uint8_t *body = in[0].data + h.body_offset; /* 动态 "[]kind"(parse 得 ndim0 但带方括号):按 body 字节数补出一维长度,与落盘数组表示一致。 */ int32_t ndim = nkx.ndim; @@ -344,172 +614,290 @@ int kvlangBuiltinXvReinterpret(kvlangFrame_t *f) { nkx.dims[0] = es > 0 ? (int32_t)(h.body_len / es) : (int32_t)h.body_len; ndim = 1; } - kvlangXvalue_t nv; kvlangXvalueNewTlvDims(&nv, nkx.kind, body, (uint32_t)h.body_len, nkx.dims, ndim); + kvlangXvalue_t nv; + kvlangXvalueNewTlvDims(&nv, nkx.kind, body, (uint32_t)h.body_len, nkx.dims, + ndim); int rc = kvlangBuiltinWriteResult(f, &nv); - kvlangXvalueFree(&nv); free(ke); kvlangBuiltinFreeInputs(in, n); + kvlangXvalueFree(&nv); + free(ke); + kvlangBuiltinFreeInputs(in, n); return rc; } /* xv·langtype(v) -> s:返回 v 的 head langtype 串(含 ref 前缀与 [dims]),作为字符串。 */ int kvlangBuiltinXvLangtype(kvlangFrame_t *f) { - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: xv.langtype requires a write param (-> s)"); - kvspaceHead_t h; const char *ke = ""; - if (xv_head1(f, &h) == 0) ke = (const char *)h.langtype; - kvlangXvalue_t r; kvlangXvalueNewCharUtf8(&r, ke); - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: xv.langtype requires a write param (-> s)"); + kvspaceHead_t h; + const char *ke = ""; + if (xv_head1(f, &h) == 0) + ke = (const char *)h.langtype; + kvlangXvalue_t r; + kvlangXvalueNewCharUtf8(&r, ke); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); return rc; } /* xv·bodylen(v) -> n:返回 v 的 body 字节数(int64)。 */ int kvlangBuiltinXvBodylen(kvlangFrame_t *f) { - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: xv.bodylen requires a write param (-> n)"); - kvspaceHead_t h; int64_t bl = 0; - if (xv_head1(f, &h) == 0) bl = h.body_len; - kvlangXvalue_t r; kvlangXvalueNewInt64(&r, bl); - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: xv.bodylen requires a write param (-> n)"); + kvspaceHead_t h; + int64_t bl = 0; + if (xv_head1(f, &h) == 0) + bl = h.body_len; + kvlangXvalue_t r; + kvlangXvalueNewInt64(&r, bl); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); return rc; } int kvlangBuiltinScatter(kvlangFrame_t *f) { - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: array.scatter requires a write param"); - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); - if (n == 0 || kvlangXvalueNone(&in[0])) { kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; } - if (kvlangXvalueElemSize(kvlangXvalueKind(&in[0])) <= 0) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: array.scatter requires a compact array ([]T), got %s", kvlangXvalueKind(&in[0])); } + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: array.scatter requires a write param"); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); + if (n == 0 || kvlangXvalueNone(&in[0])) { + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; + } + if (kvlangXvalueElemSize(kvlangXvalueKind(&in[0])) <= 0) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, + "TypeError: array.scatter requires a compact array ([]T), got %s", + kvlangXvalueKind(&in[0])); + } char *fr = kvlangKeytreeFrameRoot(f->pc); - char *dst = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + char *dst = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); int al = kvlangXvalueArrayLen(&in[0]); if (al > 0) { char err[256]; - int32_t dims[1] = { al }; - kvlangXvalue_t mark; kvlangBuiltinMapMarker(&mark, dims, 1); - kvlangKvPair_t p0 = { dst, mark }; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); + int32_t dims[1] = {al}; + /* 散 key 化的容器值类型:键是坐标段 `[i]`,值是原 compact 元素类型(逐元素原样搬)。 */ + char ty[256]; + snprintf(ty, sizeof ty, "[int64]%s%s", MEMBER_SEP, + kvlangXvalueKind(&in[0])); + kvlangXvalue_t mark; + kvlangBuiltinMapMarker(&mark, ty, dims, 1); + kvlangKvPair_t p0 = {dst, mark}; + kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); } for (int i = 0; i < al; i++) { - int64_t c[1] = { i }; + int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(dst, c, 1); - kvlangXvalue_t e; kvlangBuiltinXvalueAt(&in[0], i, &e); - kvlangKvPair_t p = { k, e }; char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - kvlangXvalueFree(&e); free(k); + kvlangXvalue_t e; + kvlangBuiltinXvalueAt(&in[0], i, &e); + kvlangKvPair_t p = {k, e}; + char err[256]; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + kvlangXvalueFree(&e); + free(k); } - free(dst); free(fr); - kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; + free(dst); + free(fr); + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; } int kvlangBuiltinCompact(kvlangFrame_t *f) { - if (f->inst->nr == 0 || f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: array.compact requires read and write params"); + if (f->inst->nr == 0 || f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: array.compact requires read and write params"); char *fr = kvlangKeytreeFrameRoot(f->pc); - char *src = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[0].name); - kvlangXvalue_t elems[1024]; int n = 0; - for (int i = 0; ; i++) { - int64_t c[1] = { i }; + char *src = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[0].name); + kvlangXvalue_t elems[1024]; + int n = 0; + for (int i = 0;; i++) { + int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(src, c, 1); - kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, k, &v); - bool none = kvlangXvalueNone(&v); free(k); - if (none) break; + kvlangXvalue_t v; + kvlangXvalueZero(&v); + kvlangKvGetOne(f->kv, k, &v); + bool none = kvlangXvalueNone(&v); + free(k); + if (none) + break; elems[n++] = v; } - if (n == 0) { free(src); free(fr); kvlangBuiltinNextPc(f); return 0; } - kvlangXvalue_t arr; pack_typed_array(kvlangXvalueKind(&elems[0]), elems, n, &arr); - char *dst = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); - kvlangKvPair_t p = { dst, arr }; char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - for (int i = 0; i < n; i++) kvlangXvalueFree(&elems[i]); + if (n == 0) { + free(src); + free(fr); + kvlangBuiltinNextPc(f); + return 0; + } + kvlangXvalue_t arr; + pack_typed_array(kvlangXvalueKind(&elems[0]), elems, n, &arr); + char *dst = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + kvlangKvPair_t p = {dst, arr}; + char err[256]; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + for (int i = 0; i < n; i++) + kvlangXvalueFree(&elems[i]); kvlangXvalueFree(&arr); - free(dst); free(src); free(fr); - kvlangBuiltinNextPc(f); return 0; + free(dst); + free(src); + free(fr); + kvlangBuiltinNextPc(f); + return 0; } int kvlangBuiltinAppend(kvlangFrame_t *f) { - if (f->inst->nr < 2) return kvlangBuiltinSetErr(f, "TypeError: array.append requires array and element"); - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: array.append requires a write param (-> arr)"); - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + if (f->inst->nr < 2) + return kvlangBuiltinSetErr( + f, "TypeError: array.append requires array and element"); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: array.append requires a write param (-> arr)"); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); char *fr = kvlangKeytreeFrameRoot(f->pc); - char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + char *base = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); ensure_scattered(f, base); int len = separated_len(f->kv, base); - int64_t c[1] = { len }; + int64_t c[1] = {len}; char *k = kvlangBuiltinScatterKey(base, c, 1); - kvlangKvPair_t p = { k, n >= 2 ? in[1] : in[0] }; - char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - free(k); free(base); free(fr); - kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; + kvlangKvPair_t p = {k, n >= 2 ? in[1] : in[0]}; + char err[256]; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + free(k); + free(base); + free(fr); + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; } int kvlangBuiltinSlice(kvlangFrame_t *f) { - if (f->inst->nr < 3) return kvlangBuiltinSetErr(f, "TypeError: array.slice requires array, start, end"); - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: array.slice requires a write param (-> arr)"); - kvlangXvalue_t in[3]; int n = kvlangBuiltinReadInputs(f, in, 3); + if (f->inst->nr < 3) + return kvlangBuiltinSetErr( + f, "TypeError: array.slice requires array, start, end"); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr( + f, "TypeError: array.slice requires a write param (-> arr)"); + kvlangXvalue_t in[3]; + int n = kvlangBuiltinReadInputs(f, in, 3); char *fr = kvlangKeytreeFrameRoot(f->pc); - char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + char *base = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); ensure_scattered(f, base); int al = separated_len(f->kv, base); - int lo = (int)kvlangScalarI64(kvlangXvalueScalar(&in[1])), hi = (int)kvlangScalarI64(kvlangXvalueScalar(&in[2])); - if (lo < 0 || hi < lo || hi > al) { free(base); free(fr); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "IndexError: array.slice: bounds [%d:%d] out of range (len=%d)", lo, hi, al); } + int lo = (int)kvlangScalarI64(kvlangXvalueScalar(&in[1])), + hi = (int)kvlangScalarI64(kvlangXvalueScalar(&in[2])); + if (lo < 0 || hi < lo || hi > al) { + free(base); + free(fr); + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "IndexError: array.slice: bounds [%d:%d] out of range (len=%d)", + lo, hi, al); + } for (int i = lo; i < hi; i++) { - int64_t sc[1] = { i }, dc[1] = { i - lo }; + int64_t sc[1] = {i}, dc[1] = {i - lo}; char *sk = kvlangBuiltinScatterKey(base, sc, 1); - kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, sk, &v); + kvlangXvalue_t v; + kvlangXvalueZero(&v); + kvlangKvGetOne(f->kv, sk, &v); char *dk = kvlangBuiltinScatterKey(base, dc, 1); - kvlangKvPair_t p = { dk, v }; char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - kvlangXvalueFree(&v); free(sk); free(dk); + kvlangKvPair_t p = {dk, v}; + char err[256]; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + kvlangXvalueFree(&v); + free(sk); + free(dk); } for (int i = hi - lo; i < al; i++) { - int64_t dc[1] = { i }; + int64_t dc[1] = {i}; char *dk = kvlangBuiltinScatterKey(base, dc, 1); - char err[256]; kvlangKvDel(f->kv, dk, err, sizeof err); free(dk); + char err[256]; + kvlangKvDel(f->kv, dk, err, sizeof err); + free(dk); } - free(base); free(fr); - kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; + free(base); + free(fr); + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; } int kvlangBuiltinObj(kvlangFrame_t *f) { - kvlangXvalue_t in[64]; int n = kvlangBuiltinReadInputs(f, in, 64); + kvlangXvalue_t in[64]; + int n = kvlangBuiltinReadInputs(f, in, 64); char *fr = kvlangKeytreeFrameRoot(f->pc); for (int w = 0; w < f->inst->nw; w++) { - char *ok = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); + char *ok = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); char err[256]; /* 重建:清旧成员(p·name),容器值随后重写。 */ char *dir = kvlangKeytreeMember(ok, ""); - char **old = NULL; int oc = 0; + char **old = NULL; + int oc = 0; kvlangKvList(f->kv, dir, false, false, &old, &oc); for (int i = 0; i < oc; i++) { char *mk = kvlangKeytreeMember(ok, old[i]); kvlangKvDel(f->kv, mk, err, sizeof err); - free(mk); free(old[i]); + free(mk); + free(old[i]); } - free(old); free(dir); + free(old); + free(dir); /* 收集成员名(跳过 None)。 */ int cnt = 0; - for (int i = 0; i + 1 < n; i += 2) if (!kvlangXvalueNone(&in[i + 1])) cnt++; + for (int i = 0; i + 1 < n; i += 2) + if (!kvlangXvalueNone(&in[i + 1])) + cnt++; char **names = malloc(sizeof(char *) * (size_t)(cnt > 0 ? cnt : 1)); for (int i = 0, j = 0; i + 1 < n; i += 2) { - if (kvlangXvalueNone(&in[i + 1])) continue; + if (kvlangXvalueNone(&in[i + 1])) + continue; names[j++] = kvlangXvalueValueString(&in[i]); } - /* 容器值 p:kind=stringkeymap,dims=[0](命名字典无形状,成员在 memindex)。 */ - int32_t odims[1] = { 0 }; - kvlangXvalue_t mark; kvlangBuiltinMapMarker(&mark, odims, 1); - kvlangKvPair_t p0 = { ok, mark }; + /* 容器值 p:langtype=声明的 map langtype,dims=[0](命名字典无形状,成员在 memindex)。 */ + int32_t odims[1] = {0}; + char *wty = declared_map_langtype(f, w); + kvlangXvalue_t mark; + kvlangBuiltinMapMarker(&mark, wty, odims, 1); + free(wty); + kvlangKvPair_t p0 = {ok, mark}; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); /* kvlangBuiltinMemindex p·:kind=index,body=[4B count][names]。 */ char *mip = kvlangKeytreeMember(ok, ""); - kvlangXvalue_t mi; kvlangBuiltinMemindex(&mi, (const char *const *)names, cnt); - kvlangKvPair_t p1 = { mip, mi }; + kvlangXvalue_t mi; + kvlangBuiltinMemindex(&mi, (const char *const *)names, cnt); + kvlangKvPair_t p1 = {mip, mi}; kvlangKvSet(f->kv, &p1, 1, err, sizeof err); - kvlangXvalueFree(&mi); free(mip); + kvlangXvalueFree(&mi); + free(mip); for (int i = 0, j = 0; i + 1 < n; i += 2) { - if (kvlangXvalueNone(&in[i + 1])) continue; + if (kvlangXvalueNone(&in[i + 1])) + continue; char *mk = kvlangKeytreeMember(ok, names[j]); - kvlangKvPair_t p = { mk, in[i + 1] }; + kvlangKvPair_t p = {mk, in[i + 1]}; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - free(mk); free(names[j]); j++; + free(mk); + free(names[j]); + j++; } free(names); free(ok); } free(fr); - kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; } static char *dupn(const char *s, size_t n) { @@ -532,7 +920,8 @@ static char *struct_field_type(const char *decl, const char *fname) { if (nlen == fl && memcmp(p, fname, fl) == 0) return dupn(colon + 1, linelen - nlen - 1); } - if (!nl) break; + if (!nl) + break; p = nl + 1; } return NULL; @@ -541,99 +930,158 @@ static char *struct_field_type(const char *decl, const char *fname) { /* struct·new:克隆 /lib/Name 原型子树到写槽,覆盖给定字段(校验字段存在性+类型)。 * in[0]=structref("/lib/Name"),其后成对 (字段名, 值)。实例基值 kind=structref。 */ int kvlangBuiltinStructNew(kvlangFrame_t *f) { - kvlangXvalue_t in[64]; int n = kvlangBuiltinReadInputs(f, in, 64); - if (n < 1) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: struct.new requires a type"); } + kvlangXvalue_t in[64]; + int n = kvlangBuiltinReadInputs(f, in, 64); + if (n < 1) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "TypeError: struct.new requires a type"); + } char *ref = kvlangXvalueValueString(&in[0]); - kvlangXvalue_t proto; kvlangXvalueZero(&proto); kvlangKvGetOne(f->kv, ref, &proto); - if (kvlangXvalueNone(&proto) || strcmp(kvlangXvalueKind(&proto), KVSPACE_KIND_STRUCT) != 0) { - int e = kvlangBuiltinSetErr(f, "TypeError: %s is not a struct type", ref); - kvlangXvalueFree(&proto); free(ref); kvlangBuiltinFreeInputs(in, n); return e; + kvlangXvalue_t proto; + kvlangXvalueZero(&proto); + kvlangKvGetOne(f->kv, ref, &proto); + if (kvlangXvalueNone(&proto) || + strcmp(kvlangXvalueKind(&proto), KVSPACE_KIND_STRUCT) != 0) { + int e = + kvlangBuiltinSetErr(f, "TypeError: %s is not a struct type", ref); + kvlangXvalueFree(&proto); + free(ref); + kvlangBuiltinFreeInputs(in, n); + return e; } - kvspaceHead_t ph; kvlangXvalueHead(&proto, &ph); - int32_t dclen = 0; const uint8_t *dcl = kvlangXvalueBody(&proto, &ph, &dclen); + kvspaceHead_t ph; + kvlangXvalueHead(&proto, &ph); + int32_t dclen = 0; + const uint8_t *dcl = kvlangXvalueBody(&proto, &ph, &dclen); char *decl = dupn((const char *)dcl, (size_t)(dclen > 0 ? dclen : 0)); kvlangXvalueFree(&proto); - char err[256]; int rc = 0; + char err[256]; + int rc = 0; char *fr = kvlangKeytreeFrameRoot(f->pc); for (int w = 0; w < f->inst->nw && rc == 0; w++) { - char *ok = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); - if (kvlangKvCpList(f->kv, ref, ok, err, sizeof err) != 0) { rc = kvlangBuiltinSetErr(f, "%s", err); free(ok); break; } - kvlangXvalue_t mark; kvlangXvalueNewTlv(&mark, ref, (const uint8_t *)"", 0, 1); - kvlangKvPair_t p0 = { ok, mark }; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); + char *ok = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); + if (kvlangKvCpList(f->kv, ref, ok, err, sizeof err) != 0) { + rc = kvlangBuiltinSetErr(f, "%s", err); + free(ok); + break; + } + kvlangXvalue_t mark; + kvlangXvalueNewTlv(&mark, ref, (const uint8_t *)"", 0, 1); + kvlangKvPair_t p0 = {ok, mark}; + kvlangKvSet(f->kv, &p0, 1, err, sizeof err); + kvlangXvalueFree(&mark); for (int i = 1; i + 1 < n && rc == 0; i += 2) { char *fname = kvlangXvalueValueString(&in[i]); char *ftype = struct_field_type(decl, fname); - if (!ftype) { rc = kvlangBuiltinSetErr(f, "TypeError: struct %s has no field %s", ref, fname); free(fname); break; } + if (!ftype) { + rc = kvlangBuiltinSetErr( + f, "TypeError: struct %s has no field %s", ref, fname); + free(fname); + break; + } const char *vk = kvlangXvalueKind(&in[i + 1]); - kvspaceHead_t vh; kvlangXvalueHead(&in[i + 1], &vh); - kvlangLangtype vkx; kvlangLangtypeParse(vh.langtype, &vkx); + kvspaceHead_t vh; + kvlangXvalueHead(&in[i + 1], &vh); + kvlangLangtype vkx; + kvlangLangtypeParse(vh.langtype, &vkx); // *T 指针字段:字段类型剥离前导 * 后与值的 langtype 比对,并校验值 ref=1; // None 是合法空指针(见 [[ptr]]),不算类型不符。 const char *fx = (ftype[0] == '*') ? ftype + 1 : ftype; - bool type_ok = ftype[0] ? kvlangLangtypeMatch(fx, vk, vkx.ndim, vkx.dims) : true; - if (ftype[0] == '*' && !kvlangXvalueNone(&in[i + 1]) && vh.ref != KVSPACE_REF_PTR) + bool type_ok = ftype[0] + ? kvlangLangtypeMatch(fx, vk, vkx.ndim, vkx.dims) + : true; + if (ftype[0] == '*' && !kvlangXvalueNone(&in[i + 1]) && + 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; + rc = kvlangBuiltinSetErr( + f, "TypeError: field %s: expected %s, got %s", fname, ftype, + vk[0] ? vk : "None"); + free(ftype); + free(fname); + break; } char *mk = kvlangKeytreeMember(ok, fname); - kvlangKvPair_t p = { mk, in[i + 1] }; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - free(mk); free(ftype); free(fname); + kvlangKvPair_t p = {mk, in[i + 1]}; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + free(mk); + free(ftype); + free(fname); } free(ok); } - free(fr); free(decl); free(ref); + free(fr); + free(decl); + free(ref); kvlangBuiltinFreeInputs(in, n); - if (rc != 0) return rc; - kvlangBuiltinNextPc(f); return 0; + if (rc != 0) + return rc; + kvlangBuiltinNextPc(f); + return 0; } int kvlangBuiltinMap(kvlangFrame_t *f) { - kvlangXvalue_t in[64]; int n = kvlangBuiltinReadInputs(f, in, 64); + kvlangXvalue_t in[64]; + int n = kvlangBuiltinReadInputs(f, in, 64); char *fr = kvlangKeytreeFrameRoot(f->pc); for (int w = 0; w < f->inst->nw; w++) { - char *ok = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); + char *ok = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); char err[256]; /* 重建:清旧成员(p·name)与旧容器值,随后重写。 */ char *dir = kvlangKeytreeMember(ok, ""); - char **old = NULL; int oc = 0; + char **old = NULL; + int oc = 0; kvlangKvList(f->kv, dir, false, false, &old, &oc); for (int i = 0; i < oc; i++) { char *mk = kvlangKeytreeMember(ok, old[i]); kvlangKvDel(f->kv, mk, err, sizeof err); - free(mk); free(old[i]); + free(mk); + free(old[i]); } - free(old); free(dir); + free(old); + free(dir); kvlangKvDel(f->kv, ok, err, sizeof err); char **names = malloc(sizeof(char *) * (size_t)(n > 0 ? n : 1)); for (int i = 0; i < n; i++) { - kvlangStrbuf_t s; kvlangStrbufInit(&s); kvlangStrbufPrintf(&s, "[%d]", i); + kvlangStrbuf_t s; + kvlangStrbufInit(&s); + kvlangStrbufPrintf(&s, "[%d]", i); names[i] = kvlangStrbufDetach(&s); } - /* 容器值 p:stringkeymap,body 空,dims=[n] 落 head。 */ - int32_t dims[1] = { n }; - kvlangXvalue_t mark; kvlangBuiltinMapMarker(&mark, dims, 1); - kvlangKvPair_t p0 = { ok, mark }; + /* 容器值 p:langtype=声明的 map langtype,body 空,dims=[n] 落 head。 */ + int32_t dims[1] = {n}; + char *wty = declared_map_langtype(f, w); + kvlangXvalue_t mark; + kvlangBuiltinMapMarker(&mark, wty, dims, 1); + free(wty); + kvlangKvPair_t p0 = {ok, mark}; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); /* kvlangBuiltinMemindex p·:kind=index,body=[4B count][[0]\n[1]...]。 */ char *mip = kvlangKeytreeMember(ok, ""); - kvlangXvalue_t mi; kvlangBuiltinMemindex(&mi, (const char *const *)names, n); - kvlangKvPair_t p1 = { mip, mi }; + kvlangXvalue_t mi; + kvlangBuiltinMemindex(&mi, (const char *const *)names, n); + kvlangKvPair_t p1 = {mip, mi}; kvlangKvSet(f->kv, &p1, 1, err, sizeof err); - kvlangXvalueFree(&mi); free(mip); + kvlangXvalueFree(&mi); + free(mip); for (int i = 0; i < n; i++) { - int64_t c[1] = { i }; + int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(ok, c, 1); - kvlangKvPair_t p = { k, in[i] }; + kvlangKvPair_t p = {k, in[i]}; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - free(k); free(names[i]); + free(k); + free(names[i]); } - free(names); free(ok); + free(names); + free(ok); } free(fr); - kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; } diff --git a/runtime/src/builtin_internal.h b/runtime/src/builtin_internal.h index 07f3d1b1..8e676d05 100644 --- a/runtime/src/builtin_internal.h +++ b/runtime/src/builtin_internal.h @@ -3,37 +3,78 @@ /* ── 跨模块共享 helper ───────────────────────────────────────────── frame I/O 与 xvalue_at 定义在 builtin.c;容器 key/index helper 定义在 builtin_coll.c。*/ -int kvlangBuiltinReadInputs(kvlangFrame_t *f, kvlangXvalue_t *out, int cap); +int kvlangBuiltinReadInputs(kvlangFrame_t *f, kvlangXvalue_t *out, int cap); void kvlangBuiltinFreeInputs(kvlangXvalue_t *in, int n); void kvlangBuiltinNextPc(kvlangFrame_t *f); -int kvlangBuiltinWriteResult(kvlangFrame_t *f, const kvlangXvalue_t *result); -int kvlangBuiltinSetErr(kvlangFrame_t *f, const char *fmt, ...); +int kvlangBuiltinWriteResult(kvlangFrame_t *f, const kvlangXvalue_t *result); +int kvlangBuiltinSetErr(kvlangFrame_t *f, const char *fmt, ...); void kvlangBuiltinXvalueAt(const kvlangXvalue_t *v, int i, kvlangXvalue_t *out); -char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, int ncoord); -void kvlangBuiltinMemindex(kvlangXvalue_t *out, const char *const *names, int n); -void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const int32_t *dims, int ndim); +char *kvlangBuiltinScatterKey(const char *base, const int64_t *coords, + int ncoord); +void kvlangBuiltinMemindex(kvlangXvalue_t *out, const char *const *names, + int n); +void kvlangBuiltinMapMarker(kvlangXvalue_t *out, const char *langtype, + const int32_t *dims, int ndim); /* char 拼接(+ 与 string·concat 共用):均须 char kind;编码不同返 false(调用方 throw),相同则 out=拼接结果并保持该编码。 */ -bool kvlangBuiltinCharConcat(const kvlangXvalue_t *a, const kvlangXvalue_t *b, kvlangXvalue_t *out); +bool kvlangBuiltinCharConcat(const kvlangXvalue_t *a, const kvlangXvalue_t *b, + kvlangXvalue_t *out); /* ── 各 lib 的 rwir handler 原型(表在 builtin.c 引用)────────────── */ -int kvlangBuiltinArray(kvlangFrame_t *f), kvlangBuiltinArrayFill(kvlangFrame_t *f), kvlangBuiltinNdarrayNumel(kvlangFrame_t *f), kvlangBuiltinNdarrayDim(kvlangFrame_t *f), kvlangBuiltinNdarrayShape(kvlangFrame_t *f), - kvlangBuiltinXvAt(kvlangFrame_t *f), kvlangBuiltinXvSet(kvlangFrame_t *f), kvlangBuiltinXvReshape(kvlangFrame_t *f), kvlangBuiltinXvReinterpret(kvlangFrame_t *f), - kvlangBuiltinXvLangtype(kvlangFrame_t *f), kvlangBuiltinXvBodylen(kvlangFrame_t *f), - kvlangBuiltinScatter(kvlangFrame_t *f), kvlangBuiltinCompact(kvlangFrame_t *f), - kvlangBuiltinAppend(kvlangFrame_t *f), kvlangBuiltinSlice(kvlangFrame_t *f), kvlangBuiltinObj(kvlangFrame_t *f), kvlangBuiltinMap(kvlangFrame_t *f), kvlangBuiltinStructNew(kvlangFrame_t *f), kvlangBuiltinStringSet(kvlangFrame_t *f), - kvlangBuiltinStringChar(kvlangFrame_t *f), kvlangBuiltinStringOrd(kvlangFrame_t *f), kvlangBuiltinStringCmp(kvlangFrame_t *f), - kvlangBuiltinStringFind(kvlangFrame_t *f), kvlangBuiltinStringLen(kvlangFrame_t *f), kvlangBuiltinStringSlice(kvlangFrame_t *f), +int kvlangBuiltinArray(kvlangFrame_t *f), + kvlangBuiltinArrayFill(kvlangFrame_t *f), + kvlangBuiltinNdarrayNumel(kvlangFrame_t *f), + kvlangBuiltinNdarrayDim(kvlangFrame_t *f), + kvlangBuiltinNdarrayShape(kvlangFrame_t *f), + kvlangBuiltinXvAt(kvlangFrame_t *f), kvlangBuiltinXvSet(kvlangFrame_t *f), + kvlangBuiltinXvReshape(kvlangFrame_t *f), + kvlangBuiltinXvReinterpret(kvlangFrame_t *f), + kvlangBuiltinXvLangtype(kvlangFrame_t *f), + kvlangBuiltinXvBodylen(kvlangFrame_t *f), + kvlangBuiltinScatter(kvlangFrame_t *f), + kvlangBuiltinCompact(kvlangFrame_t *f), + kvlangBuiltinAppend(kvlangFrame_t *f), kvlangBuiltinSlice(kvlangFrame_t *f), + kvlangBuiltinObj(kvlangFrame_t *f), kvlangBuiltinMap(kvlangFrame_t *f), + kvlangBuiltinStructNew(kvlangFrame_t *f), + kvlangBuiltinStringSet(kvlangFrame_t *f), + kvlangBuiltinStringChar(kvlangFrame_t *f), + kvlangBuiltinStringOrd(kvlangFrame_t *f), + kvlangBuiltinStringCmp(kvlangFrame_t *f), + kvlangBuiltinStringFind(kvlangFrame_t *f), + kvlangBuiltinStringLen(kvlangFrame_t *f), + kvlangBuiltinStringSlice(kvlangFrame_t *f), kvlangBuiltinStringConcat(kvlangFrame_t *f), - kvlangBuiltinStringFormatInt(kvlangFrame_t *f), kvlangBuiltinStringFormatUint(kvlangFrame_t *f), - kvlangBuiltinStringParseInt(kvlangFrame_t *f), kvlangBuiltinStringParseUint(kvlangFrame_t *f), - kvlangBuiltinTimeNow(kvlangFrame_t *f), kvlangBuiltinTimeSub(kvlangFrame_t *f), - kvlangBuiltinTimeAdd(kvlangFrame_t *f), kvlangBuiltinDurFrom(kvlangFrame_t *f), kvlangBuiltinDurTo(kvlangFrame_t *f), - kvlangBuiltinDurArith(kvlangFrame_t *f), kvlangBuiltinDurCmp(kvlangFrame_t *f), kvlangBuiltinTimeCmp(kvlangFrame_t *f), - kvlangBuiltinRandUint64(kvlangFrame_t *f), kvlangBuiltinRandInt63(kvlangFrame_t *f), kvlangBuiltinRandIntn(kvlangFrame_t *f), - kvlangBuiltinKvGet(kvlangFrame_t *f), kvlangBuiltinKvSet(kvlangFrame_t *f), kvlangBuiltinKvDel(kvlangFrame_t *f), - kvlangBuiltinKvDelTree(kvlangFrame_t *f), kvlangBuiltinKvCp(kvlangFrame_t *f), kvlangBuiltinKvCpTree(kvlangFrame_t *f), kvlangBuiltinKvCpList(kvlangFrame_t *f), kvlangBuiltinKvList(kvlangFrame_t *f), kvlangBuiltinKvListLen(kvlangFrame_t *f), kvlangBuiltinKvListN(kvlangFrame_t *f), kvlangBuiltinKvMkindex(kvlangFrame_t *f), - kvlangBuiltinKvExtIndex(kvlangFrame_t *f), kvlangBuiltinKvRmIndexExt(kvlangFrame_t *f), kvlangBuiltinKvWatch(kvlangFrame_t *f), kvlangBuiltinKvAbs(kvlangFrame_t *f), - kvlangBuiltinDebugger(kvlangFrame_t *f), kvlangBuiltinVthreadCreate(kvlangFrame_t *f), + kvlangBuiltinStringFormatInt(kvlangFrame_t *f), + kvlangBuiltinStringFormatUint(kvlangFrame_t *f), + kvlangBuiltinStringParseInt(kvlangFrame_t *f), + kvlangBuiltinStringParseUint(kvlangFrame_t *f), + kvlangBuiltinTimeNow(kvlangFrame_t *f), + kvlangBuiltinTimeSub(kvlangFrame_t *f), + kvlangBuiltinTimeAdd(kvlangFrame_t *f), + kvlangBuiltinDurFrom(kvlangFrame_t *f), + kvlangBuiltinDurTo(kvlangFrame_t *f), + kvlangBuiltinDurArith(kvlangFrame_t *f), + kvlangBuiltinDurCmp(kvlangFrame_t *f), + kvlangBuiltinTimeCmp(kvlangFrame_t *f), + kvlangBuiltinRandUint64(kvlangFrame_t *f), + kvlangBuiltinRandInt63(kvlangFrame_t *f), + kvlangBuiltinRandIntn(kvlangFrame_t *f), + kvlangBuiltinKvGet(kvlangFrame_t *f), kvlangBuiltinKvSet(kvlangFrame_t *f), + kvlangBuiltinKvDel(kvlangFrame_t *f), + kvlangBuiltinKvDelTree(kvlangFrame_t *f), + kvlangBuiltinKvCp(kvlangFrame_t *f), + kvlangBuiltinKvCpTree(kvlangFrame_t *f), + kvlangBuiltinKvCpList(kvlangFrame_t *f), + kvlangBuiltinKvList(kvlangFrame_t *f), + kvlangBuiltinKvListLen(kvlangFrame_t *f), + kvlangBuiltinKvListN(kvlangFrame_t *f), + kvlangBuiltinKvMkindex(kvlangFrame_t *f), + kvlangBuiltinKvExtIndex(kvlangFrame_t *f), + kvlangBuiltinKvRmIndexExt(kvlangFrame_t *f), + kvlangBuiltinKvWatch(kvlangFrame_t *f), + kvlangBuiltinKvAbs(kvlangFrame_t *f), + kvlangBuiltinDebugger(kvlangFrame_t *f), + kvlangBuiltinVthreadCreate(kvlangFrame_t *f), kvlangBuiltinVthreadRun(kvlangFrame_t *f), - kvlangBuiltinVthreadCall(kvlangFrame_t *f), kvlangBuiltinVthreadSleep(kvlangFrame_t *f), + kvlangBuiltinVthreadCall(kvlangFrame_t *f), + kvlangBuiltinVthreadSleep(kvlangFrame_t *f), kvlangBuiltinVthreadSetstatus(kvlangFrame_t *f); diff --git a/runtime/src/builtin_kv.c b/runtime/src/builtin_kv.c index 8fda6e98..a198b169 100644 --- a/runtime/src/builtin_kv.c +++ b/runtime/src/builtin_kv.c @@ -2,20 +2,30 @@ #include "builtin_internal.h" -static bool is_int_kind(const char *k) { return kvlangXvalueIsIntKind(k) || kvlangXvalueIsUintKind(k); } +static bool is_int_kind(const char *k) { + return kvlangXvalueIsIntKind(k) || kvlangXvalueIsUintKind(k); +} static char *kvlangKvKey(const kvlangXvalue_t *v) { - if (kvlangXvalueIsCharKind(kvlangXvalueKind(v))) return kvlangXvalueValueString(v); - if (is_int_kind(kvlangXvalueKind(v))) { char buf[32]; snprintf(buf, sizeof buf, "%lld", (long long)kvlangScalarI64(kvlangXvalueScalar(v))); return strdup(buf); } + if (kvlangXvalueIsCharKind(kvlangXvalueKind(v))) + return kvlangXvalueValueString(v); + if (is_int_kind(kvlangXvalueKind(v))) { + char buf[32]; + snprintf(buf, sizeof buf, "%lld", + (long long)kvlangScalarI64(kvlangXvalueScalar(v))); + return strdup(buf); + } return strdup(""); } static char *path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { const char *name = f->inst->reads[idx].name; - if (name[0] == '/') return strdup(name); + if (name[0] == '/') + return strdup(name); if (!kvlangXvalueNone(&in[idx])) { char *s = kvlangXvalueValueString(&in[idx]); - if (s[0] == '/') return s; + if (s[0] == '/') + return s; free(s); } return NULL; @@ -24,10 +34,12 @@ static char *path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { /* 容器 base:stringkeymap/index/extindex 或 struct 实例(kind=structref,以 / 起头)。 * 容器取其写槽路径拼成员 key;非容器把 base 值当 key 串。 */ static bool base_is_container(const kvlangXvalue_t *base) { - if (kvlangXvalueNone(base)) return true; + if (kvlangXvalueNone(base)) + return true; const char *k = kvlangXvalueKind(base); - return k[0] == '/' || strcmp(k, KVSPACE_KIND_MAP) == 0 || - strcmp(k, KVSPACE_KIND_INDEX) == 0 || strcmp(k, KVSPACE_KIND_EXT_INDEX) == 0; + return k[0] == '/' || kvlangKindIsMap(k) || + strcmp(k, KVSPACE_KIND_INDEX) == 0 || + strcmp(k, KVSPACE_KIND_EXT_INDEX) == 0; } static char *member_path(kvlangFrame_t *f, const kvlangXvalue_t *in, int n) { @@ -47,20 +59,30 @@ static char *member_path(kvlangFrame_t *f, const kvlangXvalue_t *in, int n) { for (int i = 1; i < n; i++) { char *kk = kvlangKvKey(&in[i]); char *next = kvlangKeytreeMember(bp, kk); - free(kk); free(bp); + free(kk); + free(bp); bp = next; } return bp; } int kvlangBuiltinKvGet(kvlangFrame_t *f) { - kvlangXvalue_t in[MAX_PARAMS]; int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); - char *key = f->inst->nr >= 2 ? member_path(f, in, n) : (n >= 1 ? path_arg(f, 0, in) : NULL); - if (!key) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.get requires a path"); } - kvlangXvalue_t v; kvlangXvalueZero(&v); + kvlangXvalue_t in[MAX_PARAMS]; + int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); + char *key = f->inst->nr >= 2 ? member_path(f, in, n) + : (n >= 1 ? path_arg(f, 0, in) : NULL); + if (!key) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "TypeError: kv.get requires a path"); + } + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, key, &v); - int rc = kvlangBuiltinWriteResult(f, &v); kvlangXvalueFree(&v); - free(key); kvlangBuiltinFreeInputs(in, n); return rc; + int rc = kvlangBuiltinWriteResult(f, &v); + kvlangXvalueFree(&v); + free(key); + kvlangBuiltinFreeInputs(in, n); + return rc; } /* 成员写(memitem)的前置条件:memhead(容器值 base)必须已存在。不存在即拒绝写入—— @@ -68,10 +90,13 @@ int kvlangBuiltinKvGet(kvlangFrame_t *f) { * 错误,与「禁止写入未声明的 key」一致(对齐 strict 语义,见 [[容器]])。 * `/lib` 下跳过:那是 layout/runtime 的结构域,`·` 是包·函数命名分隔符而非成员写。 * 返回 0 通过;-1 拒绝(调用方置 TypeError)。 */ -int kvlangBuiltinCheckMemhead(kvlangKv_t *kv, const char *frame_root, const char *base) { - if (!base || !base[0] || strncmp(base, "/lib", 4) == 0) return 0; +int kvlangBuiltinCheckMemhead(kvlangKv_t *kv, const char *frame_root, + const char *base) { + if (!base || !base[0] || strncmp(base, "/lib", 4) == 0) + return 0; char *path = kvlangBuiltinResolveWriteSlot(kv, frame_root, base); - kvlangXvalue_t cur; kvlangXvalueZero(&cur); + kvlangXvalue_t cur; + kvlangXvalueZero(&cur); kvlangKvGetOne(kv, path, &cur); int ok = !kvlangXvalueNone(&cur); kvlangXvalueFree(&cur); @@ -80,8 +105,10 @@ int kvlangBuiltinCheckMemhead(kvlangKv_t *kv, const char *frame_root, const char } int kvlangBuiltinKvSet(kvlangFrame_t *f) { - kvlangXvalue_t in[MAX_PARAMS]; int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); - char *key; kvlangXvalue_t *val; + kvlangXvalue_t in[MAX_PARAMS]; + int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); + char *key; + kvlangXvalue_t *val; if (f->inst->nr >= 3) { const char *base = f->inst->reads[0].name; char *fr = kvlangKeytreeFrameRoot(f->pc); @@ -89,71 +116,130 @@ int kvlangBuiltinKvSet(kvlangFrame_t *f) { free(fr); if (ok != 0) { kvlangBuiltinFreeInputs(in, n); - return kvlangBuiltinSetErr(f, "TypeError: memhead %s does not exist — declare the container first (e.g. `%s:T = {}`)", base, base); + return kvlangBuiltinSetErr( + f, + "TypeError: memhead %s does not exist — declare the container " + "first (e.g. `%s:T = {}`)", + base, base); } - key = member_path(f, in, n - 1); val = &in[n - 1]; + key = member_path(f, in, n - 1); + val = &in[n - 1]; + } else { + key = n >= 1 ? path_arg(f, 0, in) : NULL; + val = &in[1]; + } + if (!key || (f->inst->nr < 3 && n < 2)) { + free(key); + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, + "TypeError: kv.set requires path and value"); } - else { key = n >= 1 ? path_arg(f, 0, in) : NULL; val = &in[1]; } - if (!key || (f->inst->nr < 3 && n < 2)) { free(key); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.set requires path and value"); } - kvlangKvPair_t p = { key, *val }; - char err[256]; int rc = kvlangKvSet(f->kv, &p, 1, err, sizeof err); - free(key); kvlangBuiltinFreeInputs(in, n); - if (rc != 0) return kvlangBuiltinSetErr(f, "%s", err); - kvlangBuiltinNextPc(f); return 0; + kvlangKvPair_t p = {key, *val}; + char err[256]; + int rc = kvlangKvSet(f->kv, &p, 1, err, sizeof err); + free(key); + kvlangBuiltinFreeInputs(in, n); + if (rc != 0) + return kvlangBuiltinSetErr(f, "%s", err); + kvlangBuiltinNextPc(f); + return 0; } static int kv_path_void(kvlangFrame_t *f, const char *name, - int (*op)(kvlangKv_t *, const char *, char *, uint32_t)) { - kvlangXvalue_t in[1]; int n = kvlangBuiltinReadInputs(f, in, 1); + int (*op)(kvlangKv_t *, const char *, char *, + uint32_t)) { + kvlangXvalue_t in[1]; + int n = kvlangBuiltinReadInputs(f, in, 1); char *key = n >= 1 ? path_arg(f, 0, in) : NULL; - if (!key) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: %s requires 1 path arg", name); } - char err[256]; int rc = op(f->kv, key, err, sizeof err); - free(key); kvlangBuiltinFreeInputs(in, n); - if (rc != 0) return kvlangBuiltinSetErr(f, "%s", err); - kvlangBuiltinNextPc(f); return 0; + if (!key) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "TypeError: %s requires 1 path arg", + name); + } + char err[256]; + int rc = op(f->kv, key, err, sizeof err); + free(key); + kvlangBuiltinFreeInputs(in, n); + if (rc != 0) + return kvlangBuiltinSetErr(f, "%s", err); + kvlangBuiltinNextPc(f); + return 0; } -int kvlangBuiltinKvDel(kvlangFrame_t *f) { return kv_path_void(f, "kv.del", kvlangKvDel); } +int kvlangBuiltinKvDel(kvlangFrame_t *f) { + return kv_path_void(f, "kv.del", kvlangKvDel); +} -int kvlangBuiltinKvDelTree(kvlangFrame_t *f) { return kv_path_void(f, "kv.deltree", kvlangKvDelTree); } +int kvlangBuiltinKvDelTree(kvlangFrame_t *f) { + return kv_path_void(f, "kv.deltree", kvlangKvDelTree); +} /* 绝对路径 / 路径字符串直取,否则裸标识符解析为本帧槽位 key(对齐 kv.list 的裸变量处理)。 */ -static char *resolve_path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { +static char *resolve_path_arg(kvlangFrame_t *f, int idx, + const kvlangXvalue_t *in) { char *p = path_arg(f, idx, in); - if (p) return p; + if (p) + return p; char *fr = kvlangKeytreeFrameRoot(f->pc); - char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[idx].name); + char *base = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[idx].name); free(fr); return base; } static int kv_two_path_void(kvlangFrame_t *f, const char *name, - int (*op)(kvlangKv_t *, const char *, const char *, char *, uint32_t)) { - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + int (*op)(kvlangKv_t *, const char *, const char *, + char *, uint32_t)) { + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); char *src = n >= 1 ? resolve_path_arg(f, 0, in) : NULL; char *dst = n >= 2 ? resolve_path_arg(f, 1, in) : NULL; - if (!src || !dst) { free(src); free(dst); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: %s requires src,dst path args", name); } - char err[256]; int rc = op(f->kv, src, dst, err, sizeof err); - free(src); free(dst); kvlangBuiltinFreeInputs(in, n); - if (rc != 0) return kvlangBuiltinSetErr(f, "%s", err); - kvlangBuiltinNextPc(f); return 0; + if (!src || !dst) { + free(src); + free(dst); + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "TypeError: %s requires src,dst path args", name); + } + char err[256]; + int rc = op(f->kv, src, dst, err, sizeof err); + free(src); + free(dst); + kvlangBuiltinFreeInputs(in, n); + if (rc != 0) + return kvlangBuiltinSetErr(f, "%s", err); + kvlangBuiltinNextPc(f); + return 0; } -int kvlangBuiltinKvCp(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cp", kvlangKvCp); } +int kvlangBuiltinKvCp(kvlangFrame_t *f) { + return kv_two_path_void(f, "kv.cp", kvlangKvCp); +} -int kvlangBuiltinKvCpTree(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cpdir", kvlangKvCpTree); } +int kvlangBuiltinKvCpTree(kvlangFrame_t *f) { + return kv_two_path_void(f, "kv.cpdir", kvlangKvCpTree); +} -int kvlangBuiltinKvCpList(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cplist", kvlangKvCpList); } +int kvlangBuiltinKvCpList(kvlangFrame_t *f) { + return kv_two_path_void(f, "kv.cplist", kvlangKvCpList); +} int kvlangBuiltinKvAbs(kvlangFrame_t *f) { - kvlangXvalue_t in[MAX_PARAMS]; int n = kvlangBuiltinReadInputs(f, in, MAX_PARAMS); + 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"); } + 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 路径串。 */ - kvlangXvalue_t tg; kvlangXvalueZero(&tg); + kvlangXvalue_t tg; + kvlangXvalueZero(&tg); kvlangKvGetOne(f->kv, p, &tg); char lt[256] = {0}; if (!kvlangXvalueNone(&tg)) { @@ -165,15 +251,22 @@ int kvlangBuiltinKvAbs(kvlangFrame_t *f) { memcpy(lt, h.langtype, llen); } } - kvlangXvalue_t r; kvlangXvalueNewPtr(&r, lt, p); + kvlangXvalue_t r; + kvlangXvalueNewPtr(&r, lt, p); int rc = kvlangBuiltinWriteResult(f, &r); - kvlangXvalueFree(&r); kvlangXvalueFree(&tg); - free(p); kvlangBuiltinFreeInputs(in, n); return rc; + kvlangXvalueFree(&r); + kvlangXvalueFree(&tg); + free(p); + kvlangBuiltinFreeInputs(in, n); + return rc; } int kvlangBuiltinKvList(kvlangFrame_t *f) { - if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: kv.list requires a write param"); - kvlangXvalue_t in[1]; int n = kvlangBuiltinReadInputs(f, in, 1); + if (f->inst->nw == 0) + return kvlangBuiltinSetErr(f, + "TypeError: kv.list requires a write param"); + kvlangXvalue_t in[1]; + int n = kvlangBuiltinReadInputs(f, in, 1); char *key = n >= 1 ? path_arg(f, 0, in) : NULL; if (!key && f->inst->nr >= 1) { /* 裸变量(obj/map 成员目录):解析为 /. 目录。 */ @@ -186,47 +279,75 @@ int kvlangBuiltinKvList(kvlangFrame_t *f) { free(base); } } - if (!key) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.list requires 1 path arg"); } - char **names = NULL; int count = 0; + if (!key) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "TypeError: kv.list requires 1 path arg"); + } + char **names = NULL; + int count = 0; kvlangKvList(f->kv, key, false, false, &names, &count); char *fr = kvlangKeytreeFrameRoot(f->pc); - char *dst = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); + char *dst = + kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); free(fr); /* 结果是一个 stringkeymap:容器值在 dst(body 空,dims=[count] 落 head), * kvlangBuiltinMemindex 在 dst·,成员名是坐标段 [i],值是对应的成员名字符串。 */ char **coords = malloc(sizeof(char *) * (size_t)(count > 0 ? count : 1)); for (int i = 0; i < count; i++) { - kvlangStrbuf_t s; kvlangStrbufInit(&s); kvlangStrbufPrintf(&s, "[%d]", i); + kvlangStrbuf_t s; + kvlangStrbufInit(&s); + kvlangStrbufPrintf(&s, "[%d]", i); coords[i] = kvlangStrbufDetach(&s); } char err[256]; - int32_t dims[1] = { count }; - kvlangXvalue_t mark; kvlangBuiltinMapMarker(&mark, dims, 1); - kvlangKvPair_t p0 = { dst, mark }; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); + int32_t dims[1] = {count}; + kvlangXvalue_t mark; + kvlangBuiltinMapMarker(&mark, "[int64]" MEMBER_SEP "[]char/utf8", dims, 1); + kvlangKvPair_t p0 = {dst, mark}; + kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); char *dir = kvlangKeytreeMember(dst, ""); - kvlangXvalue_t mi; kvlangBuiltinMemindex(&mi, (const char *const *)coords, count); - kvlangKvPair_t p1 = { dir, mi }; kvlangKvSet(f->kv, &p1, 1, err, sizeof err); - kvlangXvalueFree(&mi); free(dir); - for (int i = 0; i < count; i++) free(coords[i]); + kvlangXvalue_t mi; + kvlangBuiltinMemindex(&mi, (const char *const *)coords, count); + kvlangKvPair_t p1 = {dir, mi}; + kvlangKvSet(f->kv, &p1, 1, err, sizeof err); + kvlangXvalueFree(&mi); + free(dir); + for (int i = 0; i < count; i++) + free(coords[i]); free(coords); for (int i = 0; i < count; i++) { - int64_t c[1] = { i }; + int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(dst, c, 1); - kvlangXvalue_t e; kvlangXvalueNewCharUtf8(&e, names[i]); - kvlangKvPair_t p = { k, e }; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - kvlangXvalueFree(&e); free(k); free(names[i]); + kvlangXvalue_t e; + kvlangXvalueNewCharUtf8(&e, names[i]); + kvlangKvPair_t p = {k, e}; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + kvlangXvalueFree(&e); + free(k); + free(names[i]); } - for (int i = count; ; i++) { - int64_t c[1] = { i }; + for (int i = count;; i++) { + int64_t c[1] = {i}; char *k = kvlangBuiltinScatterKey(dst, c, 1); - kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, k, &v); - bool none = kvlangXvalueNone(&v); kvlangXvalueFree(&v); - if (none) { free(k); break; } - kvlangKvDel(f->kv, k, err, sizeof err); free(k); + kvlangXvalue_t v; + kvlangXvalueZero(&v); + kvlangKvGetOne(f->kv, k, &v); + bool none = kvlangXvalueNone(&v); + kvlangXvalueFree(&v); + if (none) { + free(k); + break; + } + kvlangKvDel(f->kv, k, err, sizeof err); + free(k); } - free(names); free(dst); free(key); - kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; + free(names); + free(dst); + free(key); + kvlangBuiltinNextPc(f); + kvlangBuiltinFreeInputs(in, n); + return 0; } /* 解析 obj/map 成员目录 key(/.),供 kv.list/listlen/listn 共用。 */ @@ -246,71 +367,116 @@ static char *kv_list_dir(kvlangFrame_t *f, kvlangXvalue_t *in, int n) { } int kvlangBuiltinKvListLen(kvlangFrame_t *f) { - kvlangXvalue_t in[1]; int n = kvlangBuiltinReadInputs(f, in, 1); + kvlangXvalue_t in[1]; + int n = kvlangBuiltinReadInputs(f, in, 1); char *key = kv_list_dir(f, in, n); int64_t count = 0; if (key) { - char **names = NULL; int cnt = 0; + char **names = NULL; + int cnt = 0; kvlangKvList(f->kv, key, false, false, &names, &cnt); - for (int i = 0; i < cnt; i++) free(names[i]); + for (int i = 0; i < cnt; i++) + free(names[i]); free(names); count = cnt; free(key); } - kvlangXvalue_t r; kvlangXvalueNewInt64(&r, count); - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); kvlangBuiltinFreeInputs(in, n); + kvlangXvalue_t r; + kvlangXvalueNewInt64(&r, count); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); + kvlangBuiltinFreeInputs(in, n); return rc; } int kvlangBuiltinKvListN(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); char *key = kv_list_dir(f, in, n); int idx = n >= 2 ? (int)kvlangScalarI64(kvlangXvalueScalar(&in[1])) : -1; - kvlangXvalue_t r; kvlangXvalueZero(&r); + kvlangXvalue_t r; + kvlangXvalueZero(&r); if (key && idx >= 0) { - char **names = NULL; int cnt = 0; + char **names = NULL; + int cnt = 0; kvlangKvList(f->kv, key, false, false, &names, &cnt); if (idx < cnt) { kvlangXvalueNewCharUtf8(&r, names[idx]); } - for (int i = 0; i < cnt; i++) free(names[i]); + for (int i = 0; i < cnt; i++) + free(names[i]); free(names); free(key); } - int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); kvlangBuiltinFreeInputs(in, n); + int rc = kvlangBuiltinWriteResult(f, &r); + kvlangXvalueFree(&r); + kvlangBuiltinFreeInputs(in, n); return rc; } int kvlangBuiltinKvMkindex(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); char *key = n >= 1 ? path_arg(f, 0, in) : NULL; - if (!key) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.mkindex requires a path"); } - uint32_t capacity = n >= 2 ? (uint32_t)kvlangScalarI64(kvlangXvalueScalar(&in[1])) : 0; - char err[256]; int rc = kvlangKvMkindex(f->kv, key, capacity, err, sizeof err); - free(key); kvlangBuiltinFreeInputs(in, n); - if (rc != 0) return kvlangBuiltinSetErr(f, "%s", err); - kvlangBuiltinNextPc(f); return 0; + if (!key) { + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr(f, "TypeError: kv.mkindex requires a path"); + } + uint32_t capacity = + n >= 2 ? (uint32_t)kvlangScalarI64(kvlangXvalueScalar(&in[1])) : 0; + char err[256]; + int rc = kvlangKvMkindex(f->kv, key, capacity, err, sizeof err); + free(key); + kvlangBuiltinFreeInputs(in, n); + if (rc != 0) + return kvlangBuiltinSetErr(f, "%s", err); + kvlangBuiltinNextPc(f); + return 0; } int kvlangBuiltinKvExtIndex(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); char *key = n >= 1 ? path_arg(f, 0, in) : NULL; char *ext = n >= 2 ? kvlangXvalueValueString(&in[1]) : NULL; - if (!key || !ext) { free(key); free(ext); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.extindex requires path and ext path"); } - char err[256]; int rc = kvlangKvExtIndex(f->kv, key, ext, err, sizeof err); - free(key); free(ext); kvlangBuiltinFreeInputs(in, n); - if (rc != 0) return kvlangBuiltinSetErr(f, "%s", err); - kvlangBuiltinNextPc(f); return 0; + if (!key || !ext) { + free(key); + free(ext); + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "TypeError: kv.extindex requires path and ext path"); + } + char err[256]; + int rc = kvlangKvExtIndex(f->kv, key, ext, err, sizeof err); + free(key); + free(ext); + kvlangBuiltinFreeInputs(in, n); + if (rc != 0) + return kvlangBuiltinSetErr(f, "%s", err); + kvlangBuiltinNextPc(f); + return 0; } -int kvlangBuiltinKvRmIndexExt(kvlangFrame_t *f) { return kv_path_void(f, "kv.rmindexext", kvlangKvDelExtIndex); } +int kvlangBuiltinKvRmIndexExt(kvlangFrame_t *f) { + return kv_path_void(f, "kv.rmindexext", kvlangKvDelExtIndex); +} int kvlangBuiltinKvWatch(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + kvlangXvalue_t in[2]; + int n = kvlangBuiltinReadInputs(f, in, 2); char *key = n >= 1 ? path_arg(f, 0, in) : NULL; - if (!key || n < 2) { free(key); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.watch requires key and target"); } - kvlangXvalue_t v; kvlangXvalueZero(&v); + if (!key || n < 2) { + free(key); + kvlangBuiltinFreeInputs(in, n); + return kvlangBuiltinSetErr( + f, "TypeError: kv.watch requires key and target"); + } + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangKvWatch(f->kv, key, &in[1], 1000000, &v); - int rc = kvlangBuiltinWriteResult(f, &v); kvlangXvalueFree(&v); - free(key); kvlangBuiltinFreeInputs(in, n); return rc; + int rc = kvlangBuiltinWriteResult(f, &v); + kvlangXvalueFree(&v); + free(key); + kvlangBuiltinFreeInputs(in, n); + return rc; } diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index 647a7dc9..32c256f2 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -17,16 +17,23 @@ static rwir_cache_ent_t *g_rwir_cache[RWIR_CACHE_BUCKETS]; * 在 shm 后端 decode 本已近乎免费的深递归热路径上纯属净开销)。 */ static size_t rwir_hash(const char *funcdir, int addr0) { size_t h = 1469598103934665603ULL; - for (const char *s = funcdir; *s; s++) { h ^= (unsigned char)*s; h *= 1099511628211ULL; } - h ^= (size_t)(unsigned)addr0; h *= 1099511628211ULL; + for (const char *s = funcdir; *s; s++) { + h ^= (unsigned char)*s; + h *= 1099511628211ULL; + } + h ^= (size_t)(unsigned)addr0; + h *= 1099511628211ULL; return h & (RWIR_CACHE_BUCKETS - 1); } static kvlangRwirInst_t *rwir_cache_get(const char *funcdir, int addr0) { - for (rwir_cache_ent_t *e = g_rwir_cache[rwir_hash(funcdir, addr0)]; e; e = e->next) - if (e->addr0 == addr0 && strcmp(e->funcdir, funcdir) == 0) return e->inst; + for (rwir_cache_ent_t *e = g_rwir_cache[rwir_hash(funcdir, addr0)]; e; + e = e->next) + if (e->addr0 == addr0 && strcmp(e->funcdir, funcdir) == 0) + return e->inst; return NULL; } -static void rwir_cache_put(const char *funcdir, int addr0, kvlangRwirInst_t *inst) { +static void rwir_cache_put(const char *funcdir, int addr0, + kvlangRwirInst_t *inst) { size_t b = rwir_hash(funcdir, addr0); rwir_cache_ent_t *e = malloc(sizeof *e); e->funcdir = strdup(funcdir); @@ -42,20 +49,23 @@ static void rwir_cache_put(const char *funcdir, int addr0, kvlangRwirInst_t *ins * langtype 签名串在此按 opcode 驻留一次(IV-0:进程私有,绝不入 kvspace)。 */ typedef struct opmeta_ent { char *opcode; - int notinmyrwircaps; /* 1 = 不在本 runtime myrwircaps、须经 def rwir 路由;0 = 用户 rwfunc */ - char *def_sig; /* notinmyrwircaps 时的读参 langtype 签名(owned,可 NULL) */ + int notinmyrwircaps; /* 1 = 不在本 runtime myrwircaps、须经 def rwir 路由;0 = 用户 rwfunc */ + char + *def_sig; /* notinmyrwircaps 时的读参 langtype 签名(owned,可 NULL) */ int def_nr; - int def_dyn; /* 末读参变参(主槽 body 的 dynamic 字节) */ + int def_dyn; /* 末读参变参(主槽 body 的 dynamic 字节) */ struct opmeta_ent *next; } opmeta_ent_t; static opmeta_ent_t *g_opmeta_cache[RWIR_CACHE_BUCKETS]; -static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, int *out_dyn); +static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, + int *out_dyn); static opmeta_ent_t *opmeta_get(kvlangKv_t *kv, const char *opcode) { size_t b = rwir_hash(opcode, 0); for (opmeta_ent_t *e = g_opmeta_cache[b]; e; e = e->next) - if (strcmp(e->opcode, opcode) == 0) return e; + if (strcmp(e->opcode, opcode) == 0) + return e; opmeta_ent_t *e = malloc(sizeof *e); e->opcode = strdup(opcode); e->notinmyrwircaps = notinmyrwircaps(kv, opcode) ? 1 : 0; @@ -75,10 +85,13 @@ static opmeta_ent_t *opmeta_get(kvlangKv_t *kv, const char *opcode) { /* 读帧的 ‥lib 槽 → funcdir(/lib/pkg·fn),供缓存键用;无则 NULL(不缓存该指令)。 */ static char *read_seglib(kvlangKv_t *kv, const char *fr) { char *stk = kvlangKeytreeStack(fr); - kvlangStrbuf_t k; kvlangStrbufInit(&k); - kvlangStrbufPuts(&k, stk); kvlangStrbufPuts(&k, SEG_LIB); + kvlangStrbuf_t k; + kvlangStrbufInit(&k); + kvlangStrbufPuts(&k, stk); + kvlangStrbufPuts(&k, SEG_LIB); free(stk); - kvlangXvalue_t v; kvlangXvalueZero(&v); + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangKvGetOne(kv, k.p, &v); kvlangStrbufFree(&k); char *r = kvlangXvalueNone(&v) ? NULL : kvlangXvalueValueString(&v); @@ -98,19 +111,25 @@ static const char *rfind_sep(const char *s) { /* goto/br 目标:layout 已把 label 解析为 int64 irseq(≥1)。非 int64 / 越界返回 -1。 */ static int irseq_of(const kvlangParam_t *p) { - if (kvlangXvalueNone(&p->val) || !kvlangXvalueKindIs(&p->val, KVSPACE_KIND_INT64)) return -1; + if (kvlangXvalueNone(&p->val) || + !kvlangXvalueKindIs(&p->val, KVSPACE_KIND_INT64)) + return -1; int64_t n = kvlangScalarI64(kvlangXvalueScalar(&p->val)); - if (n < 1 || n > 0x7fffffff) return -1; + if (n < 1 || n > 0x7fffffff) + return -1; return (int)n; } /* 函数内跳转:只改 PC 的 [irseq],帧不变。目标非法 → RuntimeError,返回 -1。 */ -static int jump_to(kvlangKv_t *kv, const char *vtid, const char *pc, const kvlangParam_t *target, const char *op) { +static int jump_to(kvlangKv_t *kv, const char *vtid, const char *pc, + const kvlangParam_t *target, const char *op) { int irseq = irseq_of(target); if (irseq < 0) { char msg[256]; - snprintf(msg, sizeof msg, "RuntimeError: %s target is not an int64 irseq: %s (kind=%s)", - op, target->name ? target->name : "", kvlangXvalueKind(&target->val)); + snprintf(msg, sizeof msg, + "RuntimeError: %s target is not an int64 irseq: %s (kind=%s)", + op, target->name ? target->name : "", + kvlangXvalueKind(&target->val)); kvlangVthreadSetError(kv, vtid, pc, msg); return -1; } @@ -124,9 +143,11 @@ static int jump_to(kvlangKv_t *kv, const char *vtid, const char *pc, const kvlan } static bool is_literal(const char *s) { - if (!s || !s[0]) return false; - return s[0] == '"' || s[0] == '/' || strcmp(s, "true") == 0 || strcmp(s, "false") == 0 || - strcmp(s, "null") == 0 || (s[0] >= '0' && s[0] <= '9') || (s[0] == '-' && s[1]); + if (!s || !s[0]) + return false; + return s[0] == '"' || s[0] == '/' || strcmp(s, "true") == 0 || + strcmp(s, "false") == 0 || strcmp(s, "null") == 0 || + (s[0] >= '0' && s[0] <= '9') || (s[0] == '-' && s[1]); } /* 派发期读参类型校验(runtime篇-07 第八节):把每个实参的 kind 逐一匹配 @@ -135,17 +156,20 @@ static bool is_literal(const char *s) { * XValue 头只携带 array_len 不含多维 shape,故仅校验 kind 层。 * 不匹配 → 置 TypeError,返回 -1;通过返回 0。 */ static int check_read_types(kvlangKv_t *kv, const char *vtid, const char *pc, - const char *opcode, const char *def_sig, int def_nr, int dynamic, - kvlangParam_t *args, int nargs) { - if (def_nr <= 0 || !def_sig || !*def_sig) return 0; + const char *opcode, const char *def_sig, int def_nr, + int dynamic, kvlangParam_t *args, int nargs) { + if (def_nr <= 0 || !def_sig || !*def_sig) + return 0; char *dup = strdup(def_sig); char *reads[128]; int rn = 0; - for (char *s = dup; rn < def_nr && rn < 128; ) { + for (char *s = dup; rn < def_nr && rn < 128;) { reads[rn++] = s; char *nl = strchr(s, '\n'); - if (!nl) break; - *nl = 0; s = nl + 1; + if (!nl) + break; + *nl = 0; + s = nl + 1; } bool var_last = rn > 0 && dynamic; int min_args = var_last ? rn - 1 : rn; @@ -153,7 +177,8 @@ static int check_read_types(kvlangKv_t *kv, const char *vtid, const char *pc, int rc = 0; if (nargs < min_args) { char msg[256]; - snprintf(msg, sizeof msg, "TypeError: %s expects %d args, got %d", opcode, min_args, nargs); + snprintf(msg, sizeof msg, "TypeError: %s expects %d args, got %d", + opcode, min_args, nargs); kvlangVthreadSetError(kv, vtid, pc, msg); rc = -1; } @@ -161,45 +186,59 @@ static int check_read_types(kvlangKv_t *kv, const char *vtid, const char *pc, const char *exp = i < rn ? reads[i] : (var_last ? reads[rn - 1] : NULL); if (!exp) { char msg[256]; - snprintf(msg, sizeof msg, "TypeError: %s expects %d args, got %d", opcode, rn, nargs); + snprintf(msg, sizeof msg, "TypeError: %s expects %d args, got %d", + opcode, rn, nargs); kvlangVthreadSetError(kv, vtid, pc, msg); rc = -1; break; } - if (!exp[0] || !kvlangLangtypeValid(exp)) continue; /* 动态/非法 kindexp 跳过 */ - kvlangXvalue_t v; kvlangXvalueZero(&v); + if (!exp[0] || !kvlangLangtypeValid(exp)) + continue; /* 动态/非法 kindexp 跳过 */ + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangBuiltinResolveReadValue(kv, fr, args[i].name, &args[i].val, &v); const char *k = kvlangXvalueKind(&v); - kvspaceHead_t h; kvlangXvalueHead(&v, &h); - kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); + kvspaceHead_t h; + kvlangXvalueHead(&v, &h); + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); bool ok = kvlangLangtypeMatch(exp, k, kx.ndim, kx.dims); - char kbuf[40]; snprintf(kbuf, sizeof kbuf, "%s", k[0] ? k : "None"); + char kbuf[40]; + snprintf(kbuf, sizeof kbuf, "%s", k[0] ? k : "None"); kvlangXvalueFree(&v); if (!ok) { char msg[256]; - snprintf(msg, sizeof msg, "TypeError: %s arg %d: expected %s, got %s", opcode, i + 1, exp, kbuf); + snprintf(msg, sizeof msg, + "TypeError: %s arg %d: expected %s, got %s", opcode, i + 1, + exp, kbuf); kvlangVthreadSetError(kv, vtid, pc, msg); rc = -1; } } - free(fr); free(dup); + free(fr); + free(dup); return rc; } /* 读签名行 [0,x] 槽(def langtype)的 body 为 langtype 串(malloc;无槽返 NULL)。 * dir 带尾 /;x<0 读参、x>0 写参。 */ static char *read_sig_slot(kvlangKv_t *kv, const char *dir, int x) { - kvlangStrbuf_t sk; kvlangStrbufInit(&sk); + kvlangStrbuf_t sk; + kvlangStrbufInit(&sk); kvlangStrbufPrintf(&sk, "%s[0,%d]", dir, x); - kvlangXvalue_t v; kvlangXvalueZero(&v); + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangKvGetOne(kv, sk.p, &v); kvlangStrbufFree(&sk); char *s = NULL; if (!kvlangXvalueNone(&v)) { - kvspaceHead_t h; kvlangXvalueHead(&v, &h); - int32_t bl; const uint8_t *b = kvlangXvalueBody(&v, &h, &bl); + kvspaceHead_t h; + kvlangXvalueHead(&v, &h); + int32_t bl; + const uint8_t *b = kvlangXvalueBody(&v, &h, &bl); s = malloc((size_t)bl + 1); - memcpy(s, b, (size_t)bl); s[bl] = 0; + memcpy(s, b, (size_t)bl); + s[bl] = 0; } kvlangXvalueFree(&v); return s; @@ -207,9 +246,11 @@ static char *read_sig_slot(kvlangKv_t *kv, const char *dir, int x) { /* 拼读参签名(\n 连接 [0,-1..-nr] 各 def langtype 槽)。dir 带尾 /。malloc 返回。 */ static char *join_read_sig(kvlangKv_t *kv, const char *dir, int nr) { - kvlangStrbuf_t b; kvlangStrbufInit(&b); + kvlangStrbuf_t b; + kvlangStrbufInit(&b); for (int i = 1; i <= nr; i++) { - if (i > 1) kvlangStrbufPutc(&b, '\n'); + if (i > 1) + kvlangStrbufPutc(&b, '\n'); char *s = read_sig_slot(kv, dir, -i); kvlangStrbufPuts(&b, s ? s : ""); free(s); @@ -220,19 +261,30 @@ static char *join_read_sig(kvlangKv_t *kv, const char *dir, int nr) { /* 读取 rwir/rwfunc 定义的读参签名:从主槽计数头取 nr/dynamic,读参 langtype 逐条 * 落在签名行 [0,-i] 槽(def langtype)。返回 \n 连接的 kindexp-list(调用方 free) * 并置 *out_nr;无定义返回 NULL。 */ -static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, int *out_dyn) { +static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, + int *out_dyn) { *out_nr = 0; *out_dyn = 0; - kvlangXvalue_t v; kvlangXvalueZero(&v); + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangKvGetOne(kv, key, &v); - if (kvlangXvalueNone(&v)) { kvlangXvalueFree(&v); return NULL; } - kvspaceHead_t h; kvlangXvalueHead(&v, &h); - int32_t bl; const uint8_t *b = kvlangXvalueBody(&v, &h, &bl); - if (bl < 5) { kvlangXvalueFree(&v); return NULL; } + if (kvlangXvalueNone(&v)) { + kvlangXvalueFree(&v); + return NULL; + } + kvspaceHead_t h; + kvlangXvalueHead(&v, &h); + int32_t bl; + const uint8_t *b = kvlangXvalueBody(&v, &h, &bl); + if (bl < 5) { + kvlangXvalueFree(&v); + return NULL; + } *out_nr = b[0] | (b[1] << 8); *out_dyn = b[4]; kvlangXvalueFree(&v); - kvlangStrbuf_t dir; kvlangStrbufInit(&dir); + kvlangStrbuf_t dir; + kvlangStrbufInit(&dir); kvlangStrbufPrintf(&dir, "%s/", key); char *sig = join_read_sig(kv, dir.p, *out_nr); kvlangStrbufFree(&dir); @@ -240,12 +292,17 @@ static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, int *o } static char *frame_slot_key(const char *frame_root, const char *slot) { - if (!slot || !slot[0]) return NULL; - if (slot[0] == '/') return strdup(slot); - if (strncmp(slot, MEMBER_SEP, MEMBER_SEP_LEN) == 0) return NULL; - kvlangStrbuf_t b; kvlangStrbufInit(&b); + if (!slot || !slot[0]) + return NULL; + if (slot[0] == '/') + return strdup(slot); + if (strncmp(slot, MEMBER_SEP, MEMBER_SEP_LEN) == 0) + return NULL; + kvlangStrbuf_t b; + kvlangStrbufInit(&b); char *stk = kvlangKeytreeStack(frame_root); - kvlangStrbufPuts(&b, stk); free(stk); + kvlangStrbufPuts(&b, stk); + free(stk); kvlangStrbufPuts(&b, slot); return kvlangStrbufDetach(&b); } @@ -253,13 +310,17 @@ static char *frame_slot_key(const char *frame_root, const char *slot) { /* 实参名 → 其存储键(写入被调帧 [0,-k]/[0,k])。字面量返回 NULL。 * *[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; +static char *resolve_read_path(kvlangKv_t *kv, const char *frame_root, + const char *name) { + if (is_literal(name)) + return NULL; if (name[0] == '*') { char *stk = kvlangKeytreeStack(frame_root); - kvlangXvalue_t pv; kvlangXvalueZero(&pv); + kvlangXvalue_t pv; + kvlangXvalueZero(&pv); kvlangKvGetMember(kv, stk, name + 1, &pv); - char *result = kvlangXvalueIsPtr(&pv) ? kvlangXvaluePtrTarget(&pv) : NULL; + char *result = + kvlangXvalueIsPtr(&pv) ? kvlangXvaluePtrTarget(&pv) : NULL; kvlangXvalueFree(&pv); free(stk); return result; @@ -269,24 +330,32 @@ static char *resolve_read_path(kvlangKv_t *kv, const char *frame_root, const cha /* return:弹出当前帧 [d]。d==1 → 顶层结束(*out_next=NULL);否则 *out_next=‥returnpc。 * 返回链断裂(帧无 ‥returnpc)→ RuntimeError(#109),保留该帧供排查,返回 -1。 */ -static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, char **out_next) { +static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, + char **out_next) { *out_next = NULL; char *fr = kvlangKeytreeFrameRoot(pc); int d = kvlangKeytreeFrameNum(fr); char *next = NULL; if (d > 1) { - kvlangStrbuf_t rk; kvlangStrbufInit(&rk); + kvlangStrbuf_t rk; + kvlangStrbufInit(&rk); kvlangKeytreeFrameReturnpc(fr, &rk); - kvlangXvalue_t v; kvlangXvalueZero(&v); + kvlangXvalue_t v; + kvlangXvalueZero(&v); kvlangKvGetOne(kv, rk.p, &v); - if (!kvlangXvalueNone(&v)) next = kvlangXvalueValueString(&v); + if (!kvlangXvalueNone(&v)) + next = kvlangXvalueValueString(&v); kvlangXvalueFree(&v); kvlangStrbufFree(&rk); if (!next || !next[0]) { char msg[512]; - snprintf(msg, sizeof msg, "RuntimeError: broken return chain: frame %s has no returnpc (pc=%s)", fr, pc); + snprintf(msg, sizeof msg, + "RuntimeError: broken return chain: frame %s has no " + "returnpc (pc=%s)", + fr, pc); kvlangVthreadSetError(kv, vtid, pc, msg); - free(next); free(fr); + free(next); + free(fr); return -1; } } @@ -294,16 +363,20 @@ static int handle_return(kvlangKv_t *kv, const char *vtid, const char *pc, char char err[256]; kvlangKvDelExtIndex(kv, stk, err, sizeof err); kvlangKvDelTree(kv, fr, err, sizeof err); - free(stk); free(fr); + free(stk); + free(fr); *out_next = next; return 0; } -static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, char *lt, size_t cap); +static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, + char *lt, size_t cap); /* HandleCall:创建子帧。返回 EntryPC(frameRoot),失败 NULL */ -static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) { - kvlangStrbuf_t vtid_b; kvlangStrbufInit(&vtid_b); +static char *handle_call(kvlangKv_t *kv, const char *pc, + kvlangRwirInst_t *inst) { + kvlangStrbuf_t vtid_b; + kvlangStrbufInit(&vtid_b); const char *vtid = kvlangKeytreeVtidFromPc(pc, &vtid_b); const char *fn = inst->reads[0].name; char *pkg = strdup(""); @@ -312,42 +385,65 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) if (strncmp(fn, lp, 5) == 0) { const char *rest = fn + 5; const char *dot = rfind_sep(rest); - if (dot) { free(pkg); pkg = strndup(rest, (size_t)(dot - rest)); free(name); name = strdup(dot + MEMBER_SEP_LEN); } - else { free(name); name = strdup(rest); } + if (dot) { + free(pkg); + pkg = strndup(rest, (size_t)(dot - rest)); + free(name); + name = strdup(dot + MEMBER_SEP_LEN); + } else { + free(name); + name = strdup(rest); + } } else { const char *dot = rfind_sep(fn); - if (dot) { free(pkg); pkg = strndup(fn, (size_t)(dot - fn)); free(name); name = strdup(dot + MEMBER_SEP_LEN); } - else { + if (dot) { + free(pkg); + pkg = strndup(fn, (size_t)(dot - fn)); + free(name); + name = strdup(dot + MEMBER_SEP_LEN); + } else { /* 裸名调用(无 /lib/ 无 ·):同 pkg 优先——当前函数所在 lib 下有同名 rwfunc 就用之 * (lib aaa/bbb/math 内 sum(A,A) → /lib/aaa/bbb/math·sum),否则退回根 /lib/。 */ char *ff = kvlangKeytreeFrameRoot(pc); if (ff) { /* 函数目录在帧的 ‥lib 槽(/lib/aaa/bbb/math·double/):由此取调用者 pkg。 */ - kvlangStrbuf_t lk; kvlangStrbufInit(&lk); + kvlangStrbuf_t lk; + kvlangStrbufInit(&lk); char *stk = kvlangKeytreeStack(ff); - kvlangStrbufPuts(&lk, stk); free(stk); + kvlangStrbufPuts(&lk, stk); + free(stk); kvlangStrbufPuts(&lk, SEG_LIB); - kvlangXvalue_t lv; kvlangXvalueZero(&lv); + kvlangXvalue_t lv; + kvlangXvalueZero(&lv); kvlangKvGetOne(kv, lk.p, &lv); kvlangStrbufFree(&lk); - char *funcdir = kvlangXvalueNone(&lv) ? NULL : kvlangXvalueValueString(&lv); + char *funcdir = + kvlangXvalueNone(&lv) ? NULL : kvlangXvalueValueString(&lv); kvlangXvalueFree(&lv); if (funcdir) { char *rel = funcdir + 5; // 剥 /lib/ size_t rl = strlen(rel); - if (rl > 0 && rel[rl - 1] == '/') rel[rl - 1] = '\0'; // 剥尾 / + if (rl > 0 && rel[rl - 1] == '/') + rel[rl - 1] = '\0'; // 剥尾 / const char *sep = rfind_sep(rel); if (sep) { char *cand_pkg = strndup(rel, (size_t)(sep - rel)); char *cand = kvlangKeytreeLibFunc(cand_pkg, fn); - kvlangStrbuf_t sk; kvlangStrbufInit(&sk); + kvlangStrbuf_t sk; + kvlangStrbufInit(&sk); kvlangStrbufPrintf(&sk, "%s/[0,0]", cand); - kvlangXvalue_t sv; kvlangXvalueZero(&sv); + kvlangXvalue_t sv; + kvlangXvalueZero(&sv); kvlangKvGetOne(kv, sk.p, &sv); - bool ok = !kvlangXvalueNone(&sv) && kvlangXvalueKindIs(&sv, KVSPACE_KIND_RWFUNC); - kvlangXvalueFree(&sv); kvlangStrbufFree(&sk); - if (ok) { free(pkg); pkg = cand_pkg; } - else free(cand_pkg); + bool ok = !kvlangXvalueNone(&sv) && + kvlangXvalueKindIs(&sv, KVSPACE_KIND_RWFUNC); + kvlangXvalueFree(&sv); + kvlangStrbufFree(&sk); + if (ok) { + free(pkg); + pkg = cand_pkg; + } else + free(cand_pkg); free(cand); } free(funcdir); @@ -357,31 +453,42 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) } } char *func_key = kvlangKeytreeLibFunc(pkg, name); - kvlangStrbuf_t func_dir; kvlangStrbufInit(&func_dir); - kvlangStrbufPuts(&func_dir, func_key); kvlangStrbufPutc(&func_dir, '/'); + kvlangStrbuf_t func_dir; + kvlangStrbufInit(&func_dir); + kvlangStrbufPuts(&func_dir, func_key); + kvlangStrbufPutc(&func_dir, '/'); - kvlangStrbuf_t sig_key; kvlangStrbufInit(&sig_key); + kvlangStrbuf_t sig_key; + kvlangStrbufInit(&sig_key); kvlangStrbufPrintf(&sig_key, "%s[0,0]", func_dir.p); - kvlangXvalue_t sig; kvlangXvalueZero(&sig); + kvlangXvalue_t sig; + kvlangXvalueZero(&sig); kvlangKvGetOne(kv, sig_key.p, &sig); - if (kvlangXvalueNone(&sig) || !kvlangXvalueKindIs(&sig, KVSPACE_KIND_RWFUNC)) { + if (kvlangXvalueNone(&sig) || + !kvlangXvalueKindIs(&sig, KVSPACE_KIND_RWFUNC)) { /* 按 xvalue 的 kind 精确区分缺 rwir 还是缺 rwfunc: * 到这里说明 opcode 已被 notinmyrwircaps 判否(/lib/ 非 def rwir 路由头)。 */ char *rk = kvlangKeytreeRwir(fn); - kvlangXvalue_t rv; kvlangXvalueZero(&rv); + kvlangXvalue_t rv; + kvlangXvalueZero(&rv); kvlangKvGetOne(kv, rk, &rv); char msg[256]; - if (!kvlangXvalueNone(&rv) && kvlangXvalueKindIs(&rv, KVSPACE_KIND_DEF_RWIR)) - snprintf(msg, sizeof msg, "NameError: rwir 未注册/签名不匹配: %s", fn); + if (!kvlangXvalueNone(&rv) && + kvlangXvalueKindIs(&rv, KVSPACE_KIND_DEF_RWIR)) + snprintf(msg, sizeof msg, "NameError: rwir 未注册/签名不匹配: %s", + fn); else if (!kvlangXvalueNone(&sig)) - snprintf(msg, sizeof msg, "NameError: %s 不是 rwfunc (kind=%s)", fn, kvlangXvalueKind(&sig)); + snprintf(msg, sizeof msg, "NameError: %s 不是 rwfunc (kind=%s)", fn, + kvlangXvalueKind(&sig)); else snprintf(msg, sizeof msg, "NameError: rwfunc not found: %s", fn); - kvlangXvalueFree(&rv); free(rk); + kvlangXvalueFree(&rv); + free(rk); kvlangVthreadSetError(kv, vtid, pc, msg); goto fail; } - kvspaceHead_t h; kvspaceDecodeHead(sig.data, sig.len, &h); + kvspaceHead_t h; + kvspaceDecodeHead(sig.data, sig.len, &h); const uint8_t *sbody = sig.data + h.body_offset; int nr = sbody[0] | (sbody[1] << 8); int nw = sbody[2] | (sbody[3] << 8); @@ -401,49 +508,75 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err); /* 系统变量 */ - kvlangStrbuf_t npc; kvlangStrbufInit(&npc); kvlangRwirNextPc(pc, &npc); - kvlangStrbuf_t retpc; kvlangStrbufInit(&retpc); kvlangKeytreeFrameReturnpc(frame_root, &retpc); - kvlangStrbuf_t callpc; kvlangStrbufInit(&callpc); kvlangKeytreeFrameCallpc(frame_root, &callpc); + kvlangStrbuf_t npc; + kvlangStrbufInit(&npc); + kvlangRwirNextPc(pc, &npc); + kvlangStrbuf_t retpc; + kvlangStrbufInit(&retpc); + kvlangKeytreeFrameReturnpc(frame_root, &retpc); + kvlangStrbuf_t callpc; + kvlangStrbufInit(&callpc); + kvlangKeytreeFrameCallpc(frame_root, &callpc); char *ep = kvlangKeytreeEntryPc(frame_root); - kvlangStrbuf_t seglib; kvlangStrbufInit(&seglib); kvlangStrbufPuts(&seglib, stack_fr); kvlangStrbufPuts(&seglib, SEG_LIB); - kvlangXvalue_t v_npc, v_ep, v_fn; kvlangXvalueZero(&v_npc); kvlangXvalueZero(&v_ep); kvlangXvalueZero(&v_fn); + kvlangStrbuf_t seglib; + kvlangStrbufInit(&seglib); + kvlangStrbufPuts(&seglib, stack_fr); + kvlangStrbufPuts(&seglib, SEG_LIB); + kvlangXvalue_t v_npc, v_ep, v_fn; + kvlangXvalueZero(&v_npc); + kvlangXvalueZero(&v_ep); + kvlangXvalueZero(&v_fn); kvlangXvalueNewCharUtf8(&v_npc, npc.p); kvlangXvalueNewCharUtf8(&v_ep, ep); kvlangXvalueNewCharUtf8(&v_fn, func_key); - kvlangKvPair_t sys[3] = { { retpc.p, v_npc }, { callpc.p, v_ep }, { seglib.p, v_fn } }; + kvlangKvPair_t sys[3] = { + {retpc.p, v_npc}, {callpc.p, v_ep}, {seglib.p, v_fn}}; kvlangKvSet(kv, sys, 3, err, sizeof err); - kvlangXvalueFree(&v_npc); kvlangXvalueFree(&v_ep); kvlangXvalueFree(&v_fn); + kvlangXvalueFree(&v_npc); + kvlangXvalueFree(&v_ep); + kvlangXvalueFree(&v_fn); /* 读参 + 写参 */ - kvlangKvPair_t pairs[512]; int np = 0; + kvlangKvPair_t pairs[512]; + int np = 0; int lit_seq = 0; for (int i = 0; i < nr; i++) { - kvlangStrbuf_t slot; kvlangStrbufInit(&slot); + kvlangStrbuf_t slot; + kvlangStrbufInit(&slot); kvlangStrbufPrintf(&slot, "%s/[0,-%d]", frame_root, i + 1); if (i + 1 < inst->nr) { kvlangParam_t *arg = &inst->reads[i + 1]; char *rk = resolve_read_path(kv, caller_fr, arg->name); - bool concrete = !kvlangXvalueNone(&arg->val) && !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWIR) && !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWFUNC); + bool concrete = !kvlangXvalueNone(&arg->val) && + !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWIR) && + !kvlangXvalueKindIs(&arg->val, KVSPACE_KIND_RWFUNC); char lt[256] = {0}; param_decl_type(kv, func_key, -(i + 1), lt, sizeof lt); if (concrete) { /* 字面量无变量槽,一律写 ._litN;勿沿用 resolve_read_path 的返回值—— * 否则字面量内容(如 "https://x" 里的 //)会被当路径段,二次读回即丢。 */ - if (rk) free(rk); - kvlangStrbuf_t lk; kvlangStrbufInit(&lk); + if (rk) + free(rk); + kvlangStrbuf_t lk; + kvlangStrbufInit(&lk); kvlangStrbufPrintf(&lk, "%s/._lit%d", caller_fr, lit_seq++); rk = kvlangStrbufDetach(&lk); /* 写字面量到 rk(拷贝,避免 double-free) */ - kvspaceHead_t ah; kvspaceDecodeHead(arg->val.data, arg->val.len, &ah); - int32_t abl; const uint8_t *ab = kvlangXvalueBody(&arg->val, &ah, &abl); - kvlangLangtype akx; kvlangLangtypeParse(ah.langtype, &akx); + kvspaceHead_t ah; + kvspaceDecodeHead(arg->val.data, arg->val.len, &ah); + 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); + kvspaceTlvEncode(kvlangXvalueKind(&arg->val), ab, (uint32_t)abl, + akx.dims, akx.ndim, &pairs[np].val.data, + &pairs[np].val.len); np++; } if (rk) { - kvlangXvalue_t rv; kvlangXvalueNewPtr(&rv, lt, rk); + kvlangXvalue_t rv; + kvlangXvalueNewPtr(&rv, lt, rk); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = rv; np++; @@ -453,14 +586,16 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) kvlangStrbufFree(&slot); } for (int i = 0; i < nw; i++) { - kvlangStrbuf_t slot; kvlangStrbufInit(&slot); + kvlangStrbuf_t slot; + kvlangStrbufInit(&slot); kvlangStrbufPrintf(&slot, "%s/[0,%d]", frame_root, i + 1); if (i < inst->nw) { char *wk = resolve_read_path(kv, caller_fr, inst->writes[i].name); if (wk) { char lt[256] = {0}; param_decl_type(kv, func_key, i + 1, lt, sizeof lt); - kvlangXvalue_t wv; kvlangXvalueNewPtr(&wv, lt, wk); + kvlangXvalue_t wv; + kvlangXvalueNewPtr(&wv, lt, wk); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = wv; np++; @@ -472,42 +607,73 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) /* 实参绑定写失败必须报错——绝不静默:丢的是一整个参数,症状会漂到很远的地方 * (曾表现为 fs 后端下函数读到空参数)。 */ int wrc = np > 0 ? kvlangKvSet(kv, pairs, np, err, sizeof err) : 0; - for (int i = 0; i < np; i++) { free(pairs[i].key); kvlangXvalueFree(&pairs[i].val); } + for (int i = 0; i < np; i++) { + free(pairs[i].key); + kvlangXvalueFree(&pairs[i].val); + } if (wrc != 0) { kvlangVthreadSetError(kv, vtid, pc, err); goto fail; } - free(caller_fr); free(stack_fr); - kvlangStrbufFree(&npc); kvlangStrbufFree(&retpc); kvlangStrbufFree(&callpc); kvlangStrbufFree(&seglib); - kvlangStrbufFree(&func_dir); kvlangStrbufFree(&sig_key); kvlangStrbufFree(&vtid_b); - kvlangXvalueFree(&sig); free(func_key); free(pkg); free(name); + free(caller_fr); + free(stack_fr); + kvlangStrbufFree(&npc); + kvlangStrbufFree(&retpc); + kvlangStrbufFree(&callpc); + kvlangStrbufFree(&seglib); + kvlangStrbufFree(&func_dir); + kvlangStrbufFree(&sig_key); + kvlangStrbufFree(&vtid_b); + kvlangXvalueFree(&sig); + free(func_key); + free(pkg); + free(name); free(frame_root); return ep; fail: - kvlangStrbufFree(&func_dir); kvlangStrbufFree(&sig_key); kvlangStrbufFree(&vtid_b); - kvlangXvalueFree(&sig); free(func_key); free(pkg); free(name); + kvlangStrbufFree(&func_dir); + kvlangStrbufFree(&sig_key); + kvlangStrbufFree(&vtid_b); + kvlangXvalueFree(&sig); + free(func_key); + free(pkg); + free(name); return NULL; } /* 帧槽实参 Ptr 的类型来自**形参声明**:读参数定义键 funcDir.[0,±x] 的 body * (名字\x00类型串),取出类型串。绝不从实参值的 head 反推——实参可以是 None * (如递归传空子树),那时无值可取,Ptr 会因空 langtype 构造失败而整个参数丢失。 */ -static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, char *lt, size_t cap) { - kvlangStrbuf_t pk; kvlangStrbufInit(&pk); +static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, + char *lt, size_t cap) { + kvlangStrbuf_t pk; + kvlangStrbufInit(&pk); kvlangStrbufPrintf(&pk, "%s.[0,%d]", func_key, x); - kvlangXvalue_t dv; kvlangXvalueZero(&dv); + 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); + int32_t al; + const uint8_t *ab = kvlangXvalueBody(&dv, &ah, &al); for (int bi = 0; ab && bi < al; bi++) { - if (ab[bi] != 0) continue; - int tl = al - bi - 1; - if (tl > 0 && tl < (int)cap) { memcpy(lt, ab + bi + 1, (size_t)tl); lt[tl] = 0; } + if (ab[bi] != 0) + continue; + /* 声明的 `*T`/`@T` 只表引用性,**wire langtype 不带 `*`/`@` 前缀**(ref 归 head.ref)。 + * 剥掉再拿去构造实参 Ptr,否则 Ptr 目标类型串会多一个 `*` 而与真实值对不上 + * (曾致指针形参 validate_ptr 拒绝写入、帧槽整个参数丢失)。 */ + int s = bi + 1; + while (s < al && (ab[s] == '*' || ab[s] == '@')) + s++; + int tl = al - s; + if (tl > 0 && tl < (int)cap) { + memcpy(lt, ab + s, (size_t)tl); + lt[tl] = 0; + } break; } } @@ -517,7 +683,8 @@ static void param_decl_type(kvlangKv_t *kv, const char *func_key, int x, char *l int kvlangCtlCall(kvlangFrame_t *f) { char *sub = handle_call(f->kv, f->pc, f->inst); - if (!sub) return -1; + if (!sub) + return -1; kvlangVthreadSet(f->kv, f->vtid, sub, "running"); free(sub); return 0; @@ -525,8 +692,12 @@ int kvlangCtlCall(kvlangFrame_t *f) { int kvlangCtlReturn(kvlangFrame_t *f) { char *parent = NULL; - if (handle_return(f->kv, f->vtid, f->pc, &parent) != 0) return -1; - if (!parent) { kvlangVthreadSetDone(f->kv, f->vtid, "ok"); return 0; } + if (handle_return(f->kv, f->vtid, f->pc, &parent) != 0) + return -1; + if (!parent) { + kvlangVthreadSetDone(f->kv, f->vtid, "ok"); + return 0; + } kvlangVthreadSet(f->kv, f->vtid, parent, "running"); free(parent); return 0; @@ -535,7 +706,9 @@ int kvlangCtlReturn(kvlangFrame_t *f) { int kvlangCtlGoto(kvlangFrame_t *f) { kvlangRwirInst_t *inst = f->inst; if (inst->nr != 1) { - char msg[128]; snprintf(msg, sizeof msg, "RuntimeError: goto expects 1 irseq, got %d", inst->nr); + char msg[128]; + snprintf(msg, sizeof msg, "RuntimeError: goto expects 1 irseq, got %d", + inst->nr); kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg); return -1; } @@ -545,21 +718,30 @@ int kvlangCtlGoto(kvlangFrame_t *f) { int kvlangCtlBr(kvlangFrame_t *f) { kvlangRwirInst_t *inst = f->inst; if (inst->nr != 3) { - char msg[128]; snprintf(msg, sizeof msg, "RuntimeError: br expects cond trueIrseq falseIrseq, got %d", inst->nr); + char msg[128]; + snprintf(msg, sizeof msg, + "RuntimeError: br expects cond trueIrseq falseIrseq, got %d", + inst->nr); kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg); return -1; } char *fr = kvlangKeytreeFrameRoot(f->pc); - kvlangXvalue_t cond; kvlangXvalueZero(&cond); - kvlangBuiltinResolveReadValue(f->kv, fr, inst->reads[0].name, &inst->reads[0].val, &cond); + kvlangXvalue_t cond; + kvlangXvalueZero(&cond); + kvlangBuiltinResolveReadValue(f->kv, fr, inst->reads[0].name, + &inst->reads[0].val, &cond); free(fr); if (kvlangXvalueNone(&cond)) { - kvlangVthreadSetError(f->kv, f->vtid, f->pc, "TypeError: None in branch condition"); + kvlangVthreadSetError(f->kv, f->vtid, f->pc, + "TypeError: None in branch condition"); kvlangXvalueFree(&cond); return -1; } if (!kvlangXvalueKindIs(&cond, KVSPACE_KIND_BOOL)) { - char msg[128]; snprintf(msg, sizeof msg, "TypeError: branch condition must be bool, got %s", kvlangXvalueKind(&cond)); + char msg[128]; + snprintf(msg, sizeof msg, + "TypeError: branch condition must be bool, got %s", + kvlangXvalueKind(&cond)); kvlangVthreadSetError(f->kv, f->vtid, f->pc, msg); kvlangXvalueFree(&cond); return -1; @@ -571,7 +753,8 @@ int kvlangCtlBr(kvlangFrame_t *f) { /* 动态调用:以运行时得到的 funckey 在当前 vthread 造一次 OP_CALL(不新开 vid), * pc 落在被调入口,帧结束回到本指令 NextPc。供 native vthread·call 用。 */ -int kvlangKvcpuDynCall(kvlangKv_t *kv, const char *vtid, const char *pc, const char *funckey) { +int kvlangKvcpuDynCall(kvlangKv_t *kv, const char *vtid, const char *pc, + const char *funckey) { kvlangRwirInst_t ci; ci.opcode = strdup(OP_CALL); ci.op_id = 0; @@ -581,60 +764,88 @@ int kvlangKvcpuDynCall(kvlangKv_t *kv, const char *vtid, const char *pc, const c ci.nr = 1; ci.writes = NULL; ci.nw = 0; - kvlangFrame_t f = { kv, vtid, pc, &ci, NULL }; + kvlangFrame_t f = {kv, vtid, pc, &ci, NULL}; int rc = kvlangCtlCall(&f); - free(ci.opcode); free(ci.reads[0].name); free(ci.reads); + free(ci.opcode); + free(ci.reads[0].name); + free(ci.reads); return rc; } -int handoff_external_rwir(kvlangKv_t *kv, const char *vtid, const char *pc, kvlangRwirInst_t *inst) { +int handoff_external_rwir(kvlangKv_t *kv, const char *vtid, const char *pc, + kvlangRwirInst_t *inst) { /* handoff:把 pc 挂到共享队列 /lib//vids/(各 rwir 的 vids 已 Ptr 统一到 * 第一个 rwir 的 vids 下,Set 经路径穿透落到同一 strkeymap)。外部执行器认领并驱动该 vthread, * 完成后删除该条目。本端 watch 同一 key 直至变 None(== 认领方已完成),单键交接、无 id。 */ char *base = kvlangKeytreeRwir(inst->opcode); - kvlangStrbuf_t vids; kvlangStrbufInit(&vids); + kvlangStrbuf_t vids; + kvlangStrbufInit(&vids); kvlangStrbufPrintf(&vids, "%s/vids/%s", base, vtid); - kvlangXvalue_t pv; kvlangXvalueNewCharUtf8(&pv, pc); - kvlangKvPair_t p = { vids.p, pv }; + kvlangXvalue_t pv; + kvlangXvalueNewCharUtf8(&pv, pc); + kvlangKvPair_t p = {vids.p, pv}; char err[256]; kvlangKvSet(kv, &p, 1, err, sizeof err); kvlangXvalueFree(&pv); - kvlangXvalue_t none; kvlangXvalueZero(&none); /* 目标 None:等条目被删除 */ - kvlangXvalue_t got; kvlangXvalueZero(&got); + kvlangXvalue_t none; + kvlangXvalueZero(&none); /* 目标 None:等条目被删除 */ + kvlangXvalue_t got; + kvlangXvalueZero(&got); int rc = kvlangKvWatch(kv, vids.p, &none, 30000000000ULL, &got); kvlangXvalueFree(&got); - kvlangStrbufFree(&vids); free(base); + kvlangStrbufFree(&vids); + free(base); if (rc != 0) { - char msg[256]; snprintf(msg, sizeof msg, "RuntimeError: external rwir %s handoff failed", inst->opcode); + char msg[256]; + snprintf(msg, sizeof msg, + "RuntimeError: external rwir %s handoff failed", inst->opcode); kvlangVthreadSetError(kv, vtid, pc, msg); return -1; } return 0; } -char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, const char *funcname, - const char *const *args, int nargs) { +char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, + const char *funcname, const char *const *args, + int nargs) { char *pkg = strdup(""); char *name = strdup(funcname); const char *dot = rfind_sep(funcname); - if (dot) { free(pkg); pkg = strndup(funcname, (size_t)(dot - funcname)); free(name); name = strdup(dot + MEMBER_SEP_LEN); } + if (dot) { + free(pkg); + pkg = strndup(funcname, (size_t)(dot - funcname)); + free(name); + name = strdup(dot + MEMBER_SEP_LEN); + } char *func_key = kvlangKeytreeLibFunc(pkg, name); - kvlangStrbuf_t func_dir; kvlangStrbufInit(&func_dir); - kvlangStrbufPuts(&func_dir, func_key); kvlangStrbufPutc(&func_dir, '/'); + kvlangStrbuf_t func_dir; + kvlangStrbufInit(&func_dir); + kvlangStrbufPuts(&func_dir, func_key); + kvlangStrbufPutc(&func_dir, '/'); - kvlangStrbuf_t sig_key; kvlangStrbufInit(&sig_key); + kvlangStrbuf_t sig_key; + kvlangStrbufInit(&sig_key); kvlangStrbufPrintf(&sig_key, "%s[0,0]", func_dir.p); - kvlangXvalue_t sig; kvlangXvalueZero(&sig); + kvlangXvalue_t sig; + kvlangXvalueZero(&sig); kvlangKvGetOne(kv, sig_key.p, &sig); - if (kvlangXvalueNone(&sig) || !kvlangXvalueKindIs(&sig, KVSPACE_KIND_RWFUNC)) { - char msg[256]; snprintf(msg, sizeof msg, "Bootstrap: rwir/rwfunc not found: %s", funcname); + if (kvlangXvalueNone(&sig) || + !kvlangXvalueKindIs(&sig, KVSPACE_KIND_RWFUNC)) { + char msg[256]; + snprintf(msg, sizeof msg, "Bootstrap: rwir/rwfunc not found: %s", + funcname); kvlangVthreadSetError(kv, vtid, "", msg); - kvlangXvalueFree(&sig); kvlangStrbufFree(&sig_key); kvlangStrbufFree(&func_dir); - free(func_key); free(pkg); free(name); + kvlangXvalueFree(&sig); + kvlangStrbufFree(&sig_key); + kvlangStrbufFree(&func_dir); + free(func_key); + free(pkg); + free(name); return NULL; } - kvspaceHead_t h; kvspaceDecodeHead(sig.data, sig.len, &h); + kvspaceHead_t h; + kvspaceDecodeHead(sig.data, sig.len, &h); const uint8_t *sbody = sig.data + h.body_offset; int nr = sbody[0] | (sbody[1] << 8); @@ -645,62 +856,100 @@ char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, const char *funcnam kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err); char *ep = kvlangKeytreeEntryPc(frame_root); - kvlangStrbuf_t callpc; kvlangStrbufInit(&callpc); kvlangKeytreeFrameCallpc(frame_root, &callpc); - kvlangStrbuf_t seglib; kvlangStrbufInit(&seglib); kvlangStrbufPuts(&seglib, stack_fr); kvlangStrbufPuts(&seglib, SEG_LIB); - kvlangXvalue_t v_ep, v_fn; kvlangXvalueZero(&v_ep); kvlangXvalueZero(&v_fn); + kvlangStrbuf_t callpc; + kvlangStrbufInit(&callpc); + kvlangKeytreeFrameCallpc(frame_root, &callpc); + kvlangStrbuf_t seglib; + kvlangStrbufInit(&seglib); + kvlangStrbufPuts(&seglib, stack_fr); + kvlangStrbufPuts(&seglib, SEG_LIB); + kvlangXvalue_t v_ep, v_fn; + kvlangXvalueZero(&v_ep); + kvlangXvalueZero(&v_fn); kvlangXvalueNewCharUtf8(&v_ep, ep); kvlangXvalueNewCharUtf8(&v_fn, func_key); - kvlangKvPair_t sys[2] = { { callpc.p, v_ep }, { seglib.p, v_fn } }; + kvlangKvPair_t sys[2] = {{callpc.p, v_ep}, {seglib.p, v_fn}}; kvlangKvSet(kv, sys, 2, err, sizeof err); - kvlangXvalueFree(&v_ep); kvlangXvalueFree(&v_fn); + kvlangXvalueFree(&v_ep); + kvlangXvalueFree(&v_fn); if (nargs > 0) { - kvlangKvPair_t pairs[128]; int np = 0; + kvlangKvPair_t pairs[128]; + int np = 0; for (int i = 0; i < nr && i < nargs; i++) { - kvlangStrbuf_t slot; kvlangStrbufInit(&slot); + kvlangStrbuf_t slot; + kvlangStrbufInit(&slot); kvlangStrbufPrintf(&slot, "%s/[0,-%d]", frame_root, i + 1); - kvlangXvalue_t av; kvlangXvalueZero(&av); + kvlangXvalue_t av; + kvlangXvalueZero(&av); kvlangBuiltinResolveReadValue(kv, "", args[i], NULL, &av); pairs[np].key = kvlangStrbufDetach(&slot); pairs[np].val = av; np++; } - if (np > 0) kvlangKvSet(kv, pairs, np, err, sizeof err); - for (int i = 0; i < np; i++) { free(pairs[i].key); kvlangXvalueFree(&pairs[i].val); } + if (np > 0) + kvlangKvSet(kv, pairs, np, err, sizeof err); + for (int i = 0; i < np; i++) { + free(pairs[i].key); + kvlangXvalueFree(&pairs[i].val); + } } - kvlangXvalueFree(&sig); kvlangStrbufFree(&sig_key); kvlangStrbufFree(&func_dir); - kvlangStrbufFree(&callpc); kvlangStrbufFree(&seglib); - free(stack_fr); free(frame_root); free(func_key); free(pkg); free(name); + kvlangXvalueFree(&sig); + kvlangStrbufFree(&sig_key); + kvlangStrbufFree(&func_dir); + kvlangStrbufFree(&callpc); + kvlangStrbufFree(&seglib); + free(stack_fr); + free(frame_root); + free(func_key); + free(pkg); + free(name); return ep; } -int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char **out_pc) { - if (out_pc) *out_pc = NULL; - kvlangStrbuf_t vtid_b; kvlangStrbufInit(&vtid_b); +int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, + char **out_pc) { + if (out_pc) + *out_pc = NULL; + kvlangStrbuf_t vtid_b; + kvlangStrbufInit(&vtid_b); const char *vtid = kvlangKeytreeVtidFromPc(pc, &vtid_b); - if (vtid[0] == 0) { kvlangStrbufFree(&vtid_b); return -1; } + if (vtid[0] == 0) { + kvlangStrbufFree(&vtid_b); + return -1; + } char *cur = strdup(pc); - char *cur_frame = NULL, *cur_funcdir = NULL; /* 帧不变时 funcdir 只读一次,供缓存键 */ + char *cur_frame = NULL, + *cur_funcdir = NULL; /* 帧不变时 funcdir 只读一次,供缓存键 */ int rc = 0; /* status 跨轮携带:尾部 VthreadGet 已连 pc 一并取出,下轮直接复用,省掉背靠背重读 * (vthread 记录在两次 get 之间不被改写;status 串由 ValueString 自持,跨 ReadReset 存活)。 */ char *status = NULL; - { char *pcv = NULL; kvlangVthreadGet(kv, vtid, &pcv, &status); free(pcv); } + { + char *pcv = NULL; + kvlangVthreadGet(kv, vtid, &pcv, &status); + free(pcv); + } for (;;) { /* 指令边界:回收上条指令执行期借出的读池(cache 指令的读参已 Materialize 自持,不受影响)。 * durable 惰性写不再清池,全靠此处回收;shm 常驻映射侧为 no-op。 */ kvlangKvReadReset(kv); - if (!status || (strcmp(status, "init") != 0 && strcmp(status, "running") != 0 && strcmp(status, "wait") != 0)) { + if (!status || + (strcmp(status, "init") != 0 && strcmp(status, "running") != 0 && + strcmp(status, "wait") != 0)) { break; } - free(status); status = NULL; + free(status); + status = NULL; int depth = kvlangKeytreeFrameNum(cur); if (depth > MAX_STACK_DEPTH) { char msg[256]; - snprintf(msg, sizeof msg, "RecursionError: stack overflow: depth=%d pc=%s", depth, cur); + snprintf(msg, sizeof msg, + "RecursionError: stack overflow: depth=%d pc=%s", depth, + cur); kvlangVthreadSetError(kv, vtid, cur, msg); rc = -1; break; @@ -708,25 +957,32 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char * char *fr = kvlangKeytreeFrameRoot(cur); if (!cur_frame || strcmp(cur_frame, fr) != 0) { - free(cur_frame); cur_frame = strdup(fr); - free(cur_funcdir); cur_funcdir = read_seglib(kv, fr); + free(cur_frame); + cur_frame = strdup(fr); + free(cur_funcdir); + cur_funcdir = read_seglib(kv, fr); } const char *lastc = NULL; - for (const char *p = cur; (p = strstr(p, "/[")) != NULL; p += 2) lastc = p; + for (const char *p = cur; (p = strstr(p, "/[")) != NULL; p += 2) + lastc = p; int addr0 = lastc ? kvlangRwirExtractAddr0(lastc + 1) : 0; kvlangRwirInst_t tmp; kvlangRwirInst_t *inst = NULL; bool tmp_owned = false; - if (cur_funcdir) inst = rwir_cache_get(cur_funcdir, addr0); + if (cur_funcdir) + inst = rwir_cache_get(cur_funcdir, addr0); if (!inst) { char *link_base = kvlangKeytreeStack(fr); char err[256]; - if (kvlangRwirDecode(kv, link_base, cur, &tmp, err, sizeof err) != 0) { - char msg[256]; snprintf(msg, sizeof msg, "decode: %s", err); + if (kvlangRwirDecode(kv, link_base, cur, &tmp, err, sizeof err) != + 0) { + char msg[256]; + snprintf(msg, sizeof msg, "decode: %s", err); kvlangVthreadSetError(kv, vtid, cur, msg); - free(link_base); free(fr); + free(link_base); + free(fr); rc = -1; break; } @@ -738,19 +994,25 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char * rwir_cache_put(cur_funcdir, addr0, persist); inst = persist; } else { - inst = &tmp; tmp_owned = true; + inst = &tmp; + tmp_owned = true; } } - kvlangLogDebug("[%s] PC=%s OP=%s R=%d W=%d", vtid, cur, inst->opcode ? inst->opcode : "(empty)", inst->nr, inst->nw); + kvlangLogDebug("[%s] PC=%s OP=%s R=%d W=%d", vtid, cur, + inst->opcode ? inst->opcode : "(empty)", inst->nr, + inst->nw); if (!inst->opcode || !inst->opcode[0]) { /* layout 对每条路径都补了 return(lower::terminate),走到空槽只能是 /lib 损坏 * 或 goto/br 越界;报 RuntimeError 让该 vthread 停下,不拖垮整个进程。 */ char msg[512]; - snprintf(msg, sizeof msg, "RuntimeError: no instruction at %s", cur); + snprintf(msg, sizeof msg, "RuntimeError: no instruction at %s", + cur); kvlangVthreadSetError(kv, vtid, cur, msg); - free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); + free(fr); + if (tmp_owned) + kvlangRwirInstFree(&tmp); rc = -1; break; } @@ -759,27 +1021,46 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char * char *yield = NULL; if (inst->op_id >= 0) { /* 单表派发:native 算子与 control/copy 同居 myrwircaps,op_id 直查一跳到底。 */ - kvlangFrame_t f = { kv, vtid, cur, inst, &yield }; + kvlangFrame_t f = {kv, vtid, cur, inst, &yield}; exec_err = kvlangBuiltinNative(&f); if (exec_err == 0 && yield) { /* native(vthread·run return 模式)冒泡一个子 vthread 的 rwir pc 给上层驱动。 * 本 vthread(主)pc 未推进,驱动派发子 rwir 并推进子 pc 后重入即续跑。 */ - if (out_pc) *out_pc = yield; else free(yield); - free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); - free(cur); free(cur_frame); free(cur_funcdir); free(status); kvlangStrbufFree(&vtid_b); + if (out_pc) + *out_pc = yield; + else + free(yield); + free(fr); + if (tmp_owned) + kvlangRwirInstFree(&tmp); + free(cur); + free(cur_frame); + free(cur_funcdir); + free(status); + kvlangStrbufFree(&vtid_b); return 1; } } else if (opmeta_get(kv, inst->opcode)->notinmyrwircaps) { opmeta_ent_t *m = opmeta_get(kv, inst->opcode); if (m->def_sig) - exec_err = check_read_types(kv, vtid, cur, inst->opcode, m->def_sig, m->def_nr, m->def_dyn, inst->reads, inst->nr); + exec_err = check_read_types(kv, vtid, cur, inst->opcode, + m->def_sig, m->def_nr, m->def_dyn, + inst->reads, inst->nr); if (exec_err == 0 && mode == KVMODE_RETURN) { - if (out_pc) *out_pc = strdup(cur); - free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); - free(cur); free(cur_frame); free(cur_funcdir); free(status); kvlangStrbufFree(&vtid_b); + if (out_pc) + *out_pc = strdup(cur); + free(fr); + if (tmp_owned) + kvlangRwirInstFree(&tmp); + free(cur); + free(cur_frame); + free(cur_funcdir); + free(status); + kvlangStrbufFree(&vtid_b); return 1; } - if (exec_err == 0) exec_err = handoff_external_rwir(kv, vtid, cur, inst); + if (exec_err == 0) + exec_err = handoff_external_rwir(kv, vtid, cur, inst); } else { /* 用户函数 → call */ kvlangRwirInst_t ci; @@ -789,31 +1070,50 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char * ci.nw = inst->nw; ci.reads = malloc(sizeof(kvlangParam_t) * (size_t)ci.nr); ci.reads[0].name = strdup(inst->opcode); - ci.reads[0].val.data = NULL; ci.reads[0].val.len = 0; - for (int i = 0; i < inst->nr; i++) { ci.reads[i + 1] = inst->reads[i]; } + ci.reads[0].val.data = NULL; + ci.reads[0].val.len = 0; + for (int i = 0; i < inst->nr; i++) { + ci.reads[i + 1] = inst->reads[i]; + } ci.writes = inst->writes; - kvlangFrame_t cf = { kv, vtid, cur, &ci, NULL }; + kvlangFrame_t cf = {kv, vtid, cur, &ci, NULL}; exec_err = kvlangCtlCall(&cf); - free(ci.opcode); free(ci.reads[0].name); free(ci.reads); + free(ci.opcode); + free(ci.reads[0].name); + free(ci.reads); } - if (exec_err != 0) { free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); rc = -1; break; } + if (exec_err != 0) { + free(fr); + if (tmp_owned) + kvlangRwirInstFree(&tmp); + rc = -1; + break; + } char *newpc = NULL; - kvlangVthreadGet(kv, vtid, &newpc, &status); /* status 连 pc 一并取出,供下轮直接复用 */ + kvlangVthreadGet(kv, vtid, &newpc, + &status); /* status 连 pc 一并取出,供下轮直接复用 */ free(fr); - if (tmp_owned) kvlangRwirInstFree(&tmp); - if (!newpc || !newpc[0]) { free(newpc); break; } + if (tmp_owned) + kvlangRwirInstFree(&tmp); + if (!newpc || !newpc[0]) { + free(newpc); + break; + } free(cur); cur = newpc; } - free(cur); free(cur_frame); free(cur_funcdir); free(status); + free(cur); + free(cur_frame); + free(cur_funcdir); + free(status); kvlangStrbufFree(&vtid_b); return rc; } int kvlangKvcpuExecute(kvlangKv_t *kv, const char *pc) { int rc = kvlangKvcpuExecuteMode(kv, pc, KVMODE_WATCH, NULL); - return rc == 1 ? 0 : rc; /* WATCH 模式不返回 1,防御性归一 */ + return rc == 1 ? 0 : rc; /* WATCH 模式不返回 1,防御性归一 */ } diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 37493e86..6b132a7a 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -1,80 +1,101 @@ #pragma once -#include -#include +#include "const.h" +#include #include -#include -#include +#include +#include #include -#include -#include "const.h" +#include +#include /* ── kvspace-durable C ABI ─────────────────────────────────────────── */ /* 三正交轴 head(逐字段对齐 kvspace/include/kvspace/kvspace.h 的 kvspaceHead_t)。 * langtype 即该 ABI 的 langtype 槽(本 runtime 内部沿用 langtype 命名)。 */ typedef struct { - uint16_t headlen; /* head 总字节数;body 起于偏移 headlen */ - uint8_t ref; /* 存储位置:见 KVSPACE_REF_* */ - uint8_t storetype; /* 物理布局:见 KVSPACE_STORETYPE_* */ - uint8_t ro; /* 1=只读,0=可写 */ - uint32_t vid; /* vthread id */ - int32_t body_len; /* body 字节数 */ - int32_t ndim; /* ARRAYND:维数;index/extindex:3;NONE/ATOM:0 */ - int32_t dims[8]; /* 各维长度 / [len,cap,M](X_MAX_NDIM=8) */ - uint8_t langtype[256];/* 语义类型 langtype 串,NUL 终止(含 [dims]、无 ref/ext 前缀) */ - int32_t langtype_len; /* langtype 内容长度(去 padding) */ - int32_t body_offset; /* body 在 data 内的起始偏移(= headlen) */ + uint16_t headlen; /* head 总字节数;body 起于偏移 headlen */ + uint8_t ref; /* 存储位置:见 KVSPACE_REF_* */ + uint8_t storetype; /* 物理布局:见 KVSPACE_STORETYPE_* */ + uint8_t ro; /* 1=只读,0=可写 */ + uint32_t vid; /* vthread id */ + int32_t body_len; /* body 字节数 */ + int32_t ndim; /* ARRAYND:维数;index/extindex:3;NONE/ATOM:0 */ + int32_t dims[8]; /* 各维长度 / [len,cap,M](X_MAX_NDIM=8) */ + uint8_t langtype + [256]; /* 语义类型 langtype 串,NUL 终止(含 [dims]、无 ref/ext 前缀) */ + int32_t langtype_len; /* langtype 内容长度(去 padding) */ + int32_t body_offset; /* body 在 data 内的起始偏移(= headlen) */ } kvspaceHead_t; #define KVSPACE_REF_INLINE 0 -#define KVSPACE_REF_PTR 1 -#define KVSPACE_REF_EXT 2 +#define KVSPACE_REF_PTR 1 +#define KVSPACE_REF_EXT 2 -#define KVSPACE_STORETYPE_NONE 0 -#define KVSPACE_STORETYPE_ATOM 1 -#define KVSPACE_STORETYPE_ARRAYND 2 -#define KVSPACE_STORETYPE_INDEX 3 +#define KVSPACE_STORETYPE_NONE 0 +#define KVSPACE_STORETYPE_ATOM 1 +#define KVSPACE_STORETYPE_ARRAYND 2 +#define KVSPACE_STORETYPE_INDEX 3 #define KVSPACE_STORETYPE_EXTINDEX 4 extern void *kvspaceConnect(const char *dsn); -extern void kvspaceClose(void *h); +extern void kvspaceClose(void *h); /* 借用读:*out 指向后端常驻/回收空间,调用方不得 free。resolve=1 穿透 link。 */ -extern int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, uint32_t *out_len); +extern int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, + uint32_t *out_len); /* 指令边界回收读借用池;定位读/写(分片);只读 head 前缀。见 kvspace.h 契约。 */ -extern void kvspaceReadReset(void *h); -extern int kvspaceGetPart(void *h, const char *key, uint32_t offset, uint32_t len, uint8_t **out, uint32_t *out_len); -extern int kvspaceSetPart(void *h, const char *key, uint32_t offset, const uint8_t *buf, uint32_t buf_len, char *err, uint32_t err_cap); -extern int kvspaceGetHead(void *h, const char *key, kvspaceHead_t *out); +extern void kvspaceReadReset(void *h); +extern int kvspaceGetPart(void *h, const char *key, uint32_t offset, + uint32_t len, uint8_t **out, uint32_t *out_len); +extern int kvspaceSetPart(void *h, const char *key, uint32_t offset, + const uint8_t *buf, uint32_t buf_len, char *err, + uint32_t err_cap); +extern int kvspaceGetHead(void *h, const char *key, kvspaceHead_t *out); /* 就地写:key 已存在、body_len==原 body_len → 返回原 box body 偏移指针;否则非 0 + err。 */ -extern int kvspaceWriteInPlace(void *h, const char *key, int resolve, uint32_t body_len, - uint8_t **body, char *err, uint32_t err_cap); +extern int kvspaceWriteInPlace(void *h, const char *key, int resolve, + uint32_t body_len, uint8_t **body, char *err, + uint32_t err_cap); /* 新位置写:按 (ref, storetype, ro, vid, langtype, body_len) 分配新 box、写 head,返回 body 偏移指针。 */ -extern int kvspaceWriteNewPlace(void *h, const char *key, uint8_t ref, uint8_t storetype, - uint8_t ro, uint32_t vid, const char *langtype, uint32_t body_len, - uint8_t **body, char *err, uint32_t err_cap); +extern int kvspaceWriteNewPlace(void *h, const char *key, uint8_t ref, + uint8_t storetype, uint8_t ro, uint32_t vid, + const char *langtype, uint32_t body_len, + uint8_t **body, char *err, uint32_t err_cap); /* 前缀遍历:listlen 定计数,逐 idx 取名(借用回收缓冲,不得 free),不一次性返回整段名单。 */ -extern int kvspaceListLen(void *h, const char *prefix, int expand_ext, int resolve, int32_t *out_count); -extern int kvspaceListAt(void *h, const char *prefix, int expand_ext, int resolve, int32_t idx, - uint8_t *buf, uint32_t buf_cap, uint32_t *out_len); -extern int kvspaceDel(void *h, const char *const *keys, uint32_t nkeys, char *err, uint32_t err_cap); -extern int kvspaceDelTree(void *h, const char *prefix, char *err, uint32_t err_cap); -extern int kvspaceCp(void *h, const char *src, const char *dst, char *err, uint32_t err_cap); -extern int kvspaceCpTree(void *h, const char *src, const char *dst, char *err, uint32_t err_cap); -extern int kvspaceCpList(void *h, const char *src, const char *dst, char *err, uint32_t err_cap); -extern int kvspaceMkindex(void *h, const char *path, uint32_t capacity, char *err, uint32_t err_cap); -extern int kvspaceMkindexExt(void *h, const char *path, const char *ext_path, char *err, uint32_t err_cap); -extern int kvspaceRmindexExt(void *h, const char *path, char *err, uint32_t err_cap); -extern int kvspaceWatch(void *h, const char *key, const uint8_t *target, uint32_t target_len, - uint64_t tick_ns, uint8_t **out, uint32_t *out_len); -extern int kvspaceTlvEncode(const char *kind, const uint8_t *raw, uint32_t raw_len, - const int32_t *dims, int32_t ndim, uint8_t **out, uint32_t *out_len); -extern int kvspaceDecodeHead(const uint8_t *data, uint32_t data_len, kvspaceHead_t *out); -extern int kvspaceNewPtr(const char *target_langtype, const char *target, - uint8_t **out, uint32_t *out_len); -extern int kvspaceNewChar(const uint8_t *bytes, uint32_t len, uint8_t **out, uint32_t *out_len); -extern int kvspaceNewBool(uint8_t v, uint8_t **out, uint32_t *out_len); -extern int kvspaceNewInt64(int64_t v, uint8_t **out, uint32_t *out_len); -extern int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len); +extern int kvspaceListLen(void *h, const char *prefix, int expand_ext, + int resolve, int32_t *out_count); +extern int kvspaceListAt(void *h, const char *prefix, int expand_ext, + int resolve, int32_t idx, uint8_t *buf, + uint32_t buf_cap, uint32_t *out_len); +extern int kvspaceDel(void *h, const char *const *keys, uint32_t nkeys, + char *err, uint32_t err_cap); +extern int kvspaceDelTree(void *h, const char *prefix, char *err, + uint32_t err_cap); +extern int kvspaceCp(void *h, const char *src, const char *dst, char *err, + uint32_t err_cap); +extern int kvspaceCpTree(void *h, const char *src, const char *dst, char *err, + uint32_t err_cap); +extern int kvspaceCpList(void *h, const char *src, const char *dst, char *err, + uint32_t err_cap); +extern int kvspaceMkindex(void *h, const char *path, uint32_t capacity, + char *err, uint32_t err_cap); +extern int kvspaceMkindexExt(void *h, const char *path, const char *ext_path, + char *err, uint32_t err_cap); +extern int kvspaceRmindexExt(void *h, const char *path, char *err, + uint32_t err_cap); +extern int kvspaceWatch(void *h, const char *key, const uint8_t *target, + uint32_t target_len, uint64_t tick_ns, uint8_t **out, + uint32_t *out_len); +extern int kvspaceTlvEncode(const char *kind, const uint8_t *raw, + uint32_t raw_len, const int32_t *dims, int32_t ndim, + uint8_t **out, uint32_t *out_len); +extern int kvspaceDecodeHead(const uint8_t *data, uint32_t data_len, + kvspaceHead_t *out); +extern int kvspaceNewPtr(const char *target_langtype, const char *target, + uint8_t **out, uint32_t *out_len); +extern int kvspaceNewChar(const uint8_t *bytes, uint32_t len, uint8_t **out, + uint32_t *out_len); +extern int kvspaceNewBool(uint8_t v, uint8_t **out, uint32_t *out_len); +extern int kvspaceNewInt64(int64_t v, uint8_t **out, uint32_t *out_len); +extern int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len); /* ── 数值上限 ──────────────────────────────────────────────────────── */ @@ -85,49 +106,88 @@ extern int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len); /* ── 派生 head:解析 langtype 得到(不落盘) ───────────────────────── */ typedef struct { - const char *kind; /* base kind(langtype 子串,非 NUL 终止) */ - int32_t kind_len; - int32_t ndim; - int32_t dims[X_MAX_NDIM]; - int32_t array_len; + const char *kind; /* base kind(langtype 子串,非 NUL 终止) */ + int32_t kind_len; + int32_t ndim; + int32_t dims[X_MAX_NDIM]; + int32_t array_len; } kvlangLangtype; void kvlangLangtypeParse(const uint8_t *langtype, kvlangLangtype *out); /* ── 基础类型 ──────────────────────────────────────────────────────── */ -typedef struct { uint8_t *data; uint32_t len; uint8_t borrowed; } kvlangXvalue_t; +typedef struct { + uint8_t *data; + uint32_t len; + uint8_t borrowed; +} kvlangXvalue_t; -typedef struct { char *key; kvlangXvalue_t val; } kvlangKvPair_t; +typedef struct { + char *key; + kvlangXvalue_t val; +} kvlangKvPair_t; -typedef struct { void *h; } kvlangKv_t; +typedef struct { + void *h; +} kvlangKv_t; /* growable string buffer */ -typedef struct { char *p; size_t len, cap; } kvlangStrbuf_t; - -static inline void kvlangStrbufInit(kvlangStrbuf_t *b) { b->p = NULL; b->len = 0; b->cap = 0; } +typedef struct { + char *p; + size_t len, cap; +} kvlangStrbuf_t; + +static inline void kvlangStrbufInit(kvlangStrbuf_t *b) { + b->p = NULL; + b->len = 0; + b->cap = 0; +} void kvlangStrbufPutc(kvlangStrbuf_t *b, char c); void kvlangStrbufPutn(kvlangStrbuf_t *b, const char *s, size_t n); -static inline void kvlangStrbufPuts(kvlangStrbuf_t *b, const char *s) { kvlangStrbufPutn(b, s, strlen(s)); } +static inline void kvlangStrbufPuts(kvlangStrbuf_t *b, const char *s) { + kvlangStrbufPutn(b, s, strlen(s)); +} void kvlangStrbufPrintf(kvlangStrbuf_t *b, const char *fmt, ...); -char *kvlangStrbufDetach(kvlangStrbuf_t *b); /* malloc,调用方 free */ -static inline void kvlangStrbufFree(kvlangStrbuf_t *b) { free(b->p); b->p = NULL; b->len = b->cap = 0; } +char *kvlangStrbufDetach(kvlangStrbuf_t *b); /* malloc,调用方 free */ +static inline void kvlangStrbufFree(kvlangStrbuf_t *b) { + free(b->p); + b->p = NULL; + b->len = b->cap = 0; +} /* ── XValue 操作 ───────────────────────────────────────────────────── */ -static inline bool kvlangXvalueNone(const kvlangXvalue_t *v) { return v->data == NULL || v->len == 0; } -static inline void kvlangXvalueZero(kvlangXvalue_t *v) { v->data = NULL; v->len = 0; v->borrowed = 0; } -void kvlangXvalueFree(kvlangXvalue_t *v); /* free 自持 data(借用读已拷贝为自持) */ -void kvlangXvalueSetBytes(kvlangXvalue_t *v, uint8_t *data, uint32_t len); /* 接管内存 */ -void kvlangXvalueMaterialize(kvlangXvalue_t *v); /* 借用值落地为自持(存入跨指令结构前必调) */ -int kvlangXvalueHead(const kvlangXvalue_t *v, kvspaceHead_t *h); /* decode head */ -const char *kvlangXvalueKind(const kvlangXvalue_t *v); /* 返回 kind,None="" */ +static inline bool kvlangXvalueNone(const kvlangXvalue_t *v) { + return v->data == NULL || v->len == 0; +} +static inline void kvlangXvalueZero(kvlangXvalue_t *v) { + v->data = NULL; + v->len = 0; + v->borrowed = 0; +} +void kvlangXvalueFree( + kvlangXvalue_t *v); /* free 自持 data(借用读已拷贝为自持) */ +void kvlangXvalueSetBytes(kvlangXvalue_t *v, uint8_t *data, + uint32_t len); /* 接管内存 */ +void kvlangXvalueMaterialize( + kvlangXvalue_t *v); /* 借用值落地为自持(存入跨指令结构前必调) */ +int kvlangXvalueHead(const kvlangXvalue_t *v, + kvspaceHead_t *h); /* decode head */ +const char *kvlangXvalueKind(const kvlangXvalue_t *v); /* 返回 kind,None="" */ bool kvlangXvalueKindIs(const kvlangXvalue_t *v, const char *kind); +int kvlangXvalueLangtype(const kvlangXvalue_t *v, char *buf, + size_t cap); /* 完整 langtype → buf */ +bool kvlangKindIsMap( + const char *kind); /* stringkeymap 或 map langtype(`…·…`) */ bool kvlangXvalueIsPtr(const kvlangXvalue_t *v); int32_t kvlangXvalueArrayLen(const kvlangXvalue_t *v); -const uint8_t *kvlangXvalueBody(const kvlangXvalue_t *v, const kvspaceHead_t *h, int32_t *out_len); -char *kvlangXvaluePtrTarget(const kvlangXvalue_t *v); /* malloc */ -char *kvlangXvalueValueString(const kvlangXvalue_t *v); /* malloc,对齐 Go ValueString */ +const uint8_t *kvlangXvalueBody(const kvlangXvalue_t *v, const kvspaceHead_t *h, + int32_t *out_len); +char *kvlangXvaluePtrTarget(const kvlangXvalue_t *v); /* malloc */ +char *kvlangXvalueValueString( + const kvlangXvalue_t *v); /* malloc,对齐 Go ValueString */ +char *kvlangXvalueSlotName(const kvlangXvalue_t *v); /* malloc,指令槽名 */ bool kvlangXvalueIsCharKind(const char *kind); bool kvlangXvalueIsIntKind(const char *kind); bool kvlangXvalueIsUintKind(const char *kind); @@ -140,43 +200,80 @@ enum { KVLANG_LT_UNKNOWN = 0, KVLANG_LT_NONE, KVLANG_LT_BOOL, - KVLANG_LT_INT8, KVLANG_LT_INT16, KVLANG_LT_INT32, KVLANG_LT_INT64, - KVLANG_LT_UINT8, KVLANG_LT_UINT16, KVLANG_LT_UINT32, KVLANG_LT_UINT64, - KVLANG_LT_FLOAT32, KVLANG_LT_FLOAT64, - KVLANG_LT_CHAR_UTF32, KVLANG_LT_CHAR_UTF8, KVLANG_LT_CHAR_ASCII, - KVLANG_LT_MAP, KVLANG_LT_INDEX, KVLANG_LT_EXTINDEX, - KVLANG_LT_RWIR, KVLANG_LT_RWFUNC, KVLANG_LT_SCOPE, KVLANG_LT_STRUCT, - KVLANG_LT_TIME, KVLANG_LT_DURATION, + KVLANG_LT_INT8, + KVLANG_LT_INT16, + KVLANG_LT_INT32, + KVLANG_LT_INT64, + KVLANG_LT_UINT8, + KVLANG_LT_UINT16, + KVLANG_LT_UINT32, + KVLANG_LT_UINT64, + KVLANG_LT_FLOAT32, + KVLANG_LT_FLOAT64, + KVLANG_LT_CHAR_UTF32, + KVLANG_LT_CHAR_UTF8, + KVLANG_LT_CHAR_ASCII, + KVLANG_LT_MAP, + KVLANG_LT_INDEX, + KVLANG_LT_EXTINDEX, + KVLANG_LT_RWIR, + KVLANG_LT_RWFUNC, + KVLANG_LT_SCOPE, + KVLANG_LT_STRUCT, + KVLANG_LT_TIME, + KVLANG_LT_DURATION, KVLANG_LT_COUNT }; int kvlangLangTypeId(const char *s, size_t len); const char *kvlangLangTypeKind(int id); int kvlangXvalueLangTypeId(const kvlangXvalue_t *v); -static inline bool kvlangLtIsSint(int id) { return id >= KVLANG_LT_INT8 && id <= KVLANG_LT_INT64; } -static inline bool kvlangLtIsUint(int id) { return id >= KVLANG_LT_UINT8 && id <= KVLANG_LT_UINT64; } -static inline bool kvlangLtIsInt(int id) { return id >= KVLANG_LT_INT8 && id <= KVLANG_LT_UINT64; } -static inline bool kvlangLtIsFloat(int id) { return id == KVLANG_LT_FLOAT32 || id == KVLANG_LT_FLOAT64; } -static inline bool kvlangLtIsNum(int id) { return id >= KVLANG_LT_INT8 && id <= KVLANG_LT_FLOAT64; } -static inline bool kvlangLtIsChar(int id) { return id >= KVLANG_LT_CHAR_UTF32 && id <= KVLANG_LT_CHAR_ASCII; } +static inline bool kvlangLtIsSint(int id) { + return id >= KVLANG_LT_INT8 && id <= KVLANG_LT_INT64; +} +static inline bool kvlangLtIsUint(int id) { + return id >= KVLANG_LT_UINT8 && id <= KVLANG_LT_UINT64; +} +static inline bool kvlangLtIsInt(int id) { + return id >= KVLANG_LT_INT8 && id <= KVLANG_LT_UINT64; +} +static inline bool kvlangLtIsFloat(int id) { + return id == KVLANG_LT_FLOAT32 || id == KVLANG_LT_FLOAT64; +} +static inline bool kvlangLtIsNum(int id) { + return id >= KVLANG_LT_INT8 && id <= KVLANG_LT_FLOAT64; +} +static inline bool kvlangLtIsChar(int id) { + return id >= KVLANG_LT_CHAR_UTF32 && id <= KVLANG_LT_CHAR_ASCII; +} static inline int kvlangLtIntWidth(int id) { - if (id >= KVLANG_LT_INT8 && id <= KVLANG_LT_INT64) return 8 << (id - KVLANG_LT_INT8); - if (id >= KVLANG_LT_UINT8 && id <= KVLANG_LT_UINT64) return 8 << (id - KVLANG_LT_UINT8); + if (id >= KVLANG_LT_INT8 && id <= KVLANG_LT_INT64) + return 8 << (id - KVLANG_LT_INT8); + if (id >= KVLANG_LT_UINT8 && id <= KVLANG_LT_UINT64) + return 8 << (id - KVLANG_LT_UINT8); return 0; } static inline int kvlangLtElemSize(int id) { - if (kvlangLtIsInt(id)) return kvlangLtIntWidth(id) / 8; - if (id == KVLANG_LT_FLOAT32) return 4; - if (id == KVLANG_LT_FLOAT64) return 8; - if (id == KVLANG_LT_BOOL) return 1; - if (id == KVLANG_LT_CHAR_UTF32) return 4; - if (id == KVLANG_LT_CHAR_UTF8 || id == KVLANG_LT_CHAR_ASCII) return 1; - if (id == KVLANG_LT_TIME || id == KVLANG_LT_DURATION) return 8; + if (kvlangLtIsInt(id)) + return kvlangLtIntWidth(id) / 8; + if (id == KVLANG_LT_FLOAT32) + return 4; + if (id == KVLANG_LT_FLOAT64) + return 8; + if (id == KVLANG_LT_BOOL) + return 1; + if (id == KVLANG_LT_CHAR_UTF32) + return 4; + if (id == KVLANG_LT_CHAR_UTF8 || id == KVLANG_LT_CHAR_ASCII) + return 1; + if (id == KVLANG_LT_TIME || id == KVLANG_LT_DURATION) + return 8; return 0; } /* 签名 langtype(runtime篇-07)校验/匹配 */ bool kvlangLangtypeValid(const char *expr); -bool kvlangLangtypeMatch(const char *expr, const char *kind, int32_t ndim, const int32_t *dims); +bool kvlangLangtypeMatch(const char *expr, const char *kind, int32_t ndim, + const int32_t *dims); /* 标量 0copy 视图(取代 kvlangXvalueAsInt64 等按值转换):decode head 一次, * 持 langtype id + 指向 body 首字节的借用指针,热路径按 id 直读 body。 */ typedef struct { @@ -188,9 +285,15 @@ kvlangScalar_t kvlangXvalueScalar(const kvlangXvalue_t *v); int64_t kvlangScalarReadI64(int id, const uint8_t *body); double kvlangScalarReadF64(int id, const uint8_t *body); uint64_t kvlangScalarReadU64(int id, const uint8_t *body); -static inline int64_t kvlangScalarI64(kvlangScalar_t s) { return kvlangScalarReadI64(s.id, s.body); } -static inline double kvlangScalarF64(kvlangScalar_t s) { return kvlangScalarReadF64(s.id, s.body); } -static inline uint64_t kvlangScalarU64(kvlangScalar_t s) { return kvlangScalarReadU64(s.id, s.body); } +static inline int64_t kvlangScalarI64(kvlangScalar_t s) { + return kvlangScalarReadI64(s.id, s.body); +} +static inline double kvlangScalarF64(kvlangScalar_t s) { + return kvlangScalarReadF64(s.id, s.body); +} +static inline uint64_t kvlangScalarU64(kvlangScalar_t s) { + return kvlangScalarReadU64(s.id, s.body); +} uint32_t kvlangXvalueChar32At(const kvlangXvalue_t *v, int32_t idx); int32_t kvlangXvalueElemSize(const char *kind); @@ -198,13 +301,19 @@ void kvlangXvalueNewInt64(kvlangXvalue_t *v, int64_t n); void kvlangXvalueNewFloat64(kvlangXvalue_t *v, double f); void kvlangXvalueNewBool(kvlangXvalue_t *v, bool b); void kvlangXvalueNewCharUtf8(kvlangXvalue_t *v, const char *s); -void kvlangXvalueNewCharUtf32(kvlangXvalue_t *v, const char *s); /* UTF-8 → UTF-32 LE body */ -void kvlangXvalueNewCharKind(kvlangXvalue_t *v, const char *kind, const char *s); -void kvlangXvalueNewPtr(kvlangXvalue_t *v, const char *target_langtype, const char *target); -void kvlangXvalueNewDefRwir(kvlangXvalue_t *v, int32_t nr, int32_t nw, int dynamic); +void kvlangXvalueNewCharUtf32(kvlangXvalue_t *v, + const char *s); /* UTF-8 → UTF-32 LE body */ +void kvlangXvalueNewCharKind(kvlangXvalue_t *v, const char *kind, + const char *s); +void kvlangXvalueNewPtr(kvlangXvalue_t *v, const char *target_langtype, + const char *target); +void kvlangXvalueNewDefRwir(kvlangXvalue_t *v, int32_t nr, int32_t nw, + int dynamic); void kvlangXvalueNewDefLangtype(kvlangXvalue_t *v, const char *langtype); -void kvlangXvalueNewTlv(kvlangXvalue_t *v, const char *kind, const uint8_t *raw, uint32_t raw_len, int32_t al); -void kvlangXvalueNewTlvDims(kvlangXvalue_t *v, const char *kind, const uint8_t *raw, uint32_t raw_len, +void kvlangXvalueNewTlv(kvlangXvalue_t *v, const char *kind, const uint8_t *raw, + uint32_t raw_len, int32_t al); +void kvlangXvalueNewTlvDims(kvlangXvalue_t *v, const char *kind, + const uint8_t *raw, uint32_t raw_len, const int32_t *dims, int32_t ndim); void kvlangFormatFloat(char *out, size_t cap, double v); @@ -213,54 +322,77 @@ void kvlangFormatFloat(char *out, size_t cap, double v); kvlangKv_t *kvlangKvConnect(const char *dsn); void kvlangKvDisconnect(kvlangKv_t *k); -int kvlangKvGetOne(kvlangKv_t *k, const char *key, kvlangXvalue_t *out); /* None → out len=0 */ -int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXvalue_t *out); -void kvlangKvReadReset(kvlangKv_t *k); /* 指令边界回收读借用池 */ -int kvlangKvGetPart(kvlangKv_t *k, const char *key, uint32_t off, uint32_t len, kvlangXvalue_t *out); /* 借用读分片 body 字节 */ -int kvlangKvSetPart(kvlangKv_t *k, const char *key, uint32_t off, const uint8_t *buf, uint32_t buf_len, char *err, uint32_t err_cap); /* 就地写分片 */ -int kvlangKvGetHead(kvlangKv_t *k, const char *key, kvspaceHead_t *out); /* 只读 head,不取 body */ -int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, uint32_t err_cap); +int kvlangKvGetOne(kvlangKv_t *k, const char *key, + kvlangXvalue_t *out); /* None → out len=0 */ +int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, + kvlangXvalue_t *out); +void kvlangKvReadReset(kvlangKv_t *k); /* 指令边界回收读借用池 */ +int kvlangKvGetPart(kvlangKv_t *k, const char *key, uint32_t off, uint32_t len, + kvlangXvalue_t *out); /* 借用读分片 body 字节 */ +int kvlangKvSetPart(kvlangKv_t *k, const char *key, uint32_t off, + const uint8_t *buf, uint32_t buf_len, char *err, + uint32_t err_cap); /* 就地写分片 */ +int kvlangKvGetHead(kvlangKv_t *k, const char *key, + kvspaceHead_t *out); /* 只读 head,不取 body */ +int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, + uint32_t err_cap); int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap); -int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_cap); -int kvlangKvCp(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); -int kvlangKvCpTree(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); -int kvlangKvCpList(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); -int kvlangKvMkindex(kvlangKv_t *k, const char *path, uint32_t capacity, char *err, uint32_t err_cap); -int kvlangKvExtIndex(kvlangKv_t *k, const char *path, const char *ext, char *err, uint32_t err_cap); -int kvlangKvDelExtIndex(kvlangKv_t *k, const char *path, char *err, uint32_t err_cap); -int kvlangKvList(kvlangKv_t *k, const char *prefix, bool expand_ext, bool resolve, - char ***out_names, int *out_count); /* split \n */ -int kvlangKvWatch(kvlangKv_t *k, const char *key, const kvlangXvalue_t *target, uint64_t tick_ns, kvlangXvalue_t *out); +int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, + uint32_t err_cap); +int kvlangKvCp(kvlangKv_t *k, const char *src, const char *dst, char *err, + uint32_t err_cap); +int kvlangKvCpTree(kvlangKv_t *k, const char *src, const char *dst, char *err, + uint32_t err_cap); +int kvlangKvCpList(kvlangKv_t *k, const char *src, const char *dst, char *err, + uint32_t err_cap); +int kvlangKvMkindex(kvlangKv_t *k, const char *path, uint32_t capacity, + char *err, uint32_t err_cap); +int kvlangKvExtIndex(kvlangKv_t *k, const char *path, const char *ext, + char *err, uint32_t err_cap); +int kvlangKvDelExtIndex(kvlangKv_t *k, const char *path, char *err, + uint32_t err_cap); +int kvlangKvList(kvlangKv_t *k, const char *prefix, bool expand_ext, + bool resolve, char ***out_names, + int *out_count); /* split \n */ +int kvlangKvWatch(kvlangKv_t *k, const char *key, const kvlangXvalue_t *target, + uint64_t tick_ns, kvlangXvalue_t *out); /* ── keytree ───────────────────────────────────────────────────────── */ -#define SEG_LIB RUNTIME_MEMBER_SEP "lib" -#define SEG_PC "pc" -#define SEG_STATUS "status" -#define SEG_CALLPC "callpc" +#define SEG_LIB RUNTIME_MEMBER_SEP "lib" +#define SEG_PC "pc" +#define SEG_STATUS "status" +#define SEG_CALLPC "callpc" #define SEG_RETURNPC "returnpc" -#define SEG_RO "ro" -#define SEG_MSG "msg" -#define LIB_ROOT "/lib" +#define SEG_RO "ro" +#define SEG_MSG "msg" +#define LIB_ROOT "/lib" #define VTHREAD_ROOT "/vthread" -static inline void kvlangStrbufClear(kvlangStrbuf_t *b) { b->len = 0; if (b->p) b->p[0] = 0; } - -const char *kvlangKeytreeVtidFromPc(const char *pc, kvlangStrbuf_t *out); /* "" 无效 */ -char *kvlangKeytreeStack(const char *root); /* malloc */ -char *kvlangKeytreeFrameRoot(const char *pc); /* malloc,无效 NULL */ -char *kvlangKeytreeEntryPc(const char *root); /* malloc */ -char *kvlangKeytreeFrameAt(const char *vtid, int depth); /* malloc */ -int kvlangKeytreeFrameNum(const char *path); /* [d]; panics if invalid */ -char *kvlangKeytreeIrseqPc(const char *frame_root, int irseq); /* malloc */ -char *kvlangKeytreeMember(const char *base, const char *name); /* malloc */ -char *kvlangKeytreeLibFunc(const char *pkg, const char *name); /* malloc */ -char *kvlangKeytreeRwir(const char *opcode); /* malloc */ +static inline void kvlangStrbufClear(kvlangStrbuf_t *b) { + b->len = 0; + if (b->p) + b->p[0] = 0; +} + +const char *kvlangKeytreeVtidFromPc(const char *pc, + kvlangStrbuf_t *out); /* "" 无效 */ +char *kvlangKeytreeStack(const char *root); /* malloc */ +char *kvlangKeytreeFrameRoot(const char *pc); /* malloc,无效 NULL */ +char *kvlangKeytreeEntryPc(const char *root); /* malloc */ +char *kvlangKeytreeFrameAt(const char *vtid, int depth); /* malloc */ +int kvlangKeytreeFrameNum(const char *path); /* [d]; panics if invalid */ +char *kvlangKeytreeIrseqPc(const char *frame_root, int irseq); /* malloc */ +char *kvlangKeytreeMember(const char *base, const char *name); /* malloc */ +char *kvlangKeytreeLibFunc(const char *pkg, const char *name); /* malloc */ +char *kvlangKeytreeRwir(const char *opcode); /* malloc */ void kvlangKeytreeVthread(const char *vtid, kvlangStrbuf_t *out); -void kvlangKeytreeVthreadSlot(const char *vtid, const char *frame, int i, int j, kvlangStrbuf_t *out); +void kvlangKeytreeVthreadSlot(const char *vtid, const char *frame, int i, int j, + kvlangStrbuf_t *out); void kvlangKeytreeVthreadPc(const char *vtid, kvlangStrbuf_t *out); void kvlangKeytreeVthreadStatus(const char *vtid, kvlangStrbuf_t *out); -void kvlangKeytreeVthreadStatusMsg(const char *vtid, const char *status, kvlangStrbuf_t *out); +void kvlangKeytreeVthreadStatusMsg(const char *vtid, const char *status, + kvlangStrbuf_t *out); void kvlangKeytreeVthreadDebugger(const char *vtid, kvlangStrbuf_t *out); void kvlangKeytreeFrameCallpc(const char *root, kvlangStrbuf_t *out); void kvlangKeytreeFrameReturnpc(const char *root, kvlangStrbuf_t *out); @@ -269,12 +401,12 @@ bool kvlangKeytreeIsEntryPc(const char *pc); /* ── rwir ──────────────────────────────────────────────────────────── */ -#define OP_CALL "call" +#define OP_CALL "call" #define OP_RETURN "return" -#define OP_BR "br" -#define OP_GOTO "goto" +#define OP_BR "br" +#define OP_GOTO "goto" #define OP_ASSIGN "assign" -#define OP_COPY "=" +#define OP_COPY "=" /* op_id:decode 期一次固化的统一派发码(quickening),主循环据此纯整数跳表、热路径零 strcmp。 * ≥0 = 在本 runtime myrwircaps(native 算子 + control/copy 均为其中一行),直查 myrwircaps[op_id].fn @@ -292,58 +424,81 @@ static inline int kvlangOpClassify(const char *op) { return n >= 0 ? n : OPID_notinmyrwircaps; } -typedef struct { char *name; kvlangXvalue_t val; } kvlangParam_t; +typedef struct { + char *name; + kvlangXvalue_t val; +} kvlangParam_t; typedef struct { char *opcode; - int op_id; /* 统一派发码,见上 enum;decode 期固化,永不随帧变化 */ - kvlangParam_t *reads; int nr; - kvlangParam_t *writes; int nw; + int op_id; /* 统一派发码,见上 enum;decode 期固化,永不随帧变化 */ + kvlangParam_t *reads; + int nr; + kvlangParam_t *writes; + int nw; } kvlangRwirInst_t; int kvlangRwirNextPc(const char *pc, kvlangStrbuf_t *out); int kvlangRwirExtractAddr0(const char *coord); -int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, kvlangRwirInst_t *out, char *err, uint32_t err_cap); +int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, + kvlangRwirInst_t *out, char *err, uint32_t err_cap); void kvlangRwirInstFree(kvlangRwirInst_t *inst); /* 外部扩展 handoff:写共享队列 /lib//vids/=pc,阻塞 watch 该 key 直至变 None * (外部执行器认领、驱动、置 nextpc 后删除该条目 → 本端解除阻塞)。 */ -int handoff_external_rwir(kvlangKv_t *kv, const char *vtid, const char *pc, kvlangRwirInst_t *inst); +int handoff_external_rwir(kvlangKv_t *kv, const char *vtid, const char *pc, + kvlangRwirInst_t *inst); /* notinmyrwircaps:opcode 是不在本 runtime myrwircaps 内、须经 def rwir 路由给能兑现它的 * 其它 runtime 的 rwir。判据=读 kvspace /lib/ 存在 def rwir 路由头(能力唯一事实源)。 */ bool notinmyrwircaps(kvlangKv_t *kv, const char *opcode); /* ── vthread ───────────────────────────────────────────────────────── */ -void kvlangVthreadGet(kvlangKv_t *kv, const char *vtid, char **pc, char **status); -void kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, const char *status); +void kvlangVthreadGet(kvlangKv_t *kv, const char *vtid, char **pc, + char **status); +void kvlangVthreadSet(kvlangKv_t *kv, const char *vtid, const char *pc, + const char *status); void kvlangVthreadSetDone(kvlangKv_t *kv, const char *vtid, const char *ret); -void kvlangVthreadSetError(kvlangKv_t *kv, const char *vtid, const char *pc, const char *msg); +void kvlangVthreadSetError(kvlangKv_t *kv, const char *vtid, const char *pc, + const char *msg); /* ── builtin ───────────────────────────────────────────────────────── */ /* yield_pc:native builtin 把「须交回上层驱动就地派发的 pc」写入 *yield_pc(否则留 NULL)。 * 唯 vthread·run 的 return 模式用:驱动一个子 vthread 遇非本执行器 rwir 时,把其 pc 冒泡给驱动。 */ -typedef struct { kvlangKv_t *kv; const char *vtid; const char *pc; kvlangRwirInst_t *inst; char **yield_pc; } kvlangFrame_t; +typedef struct { + kvlangKv_t *kv; + const char *vtid; + const char *pc; + kvlangRwirInst_t *inst; + char **yield_pc; +} kvlangFrame_t; /* notinmycaps:查 myrwircaps table,opcode 不在本 runtime 能力表内 → true。 */ bool notinmycaps(const char *opcode); bool kvlangBuiltinNumOp(const char *opcode); -int kvlangBuiltinNative(kvlangFrame_t *f); /* dispatch + call,0 成功 */ -int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, kvlangRwirInst_t *inst); +int kvlangBuiltinNative(kvlangFrame_t *f); /* dispatch + call,0 成功 */ +int kvlangBuiltinExecuteCopy(kvlangKv_t *kv, const char *vtid, const char *pc, + kvlangRwirInst_t *inst); /* control 算子:与 native 同居 myrwircaps 一张表,frame 签名统一派发(call/return/goto/br)。 */ int kvlangCtlCall(kvlangFrame_t *f); int kvlangCtlReturn(kvlangFrame_t *f); int kvlangCtlGoto(kvlangFrame_t *f); int kvlangCtlBr(kvlangFrame_t *f); -void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, const char *name, - const kvlangXvalue_t *val, kvlangXvalue_t *out); -char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, const char *name); +void kvlangBuiltinResolveReadValue(kvlangKv_t *kv, const char *frame_root, + const char *name, const kvlangXvalue_t *val, + kvlangXvalue_t *out); +char *kvlangBuiltinResolveWriteSlot(kvlangKv_t *kv, const char *frame_root, + const char *name); /* 成员写的 base 尚无值 → 落空 stringkeymap 值(`/lib` 下跳过,见 builtin_kv.c)。 */ /* 成员写(memitem)前置条件:memhead(base 容器值)必须已存在;缺则返回 -1 拒绝写入。 */ -int kvlangBuiltinCheckMemhead(kvlangKv_t *kv, const char *frame_root, const char *base); -char *kvlangBuiltinResolveReadKey(kvlangKv_t *kv, const char *frame_root, const char *name, const kvlangXvalue_t *val); -bool kvlangBuiltinTryParseNumber(const char *s, kvlangXvalue_t *out); /* 成功 out 接管 */ -void kvlangDisplay(const kvlangXvalue_t *v, char **out); /* malloc,对齐 Go Display */ +int kvlangBuiltinCheckMemhead(kvlangKv_t *kv, const char *frame_root, + const char *base); +char *kvlangBuiltinResolveReadKey(kvlangKv_t *kv, const char *frame_root, + const char *name, const kvlangXvalue_t *val); +bool kvlangBuiltinTryParseNumber(const char *s, + kvlangXvalue_t *out); /* 成功 out 接管 */ +void kvlangDisplay(const kvlangXvalue_t *v, + char **out); /* malloc,对齐 Go Display */ /* ── kvcpu ─────────────────────────────────────────────────────────── */ @@ -353,16 +508,25 @@ void kvlangDisplay(const kvlangXvalue_t *v, char **out); /* * kvlangKvcpuExecuteMode 返回值:-1 错误;0 正常结束(done);1 遇 ext rwir(仅 KVMODE_RETURN,*out_pc=其 PC)。 */ typedef enum { KVMODE_WATCH = 0, KVMODE_RETURN = 1 } kvmode_t; -int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char **out_pc); -int kvlangKvcpuExecute(kvlangKv_t *kv, const char *pc); /* = KVMODE_WATCH,out_pc 忽略 */ -char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, const char *funcname, const char *const *args, int nargs); -int kvlangKvcpuDynCall(kvlangKv_t *kv, const char *vtid, const char *pc, const char *funckey); +int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, + char **out_pc); +int kvlangKvcpuExecute(kvlangKv_t *kv, + const char *pc); /* = KVMODE_WATCH,out_pc 忽略 */ +char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, + const char *funcname, const char *const *args, + int nargs); +int kvlangKvcpuDynCall(kvlangKv_t *kv, const char *vtid, const char *pc, + const char *funckey); /* 创建 vthread(不运行),返回 vid(free)。运行由 RunVid 承接。vthread·create 用。 */ -char *kvlangVthreadSpawn(kvlangKv_t *kv, const char *funcname, const char *const *args, int nargs); +char *kvlangVthreadSpawn(kvlangKv_t *kv, const char *funcname, + const char *const *args, int nargs); /* 按 vid 从持久化 pc 跑到结束(KVMODE_WATCH),返回终态/错误。vthread·run 用。 */ -int kvlangRuntimeRunVid(kvlangKv_t *kv, const char *vid, char **ret, char *err, uint32_t err_cap); +int kvlangRuntimeRunVid(kvlangKv_t *kv, const char *vid, char **ret, char *err, + uint32_t err_cap); /* run funcname 到结束(Spawn + RunVid),返回终态/错误。 */ -int kvlangRuntimeExecuteKv(kvlangKv_t *kv, const char *funcname, const char *const *args, int nargs, char **ret, char *err, uint32_t err_cap); +int kvlangRuntimeExecuteKv(kvlangKv_t *kv, const char *funcname, + const char *const *args, int nargs, char **ret, + char *err, uint32_t err_cap); /* ── logx ──────────────────────────────────────────────────────────── */ diff --git a/runtime/src/rwir.c b/runtime/src/rwir.c index f27fe120..88dfb763 100644 --- a/runtime/src/rwir.c +++ b/runtime/src/rwir.c @@ -2,7 +2,8 @@ int kvlangRwirExtractAddr0(const char *coord) { const char *p = coord; - while (*p == '[' || *p == ' ' || *p == '\t') p++; + while (*p == '[' || *p == ' ' || *p == '\t') + p++; char *end; long n = strtol(p, &end, 10); return end == p ? 0 : (int)n; @@ -20,19 +21,32 @@ int kvlangRwirNextPc(const char *pc, kvlangStrbuf_t *out) { void kvlangRwirInstFree(kvlangRwirInst_t *inst) { free(inst->opcode); - for (int i = 0; i < inst->nr; i++) { free(inst->reads[i].name); kvlangXvalueFree(&inst->reads[i].val); } - for (int i = 0; i < inst->nw; i++) { free(inst->writes[i].name); kvlangXvalueFree(&inst->writes[i].val); } + for (int i = 0; i < inst->nr; i++) { + free(inst->reads[i].name); + kvlangXvalueFree(&inst->reads[i].val); + } + for (int i = 0; i < inst->nw; i++) { + free(inst->writes[i].name); + kvlangXvalueFree(&inst->writes[i].val); + } free(inst->reads); free(inst->writes); - inst->opcode = NULL; inst->reads = NULL; inst->writes = NULL; inst->nr = inst->nw = 0; + inst->opcode = NULL; + inst->reads = NULL; + inst->writes = NULL; + inst->nr = inst->nw = 0; } -int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, kvlangRwirInst_t *out, - char *err, uint32_t err_cap) { +int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, + kvlangRwirInst_t *out, char *err, uint32_t err_cap) { memset(out, 0, sizeof(*out)); const char *last = NULL; - for (const char *p = pc; (p = strstr(p, "/[")) != NULL; p += 2) last = p; - if (!last) { snprintf(err, err_cap, "Decode: invalid pc (no /[coord]): %s", pc); return -1; } + for (const char *p = pc; (p = strstr(p, "/[")) != NULL; p += 2) + last = p; + if (!last) { + snprintf(err, err_cap, "Decode: invalid pc (no /[coord]): %s", pc); + return -1; + } int addr0 = kvlangRwirExtractAddr0(last + 1); kvlangStrbuf_t key; @@ -52,18 +66,23 @@ int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, kvla nm = kvlangStrbufDetach(&key); kvlangKvGetMember(kv, link_base, nm, &v); free(nm); - if (!kvlangXvalueNone(&v)) out->opcode = kvlangXvalueValueString(&v); + if (!kvlangXvalueNone(&v)) + out->opcode = kvlangXvalueValueString(&v); kvlangXvalueFree(&v); - out->op_id = out->opcode ? kvlangOpClassify(out->opcode) : OPID_notinmyrwircaps; + out->op_id = + out->opcode ? kvlangOpClassify(out->opcode) : OPID_notinmyrwircaps; for (int i = 1; i <= MAX_PARAMS; i++) { kvlangStrbufPrintf(&key, "[%d,-%d]", addr0, i); nm = kvlangStrbufDetach(&key); kvlangKvGetMember(kv, link_base, nm, &v); free(nm); - if (kvlangXvalueNone(&v)) { kvlangXvalueFree(&v); break; } - out->reads[out->nr].name = kvlangXvalueValueString(&v); - kvlangXvalueMaterialize(&v); /* 指令进 rwir_cache 长存,字面量须自持 */ + if (kvlangXvalueNone(&v)) { + kvlangXvalueFree(&v); + break; + } + out->reads[out->nr].name = kvlangXvalueSlotName(&v); + kvlangXvalueMaterialize(&v); /* 指令进 rwir_cache 长存,字面量须自持 */ out->reads[out->nr].val = v; out->nr++; } @@ -72,9 +91,12 @@ int kvlangRwirDecode(kvlangKv_t *kv, const char *link_base, const char *pc, kvla nm = kvlangStrbufDetach(&key); kvlangKvGetMember(kv, link_base, nm, &v); free(nm); - if (kvlangXvalueNone(&v)) { kvlangXvalueFree(&v); break; } - out->writes[out->nw].name = kvlangXvalueValueString(&v); - kvlangXvalueMaterialize(&v); /* 指令进 rwir_cache 长存,字面量须自持 */ + if (kvlangXvalueNone(&v)) { + kvlangXvalueFree(&v); + break; + } + out->writes[out->nw].name = kvlangXvalueSlotName(&v); + kvlangXvalueMaterialize(&v); /* 指令进 rwir_cache 长存,字面量须自持 */ out->writes[out->nw].val = v; out->nw++; } diff --git a/runtime/src/rwirext.c b/runtime/src/rwirext.c index 61f89090..86314cc9 100644 --- a/runtime/src/rwirext.c +++ b/runtime/src/rwirext.c @@ -9,272 +9,286 @@ static char *g_first_vids = NULL; * (langtype=def rwir,storetype=index)。kvspace 是能力唯一事实源;本判定在独立 * kvlang 进程内发生,进程内 myrwircaps 恒不含它,故只有 /lib 路由头可信。 */ bool notinmyrwircaps(kvlangKv_t *k, const char *opcode) { - if (opcode[0] == '/') - return false; - char *key = kvlangKeytreeRwir(opcode); - kvlangXvalue_t v; kvlangXvalueZero(&v); - kvlangKvGetOne(k, key, &v); - bool yes = !kvlangXvalueNone(&v) && kvlangXvalueKindIs(&v, KVSPACE_KIND_DEF_RWIR); - kvlangXvalueFree(&v); - free(key); - return yes; + if (opcode[0] == '/') + return false; + char *key = kvlangKeytreeRwir(opcode); + kvlangXvalue_t v; + kvlangXvalueZero(&v); + kvlangKvGetOne(k, key, &v); + bool yes = + !kvlangXvalueNone(&v) && kvlangXvalueKindIs(&v, KVSPACE_KIND_DEF_RWIR); + kvlangXvalueFree(&v); + free(key); + return yes; } /* 建立 rwir 的 vids 队列:第一个 rwir 是真实 strkeymap,后续是 Ptr 指向第一个。 * 幂等:vids 已存在则跳过——否则脏 kvspace 上重复注册时,Set 经路径穿透会把首队列 * 改成自指 Ptr,令 resolve_path 死循环(父子 kvlang 共享同一 redis 的挂起根因)。 */ static void register_vids(kvlangKv_t *k, const char *opcode) { - char *base = kvlangKeytreeRwir(opcode); - kvlangStrbuf_t tk; kvlangStrbufInit(&tk); - kvlangStrbufPuts(&tk, base); kvlangStrbufPuts(&tk, "/vids"); - bool first = (g_first_vids == NULL); - if (first) g_first_vids = strdup(tk.p); - kvlangXvalue_t cur; kvlangXvalueZero(&cur); - bool exists = (kvlangKvGetOne(k, tk.p, &cur) == 0 && !kvlangXvalueNone(&cur)); - kvlangXvalueFree(&cur); - if (!exists) { - kvlangXvalue_t v; kvlangXvalueZero(&v); - int32_t dims[1] = {0}; + char *base = kvlangKeytreeRwir(opcode); + kvlangStrbuf_t tk; + kvlangStrbufInit(&tk); + kvlangStrbufPuts(&tk, base); + kvlangStrbufPuts(&tk, "/vids"); + bool first = (g_first_vids == NULL); if (first) - kvlangXvalueNewTlvDims(&v, KVSPACE_KIND_MAP, (const uint8_t *)"", 0, dims, 1); - else - kvlangXvalueNewPtr(&v, KVSPACE_KIND_MAP, g_first_vids); - kvlangKvPair_t p = {tk.p, v}; - char err[256]; - kvlangKvSet(k, &p, 1, err, sizeof err); - kvlangXvalueFree(&v); - } - kvlangStrbufFree(&tk); - free(base); + g_first_vids = strdup(tk.p); + kvlangXvalue_t cur; + kvlangXvalueZero(&cur); + bool exists = + (kvlangKvGetOne(k, tk.p, &cur) == 0 && !kvlangXvalueNone(&cur)); + kvlangXvalueFree(&cur); + if (!exists) { + kvlangXvalue_t v; + kvlangXvalueZero(&v); + int32_t dims[1] = {0}; + if (first) + kvlangXvalueNewTlvDims(&v, KVSPACE_KIND_MAP, (const uint8_t *)"", 0, + dims, 1); + else + kvlangXvalueNewPtr(&v, KVSPACE_KIND_MAP, g_first_vids); + kvlangKvPair_t p = {tk.p, v}; + char err[256]; + kvlangKvSet(k, &p, 1, err, sizeof err); + kvlangXvalueFree(&v); + } + kvlangStrbufFree(&tk); + free(base); } /* 写 [0,x] 签名行槽 = def langtype(body 为该参数 langtype 串)。 */ -static void write_sig_slot(kvlangKv_t *k, const char *base, int x, const char *lt, size_t lt_len) { - kvlangStrbuf_t sk; kvlangStrbufInit(&sk); - kvlangStrbufPrintf(&sk, "%s/[0,%d]", base, x); - char *clean = strndup(lt, lt_len); - kvlangXvalue_t sv; kvlangXvalueNewDefLangtype(&sv, clean); - free(clean); - kvlangKvPair_t sp = {sk.p, sv}; - char err[256]; - kvlangKvSet(k, &sp, 1, err, sizeof err); - kvlangXvalueFree(&sv); - kvlangStrbufFree(&sk); +static void write_sig_slot(kvlangKv_t *k, const char *base, int x, + const char *lt, size_t lt_len) { + kvlangStrbuf_t sk; + kvlangStrbufInit(&sk); + kvlangStrbufPrintf(&sk, "%s/[0,%d]", base, x); + char *clean = strndup(lt, lt_len); + kvlangXvalue_t sv; + kvlangXvalueNewDefLangtype(&sv, clean); + free(clean); + kvlangKvPair_t sp = {sk.p, sv}; + char err[256]; + kvlangKvSet(k, &sp, 1, err, sizeof err); + kvlangXvalueFree(&sv); + kvlangStrbufFree(&sk); } /* 注册一条 rwir:/lib/ 路由头(仅计数头)+ 各参数落 [0,x] 签名行槽(def langtype)。 * 参数类型逐条传入(读参 rp[0..nr]、写参 wp[0..nw]),不再拼签名串——避免其它 runtime * 把「拼接 sig 串」误当注册标准。末读参尾缀 "..." → 变参 arity(落 dynamic 字节)。 */ -int kvlangDefRwir(void *kvspace, const char *opcode, - const char *const *rp, int32_t nr, - const char *const *wp, int32_t nw) { - kvlangKv_t k = {kvspace}; - char *base = kvlangKeytreeRwir(opcode); - char err[256]; +int kvlangDefRwir(void *kvspace, const char *opcode, const char *const *rp, + int32_t nr, const char *const *wp, int32_t nw) { + kvlangKv_t k = {kvspace}; + char *base = kvlangKeytreeRwir(opcode); + char err[256]; - int dynamic = 0; - size_t last_len = (nr > 0 && rp[nr - 1]) ? strlen(rp[nr - 1]) : 0; - if (nr > 0 && last_len >= 3 && memcmp(rp[nr - 1] + last_len - 3, "...", 3) == 0) { - dynamic = 1; - last_len -= 3; - } + int dynamic = 0; + size_t last_len = (nr > 0 && rp[nr - 1]) ? strlen(rp[nr - 1]) : 0; + if (nr > 0 && last_len >= 3 && + memcmp(rp[nr - 1] + last_len - 3, "...", 3) == 0) { + dynamic = 1; + last_len -= 3; + } - kvlangXvalue_t hv; - kvlangXvalueNewDefRwir(&hv, nr, nw, dynamic); - kvlangKvPair_t hp = {base, hv}; - int rc = kvlangKvSet(&k, &hp, 1, err, sizeof err); - kvlangXvalueFree(&hv); + kvlangXvalue_t hv; + kvlangXvalueNewDefRwir(&hv, nr, nw, dynamic); + kvlangKvPair_t hp = {base, hv}; + int rc = kvlangKvSet(&k, &hp, 1, err, sizeof err); + kvlangXvalueFree(&hv); - for (int32_t i = 0; i < nr; i++) - write_sig_slot(&k, base, -(i + 1), rp[i], (i == nr - 1) ? last_len : strlen(rp[i])); - for (int32_t i = 0; i < nw; i++) - write_sig_slot(&k, base, i + 1, wp[i], strlen(wp[i])); + for (int32_t i = 0; i < nr; i++) + write_sig_slot(&k, base, -(i + 1), rp[i], + (i == nr - 1) ? last_len : strlen(rp[i])); + for (int32_t i = 0; i < nw; i++) + write_sig_slot(&k, base, i + 1, wp[i], strlen(wp[i])); - free(base); - /* 建立共享 vids 队列(第一个真实 strkeymap,后续 Ptr 指向它;幂等查 kvspace)。 */ - register_vids(&k, opcode); - return rc; + free(base); + /* 建立共享 vids 队列(第一个真实 strkeymap,后续 Ptr 指向它;幂等查 kvspace)。 */ + register_vids(&k, opcode); + return rc; } int kvlangRwirextHandoff(void *kvspace, const char *vtid, const char *pc) { - kvlangKv_t k = {kvspace}; - char *fr = kvlangKeytreeFrameRoot(pc); - if (!fr) - return -1; - char *lb = kvlangKeytreeStack(fr); - kvlangRwirInst_t inst; - char err[256]; - if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0) { - free(fr); + kvlangKv_t k = {kvspace}; + char *fr = kvlangKeytreeFrameRoot(pc); + if (!fr) + return -1; + char *lb = kvlangKeytreeStack(fr); + kvlangRwirInst_t inst; + char err[256]; + if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0) { + free(fr); + free(lb); + return -1; + } free(lb); - return -1; - } - free(lb); - int rc = handoff_external_rwir(&k, vtid, pc, &inst); - free(fr); - kvlangRwirInstFree(&inst); - return rc; + int rc = handoff_external_rwir(&k, vtid, pc, &inst); + free(fr); + kvlangRwirInstFree(&inst); + return rc; } char *kvlangRwirextNextPc(const char *pc) { - kvlangStrbuf_t b; - kvlangStrbufInit(&b); - kvlangRwirNextPc(pc, &b); - return kvlangStrbufDetach(&b); + kvlangStrbuf_t b; + kvlangStrbufInit(&b); + kvlangRwirNextPc(pc, &b); + return kvlangStrbufDetach(&b); } char *kvlangRwirextParams(void *kvspace, const char *pc) { - kvlangKv_t k = {kvspace}; - char *fr = kvlangKeytreeFrameRoot(pc); - if (!fr) - return strdup(""); - char *lb = kvlangKeytreeStack(fr); - kvlangRwirInst_t inst; - char err[256]; - if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0) { - free(fr); + kvlangKv_t k = {kvspace}; + char *fr = kvlangKeytreeFrameRoot(pc); + if (!fr) + return strdup(""); + char *lb = kvlangKeytreeStack(fr); + kvlangRwirInst_t inst; + char err[256]; + if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0) { + free(fr); + free(lb); + return strdup(""); + } free(lb); - return strdup(""); - } - free(lb); - kvlangStrbuf_t b; - kvlangStrbufInit(&b); - kvlangStrbufPuts(&b, inst.opcode ? inst.opcode : ""); - for (int i = 0; i < inst.nr; i++) { - kvlangStrbufPutc(&b, '\n'); - kvlangStrbufPuts(&b, inst.reads[i].name ? inst.reads[i].name : ""); - } - for (int i = 0; i < inst.nw; i++) { - kvlangStrbufPutc(&b, '\n'); - kvlangStrbufPuts(&b, inst.writes[i].name ? inst.writes[i].name : ""); - } - free(fr); - kvlangRwirInstFree(&inst); - return kvlangStrbufDetach(&b); + kvlangStrbuf_t b; + kvlangStrbufInit(&b); + kvlangStrbufPuts(&b, inst.opcode ? inst.opcode : ""); + for (int i = 0; i < inst.nr; i++) { + kvlangStrbufPutc(&b, '\n'); + kvlangStrbufPuts(&b, inst.reads[i].name ? inst.reads[i].name : ""); + } + for (int i = 0; i < inst.nw; i++) { + kvlangStrbufPutc(&b, '\n'); + kvlangStrbufPuts(&b, inst.writes[i].name ? inst.writes[i].name : ""); + } + free(fr); + kvlangRwirInstFree(&inst); + return kvlangStrbufDetach(&b); } /* stringkeymap 容器值:遍历 p· 成员 → "[e0, e1, ...]"(成员为 map 时递归)。 */ static char *display_map(kvlangKv_t *k, const char *path) { - char *dir = kvlangKeytreeMember(path, ""); - char **names; - int cnt; - if (kvlangKvList(k, dir, false, false, &names, &cnt) != 0 || cnt <= 0) { - free(dir); + char *dir = kvlangKeytreeMember(path, ""); + char **names; + int cnt; + if (kvlangKvList(k, dir, false, false, &names, &cnt) != 0 || cnt <= 0) { + free(dir); + free(names); + return strdup("[]"); + } + kvlangStrbuf_t b; + kvlangStrbufInit(&b); + kvlangStrbufPutc(&b, '['); + for (int i = 0; i < cnt; i++) { + if (i) + kvlangStrbufPuts(&b, ", "); + char *mk = kvlangKeytreeMember(path, names[i]); + kvlangXvalue_t mv; + kvlangXvalueZero(&mv); + kvlangKvGetOne(k, mk, &mv); + char *ms = kvlangXvalueNone(&mv) ? strdup("") + : kvlangKindIsMap(kvlangXvalueKind(&mv)) + ? display_map(k, mk) + : kvlangXvalueValueString(&mv); + kvlangStrbufPuts(&b, ms); + free(ms); + kvlangXvalueFree(&mv); + free(mk); + free(names[i]); + } + kvlangStrbufPutc(&b, ']'); free(names); - return strdup("[]"); - } - kvlangStrbuf_t b; - kvlangStrbufInit(&b); - kvlangStrbufPutc(&b, '['); - for (int i = 0; i < cnt; i++) { - if (i) kvlangStrbufPuts(&b, ", "); - char *mk = kvlangKeytreeMember(path, names[i]); - kvlangXvalue_t mv; - kvlangXvalueZero(&mv); - kvlangKvGetOne(k, mk, &mv); - char *ms = kvlangXvalueNone(&mv) - ? strdup("") - : strcmp(kvlangXvalueKind(&mv), KVSPACE_KIND_MAP) == 0 - ? display_map(k, mk) - : kvlangXvalueValueString(&mv); - kvlangStrbufPuts(&b, ms); - free(ms); - kvlangXvalueFree(&mv); - free(mk); - free(names[i]); - } - kvlangStrbufPutc(&b, ']'); - free(names); - free(dir); - return kvlangStrbufDetach(&b); + free(dir); + return kvlangStrbufDetach(&b); } /* 解析读参 idx 为字符串(变量 → 帧槽值;路径 → 该路径下的值)。 */ char *kvlangRwirextResolveRead(void *kvspace, const char *pc, int idx) { - kvlangKv_t k = {kvspace}; - char *fr = kvlangKeytreeFrameRoot(pc); - if (!fr) - return strdup(""); - char *lb = kvlangKeytreeStack(fr); - kvlangRwirInst_t inst; - char err[256]; - if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0 || idx < 0 || - idx >= inst.nr) { - free(fr); + kvlangKv_t k = {kvspace}; + char *fr = kvlangKeytreeFrameRoot(pc); + if (!fr) + return strdup(""); + char *lb = kvlangKeytreeStack(fr); + kvlangRwirInst_t inst; + char err[256]; + if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0 || idx < 0 || + idx >= inst.nr) { + free(fr); + free(lb); + return strdup(""); + } free(lb); - return strdup(""); - } - free(lb); - kvlangXvalue_t v; - kvlangXvalueZero(&v); - kvlangBuiltinResolveReadValue(&k, fr, inst.reads[idx].name, - &inst.reads[idx].val, &v); - char *s; - if (kvlangXvalueNone(&v)) { - s = strdup(""); - } else if (strcmp(kvlangXvalueKind(&v), KVSPACE_KIND_MAP) == 0) { - char *path = kvlangBuiltinResolveWriteSlot(&k, fr, inst.reads[idx].name); - s = display_map(&k, path); - free(path); - } else { - s = kvlangXvalueValueString(&v); - } - kvlangXvalueFree(&v); - free(fr); - kvlangRwirInstFree(&inst); - return s; + kvlangXvalue_t v; + kvlangXvalueZero(&v); + kvlangBuiltinResolveReadValue(&k, fr, inst.reads[idx].name, + &inst.reads[idx].val, &v); + char *s; + if (kvlangXvalueNone(&v)) { + s = strdup(""); + } else if (kvlangKindIsMap(kvlangXvalueKind(&v))) { + char *path = + kvlangBuiltinResolveWriteSlot(&k, fr, inst.reads[idx].name); + s = display_map(&k, path); + free(path); + } else { + s = kvlangXvalueValueString(&v); + } + kvlangXvalueFree(&v); + free(fr); + kvlangRwirInstFree(&inst); + return s; } /* 解析读参 idx 为 KV 路径(变量/临时 → 帧槽路径;路径 → 直接返回;内联字面量 → * "")。 */ char *kvlangRwirextResolveReadPath(void *kvspace, const char *pc, int idx) { - kvlangKv_t k = {kvspace}; - char *fr = kvlangKeytreeFrameRoot(pc); - if (!fr) - return strdup(""); - char *lb = kvlangKeytreeStack(fr); - kvlangRwirInst_t inst; - char err[256]; - if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0 || idx < 0 || - idx >= inst.nr) { - free(fr); + kvlangKv_t k = {kvspace}; + char *fr = kvlangKeytreeFrameRoot(pc); + if (!fr) + return strdup(""); + char *lb = kvlangKeytreeStack(fr); + kvlangRwirInst_t inst; + char err[256]; + if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0 || idx < 0 || + idx >= inst.nr) { + free(fr); + free(lb); + return strdup(""); + } free(lb); - return strdup(""); - } - free(lb); - const char *nm = inst.reads[idx].name; - char *s; - if (!nm || !nm[0] || nm[0] == '"' || (nm[0] >= '0' && nm[0] <= '9') || - (nm[0] == '-' && nm[1]) || strcmp(nm, "true") == 0 || - strcmp(nm, "false") == 0 || strcmp(nm, "null") == 0) { - s = strdup(""); /* 内联字面量:无路径 */ - } else { - s = kvlangBuiltinResolveWriteSlot(&k, fr, - nm); /* 变量/临时/路径 → 帧槽路径 */ - } - free(fr); - kvlangRwirInstFree(&inst); - return s ? s : strdup(""); + const char *nm = inst.reads[idx].name; + char *s; + if (!nm || !nm[0] || nm[0] == '"' || (nm[0] >= '0' && nm[0] <= '9') || + (nm[0] == '-' && nm[1]) || strcmp(nm, "true") == 0 || + strcmp(nm, "false") == 0 || strcmp(nm, "null") == 0) { + s = strdup(""); /* 内联字面量:无路径 */ + } else { + s = kvlangBuiltinResolveWriteSlot(&k, fr, + nm); /* 变量/临时/路径 → 帧槽路径 */ + } + free(fr); + kvlangRwirInstFree(&inst); + return s ? s : strdup(""); } /* 解析写参 idx 为 KV 路径(路径 → 直接返回;变量 → 帧槽路径)。 */ char *kvlangRwirextResolveWrite(void *kvspace, const char *pc, int idx) { - kvlangKv_t k = {kvspace}; - char *fr = kvlangKeytreeFrameRoot(pc); - if (!fr) - return strdup(""); - char *lb = kvlangKeytreeStack(fr); - kvlangRwirInst_t inst; - char err[256]; - if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0 || idx < 0 || - idx >= inst.nw) { - free(fr); + kvlangKv_t k = {kvspace}; + char *fr = kvlangKeytreeFrameRoot(pc); + if (!fr) + return strdup(""); + char *lb = kvlangKeytreeStack(fr); + kvlangRwirInst_t inst; + char err[256]; + if (kvlangRwirDecode(&k, lb, pc, &inst, err, sizeof err) != 0 || idx < 0 || + idx >= inst.nw) { + free(fr); + free(lb); + return strdup(""); + } free(lb); - return strdup(""); - } - free(lb); - char *s = kvlangBuiltinResolveWriteSlot(&k, fr, inst.writes[idx].name); - free(fr); - kvlangRwirInstFree(&inst); - return s; + char *s = kvlangBuiltinResolveWriteSlot(&k, fr, inst.writes[idx].name); + free(fr); + kvlangRwirInstFree(&inst); + return s; } diff --git a/runtime/src/xvalue.c b/runtime/src/xvalue.c index 3e707677..4330175a 100644 --- a/runtime/src/xvalue.c +++ b/runtime/src/xvalue.c @@ -3,9 +3,16 @@ /* 后端无关的 XValue TLV 编解码(对齐 kvspace-durable/kvspace-c 的 kindexp TLV)。 * xval.data 一律 malloc(free 释放),后端在 kv.c 层负责拷贝。 */ -static uint32_t rd32(const uint8_t *p) { return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24); } -static uint16_t rd16(const uint8_t *p) { return (uint16_t)(p[0] | (p[1] << 8)); } -static uint64_t rd64(const uint8_t *p) { return (uint64_t)rd32(p) | ((uint64_t)rd32(p + 4) << 32); } +static uint32_t rd32(const uint8_t *p) { + return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | + ((uint32_t)p[3] << 24); +} +static uint16_t rd16(const uint8_t *p) { + return (uint16_t)(p[0] | (p[1] << 8)); +} +static uint64_t rd64(const uint8_t *p) { + return (uint64_t)rd32(p) | ((uint64_t)rd32(p + 4) << 32); +} void kvlangXvalueFree(kvlangXvalue_t *v) { if (!v->borrowed) @@ -32,12 +39,20 @@ void kvlangXvalueMaterialize(kvlangXvalue_t *v) { v->borrowed = 0; } -/* 解析 langtype 内容 → (dims, base kind)。langtype 为 NUL 终止串、无前缀(ref 归 head.ref)。 */ +/* 解析 langtype 内容 → (dims, base kind)。langtype 为 NUL 终止串、无前缀(ref 归 head.ref)。 + * **map langtype 无形状段**:`{keylt}·{valt}` 里 `·` 之前的方括号是键类型(`[int64]`、 + * `[float64,float64]`),不是维度——与 `[2]float64`(数组形状)截然不同,故整串即 base kind。 */ void kvlangLangtypeParse(const uint8_t *kx, kvlangLangtype *out) { memset(out, 0, sizeof(*out)); if (!kx) return; int32_t i = 0; + if (strstr((const char *)kx, MEMBER_SEP) != NULL) { + out->kind = (const char *)kx; + out->kind_len = (int32_t)strlen((const char *)kx); + out->array_len = 1; + return; + } if (kx[i] == '[') { i++; while (kx[i] != ']' && kx[i] != 0 && out->ndim < X_MAX_NDIM) { @@ -62,7 +77,8 @@ void kvlangLangtypeParse(const uint8_t *kx, kvlangLangtype *out) { /* head 编解码统一委托给链接的 kvspace .so(kvspace-c / kvspace-durable 同一 ABI), * runtime 不再私持 TLV head 布局,杜绝多份手写偏移不一致。 */ -static int kvlangXvalueDecodeHeadRaw(const uint8_t *d, uint32_t len, kvspaceHead_t *h) { +static int kvlangXvalueDecodeHeadRaw(const uint8_t *d, uint32_t len, + kvspaceHead_t *h) { memset(h, 0, sizeof(*h)); if (!d || len == 0) return -1; @@ -97,7 +113,8 @@ static uint8_t *kvlangXvalueOwn(uint8_t *tmp, uint32_t tl, uint32_t *out_len) { } static uint8_t *kvlangXvalueEncodeTlv(const char *kind, const uint8_t *raw, - uint32_t raw_len, int32_t array_len, uint32_t *out_len) { + uint32_t raw_len, int32_t array_len, + uint32_t *out_len) { int32_t dims[1]; int32_t ndim = al_to_dims(kind, array_len, dims); uint8_t *tmp = NULL; @@ -117,7 +134,7 @@ int kvlangXvalueHead(const kvlangXvalue_t *v, kvspaceHead_t *h) { } const char *kvlangXvalueKind(const kvlangXvalue_t *v) { - static __thread char buf[16][33]; + static __thread char buf[16][65]; static __thread int idx = 0; char *b = buf[idx]; idx = (idx + 1) & 15; @@ -133,8 +150,8 @@ const char *kvlangXvalueKind(const kvlangXvalue_t *v) { kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); int32_t kl = kx.kind_len; - if (kl > 32) - kl = 32; + if (kl > 64) + kl = 64; memcpy(b, kx.kind, (size_t)kl); b[kl] = 0; return b; @@ -148,7 +165,51 @@ bool kvlangXvalueKindIs(const kvlangXvalue_t *v, const char *kind) { return false; kvlangLangtype kx; kvlangLangtypeParse(h.langtype, &kx); - return (size_t)kx.kind_len == strlen(kind) && memcmp(kx.kind, kind, (size_t)kx.kind_len) == 0; + return (size_t)kx.kind_len == strlen(kind) && + memcmp(kx.kind, kind, (size_t)kx.kind_len) == 0; +} + +/* 完整 langtype(去 dims 前缀)拷入 buf:写槽带 map langtype 标注时据此取容器类型。 */ +int kvlangXvalueLangtype(const kvlangXvalue_t *v, char *buf, size_t cap) { + buf[0] = 0; + if (cap == 0 || kvlangXvalueNone(v)) + return -1; + kvspaceHead_t h; + if (kvlangXvalueHead(v, &h) < 0) + return -1; + kvlangLangtype kx; + kvlangLangtypeParse(h.langtype, &kx); + size_t n = (size_t)kx.kind_len; + if (n >= cap) + n = cap - 1; + memcpy(buf, kx.kind, n); + buf[n] = 0; + return (int)n; +} + +static char *strndup2(const uint8_t *p, int32_t n); + +/* 指令槽名:rwir 族槽跳过 5B 计数头取 sig;带 map langtype 标注的写槽 body 即变量名 + * (layout 的 write_slot_value 把声明容器类型落在槽的 langtype),其余交给 ValueString。 */ +char *kvlangXvalueSlotName(const kvlangXvalue_t *v) { + const char *k = kvlangXvalueKind(v); + if (strcmp(k, KVSPACE_KIND_RWIR) == 0 || + strcmp(k, KVSPACE_KIND_RWIR_OR_RWFUNC) == 0) { + kvspaceHead_t h; + if (kvlangXvalueHead(v, &h) < 0) + return strdup(""); + int32_t blen = h.body_len; + const uint8_t *body = v->data + h.body_offset; + return strndup2(body + (blen >= 5 ? 5 : 0), blen >= 5 ? blen - 5 : 0); + } + return kvlangXvalueValueString(v); +} + +/* 值容器判定:裸种类名 `stringkeymap`,或完整 map langtype `{keylt}·{valt}`(见 [[map容器]])。 + * 后者 `·` 之前是键类型(可能是 `[int64]`/`[float64,float64]`),故不能与 KIND_MAP 比串。 */ +bool kvlangKindIsMap(const char *kind) { + return kind && (strcmp(kind, KVSPACE_KIND_MAP) == 0 || + strstr(kind, MEMBER_SEP) != NULL); } bool kvlangXvalueIsPtr(const kvlangXvalue_t *v) { @@ -170,7 +231,8 @@ int32_t kvlangXvalueArrayLen(const kvlangXvalue_t *v) { return kx.array_len; } -const uint8_t *kvlangXvalueBody(const kvlangXvalue_t *v, const kvspaceHead_t *h, int32_t *out_len) { +const uint8_t *kvlangXvalueBody(const kvlangXvalue_t *v, const kvspaceHead_t *h, + int32_t *out_len) { int32_t off = h->body_offset, len = h->body_len; if (off < 0 || len < 0 || off + len > (int32_t)v->len) { if (out_len) @@ -183,13 +245,25 @@ const uint8_t *kvlangXvalueBody(const kvlangXvalue_t *v, const kvspaceHead_t *h, } /* kind 分类:经 langtypetable intern 成 id 后整数区间判定(替代旧 strcmp 链)。 */ -bool kvlangXvalueIsCharKind(const char *kind) { return kvlangLtIsChar(kvlangLangTypeId(kind, strlen(kind))); } -bool kvlangXvalueIsIntKind(const char *kind) { return kvlangLtIsSint(kvlangLangTypeId(kind, strlen(kind))); } -bool kvlangXvalueIsUintKind(const char *kind) { return kvlangLtIsUint(kvlangLangTypeId(kind, strlen(kind))); } -bool kvlangXvalueIsFloatKind(const char *kind) { return kvlangLtIsFloat(kvlangLangTypeId(kind, strlen(kind))); } -bool kvlangXvalueIsNumKind(const char *kind) { return kvlangLtIsNum(kvlangLangTypeId(kind, strlen(kind))); } +bool kvlangXvalueIsCharKind(const char *kind) { + return kvlangLtIsChar(kvlangLangTypeId(kind, strlen(kind))); +} +bool kvlangXvalueIsIntKind(const char *kind) { + return kvlangLtIsSint(kvlangLangTypeId(kind, strlen(kind))); +} +bool kvlangXvalueIsUintKind(const char *kind) { + return kvlangLtIsUint(kvlangLangTypeId(kind, strlen(kind))); +} +bool kvlangXvalueIsFloatKind(const char *kind) { + return kvlangLtIsFloat(kvlangLangTypeId(kind, strlen(kind))); +} +bool kvlangXvalueIsNumKind(const char *kind) { + return kvlangLtIsNum(kvlangLangTypeId(kind, strlen(kind))); +} -int32_t kvlangXvalueElemSize(const char *kind) { return kvlangLtElemSize(kvlangLangTypeId(kind, strlen(kind))); } +int32_t kvlangXvalueElemSize(const char *kind) { + return kvlangLtElemSize(kvlangLangTypeId(kind, strlen(kind))); +} static const uint8_t *v_body(const kvlangXvalue_t *v, kvspaceHead_t *h) { if (kvlangXvalueDecodeHeadRaw(v->data, v->len, h) < 0) @@ -219,15 +293,24 @@ int64_t kvlangScalarReadI64(int id, const uint8_t *b) { if (!b) return 0; switch (id) { - case KVLANG_LT_BOOL: return b[0] != 0; - case KVLANG_LT_INT8: return (int8_t)b[0]; - case KVLANG_LT_INT16: return (int16_t)rd16(b); - case KVLANG_LT_INT32: return (int32_t)rd32(b); - case KVLANG_LT_INT64: return (int64_t)rd64(b); - case KVLANG_LT_UINT8: return b[0]; - case KVLANG_LT_UINT16: return rd16(b); - case KVLANG_LT_UINT32: return rd32(b); - case KVLANG_LT_UINT64: return (int64_t)rd64(b); + case KVLANG_LT_BOOL: + return b[0] != 0; + case KVLANG_LT_INT8: + return (int8_t)b[0]; + case KVLANG_LT_INT16: + return (int16_t)rd16(b); + case KVLANG_LT_INT32: + return (int32_t)rd32(b); + case KVLANG_LT_INT64: + return (int64_t)rd64(b); + case KVLANG_LT_UINT8: + return b[0]; + case KVLANG_LT_UINT16: + return rd16(b); + case KVLANG_LT_UINT32: + return rd32(b); + case KVLANG_LT_UINT64: + return (int64_t)rd64(b); case KVLANG_LT_FLOAT32: { float f; uint32_t u = rd32(b); @@ -241,8 +324,10 @@ int64_t kvlangScalarReadI64(int id, const uint8_t *b) { return (int64_t)d; } case KVLANG_LT_TIME: - case KVLANG_LT_DURATION: return (int64_t)rd64(b); - default: return 0; + case KVLANG_LT_DURATION: + return (int64_t)rd64(b); + default: + return 0; } } @@ -268,11 +353,16 @@ uint64_t kvlangScalarReadU64(int id, const uint8_t *b) { if (!b) return 0; switch (id) { - case KVLANG_LT_UINT8: return b[0]; - case KVLANG_LT_UINT16: return rd16(b); - case KVLANG_LT_UINT32: return rd32(b); - case KVLANG_LT_UINT64: return rd64(b); - default: return (uint64_t)kvlangScalarReadI64(id, b); + case KVLANG_LT_UINT8: + return b[0]; + case KVLANG_LT_UINT16: + return rd16(b); + case KVLANG_LT_UINT32: + return rd32(b); + case KVLANG_LT_UINT64: + return rd64(b); + default: + return (uint64_t)kvlangScalarReadI64(id, b); } } @@ -362,8 +452,12 @@ char *kvlangXvaluePtrTarget(const kvlangXvalue_t *v) { /* ── value_string(对齐 Go ValueString)────────────────────────────── */ -static void append_num_int(kvlangStrbuf_t *b, int64_t n) { kvlangStrbufPrintf(b, "%lld", (long long)n); } -static void append_num_uint(kvlangStrbuf_t *b, uint64_t n) { kvlangStrbufPrintf(b, "%llu", (unsigned long long)n); } +static void append_num_int(kvlangStrbuf_t *b, int64_t n) { + kvlangStrbufPrintf(b, "%lld", (long long)n); +} +static void append_num_uint(kvlangStrbuf_t *b, uint64_t n) { + kvlangStrbufPrintf(b, "%llu", (unsigned long long)n); +} char *kvlangXvalueValueString(const kvlangXvalue_t *v) { if (kvlangXvalueNone(v)) @@ -432,21 +526,26 @@ char *kvlangXvalueValueString(const kvlangXvalue_t *v) { append_num_uint(&b, rd64(body)); return kvlangStrbufDetach(&b); } - if (strcmp(k, KVSPACE_KIND_FLOAT32) == 0 || strcmp(k, KVSPACE_KIND_FLOAT64) == 0) { + if (strcmp(k, KVSPACE_KIND_FLOAT32) == 0 || + strcmp(k, KVSPACE_KIND_FLOAT64) == 0) { char tmp[64]; - kvlangFormatFloat(tmp, sizeof tmp, kvlangScalarF64(kvlangXvalueScalar(v))); + kvlangFormatFloat(tmp, sizeof tmp, + kvlangScalarF64(kvlangXvalueScalar(v))); return strdup(tmp); } - if (strcmp(k, KVSPACE_KIND_CHAR_UTF8) == 0 || strcmp(k, KVSPACE_KIND_CHAR_ASCII) == 0) + if (strcmp(k, KVSPACE_KIND_CHAR_UTF8) == 0 || + strcmp(k, KVSPACE_KIND_CHAR_ASCII) == 0) return strndup2(body, blen); if (strcmp(k, KVSPACE_KIND_CHAR) == 0) return utf32_to_utf8(body, blen); - if (strcmp(k, KVSPACE_KIND_RWIR) == 0 || strcmp(k, KVSPACE_KIND_RWIR_OR_RWFUNC) == 0) + if (strcmp(k, KVSPACE_KIND_RWIR) == 0 || + strcmp(k, KVSPACE_KIND_RWIR_OR_RWFUNC) == 0) return strndup2(body + (blen >= 5 ? 5 : 0), blen >= 5 ? blen - 5 : 0); if (strcmp(k, KVSPACE_KIND_RWFUNC) == 0) { kvlangStrbuf_t b; kvlangStrbufInit(&b); - kvlangStrbufPrintf(&b, "r%d/w%d", (blen >= 2 ? rd16(body) : 0), (blen >= 4 ? rd16(body + 2) : 0)); + kvlangStrbufPrintf(&b, "r%d/w%d", (blen >= 2 ? rd16(body) : 0), + (blen >= 4 ? rd16(body + 2) : 0)); return kvlangStrbufDetach(&b); } if (strcmp(k, KVSPACE_KIND_INDEX) == 0) { @@ -473,7 +572,8 @@ char *kvlangXvalueValueString(const kvlangXvalue_t *v) { /* ── 构造 ──────────────────────────────────────────────────────────── */ -void kvlangXvalueNewTlv(kvlangXvalue_t *v, const char *kind, const uint8_t *raw, uint32_t raw_len, int32_t al) { +void kvlangXvalueNewTlv(kvlangXvalue_t *v, const char *kind, const uint8_t *raw, + uint32_t raw_len, int32_t al) { uint32_t len; v->data = kvlangXvalueEncodeTlv(kind, raw, raw_len, al, &len); v->len = len; @@ -481,11 +581,13 @@ void kvlangXvalueNewTlv(kvlangXvalue_t *v, const char *kind, const uint8_t *raw, } /* 显式 ndim/dims 构造(保留多维 shape,供 xv.shape/xv.set 用)。 */ -void kvlangXvalueNewTlvDims(kvlangXvalue_t *v, const char *kind, const uint8_t *raw, uint32_t raw_len, +void kvlangXvalueNewTlvDims(kvlangXvalue_t *v, const char *kind, + const uint8_t *raw, uint32_t raw_len, const int32_t *dims, int32_t ndim) { uint8_t *tmp = NULL; uint32_t tl = 0; - if (kvspaceTlvEncode(kind, raw, raw_len, dims, ndim, &tmp, &tl) != 0 || !tmp) { + if (kvspaceTlvEncode(kind, raw, raw_len, dims, ndim, &tmp, &tl) != 0 || + !tmp) { kvlangXvalueZero(v); return; } @@ -523,9 +625,11 @@ void kvlangXvalueNewBool(kvlangXvalue_t *v, bool b) { } void kvlangXvalueNewCharUtf8(kvlangXvalue_t *v, const char *s) { uint32_t sl = (uint32_t)strlen(s); - kvlangXvalueNewTlv(v, KVSPACE_KIND_CHAR_UTF8, (const uint8_t *)s, sl, (int32_t)sl); + kvlangXvalueNewTlv(v, KVSPACE_KIND_CHAR_UTF8, (const uint8_t *)s, sl, + (int32_t)sl); } -void kvlangXvalueNewCharKind(kvlangXvalue_t *v, const char *kind, const char *s) { +void kvlangXvalueNewCharKind(kvlangXvalue_t *v, const char *kind, + const char *s) { uint32_t sl = (uint32_t)strlen(s); kvlangXvalueNewTlv(v, kind, (const uint8_t *)s, sl, (int32_t)sl); } @@ -536,14 +640,17 @@ void kvlangXvalueNewCharUtf32(kvlangXvalue_t *v, const char *s) { size_t i = 0; while (i < len) { uint32_t cp = utf8_next(s, &i, len); - uint8_t le[4] = {cp & 0xFF, (cp >> 8) & 0xFF, (cp >> 16) & 0xFF, (cp >> 24) & 0xFF}; + uint8_t le[4] = {cp & 0xFF, (cp >> 8) & 0xFF, (cp >> 16) & 0xFF, + (cp >> 24) & 0xFF}; kvlangStrbufPutn(&raw, (const char *)le, 4); } - kvlangXvalueNewTlv(v, KVSPACE_KIND_CHAR, (const uint8_t *)raw.p, (uint32_t)raw.len, (int32_t)(raw.len / 4)); + kvlangXvalueNewTlv(v, KVSPACE_KIND_CHAR, (const uint8_t *)raw.p, + (uint32_t)raw.len, (int32_t)(raw.len / 4)); kvlangStrbufFree(&raw); } /* 指针:head langtype = "*" + target_langtype(目标完整 langtype),body = 目标 key。 */ -void kvlangXvalueNewPtr(kvlangXvalue_t *v, const char *target_langtype, const char *target) { +void kvlangXvalueNewPtr(kvlangXvalue_t *v, const char *target_langtype, + const char *target) { uint8_t *tmp = NULL; uint32_t tl = 0, len = 0; if (kvspaceNewPtr(target_langtype, target, &tmp, &tl) != 0) { @@ -556,14 +663,17 @@ void kvlangXvalueNewPtr(kvlangXvalue_t *v, const char *target_langtype, const ch } /* def rwir 路由头:body 仅计数头 [nr:u16 LE][nw:u16 LE][dynamic:u8],无参数载荷。 * 各参数类型由 kvlangDefRwir 落 /lib//[0,x] 签名行槽(def langtype)。 */ -void kvlangXvalueNewDefRwir(kvlangXvalue_t *v, int32_t nr, int32_t nw, int dynamic) { - uint8_t raw[5] = { nr & 0xFF, (nr >> 8) & 0xFF, nw & 0xFF, (nw >> 8) & 0xFF, dynamic ? 1 : 0 }; +void kvlangXvalueNewDefRwir(kvlangXvalue_t *v, int32_t nr, int32_t nw, + int dynamic) { + uint8_t raw[5] = {nr & 0xFF, (nr >> 8) & 0xFF, nw & 0xFF, (nw >> 8) & 0xFF, + dynamic ? 1 : 0}; kvlangXvalueNewTlv(v, KVSPACE_KIND_DEF_RWIR, raw, 5, 1); } /* 签名行 [0,x] 槽:一个参数的类型定义,body=该参数完整 langtype 串。 */ void kvlangXvalueNewDefLangtype(kvlangXvalue_t *v, const char *langtype) { - kvlangXvalueNewTlv(v, KVSPACE_KIND_DEF_LANGTYPE, (const uint8_t *)langtype, (uint32_t)strlen(langtype), 1); + kvlangXvalueNewTlv(v, KVSPACE_KIND_DEF_LANGTYPE, (const uint8_t *)langtype, + (uint32_t)strlen(langtype), 1); } void kvlangFormatFloat(char *out, size_t cap, double v) { diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" index 16bac8e1..1ee937ed 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" @@ -7,11 +7,14 @@ stringkeymap 数组的**容器值**是一个 storetype=index 的 XValue(langty 子 key `base·i`: ```kv -nums = {10, 20, 30, 40} // 每元素落 nums·0 … nums·3 -for (x in nums) { … } // 遍历散 key 元素 -names = {"apple", "banana", "cherry"} // 变长字符串数组必须 stringkeymap +nums:[int64]·int64 = {10, 20, 30, 40} // 每元素落 nums·[0] … nums·[3] +for (x in nums) { … } // 遍历散 key 元素 +names:[int64]·[]char/utf32 = {"apple", "banana", "cherry"} // 变长字符串数组 ``` +容器字面量的写目标**必须带 map langtype**(见 [[map容器]])——容器值的 langtype 就是它,缺标注 +layout 报错;`{}` 尤其无法推断(空字面量不含任何成员)。 + 散 key 元素以坐标段 `base·[i]` 访问(经 `kv·get`),或以 `for-in` / `kv·listlen` / `kv·listn` 遍历。 坐标段支持多值键(含小数):`geo·[39.9,116.4] = "Beijing"`,物理以字符串格式落 key(见 `tutorial/04-ndarray/geo_coord.kv`)。 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" index 075088b4..8ca2e4d2 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-map\345\256\271\345\231\250.kv" @@ -67,9 +67,14 @@ memindex 的线格式为 dims = `[len, cap, M]` 的矩阵:`len` = 有效成员 `cap × M` 字节,前 `len` 行是有序成员键(NUL 补齐),其余行全 NUL。容量内增删成员 body 长恒定、就地覆 写。`M` 是行宽(行 stride),`cap` 是可翻倍的容量。(此物理模型 struct 同样适用。) -## 空 map 字面量须带类型标注 +## 容器字面量须带类型标注 -`{}` 空 map 字面量**必须**在赋值目标处带类型标注(如 `x:[]char/utf8·int64 = {}`);无标注时 layout 报 -错——空字面量本身无法推断 memitemkey/memitemvalue 类型。 +**任何**容器字面量都必须在写目标处带完整 map langtype(如 `x:[]char/utf8·int64 = {}`、 +`nums:[int64]·int64 = {10, 20, 30}`);无标注时 layout 报错。类型来源二选一:写目标的 `:` 标注, +或签名里形参/写参的声明类型(`{n=42} -> m`,`m:[]char/utf8·int64`)。 + +容器值的 langtype **就是**该 map langtype,kvspace 里**不存在**「langtype = 种类名 `stringkeymap` 的 +值」——runtime 也**不得**在缺类型时退化兜底(无类型即 layout 漏检,直接 fatal)。空字面量 `{}` 尤其无 +法推断:它不含任何成员。 "#### -> /lib/kvlang/spec/类型系统/map容器 } diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/06-\346\216\247\345\210\266\346\265\201\350\204\261\347\263\226.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/06-\346\216\247\345\210\266\346\265\201\350\204\261\347\263\226.kv" index be9cc288..2250bfd4 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/06-\346\216\247\345\210\266\346\265\201\350\204\261\347\263\226.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/06-\346\216\247\345\210\266\346\265\201\350\204\261\347\263\226.kv" @@ -20,7 +20,7 @@ lower 阶段还执行若干降级——这些是 o0 编译器的降级(lowerin - **嵌套调用扁平化**:`f(g(x))` 展开为临时槽序列。 - **复合条件求值**:`if`/`while` 条件非单槽时先求值到临时槽。 -- **散 key 字面量展开**:`base = {v0, v1, …}` 降为 `map(...)`。 +- **散 key 字面量展开**:`base:T = {v0, v1, …}` 降为 `map(...)`(写槽承载 T)。 - **数值算子特化**:按推断类型给算子加 `·` 前缀(`+` → `int64·add`)。 这些降级保留可观察语义与 rwir 调用结构,仅把源码规约为可解释的线性指令树;优化(常量折叠、算子融合等 diff --git "a/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/06-runtime\345\274\200\345\217\221\350\247\204\350\214\203.kv" "b/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/06-runtime\345\274\200\345\217\221\350\247\204\350\214\203.kv" index ec1f324a..0de9bebf 100644 --- "a/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/06-runtime\345\274\200\345\217\221\350\247\204\350\214\203.kv" +++ "b/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/06-runtime\345\274\200\345\217\221\350\247\204\350\214\203.kv" @@ -82,7 +82,7 @@ myrwircaps **只关乎执行位置**(哪张表登记了它、由谁就地跑 写槽(见 [[执行模型]] §五)。示例(networld): ``` -networld/proc·exec rp=[[]stringkeymap, []stringkeymap] wp=[uint8, []uint8, []uint8] +networld/proc·exec rp=[[int64]·[]char/utf32, [int64]·[]char/utf32] wp=[uint8, []uint8, []uint8] networld/fs·read rp=[[]char/utf8|[]char/utf32, int64, int64] wp=[[]uint8] ``` "#### -> /lib/kvlang/spec/runtime语义/runtime开发规范 diff --git "a/stdlib/kvlang/spec/\351\231\204\345\275\225/05-\350\241\250\350\276\276\345\274\217.kv" "b/stdlib/kvlang/spec/\351\231\204\345\275\225/05-\350\241\250\350\276\276\345\274\217.kv" index f7a2528b..4e360cff 100644 --- "a/stdlib/kvlang/spec/\351\231\204\345\275\225/05-\350\241\250\350\276\276\345\274\217.kv" +++ "b/stdlib/kvlang/spec/\351\231\204\345\275\225/05-\350\241\250\350\276\276\345\274\217.kv" @@ -41,7 +41,7 @@ struct_literal = "{" [ ident "=" expr { (stmt_sep) ident "=" expr } ] "}" (* {k1=v1; k2=v2},key 为静态标识符,落 struct *) map_literal = "{" [ expr { (stmt_sep | ",") expr } ] "}" - (* stringkeymap 散 key 形态:{v0, v1, ...},head kind=stringkeymap, + (* 散 key 容器形态:{v0, v1, ...}(写目标须标注 map langtype), 每元素落坐标段 base·[i] 独立 key;字符串数组等变长集合用此形式 *) ``` "## -> /lib/kvlang/spec/附录/表达式 diff --git a/tutorial/01-basics/kv_mkindex_cap.kv b/tutorial/01-basics/kv_mkindex_cap.kv index 05245f80..8cefad9c 100644 --- a/tutorial/01-basics/kv_mkindex_cap.kv +++ b/tutorial/01-basics/kv_mkindex_cap.kv @@ -4,7 +4,7 @@ // 列出 prefix 下的子键并计数 rwfunc count_children(prefix:[]char/utf32) -> (n:int64) { kv·list(prefix) -> names - ndarray·numel(names) -> n + kv·listlen(names) -> n } rwfunc test() -> () { diff --git a/tutorial/01-basics/kv_tree.kv b/tutorial/01-basics/kv_tree.kv index e1cc0d4a..4fcd5e4b 100644 --- a/tutorial/01-basics/kv_tree.kv +++ b/tutorial/01-basics/kv_tree.kv @@ -11,10 +11,10 @@ rwfunc has_field(path:[]char/utf32, key:[]char/utf32) -> (ok:bool) { kv·get(path, key) != None -> ok } -// 列出 prefix 下的子键并计数 +// 列出 prefix 下的子键并计数(成员数查 memindex,非 ndarray 形状) rwfunc count_children(prefix:[]char/utf32) -> (n:int64) { kv·list(prefix) -> names - ndarray·numel(names) -> n + kv·listlen(names) -> n } rwfunc test() -> () { diff --git a/tutorial/04-ndarray/map_coord.kv b/tutorial/04-ndarray/map_coord.kv index aad66e46..246b871b 100644 --- a/tutorial/04-ndarray/map_coord.kv +++ b/tutorial/04-ndarray/map_coord.kv @@ -4,7 +4,7 @@ // a[0] = 10 // a[2] = 30 rwfunc test() -> () { - a = {10, 20, 30} + a:[int64]·int64 = {10, 20, 30} a·[0] -> v0 a·[2] -> v2 println("a[0] =", v0) diff --git a/tutorial/04-ndarray/separated.kv b/tutorial/04-ndarray/separated.kv index a7637407..103682f4 100644 --- a/tutorial/04-ndarray/separated.kv +++ b/tutorial/04-ndarray/separated.kv @@ -2,7 +2,7 @@ // 语义: 字面量初始化默认 compact;array·scatter 把 compact 转散 key,array·compact 反向 // 散 key 元素访问用 b·[i](走 kv·get),compact 用 b[i](走 xv·at) // 期望输出: -// ndarray·numel(b) = 3 +// kv·listlen(b) = 3 // b[0] = 10 // b[2] = 30 // ndarray·numel(c) = 3 @@ -10,8 +10,8 @@ rwfunc test() -> () { a:[]int64 = [10, 20, 30] // 字面量初始化 → compact b = array·scatter(a) // 显式 scatter:compact a → 散 key b(b·[0]..b·[2]) - ndarray·numel(b) -> n1 - println("ndarray·numel(b) =", n1) + kv·listlen(b) -> n1 + println("kv·listlen(b) =", n1) println("b[0] =", b·[0]) // 散 key 元素访问 ·[i] println("b[2] =", b·[2]) c = array·compact(b) // 显式 compact:散 key b → compact c diff --git a/tutorial/04-ndarray/sparse_literal.kv b/tutorial/04-ndarray/sparse_literal.kv index b93cb243..04656eb1 100644 --- a/tutorial/04-ndarray/sparse_literal.kv +++ b/tutorial/04-ndarray/sparse_literal.kv @@ -23,7 +23,7 @@ rwfunc print_sum(nums:[int64]·int64) -> (total:int64) { } rwfunc test() -> () { - nums = {10, 20, 30, 40} + nums:[int64]·int64 = {10, 20, 30, 40} print_sum(nums) -> total println("sum =", total) } diff --git a/tutorial/08-leetcode/056_merge_intervals.kv b/tutorial/08-leetcode/056_merge_intervals.kv index 73a03571..e4cf01de 100644 --- a/tutorial/08-leetcode/056_merge_intervals.kv +++ b/tutorial/08-leetcode/056_merge_intervals.kv @@ -10,17 +10,13 @@ rwfunc bubble_sort(n:int64) -> () { while (i < n - 1) { j = 0 while (j < n - 1 - i) { - kv·get("/tmp/mi", j) -> _t - _t[0] -> a - kv·get("/tmp/mi", j+1) -> _t - _t[0] -> b + kv·get("/tmp/mi", j) -> ta + kv·get("/tmp/mi", j+1) -> tb + ta[0] -> a + tb[0] -> b if (a > b) { - // swap start - kv·get("/tmp/mi", j) -> _t - _t[1] -> a1 - kv·get("/tmp/mi", j+1) -> _t - _t[1] -> b1 - /tmp/mi:[int64]·[2]int64 = {} + ta[1] -> a1 + tb[1] -> b1 _ = kv·set("/tmp/mi", j, [b, b1]) _ = kv·set("/tmp/mi", j+1, [a, a1]) } @@ -33,7 +29,6 @@ rwfunc bubble_sort(n:int64) -> () { rwfunc merge(n:int64) -> (m:int64) { bubble_sort(n) ki = 0 - /tmp/mi:[int64]·[2]int64 = {} kv·get("/tmp/mi", 0) -> cur idx = 1 while (idx < n) { diff --git a/tutorial/14-networld/01-exec.kv b/tutorial/14-networld/01-exec.kv index 626d6306..eceb8949 100644 --- a/tutorial/14-networld/01-exec.kv +++ b/tutorial/14-networld/01-exec.kv @@ -9,24 +9,24 @@ // missing= 127 // FOO=bar123 // run_proc 封装一次命令执行:输入 args/envs,输出退出码 -rwfunc run_proc(args:[]char/utf8·[]char/utf32, - envs:[]char/utf8·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:[int64]·[]char/utf32, + envs:[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } rwfunc test() -> () { - noenv = map() - echoargs = {"sh", "-c", "echo hello from child"} + noenv:[int64]·[]char/utf32 = {} + echoargs:[int64]·[]char/utf32 = {"sh", "-c", "echo hello from child"} run_proc(echoargs, noenv) -> ok println("ok=", ok) - failargs = {"sh", "-c", "exit 7"} + failargs:[int64]·[]char/utf32 = {"sh", "-c", "exit 7"} run_proc(failargs, noenv) -> seven println("seven=", seven) - badargs = {"/no/such/program"} + badargs:[int64]·[]char/utf32 = {"/no/such/program"} run_proc(badargs, noenv) -> missing println("missing=", missing) - envargs = {"sh", "-c", "echo FOO=$FOO"} - envs = {"FOO=bar123"} + envargs:[int64]·[]char/utf32 = {"sh", "-c", "echo FOO=$FOO"} + envs:[int64]·[]char/utf32 = {"FOO=bar123"} run_proc(envargs, envs) -> withenv println("withenv=", withenv) } diff --git a/tutorial/14-networld/02-bash.kv b/tutorial/14-networld/02-bash.kv index 970834fc..e5dcdf1f 100644 --- a/tutorial/14-networld/02-bash.kv +++ b/tutorial/14-networld/02-bash.kv @@ -2,14 +2,14 @@ // 期望输出: // bash 42 // exit= 0 -rwfunc run_proc(args:[]char/utf8·[]char/utf32, - envs:[]char/utf8·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:[int64]·[]char/utf32, + envs:[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } rwfunc test() -> () { - noenv = map() - args = {"bash", "-c", "echo bash $((6*7))"} + noenv:[int64]·[]char/utf32 = {} + args:[int64]·[]char/utf32 = {"bash", "-c", "echo bash $((6*7))"} run_proc(args, noenv) -> code println("exit=", code) } diff --git a/tutorial/14-networld/03-python.kv b/tutorial/14-networld/03-python.kv index 7452773b..a806a561 100644 --- a/tutorial/14-networld/03-python.kv +++ b/tutorial/14-networld/03-python.kv @@ -2,14 +2,14 @@ // 期望输出: // python 1024 // exit= 0 -rwfunc run_proc(args:[]char/utf8·[]char/utf32, - envs:[]char/utf8·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:[int64]·[]char/utf32, + envs:[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } rwfunc test() -> () { - noenv = map() - args = {"python3", "-c", "print('python', 2**10)"} + noenv:[int64]·[]char/utf32 = {} + args:[int64]·[]char/utf32 = {"python3", "-c", "print('python', 2**10)"} run_proc(args, noenv) -> code println("exit=", code) } diff --git a/tutorial/14-networld/04-kvlang.kv b/tutorial/14-networld/04-kvlang.kv index 06e8f1b2..1f7453d1 100644 --- a/tutorial/14-networld/04-kvlang.kv +++ b/tutorial/14-networld/04-kvlang.kv @@ -3,14 +3,14 @@ // 期望输出: // kvlang in kvlang // exit= 0 -rwfunc run_proc(args:[]char/utf8·[]char/utf32, - envs:[]char/utf8·[]char/utf32) -> (code:uint8) { +rwfunc run_proc(args:[int64]·[]char/utf32, + envs:[int64]·[]char/utf32) -> (code:uint8) { networld/proc·exec(args, envs) -> code } rwfunc test() -> () { - noenv = map() - args = {"kvlang", "-c", "print(\"kvlang in kvlang\")"} + noenv:[int64]·[]char/utf32 = {} + args:[int64]·[]char/utf32 = {"kvlang", "-c", "print(\"kvlang in kvlang\")"} run_proc(args, noenv) -> code println("exit=", code) } diff --git a/tutorial/14-networld/05-capture.kv b/tutorial/14-networld/05-capture.kv index 47ee5d07..3dd3bf41 100644 --- a/tutorial/14-networld/05-capture.kv +++ b/tutorial/14-networld/05-capture.kv @@ -8,14 +8,14 @@ // err= oops // run_capture 把 code/out/err 三个结果一并带回调用方; // 捕获句柄无 kindexpr 类型,写参声明为 any -rwfunc run_capture(args:[]char/utf8·[]char/utf32, - envs:[]char/utf8·[]char/utf32) -> (code:uint8, out:any, err:any) { +rwfunc run_capture(args:[int64]·[]char/utf32, + envs:[int64]·[]char/utf32) -> (code:uint8, out:any, err:any) { networld/proc·exec(args, envs) -> code, out, err } rwfunc test() -> () { - noenv = map() - a = {"sh", "-c", "echo hi; echo oops 1>&2"} + noenv:[int64]·[]char/utf32 = {} + a:[int64]·[]char/utf32 = {"sh", "-c", "echo hi; echo oops 1>&2"} run_capture(a, noenv) -> (code, out, err) println("code=", code) println("out=", out) diff --git a/tutorial/14-networld/06-fs.kv b/tutorial/14-networld/06-fs.kv index 0a527ed8..16f3ccc6 100644 --- a/tutorial/14-networld/06-fs.kv +++ b/tutorial/14-networld/06-fs.kv @@ -14,8 +14,8 @@ rwfunc read_text(p:[]char/utf32) -> (s:[]char/utf8, n:int64) { rwfunc test() -> () { p = "/tmp/kvlangfs_demo.txt" - noenv = map() - w = {"sh", "-c", "printf 'hello kvlang' > /tmp/kvlangfs_demo.txt"} + noenv:[int64]·[]char/utf32 = {} + w:[int64]·[]char/utf32 = {"sh", "-c", "printf 'hello kvlang' > /tmp/kvlangfs_demo.txt"} networld/proc·exec(w, noenv) -> wc read_text(p) -> (s, n) println("size=", n) diff --git a/tutorial/14-networld/08-fs-dir.kv b/tutorial/14-networld/08-fs-dir.kv index 325739be..be2044c4 100644 --- a/tutorial/14-networld/08-fs-dir.kv +++ b/tutorial/14-networld/08-fs-dir.kv @@ -1,6 +1,6 @@ // networld/fs·mkdir + list + del:目录创建、列举成员、删除 // fs·mkdir(d) -> 0 创建目录(含缺失父级,幂等) -// fs·list(d) -> names 列目录成员(名字序,[]stringkeymap);散 key 两步遍历 kv·listn+kv·get 取名 +// fs·list(d) -> names 列目录成员(名字序,[int64]·[]char/utf32);散 key 两步遍历 kv·listn+kv·get 取名 // fs·del(p) -> 0 删文件或空目录;非空目录失败返回 -1 // 期望输出: // count= 2 @@ -8,7 +8,7 @@ // name= b.txt // delfile= 0 // deldir= 0 -// print_dir 列目录成员(stringkeymap 两步遍历)并打印成员数与逐个名字 +// print_dir 列目录成员(map 两步遍历)并打印成员数与逐个名字 rwfunc print_dir(d:[]char/utf32) -> () { networld/fs·list(d) -> names kv·listlen(names) -> nc @@ -26,8 +26,8 @@ rwfunc test() -> () { d = "/tmp/kvlang_fs_dir" da = "/tmp/kvlang_fs_dir/a.txt" db = "/tmp/kvlang_fs_dir/b.txt" - noenv = map() - clean = {"rm", "-rf", "/tmp/kvlang_fs_dir"} + noenv:[int64]·[]char/utf32 = {} + clean:[int64]·[]char/utf32 = {"rm", "-rf", "/tmp/kvlang_fs_dir"} networld/proc·exec(clean, noenv) -> rc networld/fs·mkdir(d) -> mc networld/fs·write(da, xv·reinterpret("A", "[]uint8")) -> w1 diff --git a/tutorial/14-networld/09-fs-exists.kv b/tutorial/14-networld/09-fs-exists.kv index b02836b4..b12ba54e 100644 --- a/tutorial/14-networld/09-fs-exists.kv +++ b/tutorial/14-networld/09-fs-exists.kv @@ -11,8 +11,8 @@ rwfunc file_exists(p:[]char/utf32) -> (b:bool) { rwfunc test() -> () { p = "/tmp/kvlang_fs_exists.txt" - noenv = map() - rm = {"rm", "-f", "/tmp/kvlang_fs_exists.txt"} + noenv:[int64]·[]char/utf32 = {} + rm:[int64]·[]char/utf32 = {"rm", "-f", "/tmp/kvlang_fs_exists.txt"} networld/proc·exec(rm, noenv) -> rc file_exists(p) -> b0 println("before=", b0) diff --git a/tutorial/15-vthread/02-crashrecover.kv b/tutorial/15-vthread/02-crashrecover.kv index 95dc35b5..b3687ae8 100644 --- a/tutorial/15-vthread/02-crashrecover.kv +++ b/tutorial/15-vthread/02-crashrecover.kv @@ -17,8 +17,8 @@ rwfunc run_steps(limit:int64) -> (final:int64) { i + 1 -> i kv·set("/crashtest/step", i) println("step", i) - noenv = map() - a = {"sleep", "0.2"} + noenv:[int64]·[]char/utf32 = {} + a:[int64]·[]char/utf32 = {"sleep", "0.2"} networld/proc·exec(a, noenv) -> code } i -> final From 59cb4983922dbd7a50e29f759281bd4d6d2dd13b Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Fri, 11 Sep 2026 10:23:31 +0800 Subject: [PATCH 5/5] =?UTF-8?q?layout/spec/tutorial=20=E8=AF=AD=E4=B9=89?= =?UTF-8?q?=E5=BB=B6=E7=BB=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layout langtype/parser/scanner + spec 04-函数/附录类型表达式文法 + 一批 leetcode 链表 tutorial 对齐。 Co-Authored-By: Claude Opus 4.8 --- layout/src/langtype.rs | 14 +- layout/src/parser.rs | 131 +++++++++++++++++- layout/src/scanner.rs | 5 +- .../03-\345\207\275\346\225\260.kv" | 39 +++++- ...76\345\274\217\346\226\207\346\263\225.kv" | 2 +- tutorial/05-dict/literal_as_arg.kv | 20 +++ tutorial/08-leetcode/002_add_two_numbers.kv | 2 +- tutorial/08-leetcode/021_merge_two_lists.kv | 2 +- tutorial/08-leetcode/083_remove_dup_list.kv | 8 +- tutorial/08-leetcode/094_inorder_traversal.kv | 2 +- tutorial/08-leetcode/100_same_tree.kv | 2 +- tutorial/08-leetcode/104_max_tree_depth.kv | 2 +- tutorial/08-leetcode/141_linked_list_cycle.kv | 2 +- tutorial/08-leetcode/160_intersection_list.kv | 2 +- .../08-leetcode/203_remove_linked_elements.kv | 2 +- .../08-leetcode/206_reverse_linked_list.kv | 4 +- tutorial/08-leetcode/234_palindrome_list.kv | 2 +- tutorial/10-types/06-any-read-param.kv | 19 +++ tutorial/10-types/07-forward-wrapper.kv | 15 ++ tutorial/12-struct/03-linked-list.kv | 2 +- tutorial/12-struct/04-tree.kv | 2 +- tutorial/12-struct/05-graph.kv | 9 +- tutorial/12-struct/06-list-reverse.kv | 4 +- tutorial/12-struct/07-list-local.kv | 2 +- tutorial/12-struct/08-cjk-struct-name.kv | 27 ++++ tutorial/12-struct/09-ptr-param-mutate.kv | 19 +++ tutorial/14-networld/01-exec.kv | 6 +- tutorial/14-networld/10-capture-ext-type.kv | 19 +++ 28 files changed, 326 insertions(+), 39 deletions(-) create mode 100644 tutorial/05-dict/literal_as_arg.kv create mode 100644 tutorial/10-types/06-any-read-param.kv create mode 100644 tutorial/10-types/07-forward-wrapper.kv create mode 100644 tutorial/12-struct/08-cjk-struct-name.kv create mode 100644 tutorial/12-struct/09-ptr-param-mutate.kv create mode 100644 tutorial/14-networld/10-capture-ext-type.kv diff --git a/layout/src/langtype.rs b/layout/src/langtype.rs index de7c6071..5d8f5fa9 100644 --- a/layout/src/langtype.rs +++ b/layout/src/langtype.rs @@ -57,9 +57,14 @@ fn valid_structref(s: &str) -> bool { Some(r) => r, None => return false, }; + // 段名字符集与**标识符**同一套(见 scanner::is_token_delim)——否则 `点` 这类 CJK + // struct 名能作标识符、能作 struct 名,却单单不能出现在参数/返回的类型位置。 !rest.is_empty() && rest.split('/').all(|seg| { - !seg.is_empty() && seg.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') + !seg.is_empty() + && seg + .bytes() + .all(|b| !super::scanner::is_token_delim(b) && b != b'"' && b != b'\'') }) } @@ -161,7 +166,9 @@ 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('*') { + // `*`=ref ptr、`@`=ref @ext(见 [[ref存储位置]]):两者都只是**源码**前缀, + // layout 解析时剥离并落成 head.ref 字节,wire langtype 不含前缀。 + if let Some(rest) = s.strip_prefix('*').or_else(|| s.strip_prefix('@')) { return !rest.is_empty() && valid_atom(rest); } if s.starts_with('/') { @@ -189,6 +196,9 @@ fn expand_atom(s: &str) -> String { if let Some(rest) = s.strip_prefix('*') { return format!("*{}", expand_atom(rest)); } + if let Some(rest) = s.strip_prefix('@') { + return format!("@{}", expand_atom(rest)); + } if s.starts_with('/') { return s.to_string(); } diff --git a/layout/src/parser.rs b/layout/src/parser.rs index d356b399..8c6c8594 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -548,6 +548,38 @@ impl Parser { } fn check_param_types(&mut self, sig: &FuncSig) { + // 指针形参一律拒:kvlang 只有地址传递,且**由 layout 自动落到地址**——函数体里每个形参 + // 引用都被 lower 成 `*[0,±k]`(参数轴槽存的是实参地址,显式解引用才取到值)。 + // 故声明写 `p:*Point` 与 `p:Point` 完全等价,那个 `*` 是纯噪声,只会让人以为 + // 「值/地址」在签名层可选。`*T` 仍用于 struct 字段与局部标注——那里确实要区分 + // 「指向节点的引用」与节点本身。 + for (slot, ret) in sig + .params + .iter() + .map(|p| ("param", p)) + .chain(sig.returns.iter().map(|r| ("return value", r))) + { + if ptr_prefixed(&ret.ty) { + self.errors.push(Diagnostic { + pos: Pos { line: 0, col: 0 }, + message: format!( + "func {}: {slot} {:?}: pointer type {:?} is not allowed — parameter \ + references are already lowered to `*[0,±k]` (the axis slot holds the \ + argument address), so write {:?}", + sig.name, + ret.name, + ret.ty, + ret.ty.split(['|', '·']).map(|a| a.trim_start_matches('*')) + .collect::>().join("·") + ), + warn: false, + info: false, + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); + } + } for param in &sig.params { // 末读参 `...` 是签名层变参标记,校验前剥离(变参落 dynamic 字节,见 [[函数]])。 let ty = param.ty.strip_suffix("...").unwrap_or(¶m.ty); @@ -694,11 +726,48 @@ impl Parser { src_name: String::new(), }); }; + // 别名污染:`q = p` / `p -> q`(p 是读参或已污染)使 q 指到**同一个对象**, + // 于是经 q 写成员就是在写读参的对象。只读性沿别名传播,否则 `q = p; q·x = v` + // 一句话就把「签名诚实原则」洗白了。 + // 别名 → 源头读参(`q = p` 记 q→p;再 `r = q` 追到 p),供诊断点名真凶。 + let mut tainted: std::collections::HashMap = std::collections::HashMap::new(); + let mut all: Vec<&Instruction> = Vec::new(); + collect_body_insts(&func.body, &mut all); + loop { + let mut grew = false; + for inst in &all { + let Some(e) = &inst.expr else { continue }; + if !e.is_leaf() { + continue; + } + let src = if ro.contains(&e.val) { + e.val.clone() + } else { + match tainted.get(&e.val) { + Some(s0) => s0.clone(), + None => continue, + } + }; + for w in &inst.writes { + if w.contains('/') || w.contains('[') || w.contains(keytree::MEMBER_SEP) { + continue; + } + if !ro.contains(w) && !tainted.contains_key(w) { + tainted.insert(w.clone(), src.clone()); + grew = true; + } + } + } + if !grew { + break; + } + } let check = |p: &mut Self, inst: &Instruction, ro: &std::collections::HashSet, fname: &str| { - // 写槽命中读参(非路径/索引/成员) + // 写槽命中读参(非路径/索引/成员)——把参数槽重绑成别的地址,拒绝。 + // 别名局部量不在此列:`cur·next -> cur` 重绑的是局部别名本身,没碰调用方对象。 for w in inst.writes.iter() { if w.contains('/') || w.contains('[') || w.contains(keytree::MEMBER_SEP) { continue; @@ -707,12 +776,36 @@ impl Parser { bad(p, w, fname); } } - // kv.set 成员形(3 读:base, key, val)改写 base 的成员目录,命中读参即拒绝 + // kv.set 成员形(3 读:base, key, val)改写 base 的成员目录,命中读参**或别名**即拒绝。 + // 被写的参数**必须**声明在写参侧(签名诚实原则)——要就地改调用方的对象, + // 就把该参数写到 `-> (p:Point)` 里,而不是留在读参侧靠别名绕。 if let Some(e) = &inst.expr { if e.op == "kv·set" && e.args.len() >= 3 { let base = &e.args[0].val; - if !base.contains('/') && ro.contains(base) { - bad(p, base, fname); + if !base.contains('/') && (ro.contains(base) || tainted.contains_key(base)) { + if ro.contains(base) { + bad(p, base, fname); + } else { + let src = &tainted[base]; + let ty = func + .sig + .params + .iter() + .find(|pp| &pp.name == src) + .map(|pp| pp.ty.clone()) + .unwrap_or_default(); + p.errors.push(Diagnostic { + pos: Pos { line: 1, col: 1 }, + message: format!( + "func {fname}: {base:?} aliases read param {src:?} (`{base} = {src}`); writing its members writes that object — put it on the write side: `-> ({src}:{ty})`" + ), + warn: false, + info: false, + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); + } } } } @@ -2177,10 +2270,40 @@ fn is_fixed_dim_array(t: &str) -> bool { .all(|d| !d.trim().is_empty() && d.trim().bytes().all(|c| c.is_ascii_digit())) } +/// 类型串里是否有原子以 `*`(Ptr 前缀)起头——以 `|`(并)与 `·`(map 键值)切分后逐段看。 +fn ptr_prefixed(ty: &str) -> bool { + ty.split(['|', '·']).any(|a| a.starts_with('*')) +} + fn type_error(_kind: &str) -> String { "unknown type — valid: int8/16/32/64, uint8/16/32/64, float32/64, bool, char/utf32, obj, map, index, char, any, []T, [2,3]T, [?,N]T, A|B".to_string() } +/// 收集函数体(含嵌套块/分支/循环)里的全部指令,供别名污染的不动点迭代用。 +fn collect_body_insts<'a>(body: &'a [Stmt], out: &mut Vec<&'a Instruction>) { + for st in body { + match st { + Stmt::Instruction(s) => out.push(s), + Stmt::Scope(s) => collect_body_insts(&s.body, out), + Stmt::If(s) => { + if let Some(c) = &s.cond { + out.push(c); + } + collect_body_insts(&s.then_, out); + collect_body_insts(&s.else_, out); + } + Stmt::While(s) => { + if let Some(c) = &s.cond { + out.push(c); + } + collect_body_insts(&s.body, out); + } + Stmt::For(s) => collect_body_insts(&s.body, out), + _ => {} + } + } +} + fn walk_read_only( p: &mut Parser, body: &[Stmt], diff --git a/layout/src/scanner.rs b/layout/src/scanner.rs index f6b0f1e0..3b83f437 100644 --- a/layout/src/scanner.rs +++ b/layout/src/scanner.rs @@ -258,7 +258,10 @@ fn advance_line_count( } } -fn is_token_delim(c: u8) -> bool { +/// 标识符/路径段的合法字节判定:凡**不是**分隔符者皆可入标识符(CJK 等多字节字符的 +/// 各字节都不在分隔符集内,故中文标识符天然合法,见 tutorial/01-basics/chinese_ident.kv)。 +/// langtype 的 structref 校验复用它,保证「标识符能写」与「类型名能写」同一套字符集。 +pub(crate) fn is_token_delim(c: u8) -> bool { matches!( c, b' ' | b'\t' 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 ee094c2d..c1dde32a 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" @@ -24,18 +24,29 @@ kvlang 源码只有一种命名单元:**rwfunc**(有指令体的用户函数 调用时**写参 arity 必须全部匹配**(对齐 Go/Rust):`f() -> s` 对多写参函数是错误;不需要的写参**必须 **用 `_` 显式丢弃。 -## 参数类型必须标注 +## 参数类型必须标注,且**不得**写指针前缀 每个读参与写参**必须**声明类型(`name:type_expr`,type_expr 见 [[langtype签名类型表达式]])。 -type_expr 最前可写 `*`/`@` 前缀(如 `*int64`、`@[256,256]uint8`)表示 ptr/ext 存储位置;layout 解析 -时剥离该前缀并落成 head `ref` 字节,wire 层 langtype 串不含前缀(见 [[总体方案]])。`*T` 参数可接受 -**None**(空指针),判定恒写 `p != None`(见 [[ptr]])。缺类型标注是错误: +缺类型标注是错误: ``` error: func f: param "x" has no type annotation — every parameter must declare its type error: func f: return value "r" has no type annotation ``` +参数类型**禁止**写 `*` 前缀(如 `p:*Point` 是错误)。kvlang 只有**地址传递**,而这件事由 layout +自动完成:函数体里每个形参引用都被 lower 成 `*[0,±k]`(参数轴槽存的是实参地址,显式解引用才取到 +值,见 [[指令架构]])。故 `p:*Point` 与 `p:Point` 完全等价,签名里那个 `*` 是纯噪声,只会让人误以为 +「值传递 / 地址传递」在签名层可选: + +``` +error: func f: param "p": pointer type "*/lib/Point" is not allowed — parameter references are + already lowered to `*[0,±k]` (the axis slot holds the argument address), so write "/lib/Point" +``` + +由此,`p:Point` 的 `p·field = v` 改的是**调用方那个对象**(参数槽本身没动);`*T` 仍用于 struct +字段与局部标注——那里确实要区分「指向节点的引用」与节点本身。`@` 前缀(ref=@ext)不受此限。 + ## 参数变参(arity,非类型) 末读参**可**在参数上尾缀 `...` 声明变参(吸收 0..N 个同型实参,如 `A:int64|float64...`)。`...` 是** @@ -64,7 +75,25 @@ error: func f: return value "r" has no type annotation error: func f: read param "A" cannot be used as write slot (read params are read-only) ``` -**签名诚实原则**:函数体内被写的参数**必须**声明在写参侧。 +**只读覆盖到子成员,且别名不洗白**:`p·field = v`、`p[k] = v`、`kv·set(p, k, v)` 同样命中该检查 +——经 desugar 后 base 就是读参裸名。只读性还沿别名传播:`q = p` / `p -> q` 使 `q` 指到**同一个 +对象**,此后 `q·field = v` 写的仍是 `p` 的对象,layout 一并拒绝(否则一句话就能绕开本规则): + +``` +error: func f: read param "p" cannot be used as write slot (read params are read-only) +error: func f: "q" aliases read param "p" (`q = p`); writing its members writes that object + — put it on the write side: `-> (p:/lib/Point)` +``` + +(`q·next -> q` 这类**重绑别名本身**的写槽是裸名、不碰调用方对象,不受影响。) + +**签名诚实原则**:函数体内被写的参数**必须**声明在写参侧。要就地改调用方的对象,就把该参数写到 +`-> (p:Point)` 里——kvlang 只有地址传递,写参 `p` 拿到的就是调用方对象的地址: + +```kv +rwfunc shift(dx:int64) -> (p:Point) { p·x + dx -> p·x } +rwfunc test() -> () { a:Point = {x=1 y=2} shift(10) -> a } +``` ``` // 非法:acc 被写却在读参侧 diff --git "a/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" "b/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" index 2e3f9ae0..261a77b4 100644 --- "a/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" +++ "b/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" @@ -7,7 +7,7 @@ lib kvlang/spec/附录/类型表达式文法 { ``` type_expr = [ ref_prefix ] atom { "|" atom } (* ref 前缀可选;layout 剥离前缀落 head ref 字节,wire langtype 不含前缀;见 [[总体方案]] *) -ref_prefix = "*" | "@" (* * → ref=ptr(指针);@ → ref=@ext(扩展世界) *) +ref_prefix = "*" | "@" (* * → ref=ptr(指针);@ → ref=@ext(扩展世界)。**签名位置禁写 `*`**:参数引用由 layout 自动 lower 成 `*[0,±k]`(只有地址传递),见 [[函数]] *) atom = shape | mapexpr | structref shape = [ dims ] ( any | kind ) mapexpr = memitemkey "·" memitemvalue (* stringkeymap;`·` 连接两条独立 langtype:左=item 键类型、右=item 值类型。声明形如 memheadname:memitemkey·memitemvalue={},见 [[map容器]] *) diff --git a/tutorial/05-dict/literal_as_arg.kv b/tutorial/05-dict/literal_as_arg.kv new file mode 100644 index 00000000..e15cb6c0 --- /dev/null +++ b/tutorial/05-dict/literal_as_arg.kv @@ -0,0 +1,20 @@ +// 语义: 散 key 字面量只能作赋值右值或 for-in 源——作实参须先绑定到带类型的临时变量, +// 容器类型由标注提供(`{…} -> m` 写实参同规则,见 [[map容器]])。 +// layout 对字面量出现在其它位置有专门的诊断(见 error_cases/syntax_error)。 +// 期望输出: +// 3 +// hi +rwfunc count(a:[int64]·[]char/utf32) -> (n:int64) { + kv·listlen(a) -> n +} +rwfunc first(a:[int64]·[]char/utf32) -> (v:[]char/utf32) { + kv·get(a, "[0]") -> v +} +rwfunc test() -> () { + args:[int64]·[]char/utf32 = {"a", "b", "c"} + count(args) -> n + println(n) + one:[int64]·[]char/utf32 = {"hi"} + first(one) -> v + println(v) +} diff --git a/tutorial/08-leetcode/002_add_two_numbers.kv b/tutorial/08-leetcode/002_add_two_numbers.kv index 7f2e777a..6b6d08dc 100644 --- a/tutorial/08-leetcode/002_add_two_numbers.kv +++ b/tutorial/08-leetcode/002_add_two_numbers.kv @@ -10,7 +10,7 @@ struct Node { } lib lc { -rwfunc add(a:*Node, b:*Node) -> () { +rwfunc add(a:Node, b:Node) -> () { carry = 0 result:[int64]·int64 = { } 0 -> ri diff --git a/tutorial/08-leetcode/021_merge_two_lists.kv b/tutorial/08-leetcode/021_merge_two_lists.kv index 6d34bf43..a93651ac 100644 --- a/tutorial/08-leetcode/021_merge_two_lists.kv +++ b/tutorial/08-leetcode/021_merge_two_lists.kv @@ -12,7 +12,7 @@ struct Node { next:*Node=None } -rwfunc merge(a:*Node, b:*Node) -> () { +rwfunc merge(a:Node, b:Node) -> () { pa = a pb = b while (pa != None) { diff --git a/tutorial/08-leetcode/083_remove_dup_list.kv b/tutorial/08-leetcode/083_remove_dup_list.kv index a227af4d..0605ba9e 100644 --- a/tutorial/08-leetcode/083_remove_dup_list.kv +++ b/tutorial/08-leetcode/083_remove_dup_list.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc dedup(head:*Node) -> () { +rwfunc dedup() -> (head:Node) { head -> cur while (cur != None) { cur·next -> nxt @@ -33,7 +33,7 @@ rwfunc dedup(head:*Node) -> () { } } -rwfunc show(head:*Node) -> () { +rwfunc show(head:Node) -> () { head -> p while (p != None) { p·val -> v @@ -47,7 +47,7 @@ rwfunc test() -> () { e2:Node = {val=1 next=&e3} e1:Node = {val=1 next=&e2} &e1 -> eh - dedup(eh) + dedup() -> eh show(eh) f5:Node = {val=3} @@ -56,6 +56,6 @@ rwfunc test() -> () { f2:Node = {val=1 next=&f3} f1:Node = {val=1 next=&f2} &f1 -> fh - dedup(fh) + dedup() -> fh show(fh) } diff --git a/tutorial/08-leetcode/094_inorder_traversal.kv b/tutorial/08-leetcode/094_inorder_traversal.kv index a558445b..96f7edac 100644 --- a/tutorial/08-leetcode/094_inorder_traversal.kv +++ b/tutorial/08-leetcode/094_inorder_traversal.kv @@ -10,7 +10,7 @@ struct TNode { right:*TNode=None } -rwfunc inorder(root:*TNode) -> () { +rwfunc inorder(root:TNode) -> () { // iterative stack simulation si = 0 stack:[int64]·*TNode = {} diff --git a/tutorial/08-leetcode/100_same_tree.kv b/tutorial/08-leetcode/100_same_tree.kv index d9a92aa5..2a152d10 100644 --- a/tutorial/08-leetcode/100_same_tree.kv +++ b/tutorial/08-leetcode/100_same_tree.kv @@ -9,7 +9,7 @@ struct TNode { right:*TNode=None } -rwfunc same(a:*TNode, b:*TNode) -> (ok:int64) { +rwfunc same(a:TNode, b:TNode) -> (ok:int64) { if (a == None || b == None) { if (a == b) { 1 -> ok } else { 0 -> ok } } else { diff --git a/tutorial/08-leetcode/104_max_tree_depth.kv b/tutorial/08-leetcode/104_max_tree_depth.kv index da24daea..3f06eb89 100644 --- a/tutorial/08-leetcode/104_max_tree_depth.kv +++ b/tutorial/08-leetcode/104_max_tree_depth.kv @@ -8,7 +8,7 @@ struct TNode { right:*TNode=None } -rwfunc max_depth(root:*TNode) -> (depth:int64) { +rwfunc max_depth(root:TNode) -> (depth:int64) { if (root == None) { 0 -> depth } else { ld = max_depth(root·left) diff --git a/tutorial/08-leetcode/141_linked_list_cycle.kv b/tutorial/08-leetcode/141_linked_list_cycle.kv index 25259a3d..732fc015 100644 --- a/tutorial/08-leetcode/141_linked_list_cycle.kv +++ b/tutorial/08-leetcode/141_linked_list_cycle.kv @@ -3,7 +3,7 @@ // 期望输出: // 1 // 0 -rwfunc has_cycle(head:*Node) -> (result:int64) { +rwfunc has_cycle(head:Node) -> (result:int64) { result = 0 head -> slow fast = head diff --git a/tutorial/08-leetcode/160_intersection_list.kv b/tutorial/08-leetcode/160_intersection_list.kv index f7770bc6..817e37a8 100644 --- a/tutorial/08-leetcode/160_intersection_list.kv +++ b/tutorial/08-leetcode/160_intersection_list.kv @@ -8,7 +8,7 @@ struct Node { next:*Node=None } -rwfunc get_intersection(ha:*Node, hb:*Node) -> () { +rwfunc get_intersection(ha:Node, hb:Node) -> () { pa = ha pb = hb switch_a = 0 diff --git a/tutorial/08-leetcode/203_remove_linked_elements.kv b/tutorial/08-leetcode/203_remove_linked_elements.kv index f56e9910..12c86d5c 100644 --- a/tutorial/08-leetcode/203_remove_linked_elements.kv +++ b/tutorial/08-leetcode/203_remove_linked_elements.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc remove(head:*Node, val:int64) -> () { +rwfunc remove(head:Node, val:int64) -> () { head -> p while (p != None) { v = p·val diff --git a/tutorial/08-leetcode/206_reverse_linked_list.kv b/tutorial/08-leetcode/206_reverse_linked_list.kv index 1d0e243a..a4b80f3e 100644 --- a/tutorial/08-leetcode/206_reverse_linked_list.kv +++ b/tutorial/08-leetcode/206_reverse_linked_list.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc reverse(head:*Node) -> () { +rwfunc reverse() -> (head:Node) { prev = None head -> cur while (cur != None) { @@ -35,5 +35,5 @@ rwfunc test() -> () { n2:Node = {val=2 next=&n3} n1:Node = {val=1 next=&n2} &n1 -> h - reverse(h) + reverse() -> h } diff --git a/tutorial/08-leetcode/234_palindrome_list.kv b/tutorial/08-leetcode/234_palindrome_list.kv index baaa3b5c..98d4419c 100644 --- a/tutorial/08-leetcode/234_palindrome_list.kv +++ b/tutorial/08-leetcode/234_palindrome_list.kv @@ -8,7 +8,7 @@ struct Node { next:*Node=None } -rwfunc is_pal(head:*Node) -> () { +rwfunc is_pal(head:Node) -> () { arr:[int64]·int64 = {} 0 -> n head -> p diff --git a/tutorial/10-types/06-any-read-param.kv b/tutorial/10-types/06-any-read-param.kv new file mode 100644 index 00000000..11106277 --- /dev/null +++ b/tutorial/10-types/06-any-read-param.kv @@ -0,0 +1,19 @@ +// 语义: any 读参接受任意类型实参——Ptr 的目标类型检查对 any 放行 +// (any 是合法基类型,见 [[文法与合法性]];写参写 any 已可用)。 +// 实测缺陷: any 读参一律 panic「[0,-1] is not a Ptr」——实参 Ptr 的 +// 目标 langtype 与声明的 any 逐字比较不等,被 validate_ptr 拒写。 +// 期望输出: +// 3 +// 42 +rwfunc size_of(a:any) -> (n:int64) { + kv·listlen(a) -> n +} +rwfunc print_any(a:any) -> () { + println(a) +} +rwfunc test() -> () { + m:[int64]·[]char/utf32 = {"a", "b", "c"} + size_of(m) -> n + println(n) + print_any(42) +} diff --git a/tutorial/10-types/07-forward-wrapper.kv b/tutorial/10-types/07-forward-wrapper.kv new file mode 100644 index 00000000..ccfc2afe --- /dev/null +++ b/tutorial/10-types/07-forward-wrapper.kv @@ -0,0 +1,15 @@ +// 语义: 转发/包装函数**必须**写全签名——参数与返回的类型逐一声明,不做隐式推断。 +// 省略标注由 layout 直接报错(`param "a": unknown type (got "")`); +// 这是有意的严格化:类型是调用契约,不在包装层被悄悄抹掉。 +// 期望输出: +// 3 +rwfunc plus(a:int64, b:int64) -> (c:int64) { + a + b -> c +} +rwfunc wrap(a:int64, b:int64) -> (c:int64) { + plus(a, b) -> c +} +rwfunc test() -> () { + wrap(1, 2) -> c + println(c) +} diff --git a/tutorial/12-struct/03-linked-list.kv b/tutorial/12-struct/03-linked-list.kv index d0e53aa2..6692dc99 100644 --- a/tutorial/12-struct/03-linked-list.kv +++ b/tutorial/12-struct/03-linked-list.kv @@ -11,7 +11,7 @@ struct Node { next:*Node=None } -rwfunc show_list(head:*Node) -> () { +rwfunc show_list(head:Node) -> () { head -> cur while (cur != None) { cur·val -> v diff --git a/tutorial/12-struct/04-tree.kv b/tutorial/12-struct/04-tree.kv index ec4194f1..9e8006af 100644 --- a/tutorial/12-struct/04-tree.kv +++ b/tutorial/12-struct/04-tree.kv @@ -11,7 +11,7 @@ struct TNode { right:*TNode=None } -rwfunc inorder(node:*TNode) -> () { +rwfunc inorder(node:TNode) -> () { if (node != None) { node·left -> l inorder(l) diff --git a/tutorial/12-struct/05-graph.kv b/tutorial/12-struct/05-graph.kv index 06166edd..a9ca4791 100644 --- a/tutorial/12-struct/05-graph.kv +++ b/tutorial/12-struct/05-graph.kv @@ -13,7 +13,7 @@ struct GNode { seen:bool=false } -rwfunc dfs(node:*GNode) -> () { +rwfunc dfs() -> (node:GNode) { if (node != None) { node·seen -> s if (!s) { @@ -22,9 +22,9 @@ rwfunc dfs(node:*GNode) -> () { node·val -> v println(v) node·e0 -> a - dfs(a) + dfs() -> a node·e1 -> b - dfs(b) + dfs() -> b } } } @@ -37,5 +37,6 @@ rwfunc test() -> () { &/graph·2 -> /graph·1·e0 &/graph·3 -> /graph·1·e1 &/graph·3 -> /graph·2·e0 - dfs(&/graph·1) + &/graph·1 -> g1 + dfs() -> g1 } diff --git a/tutorial/12-struct/06-list-reverse.kv b/tutorial/12-struct/06-list-reverse.kv index 4423595b..794482d6 100644 --- a/tutorial/12-struct/06-list-reverse.kv +++ b/tutorial/12-struct/06-list-reverse.kv @@ -12,7 +12,7 @@ struct Node { next:*Node=None } -rwfunc reverse_list(head:*Node) -> (new_head:*Node) { +rwfunc reverse_list() -> (head:Node, new_head:Node) { prev = None head -> cur while (cur != None) { @@ -29,7 +29,7 @@ rwfunc test() -> () { n2:Node = {val=20 next=&n3} n1:Node = {val=10 next=&n2} &n1 -> h - reverse_list(h) -> head + reverse_list() -> h, head head -> cur while (cur != None) { cur·val -> v diff --git a/tutorial/12-struct/07-list-local.kv b/tutorial/12-struct/07-list-local.kv index 0dff0a5c..74b1571f 100644 --- a/tutorial/12-struct/07-list-local.kv +++ b/tutorial/12-struct/07-list-local.kv @@ -9,7 +9,7 @@ struct Node { next:*Node=None } -rwfunc sum_list(head:*Node) -> (sum:int64) { +rwfunc sum_list(head:Node) -> (sum:int64) { head -> cur 0 -> sum while (cur != None) { diff --git a/tutorial/12-struct/08-cjk-struct-name.kv b/tutorial/12-struct/08-cjk-struct-name.kv new file mode 100644 index 00000000..fdbdbdaf --- /dev/null +++ b/tutorial/12-struct/08-cjk-struct-name.kv @@ -0,0 +1,27 @@ +// 语义: struct 名(含 CJK)可用于参数/返回类型位置,与 ASCII 名同等—— +// chinese_ident.kv 头部宣称「中文可用于任意标识符位置,包括结构体名」。 +// 容器由**调用方**先声明(memhead 不存在时禁止写 memitem, +// 见 tutorial/12-struct/02-mutate.kv 同款约定)。 +// 期望输出: +// 7 +// 3 +struct 点 { + x:int64=0 + y:int64=0 +} +rwfunc 求和(p:点) -> (s:int64) { + p·x + p·y -> s +} +rwfunc 造点(a:int64, b:int64) -> (r:点) { + a -> r·x + b -> r·y +} +rwfunc test() -> () { + a:点 = {x=3 y=4} + 求和(a) -> s + println(s) + r0:点 = {} + 造点(1, 2) -> r0 + 求和(r0) -> s2 + println(s2) +} diff --git a/tutorial/12-struct/09-ptr-param-mutate.kv b/tutorial/12-struct/09-ptr-param-mutate.kv new file mode 100644 index 00000000..0c8e70d6 --- /dev/null +++ b/tutorial/12-struct/09-ptr-param-mutate.kv @@ -0,0 +1,19 @@ +// 语义: 就地改**调用方的对象**。函数体内被写的参数**必须**声明在写参侧 +// (签名诚实原则,见 04-layout语义/03-函数);kvlang 只有地址传递, +// 写参 p 拿到的就是调用方对象的地址,`p·x = v` 写的就是它。 +// 期望输出: +// 11 +// 2 +struct Point { + x:int64=0 + y:int64=0 +} +rwfunc shift(dx:int64) -> (p:Point) { + p·x + dx -> p·x +} +rwfunc test() -> () { + a:Point = {x=1 y=2} + shift(10) -> a + println(a·x) + println(a·y) +} diff --git a/tutorial/14-networld/01-exec.kv b/tutorial/14-networld/01-exec.kv index eceb8949..0144eedd 100644 --- a/tutorial/14-networld/01-exec.kv +++ b/tutorial/14-networld/01-exec.kv @@ -1,6 +1,8 @@ // networld/proc·exec:同步执行外部命令到结束,返回 uint8 退出码 -// exec(args, envs) -> exitcode。args/envs 为 stringkeymap({..} 字面量),成员 []char/utf32 -// args 首成员=可执行文件;envs 空(map())=继承父环境,非空=完整替换 +// exec(args, envs) -> exitcode。args/envs 为位置型 map({..} 字面量),容器 +// langtype 如 [int64]·[]char/utf32;成员 []char/utf32 +// args 首成员=可执行文件;envs 空(`envs:[int64]·[]char/utf32 = {}`) +// =继承父环境,非空=完整替换 // exitcode:正常=退出码;信号终止=128+signo;spawn 失败/无 argv=127(对齐 shell) // 期望输出: // hello from child diff --git a/tutorial/14-networld/10-capture-ext-type.kv b/tutorial/14-networld/10-capture-ext-type.kv new file mode 100644 index 00000000..cd4f7c64 --- /dev/null +++ b/tutorial/14-networld/10-capture-ext-type.kv @@ -0,0 +1,19 @@ +// 语义: @ext 句柄(ref=2)的源码类型写作 `@T`——源码里指针/扩展引用以 +// `*`/`@` 前缀写在类型最前,layout 解析时剥离并落成 head.ref +// (见 [[ref存储位置]]),故捕获句柄不必再退化成 any。 +// todo P2-3:目前 `@[]char/utf32` 被 layout 判为未知类型。 +// 期望输出: +// hi +// oops +rwfunc run_capture(args:[int64]·[]char/utf32, + envs:[int64]·[]char/utf32) -> (c:uint8, out:@[]char/utf32, + err:@[]char/utf32) { + networld/proc·exec(args, envs) -> c, out, err +} +rwfunc test() -> () { + noenv:[int64]·[]char/utf32 = {} + a:[int64]·[]char/utf32 = {"sh", "-c", "echo hi; echo oops 1>&2"} + run_capture(a, noenv) -> (c, out, err) + println(out) + println(err) +}