diff --git a/.gitignore b/.gitignore index 61105ad8..c4c6d338 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,6 @@ kvtut* # tutorial bench 空产物 /tutorial/benchmark.csv + +# runtime-rs 测试 shm 产物 +kvlangtest* diff --git a/layout/src/code.rs b/layout/src/code.rs index 24c10672..bdf12f0b 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -395,14 +395,20 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { let mut pairs: Vec<(String, Vec)> = Vec::new(); pairs.push(( format!("{func_dir}/[0,0]"), - kvkind::new_rwfunc(seq.len() as i32, nr, nw, fn_.sig.dynamic(), ¶m_types), + kvkind::new_rwfunc(seq.len() as i32, nr, nw, fn_.sig.dynamic()), )); pairs.push(( keytree::lib_src(pkg, &fn_.sig.name), ffi::new_char_byte(fn_.full_text().as_bytes()), )); + // 签名行:每个参数的类型定义落 [0,x] 槽(def langtype),x<0 读参、x>0 写参。 + // 运行期 call 会用实参绑定覆盖同坐标的帧槽,静态类型定义仅供 layout 类型检查/dump。 for (i, p) in fn_.sig.params.iter().enumerate() { let slot = format!("[0,-{}]", i + 1); + pairs.push(( + format!("{func_dir}/{slot}"), + kvkind::new_def_langtype(¶m_types[i]), + )); pairs.push(( format!("{func_dir}/{}", p.name), ffi::new_ptr(kvkind::KIND_CHAR, &slot), @@ -410,6 +416,10 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { } for (i, r) in fn_.sig.returns.iter().enumerate() { let slot = format!("[0,{}]", i + 1); + pairs.push(( + format!("{func_dir}/{slot}"), + kvkind::new_def_langtype(¶m_types[nr as usize + i]), + )); pairs.push(( format!("{func_dir}/{}", r.name), ffi::new_ptr(kvkind::KIND_CHAR, &slot), @@ -498,13 +508,26 @@ pub fn write_rwir_decl(kv: &mut Kv, decl: &RwirDecl) { if !decl.pkg.is_empty() { opcode = format!("{}{}{opcode}", decl.pkg, keytree::MEMBER_SEP); } - let v = kvkind::new_defrwir( - decl.sig.num_reads(), - decl.sig.num_writes(), - decl.sig.dynamic(), - &decl.sig.langtype_list().join("\n"), - ); - let _ = kv.set(&[(keytree::rwir(&opcode), v)]); + let nr = decl.sig.num_reads(); + let nw = decl.sig.num_writes(); + let param_types = decl.sig.langtype_list(); + let base = keytree::rwir(&opcode); + // 路由头:仅计数头(无参数载荷);各参数类型落 [0,x] 签名行槽(def langtype)。 + let mut pairs: Vec<(String, Vec)> = + vec![(base.clone(), kvkind::new_defrwir(nr, nw, decl.sig.dynamic()))]; + for i in 0..nr as usize { + pairs.push(( + format!("{base}/[0,-{}]", i + 1), + kvkind::new_def_langtype(¶m_types[i]), + )); + } + for i in 0..nw as usize { + pairs.push(( + format!("{base}/[0,{}]", i + 1), + kvkind::new_def_langtype(¶m_types[nr as usize + i]), + )); + } + let _ = kv.set(&pairs); } /// Flatten body into seq; ScopeStmt records label → irseq (1-based; [0,0] is the signature). diff --git a/layout/src/kvkind.rs b/layout/src/kvkind.rs index eec272ae..77c72bd1 100644 --- a/layout/src/kvkind.rs +++ b/layout/src/kvkind.rs @@ -23,7 +23,8 @@ pub const KIND_STRUCT: &str = "struct"; // kvlang 自有 kind pub const KIND_RWIR: &str = "rwir"; pub const KIND_RWFUNC: &str = "rwfunc"; -pub const KIND_DEF_RWIR: &str = "defrwir"; +pub const KIND_DEF_RWIR: &str = "def rwir"; +pub const KIND_DEF_LANGTYPE: &str = "def langtype"; pub const KIND_RWIR_OR_RWFUNC: &str = "rwir|rwfunc"; pub const KIND_SCOPE: &str = "scope"; @@ -188,7 +189,7 @@ fn plain_value(k: &str, b: &[u8]) -> String { .collect(), "index" => format!("({})", count_names(b)), // kvlang 自有 kind:body = [2B nr][2B nw][1B dynamic][sig];槽值/调用目标 nr=nw=0,取 sig 即可。 - "rwir" | "rwir|rwfunc" | "rwfunc" | "defrwir" => { + "rwir" | "rwir|rwfunc" | "rwfunc" | "def rwir" => { let (nr, nw, dynamic) = if b.len() >= 5 { ( u16::from_le_bytes([b[0], b[1]]), @@ -214,32 +215,50 @@ pub fn is_char_kind(k: &str) -> bool { k.starts_with("char/") } -// ── kvlang 自有 kind:rwir / defrwir ──────────────────────────────── +// ── kvlang 自有 kind:rwir / def rwir / def langtype ───────────────── // -// body = [2B nr LE][2B nw LE][1B dynamic][sig],array_len=1。 -// rwir=槽值(引用串/opcode),defrwir=定义(签名)。dynamic=末读参变参(arity,非 langtype)。 - -fn rwir_body(nr: i32, nw: i32, dynamic: bool, sig: &str) -> Vec { - let mut raw = Vec::with_capacity(5 + sig.len()); +// 铁律:任何 rwir/rwfunc 值的 body 只记计数头 [2B nr LE][2B nw LE][1B dynamic], +// 禁止携带具体参数。指令槽 [n,x] 的引用串(opcode/操作数名)是「每坐标一个值」, +// 落于 rwir 槽值 body 的尾部载荷;定义(rwfunc/def rwir)的各参数类型分散落在 +// 签名行 [0,x] 槽(各为一个 def langtype 值,body=该参数 langtype 串)。 +// dynamic=末读参变参(arity,非 langtype)。 + +/// 计数头 [nr:u16 LE][nw:u16 LE][dynamic:u8](定义体,无参数载荷)。 +fn counts_body(nr: i32, nw: i32, dynamic: bool) -> Vec { + let mut raw = Vec::with_capacity(5); raw.extend_from_slice(&(nr as u16).to_le_bytes()); raw.extend_from_slice(&(nw as u16).to_le_bytes()); raw.push(dynamic as u8); + raw +} + +/// 指令槽值:计数头 + 单个引用串载荷(opcode/操作数名,每坐标一个值)。 +fn rwir_slot_body(sig: &str) -> Vec { + let mut raw = counts_body(0, 0, false); raw.extend_from_slice(sig.as_bytes()); raw } pub fn new_rwir(nr: i32, nw: i32, sig: &str) -> Vec { - ffi::tlv_encode(KIND_RWIR, &rwir_body(nr, nw, false, sig), 1) + let mut raw = counts_body(nr, nw, false); + raw.extend_from_slice(sig.as_bytes()); + ffi::tlv_encode(KIND_RWIR, &raw, 1) } /// 调用目标(看起来像函数调用的 opcode)→ langtype `rwir|rwfunc` 并列。 /// 静态无法判定是扩展 rwir 还是用户 rwfunc,交 runtime 查 /lib/ 的 XValue kind 分派。 pub fn new_rwir_union(sig: &str) -> Vec { - ffi::tlv_encode(KIND_RWIR_OR_RWFUNC, &rwir_body(0, 0, false, sig), 1) + ffi::tlv_encode(KIND_RWIR_OR_RWFUNC, &rwir_slot_body(sig), 1) +} + +/// def rwir 路由头:仅计数头,无参数载荷。各参数落 [0,x] 签名行槽(def langtype)。 +pub fn new_defrwir(nr: i32, nw: i32, dynamic: bool) -> Vec { + ffi::tlv_encode(KIND_DEF_RWIR, &counts_body(nr, nw, dynamic), 1) } -pub fn new_defrwir(nr: i32, nw: i32, dynamic: bool, sig: &str) -> Vec { - ffi::tlv_encode(KIND_DEF_RWIR, &rwir_body(nr, nw, dynamic, sig), 1) +/// 签名行 [0,x] 槽:一个参数的类型定义,body=该参数完整 langtype 串。 +pub fn new_def_langtype(langtype: &str) -> Vec { + ffi::tlv_encode(KIND_DEF_LANGTYPE, langtype.as_bytes(), 1) } // ── struct 原型(对齐 runtime kvlangBuiltinMemindex)───────────────── @@ -264,21 +283,11 @@ pub fn new_memindex(names: &[String]) -> Vec { // ── kvlang 自有 kind:rwfunc ──────────────────────────────────────── // -// body = [2B nr LE][2B nw LE][1B dynamic][param_types 以 \n 连接],array_len=num_insts。 - -pub fn new_rwfunc( - num_insts: i32, - nr: i32, - nw: i32, - dynamic: bool, - param_types: &[String], -) -> Vec { - let mut raw = Vec::with_capacity(5 + param_types.iter().map(|s| s.len()).sum::()); - raw.extend_from_slice(&(nr as u16).to_le_bytes()); - raw.extend_from_slice(&(nw as u16).to_le_bytes()); - raw.push(dynamic as u8); - raw.extend_from_slice(param_types.join("\n").as_bytes()); - ffi::tlv_encode(KIND_RWFUNC, &raw, num_insts) +// body = 计数头 [2B nr LE][2B nw LE][1B dynamic],array_len=num_insts。 +// 各参数类型落签名行 [0,x] 槽(def langtype),不入 body(见上「铁律」)。 + +pub fn new_rwfunc(num_insts: i32, nr: i32, nw: i32, dynamic: bool) -> Vec { + ffi::tlv_encode(KIND_RWFUNC, &counts_body(nr, nw, dynamic), num_insts) } /// rwfunc body 访问器(layout 读回签名时用)。 @@ -295,13 +304,3 @@ pub fn rwfunc_num_writes(body: &[u8]) -> i32 { } u16::from_le_bytes([body[2], body[3]]) as i32 } - -pub fn rwfunc_param_types(body: &[u8]) -> Vec { - if body.len() <= 5 { - return Vec::new(); - } - String::from_utf8_lossy(&body[5..]) - .split('\n') - .map(|s| s.to_string()) - .collect() -} diff --git a/runtime-rs/src/rwir/http.rs b/runtime-rs/src/rwir/http.rs index a7b5b69f..787a2a37 100644 --- a/runtime-rs/src/rwir/http.rs +++ b/runtime-rs/src/rwir/http.rs @@ -84,6 +84,7 @@ mod tests { rt: std::ptr::null_mut(), kv, dsn: dsn.to_string(), + ext: None, }; let src = r#" lib http { diff --git a/runtime-rs/src/rwir/json.rs b/runtime-rs/src/rwir/json.rs index 8780ef17..d12586a1 100644 --- a/runtime-rs/src/rwir/json.rs +++ b/runtime-rs/src/rwir/json.rs @@ -323,6 +323,7 @@ mod tests { rt: std::ptr::null_mut(), kv, dsn: dsn.to_string(), + ext: None, } } diff --git a/runtime-rs/src/rwir/mod.rs b/runtime-rs/src/rwir/mod.rs index d5a92c3a..7be869c3 100644 --- a/runtime-rs/src/rwir/mod.rs +++ b/runtime-rs/src/rwir/mod.rs @@ -1,6 +1,6 @@ //! rwir —— 注册进 kvlang runtime 的 stdlib,文件/模块按 kvlang lib 嵌套关系组织 //! (lib 用 `/` 嵌套、成员用 `·`,文件树与之 1:1): -//! term : print / println / cerr / input 行输出 + 读 stdin(裸 rwir) +//! term : print / println / cerr / printf / input 行输出 + C 风格格式化 + 读 stdin(裸 rwir) //! json : json·to / json·from KV 子树 ↔ JSON 文本 //! http : http·call 网络抓取(ureq 原生) //! kvlanglayout : kvlanglayout·vet/format/layout/dump 自造 kv 代码入库(layout C ABI) @@ -54,6 +54,13 @@ pub const MYRWIRCAPS: &[(&str, Rwir)] = &[ wp: &[], }, ), + ( + "printf", + Rwir { + rp: &["[]char/utf8|[]char/utf32", "any..."], + wp: &[], + }, + ), ( "json·to", Rwir { @@ -206,6 +213,7 @@ pub fn is_inproc(op: &str) -> bool { "print" | "println" | "cerr" + | "printf" | "input" | "json·to" | "json·from" @@ -235,6 +243,7 @@ pub fn in_myrwircaps(op: &str) -> bool { pub fn dispatch(eng: &Engine, op: &str, pc: &str) { match op { "print" | "println" | "cerr" => term::print_line(eng, pc), + "printf" => term::printf(eng, pc), "input" => term::input(eng, pc), "json·to" => json::to(eng, pc), "json·from" => json::from(eng, pc), diff --git a/runtime-rs/src/rwir/term.rs b/runtime-rs/src/rwir/term.rs index 99047e53..377670a4 100644 --- a/runtime-rs/src/rwir/term.rs +++ b/runtime-rs/src/rwir/term.rs @@ -1,5 +1,6 @@ -//! rwir `print` / `println` / `cerr` / `input`。它们不是 kvlang runtime 的 builtin, -//! 由本 runtime 就地实现:print* 拼行输出;`input` 读一行 stdin 回填写槽。 +//! rwir `print` / `println` / `cerr` / `printf` / `input`。它们不是 kvlang runtime 的 builtin, +//! 由本 runtime 就地实现:print* 拼行输出;`printf` C 风格格式化串到 stdout(不自动换行,换行靠 `\n`); +//! `input` 读一行 stdin 回填写槽。 //! TTY 走 rustyline(方向键移动光标 / 上下翻历史 / 按字符退格);管道走 read_line(脚本/测试)。 use std::cell::RefCell; @@ -40,6 +41,275 @@ pub fn print_line(eng: &Engine, pc: &str) { } } +/// printf(fmt, args...):C 风格格式化串就地输出到 stdout(不自动换行,换行靠 fmt 里的 `\n`)。 +/// 支持转换 `d i u o x X f F e E g G c s %`、标志 `- 0 + 空格 #`、宽度、`.精度`; +/// 实参经 read_at 取显示串,数值型转换按需 parse 回 i64/f64。 +pub fn printf(eng: &Engine, pc: &str) { + let params = take(unsafe { kvlangRwirextParams(eng.kv, cs(pc).as_ptr()) }); + let nslots = params.split('\n').count().saturating_sub(1); + if nslots == 0 { + return; + } + let fmt = eng.read_at(pc, 0); + let args: Vec = (1..nslots as i32).map(|i| eng.read_at(pc, i)).collect(); + print!("{}", format_c(&fmt, &args)); + std::io::stdout().flush().ok(); +} + +struct Spec { + left: bool, + zero: bool, + plus: bool, + space: bool, + alt: bool, + width: Option, + prec: Option, + conv: char, +} + +fn format_c(fmt: &str, args: &[String]) -> String { + let c: Vec = fmt.chars().collect(); + let (mut out, mut ai, mut i) = (String::new(), 0usize, 0usize); + while i < c.len() { + if c[i] != '%' { + out.push(c[i]); + i += 1; + continue; + } + i += 1; + if i < c.len() && c[i] == '%' { + out.push('%'); + i += 1; + continue; + } + let (mut left, mut zero, mut plus, mut space, mut alt) = + (false, false, false, false, false); + while i < c.len() { + match c[i] { + '-' => left = true, + '0' => zero = true, + '+' => plus = true, + ' ' => space = true, + '#' => alt = true, + _ => break, + } + i += 1; + } + let mut width = None; + while i < c.len() && c[i].is_ascii_digit() { + width = Some(width.unwrap_or(0) * 10 + (c[i] as usize - '0' as usize)); + i += 1; + } + let mut prec = None; + if i < c.len() && c[i] == '.' { + i += 1; + let mut p = 0usize; + while i < c.len() && c[i].is_ascii_digit() { + p = p * 10 + (c[i] as usize - '0' as usize); + i += 1; + } + prec = Some(p); + } + while i < c.len() && matches!(c[i], 'l' | 'h' | 'z' | 'j' | 't' | 'L') { + i += 1; + } + if i >= c.len() { + out.push('%'); + break; + } + let spec = Spec { + left, + zero, + plus, + space, + alt, + width, + prec, + conv: c[i], + }; + i += 1; + let arg = args.get(ai).map(String::as_str).unwrap_or(""); + ai += 1; + out.push_str(&render(&spec, arg)); + } + out +} + +/// 数值符号:负号恒先;正数按 `+` / 空格标志补前缀。 +fn sign_of(neg: bool, plus: bool, space: bool) -> &'static str { + if neg { + "-" + } else if plus { + "+" + } else if space { + " " + } else { + "" + } +} + +/// 宽度填充:prefix(符号 / `0x`)恒留左侧,零填充嵌在 prefix 与数字之间。 +fn pad(prefix: &str, body: &str, s: &Spec, zeroable: bool) -> String { + let len = prefix.chars().count() + body.chars().count(); + match s.width { + Some(w) if w > len => { + let fill = w - len; + if s.left { + format!("{prefix}{body}{}", " ".repeat(fill)) + } else if s.zero && zeroable { + format!("{prefix}{}{body}", "0".repeat(fill)) + } else { + format!("{}{prefix}{body}", " ".repeat(fill)) + } + } + _ => format!("{prefix}{body}"), + } +} + +fn as_i64(arg: &str) -> i64 { + let t = arg.trim(); + t.parse::() + .or_else(|_| t.parse::().map(|f| f as i64)) + .unwrap_or(0) +} +fn as_f64(arg: &str) -> f64 { + arg.trim().parse::().unwrap_or(0.0) +} + +fn render(s: &Spec, arg: &str) -> String { + match s.conv { + 'd' | 'i' => { + let v = as_i64(arg); + let mag = (v as i128).unsigned_abs().to_string(); + let mag = zpad_prec(mag, s.prec); + pad(sign_of(v < 0, s.plus, s.space), &mag, s, true) + } + 'u' => pad( + "", + &zpad_prec((as_i64(arg) as u64).to_string(), s.prec), + s, + true, + ), + 'o' => { + let d = format!("{:o}", as_i64(arg) as u64); + let pfx = if s.alt && !d.starts_with('0') { + "0" + } else { + "" + }; + pad(pfx, &zpad_prec(d, s.prec), s, true) + } + 'x' | 'X' => { + let u = as_i64(arg) as u64; + let d = if s.conv == 'x' { + format!("{u:x}") + } else { + format!("{u:X}") + }; + let pfx = match (s.alt && u != 0, s.conv == 'x') { + (true, true) => "0x", + (true, false) => "0X", + _ => "", + }; + pad(pfx, &zpad_prec(d, s.prec), s, true) + } + 'f' | 'F' => { + let v = as_f64(arg); + let d = format!("{:.*}", s.prec.unwrap_or(6), v.abs()); + pad(sign_of(v.is_sign_negative(), s.plus, s.space), &d, s, true) + } + 'e' | 'E' => { + let v = as_f64(arg); + let d = fmt_e(v.abs(), s.prec.unwrap_or(6), s.conv == 'E'); + pad(sign_of(v.is_sign_negative(), s.plus, s.space), &d, s, true) + } + 'g' | 'G' => { + let v = as_f64(arg); + let d = fmt_g(v.abs(), s.prec.unwrap_or(6), s.conv == 'G', s.alt); + pad(sign_of(v.is_sign_negative(), s.plus, s.space), &d, s, true) + } + 'c' => { + let ch: String = if arg.chars().count() == 1 { + arg.to_string() + } else { + arg.trim() + .parse::() + .ok() + .and_then(char::from_u32) + .map(|c| c.to_string()) + .unwrap_or_default() + }; + pad("", &ch, s, false) + } + 's' => { + let body: String = match s.prec { + Some(p) => arg.chars().take(p).collect(), + None => arg.to_string(), + }; + pad("", &body, s, false) + } + other => { + let mut r = String::from('%'); + r.push(other); + r + } + } +} + +/// 整数精度:至少 prec 位,不足前补 0(区别于宽度的空格填充)。 +fn zpad_prec(d: String, prec: Option) -> String { + match prec { + Some(p) if d.chars().count() < p => format!("{}{d}", "0".repeat(p - d.chars().count())), + _ => d, + } +} + +/// C 风格 `%e`:尾数 . 精度 + `e±dd`(指数至少两位)。 +fn fmt_e(mag: f64, prec: usize, upper: bool) -> String { + let (mant, exp) = decompose(mag, prec); + let e = if upper { 'E' } else { 'e' }; + let sign = if exp < 0 { '-' } else { '+' }; + format!("{mant}{e}{sign}{:02}", exp.unsigned_abs()) +} + +/// 把 mag 拆成 [尾数串(含 prec 位小数, ∈[1,10)), 十进制指数];含进位归一。mag≥0。 +fn decompose(mag: f64, prec: usize) -> (String, i32) { + if mag == 0.0 { + return (format!("{:.*}", prec, 0.0), 0); + } + let mut exp = mag.log10().floor() as i32; + let mut mant = format!("{:.*}", prec, mag / 10f64.powi(exp)); + if mant.parse::().unwrap_or(0.0) >= 10.0 { + exp += 1; + mant = format!("{:.*}", prec, mag / 10f64.powi(exp)); + } + (mant, exp) +} + +/// C 风格 `%g`:按指数在 %e / %f 间取短者,精度=有效位数;除非 `#`,去尾零与尾点。 +fn fmt_g(mag: f64, prec: usize, upper: bool, alt: bool) -> String { + let p = prec.max(1); + let exp = if mag == 0.0 { + 0 + } else { + decompose(mag, p - 1).1 + }; + let mut d = if exp < -4 || exp >= p as i32 { + fmt_e(mag, p - 1, upper) + } else { + format!("{:.*}", (p as i32 - 1 - exp).max(0) as usize, mag) + }; + if !alt && d.contains('.') { + let (num, tail) = match d.find(['e', 'E']) { + Some(k) => (d[..k].to_string(), d[k..].to_string()), + None => (d.clone(), String::new()), + }; + let num = num.trim_end_matches('0').trim_end_matches('.'); + d = format!("{num}{tail}"); + } + d +} + thread_local! { static EDITOR: RefCell = RefCell::new(rustyline::DefaultEditor::new().expect("初始化 line editor 失败")); @@ -90,3 +360,50 @@ fn pipe_readline(prompt: &str) -> String { line.trim_end_matches(['\n', '\r']).to_string() } } + +#[cfg(test)] +mod tests { + // 逐条对拍真实 C printf(gcc -O2):实参以 read_at 的显示串形态传入。 + fn f(fmt: &str, args: &[&str]) -> String { + super::format_c(fmt, &args.iter().map(|s| s.to_string()).collect::>()) + } + #[test] + fn c_printf_parity() { + assert_eq!(f("[%d,%d]", &["3", "7"]), "[3,7]"); + assert_eq!( + f("|%5d|%-5d|%05d|%+d|% d|", &["42", "42", "42", "42", "42"]), + "| 42|42 |00042|+42| 42|" + ); + assert_eq!(f("|%5d|%05d|", &["-42", "-42"]), "| -42|-0042|"); + assert_eq!( + f("|%x|%X|%#x|%08x|%o|", &["255", "255", "255", "255", "64"]), + "|ff|FF|0xff|000000ff|100|" + ); + assert_eq!(f("|%u|", &["42"]), "|42|"); + assert_eq!( + f( + "|%f|%.2f|%8.2f|%-8.2f|%+.1f|", + &["3.14159", "3.14159", "3.14159", "3.14159", "3.14159"] + ), + "|3.141590|3.14| 3.14|3.14 |+3.1|" + ); + assert_eq!(f("|%f|", &["-2.5"]), "|-2.500000|"); + assert_eq!( + f("|%e|%E|%.2e|", &["12345.678", "12345.678", "12345.678"]), + "|1.234568e+04|1.234568E+04|1.23e+04|" + ); + assert_eq!( + f( + "|%g|%g|%g|%.3g|", + &["0.0001", "100000.0", "3.14159", "3.14159"] + ), + "|0.0001|100000|3.14159|3.14|" + ); + assert_eq!( + f("|%s|%10s|%-10s|%.3s|", &["hi", "hi", "hi", "hello"]), + "|hi| hi|hi |hel|" + ); + assert_eq!(f("|%c|%c|%%|", &["A", "66"]), "|A|B|%|"); + assert_eq!(f("|%.3d|%6.3d|", &["42", "42"]), "|042| 042|"); + } +} diff --git a/runtime/src/const.h b/runtime/src/const.h index d518ac66..c19bb4df 100644 --- a/runtime/src/const.h +++ b/runtime/src/const.h @@ -30,7 +30,8 @@ #define KVSPACE_KIND_EXT_INDEX "extindex" #define KVSPACE_KIND_RWIR "rwir" #define KVSPACE_KIND_RWFUNC "rwfunc" -#define KVSPACE_KIND_DEF_RWIR "defrwir" +#define KVSPACE_KIND_DEF_RWIR "def rwir" +#define KVSPACE_KIND_DEF_LANGTYPE "def langtype" #define KVSPACE_KIND_RWIR_OR_RWFUNC "rwir|rwfunc" #define KVSPACE_KIND_SCOPE "scope" #define KVSPACE_KIND_STRUCT "struct" diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index a89ed0f5..b9724cfe 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -201,7 +201,8 @@ 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 kvlangXvalueNewRwir(kvlangXvalue_t *v, int32_t nr, int32_t nw, const char *sig); +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, const int32_t *dims, int32_t ndim); diff --git a/runtime/src/rwirext.c b/runtime/src/rwirext.c index 3ec7fa47..761e9eb1 100644 --- a/runtime/src/rwirext.c +++ b/runtime/src/rwirext.c @@ -48,17 +48,49 @@ static void register_vids(kvlangKv_t *k, const char *opcode) { free(base); } -int kvlangRwirextRegister(void *kvspace, const char *opcode, int32_t nr, - int32_t nw, const char *sig) { +/* 写 [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); +} + +/* 注册一条 rwir:/lib/ 路由头(仅计数头)+ 各参数落 [0,x] 签名行槽(def langtype)。 + * 参数类型逐条传入(读参 rp[0..nr]、写参 wp[0..nw]),不再拼签名串——避免其它 runtime + * 把「拼接 sig 串」误当注册标准。末读参尾缀 "..." → 变参 arity(落 dynamic 字节)。 */ +int kvlangRwirextRegister(void *kvspace, const char *opcode, + const char *const *rp, int32_t nr, + const char *const *wp, int32_t nw) { kvlangKv_t k = {kvspace}; - char *key = kvlangKeytreeRwir(opcode); - kvlangXvalue_t v; - kvlangXvalueNewRwir(&v, nr, nw, sig); - kvlangKvPair_t p = {key, v}; + char *base = kvlangKeytreeRwir(opcode); char err[256]; - int rc = kvlangKvSet(&k, &p, 1, err, sizeof err); - kvlangXvalueFree(&v); - free(key); + + 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); + + 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; diff --git a/runtime/src/xvalue.c b/runtime/src/xvalue.c index ac77ac7c..d8f0303c 100644 --- a/runtime/src/xvalue.c +++ b/runtime/src/xvalue.c @@ -556,36 +556,16 @@ void kvlangXvalueNewPtr(kvlangXvalue_t *v, const char *target_langtype, const ch v->len = len; v->borrowed = 0; } -/* body = [nr:u16 LE][nw:u16 LE][dynamic:u8][clean_sig]。 - * 末读参尾缀 "..." 是签名层变参标记:此处剥离,置 dynamic=1,langtype 串保持纯净。 */ -void kvlangXvalueNewRwir(kvlangXvalue_t *v, int32_t nr, int32_t nw, const char *sig) { - size_t sl = strlen(sig); - uint8_t *raw = malloc(5 + sl); - raw[0] = nr & 0xFF; - raw[1] = (nr >> 8) & 0xFF; - raw[2] = nw & 0xFF; - raw[3] = (nw >> 8) & 0xFF; - raw[4] = 0; - memcpy(raw + 5, sig, sl); - /* 末读参尾缀 "..." 是签名层变参标记:此处剥离,置 dynamic=1,langtype 串保持纯净。 */ - if (nr > 0) { - char *base = (char *)raw + 5; - char *p = base; - for (int i = 0; i < nr - 1; i++) { - char *nl = strchr(p, '\n'); - if (!nl) { p = base + sl; break; } - p = nl + 1; - } - char *nl = strchr(p, '\n'); - size_t seg = nl ? (size_t)(nl - p) : (size_t)(base + sl - p); - if (seg >= 3 && memcmp(p + seg - 3, "...", 3) == 0) { - raw[4] = 1; - memmove(p + seg - 3, p + seg, (size_t)(base + sl - (p + seg))); - sl -= 3; - } - } - kvlangXvalueNewTlv(v, KVSPACE_KIND_DEF_RWIR, raw, (uint32_t)(5 + sl), 1); - free(raw); +/* def rwir 路由头:body 仅计数头 [nr:u16 LE][nw:u16 LE][dynamic:u8],无参数载荷。 + * 各参数类型由 kvlangRwirextRegister 落 /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 }; + 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); } void kvlangFormatFloat(char *out, size_t cap, double v) { diff --git a/tutorial/11-string/09-printf.kv b/tutorial/11-string/09-printf.kv new file mode 100644 index 00000000..2c170685 --- /dev/null +++ b/tutorial/11-string/09-printf.kv @@ -0,0 +1,22 @@ +// printf:C 风格格式化串输出到 stdout(runtime-rs term rwir,对齐 C printf / Go fmt.Printf) +// 语义: printf(fmt, args...) 逐字节对齐 C printf;不自动换行,换行靠 fmt 里的 \n。 +// 转换 d i u o x X f F e E g G c s %;标志 - 0 + 空格 #;宽度;.精度。 +// 期望输出: +// [3,7] +// | 42|42 |00042|+42| +// |ff|FF|000000ff|100| +// pi=3.14 +// | 3.14|3.14 | +// |hi| hi|hi |hel| +// AB +// 50% +rwfunc test() -> () { + printf("[%d,%d]\n", 3, 7) + printf("|%5d|%-5d|%05d|%+d|\n", 42, 42, 42, 42) + printf("|%x|%X|%08x|%o|\n", 255, 255, 255, 64) + printf("pi=%.2f\n", 3.14159) + printf("|%8.2f|%-8.2f|\n", 3.14159, 3.14159) + printf("|%s|%10s|%-10s|%.3s|\n", "hi", "hi", "hi", "hello") + printf("%c%c\n", 65, 66) + printf("%d%%\n", 50) +}