From 4492de83c2b1b12ba4d3a76852a6a19560ba7250 Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Mon, 17 Aug 2026 06:59:01 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20layout=20=E4=B8=8B=E6=B2=89?= =?UTF-8?q?=E4=B8=BA=E7=8B=AC=E7=AB=8B=20Rust=20=E5=AD=90=20crate=EF=BC=8C?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=20root=20Rust=20crate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 layout/(kvlang-layout):parser/scanner/ast/lower/code/keytree/symbol/kvkind/builtin, build.rs 链接 kvspace-durable cdylib,只走 C ABI。 - 删除 root Rust crate:lib.rs、keytree/、kvcpu/、rwir/、vthread/、logx/、cmd/kvlang/、Cargo.toml 等 (runtime 侧 kvcpu/rwir/vthread 后续作 core-runtime 再迁入)。 - Makefile 去掉 rust/rust-test 目标。 - 新增 tutorial/test_crosslang.py。 Co-Authored-By: Claude --- .cargo/config.toml | 3 - Cargo.lock | 16 - Cargo.toml | 16 - Makefile | 9 +- build.rs | 1 - cmd/kvlang/main.rs | 15 - cmd/kvlang/main.rsx | 23 - keytree/const.rs | 89 -- keytree/dev.rs | 1 - keytree/entry.rs | 1 - keytree/frame.rs | 1 - keytree/member.rs | 1 - keytree/mod.rs | 1 - keytree/sys.rs | 1 - keytree/vthread.rs | 1 - kvcpu/controlflow.rs | 179 ---- kvcpu/cpu.rs | 130 --- kvcpu/debug.rs | 1 - kvcpu/execute.rs | 182 ---- kvcpu/mod.rs | 5 - kvcpu/sched.rs | 1 - layout/.gitignore | 1 + layout/Cargo.lock | 7 + layout/Cargo.toml | 12 + layout/Makefile | 13 + layout/build.rs | 9 + layout/examples/dump.rs | 25 + layout/examples/layout_file.rs | 44 + layout/src/ast.rs | 564 ++++++++++ layout/src/builtin.rs | 182 ++++ layout/src/code.rs | 285 ++++++ layout/src/ffi.rs | 316 ++++++ layout/src/keytree.rs | 151 +++ layout/src/kvkind.rs | 130 +++ layout/src/lib.rs | 18 + layout/src/lower.rs | 648 ++++++++++++ layout/src/parser.rs | 1417 ++++++++++++++++++++++++++ layout/src/scanner.rs | 454 +++++++++ layout/src/symbol.rs | 127 +++ layout/tests/pipeline_test.rs | 89 ++ layout/tests/redis_roundtrip_test.rs | 46 + lib.rs | 6 - logx/logx.rs | 19 - logx/mod.rs | 1 - rwir/builtin/arith.rs | 51 - rwir/builtin/array.rs | 1 - rwir/builtin/bit.rs | 10 - rwir/builtin/builtin.rs | 4 - rwir/builtin/call.rs | 1 - rwir/builtin/cast.rs | 8 - rwir/builtin/cmp.rs | 34 - rwir/builtin/coerce.rs | 1 - rwir/builtin/debugger.rs | 1 - rwir/builtin/dict.rs | 1 - rwir/builtin/helper.rs | 1 - rwir/builtin/io.rs | 12 - rwir/builtin/kvop.rs | 18 - rwir/builtin/logic.rs | 8 - rwir/builtin/math.rs | 23 - rwir/builtin/mod.rs | 11 - rwir/builtin/ops.rs | 69 -- rwir/builtin/resolve.rs | 1 - rwir/builtin/string.rs | 38 - rwir/builtin/strops.rs | 1 - rwir/builtin/time.rs | 37 - rwir/control.rs | 1 - rwir/frame.rs | 1 - rwir/instruction.rs | 23 - rwir/mod.rs | 2 - rwir/pc.rs | 1 - rwir/rwir.rs | 89 -- rwir/tensor.rs | 1 - tutorial/test_crosslang.py | 113 ++ vthread/mod.rs | 1 - vthread/vthread.rs | 11 - 75 files changed, 4652 insertions(+), 1163 deletions(-) delete mode 100644 .cargo/config.toml delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml delete mode 100644 build.rs delete mode 100644 cmd/kvlang/main.rs delete mode 100644 cmd/kvlang/main.rsx delete mode 100644 keytree/const.rs delete mode 100644 keytree/dev.rs delete mode 100644 keytree/entry.rs delete mode 100644 keytree/frame.rs delete mode 100644 keytree/member.rs delete mode 100644 keytree/mod.rs delete mode 100644 keytree/sys.rs delete mode 100644 keytree/vthread.rs delete mode 100644 kvcpu/controlflow.rs delete mode 100644 kvcpu/cpu.rs delete mode 100644 kvcpu/debug.rs delete mode 100644 kvcpu/execute.rs delete mode 100644 kvcpu/mod.rs delete mode 100644 kvcpu/sched.rs create mode 100644 layout/.gitignore create mode 100644 layout/Cargo.lock create mode 100644 layout/Cargo.toml create mode 100644 layout/Makefile create mode 100644 layout/build.rs create mode 100644 layout/examples/dump.rs create mode 100644 layout/examples/layout_file.rs create mode 100644 layout/src/ast.rs create mode 100644 layout/src/builtin.rs create mode 100644 layout/src/code.rs create mode 100644 layout/src/ffi.rs create mode 100644 layout/src/keytree.rs create mode 100644 layout/src/kvkind.rs create mode 100644 layout/src/lib.rs create mode 100644 layout/src/lower.rs create mode 100644 layout/src/parser.rs create mode 100644 layout/src/scanner.rs create mode 100644 layout/src/symbol.rs create mode 100644 layout/tests/pipeline_test.rs create mode 100644 layout/tests/redis_roundtrip_test.rs delete mode 100644 lib.rs delete mode 100644 logx/logx.rs delete mode 100644 logx/mod.rs delete mode 100644 rwir/builtin/arith.rs delete mode 100644 rwir/builtin/array.rs delete mode 100644 rwir/builtin/bit.rs delete mode 100644 rwir/builtin/builtin.rs delete mode 100644 rwir/builtin/call.rs delete mode 100644 rwir/builtin/cast.rs delete mode 100644 rwir/builtin/cmp.rs delete mode 100644 rwir/builtin/coerce.rs delete mode 100644 rwir/builtin/debugger.rs delete mode 100644 rwir/builtin/dict.rs delete mode 100644 rwir/builtin/helper.rs delete mode 100644 rwir/builtin/io.rs delete mode 100644 rwir/builtin/kvop.rs delete mode 100644 rwir/builtin/logic.rs delete mode 100644 rwir/builtin/math.rs delete mode 100644 rwir/builtin/mod.rs delete mode 100644 rwir/builtin/ops.rs delete mode 100644 rwir/builtin/resolve.rs delete mode 100644 rwir/builtin/string.rs delete mode 100644 rwir/builtin/strops.rs delete mode 100644 rwir/builtin/time.rs delete mode 100644 rwir/control.rs delete mode 100644 rwir/frame.rs delete mode 100644 rwir/instruction.rs delete mode 100644 rwir/mod.rs delete mode 100644 rwir/pc.rs delete mode 100644 rwir/rwir.rs delete mode 100644 rwir/tensor.rs create mode 100644 tutorial/test_crosslang.py delete mode 100644 vthread/mod.rs delete mode 100644 vthread/vthread.rs diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index c0d7466d..00000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[build] -rustflags = ["-L", "/home/peng.li24/github.com/array2d/kvspace-c/build"] - diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 9ed0b4df..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,16 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "kvlang" -version = "0.1.0" -dependencies = [ - "libc", -] - -[[package]] -name = "libc" -version = "0.2.189" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index a1b9cd8d..00000000 --- a/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "kvlang" -version = "0.1.0" -edition = "2021" -description = "kvlang VM — Rust runtime (kvcpu executor)" -license = "MIT" - -[lib] -path = "lib.rs" - -[[bin]] -name = "kvlang" -path = "cmd/kvlang/main.rs" - -[dependencies] -libc = "0.2" diff --git a/Makefile b/Makefile index e583a709..f374e7a9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build test vet clean kvspace install rust rust-test +.PHONY: build test vet clean kvspace install export GOPROXY ?= https://goproxy.cn,direct PREFIX ?= ~/.local @@ -9,16 +9,9 @@ build: install -d $(PREFIX)/bin install kvlang $(PREFIX)/bin/kvlang -rust: - cargo build --manifest-path $(CURDIR)/Cargo.toml - -rust-test: - python3 tutorial/test.py --runtime=rust - vet: go vet ./... clean: go clean - cargo clean --manifest-path $(CURDIR)/Cargo.toml rm -f kvlang diff --git a/build.rs b/build.rs deleted file mode 100644 index c53ffd89..00000000 --- a/build.rs +++ /dev/null @@ -1 +0,0 @@ -fn main() { println!("cargo:rustc-link-search=native=/home/peng.li24/github.com/array2d/kvspace-c/build"); println!("cargo:rustc-link-lib=kvspace-c"); } diff --git a/cmd/kvlang/main.rs b/cmd/kvlang/main.rs deleted file mode 100644 index a590f3ed..00000000 --- a/cmd/kvlang/main.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::env; -use kvlang::kvcpu::cpu::KVCpu; -use kvlang::kvcpu::execute; - -fn main() { - let func = env::args().nth(1).unwrap_or_else(|| "main".into()); - let shm = env::var("KVSPACE_SHM").expect("KVSPACE_SHM not set"); - let kv_ptr = KVCpu::open(&shm).expect("open shm"); - let cpu = KVCpu::new(kv_ptr, "rust"); - if let Err(e) = execute::execute(&cpu, &func) { - kvlang::logx::logx::error(format_args!("{}", e)); - std::process::exit(1); - } - KVCpu::close(kv_ptr); -} diff --git a/cmd/kvlang/main.rsx b/cmd/kvlang/main.rsx deleted file mode 100644 index 2e5b7bd8..00000000 --- a/cmd/kvlang/main.rsx +++ /dev/null @@ -1,23 +0,0 @@ -// kvlang Rust CLI — entry point for "run" command. -// This is the Rust counterpart to cmd/kvlang/main.go (Go toolchain CLI) -// and cmd/kvlang/main.cpp (C++ runtime CLI). - -use std::env; - -fn main() { - let args: Vec = env::args().collect(); - if args.len() < 2 { - eprintln!("usage: kvlang run "); - std::process::exit(1); - } - - match args[1].as_str() { - "run" => { - println!("[rust] run: not yet implemented"); - } - cmd => { - eprintln!("unknown command: {cmd}"); - std::process::exit(1); - } - } -} diff --git a/keytree/const.rs b/keytree/const.rs deleted file mode 100644 index 8ad704d3..00000000 --- a/keytree/const.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Path constants — matching keytree/const.go, keytree/frame.go, keytree/vthread.go. - -pub const PATH_SEP: &str = "/"; -pub const PATH_SEG_LIB: &str = "lib"; -pub const PATH_SEG_VTHREAD: &str = "vthread"; -pub const RUNTIME_MEMBER_SEP: &str = "\u{2025}"; // U+2025, matching Go RuntimeMemberSep -pub const MEMBER_SEP: &str = "."; - -// runtime reserved segments (keytree/const.go) -const SEG_LIB: &str = "\u{2025}lib"; // frame marker -const SEG_PC: &str = "pc"; -const SEG_STATUS: &str = "status"; -const SEG_CALLPC: &str = "callpc"; -const SEG_RETURNPC: &str = "returnpc"; - -// ── path builders ── - -pub fn lib_path(name: &str) -> String { format!("/{}/{}", PATH_SEG_LIB, name) } - -pub fn lib_func_dir(pkg: &str, name: &str) -> String { - if pkg.is_empty() { format!("/{}/{}", PATH_SEG_LIB, name) } - else { format!("/{}/{}.{}", PATH_SEG_LIB, pkg, name) } -} - -pub fn vthread_root(vtid: &str) -> String { format!("/{}/{}", PATH_SEG_VTHREAD, vtid) } - -fn vt_member(vtid: &str, seg: &str) -> String { - format!("{}/{}{}", vthread_root(vtid), RUNTIME_MEMBER_SEP, seg) -} - -pub fn vthread_pc(vtid: &str) -> String { vt_member(vtid, SEG_PC) } -pub fn vthread_status(vtid: &str) -> String { vt_member(vtid, SEG_STATUS) } - -// frame-level keys (keytree/frame.go) -pub fn frame_root(pc: &str) -> &str { - if let Some(idx) = pc.rfind(&format!("{}{}", PATH_SEP, "[")) { - &pc[..idx] - } else { - pc - } -} - -pub fn entry_pc(root: &str) -> String { - let trimmed = root.trim_end_matches(PATH_SEP); - format!("{}/[1,0]", trimmed) -} - -pub fn frame_stack(root: &str) -> String { - let trimmed = root.trim_end_matches(PATH_SEP); - format!("{}/", trimmed) -} - -fn frame_member(root: &str, seg: &str) -> String { - format!("{}{}{}", frame_stack(root), RUNTIME_MEMBER_SEP, seg) -} - -pub fn frame_lib(root: &str) -> String { frame_member(root, "lib") } -pub fn frame_callpc(root: &str) -> String { frame_member(root, SEG_CALLPC) } -pub fn frame_returnpc(root: &str) -> String { frame_member(root, SEG_RETURNPC) } - -// ── XValue kind constants (matching kvspace-go/const.go) ── - -pub const KIND_NONE: &str = ""; -pub const KIND_BOOL: &str = "bool"; -pub const KIND_INT8: &str = "int8"; -pub const KIND_INT16: &str = "int16"; -pub const KIND_INT32: &str = "int32"; -pub const KIND_INT64: &str = "int64"; -pub const KIND_UINT8: &str = "uint8"; -pub const KIND_UINT16: &str = "uint16"; -pub const KIND_UINT32: &str = "uint32"; -pub const KIND_UINT64: &str = "uint64"; -pub const KIND_FLOAT32: &str = "float32"; -pub const KIND_FLOAT64: &str = "float64"; -pub const KIND_STRING: &str = "string"; -pub const KIND_BYTES: &str = "bytes"; -pub const KIND_INDEX: &str = "index"; -pub const KIND_LINKINDEX: &str = "linkindex"; -pub const KIND_EXTINDEX: &str = "extindex"; -pub const KIND_RWIR: &str = "rwir"; -pub const KIND_RWFUNC: &str = "rwfunc"; -pub const KIND_SCOPE: &str = "scope"; - -// ── opcode constants (matching rwir/rwir.go OpCall etc.) ── - -pub const OP_CALL: &str = "call"; -pub const OP_RETURN: &str = "return"; -pub const OP_GOTO: &str = "goto"; -pub const OP_BR: &str = "br"; diff --git a/keytree/dev.rs b/keytree/dev.rs deleted file mode 100644 index d6111c01..00000000 --- a/keytree/dev.rs +++ /dev/null @@ -1 +0,0 @@ -// keytree::dev diff --git a/keytree/entry.rs b/keytree/entry.rs deleted file mode 100644 index e3b3ee57..00000000 --- a/keytree/entry.rs +++ /dev/null @@ -1 +0,0 @@ -// keytree::entry diff --git a/keytree/frame.rs b/keytree/frame.rs deleted file mode 100644 index a495461a..00000000 --- a/keytree/frame.rs +++ /dev/null @@ -1 +0,0 @@ -// keytree::frame diff --git a/keytree/member.rs b/keytree/member.rs deleted file mode 100644 index b27053df..00000000 --- a/keytree/member.rs +++ /dev/null @@ -1 +0,0 @@ -// keytree::member diff --git a/keytree/mod.rs b/keytree/mod.rs deleted file mode 100644 index 5173f07a..00000000 --- a/keytree/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod r#const; diff --git a/keytree/sys.rs b/keytree/sys.rs deleted file mode 100644 index f2fc248b..00000000 --- a/keytree/sys.rs +++ /dev/null @@ -1 +0,0 @@ -// keytree::sys diff --git a/keytree/vthread.rs b/keytree/vthread.rs deleted file mode 100644 index a8a69b6a..00000000 --- a/keytree/vthread.rs +++ /dev/null @@ -1 +0,0 @@ -// keytree::vthread diff --git a/kvcpu/controlflow.rs b/kvcpu/controlflow.rs deleted file mode 100644 index 4b31afa3..00000000 --- a/kvcpu/controlflow.rs +++ /dev/null @@ -1,179 +0,0 @@ -//! Control flow — matching kvcpu/controlflow.go. -use std::collections::HashMap; -use super::cpu::KVCpu; -use crate::rwir::rwir::{Rwir, decode}; -use crate::rwir::builtin::ops; -use crate::keytree::r#const; - -use super::execute::{SlotValue, parse_func_dir, execute_inner, build_name_slot_map, build_param_names, build_return_names, resolve_read}; - -/// handle_call: read callee params, pass args, execute, propagate return values. -pub fn handle_call( - cpu: &KVCpu, - _caller_dir: &str, - inst: &Rwir, - caller_name_slot: &HashMap, - caller_slot_vals: &HashMap, - vars: &mut HashMap)>, -) -> Result<(), String> { - if inst.reads.is_empty() { return Err("call needs func name".into()); } - let func_name = &inst.reads[0].name; - - let callee_dir = if func_name.starts_with("/lib/") { - func_name["/lib/".len()..].to_string() - } else { - func_name.clone() - }; - let callee_dir = parse_func_dir(&callee_dir); - - let sig_rv = cpu.get(&format!("{}/[0,0]", callee_dir)) - .ok_or_else(|| format!("call: func not found: {}", func_name))?; - if sig_rv.kind != r#const::KIND_RWFUNC { - return Err(format!("call: {} has no rwfunc", func_name)); - } - let nr = unsafe { sig_rv.u16_le(0) } as usize; - - let callee_names = build_param_names(cpu, &callee_dir); - - // Build args by position - let mut args: HashMap = HashMap::new(); - for i in 0..nr { - let slot = format!("[0,-{}]", i + 1); - if let Some(arg) = inst.reads.get(i) { - let sv = resolve_read(cpu, caller_name_slot, caller_slot_vals, vars, arg); - args.insert(slot, sv); - } - } - - let callee_returns = build_return_names(cpu, &callee_dir); - let ret = execute_inner(cpu, &callee_dir, &callee_names, &args, &callee_returns)?; - for (i, ret_name) in callee_returns.iter().enumerate() { - if let Some((k, v)) = ret.get(ret_name) { - if let Some(w) = inst.writes.get(i) { - vars.insert(w.name.clone(), (k.clone(), v.clone())); - } - } - } - Ok(()) -} - -pub fn handle_return() -> Result<(), String> { Ok(()) } - -/// handle_br(cond, trueLabel, falseLabel): evaluate resolved cond → goto label. -pub fn handle_br( - cpu: &KVCpu, func_dir: &str, inst: &Rwir, - name_slot: &HashMap, - slot_vals: &HashMap, - vars: &mut HashMap)>, -) -> Result<(), String> { - if inst.reads.len() < 3 { return Err("br needs 3 args".into()); } - let cond = unsafe { inst.reads[0].first_byte() != 0 }; - let label_idx = if cond { 1 } else { 2 }; - let label_name = unsafe { - String::from_utf8_lossy(inst.reads[label_idx].bytes()).to_string() - }; - execute_scope(cpu, func_dir, &label_name, name_slot, slot_vals, vars) -} - -/// handle_goto(label): execute label scope. -pub fn handle_goto( - cpu: &KVCpu, func_dir: &str, inst: &Rwir, - name_slot: &HashMap, - slot_vals: &HashMap, - vars: &mut HashMap)>, -) -> Result<(), String> { - if inst.reads.is_empty() { return Err("goto needs label".into()); } - let label = unsafe { String::from_utf8_lossy(inst.reads[0].bytes()).to_string() }; - execute_scope(cpu, func_dir, &label, name_slot, slot_vals, vars) -} - -/// execute_scope: run instructions under func_dir/label[0,0], [1,0], ... -/// Supports br within scopes for while loops. -fn execute_scope( - cpu: &KVCpu, func_dir: &str, label: &str, - name_slot: &HashMap, - slot_vals: &HashMap, - vars: &mut HashMap)>, -) -> Result<(), String> { - // Go layout: func_dir/label[0,0], func_dir/label[1,0], ... - let scope_prefix = format!("{}/{}", func_dir, label); - let scope_base = if cpu.get(&format!("{}[0,0]", scope_prefix)).is_some() { - scope_prefix - } else { - format!("{}{}", func_dir, label) - }; - - // Scope keys: func_dir/label[0,0], func_dir/label[1,0] — no "/" before "[" - let mut slot: i32 = 0; - loop { - let mut inst = decode_scope(cpu, &scope_base, slot) - .ok_or_else(|| format!("scope decode failed at {}", slot))?; - if inst.opcode.is_empty() { break; } - - // Resolve variable references in-place - for p in &mut inst.reads { - if p.val_kind == r#const::KIND_RWIR { - let sv = resolve_read(cpu, name_slot, slot_vals, vars, p); - if sv.body_ptr.is_null() { continue; } - p.val_kind = sv.kind; - p.body_ptr = sv.body_ptr; - p.body_len = sv.body_len; - } - } - - let op = inst.opcode.clone(); - if ops::native(cpu, &op, &inst.reads, &inst.writes, vars) { - // handled by builtin - } else if op == "br" { - // Conditional branch within scope → jump to different scope - if inst.reads.len() < 3 { continue; } - let cond = unsafe { inst.reads[0].first_byte() != 0 }; - let label_idx = if cond { 1 } else { 2 }; - let next_label = unsafe { String::from_utf8_lossy(inst.reads[label_idx].bytes()).to_string() }; - return execute_scope(cpu, func_dir, &next_label, name_slot, slot_vals, vars); - } else if op == "goto" { - if let Some(p) = inst.reads.first() { - let next_label = unsafe { String::from_utf8_lossy(p.bytes()).to_string() }; - return execute_scope(cpu, func_dir, &next_label, name_slot, slot_vals, vars); - } - } - // Other opcodes in scope (like "return") — skip - slot += 1; - } - Ok(()) -} - -/// Decode scope instruction: scope_base[slot,0], scope_base[slot,-j], scope_base[slot,+j]. -fn decode_scope(cpu: &KVCpu, scope_base: &str, slot: i32) -> Option { - use crate::rwir::rwir::Param; - let base = format!("{}[{}", scope_base, slot); - let mut r = Rwir { opcode: String::new(), reads: Vec::new(), writes: Vec::new() }; - if let Some(rv) = cpu.get(&format!("{},0]", base)) { - let raw = unsafe { std::slice::from_raw_parts(rv.body_ptr, rv.body_len as usize) }; - let skip = if rv.kind == r#const::KIND_RWIR && raw.len() >= 4 { 4 } else { 0 }; - r.opcode = String::from_utf8_lossy(&raw[skip..]).to_string(); - } - for i in 1..=32 { - if let Some(rv) = cpu.get(&format!("{},-{}]", base, i)) { - let name = if rv.kind == r#const::KIND_RWIR && rv.body_len >= 4 { - let n = unsafe { std::slice::from_raw_parts(rv.body_ptr, rv.body_len as usize) }; - String::from_utf8_lossy(&n[4..]).to_string() - } else { - let n = unsafe { std::slice::from_raw_parts(rv.body_ptr, rv.body_len as usize) }; - String::from_utf8_lossy(n).to_string() - }; - r.reads.push(Param { name, val_kind: rv.kind.clone(), body_ptr: rv.body_ptr, body_len: rv.body_len }); - } - if let Some(rv) = cpu.get(&format!("{},{}]", base, i)) { - let name = if rv.kind == r#const::KIND_RWIR && rv.body_len >= 4 { - let n = unsafe { std::slice::from_raw_parts(rv.body_ptr, rv.body_len as usize) }; - String::from_utf8_lossy(&n[4..]).to_string() - } else { - let n = unsafe { std::slice::from_raw_parts(rv.body_ptr, rv.body_len as usize) }; - String::from_utf8_lossy(n).to_string() - }; - r.writes.push(Param { name, val_kind: rv.kind.clone(), body_ptr: rv.body_ptr, body_len: rv.body_len }); - } - } - Some(r) -} diff --git a/kvcpu/cpu.rs b/kvcpu/cpu.rs deleted file mode 100644 index 7f139750..00000000 --- a/kvcpu/cpu.rs +++ /dev/null @@ -1,130 +0,0 @@ -//! KVCpu — zero-copy FFI wrapper over kvspace-c. - -use std::ffi::{c_char, c_int, c_void, CString}; - -#[link(name = "kvspace-c")] -extern "C" { - fn kvspace_open(path: *const c_char, data_size: usize) -> *mut c_void; - fn kvspace_close(kv: *mut c_void); - fn kvspace_get(kv: *mut c_void, key: *const c_char, resolve: c_int, out_len: *mut i32) -> *mut u8; - fn kvspace_set(kv: *mut c_void, key: *const c_char, val: *const u8, val_len: i32) -> c_int; - fn kvspace_list(kv: *mut c_void, prefix: *const c_char, expand_ext: c_int, resolve: c_int, names: *mut *mut *mut c_char, count: *mut i32) -> c_int; - fn kvspace_mkindex(kv: *mut c_void, path: *const c_char) -> c_int; - fn kvspace_del(kv: *mut c_void, key: *const c_char) -> c_int; - fn kvspace_notify(kv: *mut c_void, key: *const c_char, val: *const u8, val_len: i32) -> c_int; - fn kvspace_watch(kv: *mut c_void, key: *const c_char, timeout_ms: i32, out_len: *mut i32) -> *mut u8; - fn kvspace_link(kv: *mut c_void, target: *const c_char, linkpath: *const c_char) -> c_int; -} - -/// Raw XValue reference: points directly into SHM, zero-copy. -/// TLV header: [1B: b7=isptr|b6-0=kind_len][N B kind][4B al LE][4B raw_len LE][M B raw_body] -pub struct RawValue { - pub is_ptr: bool, // bit7 of header byte - pub kind: String, // small, copied from TLV header - pub array_len: i32, - pub body_len: i32, // raw body length - pub body_ptr: *const u8, // direct pointer into SHM; zero-copy -} - -impl RawValue { - pub unsafe fn bytes(&self) -> &[u8] { std::slice::from_raw_parts(self.body_ptr, self.body_len as usize) } - pub unsafe fn i64(&self) -> i64 { (self.body_ptr as *const i64).read_unaligned() } - pub unsafe fn f64(&self) -> f64 { (self.body_ptr as *const f64).read_unaligned() } - pub unsafe fn u16_le(&self, offset: usize) -> u16 { - let ptr = self.body_ptr.add(offset); - (ptr as *const u16).read_unaligned() - } - /// body as &str (zero-copy) - pub unsafe fn body_str(&self) -> &str { - let s = std::slice::from_raw_parts(self.body_ptr, self.body_len as usize); - std::str::from_utf8_unchecked(s) - } - /// ptr target path: body is the target key - pub unsafe fn ptr_target(&self) -> &str { self.body_str() } -} - -pub struct KVCpu { - pub kv: *mut c_void, - pub vm_id: String, -} - -impl KVCpu { - pub fn open(shm_path: &str) -> Option<*mut c_void> { - let cp = CString::new(shm_path).ok()?; - let kv = unsafe { kvspace_open(cp.as_ptr(), 2097152) }; - if kv.is_null() { None } else { Some(kv) } - } - - pub fn close(kv: *mut c_void) { unsafe { kvspace_close(kv); } } - - pub fn new(kv: *mut c_void, vm_id: &str) -> Self { - KVCpu { kv, vm_id: vm_id.to_string() } - } - - /// Zero-copy get: parses TLV, returns RawValue with SHM body pointer. - /// is_ptr is bit7 of first header byte. - pub fn get(&self, key: &str) -> Option { - let ck = CString::new(key).ok()?; - let mut len: i32 = 0; - let ptr = unsafe { kvspace_get(self.kv, ck.as_ptr(), 1, &mut len) }; - if ptr.is_null() || len == 0 { return None; } - let data = unsafe { std::slice::from_raw_parts(ptr, len as usize) }; - if data.len() < 10 { return None; } - - let is_ptr = data[0] & 0x80 != 0; - let kl = (data[0] & 0x7F) as usize; - let p = 1 + kl; - if data.len() < p + 8 { return None; } - - let kind = String::from_utf8_lossy(&data[1..p]).to_string(); - let al = i32::from_le_bytes([data[p], data[p+1], data[p+2], data[p+3]]); - let rl = i32::from_le_bytes([data[p+4], data[p+5], data[p+6], data[p+7]]); - if (rl as usize) + p + 8 > data.len() { return None; } - - Some(RawValue { - is_ptr, - kind, - array_len: al, - body_len: rl, - body_ptr: unsafe { ptr.add(p + 8) }, - }) - } - - pub fn set(&self, key: &str, kind: &str, raw: &[u8], al: i32, is_ptr: bool) { - let ck = CString::new(key).unwrap(); - let first_byte = if is_ptr { kind.len() as u8 | 0x80 } else { kind.len() as u8 }; - let mut tlv = vec![first_byte]; - tlv.extend_from_slice(kind.as_bytes()); - tlv.extend_from_slice(&al.to_le_bytes()); - tlv.extend_from_slice(&(raw.len() as i32).to_le_bytes()); - tlv.extend_from_slice(raw); - unsafe { kvspace_set(self.kv, ck.as_ptr(), tlv.as_ptr(), tlv.len() as i32) }; - } - - /// List children of a directory prefix, filtering by prefix. - pub fn list(&self, prefix: &str) -> Vec { - let cp = CString::new(prefix).ok(); - if cp.is_none() { return vec![]; } - let mut names: *mut *mut c_char = std::ptr::null_mut(); - let mut count: i32 = 0; - let rc = unsafe { kvspace_list(self.kv, cp.unwrap().as_ptr(), 0, 0, &mut names, &mut count) }; - if rc != 0 || count <= 0 || names.is_null() { return vec![]; } - let mut result = Vec::with_capacity(count as usize); - for i in 0..count as isize { - let s = unsafe { std::ffi::CStr::from_ptr(*names.offset(i)) }; - if let Ok(s) = s.to_str() { result.push(s.to_string()); } - } - result - } - - pub fn mkindex(&self, path: &str) { - let cp = CString::new(path).unwrap(); - unsafe { kvspace_mkindex(self.kv, cp.as_ptr()) }; - } - - pub fn del(&self, key: &str) { - if let Ok(ck) = CString::new(key) { - unsafe { kvspace_del(self.kv, ck.as_ptr()) }; - } - } -} diff --git a/kvcpu/debug.rs b/kvcpu/debug.rs deleted file mode 100644 index c46d706c..00000000 --- a/kvcpu/debug.rs +++ /dev/null @@ -1 +0,0 @@ -pub fn debugger_active() -> bool { false } diff --git a/kvcpu/execute.rs b/kvcpu/execute.rs deleted file mode 100644 index 7436794c..00000000 --- a/kvcpu/execute.rs +++ /dev/null @@ -1,182 +0,0 @@ -use std::collections::HashMap; -use super::cpu::KVCpu; -use crate::rwir::rwir::{Rwir, Param, decode}; -use crate::rwir::builtin::ops; -use crate::keytree::r#const; - -/// SlotValue references SHM directly — zero-copy. -/// kind is copied (small), body_ptr points into SHM. -#[derive(Clone)] -pub struct SlotValue { - pub kind: String, - pub body_ptr: *const u8, - pub body_len: i32, -} - -/// Execute a function from Go-layout kvspace SHM. -pub fn execute(cpu: &KVCpu, func_name: &str) -> Result<(), String> { - let func_dir = parse_func_dir(func_name); - execute_inner(cpu, &func_dir, &[], &HashMap::new(), &[])?; - Ok(()) -} - -pub fn parse_func_dir(name: &str) -> String { - let (pkg, fname) = if let Some(dot) = name.rfind('.') { - (&name[..dot], &name[dot+1..]) - } else { - ("", name) - }; - r#const::lib_func_dir(pkg, fname) -} - -/// Execute with pre-resolved args. Returns the callee's local vars (for return values). -pub fn execute_inner( - cpu: &KVCpu, - func_dir: &str, - _arg_names: &[String], - caller_args: &HashMap, - return_names: &[String], // ordered write-param names to return -) -> Result)>, String> { - let sig_rv = cpu.get(&format!("{}/[0,0]", func_dir)) - .ok_or_else(|| format!("func not found: {}", func_dir))?; - if sig_rv.kind != r#const::KIND_RWFUNC { - return Err(format!("{} has no rwfunc at [0,0]", func_dir)); - } - let nr = unsafe { sig_rv.u16_le(0) } as i32; - let nw = unsafe { sig_rv.u16_le(2) } as i32; - - let name_slot = build_name_slot_map(cpu, func_dir); - - let mut slot_vals: HashMap = HashMap::new(); - for (slot, val) in caller_args { - slot_vals.insert(slot.clone(), val.clone()); - } - - let mut vars: HashMap)> = HashMap::new(); - - let mut slot_idx: i32 = 1; - loop { - let inst = decode(cpu, func_dir, slot_idx, 128, 128) - .ok_or_else(|| format!("decode failed at slot {}", slot_idx))?; - if inst.opcode.is_empty() { break; } - - let mut resolved_inst = inst.clone(); - for p in &mut resolved_inst.reads { - if p.val_kind == r#const::KIND_RWIR { - let sv = resolve_read(cpu, &name_slot, &slot_vals, &vars, p); - if sv.body_ptr.is_null() { continue; } - p.val_kind = sv.kind; - p.body_ptr = sv.body_ptr; - p.body_len = sv.body_len; - } - } - - let op = resolved_inst.opcode.clone(); - if ops::native(cpu, &op, &resolved_inst.reads, &resolved_inst.writes, &mut vars) { - // handled by builtin - } else if matches!(op.as_str(), "call" | "goto" | "br" | "return") { - match op.as_str() { - "call" => super::controlflow::handle_call(cpu, func_dir, &resolved_inst, &name_slot, &slot_vals, &mut vars)?, - "return" => break, // exit loop, return vars - "goto" => super::controlflow::handle_goto(cpu, func_dir, &resolved_inst, &name_slot, &slot_vals, &mut vars)?, - "br" => super::controlflow::handle_br(cpu, func_dir, &resolved_inst, &name_slot, &slot_vals, &mut vars)?, - _ => {} - }; - } else { - let callee_dir = parse_func_dir(&op); - let callee_returns = build_return_names(cpu, &callee_dir); - let mut args: HashMap = HashMap::new(); - for (i, read) in resolved_inst.reads.iter().enumerate() { - args.insert(format!("[0,-{}]", i+1), SlotValue { - kind: read.val_kind.clone(), - body_ptr: read.body_ptr, - body_len: read.body_len, - }); - } - let ret_vars = execute_inner(cpu, &callee_dir, &[], &args, &callee_returns)?; - for (i, ret_name) in callee_returns.iter().enumerate() { - if let Some((k, v)) = ret_vars.get(ret_name) { - if let Some(w) = resolved_inst.writes.get(i) { - vars.insert(w.name.clone(), (k.clone(), v.clone())); - } - } - } - } - slot_idx += 1; - } - - // Collect return values - let mut ret = HashMap::new(); - for name in return_names { - if let Some(v) = vars.get(name) { - ret.insert(name.clone(), v.clone()); - } - } - Ok(ret) -} - -pub fn build_name_slot_map(cpu: &KVCpu, func_dir: &str) -> HashMap { - let mut map = HashMap::new(); - let children = cpu.list(&format!("{}/", func_dir)); - for child in children { - if child.starts_with('[') || child.starts_with('.') { continue; } - if let Some(rv) = cpu.get(&format!("{}/{}", func_dir, child)) { - if rv.is_ptr { - let target = unsafe { rv.ptr_target() }.to_string(); - map.insert(child, target); - } - } - } - map -} - -pub fn build_param_names(cpu: &KVCpu, func_dir: &str) -> Vec { - let name_slot = build_name_slot_map(cpu, func_dir); - let mut pairs: Vec<(i32, String)> = name_slot.iter() - .filter_map(|(name, slot)| { - if slot.starts_with("[0,-") { - let idx: i32 = slot.trim_start_matches("[0,-").trim_end_matches(']').parse().ok()?; - Some((idx, name.clone())) - } else { None } - }) - .collect(); - pairs.sort_by_key(|(idx, _)| *idx); - pairs.into_iter().map(|(_, name)| name).collect() -} - -pub fn build_return_names(cpu: &KVCpu, func_dir: &str) -> Vec { - let name_slot = build_name_slot_map(cpu, func_dir); - let mut pairs: Vec<(i32, String)> = name_slot.iter() - .filter_map(|(name, slot)| { - if slot.starts_with("[0,") && !slot.starts_with("[0,-") { - let idx: i32 = slot.trim_start_matches("[0,").trim_end_matches(']').parse().ok()?; - Some((idx, name.clone())) - } else { None } - }) - .collect(); - pairs.sort_by_key(|(idx, _)| *idx); - pairs.into_iter().map(|(_, name)| name).collect() -} - -pub fn resolve_read( - _cpu: &KVCpu, - name_slot: &HashMap, - slot_vals: &HashMap, - vars: &HashMap)>, - p: &Param, -) -> SlotValue { - if p.val_kind != r#const::KIND_RWIR && p.val_kind != r#const::KIND_RWFUNC { - return SlotValue { kind: p.val_kind.clone(), body_ptr: p.body_ptr, body_len: p.body_len }; - } - let name = &p.name; - if name.is_empty() { return SlotValue { kind: r#const::KIND_NONE.to_string(), body_ptr: std::ptr::null(), body_len: 0 }; } - if let Some(slot) = name_slot.get(name) { - if let Some(sv) = slot_vals.get(slot) { - return sv.clone(); - } - } - if let Some((k, v)) = vars.get(name) { - return SlotValue { kind: k.clone(), body_ptr: v.as_ptr(), body_len: v.len() as i32 }; - } - SlotValue { kind: r#const::KIND_NONE.to_string(), body_ptr: std::ptr::null(), body_len: 0 } -} diff --git a/kvcpu/mod.rs b/kvcpu/mod.rs deleted file mode 100644 index 8ccd391b..00000000 --- a/kvcpu/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod cpu; -pub mod execute; -pub mod controlflow; -pub mod sched; -pub mod debug; diff --git a/kvcpu/sched.rs b/kvcpu/sched.rs deleted file mode 100644 index 07232870..00000000 --- a/kvcpu/sched.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct Scheduler; impl Scheduler { pub fn new() -> Self { Scheduler } } diff --git a/layout/.gitignore b/layout/.gitignore new file mode 100644 index 00000000..b83d2226 --- /dev/null +++ b/layout/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/layout/Cargo.lock b/layout/Cargo.lock new file mode 100644 index 00000000..5f3c17b3 --- /dev/null +++ b/layout/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "kvlang-layout" +version = "0.1.0" diff --git a/layout/Cargo.toml b/layout/Cargo.toml new file mode 100644 index 00000000..c3971477 --- /dev/null +++ b/layout/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "kvlang-layout" +version = "0.1.0" +edition = "2021" +license = "MIT" +description = "kvlang 编译期(parser/lower/layoutcode),只依赖 kvspace-durable 的 C ABI" + +[lib] +name = "kvlang_layout" +path = "src/lib.rs" + +[dependencies] diff --git a/layout/Makefile b/layout/Makefile new file mode 100644 index 00000000..3c01bdd7 --- /dev/null +++ b/layout/Makefile @@ -0,0 +1,13 @@ +.PHONY: build test clean + +# 前置:kvspace-durable 必须先产出 libkvspace_durable.so(layout 通过 C ABI 动态链接它)。 +# 两段式:durable(../..) 先 build --release,再 build 本 crate。 +build: + cd ../../kvspace-durable && cargo build --release + cargo build + +test: build + cargo test + +clean: + cargo clean diff --git a/layout/build.rs b/layout/build.rs new file mode 100644 index 00000000..70640d5a --- /dev/null +++ b/layout/build.rs @@ -0,0 +1,9 @@ +// 链接 kvspace-durable 的动态库(cdylib)。 +// 布局侧只通过 extern "C" ABI 调用 kvspace_durable,不依赖其 Rust API。 +// 前置:先在 ../kvspace-durable 执行 `cargo build --release`,产出 libkvspace_durable.so。 +fn main() { + let lib_dir = "/home/peng.li24/github.com/array2d/kvspace-durable/target/release"; + println!("cargo:rustc-link-search=native={lib_dir}"); + println!("cargo:rustc-link-lib=dylib=kvspace_durable"); + println!("cargo:rustc-link-arg=-Wl,-rpath,{lib_dir}"); +} diff --git a/layout/examples/dump.rs b/layout/examples/dump.rs new file mode 100644 index 00000000..02b1b0e0 --- /dev/null +++ b/layout/examples/dump.rs @@ -0,0 +1,25 @@ +//! 调试用:编译一个 .kv 文件,递归 dump /lib 树(key → kind)。 +use std::env; +use std::fs; + +use kvlang_layout::{compile, init_dirs, kvkind, Kv}; + +fn dump(kv: &mut Kv, prefix: &str) { + for c in kv.list(prefix, false, true) { + let full = format!("{prefix}{c}"); + let v = kv.get_one(&full); + println!("{full}\tkind={} array_len={}", kvkind::kind(&v), kvkind::array_len(&v)); + if c.ends_with('/') { + dump(kv, &full); + } + } +} + +fn main() { + let args: Vec = env::args().collect(); + let src = fs::read_to_string(&args[1]).unwrap(); + let mut kv = Kv::conn("redis://127.0.0.1:6379"); + init_dirs(&mut kv).unwrap(); + compile(&mut kv, &src).unwrap(); + dump(&mut kv, "/lib/"); +} diff --git a/layout/examples/layout_file.rs b/layout/examples/layout_file.rs new file mode 100644 index 00000000..9dfaffe9 --- /dev/null +++ b/layout/examples/layout_file.rs @@ -0,0 +1,44 @@ +//! 读 .kv 文件,用 Rust layout 编译进 kvspace(默认 redis),并输出入口(ENTRY=...), +//! 供 Go runtime 执行验证。 +//! 用法:layout_file [dsn] + +use std::env; +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 { + 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 sub = format!("{prefix}{c}/"); + let entry = find_entry(kv, &sub); + if !entry.is_empty() { + return format!("{c}/{entry}"); + } + } + String::new() +} + +fn main() { + let args: Vec = env::args().collect(); + if args.len() < 2 { + eprintln!("usage: layout_file [dsn]"); + std::process::exit(1); + } + let dsn = args.get(2).map(String::as_str).unwrap_or("redis://127.0.0.1:6379"); + let src = fs::read_to_string(&args[1]).expect("read file"); + + let mut kv = Kv::conn(dsn); + init_dirs(&mut kv).expect("init_dirs"); + compile(&mut kv, &src).expect("compile"); + + 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); +} diff --git a/layout/src/ast.rs b/layout/src/ast.rs new file mode 100644 index 00000000..46cfa7a3 --- /dev/null +++ b/layout/src/ast.rs @@ -0,0 +1,564 @@ +//! AST 节点类型(对齐 ast/ast.go、astfile.go、escape.go)。纯数据结构,不依赖存储层。 + +use std::fmt; + +use super::symbol; + +// ── 语句 ───────────────────────────────────────────────────────────── + +#[derive(Clone)] +pub enum Stmt { + Instruction(Instruction), + If(IfStmt), + For(ForStmt), + While(WhileStmt), + Break(BreakStmt), + Continue(ContinueStmt), + Scope(ScopeStmt), +} + +impl fmt::Display for Stmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Stmt::Instruction(s) => write!(f, "{s}"), + Stmt::If(s) => write!(f, "{s}"), + Stmt::For(s) => write!(f, "{s}"), + Stmt::While(s) => write!(f, "{s}"), + Stmt::Break(s) => write!(f, "{s}"), + Stmt::Continue(s) => write!(f, "{s}"), + Stmt::Scope(s) => write!(f, "{s}"), + } + } +} + +impl Stmt { + pub fn first_line(&self) -> String { + match self { + Stmt::Instruction(s) => s.to_string(), + Stmt::If(_) => "if".to_string(), + Stmt::For(_) => "for".to_string(), + Stmt::While(_) => "while".to_string(), + Stmt::Break(_) => "break".to_string(), + Stmt::Continue(_) => "continue".to_string(), + Stmt::Scope(s) => s.label.clone(), + } + } +} + +/// 返回语句的前置行注释(供 FullText 使用)。 +pub fn stmt_comments(st: &Stmt) -> &[String] { + match st { + Stmt::Instruction(s) => &s.comments, + Stmt::If(s) => &s.comments, + Stmt::For(s) => &s.comments, + Stmt::While(s) => &s.comments, + Stmt::Break(s) => &s.comments, + Stmt::Continue(s) => &s.comments, + Stmt::Scope(s) => &s.comments, + } +} + +// ── 签名 ───────────────────────────────────────────────────────────── + +#[derive(Clone)] +pub struct Param { + pub name: String, + pub ty: String, // "" = 动态类型 +} + +#[derive(Clone)] +pub struct FuncSig { + pub name: String, + pub params: Vec, + pub returns: Vec, +} + +impl FuncSig { + fn sig_string(&self, prefix: &str) -> String { + let mut sb = String::new(); + sb.push_str(prefix); + sb.push(' '); + sb.push_str(&self.name); + sb.push('('); + for (i, p) in self.params.iter().enumerate() { + if i > 0 { + sb.push_str(", "); + } + sb.push_str(&p.name); + if !p.ty.is_empty() { + sb.push(':'); + sb.push_str(&p.ty); + } + } + sb.push_str(") -> ("); + for (i, p) in self.returns.iter().enumerate() { + if i > 0 { + sb.push_str(", "); + } + sb.push_str(&p.name); + if !p.ty.is_empty() { + sb.push(':'); + sb.push_str(&p.ty); + } + } + sb.push(')'); + sb + } + + pub fn param_names(&self) -> Vec { + self.params.iter().map(|p| p.name.clone()).collect() + } + + pub fn num_reads(&self) -> i32 { + self.params.len() as i32 + } + + pub fn num_writes(&self) -> i32 { + self.returns.len() as i32 + } +} + +impl fmt::Display for FuncSig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.sig_string("rwfunc")) + } +} + +// ── 函数 ───────────────────────────────────────────────────────────── + +#[derive(Clone)] +pub struct Func { + pub comments: Vec, + pub sig: FuncSig, + pub body: Vec, + pub pkg: String, +} + +impl Func { + pub fn full_text(&self) -> String { + let mut sb = String::new(); + for c in &self.comments { + sb.push_str(c); + sb.push('\n'); + } + sb.push_str(&self.sig.to_string()); + sb.push_str(" {\n"); + for st in &self.body { + for c in stmt_comments(st) { + sb.push_str(" "); + sb.push_str(c); + sb.push('\n'); + } + sb.push_str(" "); + sb.push_str(&st.to_string()); + sb.push('\n'); + } + sb.push('}'); + sb + } +} + +#[derive(Clone)] +pub struct RwirDecl { + pub comments: Vec, + pub sig: FuncSig, + pub pkg: String, +} + +impl RwirDecl { + pub fn sig_string(&self) -> String { + self.sig.sig_string("rwir") + } +} + +// ── Expr ───────────────────────────────────────────────────────────── + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum LitKind { + LitNone, + LitInt, + LitFloat, + LitString, + LitRawString, + LitBool, + LitNil, +} + +impl fmt::Display for LitKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + LitKind::LitNone => "none", + LitKind::LitInt => "int", + LitKind::LitFloat => "float", + LitKind::LitString => "string", + LitKind::LitRawString => "rawstring", + LitKind::LitBool => "bool", + LitKind::LitNil => "nil", + }; + write!(f, "{s}") + } +} + +#[derive(Clone)] +pub struct Expr { + pub op: String, // 算子/函数名("" = 叶节点) + pub args: Vec, // 操作数(叶节点为空) + pub val: String, // 叶节点值 + pub quote: u8, // 0=非字符串, '"'=双引号, '`'=反引号 + pub lit: LitKind, // 字面量类型(仅叶节点有意义) +} + +impl Expr { + pub fn is_leaf(&self) -> bool { + self.op.is_empty() + } +} + +pub fn leaf(v: &str) -> Expr { + Expr { op: String::new(), args: Vec::new(), val: v.to_string(), quote: 0, lit: LitKind::LitNone } +} + +pub fn str_lit(v: &str) -> Expr { + Expr { op: String::new(), args: Vec::new(), val: v.to_string(), quote: b'"', lit: LitKind::LitString } +} + +pub fn raw_str(v: &str) -> Expr { + Expr { op: String::new(), args: Vec::new(), val: v.to_string(), quote: b'`', lit: LitKind::LitRawString } +} + +pub fn int_lit(v: &str) -> Expr { + Expr { op: String::new(), args: Vec::new(), val: v.to_string(), quote: 0, lit: LitKind::LitInt } +} + +pub fn float_lit(v: &str) -> Expr { + Expr { op: String::new(), args: Vec::new(), val: v.to_string(), quote: 0, lit: LitKind::LitFloat } +} + +pub fn bool_lit(v: &str) -> Expr { + Expr { op: String::new(), args: Vec::new(), val: v.to_string(), quote: 0, lit: LitKind::LitBool } +} + +pub fn none_lit() -> Expr { + Expr { op: String::new(), args: Vec::new(), val: "None".to_string(), quote: 0, lit: LitKind::LitNil } +} + +pub fn call(op: &str, args: Vec) -> Expr { + Expr { op: op.to_string(), args, val: String::new(), quote: 0, lit: LitKind::LitNone } +} + +impl Expr { + /// 算子的中缀优先级(0 = 非中缀)。 + pub fn infix_prec(op: &str) -> i32 { + symbol::lookup(op).precedence + } + + fn string_prec(&self, outer_prec: i32) -> String { + if self.is_leaf() { + if self.quote != 0 { + if self.quote == b'"' { + return format!("\"{}\"", escape_string(&self.val)); + } + return format!("`{}`", self.val); + } + return self.val.clone(); + } + let p = symbol::lookup(&self.op).precedence; + if p > 0 && self.args.len() == 2 { + let left = self.args[0].string_prec(p); + let right = self.args[1].string_prec(p + 1); + let s = format!("{left} {} {right}", self.op); + if outer_prec > p { + return format!("({s})"); + } + return s; + } + if self.args.len() == 1 && is_operator_char(self.op.as_bytes()[0]) { + return format!("{}{}", self.op, self.args[0].string_prec(200)); + } + let args: Vec = self.args.iter().map(|a| a.to_string()).collect(); + format!("{}({})", self.op, args.join(", ")) + } +} + +impl fmt::Display for Expr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.string_prec(0)) + } +} + +// ── Instruction ────────────────────────────────────────────────────── + +#[derive(Clone, Default)] +pub struct Instruction { + pub comments: Vec, + pub expr: Option, // None = 空指令 + pub writes: Vec, + pub write_types: Vec, + pub arrow_left: bool, // true = 写槽在左(<- 或 =) + pub eq: bool, // true = 源码用 = 书写 +} + +impl Instruction { + fn left_arrow(&self) -> &'static str { + if self.eq { + symbol::ARROW_EQ + } else { + symbol::ARROW_LEFT + } + } + + /// 扁平化 (opcode, reads)。前提:lower 已把复合子表达式展开为叶节点。 + pub fn flat(&self) -> (String, Vec) { + let e = match &self.expr { + Some(e) => e, + None => return (String::new(), Vec::new()), + }; + if e.is_leaf() { + let v = &e.val; + if v == "return" { + return ("return".to_string(), Vec::new()); + } + if e.quote != 0 { + return ("=".to_string(), vec![format!("\"{v}")]); + } + return ("=".to_string(), vec![v.clone()]); + } + let opcode = e.op.clone(); + let reads: Vec = e + .args + .iter() + .map(|a| if a.quote != 0 { format!("\"{}", a.val) } else { a.val.clone() }) + .collect(); + (opcode, reads) + } + + fn join_typed_writes(&self) -> String { + if self.writes.is_empty() { + return String::new(); + } + let parts: Vec = self + .writes + .iter() + .enumerate() + .map(|(j, w)| { + if j < self.write_types.len() && !self.write_types[j].is_empty() { + format!("{w}:{}", self.write_types[j]) + } else { + w.clone() + } + }) + .collect(); + if parts.len() == 1 { + parts[0].clone() + } else { + format!("({})", parts.join(", ")) + } + } +} + +fn idx_string(e: &Expr) -> String { + if e.quote == b'"' { + format!("\"{}\"", e.val) + } else { + e.to_string() + } +} + +impl fmt::Display for Instruction { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let e = match &self.expr { + Some(e) => e, + None => return write!(f, ""), + }; + let writes = self.join_typed_writes(); + // array(...) → [...] + if e.op == "array" { + let args: Vec = e.args.iter().map(|a| a.to_string()).collect(); + let s = format!("[{}]", args.join(", ")); + if !self.writes.is_empty() { + if self.arrow_left { + return write!(f, "{writes}{}{s}", self.left_arrow()); + } + return write!(f, "{s}{}{writes}", symbol::ARROW_RIGHT); + } + return write!(f, "{s}"); + } + // dict("k1", v1, ...) → { k1=v1; k2=v2 } + if e.op == "dict" { + let mut pairs = Vec::new(); + let mut j = 0; + while j + 1 < e.args.len() { + pairs.push(format!("{}={}", e.args[j].val, e.args[j + 1])); + j += 2; + } + let s = format!("{{ {} }}", pairs.join("; ")); + if !self.writes.is_empty() { + if self.arrow_left { + return write!(f, "{writes}{}{s}", self.left_arrow()); + } + return write!(f, "{s}{}{writes}", symbol::ARROW_RIGHT); + } + return write!(f, "{s}"); + } + // set(base, idx, val) → a[idx] <- val(仅 <- 形式) + if e.op == "set" && e.args.len() >= 3 && self.arrow_left { + let base = e.args[0].to_string(); + let idx = idx_string(&e.args[1]); + let val = e.args[2].to_string(); + return write!(f, "{base}[{idx}]{}{val}", self.left_arrow()); + } + let mut s = e.to_string(); + // at(base, idx) → base[idx] 或 base.field + if e.op == "at" && e.args.len() >= 2 { + if e.args[1].quote == b'"' { + s = format!("{}.{}", e.args[0], e.args[1].val); + } else { + s = format!("{}[{}]", e.args[0], e.args[1]); + } + } + if !self.writes.is_empty() { + if self.arrow_left { + s = format!("{writes}{}{s}", self.left_arrow()); + } else { + s = format!("{s}{}{writes}", symbol::ARROW_RIGHT); + } + } + write!(f, "{s}") + } +} + +// ── 控制流节点 ────────────────────────────────────────────────────── + +#[derive(Clone)] +pub struct IfStmt { + pub comments: Vec, + pub cond: Option, + pub then_: Vec, + pub else_: Vec, +} + +impl fmt::Display for IfStmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let cond = self.cond.as_ref().map(|c| c.to_string()).unwrap_or_default(); + let mut r = format!("if ({cond}) {{\n"); + for st in &self.then_ { + r.push_str(&format!("\t{st}\n")); + } + r.push('}'); + if !self.else_.is_empty() { + r.push_str(" else {\n"); + for st in &self.else_ { + r.push_str(&format!("\t{st}\n")); + } + r.push('}'); + } + write!(f, "{r}") + } +} + +#[derive(Clone)] +pub struct ForStmt { + pub comments: Vec, + pub var: String, + pub iter: String, + pub body: Vec, +} + +impl fmt::Display for ForStmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut r = format!("for ({} in {}) {{\n", self.var, self.iter); + for st in &self.body { + r.push_str(&format!("\t{st}\n")); + } + write!(f, "{r}}}") + } +} + +#[derive(Clone)] +pub struct WhileStmt { + pub comments: Vec, + pub cond: Option, + pub body: Vec, +} + +impl fmt::Display for WhileStmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let cond = self.cond.as_ref().map(|c| c.to_string()).unwrap_or_default(); + let mut r = format!("while ({cond}) {{\n"); + for st in &self.body { + r.push_str(&format!("\t{st}\n")); + } + write!(f, "{r}}}") + } +} + +#[derive(Clone)] +pub struct BreakStmt { + pub comments: Vec, +} + +impl fmt::Display for BreakStmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "break") + } +} + +#[derive(Clone)] +pub struct ContinueStmt { + pub comments: Vec, +} + +impl fmt::Display for ContinueStmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "continue") + } +} + +#[derive(Clone)] +pub struct ScopeStmt { + pub comments: Vec, + pub label: String, + pub body: Vec, +} + +impl fmt::Display for ScopeStmt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut r = format!("{}: {{\n", self.label); + for st in &self.body { + r.push_str(&format!("\t{st}\n")); + } + write!(f, "{r}}}") + } +} + +// ── File ───────────────────────────────────────────────────────────── + +#[derive(Clone, Default)] +pub struct File { + pub package: String, + pub rwir_decls: Vec, + pub funcs: Vec, + pub top_level_calls: Vec, + pub init_body: Vec, +} + +// ── 工具 ───────────────────────────────────────────────────────────── + +fn escape_string(s: &str) -> String { + let mut b = String::new(); + for c in s.bytes() { + match c { + b'\\' => b.push_str("\\\\"), + b'"' => b.push_str("\\\""), + b'\n' => b.push_str("\\n"), + b'\t' => b.push_str("\\t"), + b'\r' => b.push_str("\\r"), + _ => b.push(c as char), + } + } + b +} + +fn is_operator_char(c: u8) -> bool { + symbol::scanner_one_char_ops().contains(&c) +} diff --git a/layout/src/builtin.rs b/layout/src/builtin.rs new file mode 100644 index 00000000..97e71310 --- /dev/null +++ b/layout/src/builtin.rs @@ -0,0 +1,182 @@ +//! builtin 辅助(对齐 rwir/builtin 中 lower/layout 依赖的部分: +//! NumOp/IsNumKind/WiderNumKind/OpKind/TryParseNumber/IsNativeRwir/IsGlobalRwir)。 + +use super::ffi; + +// ── 数值 kind 工具 ─────────────────────────────────────────────────── + +pub fn is_int_kind(k: &str) -> bool { + matches!(k, "int8" | "int16" | "int32" | "int64" | "uint8" | "uint16" | "uint32" | "uint64") +} + +pub fn is_float_kind(k: &str) -> bool { + k == "float32" || k == "float64" +} + +pub fn is_unsigned_kind(k: &str) -> bool { + matches!(k, "uint8" | "uint16" | "uint32" | "uint64") +} + +pub fn is_num_kind(k: &str) -> bool { + is_int_kind(k) || is_float_kind(k) +} + +pub fn int_kind_width(k: &str) -> i32 { + match k { + "int8" | "uint8" => 8, + "int16" | "uint16" => 16, + "int32" | "uint32" => 32, + "int64" | "uint64" => 64, + _ => 0, + } +} + +pub fn wider_int_kind(ak: &str, bk: &str) -> String { + let (aw, bw) = (int_kind_width(ak), int_kind_width(bk)); + let (au, bu) = (is_unsigned_kind(ak), is_unsigned_kind(bk)); + if au == bu { + return if aw >= bw { ak.to_string() } else { bk.to_string() }; + } + let w = aw.max(bw); + match w { + 8 => "int16".to_string(), + 16 => "int32".to_string(), + _ => "int64".to_string(), + } +} + +pub fn wider_float_kind(ak: &str, bk: &str) -> String { + if ak == "float64" || bk == "float64" { + "float64".to_string() + } else if ak == "float32" || bk == "float32" { + "float32".to_string() + } else { + "float64".to_string() + } +} + +pub fn wider_num_kind(ak: &str, bk: &str) -> String { + if is_int_kind(ak) && is_int_kind(bk) { + return wider_int_kind(ak, bk); + } + if is_float_kind(ak) && is_float_kind(bk) { + return wider_float_kind(ak, bk); + } + if is_float_kind(ak) { + ak.to_string() + } else { + bk.to_string() + } +} + +/// op 在给定输入 kind 下的注册 kind("" 表示该 kind 无此 op)。 +pub fn op_kind(op: &str, k: &str) -> String { + if !is_num_kind(k) { + return String::new(); + } + match op { + "pow" | "sqrt" | "exp" | "log" => { + if is_float_kind(k) { + k.to_string() + } else { + "float64".to_string() + } + } + "mod" => { + if is_int_kind(k) { + k.to_string() + } else { + String::new() + } + } + "bitand" | "bitor" | "bitxor" | "shl" | "shr" => { + if is_int_kind(k) { + if is_unsigned_kind(k) { + "uint64".to_string() + } else { + "int64".to_string() + } + } else { + String::new() + } + } + "neg" | "abs" => { + if is_unsigned_kind(k) { + String::new() + } else { + k.to_string() + } + } + _ => k.to_string(), + } +} + +/// 判断 opcode 是否为多态数值 op。 +pub fn num_op(opcode: &str) -> bool { + matches!( + opcode, + "add" | "sub" | "mul" | "div" | "neg" | "mod" + | "bitand" | "bitor" | "bitxor" | "shl" | "shr" + | "eq" | "neq" | "lt" | "le" | "gt" | "ge" + | "pow" | "sqrt" | "exp" | "log" + | "abs" | "sign" | "max" | "min" + ) +} + +// ── 字面量解析 ─────────────────────────────────────────────────────── + +/// 将数字字面量解析为 int64/uint64/float64 的 TLV 字节。 +pub fn try_parse_number(s: &str) -> Option> { + if s.is_empty() { + return None; + } + let c0 = s.as_bytes()[0]; + let is_digit = c0.is_ascii_digit(); + let is_neg = c0 == b'-' && s.len() >= 2 && s.as_bytes()[1].is_ascii_digit(); + if !is_digit && !is_neg { + return None; + } + if let Ok(i) = s.parse::() { + return Some(ffi::new_int64(i)); + } + if c0 != b'-' && !s.contains('.') && !s.contains('e') && !s.contains('E') { + if let Ok(u) = s.parse::() { + return Some(ffi::tlv_encode("uint64", &u.to_le_bytes(), 1)); + } + } + if let Ok(f) = s.parse::() { + return Some(ffi::new_float64(f)); + } + None +} + +// ── native / global rwir 判定(layout 命名空间用) ─────────────────── + +/// VM 原生 rwir 中不带 `.` 的 opcode(带 `.` 的由 MemberSep 检查兜底)。 +fn native_rwir_set() -> &'static [&'static str] { + &[ + // 单字 builtin + "array", "len", "at", "set", "has", "kvhas", "kvat", "dict", "debugger", + // cast kind + "bool", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", + "float32", "float64", "char/utf8", "char/ascii", "char/utf32", + // 数值 op word + glyph + "add", "+", "sub", "-", "mul", "×", "div", "÷", "mod", "%", + "eq", "==", "neq", "!=", "≠", "lt", "<", "gt", ">", "le", "<=", "≤", "ge", ">=", "≥", + "and", "&&", "or", "||", "not", "!", + "bitand", "&", "bitor", "|", "bitxor", "^", "shl", "<<", "shr", ">>", + "sqrt", "√", "neg", "abs", "sign", "max", "min", "pow", "exp", "log", + ] +} + +pub fn is_native_rwir(opcode: &str) -> bool { + native_rwir_set().contains(&opcode) +} + +fn global_rwir_set() -> &'static [&'static str] { + &["print", "println", "cerr", "input", "json.to", "json.from"] +} + +pub fn is_global_rwir(opcode: &str) -> bool { + global_rwir_set().contains(&opcode) +} diff --git a/layout/src/code.rs b/layout/src/code.rs new file mode 100644 index 00000000..384b692a --- /dev/null +++ b/layout/src/code.rs @@ -0,0 +1,285 @@ +//! layoutcode(对齐 layout/layout.go 的编译期部分):把 AST 写到 /lib/ 下的结构化 KV。 +//! +//! 存储约定: +//! /lib/./[0,0] 编译后签名(kind=rwfunc) +//! /lib/./ 命名参数→slot 指针(kind=char, isptr=1) +//! /lib/./[i,j] 编译后指令(kind=rwir),i 从 1 开始 +//! /lib/./