Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions layout/examples/layout_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,23 @@ use std::fs;

use kvlang_layout::{compile, init_dirs, Kv};

/// 复刻 Go runtime 的 findEntry:DFS /lib/ 找首个 init`,否则 "init"
fn find_entry(kv: &mut Kv, prefix: &str) -> String {
/// 复刻 Go runtime 的 findEntry:DFS /lib/ 找首个 init(顶层 `init` 或 lib 块内 `pkg·init`)
fn find_entry(kv: &mut Kv, prefix: &str, pkg: &str) -> String {
let children = kv.list(prefix, false, true);
for c in &children {
let c = c.trim_end_matches('/');
if c.ends_with("·init") {
return c.to_string();
let base = c.trim_end_matches('/');
if base.ends_with(".src") {
continue;
}
let sub = format!("{prefix}{c}/");
let entry = find_entry(kv, &sub);
let full = if pkg.is_empty() { base.to_string() } else { format!("{pkg}·{base}") };
if base == "init" {
return full;
}
let next_pkg = if pkg.is_empty() { base.to_string() } else { format!("{pkg}/{base}") };
let sub = if c.ends_with('/') { format!("{prefix}{base}/") } else { format!("{prefix}{base}·") };
let entry = find_entry(kv, &sub, &next_pkg);
if !entry.is_empty() {
return format!("{c}/{entry}");
return entry;
}
}
String::new()
Expand All @@ -37,7 +42,7 @@ fn main() {
init_dirs(&mut kv).expect("init_dirs");
compile(&mut kv, &src).expect("compile");

let entry = find_entry(&mut kv, "/lib/");
let entry = find_entry(&mut kv, "/lib/", "");
let entry = if entry.is_empty() { "init" } else { &entry };
println!("ENTRY={entry}");
eprintln!("[rust-layout] {} -> {} (entry={entry})", args[1], dsn);
Expand Down
21 changes: 13 additions & 8 deletions layout/src/capi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,21 @@ use std::panic::catch_unwind;
use crate::{compile, format, init_dirs, kvkind, vet, Kv};

/// 复刻 Go runtime / layout_file 的 findEntry:DFS /lib/ 找首个 `·init`,否则 "init"。
fn find_entry(kv: &mut Kv, prefix: &str) -> String {
fn find_entry(kv: &mut Kv, prefix: &str, pkg: &str) -> String {
for c in kv.list(prefix, false, true) {
let c = c.trim_end_matches('/');
if c.ends_with("·init") {
return c.to_string();
let base = c.trim_end_matches('/');
if base.ends_with(".src") {
continue;
}
let sub = format!("{prefix}{c}/");
let entry = find_entry(kv, &sub);
let full = if pkg.is_empty() { base.to_string() } else { format!("{pkg}·{base}") };
if base == "init" {
return full;
}
let next_pkg = if pkg.is_empty() { base.to_string() } else { format!("{pkg}/{base}") };
let sub = if c.ends_with('/') { format!("{prefix}{base}/") } else { format!("{prefix}{base}·") };
let entry = find_entry(kv, &sub, &next_pkg);
if !entry.is_empty() {
return format!("{c}/{entry}");
return entry;
}
}
String::new()
Expand Down Expand Up @@ -58,7 +63,7 @@ fn layout_core(src: &str, dsn: &str) -> Result<String, String> {
let mut kv = Kv::conn(dsn);
init_dirs(&mut kv)?;
compile(&mut kv, src)?;
let entry = find_entry(&mut kv, "/lib/");
let entry = find_entry(&mut kv, "/lib/", "");
Ok(if entry.is_empty() { "init".to_string() } else { entry })
}

Expand Down
4 changes: 4 additions & 0 deletions layout/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1403,6 +1403,10 @@ impl Parser {
return;
}
let s = inst.writes[0].clone();
// 路径字面量(/ 开头)是完整 key,不是成员写,勿脱糖。
if s.starts_with('/') {
return;
}
let dt = s.find(keytree::MEMBER_SEP).unwrap_or(s.len());
let base = s[..dt].to_string();
let field = s[dt + keytree::MEMBER_SEP.len()..].to_string();
Expand Down
2 changes: 1 addition & 1 deletion layout/src/scanner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use std::fmt;

use super::{keytree, symbol};
use super::symbol;

#[derive(Clone, Copy)]
pub struct Pos {
Expand Down
34 changes: 34 additions & 0 deletions runtime-rwirext_example/go/json/native_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,37 @@ func TestNativeDataTo(t *testing.T) {
t.Errorf("dir with scatter got %s", got)
}
}

// TestLocalContainer:object/stringkeymap 值(模拟 kvlang 局部变量,无尾 / 的帧槽路径)→ json。
func TestLocalContainer(t *testing.T) {
c := rtConn(t)
defer disconnect(c)

// object 局部变量:混合类型 + compact 数组成员
if err := write(c, "/rt/profile", map[string]any{
"name": "alice", "age": json.Number("42"),
"active": true, "score": json.Number("3.14"),
"nums": []interface{}{json.Number("1"), json.Number("2"), json.Number("3")},
}); err != nil {
t.Fatal(err)
}
if got, _ := json.Marshal(build(c, "/rt/profile")); string(got) != `{"active":true,"age":42,"name":"alice","nums":[1,2,3],"score":3.14}` {
t.Errorf("obj got %s", got)
}

// stringkeymap 局部变量:整型散 key 数组
if err := write(c, "/rt/map", []interface{}{json.Number("10"), json.Number("20"), json.Number("30")}); err != nil {
t.Fatal(err)
}
if got, _ := json.Marshal(build(c, "/rt/map")); string(got) != `[10,20,30]` {
t.Errorf("map got %s", got)
}

// stringkeymap 局部变量:字符串散 key 数组
if err := write(c, "/rt/tags", []interface{}{"a", "b", "c"}); err != nil {
t.Fatal(err)
}
if got, _ := json.Marshal(build(c, "/rt/tags")); string(got) != `["a","b","c"]` {
t.Errorf("tags got %s", got)
}
}
30 changes: 21 additions & 9 deletions runtime-rwirext_example/go/json/tutorial/json.kv
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,30 @@
# json·from(json) 反序列化 JSON 写回子树。目录→嵌套对象;compact 数组([]T)与
# 散 key 数组(name[0]..name[N-1])统一序列化为 JSON 数组。
# 期望输出:
# j = {"active":true,"age":42,"cont":[10,20,30],"grp":{"c":1,"d":2},"name":"alice","scat":[10,20,30],"score":3.14}
# j = {"active":true,"age":42,"cont":[10,20,30],"grp":{"c":1,"d":2},"name":"alice","nested":{"level1":{"count":3,"flags":[true,false,true],"level2":"deep"},"list":[1,2,3]},"scat":[10,20,30],"score":3.14}
# name = alice
# c = 1
# cont = [10, 20, 30]
# scat = [10, 20, 30]
# len = 3 3
# level2 = deep
# count = 3
# flags = [true, false, true]
# list = [1, 2, 3]
rwfunc main() -> () {
42 -> /data/age
"alice" -> /data/name
3.14 -> /data/score
true -> /data/active
1 -> /data/grp/c
2 -> /data/grp/d
42 -> /data·age
"alice" -> /data·name
3.14 -> /data·score
true -> /data·active
1 -> /data·grp·c
2 -> /data·grp·d
"deep" -> /data·nested·level1·level2
3 -> /data·nested·level1·count
[true, false, true] -> /data·nested·level1·flags
[1, 2, 3] -> /data·nested·list
a:[]int64 = [10, 20, 30]
[10, 20, 30] -> /data/cont
array·scatter(a) -> /data/scat
[10, 20, 30] -> /data·cont
array·scatter(a) -> /data·scat
json·to(/data) -> j
println("j =", j)
json·from(j) -> /data2
Expand All @@ -33,6 +41,10 @@ rwfunc main() -> () {
ndarray·numel(/data2·cont) -> n1
ndarray·numel(/data2·scat) -> n2
println("len =", n1, n2)
println("level2 =", /data2·nested·level1·level2)
println("count =", /data2·nested·level1·count)
println("flags =", /data2·nested·level1·flags)
println("list =", /data2·nested·list)
}

main()
33 changes: 33 additions & 0 deletions runtime-rwirext_example/go/json/tutorial/json_local.kv
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# json_local: 局部变量 object/stringkeymap → json 转换
# extern: 需先启动 redis 与 json rwirext 两个外部进程
# 1) make kvspace # 起 redis 并清空 kvspace
# 2) go build -o /tmp/json-rwirext ./rwirext/json/cmd/ && /tmp/json-rwirext &
# 3) ./kvlang tutorial/10-rwirext/json_local.kv
# 语义: json·to 接受局部 object/stringkeymap 变量(resolve 到帧槽路径后序列化)。
# object 局部变量({} + kv·set)→ JSON 对象;stringkeymap 局部变量({..} 散 key
# 数组)→ JSON 数组。散 key 数组成员名是坐标段 "[i]"。
# 期望输出:
# profile = {"active":true,"age":42,"name":"alice","nums":[1,2,3],"score":3.14}
# map = [10,20,30]
# tags = ["a","b","c"]
rwfunc main() -> () {
profile = {}
kv·set(profile, "name", "alice")
kv·set(profile, "age", 42)
kv·set(profile, "active", true)
kv·set(profile, "score", 3.14)
nums = [1, 2, 3]
kv·set(profile, "nums", nums)
json·to(profile) -> j1
println("profile =", j1)

a = {10, 20, 30}
json·to(a) -> j2
println("map =", j2)

tags = {"a", "b", "c"}
json·to(tags) -> j3
println("tags =", j3)
}

main()
11 changes: 0 additions & 11 deletions runtime/src/builtin.c
Original file line number Diff line number Diff line change
Expand Up @@ -987,22 +987,11 @@ int kvlangBuiltinScatter(kvlangFrame_t *f) {
char *dst = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name);
int al = kvlangXvalueArrayLen(&in[0]);
if (al > 0) {
char **names = malloc(sizeof(char *) * (size_t)al);
for (int i = 0; i < al; i++) {
kvlangStrbuf_t s; kvlangStrbufInit(&s); kvlangStrbufPrintf(&s, "[%d]", i);
names[i] = kvlangStrbufDetach(&s);
}
char err[256];
int32_t dims[1] = { al };
kvlangXvalue_t mark; map_marker(&mark, dims, 1);
kvlangKvPair_t p0 = { dst, mark }; kvlangKvSet(f->kv, &p0, 1, err, sizeof err);
kvlangXvalueFree(&mark);
char *dir = kvlangKeytreeMember(dst, "");
kvlangXvalue_t mi; memindex(&mi, (const char *const *)names, al);
kvlangKvPair_t p1 = { dir, mi }; kvlangKvSet(f->kv, &p1, 1, err, sizeof err);
kvlangXvalueFree(&mi); free(dir);
for (int i = 0; i < al; i++) free(names[i]);
free(names);
}
for (int i = 0; i < al; i++) {
int64_t c[1] = { i };
Expand Down
47 changes: 46 additions & 1 deletion runtime/src/rwirext.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,42 @@ char *kvlang_rwirextParams(void *kvspace, const char *pc) {
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);
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);
}

/* 解析读参 idx 为字符串(变量 → 帧槽值;路径 → 该路径下的值)。 */
char *kvlang_rwirextResolveRead(void *kvspace, const char *pc, int idx) {
kvlangKv_t k = {kvspace};
Expand All @@ -97,7 +133,16 @@ char *kvlang_rwirextResolveRead(void *kvspace, const char *pc, int idx) {
kvlangXvalueZero(&v);
kvlangBuiltinResolveReadValue(&k, fr, inst.reads[idx].name,
&inst.reads[idx].val, &v);
char *s = kvlangXvalueNone(&v) ? strdup("") : kvlangXvalueValueString(&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);
Expand Down
Loading