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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ make all # runtime(C) + layout(Rust) + runti
./bin/kvlang vet my.kv # parse + lower only
./bin/kvlang format my.kv # format to stdout
./bin/kvlang layout my.kv # print the entry point
./bin/kvlang dump my.kv # reconstruct /lib as runnable kvlang
./bin/kvlang printlib my.kv # reconstruct /lib as runnable kvlang (layout result, no .src)
```

`make` targets: `all` · `runtime` · `layout` · `runtime-rs` · `json` · `oldhero` · `test` · `install` · `clean`. `make install` places the libraries in `/usr/lib`, the CLIs (`kvlang`, `kvlanglayout`) in `/usr/bin`, and headers in `/usr/include/kvlang`.
Expand Down Expand Up @@ -279,7 +279,7 @@ Map traversal uses `while` + `kvspace·listlen` + `kvspace·listn` (`for`-`in` i
**`time·` / `time/duration·` / `random·`:** `now` `sub` `add` `before` `after`; `nanos` `millis` `seconds` `minutes` `hours` and the `as_*` forms; `uint64` `int63` `intn`
**`vthread·` / debug:** `create` `run` `call` `sleep` `setstatus`; `debugger` (≡ `vthread·setstatus("paused")`)

`print` / `println` / `cerr` / `printf` / `input` are **not** builtins. In the KV world there is no terminal — only keys and values — so I/O is not a core-language primitive. They are opcodes of the `term` runtime, reached through the `def rwir` route-header mechanism described above. The same mechanism covers `json·to` / `json·from`, `http·call` / `http·get|post|put|del`, `networld/proc·exec`, `networld/fs·size|read|write|append|list|del|mkdir|exists`, and the self-hosting `kvlang·vet|format|layout|dump`.
`print` / `println` / `cerr` / `printf` / `input` are **not** builtins. In the KV world there is no terminal — only keys and values — so I/O is not a core-language primitive. They are opcodes of the `term` runtime, reached through the `def rwir` route-header mechanism described above. The same mechanism covers `json·to` / `json·from`, `http·call` / `http·get|post|put|del`, `networld/proc·exec`, `networld/fs·size|read|write|append|list|del|mkdir|exists`, and the self-hosting `kvlang·vet|format|layout|printlib|printstack`.

```kv
print(x,…) // no spaces, no newline
Expand Down
4 changes: 2 additions & 2 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ make all # runtime(C) + layout(Rust) + runti
./bin/kvlang vet my.kv # 只 parse + lower
./bin/kvlang format my.kv # 格式化到 stdout
./bin/kvlang layout my.kv # 打印入口点
./bin/kvlang dump my.kv # 把 /lib 逆向重建为可运行 kvlang
./bin/kvlang printlib my.kv # 把 /lib 重建为可运行 kvlang(看 layout 结果,不读 .src)
```

`make` 目标:`all` · `runtime` · `layout` · `runtime-rs` · `json` · `oldhero` · `test` · `install` · `clean`。`make install` 把库装到 `/usr/lib`、CLI(`kvlang`、`kvlanglayout`)装到 `/usr/bin`、头文件装到 `/usr/include/kvlang`。
Expand Down Expand Up @@ -279,7 +279,7 @@ map 遍历用 `while` + `kvspace·listlen` + `kvspace·listn`(`for`-`in` 对 m
**`time·` / `time/duration·` / `random·`:** `now` `sub` `add` `before` `after`;`nanos` `millis` `seconds` `minutes` `hours` 及各 `as_*` 形式;`uint64` `int63` `intn`
**`vthread·` / 调试:** `create` `run` `call` `sleep` `setstatus`;`debugger`(≡ `vthread·setstatus("paused")`)

`print` / `println` / `cerr` / `printf` / `input` **不是**内建。KV 世界里没有终端,只有 key 和 value——I/O 不是核心语言原语。它们是 `term` runtime 的操作码,经上文所述的 `def rwir` 路由头机制到达。同一机制覆盖 `json·to` / `json·from`、`http·call` / `http·get|post|put|del`、`networld/proc·exec`、`networld/fs·size|read|write|append|list|del|mkdir|exists`,以及自举的 `kvlang·vet|format|layout|dump`。
`print` / `println` / `cerr` / `printf` / `input` **不是**内建。KV 世界里没有终端,只有 key 和 value——I/O 不是核心语言原语。它们是 `term` runtime 的操作码,经上文所述的 `def rwir` 路由头机制到达。同一机制覆盖 `json·to` / `json·from`、`http·call` / `http·get|post|put|del`、`networld/proc·exec`、`networld/fs·size|read|write|append|list|del|mkdir|exists`,以及自举的 `kvlang·vet|format|layout|printlib|printstack`。

```kv
print(x,…) // 无空格、无换行
Expand Down
10 changes: 5 additions & 5 deletions layout/src/bin/kvlanglayout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
//! kvlanglayout <file.kv> [dsn] 仅 layout,打印 ENTRY=<entry>(默认子命令)
//! kvlanglayout vet <file.kv> 仅校验(parse+lower),打印 ok 或错误
//! kvlanglayout format <file.kv> 格式化输出到 stdout
//! kvlanglayout dump <file.kv> [prefix] [dsn] layout 后把 /lib(或 prefix)子树 dump 为可运行 kvlang + 槽位注释
//! kvlanglayout printlib <file.kv> [prefix] [dsn] layout 后把 /lib(或 prefix)子树重建为可运行 kvlang + 槽位注释(不读 .src)

use std::env;
use std::fs;

use kvlanglayout::{compile, dump, format, init_dirs, vet, Kv};
use kvlanglayout::{compile, format, init_dirs, printlib, vet, Kv};

/// 复刻 Go runtime 的 findEntry:DFS /lib/ 找首个 init(顶层 `init` 或 lib 块内 `pkg·init`)。
fn find_entry(kv: &mut Kv, prefix: &str, pkg: &str) -> String {
Expand Down Expand Up @@ -69,7 +69,7 @@ fn main() {
}
return;
}
if args.len() >= 3 && args[1] == "dump" {
if args.len() >= 3 && args[1] == "printlib" {
let prefix = args.get(3).map(String::as_str).unwrap_or("/lib");
let dsn = args
.get(4)
Expand All @@ -79,11 +79,11 @@ fn main() {
let mut kv = Kv::conn(dsn);
init_dirs(&mut kv).expect("init_dirs");
compile(&mut kv, &src).expect("compile");
print!("{}", dump(&mut kv, prefix));
print!("{}", printlib(&mut kv, prefix));
return;
}
if args.len() < 2 {
eprintln!("usage: kvlanglayout <file.kv> [dsn] | kvlanglayout {{vet|format}} <file.kv> | kvlanglayout dump <file.kv> [prefix] [dsn]");
eprintln!("usage: kvlanglayout <file.kv> [dsn] | kvlanglayout {{vet|format}} <file.kv> | kvlanglayout printlib <file.kv> [prefix] [dsn]");
std::process::exit(1);
}
let dsn = args
Expand Down
50 changes: 43 additions & 7 deletions layout/src/capi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
//! kvlangLayoutVet(src,…) 只校验(parse+lower),不写 kvspace —— 自造代码闸门
//! kvlangLayoutFormat(src,…) 格式化(parse → 规范化源码),不写 kvspace
//! kvlangLayoutCode(src,dsn,…) 从源码串 layout 进 kvspace(LLM 生成即插入,不落盘)
//! kvlangLayoutDump(lib,dsn,…) 把 /lib 子树递归导出为可读文本(审查 lower 后的 code)
//! kvlangLayoutPrintlib(lib,dsn,…) 把 /lib 子树逆向重建为可运行 kvlang(审查 layout 结果,不读 .src)
//! kvlangLayoutPrintstack(vid,dsn,…) 把 /vthread 活动栈渲染成可读文本(帧链 + 实参 + 当前指令)
//! kvlangLayoutFile(path,…) 是 Code 的薄封装(读文件后走同一 core)。源码读回(`.src`)
//! 是纯 KV 读(/lib/<fn>.src),不在此 ABI。
//!
Expand All @@ -17,7 +18,7 @@ use std::fs;
use std::os::raw::c_char;
use std::panic::catch_unwind;

use crate::{compile, dump, format, init_dirs, kvkind, vet, Kv};
use crate::{compile, format, init_dirs, kvkind, printlib, printstack, vet, Kv};

fn cstr<'a>(p: *const c_char) -> &'a str {
if p.is_null() {
Expand Down Expand Up @@ -149,11 +150,12 @@ pub extern "C" fn kvlangLayoutFormat(
}
}

/// dump:把 lib 前缀下的整棵子树重构为可运行的 kvlang 源码(还原 `lib {}` 与 `rwfunc`),
/// printlib:把 lib 前缀下的整棵子树重构为可运行的 kvlang 源码(还原 `lib {}` 与 `rwfunc`),
/// lower 后的原始槽位以 `#` 注释附在各自函数后,供审查。
/// 成功返回 0(out=dump 文本),失败返回 -1(err_out=错误)。
/// **只看 layout 结果**:签名读参数定义键、体读线性指令槽,不读 `.src` 源码副本。
/// 成功返回 0(out=printlib 文本),失败返回 -1(err_out=错误)。
#[no_mangle]
pub extern "C" fn kvlangLayoutDump(
pub extern "C" fn kvlangLayoutPrintlib(
lib: *const c_char,
dsn: *const c_char,
out: *mut c_char,
Expand All @@ -165,7 +167,7 @@ pub extern "C" fn kvlangLayoutDump(
let dsn = cstr(dsn).to_string();
let r = catch_unwind(|| -> Result<String, String> {
let mut kv = Kv::conn(&dsn);
Ok(dump(&mut kv, &lib))
Ok(printlib(&mut kv, &lib))
});
match r {
Ok(Ok(s)) => {
Expand All @@ -177,7 +179,41 @@ pub extern "C" fn kvlangLayoutDump(
-1
}
Err(_) => {
write_out(err_out, err_cap, "dump panicked");
write_out(err_out, err_cap, "printlib panicked");
-1
}
}
}

/// printstack:把 /vthread/<vid> 的活动栈渲染成可读文本(帧链 + 每帧实参 + 顶帧当前指令)。
/// 只读,不改 ‥pc/‥status;暂停/恢复由调用方(harness)负责。
/// 成功返回 0(out=printstack 文本),失败返回 -1(err_out=错误)。
#[no_mangle]
pub extern "C" fn kvlangLayoutPrintstack(
vid: *const c_char,
dsn: *const c_char,
out: *mut c_char,
out_cap: u32,
err_out: *mut c_char,
err_cap: u32,
) -> i32 {
let vid = cstr(vid).to_string();
let dsn = cstr(dsn).to_string();
let r = catch_unwind(|| -> Result<String, String> {
let mut kv = Kv::conn(&dsn);
Ok(printstack(&mut kv, &vid))
});
match r {
Ok(Ok(s)) => {
write_out(out, out_cap, &s);
0
}
Ok(Err(e)) => {
write_out(err_out, err_cap, &e);
-1
}
Err(_) => {
write_out(err_out, err_cap, "printstack panicked");
-1
}
}
Expand Down
145 changes: 138 additions & 7 deletions layout/src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
//! /lib/<pkg>·<name>.[0,±k] 参数定义键(langtype=def langtype, body=名字\x00类型)
//! /lib/<pkg>·<name>/[i,j] 编译后指令(kind=rwir),i 从 1 开始
//! /lib/<pkg>·<name>/‥labels/<l> label → irseq
//! /lib/<pkg>·<name>.src 源码副本(仅 write_func 保留写入,dump 不再依赖)
//! /lib/<pkg>·<name>.src 源码副本(仅 write_func 保留写入,printlib 不再依赖)
//!
//! WriteBody: DFS-number insts (incl. ScopeStmt), emit [i,j], rewrite goto/br labels to irseq.
//! dump: 反向——严格读 /lib/<pkg> 子树重建 AST(签名读参数定义键 .[0,±k]、体读线性槽+‥labels),
//! 不读 .src、不依赖签名行 [0,x] 静态槽。
//! printlib: 反向——严格读 /lib/<pkg> 子树重建 AST(签名读参数定义键 .[0,±k]、体读线性槽+‥labels),
//! 不读 .src、不依赖签名行 [0,x] 静态槽。printstack: /vthread/<vid> 活动栈渲染(只读)。

use std::collections::HashMap;

Expand Down Expand Up @@ -165,9 +165,10 @@ pub fn vet(src: &str) -> Result<(), String> {
Ok(())
}

/// dump:把 /lib 子树重构为可运行的 kvlang 源码(还原 `lib {}` 与 `rwfunc`),
/// lower 后的原始槽位(`key kind:value`)以 `#` 注释附在各自函数后,供审查。
pub fn dump(kv: &mut Kv, lib: &str) -> String {
/// printlib:把 /lib 子树重构为可运行的 kvlang 源码(**只看 layout 结果,不读 `.src`**:
/// 签名读参数定义键、函数体读线性指令槽),lower 后的原始槽位(`key kind:value`)
/// 以 `#` 注释附在各自函数后,供审查。
pub fn printlib(kv: &mut Kv, lib: &str) -> String {
// lib 是 /lib 下任意 prefix,只 dump 该子树。三种情形:
// /lib → 全量
// /lib/foo → 虚拟 pkg(func 存于 /lib/foo·* 扁平目录):走全树后过滤
Expand Down Expand Up @@ -369,7 +370,7 @@ fn emit_func(out: &mut String, f: &DumpFunc, indent: &str) {
out.push('\n');
}

// ── dump 重建:严格从 /lib 子树反出可运行 kvlang(不读 .src)─────────────
// ── printlib 重建:严格从 /lib 子树反出可运行 kvlang(**不读 .src**)───────
//
// 数据来源(对齐 write_func / spec「指令布局格式」):
// 签名 ← [0,0] 计数头(nr,nw,dyn) + 命名参数 Ptr 键(langtype=类型、body=[0,±k] 定位读/写与序)
Expand Down Expand Up @@ -991,6 +992,136 @@ fn is_literal(s: &str) -> bool {
|| (b[0] == b'-' && s.len() > 1)
}

// ── printstack:/vthread 活动栈渲染(帧链 + 每帧实参 + 顶帧当前指令)──────────
//
// 实测帧布局(2026-09):
// /vthread/<vid>/‥pc / ‥status / ‥error/msg vthread 头(仅根)
// /vthread/<vid>/[k]/‥lib / ‥callpc / ‥returnpc 第 k 帧(帧目录,k 为帧号)
// /vthread/<vid>/[k]/[0,±j] 该帧绑定的实参 / 写参
// /vthread/<vid>/[k]/[s0,s1] 帧内指令槽;PC 指向当前那一条
// 只读:不碰 ‥pc/‥status,不写任何槽——暂停/恢复是调用方(harness)的事。

/// 帧目录按帧号升序:`/vthread/<vid>/[k]/`(k 为整数)。
fn frame_dirs(kv: &mut Kv, root: &str) -> Vec<String> {
let mut v: Vec<(i64, String)> = kv
.list(&format!("{root}/"), false, false)
.into_iter()
.filter_map(|n| {
let t = n.trim_end_matches('/');
if !(t.starts_with('[') && t.ends_with(']')) {
return None;
}
t[1..t.len() - 1]
.parse::<i64>()
.ok()
.map(|k| (k, format!("{root}/{t}")))
})
.collect();
v.sort();
v.into_iter().map(|(_, p)| p).collect()
}

/// 值截断到 200 字符(整份文件内容躺在槽里时不该刷屏)。
fn clip(s: String) -> String {
let n = s.chars().count();
if n <= 200 {
return s;
}
let mut out: String = s.chars().take(200).collect();
out.push('…');
out
}

/// 字符串键的纯值(char 类给内容,其余给 kind:value / 空串)。
fn plain_of(kv: &mut Kv, key: &str) -> String {
let d = kv.get_one(key);
if d.is_empty() {
return String::new();
}
let k = kvkind::kind(&d);
if kvkind::is_char_kind(&k) {
kvkind::value_string(&d)
} else {
let s = kvkind::display(&d);
if s == "None" {
String::new()
} else {
s
}
}
}

/// printstack:把 /vthread/<vid> 的活动栈渲染成可读文本;vid 必填
/// (「当前 vthread」由调用方从自己的 PC 前缀取,见 runtime 侧 rwir)。
pub fn printstack(kv: &mut Kv, vid: &str) -> String {
if vid.is_empty() {
return "error: printstack requires vid".to_string();
}
let root = format!("/vthread/{vid}");
// 头部字段都是字符串值:给纯值(不带 kind 前缀);槽位值才用 kind:value(类型有意义)。
let status = plain_of(kv, &format!("{root}/‥status"));
let pc = plain_of(kv, &format!("{root}/‥pc"));
let emsg = plain_of(kv, &format!("{root}/‥error/msg"));
let frames = frame_dirs(kv, &root);
let mut out = String::new();
out.push_str(&format!(
"vthread {vid} status={status} frames={}\n",
frames.len()
));
if !emsg.is_empty() {
out.push_str(&format!("error: {emsg}\n"));
}
if !pc.is_empty() {
out.push_str(&format!("pc: {pc}\n"));
}
// 顶帧 = 其路径是 PC 前缀的那个(帧号最大者优先)。帧内**指令槽不是帧成员**
// (帧只存自己的实参/写参),指令经 `‥lib` 软链到函数指令树、由 PC 定位——
// 所以当前指令直接读 PC 那个键。
let cur = frames
.iter()
.rev()
.find(|f| pc.contains(f.as_str()))
.cloned();
for (i, f) in frames.iter().enumerate() {
let lib = plain_of(kv, &format!("{f}/‥lib"));
out.push_str(&format!("frame[{i}] {lib}\n"));
for key in ["‥callpc", "‥returnpc"] {
let v = plain_of(kv, &format!("{f}/{key}"));
if !v.is_empty() {
out.push_str(&format!(" {key} = {v}\n"));
}
}
// 帧成员 = 实参槽 `[0,±j]` + **命名局部**(变量名即成员名,如 text/total/sz)
// + `‥*`。命名局部要用 expand_ext 才列得出来(kvspace·list 默认不展开)。
let mut names: Vec<String> = kv
.list(&format!("{f}/"), true, true)
.into_iter()
.map(|n| n.trim_end_matches('/').to_string())
.collect();
names.sort();
for n in names {
if n.starts_with('‥') {
continue;
}
if n == "[0,0]" {
continue; // 帧的签名锚点,不是值槽
}
let is_arg = n.starts_with("[0,");
let is_local = !is_arg && !n.starts_with('[');
if !(is_arg || is_local) {
continue;
}
let v = kvkind::display(&kv.get_one(&format!("{f}/{n}")));
out.push_str(&format!(" {n} = {}\n", clip(v)));
}
if cur.as_deref() == Some(f.as_str()) && !pc.is_empty() {
let v = kvkind::display(&kv.get_one(&pc));
out.push_str(&format!(" cur = {}\n", clip(v)));
}
}
out
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
4 changes: 3 additions & 1 deletion layout/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub mod parser;
pub mod scanner;
pub mod symbol;

pub use code::{compile, dump, format, init_dirs, vet, write_func, write_rwir_decl};
pub use code::{
compile, format, init_dirs, printlib, printstack, vet, write_func, write_rwir_decl,
};
pub use ffi::Kv;
pub use scanner::Diagnostic;
3 changes: 2 additions & 1 deletion runtime-rs/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ fn main() {
let bin = format!("{manifest}/../bin"); // kvlang/bin(新构建的 libkvlang_runtime.so)

// KVSPACE_LIB_DIR 可覆盖安装目录:macOS 的 /usr 受 SIP 保护,应指向 <prefix>/lib/kvspace。
let kvspace_dir = std::env::var("KVSPACE_LIB_DIR").unwrap_or_else(|_| "/usr/lib/kvspace".into());
let kvspace_dir =
std::env::var("KVSPACE_LIB_DIR").unwrap_or_else(|_| "/usr/lib/kvspace".into());

println!("cargo:rustc-link-search=native={bin}");
println!("cargo:rustc-link-search=native={kvspace_dir}");
Expand Down
10 changes: 9 additions & 1 deletion runtime-rs/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,14 +221,22 @@ unsafe extern "C" {
err_cap: u32,
) -> c_int;
pub fn kvlangLayoutVet(src: *const c_char, err: *mut c_char, err_cap: u32) -> c_int;
pub fn kvlangLayoutDump(
pub fn kvlangLayoutPrintlib(
lib: *const c_char,
dsn: *const c_char,
out: *mut c_char,
out_cap: u32,
err: *mut c_char,
err_cap: u32,
) -> c_int;
pub fn kvlangLayoutPrintstack(
vid: *const c_char,
dsn: *const c_char,
out: *mut c_char,
out_cap: u32,
err: *mut c_char,
err_cap: u32,
) -> c_int;
}

pub fn cs(s: &str) -> CString {
Expand Down
Loading
Loading