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: 23 additions & 0 deletions runtime-rs/src/rwir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@ pub const MYRWIRCAPS: &[(&str, Rwir)] = &[
wp: &["int64"],
},
),
(
"networld/fs·rename",
Rwir {
rp: &["[]char/utf8|[]char/utf32", "[]char/utf8|[]char/utf32"],
wp: &["int64"],
},
),
(
"networld/fs·isutf8",
Rwir {
rp: &["[]char/utf8|[]char/utf32"],
wp: &["bool"],
},
),
(
"networld/fs·append",
Rwir {
Expand Down Expand Up @@ -190,6 +204,11 @@ pub fn rwirmap() -> &'static HashMap<&'static str, &'static Rwir> {

pub fn register(eng: &Engine) {
for (op, r) in MYRWIRCAPS {
// 一致性闸门:登记了签名却漏了 is_inproc 的 op,调用会被当外部 rwir 排队等别人兑现
// (表现为调用挂起,不是报错)——这里一次性喊出来。
if !is_inproc(op) {
eprintln!("[rwir] {op} 在 MYRWIRCAPS 但不在 is_inproc:调用会挂起,请补登记");
}
let rp: Vec<CString> = r.rp.iter().map(|s| cs(s)).collect();
let wp: Vec<CString> = r.wp.iter().map(|s| cs(s)).collect();
let rpp: Vec<*const c_char> = rp.iter().map(|c| c.as_ptr()).collect();
Expand Down Expand Up @@ -228,10 +247,12 @@ pub fn is_inproc(op: &str) -> bool {
| "networld/fs·read"
| "networld/fs·write"
| "networld/fs·append"
| "networld/fs·rename"
| "networld/fs·list"
| "networld/fs·del"
| "networld/fs·mkdir"
| "networld/fs·exists"
| "networld/fs·isutf8"
)
}

Expand All @@ -254,10 +275,12 @@ pub fn dispatch(eng: &Engine, op: &str, pc: &str) {
"networld/fs·read" => networld::fs::read(eng, pc),
"networld/fs·write" => networld::fs::write(eng, pc),
"networld/fs·append" => networld::fs::append(eng, pc),
"networld/fs·rename" => networld::fs::rename(eng, pc),
"networld/fs·list" => networld::fs::list(eng, pc),
"networld/fs·del" => networld::fs::del(eng, pc),
"networld/fs·mkdir" => networld::fs::mkdir(eng, pc),
"networld/fs·exists" => networld::fs::exists(eng, pc),
"networld/fs·isutf8" => networld::fs::isutf8(eng, pc),
"kvlang·vet" => {
let out = kvlanglayout::vet(eng, &eng.read0(pc));
eng.set_kv(&eng.write0(pc), &out);
Expand Down
20 changes: 20 additions & 0 deletions runtime-rs/src/rwir/networld/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ pub fn append(eng: &Engine, pc: &str) {
eng.set_tlv_encoded(&eng.write0(pc), "int64", &n.to_le_bytes(), &[]);
}

/// networld/fs·rename(src, dst) -> code:改名/移动(POSIX rename 语义,同文件系统内原子);
/// 0 成功 / -1 失败。配合 write 临时文件 + rename 可得「要么全有要么全无」的原子落盘。
pub fn rename(eng: &Engine, pc: &str) {
let src = eng.read0(pc);
let dst = eng.read_at(pc, 1);
let code = std::fs::rename(&src, &dst).map(|_| 0i64).unwrap_or(-1);
eng.set_tlv_encoded(&eng.write0(pc), "int64", &code.to_le_bytes(), &[]);
}

/// networld/fs·list(p) -> names:列目录 p 的成员名(名字序)。p 必须是目录,否则空列表。
pub fn list(eng: &Engine, pc: &str) {
let path = eng.read0(pc);
Expand Down Expand Up @@ -96,6 +105,17 @@ pub fn exists(eng: &Engine, pc: &str) {
eng.set_tlv_encoded(&eng.write0(pc), "bool", &[b as u8], &[]);
}

/// networld/fs·isutf8(p) -> b:p 的内容是否是合法 UTF-8(缺失/不可读 = false)。
/// 文本级改写(networld/edit)在落盘前用它把二进制文件挡在外面——解码成 []char/utf32
/// 再写回是有损的,非法字节会被替换字符吃掉。
pub fn isutf8(eng: &Engine, pc: &str) {
let path = eng.read0(pc);
let b = std::fs::read(&path)
.map(|bytes| std::str::from_utf8(&bytes).is_ok())
.unwrap_or(false);
eng.set_tlv_encoded(&eng.write0(pc), "bool", &[b as u8], &[]);
}

fn read_slice(path: &str, start: i64, len: i64) -> Vec<u8> {
let Ok(mut f) = std::fs::File::open(path) else {
return Vec::new();
Expand Down
1 change: 1 addition & 0 deletions runtime/src/myrwircaps.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ static const struct { const char *op; kvlangBuiltinFn fn; } myrwircaps[] = {
{"xv·at", kvlangBuiltinXvAt}, {"xv·set", kvlangBuiltinXvSet}, {"xv·reshape", kvlangBuiltinXvReshape},
{"xv·reinterpret", kvlangBuiltinXvReinterpret},
{"xv·langtype", kvlangBuiltinXvLangtype}, {"xv·bodylen", kvlangBuiltinXvBodylen},
{"xv·parselangtype", kvlangBuiltinXvParselangtype},
{"string·set", kvlangBuiltinStringSet}, {"string·char", kvlangBuiltinStringChar}, {"string·ord", kvlangBuiltinStringOrd},
{"string·cmp", kvlangBuiltinStringCmp}, {"string·find", kvlangBuiltinStringFind}, {"string·len", kvlangBuiltinStringLen},
{"string·slice", kvlangBuiltinStringSlice}, {"string·concat", kvlangBuiltinStringConcat},
Expand Down
1 change: 1 addition & 0 deletions runtime/src/rwir_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ int kvlangBuiltinArray(kvlangFrame_t *f),
kvlangBuiltinXvReinterpret(kvlangFrame_t *f),
kvlangBuiltinXvLangtype(kvlangFrame_t *f),
kvlangBuiltinXvBodylen(kvlangFrame_t *f),
kvlangBuiltinXvParselangtype(kvlangFrame_t *f),
kvlangBuiltinScatter(kvlangFrame_t *f),
kvlangBuiltinCompact(kvlangFrame_t *f),
kvlangBuiltinAppend(kvlangFrame_t *f), kvlangBuiltinSlice(kvlangFrame_t *f),
Expand Down
10 changes: 5 additions & 5 deletions runtime/src/rwir_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ int kvlangBuiltinStringSet(kvlangFrame_t *f) {
int kvlangBuiltinStringChar(kvlangFrame_t *f) {
kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2);
if (n < 2) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.char requires string and index"); }
if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); }
{ const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } }
int idx = (int)kvlangScalarI64(kvlangXvalueScalar(&in[1]));
int rn; uint32_t *r = string_runes(&in[0], &rn);
int rc;
Expand All @@ -104,7 +104,7 @@ int kvlangBuiltinStringChar(kvlangFrame_t *f) {
int kvlangBuiltinStringOrd(kvlangFrame_t *f) {
kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2);
if (n < 1) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.ord requires a string"); }
if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); }
{ const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } }
int rn; uint32_t *r = string_runes(&in[0], &rn);
kvlangXvalue_t e; kvlangXvalueNewInt64(&e, rn == 0 ? -1 : (int64_t)r[0]);
int rc = kvlangBuiltinWriteResult(f, &e); kvlangXvalueFree(&e); free(r); kvlangBuiltinFreeInputs(in, n);
Expand All @@ -124,7 +124,7 @@ int kvlangBuiltinStringCmp(kvlangFrame_t *f) {
int kvlangBuiltinStringFind(kvlangFrame_t *f) {
kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2);
if (n < 2) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.find requires two strings"); }
if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); }
{ const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } }
int hn; uint32_t *hay = string_runes(&in[0], &hn);
int nn; uint32_t *needle = string_runes(&in[1], &nn);
int64_t r = -1;
Expand All @@ -139,7 +139,7 @@ int kvlangBuiltinStringLen(kvlangFrame_t *f) {
kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2);
int len = 0;
if (n > 0) {
if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); }
{ const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } }
if (kvlangXvalueKindIs(&in[0], KVSPACE_KIND_CHAR)) len = kvlangXvalueArrayLen(&in[0]);
else { uint32_t *r = string_runes(&in[0], &len); free(r); }
}
Expand All @@ -151,7 +151,7 @@ int kvlangBuiltinStringLen(kvlangFrame_t *f) {
int kvlangBuiltinStringSlice(kvlangFrame_t *f) {
kvlangXvalue_t in[3]; int n = kvlangBuiltinReadInputs(f, in, 3);
if (n < 3) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: string.slice requires string, start, end"); }
if (var_len_char_err(kvlangXvalueKind(&in[0]))) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", var_len_char_err(kvlangXvalueKind(&in[0]))); }
{ const char *cerr = var_len_char_err(kvlangXvalueKind(&in[0])); if (cerr) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "%s", cerr); } }
int lo = (int)kvlangScalarI64(kvlangXvalueScalar(&in[1])), hi = (int)kvlangScalarI64(kvlangXvalueScalar(&in[2]));
int rn; uint32_t *r = string_runes(&in[0], &rn);
if (lo < 0 || hi > rn || lo > hi) { free(r); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "IndexError: at: slice index out of bounds (lo=%d hi=%d char count=%d)", lo, hi, rn); }
Expand Down
16 changes: 14 additions & 2 deletions runtime/src/rwir_vthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,24 @@ int kvlangBuiltinVthreadRun(kvlangFrame_t *f) {
kvlangBuiltinFreeInputs(in, n);
char *subpc = NULL, *status = NULL;
kvlangVthreadGet(f->kv, vid, &subpc, &status);
free(status); free(vid);
free(status);
if (!subpc || !subpc[0]) { free(subpc); kvlangBuiltinNextPc(f); return 0; }
char *subout = NULL;
int rc = kvlangKvcpuExecuteMode(f->kv, subpc, KVMODE_RETURN, &subout);
free(subpc);
if (rc < 0) { free(subout); return kvlangBuiltinSetErr(f, "RuntimeError: vthread.run failed"); }
if (rc < 0) {
/* 子 vthread 自己失败(status=error)是**子**的终态:监督者要能活着检视
* /vthread/<vid>/‥status 与 ‥error/msg(一个子任务失败不该杀掉整条会话)。
* 只有驱动本身失败才冒泡给父。 */
char *sp = NULL, *st = NULL;
kvlangVthreadGet(f->kv, vid, &sp, &st);
free(sp);
int child_failed = st && strcmp(st, "error") == 0;
free(st); free(vid); free(subout);
if (child_failed) { kvlangBuiltinNextPc(f); return 0; }
return kvlangBuiltinSetErr(f, "RuntimeError: vthread.run failed");
}
free(vid);
if (rc == 1) { *f->yield_pc = subout; return 0; }
kvlangBuiltinNextPc(f);
return 0;
Expand Down
59 changes: 59 additions & 0 deletions runtime/src/rwir_xvalue.c
Original file line number Diff line number Diff line change
Expand Up @@ -368,3 +368,62 @@ int kvlangBuiltinXvBodylen(kvlangFrame_t *f) {
kvlangXvalueFree(&r);
return rc;
}

/* 写第 i 个写参槽(多输出用;kvlangBuiltinWriteResult 只写 writes[0])。 */
static void xv_write_slot(kvlangFrame_t *f, int i, const kvlangXvalue_t *v) {
char *owned = NULL;
const char *fr = f->frame_root;
if (!fr) {
owned = kvlangKeytreeFrameRoot(f->pc);
fr = owned;
}
char *key = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[i].name);
kvlangKvPair_t pair = {key, *v};
char err[256];
kvlangKvSet(f->kv, &pair, 1, err, sizeof err);
free(key);
free(owned);
}

/* xv·parselangtype(v) -> (kind[, ndim]):解析 v 的 langtype(三轴正交的第三轴)。
* kind —— 剥掉前导 [dims] 与 "def " 前缀后的基名:char / char/utf8 / float64 /
* rwfunc / rwir / langtype / stringkeymap / index …
* ndim —— 维数(可省:只绑一个写参时不出)
* 用途:按类型给 KV 成员分类(如 def langtype = 形参槽、def rwir = 路由头),
* 调用方不必对 langtype 串做字符串手术。 */
int kvlangBuiltinXvParselangtype(kvlangFrame_t *f) {
if (f->inst->nw == 0)
return kvlangBuiltinSetErr(
f, "TypeError: xv.parselangtype requires a write param (-> kind[, ndim])");
kvspaceHead_t h;
const char *lt = "";
if (xv_head1(f, &h) == 0)
lt = (const char *)h.langtype;
kvlangLangtype p;
kvlangLangtypeParse((const uint8_t *)lt, &p);
const char *k = p.kind ? p.kind : "";
int32_t kl = p.kind_len;
if (kl >= 4 && strncmp(k, "def ", 4) == 0) {
k += 4;
kl -= 4;
}
char buf[256];
if (kl < 0)
kl = 0;
if ((size_t)kl >= sizeof buf)
kl = (int32_t)sizeof buf - 1;
memcpy(buf, k, (size_t)kl);
buf[kl] = 0;
kvlangXvalue_t vk;
kvlangXvalueNewCharUtf8(&vk, buf);
xv_write_slot(f, 0, &vk);
kvlangXvalueFree(&vk);
if (f->inst->nw > 1) {
kvlangXvalue_t vn;
kvlangXvalueNewInt64(&vn, p.ndim);
xv_write_slot(f, 1, &vn);
kvlangXvalueFree(&vn);
}
kvlangBuiltinNextPc(f);
return 0;
}
55 changes: 55 additions & 0 deletions stdlib/kv.kv
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,59 @@ lib kvspace {
kvspace·set(path, val)
}
}
// members(path) -> (names, n):列目录成员,并**折叠同名孪生条目**。
// 值与同名目录可以共存(见 kvspace/tutorial-durable/10-dir-file-coexist.sh),此时
// 列举会同时给出裸名 X 与目录名 X/(或 X·)——它们是两个实体,**后端不去重**(fs 发两
// 条、redis 视场景而定,无统一规则)。需要「这层有哪些名字」的自省/遍历类调用方一律用
// 本函数:裸名后面紧跟同名 X/(或 X·)时只保留带目录标记的那条,名字取 `X/`。
// 调用方先声明空容器:`m:[int64]·[]char/utf32 = {}` + `kvspace·members(p) -> (m, n)`。
rwfunc members(path:[]char/utf32) -> (
names:*[int64]·[]char/utf32, n:int64) {
kvspace·listlen(path) -> total
n = 0
i = 0
while (i < total) {
kvspace·listn(path, i) -> m8
char/utf32(m8) -> m
skip = false
string·len(m) -> ml
if (ml > 0) {
string·char(m, ml - 1) -> tail
if (tail != "/" && tail != "·") {
// 名字带 `.[…]`/`.src` 的兄弟把裸名与 `<名>/` 隔开
// (列举按 cmp_coord 排序:fields, fields.[0,-1], …, fields/),
// 所以要向后扫完**同前缀**的条目,不能只看下一条。
j = i + 1
while (j < total) {
kvspace·listn(path, j) -> n8
char/utf32(n8) -> nx
string·len(nx) -> xl
if (xl < ml) {
break
}
string·slice(nx, 0, ml) -> head
if (string·cmp(head, m) != 0) {
break
}
m + "/" -> t1
m + "·" -> t2
if (string·cmp(nx, t1) == 0) {
skip = true
break
}
if (string·cmp(nx, t2) == 0) {
skip = true
break
}
j = j + 1
}
}
}
if (!skip) {
array·append(names, m) -> names
n = n + 1
}
i = i + 1
}
}
}
2 changes: 2 additions & 0 deletions stdlib/kvlang/kvlangbrief.kv
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ networld/fs·write("/tmp/x.txt", xv·reinterpret("hi", "[]uint8")) -> n

### 数组(ndarray·* / xv·*)
- `ndarray·numel / dim / shape`;`xv·at / set / reshape / reinterpret / langtype / bodylen`。
- `xv·parselangtype(v) -> (kind[, ndim])`:解析 langtype 基名(剥 `[dims]` 与 `def ` 前缀),
如 `def langtype`→`langtype`、`def rwir`→`rwir`、`[508]char/utf8`→`char/utf8`;用于按类型分类 KV 成员。

### 数学
- 运算符见上;`pow / sqrt / exp / log / abs / sign / neg / min / max`;常量 `/lib/math·Pi` `/lib/math·E` `/lib/math·Tau`。
Expand Down
53 changes: 53 additions & 0 deletions stdlib/kvlang/reflect.kv
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 欢迎加入kvspace世界
// lib kvlang/reflect —— kvlang 自省(rwfunc,kv 源码)。
// fnlist(name) -> listing:枚举 /lib/<name> 下的能力名,逗号连接。
// 分类看 xvalue 的 langtype(三轴正交:ref / storetype / langtype),不看名字猜——
// 用 `xv·parselangtype(v) -> kind` 拿基名(剥掉 [dims] 与 "def " 前缀):
// langtype 形参 / 返回槽(def langtype)—— 不是能力,跳过
// rwir rwir 路由头(def rwir)—— 能力,名字原样
// rwfunc 函数签名槽 —— 能力
// 其他数据 常量等普通 key —— 能力,名字原样
// 每个 rwfunc 在容器里有三条成员:`<fn>.[0,±k]`(形参)、`<fn>.src`(源码副本)、
// `<fn>/`(函数目录条目)。前者按 langtype 跳过,中者是源码副本跳过,后者去尾斜杠即函数名。
// 成员一律经 `kvspace·members` 取:值与同名目录共存时后端会同时给出裸名与目录名(两个实体,
// 后端不去重),members 把它折成一条,这里就不必再猜「裸名是不是目录的孪生」。
// 用途:把一棵 lib 子树的「有什么能力」压成一行文本,供 LLM 生成能力记忆 / 工具清单。
// 命名沿用 Go/Rust 的 reflect(自省);不占用 `lib kvlang`——那是 kvlangbrief 与
// kvlang·vet/·layout/·format/·dump 四个 def rwir 路由头的命名空间。

lib kvlang/reflect {
rwfunc fnlist(name:[]char/utf32) -> (listing:[]char/utf32) {
"" -> listing
"/lib/" + name + "·" -> mdir
ms:[int64]·[]char/utf32 = {}
kvspace·members(mdir) -> (ms, n)
i = 0
while (i < n) {
kvspace·listn(ms, i) -> mk
kvspace·get(ms, mk) -> k8
char/utf32(k8) -> key
string·concat(mdir, key) -> kp
kvspace·get(kp) -> v
xv·parselangtype(v) -> k
if (k != "langtype") {
string·find(key, ".src") -> src
if (src < 0) {
string·len(key) -> klen
string·char(key, klen - 1) -> tail
if (tail == "/") {
klen - 1 -> cut
string·slice(key, 0, cut) -> cap
} else {
key -> cap
}
if (listing == "") {
cap -> listing
} else {
listing + ", " + cap -> listing
}
}
}
i + 1 -> i
}
}
}
Loading
Loading