背景
本条已拆分:runtime 侧(取容器成员失败)留在本 issue,layout 侧(成员链 lower 为嵌套 kv·get 的值语义问题)见 #110。两个根因叠加,只修一条链路仍不通。
成员容器(objindex / strkeymapindex)在存储层完全正确,但 kv·get 取「值为容器」的成员时恒返回 None。
kvlangBuiltinKvGet(runtime/src/builtin.c:1409):
char *key = f->inst->nr >= 2 ? member_path(f, in) : ...;
kvlangKvGetOne(f->kv, key, &v);
member_path 拼出 p·k 后单次 Get。但容器成员的 marker 存在 p·k·(尾随分隔符),p·k 这个 key 根本不存在。
实测(json·from("{\"a\":1,\"b\":{\"c\":2},\"d\":[7,8]}") -> /t):
存储层(kvspace CLI 逐 key 可读,结构与 kind 均正确):
/t· → objindex:{3}
/t·a → int64:1
/t·b· → objindex:{1} ← 注意:没有 /t·b 这个 key
/t·b·c → int64:2
/t·d· → strkeymapindex:map[2]{2}
/t·d·[0] → int64:7
/t·d·[1] → int64:8
kvlang 侧:
/t·a → 1 ✅ 成员值是标量
kv·get(/t, "b") → ❌ 成员值是容器
两行只差「成员值是标量还是容器」,这是本 issue 的最小对照。
/t·b·c 与 /t·d·[0] 也失败,但那是 #110 的嵌套问题叠加,不由本条单独负责。
需求描述
kv·get(p, k) 在成员值为容器时返回该容器(marker),与标量成员行为一致。
预期结果
kv·get(/t, "b") 返回 /t·b· 的 objindex 值。
kv·get(/t, "d") 返回 /t·d· 的 strkeymapindex 值。
- 成员确实不存在时返回 None(与「存在但是容器」区分开)。
方案简述
kvlangBuiltinKvGet 取值失败时回退探测 p·k·(marker key);或在 member_path 阶段先判定成员类型再决定拼哪个 key。前者少一次判定但多一次 Get,后者需要父 marker 的成员表信息——需确认 index body 的成员名列表是否已带类型标注(当前 body = [4B count][name\n...],不含类型,可能需要读子节点)。
补 tutorial 用例覆盖「容器成员取值」,纳入回归。
Related: #110, #55, #67
背景
成员容器(objindex / strkeymapindex)在存储层完全正确,但
kv·get取「值为容器」的成员时恒返回 None。kvlangBuiltinKvGet(runtime/src/builtin.c:1409):member_path拼出p·k后单次 Get。但容器成员的 marker 存在p·k·(尾随分隔符),p·k这个 key 根本不存在。实测(
json·from("{\"a\":1,\"b\":{\"c\":2},\"d\":[7,8]}") -> /t):存储层(kvspace CLI 逐 key 可读,结构与 kind 均正确):
kvlang 侧:
两行只差「成员值是标量还是容器」,这是本 issue 的最小对照。
/t·b·c与/t·d·[0]也失败,但那是 #110 的嵌套问题叠加,不由本条单独负责。需求描述
kv·get(p, k)在成员值为容器时返回该容器(marker),与标量成员行为一致。预期结果
kv·get(/t, "b")返回/t·b·的 objindex 值。kv·get(/t, "d")返回/t·d·的 strkeymapindex 值。方案简述
kvlangBuiltinKvGet取值失败时回退探测p·k·(marker key);或在member_path阶段先判定成员类型再决定拼哪个 key。前者少一次判定但多一次 Get,后者需要父 marker 的成员表信息——需确认 index body 的成员名列表是否已带类型标注(当前 body =[4B count][name\n...],不含类型,可能需要读子节点)。补 tutorial 用例覆盖「容器成员取值」,纳入回归。
Related: #110, #55, #67