diff --git a/.gitignore b/.gitignore index 17da4106..9fddc79c 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,6 @@ kvlanglayout silent* dumptest* d[0-9][0-9][0-9][0-9][0-9][0-9][0-9] + +# 五语言对齐 reference 材料(本地查阅用,不入库) +stdlib/kvlang/reference/ diff --git a/README_CN.md b/README_CN.md index 6a9898c6..7234c8ac 100644 --- a/README_CN.md +++ b/README_CN.md @@ -55,7 +55,7 @@ kvspace 是核心的寻址空间与内存空间;语言本体是小核心 runti ![kvlang 生态架构](docs/kvlang-ecosystem-architecture.png) - **kvspace** — 一套 C ABI(`kvspace_*`,24 符号),由 DSN 选择两种实现:`kvspace-c`(C,`shm://`,链接 `blockmalloc` + `slotsboxmalloc`)与 `kvspace-durable`(Rust,`redis://` / `fs://`,s3/tikv 规划中)。 -- **kvlang** — `layout`(Rust,编译)与 `runtime`(C,执行),二者都只依赖 `kvspace_*` C ABI。 +- **kvlang** — `layout`(Rust,语法检查+布局)与 `runtime`(C,执行),二者都只依赖 `kvspace_*` C ABI。 - **rwirext** — 构建在 runtime 之上的扩展。嵌入式(Rust `term`,经 `kvlang_rwirext.h` 链接 `libkvlang_runtime`)或独立进程 handoff(Go `json`、Python `numpy`)。`term` / `json` 只是基础示例扩展,不是招牌能力。 --- @@ -191,7 +191,7 @@ if (sum > 50) { println("big") } else { println("small") } for (x in [7, 2, 9, 4]) { println(x) } ``` -条件支持复合表达式:`if (7 % 2 != 0)`、`while (i < string.len(s))` 均可(编译期自动展平为临时槽)。 +条件支持复合表达式:`if (7 % 2 != 0)`、`while (i < string.len(s))` 均可(布局期自动展平为临时槽)。 ### 操作符 diff --git a/docs/kvlang-ecosystem-architecture.drawio b/docs/kvlang-ecosystem-architecture.drawio new file mode 100644 index 00000000..8a905729 --- /dev/null +++ b/docs/kvlang-ecosystem-architecture.drawio @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tutorial/error_cases/array_param/mutate_correct.kv b/error_cases/array_param/mutate_correct.kv similarity index 100% rename from tutorial/error_cases/array_param/mutate_correct.kv rename to error_cases/array_param/mutate_correct.kv diff --git a/tutorial/error_cases/array_param/mutate_literal.kv b/error_cases/array_param/mutate_literal.kv similarity index 100% rename from tutorial/error_cases/array_param/mutate_literal.kv rename to error_cases/array_param/mutate_literal.kv diff --git a/tutorial/error_test.py b/error_cases/error_test.py similarity index 93% rename from tutorial/error_test.py rename to error_cases/error_test.py index c7b44762..11735a86 100644 --- a/tutorial/error_test.py +++ b/error_cases/error_test.py @@ -5,7 +5,7 @@ 用法: python3 tutorial/error_test.py """ from __future__ import annotations -import os, re, subprocess, sys +import os, subprocess, sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent @@ -44,11 +44,6 @@ def _unescape_logx(stderr: str) -> str: """logx msg= 域为 JSON-style 引号串,\" → " 以利子串匹配。""" return stderr.replace('\\"', '"') -def _detect_entry(out: str) -> str: - m = re.search(r"ENTRY=(\S+)", out) - return m.group(1) if m else "init" - - def collect_errors(rel: str) -> str: """layout(编译期诊断)+ run(运行时诊断),收集 stderr 合并。""" # 用 kvspace clear 隔离各 case @@ -58,8 +53,7 @@ def collect_errors(rel: str) -> str: text=True, timeout=60, cwd=str(ROOT)) stderr += _unescape_logx(layout.stderr) if layout.returncode == 0: - entry = _detect_entry(layout.stdout) - crun = subprocess.run([TERM_BIN, entry], capture_output=True, + crun = subprocess.run([TERM_BIN, "test"], capture_output=True, text=True, timeout=120, cwd=str(ROOT), env={**os.environ, "KVSPACE": _C_DSN}) stderr += "\n" + _unescape_logx(crun.stderr) diff --git a/tutorial/error_cases/index_error/at_oob.kv b/error_cases/index_error/at_oob.kv similarity index 100% rename from tutorial/error_cases/index_error/at_oob.kv rename to error_cases/index_error/at_oob.kv diff --git a/tutorial/error_cases/index_error/char_oob.kv b/error_cases/index_error/char_oob.kv similarity index 100% rename from tutorial/error_cases/index_error/char_oob.kv rename to error_cases/index_error/char_oob.kv diff --git a/tutorial/error_cases/index_error/slice_oob.kv b/error_cases/index_error/slice_oob.kv similarity index 100% rename from tutorial/error_cases/index_error/slice_oob.kv rename to error_cases/index_error/slice_oob.kv diff --git a/tutorial/error_cases/index_error/typed_array_string_key.kv b/error_cases/index_error/typed_array_string_key.kv similarity index 100% rename from tutorial/error_cases/index_error/typed_array_string_key.kv rename to error_cases/index_error/typed_array_string_key.kv diff --git a/tutorial/error_cases/key_error/kv_at_missing.kv b/error_cases/key_error/kv_at_missing.kv similarity index 100% rename from tutorial/error_cases/key_error/kv_at_missing.kv rename to error_cases/key_error/kv_at_missing.kv diff --git a/tutorial/error_cases/name_error/nosuch_func.kv b/error_cases/name_error/nosuch_func.kv similarity index 100% rename from tutorial/error_cases/name_error/nosuch_func.kv rename to error_cases/name_error/nosuch_func.kv diff --git a/tutorial/error_cases/none_type_error/none_arith.kv b/error_cases/none_type_error/none_arith.kv similarity index 100% rename from tutorial/error_cases/none_type_error/none_arith.kv rename to error_cases/none_type_error/none_arith.kv diff --git a/tutorial/error_cases/none_type_error/none_bool.kv b/error_cases/none_type_error/none_bool.kv similarity index 100% rename from tutorial/error_cases/none_type_error/none_bool.kv rename to error_cases/none_type_error/none_bool.kv diff --git a/tutorial/error_cases/none_type_error/none_cast.kv b/error_cases/none_type_error/none_cast.kv similarity index 100% rename from tutorial/error_cases/none_type_error/none_cast.kv rename to error_cases/none_type_error/none_cast.kv diff --git a/tutorial/error_cases/none_type_error/none_cmp.kv b/error_cases/none_type_error/none_cmp.kv similarity index 100% rename from tutorial/error_cases/none_type_error/none_cmp.kv rename to error_cases/none_type_error/none_cmp.kv diff --git a/tutorial/error_cases/read_only/dup_param.kv b/error_cases/read_only/dup_param.kv similarity index 100% rename from tutorial/error_cases/read_only/dup_param.kv rename to error_cases/read_only/dup_param.kv diff --git a/tutorial/error_cases/read_only/write_param.kv b/error_cases/read_only/write_param.kv similarity index 100% rename from tutorial/error_cases/read_only/write_param.kv rename to error_cases/read_only/write_param.kv diff --git a/tutorial/error_cases/recursion_error/stack_overflow.kv b/error_cases/recursion_error/stack_overflow.kv similarity index 100% rename from tutorial/error_cases/recursion_error/stack_overflow.kv rename to error_cases/recursion_error/stack_overflow.kv diff --git a/tutorial/error_cases/runtime_error/bootstrap_missing.kv b/error_cases/runtime_error/bootstrap_missing.kv similarity index 100% rename from tutorial/error_cases/runtime_error/bootstrap_missing.kv rename to error_cases/runtime_error/bootstrap_missing.kv diff --git a/tutorial/error_cases/syntax_error/break_outside_loop.kv b/error_cases/syntax_error/break_outside_loop.kv similarity index 100% rename from tutorial/error_cases/syntax_error/break_outside_loop.kv rename to error_cases/syntax_error/break_outside_loop.kv diff --git a/tutorial/error_cases/syntax_error/return_with_value.kv b/error_cases/syntax_error/return_with_value.kv similarity index 100% rename from tutorial/error_cases/syntax_error/return_with_value.kv rename to error_cases/syntax_error/return_with_value.kv diff --git a/tutorial/error_cases/syntax_error/sparse_literal_position.kv b/error_cases/syntax_error/sparse_literal_position.kv similarity index 100% rename from tutorial/error_cases/syntax_error/sparse_literal_position.kv rename to error_cases/syntax_error/sparse_literal_position.kv diff --git a/tutorial/error_cases/syntax_error/top_level_if.kv b/error_cases/syntax_error/top_level_if.kv similarity index 100% rename from tutorial/error_cases/syntax_error/top_level_if.kv rename to error_cases/syntax_error/top_level_if.kv diff --git a/tutorial/error_cases/syntax_error/unclosed_block.kv b/error_cases/syntax_error/unclosed_block.kv similarity index 100% rename from tutorial/error_cases/syntax_error/unclosed_block.kv rename to error_cases/syntax_error/unclosed_block.kv diff --git a/tutorial/error_cases/type_error/arith_bool.kv b/error_cases/type_error/arith_bool.kv similarity index 100% rename from tutorial/error_cases/type_error/arith_bool.kv rename to error_cases/type_error/arith_bool.kv diff --git a/tutorial/error_cases/type_error/arith_string.kv b/error_cases/type_error/arith_string.kv similarity index 100% rename from tutorial/error_cases/type_error/arith_string.kv rename to error_cases/type_error/arith_string.kv diff --git a/tutorial/error_cases/type_error/bitwise_bool.kv b/error_cases/type_error/bitwise_bool.kv similarity index 100% rename from tutorial/error_cases/type_error/bitwise_bool.kv rename to error_cases/type_error/bitwise_bool.kv diff --git a/tutorial/error_cases/type_error/bitwise_float.kv b/error_cases/type_error/bitwise_float.kv similarity index 100% rename from tutorial/error_cases/type_error/bitwise_float.kv rename to error_cases/type_error/bitwise_float.kv diff --git a/error_cases/type_error/bool_branch.kv b/error_cases/type_error/bool_branch.kv new file mode 100644 index 00000000..6e9eb511 --- /dev/null +++ b/error_cases/type_error/bool_branch.kv @@ -0,0 +1,6 @@ +// expected +// TypeError: branch condition must be bool, got int64 +// 修复: B2 铁律 — 分支条件必须是 bool,禁止 int 隐式真值化(用 != 0) +rwfunc test() -> () { + if (int64(1)) { } +} diff --git a/error_cases/type_error/bool_logic.kv b/error_cases/type_error/bool_logic.kv new file mode 100644 index 00000000..16f84ec5 --- /dev/null +++ b/error_cases/type_error/bool_logic.kv @@ -0,0 +1,6 @@ +// expected +// TypeError: && requires bool, got int64 +// 修复: B2 铁律 — 逻辑运算只接受 bool,禁止 int 隐式真值化(用 != 0) +rwfunc test() -> () { + int64(1) && true -> x +} diff --git a/tutorial/error_cases/type_error/char_bad_args.kv b/error_cases/type_error/char_bad_args.kv similarity index 100% rename from tutorial/error_cases/type_error/char_bad_args.kv rename to error_cases/type_error/char_bad_args.kv diff --git a/tutorial/error_cases/type_error/cmp_int_bool.kv b/error_cases/type_error/cmp_int_bool.kv similarity index 100% rename from tutorial/error_cases/type_error/cmp_int_bool.kv rename to error_cases/type_error/cmp_int_bool.kv diff --git a/tutorial/error_cases/type_error/cmp_mixed.kv b/error_cases/type_error/cmp_mixed.kv similarity index 100% rename from tutorial/error_cases/type_error/cmp_mixed.kv rename to error_cases/type_error/cmp_mixed.kv diff --git a/tutorial/error_cases/type_error/compact_string_array.kv b/error_cases/type_error/compact_string_array.kv similarity index 100% rename from tutorial/error_cases/type_error/compact_string_array.kv rename to error_cases/type_error/compact_string_array.kv diff --git a/tutorial/error_cases/type_error/concat_non_string.kv b/error_cases/type_error/concat_non_string.kv similarity index 100% rename from tutorial/error_cases/type_error/concat_non_string.kv rename to error_cases/type_error/concat_non_string.kv diff --git a/tutorial/error_cases/type_error/minmax_mixed.kv b/error_cases/type_error/minmax_mixed.kv similarity index 100% rename from tutorial/error_cases/type_error/minmax_mixed.kv rename to error_cases/type_error/minmax_mixed.kv diff --git a/tutorial/error_cases/type_error/mod_float.kv b/error_cases/type_error/mod_float.kv similarity index 100% rename from tutorial/error_cases/type_error/mod_float.kv rename to error_cases/type_error/mod_float.kv diff --git a/tutorial/error_cases/type_error/neg_unsigned.kv b/error_cases/type_error/neg_unsigned.kv similarity index 100% rename from tutorial/error_cases/type_error/neg_unsigned.kv rename to error_cases/type_error/neg_unsigned.kv diff --git a/tutorial/error_cases/type_error/sqrt_string.kv b/error_cases/type_error/sqrt_string.kv similarity index 100% rename from tutorial/error_cases/type_error/sqrt_string.kv rename to error_cases/type_error/sqrt_string.kv diff --git a/tutorial/error_cases/value_error/log_range.kv b/error_cases/value_error/log_range.kv similarity index 100% rename from tutorial/error_cases/value_error/log_range.kv rename to error_cases/value_error/log_range.kv diff --git a/tutorial/error_cases/zero_division/div_zero.kv b/error_cases/zero_division/div_zero.kv similarity index 100% rename from tutorial/error_cases/zero_division/div_zero.kv rename to error_cases/zero_division/div_zero.kv diff --git a/tutorial/error_cases/zero_division/float_underflow_div.kv b/error_cases/zero_division/float_underflow_div.kv similarity index 100% rename from tutorial/error_cases/zero_division/float_underflow_div.kv rename to error_cases/zero_division/float_underflow_div.kv diff --git a/tutorial/error_cases/zero_division/mod_zero.kv b/error_cases/zero_division/mod_zero.kv similarity index 100% rename from tutorial/error_cases/zero_division/mod_zero.kv rename to error_cases/zero_division/mod_zero.kv diff --git a/layout/Cargo.toml b/layout/Cargo.toml index 70018221..afe884ac 100644 --- a/layout/Cargo.toml +++ b/layout/Cargo.toml @@ -3,7 +3,7 @@ name = "kvlang-layout" version = "0.1.0" edition = "2021" license = "MIT" -description = "kvlang 编译期(parser/lower/layoutcode),只依赖 kvspace-durable 的 C ABI" +description = "kvlang 语法检查 + 布局工具(parser/lower/layoutcode),只依赖 kvspace-durable 的 C ABI" [lib] name = "kvlanglayout" diff --git a/layout/src/ast.rs b/layout/src/ast.rs index fdcaa317..e5e829a5 100644 --- a/layout/src/ast.rs +++ b/layout/src/ast.rs @@ -180,6 +180,43 @@ impl RwirDecl { } } +// ── struct ─────────────────────────────────────────────────────────── + +#[derive(Clone)] +pub struct Field { + pub name: String, + pub ty: String, // kindexpr(字段类型) + pub default: Option, // 默认值字面量(None = 未给) +} + +#[derive(Clone)] +pub struct StructDecl { + pub comments: Vec, + pub name: String, + pub pkg: String, + pub fields: Vec, +} + +impl StructDecl { + pub fn sig_string(&self) -> String { + let mut sb = format!("struct {} {{", self.name); + for (i, fld) in self.fields.iter().enumerate() { + sb.push_str(if i > 0 { "; " } else { " " }); + sb.push_str(&fld.name); + if !fld.ty.is_empty() { + sb.push(':'); + sb.push_str(&fld.ty); + } + if let Some(d) = &fld.default { + sb.push('='); + sb.push_str(&d.to_string()); + } + } + sb.push_str(" }"); + sb + } +} + // ── Expr ───────────────────────────────────────────────────────────── #[derive(Clone, Copy, PartialEq, Eq)] @@ -608,6 +645,7 @@ impl fmt::Display for ScopeStmt { pub struct File { pub package: String, pub rwir_decls: Vec, + pub structs: Vec, pub funcs: Vec, pub top_level_calls: Vec, pub init_body: Vec, @@ -617,6 +655,7 @@ pub struct File { #[derive(Default)] struct PkgNode { rwirs: Vec, + structs: Vec, funcs: Vec, body: Vec, children: std::collections::BTreeMap, @@ -635,6 +674,17 @@ fn pkg_node<'a>(root: &'a mut PkgNode, pkg: &str) -> &'a mut PkgNode { fn emit_node(sb: &mut String, node: &PkgNode, indent: &str) { let mut items: Vec = Vec::new(); + for d in &node.structs { + let mut s = String::new(); + for c in &d.comments { + s.push_str(indent); + s.push_str(c); + s.push('\n'); + } + s.push_str(indent); + s.push_str(&d.sig_string()); + items.push(s); + } for d in &node.rwirs { let mut s = String::new(); for c in &d.comments { @@ -684,6 +734,9 @@ impl File { /// 格式化为规范 kvlang 源码(重建 lib 分组,保留包名,round-trip 语义等价)。 pub fn format(&self) -> String { let mut root = PkgNode::default(); + for d in &self.structs { + pkg_node(&mut root, &d.pkg).structs.push(d.clone()); + } for d in &self.rwir_decls { pkg_node(&mut root, &d.pkg).rwirs.push(d.clone()); } diff --git a/layout/src/bin/kvlanglayout.rs b/layout/src/bin/kvlanglayout.rs index 03475079..a4134eb2 100644 --- a/layout/src/bin/kvlanglayout.rs +++ b/layout/src/bin/kvlanglayout.rs @@ -1,4 +1,4 @@ -//! 读 .kv 文件,用 Rust layout 编译进 kvspace(默认 redis),并输出入口(ENTRY=...), +//! 读 .kv 文件,用 Rust layout 检查语法并布局进 kvspace(默认 redis),并输出入口(ENTRY=...), //! 供 Go runtime 执行验证。 //! 用法: //! kvlanglayout [dsn] 仅 layout,打印 ENTRY=(默认子命令) diff --git a/layout/src/capi.rs b/layout/src/capi.rs index 249fb5ca..dda220d6 100644 --- a/layout/src/capi.rs +++ b/layout/src/capi.rs @@ -209,9 +209,9 @@ pub extern "C" fn kvlangKindexprParse(kindexpr: *const c_char, out: *mut kvlangK if s.is_empty() { return -1; } - let (r, dims, kind) = kvkind::parse_kindexpr(s); + let (dims, kind) = kvkind::parse_kindexpr(s); let out = unsafe { &mut *out }; - out.ref_ = r; + out.ref_ = 0; out.ndim = dims.len() as i32; for (i, d) in dims.iter().enumerate() { if i < 8 { diff --git a/layout/src/code.rs b/layout/src/code.rs index 0c2d7647..8ee8d79b 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -1,7 +1,7 @@ -//! layoutcode(对齐 layout/layout.go 的编译期部分):把 AST 写到 /lib/ 下的结构化 KV。 +//! layoutcode(对齐 layout/layout.go):检查 AST 并把结果布局写到 /lib/ 下的结构化 KV。 //! //! 存储约定: -//! /lib/·/[0,0] 编译后签名(kind=rwfunc) +//! /lib/·/[0,0] 布局后签名(kind=rwfunc) //! /lib/·/ 命名参数→slot 指针(kind=char, isptr=1) //! /lib/·/[i,j] 编译后指令(kind=rwir),i 从 1 开始 //! /lib/·/‥labels/ label → irseq @@ -11,7 +11,7 @@ use std::collections::HashMap; -use super::ast::{Func, Instruction, RwirDecl, Stmt}; +use super::ast::{Expr, Func, Instruction, RwirDecl, Stmt, StructDecl}; use super::ffi::Kv; use super::{builtin, ffi, keytree, kvkind, lower, parser}; @@ -39,6 +39,10 @@ pub fn compile(kv: &mut Kv, src: &str) -> Result, String> { let mut any_code = false; let mut inits: Vec = Vec::new(); + for decl in &file.structs { + write_struct_decl(kv, decl); + any_code = true; + } for func in &file.funcs { let pkg = if func.pkg.is_empty() { file.package.clone() @@ -144,7 +148,7 @@ pub fn vet(src: &str) -> Result<(), String> { let _ = lower::lower_func(func); any_code = true; } - if !file.init_body.is_empty() || !file.top_level_calls.is_empty() { + if !file.init_body.is_empty() || !file.top_level_calls.is_empty() || !file.structs.is_empty() { any_code = true; } if !any_code { @@ -315,7 +319,7 @@ fn collect_slots(kv: &mut Kv, prefix: &str, out: &mut Vec) { } } -/// 注释不允许换行:defrwfunc 的参数类型以 \n 连接,改 " | " 呈现。 +/// 注释不允许换行:rwfunc 签名的参数类型以 \n 连接,改 " | " 呈现。 fn sanitize(s: &str) -> String { s.replace('\n', " | ") } @@ -431,6 +435,63 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { } } +/// 写 struct 原型到 /lib/:基节点(kind=struct) + memindex(/lib/Name·) + 各字段默认值。 +/// 运行时 struct·new 以此为原型 cplist 克隆,故字段默认值即实例初值。 +pub fn write_struct_decl(kv: &mut Kv, decl: &StructDecl) { + let mut name = decl.name.clone(); + if !decl.pkg.is_empty() { + name = format!("{}{}{name}", decl.pkg, keytree::MEMBER_SEP); + } + let base = keytree::rwir(&name); + let _ = kv.del_tree(&base); + let fnames: Vec = decl.fields.iter().map(|f| f.name.clone()).collect(); + let ftypes: Vec<(String, String)> = decl + .fields + .iter() + .map(|f| (f.name.clone(), f.ty.clone())) + .collect(); + let mut pairs: Vec<(String, Vec)> = Vec::new(); + pairs.push((base.clone(), kvkind::new_struct(&ftypes))); + pairs.push((keytree::member(&base, ""), kvkind::new_memindex(&fnames))); + for fld in &decl.fields { + pairs.push(( + keytree::member(&base, &fld.name), + field_default(&fld.ty, fld.default.as_ref()), + )); + } + let _ = kv.set(&pairs); +} + +/// 字段默认值 XValue:head kind = 字段类型,body = 默认字面量(未给则零值)。 +/// 标量+char 直接编码;带 dims / structref 仅记录类型(空 body),嵌套 struct 待定。 +fn field_default(ty: &str, default: Option<&Expr>) -> Vec { + let (dims, base) = kvkind::parse_kindexpr(ty); + let s = default.map(|e| e.val.clone()).unwrap_or_default(); + if base.starts_with("char/") { + return ffi::new_char(&base, &s); + } + if !dims.is_empty() { + return ffi::tlv_encode(&base, &[], dims.iter().product()); + } + let i = || s.parse::().unwrap_or(0); + let u = || s.parse::().unwrap_or(0); + let f = || s.parse::().unwrap_or(0.0); + match base.as_str() { + "bool" => ffi::new_bool(s == "true"), + "int8" => ffi::tlv_encode("int8", &(i() as i8).to_le_bytes(), 1), + "int16" => ffi::tlv_encode("int16", &(i() as i16).to_le_bytes(), 1), + "int32" => ffi::tlv_encode("int32", &(i() as i32).to_le_bytes(), 1), + "int64" => ffi::new_int64(i()), + "uint8" => ffi::tlv_encode("uint8", &[u() as u8], 1), + "uint16" => ffi::tlv_encode("uint16", &(u() as u16).to_le_bytes(), 1), + "uint32" => ffi::tlv_encode("uint32", &(u() as u32).to_le_bytes(), 1), + "uint64" => ffi::tlv_encode("uint64", &u().to_le_bytes(), 1), + "float32" => ffi::tlv_encode("float32", &(f() as f32).to_le_bytes(), 1), + "float64" => ffi::new_float64(f()), + _ => ffi::tlv_encode(&base, &[], 1), + } +} + /// 写用户声明的 rwir(无体)到 /lib/。 pub fn write_rwir_decl(kv: &mut Kv, decl: &RwirDecl) { let mut opcode = decl.sig.name.clone(); @@ -593,7 +654,6 @@ fn is_literal(s: &str) -> bool { || b[0] == b'/' || s == "true" || s == "false" - || s == "null" || b[0].is_ascii_digit() || (b[0] == b'-' && s.len() > 1) } @@ -614,6 +674,16 @@ mod tests { roundtrip("lib byteseek {\nlib session {\nlib s1 {\nrwfunc main() -> () {\n3 + 4 -> s\nprintln(s)\n}\n}\n}\n}\nmain()\n"); } + #[test] + fn null_literal_rejected() { + // 空值只有 None;书写 null 是错误,layout 必须显式拒绝。 + assert!(vet("lib t {\nrwfunc main() -> () {\nnull -> x\n}\n}\n").is_err()); + assert!(vet("lib t {\nrwfunc main() -> () {\nprintln(null)\n}\n}\n").is_err()); + // None、字符串 "null"、含 null 的路径不受影响。 + assert!(vet("lib t {\nrwfunc main() -> () {\nNone -> x\n}\n}\n").is_ok()); + assert!(vet("lib t {\nrwfunc main() -> () {\n\"null\" -> x\n}\n}\n").is_ok()); + } + #[test] fn nested_lib_layout_and_merge() { let mut kv = Kv::conn(&format!( @@ -627,7 +697,7 @@ mod tests { "lib a {\nlib b {\nrwfunc f() -> (r:int64) {\n1 -> r\n}\n}\n}\n", ) .unwrap(); - assert_eq!(kvkind::kind(&kv.get_one("/lib/a/b·f/[0,0]")), "defrwfunc"); + assert_eq!(kvkind::kind(&kv.get_one("/lib/a/b·f/[0,0]")), "rwfunc"); // 同 lib a 下再 layout 另一嵌套 lib c,验证 b·f 未被整库删除(增量合并) compile( @@ -637,10 +707,10 @@ mod tests { .unwrap(); assert_eq!( kvkind::kind(&kv.get_one("/lib/a/b·f/[0,0]")), - "defrwfunc", + "rwfunc", "b·f 应保留" ); - assert_eq!(kvkind::kind(&kv.get_one("/lib/a/c·g/[0,0]")), "defrwfunc"); + assert_eq!(kvkind::kind(&kv.get_one("/lib/a/c·g/[0,0]")), "rwfunc"); } #[test] @@ -658,7 +728,7 @@ mod tests { "lib t {\nrwfunc main() -> () {\nfoo.bar(\"x\") -> y\nbaz(\"z\") -> w\n}\n}\n", ) .unwrap(); - assert_eq!(kvkind::kind(&kv.get_one("/lib/t·main/[0,0]")), "defrwfunc"); + assert_eq!(kvkind::kind(&kv.get_one("/lib/t·main/[0,0]")), "rwfunc"); assert!( kvkind::value_string(&kv.get_one("/lib/t·main/[1,0]")).contains("foo.bar"), "'.' 应作普通字符保留在 opcode 内(单一 token foo.bar)" diff --git a/layout/src/ffi.rs b/layout/src/ffi.rs index 10236a17..79b24df3 100644 --- a/layout/src/ffi.rs +++ b/layout/src/ffi.rs @@ -38,10 +38,11 @@ extern "C" { err: *mut c_char, err_cap: u32, ) -> c_int; - /// 新位置写:按 (kindexpr, body_len) 分配新 box、写 head,返回 body 偏移指针。 + /// 新位置写:按 (xkind, kindexpr, body_len) 分配新 box、写 head,返回 body 偏移指针。 fn kvspaceWriteNewPlace( h: Handle, key: *const c_char, + xkind: u8, kindexpr: *const c_char, body_len: u32, body: *mut *mut u8, @@ -62,7 +63,8 @@ extern "C" { expand_ext: c_int, resolve: c_int, idx: i32, - out: *mut *mut u8, + buf: *mut u8, + buf_cap: u32, out_len: *mut u32, ) -> c_int; fn kvspaceDel( @@ -73,7 +75,13 @@ extern "C" { err_cap: u32, ) -> c_int; fn kvspaceDelTree(h: Handle, prefix: *const c_char, err: *mut c_char, err_cap: u32) -> c_int; - fn kvspaceMkindex(h: Handle, path: *const c_char, err: *mut c_char, err_cap: u32) -> c_int; + fn kvspaceMkindex( + h: Handle, + path: *const c_char, + capacity: u32, + err: *mut c_char, + err_cap: u32, + ) -> c_int; fn kvspaceMkindexExt( h: Handle, path: *const c_char, @@ -111,7 +119,11 @@ extern "C" { /// XValueHead 解码结果(与 kvspace-durable 的 kvspaceHead_t 布局一致)。kindexpr 为唯一类型真相。 #[repr(C)] pub struct kvspaceHead_t { + pub xkind: u8, pub kindexpr: [u8; 256], + pub kind_off: i32, + pub ndim: i32, + pub dims: [i32; 8], pub ro: u8, pub vid: u32, pub body_len: i32, @@ -194,6 +206,7 @@ impl Kv { kvspaceWriteNewPlace( self.h, ck.as_ptr(), + h.xkind, kindexpr.as_ptr(), body_len as u32, &mut body, @@ -237,19 +250,22 @@ impl Kv { } let mut v = Vec::with_capacity(count as usize); for i in 0..count { - let bytes = call_borrow(|out, out_len| unsafe { + let mut buf = [0u8; 1024]; + let mut out_len: u32 = 0; + let ok = unsafe { kvspaceListAt( self.h, c.as_ptr(), expand_ext as c_int, resolve as c_int, i, - out, - out_len, + buf.as_mut_ptr(), + buf.len() as u32, + &mut out_len, ) - }); - if !bytes.is_empty() { - v.push(String::from_utf8_lossy(&bytes).into_owned()); + } == 0; + if ok && out_len > 0 { + v.push(String::from_utf8_lossy(&buf[..out_len as usize]).into_owned()); } } v @@ -265,7 +281,8 @@ impl Kv { pub fn mkindex(&mut self, path: &str) -> Result<(), String> { let c = CString::new(path).expect("no NUL"); let mut err: [c_char; 256] = [0; 256]; - let ret = unsafe { kvspaceMkindex(self.h, c.as_ptr(), err.as_mut_ptr(), err.len() as u32) }; + let ret = + unsafe { kvspaceMkindex(self.h, c.as_ptr(), 0, err.as_mut_ptr(), err.len() as u32) }; err_ret(&mut err, ret) } @@ -333,7 +350,11 @@ pub fn tlv_encode(kind: &str, raw: &[u8], array_len: i32) -> Vec { /// 解码 XValueHead。 pub fn decode_head(data: &[u8]) -> kvspaceHead_t { let mut h = kvspaceHead_t { + xkind: 0, kindexpr: [0u8; 256], + kind_off: 0, + ndim: 0, + dims: [0i32; 8], ro: 0, vid: 0, body_len: 0, diff --git a/layout/src/kindexpr.rs b/layout/src/kindexpr.rs index a9e274e3..d6e28b46 100644 --- a/layout/src/kindexpr.rs +++ b/layout/src/kindexpr.rs @@ -39,11 +39,25 @@ fn known_kind(k: &str) -> bool { | "rwir" | "rwfunc" | "scope" + | "struct" | "time" | "duration" ) } +/// structref = "/" path:指向 /lib 下类型定义节点的完整路径(实例 kindexpr、struct 字段类型)。 +/// 仅做语法承认(`/` + 合法路径段),不解析 /lib 是否存在该类型 —— 存在性/字段一致性留给 runtime。 +fn valid_structref(s: &str) -> bool { + let rest = match s.strip_prefix('/') { + Some(r) => r, + None => return false, + }; + !rest.is_empty() + && rest.split('/').all(|seg| { + !seg.is_empty() && seg.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') + }) +} + fn valid_base(s: &str) -> bool { if s.is_empty() { return false; @@ -118,8 +132,11 @@ fn valid_key(s: &str) -> bool { tail.is_empty() && inner.split(',').all(valid_scalar) } -/// atom = shape | mapexpr;mapexpr = key "·" type(value 可递归含容器,故支持嵌套 map)。 +/// atom = shape | mapexpr | structref;mapexpr = key "·" type;structref = "/" path。 fn valid_atom(s: &str) -> bool { + if s.starts_with('/') { + return valid_structref(s); + } if let Some(i) = s.find('·') { return valid_key(&s[..i]) && valid_kindexpr(&s[i + '·'.len_utf8()..]); } @@ -151,7 +168,7 @@ fn base_match(s: &str, kind: &str) -> bool { fn match_shape(s: &str, ndim: i32, dims: &[i32]) -> bool { if s.is_empty() { - return ndim >= 1; + return ndim == 1; } let parts: Vec<&str> = s.split(',').collect(); if parts.len() as i32 != ndim { @@ -235,6 +252,11 @@ mod tests { "[int32,int32]·[]char/utf8", "[float32,float32]·[]char/utf8", "[float32,int8,uint32]·int64", + "struct", + "/lib/Point", + "/lib/geom/Point", + "/lib/Node", + "/lib/Point|/lib/Node", ] { assert!(valid_kindexpr(e), "{e} should be valid"); } @@ -248,9 +270,16 @@ mod tests { "[?]·int64", "[int32,]·int64", "[foo,int32]·int64", + "/", + "/lib/", + "/lib//Point", + "lib/Point", + "/lib/Po int", ] { assert!(!valid_kindexpr(e), "{e} should be invalid"); } + assert!(match_kindexpr("/lib/Point", "/lib/Point", 0, &[])); + assert!(!match_kindexpr("/lib/Point", "/lib/Node", 0, &[])); assert!(match_kindexpr("[]char/utf8·int64", "stringkeymap", 1, &[3])); assert!(!match_kindexpr("[]char/utf8·int64", "object", 0, &[])); } @@ -315,6 +344,8 @@ mod tests { ("int64|float64", "float64", 0, &[], true), ("int64|float64", "bool", 0, &[], false), ("[]float32", "float32", 1, &[5], true), + ("[]float32", "float32", 2, &[2, 3], false), + ("[]float32", "float32", 0, &[], false), ("[2,3]float32", "float32", 2, &[2, 3], true), ("[2,3]float32", "float32", 2, &[2, 4], false), ("[?,768]float32", "float32", 2, &[100, 768], true), diff --git a/layout/src/kvkind.rs b/layout/src/kvkind.rs index 01889402..bac60325 100644 --- a/layout/src/kvkind.rs +++ b/layout/src/kvkind.rs @@ -18,12 +18,12 @@ pub const KIND_OBJ: &str = "object"; pub const KIND_MAP: &str = "stringkeymap"; pub const KIND_INDEX: &str = "index"; pub const KIND_EXT_INDEX: &str = "extindex"; +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_RWFUNC: &str = "defrwfunc"; pub const KIND_RWIR_OR_RWFUNC: &str = "rwir|rwfunc"; pub const KIND_SCOPE: &str = "scope"; @@ -38,28 +38,22 @@ pub fn head(data: &[u8]) -> ffi::kvspaceHead_t { ffi::decode_head(data) } -/// 解析 head 的 kindexpr 内容 → (ref, dims, base kind)。 -pub fn parse_kindexpr(kx: &str) -> (i32, Vec, String) { - let (r, rest) = match kx.as_bytes().first() { - Some(b'*') => (1, &kx[1..]), - Some(b'@') => (2, &kx[1..]), - _ => (0, kx), - }; - if rest.starts_with('[') { - match rest.find(']') { +/// 解析 kindexpr 内容 → (dims, base kind)。kindexpr 无前缀(ref/ptr 归 head.xkind)。 +pub fn parse_kindexpr(kx: &str) -> (Vec, String) { + if kx.starts_with('[') { + match kx.find(']') { Some(end) => ( - r, - rest[1..end] + kx[1..end] .split(',') .filter(|d| !d.is_empty()) .map(|d| d.parse().unwrap_or(0)) .collect(), - rest[end + 1..].to_string(), + kx[end + 1..].to_string(), ), - None => (r, Vec::new(), rest.to_string()), + None => (Vec::new(), kx.to_string()), } } else { - (r, Vec::new(), rest.to_string()) + (Vec::new(), kx.to_string()) } } @@ -81,18 +75,18 @@ pub fn kind(data: &[u8]) -> String { if data.is_empty() { return String::new(); } - parse_kindexpr(&kindexpr(data)).2 + parse_kindexpr(&kindexpr(data)).1 } pub fn is_ptr(data: &[u8]) -> bool { - !data.is_empty() && parse_kindexpr(&kindexpr(data)).0 == 1 + !data.is_empty() && ffi::decode_head(data).xkind == 1 } pub fn array_len(data: &[u8]) -> i32 { if data.is_empty() { return 0; } - let dims = parse_kindexpr(&kindexpr(data)).1; + let dims = parse_kindexpr(&kindexpr(data)).0; if dims.is_empty() { 1 } else { @@ -128,13 +122,13 @@ pub fn display(data: &[u8]) -> String { if data.is_empty() { return "None".to_string(); } - let (r, _, k) = parse_kindexpr(&kindexpr(data)); + let (_, k) = parse_kindexpr(&kindexpr(data)); if k.is_empty() { return "None".to_string(); } let h = ffi::decode_head(data); let b = body(data, &h); - if r == 1 { + if h.xkind == 1 { return format!("→{}:{}", String::from_utf8_lossy(b), k); } format!("{}:{}", k, plain_value(&k, b)) @@ -194,7 +188,7 @@ fn plain_value(k: &str, b: &[u8]) -> String { .collect(), "index" => format!("({})", count_names(b)), // kvlang 自有 kind:body = [2B nr][2B nw][sig];槽值/调用目标 nr=nw=0,取 sig 即可。 - "rwir" | "rwir|rwfunc" | "defrwir" | "defrwfunc" => { + "rwir" | "rwir|rwfunc" | "rwfunc" | "defrwir" => { let (nr, nw) = if b.len() >= 4 { ( u16::from_le_bytes([b[0], b[1]]), @@ -245,6 +239,26 @@ pub fn new_defrwir(nr: i32, nw: i32, sig: &str) -> Vec { ffi::tlv_encode(KIND_DEF_RWIR, &rwir_body(nr, nw, sig), 1) } +// ── struct 原型(对齐 runtime kvlangBuiltinMemindex)───────────────── +// +// /lib/Name kind=struct,body="name:kindexpr\n..."(字段声明类型,供实例化类型校验) +// /lib/Name· kind=index,body=[4B count LE][name\n...](字段名唯一权威) + +pub fn new_struct(fields: &[(String, String)]) -> Vec { + let body = fields + .iter() + .map(|(n, t)| format!("{n}:{t}")) + .collect::>() + .join("\n"); + ffi::tlv_encode(KIND_STRUCT, body.as_bytes(), 1) +} + +pub fn new_memindex(names: &[String]) -> Vec { + let mut raw = (names.len() as u32).to_le_bytes().to_vec(); + raw.extend_from_slice(names.join("\n").as_bytes()); + ffi::tlv_encode(KIND_INDEX, &raw, 1) +} + // ── kvlang 自有 kind:rwfunc ──────────────────────────────────────── // // body = [2B nr LE][2B nw LE][param_types 以 \n 连接],array_len=num_insts。 @@ -254,7 +268,7 @@ pub fn new_rwfunc(num_insts: i32, nr: i32, nw: i32, param_types: &[String]) -> V raw.extend_from_slice(&(nr as u16).to_le_bytes()); raw.extend_from_slice(&(nw as u16).to_le_bytes()); raw.extend_from_slice(param_types.join("\n").as_bytes()); - ffi::tlv_encode(KIND_DEF_RWFUNC, &raw, num_insts) + ffi::tlv_encode(KIND_RWFUNC, &raw, num_insts) } /// rwfunc body 访问器(layout 读回签名时用)。 diff --git a/layout/src/lib.rs b/layout/src/lib.rs index e80e6513..93089b17 100644 --- a/layout/src/lib.rs +++ b/layout/src/lib.rs @@ -1,5 +1,5 @@ #![allow(non_snake_case)] -//! kvlang-layout — 编译期:parse → lower → layoutcode。 +//! kvlang-layout — 语法检查 + 布局工具:parse → lower → layoutcode(把 AST 检查后布局写入 /lib/)。 //! 只依赖 kvspace-durable 的 C ABI(见 [`ffi`]),不依赖其 Rust 类型。 //! 翻译自 kvlang 的 Go 源码:parser/ lower/ layout/ ast/ symbol/ keytree/。 diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 96723b86..6d311b57 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -2,7 +2,9 @@ //! //! 入口:`parse_code(src) → Result<(File, Vec), String>`。 -use super::ast::{self, Expr, Func, FuncSig, Instruction, Param, RwirDecl, Stmt}; +use super::ast::{ + self, Expr, Field, Func, FuncSig, Instruction, Param, RwirDecl, Stmt, StructDecl, +}; use super::keytree; use super::scanner::{scan, Diagnostic, Kind, Pos, Token}; use super::symbol; @@ -144,6 +146,17 @@ impl Parser { continue; } + let is_struct = self.peek().kind == Kind::Ident + && self.peek().value == "struct" + && self.peek_at(1).kind == Kind::Ident + && self.peek_at(2).kind == Kind::LBrace; + if is_struct { + let mut decl = self.parse_struct_decl(); + decl.comments = comments; + f.structs.push(decl); + continue; + } + if self.peek().kind == Kind::Ident && self.peek().value == "rwir" { let decl = self.parse_rwir_decl(); f.rwir_decls.push(decl); @@ -258,6 +271,17 @@ impl Parser { self.parse_lib_body(f, &pkg); continue; } + let is_struct = self.peek().kind == Kind::Ident + && self.peek().value == "struct" + && self.peek_at(1).kind == Kind::Ident + && self.peek_at(2).kind == Kind::LBrace; + if is_struct { + let mut decl = self.parse_struct_decl(); + decl.pkg = pkg.clone(); + decl.comments = comments; + f.structs.push(decl); + continue; + } if self.peek().kind == Kind::Ident && self.peek().value == "rwir" { let mut decl = self.parse_rwir_decl(); decl.pkg = pkg.clone(); @@ -351,6 +375,60 @@ impl Parser { decl } + fn parse_struct_decl(&mut self) -> StructDecl { + self.advance(); // consume 'struct' + let name = self.advance().value; // struct 名 + self.expect(Kind::LBrace); + let mut fields = Vec::new(); + loop { + while matches!( + self.peek().kind, + Kind::Newline | Kind::Comma | Kind::Comment + ) { + self.advance(); + } + if self.peek().kind == Kind::RBrace || self.peek().kind == Kind::EOF { + break; + } + if self.peek().kind != Kind::Ident { + let t = self.peek(); + self.errors.push(Diagnostic { + pos: t.pos, + message: format!("struct {name:?}: expected field name, got {:?}", t.value), + warn: false, + info: false, + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); + break; + } + let fname = self.advance().value; + let mut ty = String::new(); + if self.peek().kind == Kind::Colon { + self.advance(); + ty = self.parse_type(); + } + let mut default = None; + if self.peek().kind == Kind::Arrow && self.peek().value == "=" { + self.advance(); + default = self.parse_pratt(0); + } + fields.push(Field { + name: fname, + ty, + default, + }); + } + self.expect(Kind::RBrace); + StructDecl { + comments: Vec::new(), + name, + pkg: String::new(), + fields, + } + } + fn parse_func_sig(&mut self) -> FuncSig { self.advance(); // consume 'rwfunc' let mut sig = FuncSig { @@ -1163,6 +1241,76 @@ impl Parser { return Some(ast::call("array", elems)); } + // struct 字面量 Name{...} 或 pkg.Name{...} → struct·new("/lib/…", "f", v, …) + let struct_lit = t.kind == Kind::Ident + && (self.peek_at(1).kind == Kind::LBrace || { + let mut j = 1isize; + while self.peek_at(j).kind == Kind::Dot && self.peek_at(j + 1).kind == Kind::Ident { + j += 2; + } + j > 1 && self.peek_at(j).kind == Kind::LBrace + }); + if struct_lit { + let mut path = format!("{}/{}", keytree::LIB_ROOT, self.advance().value); + while self.peek().kind == Kind::Dot && self.peek_at(1).kind == Kind::Ident { + self.advance(); // skip Dot + path.push_str(keytree::MEMBER_SEP); + path.push_str(&self.advance().value); + } + let mut args = vec![ast::str_lit(&path)]; + self.advance(); // consume { + loop { + while matches!( + self.peek().kind, + Kind::Newline | Kind::Comma | Kind::Comment + ) { + self.advance(); + } + if self.peek().kind == Kind::RBrace || self.peek().kind == Kind::EOF { + break; + } + if self.peek().kind != Kind::Ident { + let bt = self.peek(); + self.errors.push(Diagnostic { + pos: bt.pos, + warn: false, + info: false, + message: format!("struct literal: expected field name, got {:?}", bt.value), + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); + break; + } + let key = self.advance().value; + if !(self.peek().kind == Kind::Arrow && self.peek().value == "=") { + let bt = self.peek(); + self.errors.push(Diagnostic { + pos: bt.pos, + warn: false, + info: false, + message: format!("struct literal: expected '=' after {key:?}"), + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); + break; + } + self.advance(); // consume = + let val = match self.parse_pratt(0) { + Some(v) => v, + None => break, + }; + args.push(ast::str_lit(&key)); + args.push(val); + } + self.expect(Kind::RBrace); + return Some(ast::call( + &format!("struct{}new", keytree::MEMBER_SEP), + args, + )); + } + // dict 字面量 if t.kind == Kind::LBrace { let mut j = 1isize; @@ -1407,6 +1555,17 @@ impl Parser { } } } + if t.kind == Kind::Ident && t.value == "null" { + self.errors.push(Diagnostic { + pos: t.pos, + message: "null 不是合法字面量;空值只有 None".to_string(), + warn: false, + info: false, + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); + } Some(ast::leaf(&t.value)) } @@ -1703,6 +1862,21 @@ impl Parser { if s.starts_with('/') { return; } + // struct 赋值(RHS = struct·new):浅拷 base+一层成员,lower 为 kv·cplist(struct·new→temp, dst)。 + // 不走 kv·set —— 后者只搬基值,struct 的成员会丢在临时槽。dst 传完整成员槽串, + // runtime ResolveWriteSlot 拼 +"base·key…" 即成员绝对路径。 + let is_struct_new = inst + .expr + .as_ref() + .map(|e| e.op == format!("struct{}new", keytree::MEMBER_SEP)) + .unwrap_or(false); + if is_struct_new && !s.contains('*') { + let e = inst.expr.take().unwrap(); + inst.expr = Some(ast::call("kv·cplist", vec![e, ast::leaf(&s)])); + inst.writes = Vec::new(); + inst.write_types = Vec::new(); + return; + } // 成员链写槽 p·a·b = v → 变参 kv·set(p, "a", "b", v),与读侧 #110 一致逐段拼路径。 // 不再按首个 · 压扁成 kv·set(p, "a·b", v):扁平段把「成员链」与「含 · 的成员名」混为一谈。 let mut parts = s.split(keytree::MEMBER_SEP); diff --git a/layout/tests/pipeline_test.rs b/layout/tests/pipeline_test.rs index dcec290d..ada355ca 100644 --- a/layout/tests/pipeline_test.rs +++ b/layout/tests/pipeline_test.rs @@ -25,7 +25,7 @@ fn compile_simple_func() { // 函数签名 let sig_val = kv.get_one("/lib/sum/[0,0]"); - assert_eq!(kvkind::kind(&sig_val), "defrwfunc"); + assert_eq!(kvkind::kind(&sig_val), "rwfunc"); assert_eq!(kvkind::array_len(&sig_val), 2); // add + return let b = body(&sig_val); assert_eq!(kvkind::rwfunc_num_reads(b), 2); @@ -66,7 +66,7 @@ fn compile_string_literal_and_lib() { // 函数在 /lib/p·hi/ 下(lib 块 pkg 前缀) let sig_val = kv.get_one("/lib/p·hi/[0,0]"); - assert_eq!(kvkind::kind(&sig_val), "defrwfunc"); + assert_eq!(kvkind::kind(&sig_val), "rwfunc"); // 字符串字面量读槽 → char/utf32(UTF-32 LE 码点) let r = kv.get_one("/lib/p·hi/[1,-1]"); @@ -102,7 +102,7 @@ fn compile_control_flow_is_single_plane() { compile(&mut kv, src).unwrap(); let sig_val = kv.get_one("/lib/f/[0,0]"); - assert_eq!(kvkind::kind(&sig_val), "defrwfunc"); + assert_eq!(kvkind::kind(&sig_val), "rwfunc"); let children = kv.list("/lib/f/", false, false); let old_scope = children.iter().any(|c| { let b = c.trim_end_matches('/'); diff --git a/layout/tests/redis_roundtrip_test.rs b/layout/tests/redis_roundtrip_test.rs index 39212c44..da7cae82 100644 --- a/layout/tests/redis_roundtrip_test.rs +++ b/layout/tests/redis_roundtrip_test.rs @@ -35,7 +35,7 @@ fn redis_compile() { compile(&mut kv, src).unwrap(); let sig_val = kv.get_one("/lib/sum/[0,0]"); - assert_eq!(kvkind::kind(&sig_val), "defrwfunc"); + assert_eq!(kvkind::kind(&sig_val), "rwfunc"); let b = body(&sig_val); assert_eq!(kvkind::rwfunc_num_reads(b), 2); assert_eq!(kvkind::rwfunc_num_writes(b), 1); diff --git a/runtime-rs/src/engine.rs b/runtime-rs/src/engine.rs index 2b1aba2a..16b92f8b 100644 --- a/runtime-rs/src/engine.rs +++ b/runtime-rs/src/engine.rs @@ -20,7 +20,7 @@ impl Engine { /// 写即构造:按 (kindexpr, body) 向 kvspace 要偏移指针后直接写 body 字节—— /// key 已存在且同 body_len → WriteInPlace(原 box 就地);否则 WriteNewPlace(新 box)。 /// 两分支各调唯一原语、无预 encode 整条 TLV、无中转 buffer、无 free。 - fn write_construct(&self, key: &str, kindexpr: &str, body: &[u8]) { + fn write_construct(&self, key: &str, xkind: u8, kindexpr: &str, body: &[u8]) { unsafe { let ck = cs(key); let mut bp: *mut u8 = null_mut(); @@ -38,6 +38,7 @@ impl Engine { kvspaceWriteNewPlace( self.kv, ck.as_ptr(), + xkind, cs(kindexpr).as_ptr(), body.len() as u32, &mut bp, @@ -90,7 +91,7 @@ impl Engine { /// 扩展世界(@ ref=2)句柄编码写入:kind=目标完整 kindexpr(如 "[]uint8"),body=定位串。 /// 读取该 key 时由 read_at 按 body 前缀路由给对应 /lib/networld/* 兑现器还原真实字节。 pub fn set_ext_handle(&self, key: &str, target_kindexpr: &str, locator: &str) { - self.write_construct(key, &format!("@{target_kindexpr}"), locator.as_bytes()); + self.write_construct(key, 2, target_kindexpr, locator.as_bytes()); } /// 读 key 的 head,返回 (ref, body 串)。仅 ref==2 时 body 有意义(扩展句柄定位串)。 @@ -102,9 +103,9 @@ impl Engine { unsafe { let mut head = KvspaceHead::default(); kvspaceDecodeHead(tlv.as_ptr(), tlv.len() as u32, &mut head); - let r = match head.kindexpr[0] { - b'@' => 2, - b'*' => 1, + let r = match head.xkind { + 2 => 2, + 1 => 1, _ => 0, }; if r != 2 { @@ -172,9 +173,9 @@ impl Engine { if kvspaceDecodeHead(tlv.as_ptr(), tlv.len() as u32, &mut h) != 0 { return Vec::new(); } - let r = match h.kindexpr[0] { - b'@' => 2, - b'*' => 1, + let r = match h.xkind { + 2 => 2, + 1 => 1, _ => 0, }; let (bo, bl) = (h.body_offset as usize, h.body_len.max(0) as usize); @@ -221,15 +222,21 @@ impl Engine { } let mut v = Vec::with_capacity(count as usize); for i in 0..count { - let (mut out, mut olen) = (null_mut(), 0u32); - if kvspaceListAt(self.kv, cp.as_ptr(), 0, 0, i, &mut out, &mut olen) == 0 - && !out.is_null() + let mut buf = [0u8; 1024]; + let mut olen = 0u32; + if kvspaceListAt( + self.kv, + cp.as_ptr(), + 0, + 0, + i, + buf.as_mut_ptr(), + buf.len() as u32, + &mut olen, + ) == 0 && olen > 0 { - v.push( - String::from_utf8_lossy(std::slice::from_raw_parts(out, olen as usize)) - .into_owned(), - ); + v.push(String::from_utf8_lossy(&buf[..olen as usize]).into_owned()); } } v @@ -285,7 +292,7 @@ impl Engine { .trim_end_matches('\0') .to_string(); let (bo, bl) = (head.body_offset as usize, head.body_len.max(0) as usize); - self.write_construct(key, &kx, &tlv[bo..bo + bl]); + self.write_construct(key, head.xkind, &kx, &tlv[bo..bo + bl]); } } diff --git a/runtime-rs/src/ffi.rs b/runtime-rs/src/ffi.rs index e6b1f77e..66423bec 100644 --- a/runtime-rs/src/ffi.rs +++ b/runtime-rs/src/ffi.rs @@ -8,7 +8,11 @@ use std::ffi::{c_char, c_int, c_void, CStr, CString}; /// kvspaceDecodeHead 输出(与 kvspace-durable/src/ffi.rs::kvspaceHead_t 对齐)。kindexpr 为唯一类型真相。 #[repr(C)] pub struct KvspaceHead { + pub xkind: u8, pub kindexpr: [u8; 256], + pub kind_off: i32, + pub ndim: i32, + pub dims: [i32; 8], pub ro: u8, pub vid: u32, pub body_len: i32, @@ -18,7 +22,11 @@ pub struct KvspaceHead { impl Default for KvspaceHead { fn default() -> Self { KvspaceHead { + xkind: 0, kindexpr: [0u8; 256], + kind_off: 0, + ndim: 0, + dims: [0i32; 8], ro: 0, vid: 0, body_len: 0, @@ -95,6 +103,7 @@ unsafe extern "C" { pub fn kvspaceWriteNewPlace( h: *mut c_void, key: *const c_char, + xkind: u8, kindexpr: *const c_char, body_len: u32, body: *mut *mut u8, @@ -116,7 +125,8 @@ unsafe extern "C" { expand_ext: c_int, resolve: c_int, idx: i32, - out: *mut *mut u8, + buf: *mut u8, + buf_cap: u32, out_len: *mut u32, ) -> c_int; pub fn kvspaceTlvEncode( @@ -147,6 +157,8 @@ unsafe extern "C" { ) -> c_int; // ── kvlang runtime:rwirext 宿主 ABI(均传 kvspace 句柄)───────── + // C 头 kvlang_rwirext.h 导出 9 符号;此处声明 7:故意省略 KindexprValid/KindexprMatch + // ——kindexpr 校验属 layout 期、匹配属 C dispatch 内部,Rust term 侧不调用(非缺陷)。 pub fn kvlang_rwirextRegister( kvspace: *mut c_void, opcode: *const c_char, diff --git a/runtime-rs/src/rwir/json.rs b/runtime-rs/src/rwir/json.rs index c467fa73..81570fbf 100644 --- a/runtime-rs/src/rwir/json.rs +++ b/runtime-rs/src/rwir/json.rs @@ -118,7 +118,7 @@ fn parse_tlv(data: &[u8]) -> (String, Vec, usize) { let kx = String::from_utf8_lossy(&h.kindexpr) .trim_end_matches('\0') .to_string(); - let (_, dims, kind) = parse_kindexpr(&kx); + let (dims, kind) = parse_kindexpr(&kx); let (bo, bl) = (h.body_offset as usize, h.body_len.max(0) as usize); let raw = if bo + bl <= data.len() { data[bo..bo + bl].to_vec() @@ -271,27 +271,21 @@ fn mk_map_value(n: usize) -> Vec { // ── kindexpr 串解析(反序列化按类型/形状分发用;kvspace 未导出串解析器)───────── -fn parse_kindexpr(kx: &str) -> (i32, Vec, String) { - let (r, rest) = match kx.as_bytes().first() { - Some(b'*') => (1, &kx[1..]), - Some(b'@') => (2, &kx[1..]), - _ => (0, kx), - }; - if rest.starts_with('[') { - match rest.find(']') { +fn parse_kindexpr(kx: &str) -> (Vec, String) { + if kx.starts_with('[') { + match kx.find(']') { Some(end) => ( - r, - rest[1..end] + kx[1..end] .split(',') .filter(|d| !d.is_empty()) .map(|d| d.parse().unwrap_or(0)) .collect(), - rest[end + 1..].to_string(), + kx[end + 1..].to_string(), ), - None => (r, Vec::new(), rest.to_string()), + None => (Vec::new(), kx.to_string()), } } else { - (r, Vec::new(), rest.to_string()) + (Vec::new(), kx.to_string()) } } diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index 0a10c5a9..137a3d98 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -360,7 +360,7 @@ static int kvlangBuiltinCmp(kvlangFrame_t *f, cmp_op op) { r = op == CMP_EQ ? c == 0 : op == CMP_NEQ ? c != 0 : op == CMP_LT ? c < 0 : op == CMP_GT ? c > 0 : op == CMP_LE ? c <= 0 : c >= 0; free(a); free(b); } else if (strcmp(ka, KVSPACE_KIND_BOOL) == 0 && strcmp(kb, KVSPACE_KIND_BOOL) == 0) { - bool a = kvlangXvalueAsBool(&in[0]), b = kvlangXvalueAsBool(&in[1]); + bool a = kvlangXvalueAsInt64(&in[0]) != 0, b = kvlangXvalueAsInt64(&in[1]) != 0; r = op == CMP_EQ ? a == b : op == CMP_NEQ ? a != b : op == CMP_LT ? a < b : op == CMP_GT ? a > b : op == CMP_LE ? a <= b : a >= b; } else { kvlangBuiltinSetErr(f, "TypeError: cannot compare %s with %s", ka, kb); kvlangBuiltinFreeInputs(in, n); return -1; @@ -377,21 +377,34 @@ static int kvlangBuiltinGt(kvlangFrame_t *f) { return kvlangBuiltinCmp(f, CMP_GT static int kvlangBuiltinLe(kvlangFrame_t *f) { return kvlangBuiltinCmp(f, CMP_LE); } static int kvlangBuiltinGe(kvlangFrame_t *f) { return kvlangBuiltinCmp(f, CMP_GE); } +static bool require_bool(kvlangFrame_t *f, const char *op, kvlangXvalue_t *in, int n, int want) { + if (n < want) return false; + for (int i = 0; i < want; i++) { + if (!kvlangXvalueKindIs(&in[i], KVSPACE_KIND_BOOL)) { + kvlangBuiltinSetErr(f, "TypeError: %s requires bool, got %s", op, kvlangXvalueKind(&in[i])); + return false; + } + } + return true; +} static int kvlangBuiltinAnd(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); - kvlangXvalue_t r; kvlangXvalueNewBool(&r, n >= 2 && kvlangXvalueAsBool(&in[0]) && kvlangXvalueAsBool(&in[1])); + if (!require_bool(f, "&&", in, n, 2)) { kvlangBuiltinFreeInputs(in, n); return -1; } + kvlangXvalue_t r; kvlangXvalueNewBool(&r, kvlangXvalueAsInt64(&in[0]) != 0 && kvlangXvalueAsInt64(&in[1]) != 0); int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); kvlangBuiltinFreeInputs(in, n); return rc; } static int kvlangBuiltinOr(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); - kvlangXvalue_t r; kvlangXvalueNewBool(&r, n >= 2 && (kvlangXvalueAsBool(&in[0]) || kvlangXvalueAsBool(&in[1]))); + if (!require_bool(f, "||", in, n, 2)) { kvlangBuiltinFreeInputs(in, n); return -1; } + kvlangXvalue_t r; kvlangXvalueNewBool(&r, kvlangXvalueAsInt64(&in[0]) != 0 || kvlangXvalueAsInt64(&in[1]) != 0); int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); kvlangBuiltinFreeInputs(in, n); return rc; } static int kvlangBuiltinNot(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); - kvlangXvalue_t r; kvlangXvalueNewBool(&r, !(n >= 1 && kvlangXvalueAsBool(&in[0]))); + if (!require_bool(f, "!", in, n, 1)) { kvlangBuiltinFreeInputs(in, n); return -1; } + kvlangXvalue_t r; kvlangXvalueNewBool(&r, kvlangXvalueAsInt64(&in[0]) == 0); int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); kvlangBuiltinFreeInputs(in, n); return rc; } @@ -470,7 +483,10 @@ static int kvlangBuiltinCastNum(kvlangFrame_t *f, const char *kind) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); if (n < 1 || kvlangXvalueNone(&in[0])) { kvlangBuiltinSetErr(f, "TypeError: cannot cast None"); kvlangBuiltinFreeInputs(in, n); return -1; } kvlangXvalue_t r; - if (strcmp(kind, KVSPACE_KIND_BOOL) == 0) kvlangXvalueNewBool(&r, kvlangXvalueAsBool(&in[0])); + if (strcmp(kind, KVSPACE_KIND_BOOL) == 0) { + if (!kvlangXvalueKindIs(&in[0], KVSPACE_KIND_BOOL)) { kvlangBuiltinSetErr(f, "TypeError: cannot cast %s to bool — use != 0", kvlangXvalueKind(&in[0])); kvlangBuiltinFreeInputs(in, n); return -1; } + kvlangXvalueNewBool(&r, kvlangXvalueAsInt64(&in[0]) != 0); + } else if (strcmp(kind, KVSPACE_KIND_FLOAT32) == 0) { float fv = (float)kvlangXvalueAsFloat64(&in[0]); uint8_t b[4]; memcpy(b, &fv, 4); kvlangXvalueNewTlv(&r, KVSPACE_KIND_FLOAT32, b, 4, 1); } else if (strcmp(kind, KVSPACE_KIND_FLOAT64) == 0) kvlangXvalueNewFloat64(&r, kvlangXvalueAsFloat64(&in[0])); else { int64_t v = kvlangXvalueAsInt64(&in[0]); narrow_int(kind, kind, v, &r); } @@ -545,7 +561,7 @@ static const struct { const char *op; kvlangBuiltinFn fn; } myrwircaps[] = { {"array", kvlangBuiltinArray}, {"array·scatter", kvlangBuiltinScatter}, {"array·compact", kvlangBuiltinCompact}, {"array·append", kvlangBuiltinAppend}, {"array·slice", kvlangBuiltinSlice}, - {"obj", kvlangBuiltinObj}, {"map", kvlangBuiltinMap}, + {"obj", kvlangBuiltinObj}, {"map", kvlangBuiltinMap}, {"struct·new", kvlangBuiltinStructNew}, {"ndarray·numel", kvlangBuiltinNdarrayNumel}, {"ndarray·dim", kvlangBuiltinNdarrayDim}, {"ndarray·shape", kvlangBuiltinNdarrayShape}, {"xv·at", kvlangBuiltinXvAt}, {"xv·set", kvlangBuiltinXvSet}, {"xv·reshape", kvlangBuiltinXvReshape}, {"xv·reinterpret", kvlangBuiltinXvReinterpret}, @@ -567,8 +583,8 @@ static const struct { const char *op; kvlangBuiltinFn fn; } myrwircaps[] = { {"time·before", kvlangBuiltinTimeCmp}, {"time·after", kvlangBuiltinTimeCmp}, {"random·uint64", kvlangBuiltinRandUint64}, {"random·int63", kvlangBuiltinRandInt63}, {"random·intn", kvlangBuiltinRandIntn}, {"kv·get", kvlangBuiltinKvGet}, {"kv·set", kvlangBuiltinKvSet}, {"kv·del", kvlangBuiltinKvDel}, - {"kv·deltree", kvlangBuiltinKvDelTree}, {"kv·list", kvlangBuiltinKvList}, {"kv·listlen", kvlangBuiltinKvListLen}, {"kv·listn", kvlangBuiltinKvListN}, {"kv·mkindex", kvlangBuiltinKvMkindex}, - {"kv·extindex", kvlangBuiltinKvExtIndex}, {"kv·rmindexext", kvlangBuiltinKvRmIndexExt}, {"kv·watch", kvlangBuiltinKvWatch}, + {"kv·deltree", kvlangBuiltinKvDelTree}, {"kv·cp", kvlangBuiltinKvCp}, {"kv·cpdir", kvlangBuiltinKvCpTree}, {"kv·cplist", kvlangBuiltinKvCpList}, {"kv·list", kvlangBuiltinKvList}, {"kv·listlen", kvlangBuiltinKvListLen}, {"kv·listn", kvlangBuiltinKvListN}, {"kv·mkindex", kvlangBuiltinKvMkindex}, + {"kv·extindex", kvlangBuiltinKvExtIndex}, {"kv·rmindexext", kvlangBuiltinKvRmIndexExt}, {"kv·watch", kvlangBuiltinKvWatch}, {"kv·abs", kvlangBuiltinKvAbs}, {"vthread·create", kvlangBuiltinVthreadCreate}, {"vthread·run", kvlangBuiltinVthreadRun}, {"vthread·call", kvlangBuiltinVthreadCall}, diff --git a/runtime/src/builtin_collection.c b/runtime/src/builtin_collection.c index 007a4f47..1e5816ad 100644 --- a/runtime/src/builtin_collection.c +++ b/runtime/src/builtin_collection.c @@ -402,6 +402,78 @@ int kvlangBuiltinObj(kvlangFrame_t *f) { kvlangBuiltinNextPc(f); kvlangBuiltinFreeInputs(in, n); return 0; } +static char *dupn(const char *s, size_t n) { + char *r = malloc(n + 1); + memcpy(r, s, n); + r[n] = 0; + return r; +} + +/* 在 "name:kindexpr\n..." 声明串里查字段名,返回其类型(malloc)或 NULL(无此字段)。 */ +static char *struct_field_type(const char *decl, const char *fname) { + size_t fl = strlen(fname); + const char *p = decl; + while (*p) { + const char *nl = strchr(p, '\n'); + size_t linelen = nl ? (size_t)(nl - p) : strlen(p); + const char *colon = memchr(p, ':', linelen); + if (colon) { + size_t nlen = (size_t)(colon - p); + if (nlen == fl && memcmp(p, fname, fl) == 0) + return dupn(colon + 1, linelen - nlen - 1); + } + if (!nl) break; + p = nl + 1; + } + return NULL; +} + +/* struct·new:克隆 /lib/Name 原型子树到写槽,覆盖给定字段(校验字段存在性+类型)。 + * in[0]=structref("/lib/Name"),其后成对 (字段名, 值)。实例基值 kind=structref。 */ +int kvlangBuiltinStructNew(kvlangFrame_t *f) { + kvlangXvalue_t in[64]; int n = kvlangBuiltinReadInputs(f, in, 64); + if (n < 1) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: struct.new requires a type"); } + char *ref = kvlangXvalueValueString(&in[0]); + kvlangXvalue_t proto; kvlangXvalueZero(&proto); kvlangKvGetOne(f->kv, ref, &proto); + if (kvlangXvalueNone(&proto) || strcmp(kvlangXvalueKind(&proto), KVSPACE_KIND_STRUCT) != 0) { + int e = kvlangBuiltinSetErr(f, "TypeError: %s is not a struct type", ref); + kvlangXvalueFree(&proto); free(ref); kvlangBuiltinFreeInputs(in, n); return e; + } + kvspaceHead_t ph; kvlangXvalueHead(&proto, &ph); + int32_t dclen = 0; const uint8_t *dcl = kvlangXvalueBody(&proto, &ph, &dclen); + char *decl = dupn((const char *)dcl, (size_t)(dclen > 0 ? dclen : 0)); + kvlangXvalueFree(&proto); + + char err[256]; int rc = 0; + char *fr = kvlangKeytreeFrameRoot(f->pc); + for (int w = 0; w < f->inst->nw && rc == 0; w++) { + char *ok = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); + if (kvlangKvCpList(f->kv, ref, ok, err, sizeof err) != 0) { rc = kvlangBuiltinSetErr(f, "%s", err); free(ok); break; } + kvlangXvalue_t mark; kvlangXvalueNewTlv(&mark, ref, (const uint8_t *)"", 0, 1); + kvlangKvPair_t p0 = { ok, mark }; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); + for (int i = 1; i + 1 < n && rc == 0; i += 2) { + char *fname = kvlangXvalueValueString(&in[i]); + char *ftype = struct_field_type(decl, fname); + if (!ftype) { rc = kvlangBuiltinSetErr(f, "TypeError: struct %s has no field %s", ref, fname); free(fname); break; } + const char *vk = kvlangXvalueKind(&in[i + 1]); + kvspaceHead_t vh; kvlangXvalueHead(&in[i + 1], &vh); + kvlang_kindexpr_t vkx; kvlang_kindexpr_parse(vh.kindexpr, &vkx); + if (ftype[0] && !kvlang_rwirextKindexprMatch(ftype, vk, vkx.ndim, vkx.dims)) { + rc = kvlangBuiltinSetErr(f, "TypeError: field %s: expected %s, got %s", fname, ftype, vk[0] ? vk : "None"); + free(ftype); free(fname); break; + } + char *mk = kvlangKeytreeMember(ok, fname); + kvlangKvPair_t p = { mk, in[i + 1] }; kvlangKvSet(f->kv, &p, 1, err, sizeof err); + free(mk); free(ftype); free(fname); + } + free(ok); + } + free(fr); free(decl); free(ref); + kvlangBuiltinFreeInputs(in, n); + if (rc != 0) return rc; + kvlangBuiltinNextPc(f); return 0; +} + int kvlangBuiltinMap(kvlangFrame_t *f) { kvlangXvalue_t in[64]; int n = kvlangBuiltinReadInputs(f, in, 64); char *fr = kvlangKeytreeFrameRoot(f->pc); diff --git a/runtime/src/builtin_internal.h b/runtime/src/builtin_internal.h index cc452725..a6ed9996 100644 --- a/runtime/src/builtin_internal.h +++ b/runtime/src/builtin_internal.h @@ -20,7 +20,7 @@ int kvlangBuiltinArray(kvlangFrame_t *f), kvlangBuiltinNdarrayNumel(kvlangFrame_ kvlangBuiltinXvAt(kvlangFrame_t *f), kvlangBuiltinXvSet(kvlangFrame_t *f), kvlangBuiltinXvReshape(kvlangFrame_t *f), kvlangBuiltinXvReinterpret(kvlangFrame_t *f), kvlangBuiltinXvKindexpr(kvlangFrame_t *f), kvlangBuiltinXvBodylen(kvlangFrame_t *f), kvlangBuiltinScatter(kvlangFrame_t *f), kvlangBuiltinCompact(kvlangFrame_t *f), - kvlangBuiltinAppend(kvlangFrame_t *f), kvlangBuiltinSlice(kvlangFrame_t *f), kvlangBuiltinObj(kvlangFrame_t *f), kvlangBuiltinMap(kvlangFrame_t *f), kvlangBuiltinStringSet(kvlangFrame_t *f), + kvlangBuiltinAppend(kvlangFrame_t *f), kvlangBuiltinSlice(kvlangFrame_t *f), kvlangBuiltinObj(kvlangFrame_t *f), kvlangBuiltinMap(kvlangFrame_t *f), kvlangBuiltinStructNew(kvlangFrame_t *f), kvlangBuiltinStringSet(kvlangFrame_t *f), kvlangBuiltinStringChar(kvlangFrame_t *f), kvlangBuiltinStringOrd(kvlangFrame_t *f), kvlangBuiltinStringCmp(kvlangFrame_t *f), kvlangBuiltinStringFind(kvlangFrame_t *f), kvlangBuiltinStringLen(kvlangFrame_t *f), kvlangBuiltinStringSlice(kvlangFrame_t *f), kvlangBuiltinStringConcat(kvlangFrame_t *f), @@ -31,8 +31,8 @@ int kvlangBuiltinArray(kvlangFrame_t *f), kvlangBuiltinNdarrayNumel(kvlangFrame_ kvlangBuiltinDurArith(kvlangFrame_t *f), kvlangBuiltinDurCmp(kvlangFrame_t *f), kvlangBuiltinTimeCmp(kvlangFrame_t *f), kvlangBuiltinRandUint64(kvlangFrame_t *f), kvlangBuiltinRandInt63(kvlangFrame_t *f), kvlangBuiltinRandIntn(kvlangFrame_t *f), kvlangBuiltinKvGet(kvlangFrame_t *f), kvlangBuiltinKvSet(kvlangFrame_t *f), kvlangBuiltinKvDel(kvlangFrame_t *f), - kvlangBuiltinKvDelTree(kvlangFrame_t *f), kvlangBuiltinKvList(kvlangFrame_t *f), kvlangBuiltinKvListLen(kvlangFrame_t *f), kvlangBuiltinKvListN(kvlangFrame_t *f), kvlangBuiltinKvMkindex(kvlangFrame_t *f), - kvlangBuiltinKvExtIndex(kvlangFrame_t *f), kvlangBuiltinKvRmIndexExt(kvlangFrame_t *f), kvlangBuiltinKvWatch(kvlangFrame_t *f), + kvlangBuiltinKvDelTree(kvlangFrame_t *f), kvlangBuiltinKvCp(kvlangFrame_t *f), kvlangBuiltinKvCpTree(kvlangFrame_t *f), kvlangBuiltinKvCpList(kvlangFrame_t *f), kvlangBuiltinKvList(kvlangFrame_t *f), kvlangBuiltinKvListLen(kvlangFrame_t *f), kvlangBuiltinKvListN(kvlangFrame_t *f), kvlangBuiltinKvMkindex(kvlangFrame_t *f), + kvlangBuiltinKvExtIndex(kvlangFrame_t *f), kvlangBuiltinKvRmIndexExt(kvlangFrame_t *f), kvlangBuiltinKvWatch(kvlangFrame_t *f), kvlangBuiltinKvAbs(kvlangFrame_t *f), kvlangBuiltinDebugger(kvlangFrame_t *f), kvlangBuiltinVthreadCreate(kvlangFrame_t *f), kvlangBuiltinVthreadRun(kvlangFrame_t *f), kvlangBuiltinVthreadCall(kvlangFrame_t *f), kvlangBuiltinVthreadSleep(kvlangFrame_t *f), diff --git a/runtime/src/builtin_kv.c b/runtime/src/builtin_kv.c index 0072ef23..d3d06dff 100644 --- a/runtime/src/builtin_kv.c +++ b/runtime/src/builtin_kv.c @@ -21,10 +21,19 @@ static char *path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { return NULL; } +/* 容器 base:object/map/index/extindex 或 struct 实例(kind=structref,以 / 起头)。 + * 容器取其写槽路径拼成员 key;非容器把 base 值当 key 串。 */ +static bool base_is_container(const kvlangXvalue_t *base) { + if (kvlangXvalueNone(base)) return true; + const char *k = kvlangXvalueKind(base); + return k[0] == '/' || strcmp(k, KVSPACE_KIND_OBJ) == 0 || strcmp(k, KVSPACE_KIND_MAP) == 0 || + strcmp(k, KVSPACE_KIND_INDEX) == 0 || strcmp(k, KVSPACE_KIND_EXT_INDEX) == 0; +} + static char *member_path(kvlangFrame_t *f, const kvlangXvalue_t *in, int n) { const kvlangXvalue_t *base = &in[0]; char *fr = kvlangKeytreeFrameRoot(f->pc); - char *bp = kvlangXvalueNone(base) || (strcmp(kvlangXvalueKind(base), KVSPACE_KIND_OBJ) == 0 || strcmp(kvlangXvalueKind(base), KVSPACE_KIND_MAP) == 0 || strcmp(kvlangXvalueKind(base), KVSPACE_KIND_INDEX) == 0 || strcmp(kvlangXvalueKind(base), KVSPACE_KIND_EXT_INDEX) == 0) ? kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[0].name) : kvlangXvalueValueString(base); + char *bp = base_is_container(base) ? kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[0].name) : kvlangXvalueValueString(base); free(fr); /* 成员链:base 之后逐段拼 key(变参),每段可为静态字面量或动态键(运行时值)。 */ for (int i = 1; i < n; i++) { @@ -74,6 +83,43 @@ int kvlangBuiltinKvDel(kvlangFrame_t *f) { return kv_path_void(f, "kv.del", kvla int kvlangBuiltinKvDelTree(kvlangFrame_t *f) { return kv_path_void(f, "kv.deltree", kvlangKvDelTree); } +/* 绝对路径 / 路径字符串直取,否则裸标识符解析为本帧槽位 key(对齐 kv.list 的裸变量处理)。 */ +static char *resolve_path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { + char *p = path_arg(f, idx, in); + if (p) return p; + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *base = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[idx].name); + free(fr); + return base; +} + +static int kv_two_path_void(kvlangFrame_t *f, const char *name, + int (*op)(kvlangKv_t *, const char *, const char *, char *, uint32_t)) { + kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + char *src = n >= 1 ? resolve_path_arg(f, 0, in) : NULL; + char *dst = n >= 2 ? resolve_path_arg(f, 1, in) : NULL; + if (!src || !dst) { free(src); free(dst); kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: %s requires src,dst path args", name); } + char err[256]; int rc = op(f->kv, src, dst, err, sizeof err); + free(src); free(dst); kvlangBuiltinFreeInputs(in, n); + if (rc != 0) return kvlangBuiltinSetErr(f, "%s", err); + kvlangBuiltinNextPc(f); return 0; +} + +int kvlangBuiltinKvCp(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cp", kvlangKvCp); } + +int kvlangBuiltinKvCpTree(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cpdir", kvlangKvCpTree); } + +int kvlangBuiltinKvCpList(kvlangFrame_t *f) { return kv_two_path_void(f, "kv.cplist", kvlangKvCpList); } + +int kvlangBuiltinKvAbs(kvlangFrame_t *f) { + kvlangXvalue_t in[1]; int n = kvlangBuiltinReadInputs(f, in, 1); + char *p = n >= 1 ? resolve_path_arg(f, 0, in) : NULL; + if (!p) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.abs requires a key"); } + kvlangXvalue_t r; kvlangXvalueNewCharUtf32(&r, p); + int rc = kvlangBuiltinWriteResult(f, &r); kvlangXvalueFree(&r); + free(p); kvlangBuiltinFreeInputs(in, n); return rc; +} + int kvlangBuiltinKvList(kvlangFrame_t *f) { if (f->inst->nw == 0) return kvlangBuiltinSetErr(f, "TypeError: kv.list requires a write param"); kvlangXvalue_t in[1]; int n = kvlangBuiltinReadInputs(f, in, 1); @@ -184,7 +230,16 @@ int kvlangBuiltinKvListN(kvlangFrame_t *f) { return rc; } -int kvlangBuiltinKvMkindex(kvlangFrame_t *f) { return kv_path_void(f, "kv.mkindex", kvlangKvMkindex); } +int kvlangBuiltinKvMkindex(kvlangFrame_t *f) { + kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); + char *key = n >= 1 ? path_arg(f, 0, in) : NULL; + if (!key) { kvlangBuiltinFreeInputs(in, n); return kvlangBuiltinSetErr(f, "TypeError: kv.mkindex requires a path"); } + uint32_t capacity = n >= 2 ? (uint32_t)kvlangXvalueAsInt64(&in[1]) : 0; + char err[256]; int rc = kvlangKvMkindex(f->kv, key, capacity, err, sizeof err); + free(key); kvlangBuiltinFreeInputs(in, n); + if (rc != 0) return kvlangBuiltinSetErr(f, "%s", err); + kvlangBuiltinNextPc(f); return 0; +} int kvlangBuiltinKvExtIndex(kvlangFrame_t *f) { kvlangXvalue_t in[2]; int n = kvlangBuiltinReadInputs(f, in, 2); diff --git a/runtime/src/const.h b/runtime/src/const.h index 7ecbdc03..4e5d5ad7 100644 --- a/runtime/src/const.h +++ b/runtime/src/const.h @@ -27,9 +27,9 @@ #define KVSPACE_KIND_RWIR "rwir" #define KVSPACE_KIND_RWFUNC "rwfunc" #define KVSPACE_KIND_DEF_RWIR "defrwir" -#define KVSPACE_KIND_DEF_RWFUNC "defrwfunc" #define KVSPACE_KIND_RWIR_OR_RWFUNC "rwir|rwfunc" #define KVSPACE_KIND_SCOPE "scope" +#define KVSPACE_KIND_STRUCT "struct" #define KVSPACE_KIND_TIME "time" #define KVSPACE_KIND_DURATION "duration" diff --git a/runtime/src/kindexpr.c b/runtime/src/kindexpr.c index 7509a724..57e9e3ce 100644 --- a/runtime/src/kindexpr.c +++ b/runtime/src/kindexpr.c @@ -31,10 +31,31 @@ static bool known_kind(const char *s, size_t len) { kind_eq(s, len, KVSPACE_KIND_OBJ) || kind_eq(s, len, KVSPACE_KIND_MAP) || kind_eq(s, len, KVSPACE_KIND_INDEX) || kind_eq(s, len, KVSPACE_KIND_EXT_INDEX) || kind_eq(s, len, KVSPACE_KIND_RWIR) || kind_eq(s, len, KVSPACE_KIND_RWFUNC) || - kind_eq(s, len, KVSPACE_KIND_SCOPE) || kind_eq(s, len, KVSPACE_KIND_TIME) || + kind_eq(s, len, KVSPACE_KIND_SCOPE) || kind_eq(s, len, KVSPACE_KIND_STRUCT) || + kind_eq(s, len, KVSPACE_KIND_TIME) || kind_eq(s, len, KVSPACE_KIND_DURATION); } +/* structref = "/" path:指向 /lib 下 struct 定义节点的完整路径(实例 kind / 字段类型)。 + * 仅语法承认(`/` + 合法路径段),存在性/字段一致性留给 runtime 判定。 */ +static bool valid_structref(const char *s, size_t len) { + if (len < 2 || s[0] != '/') return false; + size_t seg = 0; + for (size_t i = 1; i < len; i++) { + if (s[i] == '/') { + if (seg == 0) return false; + seg = 0; + continue; + } + char c = s[i]; + if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '_')) + return false; + seg++; + } + return seg > 0; +} + /* base = any | kind(kind 为精确合法 kind 串) */ static bool valid_base(const char *s, size_t len) { if (len == 0) return false; @@ -63,9 +84,10 @@ static bool valid_dims(const char *s, size_t len) { return true; } -/* atom = [dims] base */ +/* atom = [dims] base | structref */ static bool valid_atom(const char *s, size_t len) { if (len == 0) return false; + if (s[0] == '/') return valid_structref(s, len); const char *p = s; if (*p == '[') { const char *end = memchr(p, ']', len); diff --git a/runtime/src/kv.c b/runtime/src/kv.c index 91122dd1..4c2e7ef9 100644 --- a/runtime/src/kv.c +++ b/runtime/src/kv.c @@ -64,7 +64,7 @@ int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, ui const uint8_t *body = v->data + h.body_offset; uint8_t *dst = NULL; if (kvspaceWriteInPlace(k->h, pairs[i].key, 1, body_len, &dst, err, err_cap) != 0) { - if (kvspaceWriteNewPlace(k->h, pairs[i].key, (const char *)h.kindexpr, body_len, &dst, err, err_cap) != 0) + if (kvspaceWriteNewPlace(k->h, pairs[i].key, h.xkind, (const char *)h.kindexpr, body_len, &dst, err, err_cap) != 0) return -1; } if (body_len > 0 && dst) memcpy(dst, body, body_len); @@ -81,8 +81,20 @@ int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_c return kvspaceDelTree(k->h, prefix, err, err_cap); } -int kvlangKvMkindex(kvlangKv_t *k, const char *path, char *err, uint32_t err_cap) { - return kvspaceMkindex(k->h, path, err, err_cap); +int kvlangKvCp(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap) { + return kvspaceCp(k->h, src, dst, err, err_cap); +} + +int kvlangKvCpTree(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap) { + return kvspaceCpTree(k->h, src, dst, err, err_cap); +} + +int kvlangKvCpList(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap) { + return kvspaceCpList(k->h, src, dst, err, err_cap); +} + +int kvlangKvMkindex(kvlangKv_t *k, const char *path, uint32_t capacity, char *err, uint32_t err_cap) { + return kvspaceMkindex(k->h, path, capacity, err, err_cap); } int kvlangKvExtIndex(kvlangKv_t *k, const char *path, const char *ext, char *err, uint32_t err_cap) { @@ -102,9 +114,9 @@ int kvlangKvList(kvlangKv_t *k, const char *prefix, bool expand_ext, bool resolv if (count <= 0) return 0; char **names = malloc(sizeof(char *) * (size_t)count); for (int32_t i = 0; i < count; i++) { - uint8_t *d = NULL; uint32_t len = 0; - if (kvspaceListAt(k->h, prefix, ex, rs, i, &d, &len) == 0 && d) - names[i] = strndup((const char *)d, len); + uint8_t buf[1024]; uint32_t len = 0; + if (kvspaceListAt(k->h, prefix, ex, rs, i, buf, sizeof buf, &len) == 0) + names[i] = strndup((const char *)buf, len); else names[i] = strdup(""); } diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index 4d9cf415..026190e3 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -241,7 +241,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) kvlangStrbufPrintf(&sk, "%s/[0,0]", cand); kvlangXvalue_t sv; kvlangXvalueZero(&sv); kvlangKvGetOne(kv, sk.p, &sv); - bool ok = !kvlangXvalueNone(&sv) && kvlangXvalueKindIs(&sv, KVSPACE_KIND_DEF_RWFUNC); + bool ok = !kvlangXvalueNone(&sv) && kvlangXvalueKindIs(&sv, KVSPACE_KIND_RWFUNC); kvlangXvalueFree(&sv); kvlangStrbufFree(&sk); if (ok) { free(pkg); pkg = cand_pkg; } else free(cand_pkg); @@ -261,7 +261,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) kvlangStrbufPrintf(&sig_key, "%s[0,0]", func_dir.p); kvlangXvalue_t sig; kvlangXvalueZero(&sig); kvlangKvGetOne(kv, sig_key.p, &sig); - if (kvlangXvalueNone(&sig) || !kvlangXvalueKindIs(&sig, KVSPACE_KIND_DEF_RWFUNC)) { + if (kvlangXvalueNone(&sig) || !kvlangXvalueKindIs(&sig, KVSPACE_KIND_RWFUNC)) { /* 按 xvalue 的 kind 精确区分缺 rwir 还是缺 rwfunc: * 到这里说明 opcode 已被 isothersrwir 判否(/lib/ 非 rwir)。 */ char *rk = kvlangKeytreeRwir(fn); @@ -298,7 +298,7 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) char err[256]; kvlangKvDelTree(kv, frame_root, err, sizeof err); char *stack_fr = kvlangKeytreeStack(frame_root); - kvlangKvMkindex(kv, stack_fr, err, sizeof err); + kvlangKvMkindex(kv, stack_fr, 0, err, sizeof err); kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err); /* 系统变量 */ @@ -421,7 +421,13 @@ static int handle_control(kvlangKv_t *kv, const char *vtid, const char *pc, kvla kvlangXvalueFree(&cond); return -1; } - bool taken = kvlangXvalueAsBool(&cond); + if (!kvlangXvalueKindIs(&cond, KVSPACE_KIND_BOOL)) { + char msg[128]; snprintf(msg, sizeof msg, "TypeError: branch condition must be bool, got %s", kvlangXvalueKind(&cond)); + kvlangVthreadSetError(kv, vtid, pc, msg); + kvlangXvalueFree(&cond); + return -1; + } + bool taken = kvlangXvalueAsInt64(&cond) != 0; kvlangXvalueFree(&cond); return jump_to(kv, vtid, pc, &inst->reads[taken ? 1 : 2], OP_BR); } @@ -488,7 +494,7 @@ char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, const char *funcnam kvlangStrbufPrintf(&sig_key, "%s[0,0]", func_dir.p); kvlangXvalue_t sig; kvlangXvalueZero(&sig); kvlangKvGetOne(kv, sig_key.p, &sig); - if (kvlangXvalueNone(&sig) || !kvlangXvalueKindIs(&sig, KVSPACE_KIND_DEF_RWFUNC)) { + if (kvlangXvalueNone(&sig) || !kvlangXvalueKindIs(&sig, KVSPACE_KIND_RWFUNC)) { char msg[256]; snprintf(msg, sizeof msg, "Bootstrap: rwir/rwfunc not found: %s", funcname); kvlangVthreadSetError(kv, vtid, "", msg); kvlangXvalueFree(&sig); kvlangStrbufFree(&sig_key); kvlangStrbufFree(&func_dir); @@ -502,7 +508,7 @@ char *kvlangKvcpuBootstrap(kvlangKv_t *kv, const char *vtid, const char *funcnam char *frame_root = kvlangKeytreeFrameAt(vtid, 1); char *stack_fr = kvlangKeytreeStack(frame_root); char err[256]; - kvlangKvMkindex(kv, stack_fr, err, sizeof err); + kvlangKvMkindex(kv, stack_fr, 0, err, sizeof err); kvlangKvExtIndex(kv, stack_fr, func_dir.p, err, sizeof err); char *ep = kvlangKeytreeEntryPc(frame_root); diff --git a/runtime/src/runtime.c b/runtime/src/runtime.c index ba2a2192..7c958b03 100644 --- a/runtime/src/runtime.c +++ b/runtime/src/runtime.c @@ -85,7 +85,7 @@ char *kvlangVthreadSpawn(kvlangKv_t *kv, const char *funcname, kvlangKeytreeVthread(vtid, &vtroot); char *stack_vt = kvlangKeytreeStack(vtroot.p); char e[256]; - kvlangKvMkindex(kv, stack_vt, e, sizeof e); + kvlangKvMkindex(kv, stack_vt, 0, e, sizeof e); char *first_pc = kvlangKvcpuBootstrap(kv, vtid, funcname, args, nargs); if (!first_pc) { free(stack_vt); diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 6ad89548..a83a7f15 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -11,13 +11,23 @@ /* ── kvspace-durable C ABI ─────────────────────────────────────────── */ typedef struct { - uint8_t kindexpr[256]; /* NUL 终止(含 ref 前缀与 [dims],去 padding) */ + uint8_t xkind; /* 五分类:0=None 1=Ptr 2=ExtValue 3=DefKindexpr 4=RealValue */ + uint8_t kindexpr[256]; /* NUL 终止(含 [dims]、无前缀,去 padding) */ + int32_t kind_off; /* base 种类在 kindexpr 内的起始字节偏移 */ + int32_t ndim; /* 维数(标量=0) */ + int32_t dims[8]; /* 各维长度(X_MAX_NDIM=8) */ uint8_t ro; /* 1=只读,0=可写 */ uint32_t vid; /* vthread id */ int32_t body_len; /* body 字节数 */ int32_t body_offset; /* body 在 data 内的起始偏移(= head_len) */ } kvspaceHead_t; +#define KVSPACE_XKIND_NONE 0 +#define KVSPACE_XKIND_PTR 1 +#define KVSPACE_XKIND_EXTVALUE 2 +#define KVSPACE_XKIND_DEFKINDEXPR 3 +#define KVSPACE_XKIND_REALVALUE 4 + extern void *kvspaceConnect(const char *dsn); extern void kvspaceClose(void *h); /* 借用读:*out 指向后端常驻/回收空间,调用方不得 free。resolve=1 穿透 link。 */ @@ -25,16 +35,19 @@ extern int kvspaceGet(void *h, const char *key, int resolve, uint8_t **out, ui /* 就地写:key 已存在、body_len==原 body_len → 返回原 box body 偏移指针;否则非 0 + err。 */ extern int kvspaceWriteInPlace(void *h, const char *key, int resolve, uint32_t body_len, uint8_t **body, char *err, uint32_t err_cap); -/* 新位置写:按 (kindexpr, body_len) 分配新 box、写 head,返回 body 偏移指针。 */ -extern int kvspaceWriteNewPlace(void *h, const char *key, const char *kindexpr, uint32_t body_len, - uint8_t **body, char *err, uint32_t err_cap); +/* 新位置写:按 (xkind, kindexpr, body_len) 分配新 box、写 head,返回 body 偏移指针。 */ +extern int kvspaceWriteNewPlace(void *h, const char *key, uint8_t xkind, const char *kindexpr, + uint32_t body_len, uint8_t **body, char *err, uint32_t err_cap); /* 前缀遍历:listlen 定计数,逐 idx 取名(借用回收缓冲,不得 free),不一次性返回整段名单。 */ extern int kvspaceListLen(void *h, const char *prefix, int expand_ext, int resolve, int32_t *out_count); extern int kvspaceListAt(void *h, const char *prefix, int expand_ext, int resolve, int32_t idx, - uint8_t **out, uint32_t *out_len); + uint8_t *buf, uint32_t buf_cap, uint32_t *out_len); extern int kvspaceDel(void *h, const char *const *keys, uint32_t nkeys, char *err, uint32_t err_cap); extern int kvspaceDelTree(void *h, const char *prefix, char *err, uint32_t err_cap); -extern int kvspaceMkindex(void *h, const char *path, char *err, uint32_t err_cap); +extern int kvspaceCp(void *h, const char *src, const char *dst, char *err, uint32_t err_cap); +extern int kvspaceCpTree(void *h, const char *src, const char *dst, char *err, uint32_t err_cap); +extern int kvspaceCpList(void *h, const char *src, const char *dst, char *err, uint32_t err_cap); +extern int kvspaceMkindex(void *h, const char *path, uint32_t capacity, char *err, uint32_t err_cap); extern int kvspaceMkindexExt(void *h, const char *path, const char *ext_path, char *err, uint32_t err_cap); extern int kvspaceRmindexExt(void *h, const char *path, char *err, uint32_t err_cap); extern int kvspaceWatch(void *h, const char *key, const uint8_t *target, uint32_t target_len, @@ -60,7 +73,6 @@ extern int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len); typedef struct { const char *kind; /* base kind(kindexpr 子串,非 NUL 终止) */ int32_t kind_len; - int32_t ref; /* 0=内联 1=指针 2=扩展句柄 */ int32_t ndim; int32_t dims[X_MAX_NDIM]; int32_t array_len; @@ -113,7 +125,6 @@ bool kvlang_rwirextKindexprVariadic(const char *expr); int64_t kvlangXvalueAsInt64(const kvlangXvalue_t *v); double kvlangXvalueAsFloat64(const kvlangXvalue_t *v); uint64_t kvlangXvalueAsUint64(const kvlangXvalue_t *v); -bool kvlangXvalueAsBool(const kvlangXvalue_t *v); uint32_t kvlangXvalueChar32At(const kvlangXvalue_t *v, int32_t idx); int32_t kvlangXvalueElemSize(const char *kind); @@ -140,7 +151,10 @@ int kvlangKvGetMember(kvlangKv_t *k, const char *dir, const char *name, kvlangXv int kvlangKvSet(kvlangKv_t *k, const kvlangKvPair_t *pairs, int n, char *err, uint32_t err_cap); int kvlangKvDel(kvlangKv_t *k, const char *key, char *err, uint32_t err_cap); int kvlangKvDelTree(kvlangKv_t *k, const char *prefix, char *err, uint32_t err_cap); -int kvlangKvMkindex(kvlangKv_t *k, const char *path, char *err, uint32_t err_cap); +int kvlangKvCp(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); +int kvlangKvCpTree(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); +int kvlangKvCpList(kvlangKv_t *k, const char *src, const char *dst, char *err, uint32_t err_cap); +int kvlangKvMkindex(kvlangKv_t *k, const char *path, uint32_t capacity, char *err, uint32_t err_cap); int kvlangKvExtIndex(kvlangKv_t *k, const char *path, const char *ext, char *err, uint32_t err_cap); int kvlangKvDelExtIndex(kvlangKv_t *k, const char *path, char *err, uint32_t err_cap); int kvlangKvList(kvlangKv_t *k, const char *prefix, bool expand_ext, bool resolve, diff --git a/runtime/src/vthread.c b/runtime/src/vthread.c index 52c708f8..c24bf976 100644 --- a/runtime/src/vthread.c +++ b/runtime/src/vthread.c @@ -53,7 +53,7 @@ void kvlangVthreadSetError(kvlangKv_t *kv, const char *vtid, const char *pc, con kvlangStrbuf_t dir; kvlangStrbufInit(&dir); kvlangStrbufPutn(&dir, msg_path.p, (size_t)(sep - msg_path.p) + 1); char err[256]; - kvlangKvMkindex(kv, dir.p, err, sizeof err); + kvlangKvMkindex(kv, dir.p, 0, err, sizeof err); kvlangStrbufFree(&dir); } diff --git a/runtime/src/xvalue.c b/runtime/src/xvalue.c index aea7687d..97287f34 100644 --- a/runtime/src/xvalue.c +++ b/runtime/src/xvalue.c @@ -16,13 +16,11 @@ void kvlangXvalueSetBytes(kvlangXvalue_t *v, uint8_t *data, uint32_t len) { v->data = data; v->len = len; } -/* 解析 kindexpr 内容 → (ref, dims, base kind)。kindexpr 为 NUL 终止串。 */ +/* 解析 kindexpr 内容 → (dims, base kind)。kindexpr 为 NUL 终止串、无前缀(ref 归 head.xkind)。 */ void kvlang_kindexpr_parse(const uint8_t *kx, kvlang_kindexpr_t *out) { memset(out, 0, sizeof(*out)); if (!kx) return; int32_t i = 0; - if (kx[0] == '*') { out->ref = 1; i = 1; } - else if (kx[0] == '@') { out->ref = 2; i = 1; } if (kx[i] == '[') { i++; while (kx[i] != ']' && kx[i] != 0 && out->ndim < X_MAX_NDIM) { @@ -107,8 +105,7 @@ bool kvlangXvalueIsPtr(const kvlangXvalue_t *v) { if (kvlangXvalueNone(v)) return false; kvspaceHead_t h; if (kvlangXvalueHead(v, &h) < 0) return false; - kvlang_kindexpr_t kx; kvlang_kindexpr_parse(h.kindexpr, &kx); - return kx.ref == 1; + return h.xkind == KVSPACE_XKIND_PTR; } int32_t kvlangXvalueArrayLen(const kvlangXvalue_t *v) { @@ -204,12 +201,6 @@ uint64_t kvlangXvalueAsUint64(const kvlangXvalue_t *v) { return (uint64_t)kvlangXvalueAsInt64(v); } -bool kvlangXvalueAsBool(const kvlangXvalue_t *v) { - if (kvlangXvalueNone(v)) return false; - kvspaceHead_t h; const uint8_t *b = v_body(v, &h); - return b && h.body_len > 0 && b[0] != 0; -} - uint32_t kvlangXvalueChar32At(const kvlangXvalue_t *v, int32_t idx) { kvspaceHead_t h; const uint8_t *b = v_body(v, &h); if (!b) return 0; @@ -275,9 +266,8 @@ char *kvlangXvalueValueString(const kvlangXvalue_t *v) { if (!body) return strdup(KVSPACE_KIND_NONE); int32_t blen = h.body_len; const char *k = kvlangXvalueKind(v); - kvlang_kindexpr_t kx; kvlang_kindexpr_parse(h.kindexpr, &kx); - if (kx.ref == 1) { + if (h.xkind == KVSPACE_XKIND_PTR) { kvlangStrbuf_t b; kvlangStrbufInit(&b); kvlangStrbufPutn(&b, "\xE2\x86\x92", 3); kvlangStrbufPutn(&b, (const char *)body, (size_t)blen); @@ -311,9 +301,9 @@ char *kvlangXvalueValueString(const kvlangXvalue_t *v) { if (strcmp(k, KVSPACE_KIND_MAP) == 0) { kvlangStrbuf_t b; kvlangStrbufInit(&b); kvlangStrbufPuts(&b, "map["); - for (int d = 0; d < kx.ndim; d++) { + for (int d = 0; d < h.ndim; d++) { if (d) kvlangStrbufPutc(&b, ','); - kvlangStrbufPrintf(&b, "%d", kx.dims[d]); + kvlangStrbufPrintf(&b, "%d", h.dims[d]); } kvlangStrbufPutc(&b, ']'); return kvlangStrbufDetach(&b); diff --git a/site/src/highlight.ts b/site/src/highlight.ts index a5199e66..4a92b7bd 100644 --- a/site/src/highlight.ts +++ b/site/src/highlight.ts @@ -6,7 +6,6 @@ const KEYWORDS = new Set([ "rwfunc", "rwir", "defrwir", - "defrwfunc", "while", "if", "else", diff --git a/stdlib/kvlang/deepdive/01-storage-compute-separation-kv-tree.kv b/stdlib/kvlang/deepdive/01-storage-compute-separation-kv-tree.kv deleted file mode 100644 index a4f755e6..00000000 --- a/stdlib/kvlang/deepdive/01-storage-compute-separation-kv-tree.kv +++ /dev/null @@ -1,68 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/01_storage_compute_separation_kv_tree { - r#"# 存、算、控制流严格分离的 kv 树计算架构 - - -## 存、算、控制流严格分离 - -kvlang 架构的顶层公理:**存储、计算、控制流三者严格分离,各司其职。** - -| 层 | 职责 | 载体 | 说明 | -|----|------|------|------| -| **存储** | 持有所有代码、数据、执行状态 | kvspace 树形路径 | 元存:kvspace 核心能力,管理 slot/索引/系统变量/指令树/帧栈 | -| **计算** | 执行算子、张量运算 | op-plat(GPU/CPU 算子) | 不持有状态,只消费存储层的数据、产出写回存储层 | -| **控制流** | 调度、跳转、调用/返回 | kvcpu(PC 路径导航) | 不产生数据,只改变 PC 在存储树中的位置 | - -与传统架构的对比: - -| | 传统 VM | kvlang | -|--|---------|--------| -| 存储 | 栈+堆+全局段,混在内存中 | **kvspace 树**,统一的路径地址空间 | -| 计算 | 指令内联(ALU op + 寻址) | **rwir `<-`/`->`**:显式读参写参,计算与寻址解耦 | -| 控制流 | PC 整数偏移,与数据同空间 | **PC = KV 路径字符串**,控制流即树导航 | - -分离的成果:崩溃恢复只需 PC 路径(已存 KV);算子可独立替换;调试只需 `kvspace tree` 静态观察存储树,无需 attach 进程。 - -### 存储再分:元存与扩展存储 - -存储层内部进一步分为两层: - -| 存储层 | 存放内容 | 管理方 | 可见性 | -|--------|---------|--------|--------| -| **元存** | slot、索引、系统变量、指令树、帧栈、lib 签名 | **kvspace**(Redis/内存 KV) | **全局公有**:任意 kvspace 连接者均可读写 | -| **扩展存储** | tensor data(权重、激活值、梯度) | heap-plat / op-plat | **连接者私有**:仅直连该存储的节点可访问 | - -二者关系: - -``` -kvspace 元存(slot → XValue) - ├── 基础类型:int/float/bool/string/bytes → XValue.raw 自包含 - └── tensor 类型:XValue 只存元数据(shape + dtype + 扩展存储句柄) - ↓ 句柄指向 - 扩展存储(tensor data 本体,不在 kvspace 内) -``` - -**元存全局公有**:任意节点直连 kvspace 即可读写任意 slot,所有 worker 共享同一份代码(`/lib/`)与执行状态(`/vthread/`),无需独立调度器元数据中心。 - -**扩展存储连接者私有**:GPU 显存、本地 shm 仅直连进程可访问,跨卡 tensor 搬运由 op-plat 显式管理(AllReduce/all-to-all 等)。 - -**铁律**:kvspace 只存 tensor 元数据与扩展存储句柄,不存 tensor 原始数据。 - -### XValue 整存整取铁律 - -**XValue 是 kvspace 存储的原子单位,只能整存整取——禁止对单个 XValue 的 raw 做部分读写。** - -- **禁止部分读**:不得按字节偏移读 XValue.raw 的片段。 -- **禁止部分写**:不得按字节偏移写 XValue.raw 的片段。 - -数组元素访问因此只有两条路,二者都保持整存整取: - -| 存储形态 | head kind | 元素访问 | 是否整存整取 | -|---------|-----------|---------|-------------| -| compact 形态 | 元素类型(`int32`,`[10]int32`) | 整读单个 XValue,内存内下标 | 是(一次整读) | -| stringkeymap 形态(散 key) | `stringkeymap`(`{1,2,3}`) | 每元素是独立子 key `base·i` 的标量 XValue | 是(每个 key 一个整 XValue) | - -理由:原子单位从"raw 字节区间"上移到"XValue",使每次读写均为单 key 的原子替换,消除并发 RMW 竞态,与"元存全局公有"定位一致。 - -"# -> /lib/kvlang/deepdive/01_storage_compute_separation_kv_tree -} diff --git a/stdlib/kvlang/deepdive/02-everything-is-plaintext.kv b/stdlib/kvlang/deepdive/02-everything-is-plaintext.kv deleted file mode 100644 index a561a175..00000000 --- a/stdlib/kvlang/deepdive/02-everything-is-plaintext.kv +++ /dev/null @@ -1,42 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/02_everything_is_plaintext { - r#"# 一切皆明文 - -## 一切皆明文 - -传统 VM:PC 是整数、指令是字节码、指针是内存地址、类型在编译期丢失、调试需要反汇编器。kvlang 相反:**一切关键信息以明文字符串存于 kvspace 树**,`kvspace tree`/`kvspace get` 即完整系统自省。 - -目标:让程序对人与 agent 彻底透明——人用 `kvspace tree` 直读,agent 用 `kv.Get` 程序读,同一份数据,零中间层。 - -### 路径即 PC、指针、地址 - -``` -传统:PC = 0x7fff5fbff830 指针 = 0x7fff9b3c 变量 = rbp-8 -kvlang:PC = "/vthread/run/[0,0]/[1,0]/[0,0]" 指针 = "/n0" 变量 = 帧根 + "/x" -``` - -PC 字符串即调用栈(路径深度 = 栈深度)。裸名即相对指针,`/` 开头即绝对指针。不存在 `&` 取址——变量名本身就是该变量的 kvspace 路径。`"/n0" -> p; p.next` 的值是路径字符串,`kvspace get` 直读。 - -### 指令与调用:字符串 opcode,全路径调用 - -``` -传统:B8 01 00 00 00 (mov eax, 1) 调用 = call 0x400200 (符号表间接) -kvlang:[0,0]="+" [0,-1]="A" [0,1]="C" 调用 = /lib/math·sum(3, 4) -> s -``` - -opcode 是 `"+"` `"print"` `"call"` 字符串,`kvspace tree /lib/func` 即反汇编。lib 树即全局命名空间——调用即全路径,无 import/符号表/链接器重定位。 - -### 类型与状态:kind 自描述,`/.` 暴露一切 - -``` -传统:运行时值 = 裸字节 系统状态 = 需 ptrace -kvlang:XValue 带 kind "int64" /vthread/7/.pc .status .rootfunc 直接 Get -``` - -`kvspace get /x` 返回 `int64:42`——类型跟值一起存。VM 内部状态以 `X/.var` 影子键暴露:`.pc` `.status` `.rootfunc` `.rparam` `.wparam`。源码存 `/lib/.*.src` 与指令树同目录。 - -### 代价与补偿 - -明文化贯彻到 kvlang 的方方面面——PC 是字符串而非整数、opcode 是 `"+"` 而非单字节、指针是路径而非地址——这天然损失了解释执行的速度。但 kvlang 的定位是**调度层**,最耗时的 tensor 计算不在 kvlang 内执行:op-gpu 将计算编译为 GPU kernel(TileLang → AOT `.so` → dlopen),通过 GPU 硬件加速弥补。透明性在调度层兑现,算力在扩展引擎层找回。 -"# -> /lib/kvlang/deepdive/02_everything_is_plaintext -} diff --git a/stdlib/kvlang/deepdive/03-program-as-datastructure-func-data.kv b/stdlib/kvlang/deepdive/03-program-as-datastructure-func-data.kv deleted file mode 100644 index 247cae6c..00000000 --- a/stdlib/kvlang/deepdive/03-program-as-datastructure-func-data.kv +++ /dev/null @@ -1,13 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/03_program_as_datastructure_func_data { - r#"# 程序 = 数据结构 + 函数 + 数据 - -## 程序 = 数据结构 + 函数 + 数据 - -Niklaus Wirth 的经典公式「程序 = 数据结构 + 算法」将程序员的精力引向"先设计数据结构,再编写算法"。 - -kvlang 的主张:**程序 = 数据结构 + 函数 + 数据**。数据结构不再由用户自定义——kvlang 的全部数据结构都是 builtin:`struct`(键族前缀),compact 形态数组(`[N]T`,元素连续打包进单个 XValue),stringkeymap 形态数组(`{...}`,元素散落 `base·i` 独立键),链表(`/n0·val`、`/n0·next` 平坦键,路径字符串作指针)。使用者只需声明 `lib`、编写 `rwfunc`,数据自然落地到 `/lib/` 与 `/vthread/` 及用户自定义路径。 - -键族是 kvlang 唯一的数据结构机制,上层全部收敛为函数(rwfunc/rwir)与数据(kvspace slot)的组合。以 kvspace 树形路径为统一地址空间,同一语法同时承担 VM 指令、高级语言、编译器 IR 三种职能。 -"# -> /lib/kvlang/deepdive/03_program_as_datastructure_func_data -} diff --git a/stdlib/kvlang/deepdive/04-four-level-code-hierarchy.kv b/stdlib/kvlang/deepdive/04-four-level-code-hierarchy.kv deleted file mode 100644 index e01ff5f9..00000000 --- a/stdlib/kvlang/deepdive/04-four-level-code-hierarchy.kv +++ /dev/null @@ -1,52 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/04_four_level_code_hierarchy { - r"# 代码四级层次 - -kvlang 中所有代码组织为四个层级,从宏观到微观,每层在 kvspace 树中有对应路径。 - -## 四级定义 - -| 层 | 关键字 | 职责 | 拥有 | -|----|--------|------|------| -| **lib** | `lib name { }` | 包组织,管理命名空间下的所有 rwfunc | rwfunc 集合 | -| **rwfunc** | `rwfunc name(...) -> (...) { }` | 函数级,拥有自己的 rwir 指令序列和变量 | rwir 序列 + scope 树 | -| **scope** | (lower 产物,不可手写) | 域级,if/while/for 降级产生,拥有自己的指令序列和变量,可嵌套 | rwir 序列 + 子 scope | -| **rwir** | `rwir name(...) -> (...)` | 指令级,执行由解释器直执行或委托后端引擎 | 无——原子不可分 | - -``` -/lib/leetcode/ ← lib: 顶层包 -└── /lib/leetcode/search/ ← lib: 嵌套子包 - ├── [rwfunc] binary(target) ← rwfunc: 函数签名 - ├── [0,0]: lo <- 0 ← rwir: 原子指令 - ├── [1,0]: hi <- 100 - ├── _while_1/ ← scope: while 循环域 - │ ├── cond/ - │ │ └── [0,0]: found == 0 -> _c - │ ├── body/ - │ │ ├── [0,0]: lo + hi -> s - │ │ └── _if_1/ ← scope: 嵌套 if 域 - │ │ ├── then/ - │ │ └── else/ - │ └── exit/ - └── _exit_1/ -``` - -## 层次约束 - -- **lib** 可嵌套子 lib(`lib a { lib b { } }`),叶子 lib 包含 rwfunc。不可直接含 rwir 或 scope。对应 `/lib//` 路径,嵌套时路径级联 `/lib/a/b/`。 -- **rwfunc** 只包含 rwir 和 scope——不可含其他 rwfunc。函数调用不嵌入 body,走 `call`/`return` 跨帧机制。rwfunc 对应 `/lib///`,运行时实例化为 `/vthread//[N,0]/` 帧。 -- **scope** 只包含 rwir 和子 scope。语义同 rwfunc 但无独立调用栈——scope 的变量和指令序列平铺在所属 rwfunc 帧内。scope 由 lower 阶段从 if/while/for 产生,不可手写。 -- **rwir** 是最小执行单元,不可再分,由 C runtime 解释器或后端引擎原子执行。rwir 是**空函数体**(仅签名,无指令体),扩展 rwir 的签名与 rwfunc 统一存于 `/lib/`(`` 可带包前缀),运行时签名可供 agent 自省;native rwir 签名内联在 C runtime 注册表中、不落盘。 - -## 与 KV 树的映射 - -每一层在 kvspace 树中都有对应路径,`kvspace tree` 即完整自省: - -| 层 | KV Kind | `/lib/` 路径 | `/vthread/` 路径 | -|----|---------|-------------|-----------------| -| lib | — | `/lib//` | — | -| rwfunc | `rwfunc` | `/lib///` | `/vthread//[N,0]/` | -| scope | `scope` | `/lib///_while_N/` | `/vthread//[N,0]/_while_N/` | -| rwir | `rwir` | `/lib/`(扩展);native 内联 C 表不落盘 | — | -" -> /lib/kvlang/deepdive/04_four_level_code_hierarchy -} diff --git a/stdlib/kvlang/deepdive/grammar/kvlang-bnf-grammar.kv b/stdlib/kvlang/deepdive/grammar/kvlang-bnf-grammar.kv deleted file mode 100644 index 893ce04c..00000000 --- a/stdlib/kvlang/deepdive/grammar/kvlang-bnf-grammar.kv +++ /dev/null @@ -1,189 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/grammar/kvlang_bnf_grammar { - r##"# kvlang 语法 (BNF) - -> 锚定于 kvlang-deep-dive(根设计文档)——每次 deep-dive 更新后需验证本文与实现一致。 - -## 词法 - -``` -digit = "0" … "9" -letter = "a" … "z" | "A" … "Z" | "_" -ident = letter { letter | digit } -integer = digit { digit } -float = integer [ "." integer ] [ ("e" | "E") [ "+" | "-" ] integer ] -path = "/" ident { ("/" | ".") ident | "·" "[" integer { "," integer } "]" } - (* 绝对路径字面量;/ 分隔段,. 为子键,· [i,j] 为 strkeymap 坐标段 *) - -string = '"' { char | escape } '"' - | "r" { "#" } '"' { char } '"' { "#" } - | "'" { char } "'" - (* 双引号:\ 转义(\n \t \" \\ …),可直接跨行。 - r"…" 原始串(零转义);开头 N 个 # 时须以 " 后接同样 N 个 # 闭合, - 故 r#"…"# 内容可含 ",# 越多可嵌套越深。单引号为 char/keypath。 - 无三引号、无反引号原始串——完全对齐 Rust。 *) -comment = "//" { char } newline (* 行注释 *) - | "/*" { char | comment } "*/" (* 块注释,可嵌套;无 # 注释 *) -``` - -## 程序结构 - -``` - (* 无 import——lib 树即全局命名空间,跨 lib 调用走全路径 /lib/{pkg}·{func}() *) - -file = { lib_block | rwir_decl | rwfunc_decl | instruction } - -lib_block = "lib" ident "{" { lib_item } "}" -lib_item = lib_block | rwir_decl | rwfunc_decl | instruction - (* lib 内的裸 instruction 收集为隐式 init 函数,无 init 关键字 *) - -rwfunc_decl = "rwfunc" ident "(" [ param_list ] ")" - [ "->" ( "(" [ param_list ] ")" | param_list ) ] - "{" { statement } "}" - -rwir_decl = "rwir" ident "(" [ param_list ] ")" [ "->" "(" [ param_list ] ")" ] - (* 仅签名,无函数体;绑定至扩展执行器 *) - -param_list = param { "," param } -param = ident ":" type_expr (* 类型必须标注;末参的 type_expr 可加 "..." 变参 *) - -statement = instruction | if_stmt | while_stmt | for_stmt | block_stmt - | break_stmt | continue_stmt | return_stmt -``` - -## 类型表达式(签名参数/返回值类型,多态 + 高维数组) - -> 完整语义见 [[runtime篇-07-签名类型表达式]]。铁律:无家族简写(`int`/`uint`/`float`/`num`/`char`)——位宽是开放集合(int4/fp8/fp16…),编码须写明确;多态靠显式 `|` 枚举。 - -``` -type_expr = atom { "|" atom } (* 并集:A|B 表 A 或 B *) -atom = shape | mapexpr -shape = [ dims ] ( any | kind ) -mapexpr = key "·" type_expr (* stringkeymap;value 可递归嵌套 *) -key = "[]" char_enc (* 字符串键,等价 Go map[string]V *) - | "[" scalar { "," scalar } "]" (* 标量元组键,物理以字符串格式落 key *) -char_enc = "char/utf8" | "char/utf32" | "char/ascii" -scalar = "bool" - | "int8" | "int16" | "int32" | "int64" - | "uint8" | "uint16" | "uint32" | "uint64" - | "float32" | "float64" -dims = "[]" (* 1 维,长度任意,等价 [?] *) - | "[" dim { "," dim } "]" (* 逗号分割的维;rank = 项数 *) -dim = integer | "?" (* 精确大小或动态维 *) -any = "any" (* 通配,匹配任意 kind *) -kind = "bool" - | "int8" | "int16" | "int32" | "int64" - | "uint8" | "uint16" | "uint32" | "uint64" - | "float32" | "float64" - | "char/utf8" | "char/utf32" | "char/ascii" - | "object" | "stringkeymap" - | "index" | "extindex" - | "rwir" | "rwfunc" | "scope" | "time" | "duration" -variadic = type_expr "..." (* 仅末参:0..N 个同型实参,每个按 type_expr 匹配 *) -``` - -## 指令 - -``` -instruction = [ writes "=" ] expr - | [ writes "<-" ] expr - | expr [ "->" writes ] - | expr (* 纯副作用调用 *) - -writes = write_slot { "," write_slot } -write_slot = ident (* 裸名:帧内变量 *) - | path (* 绝对路径 *) - | ident "·" ident (* 成员写:base·field *) - | ident "·" "*" ident (* 动态键写:base·*key *) - | ident "[" expr "]" (* 下标写:arr[idx] *) -``` - -## 表达式 - -``` -expr = primary { infix_primary } - | prefix_unary primary - -primary = literal | ident | path - | primary "·" ident (* 成员读:静态键 *) - | primary "·" "*" ident (* 成员读:动态键 *) - | primary "·" "[" expr { "," expr } "]" (* strkeymap 坐标访问 *) - | primary "[" expr { "," expr } "]" (* compact 下标读 *) - | "(" expr ")" (* 分组 *) - | ident "(" [ arg_list ] ")" (* 函数调用 *) - | ident "·" ident { "·" ident } "(" [ arg_list ] ")" (* 点号限定调用 *) - | array_literal - | object_literal - | map_literal - -prefix_unary = "-" | "!" - -infix_primary = binary_op expr - -binary_op = "+" | "-" | "×" | "÷" | "%" | "==" | "!=" | "<" | ">" | "<=" | ">=" - | "&&" | "||" | "&" | "|" | "^" | "<<" | ">>" - (* 乘 = ×(U+00D7),除 = ÷(U+00F7);* 保留、/ 仅路径,均非算术符 *) - -arg_list = expr { "," expr } - -literal = integer | float | string | "None" | "true" | "false" - (* 空值字面量是 None,非 null *) - -array_literal = "[" [ expr { "," expr } ] "]" - (* compact 形态数组:元素连续打包进单个 XValue,head kind=元素类型; - 须定长同类型,元素不可为字符串字面量(变长)*) - -object_literal = "{" [ ident "=" expr { (";" | newline) ident "=" expr } ] "}" - (* 对象/映射字面量:{k1=v1; k2=v2},key 为静态标识符,落 object 或 stringkeymap(map) *) - -map_literal = "{" [ expr { (";" | newline | ",") expr } ] "}" - (* stringkeymap 形态数组(散 key):{v0, v1, ...},head kind=stringkeymap, - 每元素落 base·i 独立 key;字符串数组等变长集合用此形式 *) -``` - -## 控制流(仅限 rwfunc 体内) - -``` -if_stmt = "if" "(" expr ")" "{" { statement } "}" - { "else" "if" "(" expr ")" "{" { statement } "}" } - [ "else" "{" { statement } "}" ] - -while_stmt = "while" "(" expr ")" "{" { statement } "}" - -for_stmt = "for" "(" ident "in" expr ")" "{" { statement } "}" - (* iter 可为变量、路径或任意表达式(包括散 key 字面量) *) - -block_stmt = ident ":" "{" { statement } "}" (* 具名作用域 *) - -break_stmt = "break" -continue_stmt = "continue" -return_stmt = "return" (* 无返回值;输出通过写参传递 *) -``` - -## 内建函数(部分) - -``` -builtin_call = ("print" | "println" - | "abs" | "neg" | "sign" | "pow" | "sqrt" | "exp" | "log" - | "min" | "max" - | "bool" | "int8" | "int16" | "int32" | "int64" - | "uint8" | "uint16" | "uint32" | "uint64" - | "float32" | "float64" - | "char/utf8" | "char/utf32" | "char/ascii" - | "array" | "obj" | "map" - | "debugger") "(" [ arg_list ] ")" - -(* 裸名内建仅上表;string·len/char/slice/find、xv·at/set、kv·get/set/del、 - ndarray·numel/dim/shape、array·append/slice、vthread·*、time·* 等一律走 - primary 的 · 限定调用(base·method(...)),不是裸名终结符 *) -(* 类型构造器兼转换:int8(x)/float32(x)/char/utf8(x)/bool(x) ... *) -(* min/max 支持变参: min(a,b,c,...) *) -(* print : Go 风格——无空格拼接,不追加换行 *) -(* println: Go 风格——空格分隔,追加换行 *) -(* debugger(): 内联暂停, 非调试模式 no-op *) -(* ÷ : 两侧 int → 整除(C 风格); 任一侧 float → 浮除;/ 仅用于路径 *) -(* s[i] 读返单字符字符串, 越界返 ""; s[i]="X" 单字符替换写回新串 *) -(* + 作用在 string∧string → 拼接 *) -``` -"## -> /lib/kvlang/deepdive/grammar/kvlang_bnf_grammar -} diff --git a/stdlib/kvlang/deepdive/kvspace/00-design-and-implementation.kv b/stdlib/kvlang/deepdive/kvspace/00-design-and-implementation.kv deleted file mode 100644 index df9a5936..00000000 --- a/stdlib/kvlang/deepdive/kvspace/00-design-and-implementation.kv +++ /dev/null @@ -1,63 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/kvspace/00_design_and_implementation { - r#"# KVSpace 基础模型 - -## 设计美学 - -代码必须按唯一正确方式运行;遇非预期输入直接 fatal/panic,不用 if 包庇错误,不容许以奇怪方式成功运行。走唯一主干,砍一切分支。 - -## 核心概念:KV 树 - -KVSpace 是文件系统风格的 KV 存储抽象,`/` 管层级,`·` 管成员: - -- **文件 key**:普通值 key,例如 `/a/b` → `int64:42` -- **目录 key**:以 `/` 结尾,value 为 index(子名列表),例如 `/a/` → `index: ["b"]` -- 同一路径下 `/a` 与 `/a/` 可独立共存(文件与目录同名) - -分隔符:`/` = 层级;`·`(U+00B7)= 用户成员;`‥`(U+2025)= 运行时系统变量前缀(见系统变量篇)。 - -## XValue 类型系统 - -XValue = XValueHead + body: - -``` -XValueHead = [1B kind_len][N B kind][1B ref][1B ndim][ndim×4B dims LE][4B raw_len LE] -body = [M B raw] M = raw_len -ref: 0=内联 1=`*`软链接(body=目标key路径) 2=`@`扩展存储句柄(body=位置描述符) -ndim: 0=标量,≥1=数组维数(无独立 arr_flag,形态由 kind 表达:compact 形态 kind=元素类型;stringkeymap 形态 kind=stringkeymap) -``` - -`HeadLen() = 7 + kind_len + ndim*4`;body 在 `value[HeadLen : HeadLen+raw_len]`。 - -**kind 列表**(`runtime/src/const.h` 唯一事实源): - -| kind | 说明 | elemSize | -|------|------|----------| -| `uint8` / `bool` / `int8` / `char/utf8` | 字节类 | 1B | -| `int16` / `uint16` | 2B 整数 | 2B | -| `int32` / `uint32` / `float32` | 4B 数值 | 4B | -| `int64` / `uint64` / `float64` / `char/utf32` | 8B/4B 数值 | 8B/4B | -| `object` | 有序成员容器(`·` 成员访问) | — | -| `stringkeymap` | 字符串键映射容器 | — | -| `index` | 通用目录 | — | -| `extindex` | 写时复制叠加层 | — | -| `rwir` / `rwfunc` | 运行时指令/函数 | — | -| `defrwir` / `defrwfunc` | 扩展注册指令/函数(/lib/ 类型) | — | -| `scope` | 作用域帧签名 | — | -| `None` | 空值 | — | - -**Ptr(软链接)**:ref=1,body=目标路径字符串。读/写/List 透明穿透,Del 末段作用于链接本体。 - -## ExtIndex - -ExtIndex 是写时复制叠加层:`ExtIndex("/merge/", "/base/")` 后,读 `/merge/x` 先查本地、后回落 `/base/x`;写入本地不影响 `/base/`;List 合并两侧。不允许级联(exttarget 本身不能是 extindex)。 - -## 辅助设施 - -**Watch/Notify**:一次性通知机制,运行时用于 vthread 状态变更和外部 rwir 协调。 - -**路径工具**:`JoinPath` 拼路径避免 `//`;`SepPath` 拆前缀+末段。 - -**常量集中管理**:所有路径/分隔符/kind 字符串在 `runtime/src/const.h`(C)与 `layout/src/keytree.rs`(Rust),禁止散落硬编码。 -"# -> /lib/kvlang/deepdive/kvspace/00_design_and_implementation -} diff --git a/stdlib/kvlang/deepdive/kvspace/01-address-space.kv b/stdlib/kvlang/deepdive/kvspace/01-address-space.kv deleted file mode 100644 index 78c9e0ef..00000000 --- a/stdlib/kvlang/deepdive/kvspace/01-address-space.kv +++ /dev/null @@ -1,46 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/kvspace/01_address_space { - r#"# 地址空间 - -> 本章讲述 kvlang **使用** kvspace 的过程设计与约束,而非 kvspace 自身的设计实现。 - -## 系统域 - -kvspace 树形路径只保留两个系统域,借用 Unix 文件系统思想;**其余 `/` 路径全部自由,由用户定义**: - -``` -/lib/{pkg}·{name} 编译优化后函数(签名 + 指令树)+ .src 源码副本;扩展 rwir 也注册于此 -/vthread/{vid}/ 虚线程栈帧(运行时) -``` - -> 代码里只有 `LIB_ROOT="/lib"` 与 `VTHREAD_ROOT="/vthread"` 两个前缀(`runtime/src/runtime_internal.h`)。曾设想的 `/sys/`(基础设施注册表)与 `/dev/`(I/O 设备域)**已废弃**:基础设施与扩展 rwir 统一落在 `/lib/`,I/O 不经设备域(见下"没有 /dev 设备域")。 - -### `/lib/` - -借鉴 Unix `/lib/`——共享库的标准路径,是函数(layout 产物)的单一事实源。`lib name { }` 命名空间块声明包。多文件通过 `kvlang layoutrwir ` 拼接为单一源 → parse → lower → 写入 `/lib/`,无 `import` 关键字——lib 树即全局命名空间,跨 lib 调用走全路径 `/lib/{lib}·{func}()`。`.src` 源码副本与指令树同目录。已加载文件自动去重。 - -### `/vthread/` - -运行时栈帧,借用 Unix `/proc//` 思想——每 vthread 一棵子树,`‥pc`/`‥status` 等系统键暴露执行状态(见系统变量篇);帧根本身是 extindex 指向 `/lib/` 指令树。 - -### 没有 /dev 设备域 - -KV 世界里没有终端、没有设备文件,只有 key 和 value。`print`/`println`/`cerr`/`input` 这类 I/O **不是**地址空间的一个域,也不是 runtime 的 native builtin——它们是**扩展 rwir**:`term` 扩展运行时把签名注册到 `/lib/`(kind=`defrwir`),执行时直接写宿主进程的 `stdout`/`stderr`,不经任何 `/dev/*` 键。与 `json·to`/`json·from`、tensor 算子同一套 rwir 扩展机制(见扩展运行时篇)。 - -### 两域之外 - -`/` 路径(如 `/counter`、`/n0·val`、`/tmp/seen`)完全由用户代码定义——kvspace 不预设 schema,只提供 Write/Read/Watch 原语。 - -## 存储铁律与扩展存储 - -**kvspace 存储铁律**:Key 必须是字符串路径(`/` 分隔的树形层级);Value 必须是 XValue 序列化后的字节数组。**严禁**直接写入裸基础类型——所有值必须经 XValue 编解码。违反此铁律的写入在 reader 侧读到非法字节时 behavior undefined。 - -kvspace 存储两类数据:**基础数据类型**(int、float、bool、string)和 **tensor 元数据**(shape、dtype、指向扩展存储的句柄)。tensor 完整数据在扩展存储中: - -| 扩展位置 | 典型数据 | -|---------|---------| -| 集群节点共享内存 | 大张量、激活值(heap-plat 管理生命周期) | -| GPU 显存 | 计算张量(op-plat 在设备侧持有句柄) | -| 文件系统/对象存储 | 模型权重、检查点、数据集 | -"# -> /lib/kvlang/deepdive/kvspace/01_address_space -} diff --git a/stdlib/kvlang/deepdive/kvspace/02-addressing-model-and-naming.kv b/stdlib/kvlang/deepdive/kvspace/02-addressing-model-and-naming.kv deleted file mode 100644 index 54352ced..00000000 --- a/stdlib/kvlang/deepdive/kvspace/02-addressing-model-and-naming.kv +++ /dev/null @@ -1,76 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/kvspace/02_addressing_model_and_naming { - r#"# 寻址模型与命名 - -## 寻址模型:KV 路径 vs 内存地址 - -### 传统 VM (Python/Lua/JVM) - -``` -程序计数器 PC = 0x7fff5fbff830 (64-bit 内存地址) -指令 = 内存[PC] → 1 字节 opcode → 操作数 -跳转 = PC = 新地址 (直接修改寄存器) -调用 = push 返回地址 → PC = 函数入口地址 -栈帧 = 连续内存 [rbp-8] = 局部变量 -``` - -内存地址是**一维线性整数**,跳转和调用本质是整数算术。 - -### kvlang - -``` -程序计数器 PC = "[0,0]/entry/[0,0]" (KV 路径字符串) -指令 = kv.Get("/vthread/tid/[0,0]/entry/[0,0]") -跳转 = PC = "[0,0]/merge/[0,0]" (字符串拼接) -调用 = PC = "[0,0]/then/[0,0]" (路径嵌套) -栈帧 = /vthread/tid/[0,0]/ 子树 (KV key 层级) -``` - -KV 路径是**树形层级字符串**,跳转和调用本质是路径拼接 + 子树导航。 - -| 维度 | x86/ARM | Python | Lua | kvlang | -|------|---------|--------|-----|--------| -| PC 类型 | `uint64` | `*PyCodeObject + offset` | `Instruction*` | `string` (KV path) | -| 指令获取 | `mov rax, [rip]` | `_PyEval_EvalFrameDefault` 循环 | `luaV_execute` 循环 | `kv.Get("/vthread/tid/" + pc)` | -| 跳转 | `jmp 0x400100` | `next_instr += oparg` | `pc++` | `pc = new_path` | -| 调用 | `call 0x400200` | `call_function` 压栈 | `luaD_precall` | `pc = pc + "/[0,0]"` | -| 栈帧 | `push rbp; sub rsp, N` | `PyFrameObject` (堆分配) | `CallInfo + L->stack` | `/vthread/tid//` KV 子树 | -| 作用域 | 栈偏移 | `f_localsplus` 数组 | 寄存器索引 | KV key 子路径(裸名 `x`, `y`) | - -## 变量名即指针 - -kvlang 没有 `&` 取址运算符——**代码中对象的变量名,本身就是这个变量的指针**(kvspace 路径)。指令槽里存的从来不是值,而是指针文本(`[0,-1] = "A"`、`[0,1] = "C"`),求值永远经过一次指针间接。 - -指针分两种形态: - -| 形态 | 写法 | 语义 | 解析时机 | -|------|------|------|---------| -| **相对指针** | 裸标识符 `x` | 相对当前栈帧的偏移 | 运行时与栈路径拼接 | -| **绝对指针** | `/counter` | kvspace 全局绝对路径 | 零拼接,直接 Get/Set | - -**局部变量的变量名就是相对指针**。运行时解析公式: - -``` -绝对路径指针 = FrameRoot(PC) + "/" + 相对指针 - -例:PC = /vthread/7/[3,0]/[1,0] - FrameRoot(PC) = /vthread/7/[3,0] ← 去掉末尾 /[coord] - x → /vthread/7/[3,0]/x -``` - -栈路径(帧根)不需要单独的寄存器——PC 本身是帧内路径(帧根本身是 extindex 指向 `/lib/` 指令树),`FrameRoot(PC)` 截取即得。这与 C 的 `rbp + offset` 同构: - -| | C/x86 | kvlang | -|--|-------|--------| -| 帧基址 | `rbp` 寄存器 | `FrameRoot(PC)`——从 PC 截取 | -| 局部变量地址 | `rbp - 8`(基址 + 偏移) | `帧根 + "/x"`(栈路径 + 相对指针) | -| 全局变量地址 | `.data` 固定地址 | `/` 开头绝对指针,零拼接 | -| 指针变量 | 存整数地址 | 存路径字符串:`"/n0" -> ptr`,`ptr·val` 解引用 | - -`/lib/` 下的函数模板中只有相对指针,因此天然可重入:每次调用创建不同的帧路径,同一份相对指针拼接出互不干扰的绝对指针——递归、TCO(尾调用优化:goto/br 不建新帧)无需任何额外机制。 - -全局变量 `/counter` 零成本——绝对指针不经过帧前缀拼接。数组能作为参数传递——`flattenNestedCalls` 将 `[1,2,3]` 展开为临时变量,再将临时变量(持有 XValue)作为普通参数传递。 - -**参数不得同名(fix-032)**:变量名即指针——同一帧内两个同名参数将指向同一个 kvspace 位置。读参列表内部、写参列表内部、以及读写列表之间均不可同名。`rwfunc f(A:int64) -> (A:int64)` 签名非法。layout 的 `checkParamDup` 阻断源码路径,C runtime 兜底非法签名。 -"# -> /lib/kvlang/deepdive/kvspace/02_addressing_model_and_naming -} diff --git a/stdlib/kvlang/deepdive/kvspace/03-instruction-layout-format.kv b/stdlib/kvlang/deepdive/kvspace/03-instruction-layout-format.kv deleted file mode 100644 index 164c3a5f..00000000 --- a/stdlib/kvlang/deepdive/kvspace/03-instruction-layout-format.kv +++ /dev/null @@ -1,104 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/kvspace/03_instruction_layout_format { - r#"# 代码指令的布局格式 - -> 签名参数/返回值的 `type` 用**签名类型表达式**(并集 `A|B`、通配 `any`、高维 shape `[2,3]`、动态维 `?`),BNF 见 `grammar.bnf`。 - -## layoutrwir 的设计原理与函数调用 extindex 机制 - -传统 VM:编译器产线性字节码,call = push 返回地址 + 跳转到函数入口。kvlang 不用字节码拷贝——**函数体永不被复制,调用 = ExtIndex 将帧根设为扩展索引**指向 `/lib/` 下的指令树。 - -layoutrwir 在五语言中的对标: - -| 语言 | 阶段名 | 产物 | -|------|--------|------| -| **C (GCC)** | codegen + assemble + link | 线性机器码 | -| **Go** | compile (walk + SSA) | 线性机器码 | -| **Rust** | codegen (MIR→LLVM IR) | 线性机器码 | -| **Python** | compile | 线性字节码 `.pyc` | -| **V8** | Ignition bytecode + TurboFan | 线性字节码→机器码 | -| **kvlang** | **layoutrwir** | **树形 KV 键**,可逐槽寻址 | - -五种语言都在生成线性序列。kvlang 的 layoutrwir 不是序列化——是**空间布局**:每条指令展开为一组 `[s0,s1]` 坐标,读参负轴、写参正轴、opcode 零点。产物可逐槽 `kv·get`/`kv·list`,无需反汇编器。 - -### 指针传址方案(φ1) - -函数定义利用 slot 坐标 row 0,指令从 row 1 开始。参数名通过命名 Ptr key 映射到 slot 坐标,运行时走 3 跳 Ptr 链解析。 - -**Layout(`/lib/` 下,只读):** - -``` -/lib/main·add/[0,0] → Rwfunc(nr=2, nw=1, al=2) ← 函数签名(仅计数) -/lib/main·add/a → Ptr(string, "[0,-1]", 1) ← 命名参数→slot -/lib/main·add/b → Ptr(string, "[0,-2]", 1) -/lib/main·add/c → Ptr(string, "[0,1]", 1) ← 命名返回值→slot -/lib/main·add/[1,0] → "+" ← 指令0(idx+1) -/lib/main·add/[1,-1] → Rwir("a") ← 指令0 read slot -/lib/main·add/[1,-2] → Rwir("b") -/lib/main·add/[1,1] → Rwir("c") -/lib/main·add/[2,0] → "return" ← 指令1 -``` - -**Runtime(`/vthread/.../` 帧根,extindex → `/lib/main·add/`):** - -``` -frameRoot/a → ext→ Ptr("[0,-1]") ← layout 写入,只读 -frameRoot/b → ext→ Ptr("[0,-2]") ← 只读 -frameRoot/c → ext→ Ptr("[0,1]") ← 只读 -frameRoot/[0,-1] → Char("/vthread/1/_lit0") ← runtime 写入,无 ext 冲突 -frameRoot/[0,-2] → Char("/vthread/1/x") ← runtime 写入 -frameRoot/[0,1] → Char("/vthread/1/result") ← runtime 写入 -frameRoot/[1,0] → ext→ "+" ← 指令 -``` - -**Resolve(3 跳 Ptr 链):** - -``` -指令 [1,-1]=Rwir("a"): - 1. GetOne(frameRoot+"/a") → ext→ Ptr("[0,-1]") ← name→slot - 2. GetOne(frameRoot+"/[0,-1]") → Char(path) ← slot→arg地址 - 3. GetOne(path) → value ← 解引用 -``` - -**XValueHead ref 字段**:`ref=0` 内联,`ref=1` 软链接(body=目标路径),`ref=2` 扩展存储句柄。完整格式见 key系统篇。 - -### ExtIndex:帧根指向指令树 - -函数调用时,不是把指令字节码拷贝到新帧——而是通过 **ExtIndex** 让帧根成为指向 `/lib/` 指令树的扩展索引: - -``` -编译期(WriteFunc): - AST → KV 结构化写入 /lib/main·add/: - /lib/main·add/[0,0] = Rwfunc(r2/w1) ← 函数签名 - /lib/main·add/a = Ptr("[0,-1]") ← 命名参数→slot - /lib/main·add/b = Ptr("[0,-2]") - /lib/main·add/c = Ptr("[0,1]") ← 命名返回值→slot - /lib/main·add/[1,0] = "+" ← 指令0 - /lib/main·add/[1,-1] = Rwir("a") - /lib/main·add/[1,-2] = Rwir("b") - /lib/main·add/[1,1] = Rwir("c") - /lib/main·add/[2,0] = "return" ← 指令1 - -调用时: - kv·ExtIndex(frameRoot+"/", "/lib/main·add/") ← 帧根→/lib/ 指令树 - kv·Set(frameRoot+"/[0,-1]", Char(argAddr)) ← runtime arg 槽,无 ext 冲突 - kv·Set(frameRoot+"/[0,-2]", Char(argAddr)) - kv·Set(frameRoot+"/[0,1]", Char(resultAddr)) - # 所有帧共享 /lib/ 下同一份指令树,零拷贝 -``` - -**关键设计**:`[0,-j]` key 在 `/lib/` 下不存在(只有 named key `a`/`b`/`c`),因此 runtime 写入 `frameRoot/[0,-j]` 不会触发 extindex 写保护。 - -### 与传统 VM 的关键差异 - -| | 传统 VM | kvlang | -|--|---------|--------| -| 代码传递 | copy 字节码到新栈帧 | **ExtIndex**(所有帧共享 /lib/ 下同一份指令树) | -| 帧模型 | 一种帧(call/return) | **两种帧**:rwfunc(函数调用)+ scope(goto/br,详见控制流篇) | -| 崩溃恢复 | 栈帧在内存,进程死即全失 | PC=路径字符串、frameRoot=返回点落 KV——重启续跑 | -| 可观测 | 需调试器 attach | `kvspace tree /vthread/…` 看 extindex 指向、frameRoot,局部变量直读帧根 | -| 参数模型 | 寄存器/栈槽传值 | Ptr 链:name→slot→arg→value(3 跳) | - -**`=` 操作码是值拷贝,不是函数调用**:`a -> b` 编码为 `[s0,0]="="`(值拷贝),函数调用 opcode 位是 `call`,ExtIndex 发生在调用处理函数内部。二者在 KV 层无歧义,opcode 位永远不放变量引用。 -"# -> /lib/kvlang/deepdive/kvspace/03_instruction_layout_format -} diff --git a/stdlib/kvlang/deepdive/kvspace/04-system-variables.kv b/stdlib/kvlang/deepdive/kvspace/04-system-variables.kv deleted file mode 100644 index ffebaddf..00000000 --- a/stdlib/kvlang/deepdive/kvspace/04-system-variables.kv +++ /dev/null @@ -1,66 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/kvspace/04_system_variables { - r#"# 系统变量 - -## `X/‥var` 影子键 - -> **本节是全部 `‥` 系统键的唯一事实源。** -> 任何代码变更(新增、修改、删除 `‥` 键)**必须先更新本节**,再改代码。 - -VM 运行时为它管理的对象生成**内置变量(系统变量)**,以 `{对象key}/‥var` 形式存放:`/` 下探一层、键名以 `‥`(U+2025,`RUNTIME_MEMBER_SEP`)开头。kvlang 标识符不能以此字符开头,因此所有 `‥` 前缀键均为引擎保留,用户代码无法直接读写——类比 Unix 隐藏文件:默认视图不显示,引擎可见。 - -**与用户成员键的区分**:`X·attr`(`·` 中点键,U+00B7,如 `obj·prop`、`lib·func`)是用户 kv 代码可访问的正常成员;`X/‥attr`(`/` + `‥` 前缀)是引擎专属。零交集:用户侧永远造不出 `‥` 开头的路径段。 - -### 全量清单(按宿主对象分类) - -**vthread 对象**(宿主 = `/vthread/`;代码 `runtime/src/keytree.c`、`layout/src/keytree.rs`): - -| 键 | 语义 | -|----|------| -| `‥pc` | 当前执行 PC(绝对路径字符串) | -| `‥status` | 运行状态:`init`/`running`/`wait`/`paused`;终态 Del + Notify(retVal) | -| `‥debugger` | 调试控制:`""` 正常,`"paused"` 暂停;`debugger()` builtin 写此键 | - -**帧对象**(宿主 = frameRoot;代码 `runtime/src/keytree.c`、`layout/src/keytree.rs`): - -| 键 | 语义 | -|----|------| -| *(帧根 extindex)* | rwfunc 帧根 extindex → `/lib/·` 只读指令区;scope 帧不建 extindex | -| `‥lib` | rwfunc 帧的 lib 路径(如 `/lib/sum_to`),用于识别 rwfunc 帧边界 | -| `‥callpc` | 每帧执行进度(每 op 更新);scope 帧每次重入更新 | -| `‥returnpc` | 返回地址(帧创建时固化);scope 帧仅首次设置,重入不覆写 | -| `‥ro` | 只读参数名单(逗号分隔),kvcpu 写槽检查用;无读参函数不写 | -| `‥rparam/` | 读参重定向:存调用方值的绝对路径;CPU 读参从此路径直读,零拷贝(layout 侧定义,C runtime 待对齐) | -| `‥wparam/` | 写参重定向:存调用方写目标的绝对路径;CPU 写参直写此路径(layout 侧定义,C runtime 待对齐) | - -**语法层保留名(不落盘,`frameSlotKey` 拦截)**: - -| 键 | 语义 | -|----|------| -| `._` | 丢弃槽——`frameSlotKey` 遇到此名返回空路径,不写入 kvspace | - -注意:`._` 与上述 `‥` 键不同,这是源码层的语法占位符,不落 kvspace。 - -**内联暂停指令**:`debugger()`(对齐 V8/TypeScript `debugger;` 语句)——源码内联暂停点;非调试模式下 no-op,调试模式下将 `‥status` 置为 `paused`,外部驱动改回 `running` 恢复执行。 - -注意区分:地址空间的系统**域**(`/lib/`、`/vthread/` 顶层树,见地址空间篇)与对象随身的 `‥var` 系统**变量**是两种机制。 - -### 三种键形态:一眼判型 - -任意 key 的形态唯一确定其性质: - -| 形态 | 例 | 性质 | 所有权 | -|------|----|------|--------| -| `X/名`(`/` + 普通名) | `/vthread/7/[3,0]`、`/_while_2/[0,0]` | 结构:帧、指令槽、scope 帧 | VM | -| `X·名`(`·` 中点键) | `/c0·next`、`frame/obj·prop` | 用户数据成员 | 用户 | -| `X/‥名`(`/` + `‥` 名) | `/vthread/7/‥pc`、`/vthread/7/‥status` | 系统变量(影子元数据) | VM | - -### 设计结论:系统变量维持 `/‥` 分隔 - -系统变量用 `/‥` 分隔,与用户成员的 `·` 分隔形成正交: - -1. **零冲突的专属命名空间**。用户成员语法只产生 `·` 中点键,标识符禁止 `‥` 开头;用户侧永远造不出 `‥` 序列。若系统变量也用 `·` 拼接,则需维护保留字表,且动态键注入可能命中系统键。 -2. **生命周期绑定**。`X/‥var` 在 X 的 `/` 子树内,`DelTree(X)` 连带清除全部系统变量;用户键族 `X·*` 由前缀删除管理。两个删除平面各归其主。 -3. **统一公理**。任何 kvspace 对象 X:VM 元数据在 `X/‥名`(引擎保留),帧根 extindex 指向 `/lib/` 代码区,用户数据在 `X·名`(成员),子级在 `X/名`(结构)。 -"# -> /lib/kvlang/deepdive/kvspace/04_system_variables -} diff --git a/stdlib/kvlang/deepdive/kvspace/05-key-system-and-array-access.kv b/stdlib/kvlang/deepdive/kvspace/05-key-system-and-array-access.kv deleted file mode 100644 index 5a346e1f..00000000 --- a/stdlib/kvlang/deepdive/kvspace/05-key-system-and-array-access.kv +++ /dev/null @@ -1,151 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/kvspace/05_key_system_and_array_access { - r#"# key 系统与数组访问 - -## 背景:分隔符语义化 - -kvspace 的树形 key 与 XValue 的 kindexp 共用一套分隔符语义,每个分隔符承载固定含义: - -| 分隔符 | 语义 | key 层 | kindexp 层 | -|--------|------|--------|-----------| -| `/` | 层级 | `/lib/f/`、`/vthread/1/` | — | -| `·` | 成员(U+00B7) | `obj·x`、`lib·func` | — | -| `[]` | 数组访问 | `arr[3]` 数组下标 | `[]int32`、`[10]int32`、`[256,256]uint8` | -| `@` | 扩展存储句柄 | — | `@[256,256]uint8` | - -## rwir 指令序列的 key:`[s0,s1]` - -函数 layout 后的 rwir 指令序列,每个指令槽用 `[s0,s1]` 坐标 key(与当前实现一致)。 - -``` -/lib/main·add/[0,0] → Rwfunc(nr=2, nw=1) ← 函数签名 -/lib/main·add/[1,0] → "+" ← 指令 0 opcode -/lib/main·add/[1,-1] → Rwir("a") ← 指令 0 读槽 1 -/lib/main·add/[1,-2] → Rwir("b") ← 指令 0 读槽 2 -/lib/main·add/[1,1] → Rwir("c") ← 指令 0 写槽 1 -/lib/main·add/[2,0] → "return" ← 指令 1 -``` - -坐标约定:`[s0,0]` = opcode,`[s0,-j]` = 第 j 个读槽,`[s0,+j]` = 第 j 个写槽。 - -## 索引目录符号 - -kvspace 路径系统有 `/`(层级目录)与 `·`(成员目录)两个索引目录符号。stringkeymap 形态(散 key)的元素 key 用 `base·i` 后缀作为**内部命名**——后端存储约定,非用户语法;遍历经 `for-in`/`kv·get`。compact 形态无子 key——元素打包在单个 body,`arr[i]` desugar 为 `xv·at` 在 body 内定位。 - -| 符号 | 位置 | 语义 | -|------|------|------| -| `/` | 后缀 | 层级目录 | -| `·` | 后缀 | 成员目录 | -| `·i` | 内部后缀 | stringkeymap 形态元素 key(存储层,`arr·0`, `arr·1` …) | - -## kind 与 kindexp 二分 - -| 概念 | 内容 | 例子 | -|------|------|------| -| **kind** | 基础类型字符串,标量/元素类型 | `uint8` `int16` `int32` `float64` `bool` `char/utf32` `object` `stringkeymap` `index` `rwir` `rwfunc` … | -| **kindexp** | 完整类型表达式,kind + 修饰符 | `int8` `*int8` `*[]int32` `*[16]uint8` `[256,256]uint8` `[10]int32` `@[256,256]uint8` | - -**kind 是 kindexp 的叶子**:剥掉所有 `*`/`@`/`[]` 修饰符即得 kind。kindexp 字符串是显示/讨论形式,落盘为下方 XValueHead 的 3 区二进制 head。 - -## kindexp 文法 - -**全前缀**:从左到右读,每个前缀修饰其右侧的类型;最左符号即最外层构造器,无优先级歧义(对标 Go/Rust 的 `*[]int`、`*[10]int`)。 - -```bnf -kindexp ::= kind # 标量 - | '*' kindexp # 指针/软链接(body = 目标 key 路径) - | '@' kindexp # 扩展存储句柄(body = 位置信息,data 在扩展存储) - | '[' ']' kindexp # 变长一维数组类型;compact/stringkeymap 由初始化括号([...]/{...})定 - | '[' dims ']' kindexp # 定长/多维数组(compact 形态,单 XValue 连续 body) -dims ::= INT (',' INT)* -kind ::= uint8 | int8 | int16 | uint16 | int32 | uint32 | int64 | uint64 - | float32 | float64 | bool | char/utf32 | char/utf8 | char/ascii | time | duration - | object | stringkeymap | index | extindex | rwir | rwfunc | scope -``` - -## 「数组」的两种存储形态:compact 与 stringkeymap - -计算机理论中的数组(含多维数组)在 kvlang 有两种物理存储形态,落盘为不同 kind: - -| 形态 | head kind | 触发写法 | 元素位置 | XValue 数 | -|------|-----------|---------|---------|----------| -| **compact 形态** | 元素 kind(`int64`…) | `[1,2,3]`、`[N]T`、`[d0,d1]T` | 连续打包进**单个 XValue** 的 body | **1** | -| **stringkeymap 形态**(散 key) | `stringkeymap` | `{v0,v1,…}`、`array·append`/`slice`/`scatter` | 每元素落 `base·0`、`base·1`… **独立 key** | N+1 | - -- **compact 形态**:元素连续打包进一个 XValue,`raw_len = ∏dims × ElemSize`,head 的 kind **即元素类型**。对齐 C `int[10]`、Go `[10]int`、Rust `[i32;10]`。多维 `[256,256]uint8` = `ndim=2`,即 **ndarray/tensor** 连续布局(与 op-gpu 对齐)。要求元素定长同类型——含变长字符串字面量的 `[...]` 在 layout 阶段报错。支持 `arr[i]`/`xv·at`/`ndarray·numel/dim/shape` 随机访问,可零拷贝整块读。 -- **stringkeymap 形态**:head kind=`stringkeymap`、`ndim=1`、`dims=[n]`,每个元素是独立子 key `base·i`(`·` 内部后缀,见上)。变长、可增长、允许变长元素——**字符串数组、可追加数组走这里**。`array·append`/`array·slice` 作用于 compact 数组时会先自动转成 stringkeymap 形态(见「转换与变长」)。 - -## 字面量括号即形态:`[...]` 与 `{...}` - -| 字面量 | 形态 | 约束 | -|--------|------|------| -| `[1, 2, 3]` | **compact**(kind=元素类型) | 元素须定长同 kind、固定字节宽;含变长字符串成员时 layout 阶段报错 | -| `{1, 2, 3}` / `{"a", "bb"}` | **stringkeymap**(散 key) | 变长/可增长;**字符串数组必须走这里** | -| `{k = v; ...}` | object / stringkeymap(map) | 靠 `=` 键值对与散 key 数组区分;空 `{}` 须带类型标注 | - -- **为何字符串禁入 `[...]`**:compact 要求元素定长连续打包,变长字符串无法定长打包,故 layout 阶段报错,改用 `{...}`。 -- **散 key 字面量位置约束**:`{...}` 只允许作赋值右值 `x = {...}` 或 for-in 源 `for (s in {...})`。 -- **随机访问**:compact 形态支持 `arr[i]`/`xv·at`;stringkeymap 形态目前经 `for-in` 或 `kv·get`/`kv·list` 遍历访问。 - -## `@`:扩展存储句柄 - -| 符号 | 语义 | body 存什么 | -|------|------|-----------| -| (无) | 内联值 | 数据本体 | -| `*` | kvspace 内软链接 | kvspace key 路径 | -| `@` | 扩展存储句柄 | 扩展存储位置(device + address) | - -- `@[256,256]uint8` = 256×256 uint8 tensor 的句柄,data 在 SHM/GPU,body 存 `{device, shm_name/offset}`。 -- `*` 与 `@` 的差别是**目标命名空间**:`*` 指向元存内另一个 key,`@` 指向元存外(SHM/GPU/文件)的一块连续数据。 -- **约束**:`@` 只组合 `kind` 与 `[dims]kind`(compact 形态);外部分散数组无意义,拒绝。 - -## XValueHead 与 body - -``` -XValue = XValueHead + body -XValueHead = [1B kind_len][N B kind][1B ref][1B ndim][ndim×4B dims LE][4B raw_len LE] -body = [M B raw] M = raw_len,offset = HeadLen() -``` - -**XValueHead 字段**: - -| 字段 | 大小 | 含义 | -|------|------|------| -| `kind_len` | 1B | kind 字符串字节数 | -| `kind` | N B | 基础类型叶子(明文字符串) | -| `ref` | 1B | 0=内联 1=`*`软链接 2=`@`扩展句柄 | -| `ndim` | 1B | 0=标量,≥1=数组维数 | -| `dims` | ndim×4B | 各维长度 LE | -| `raw_len` | 4B | body 字节数 LE | - -`HeadLen() = 7 + kind_len + ndim*4`;派生字段:`isptr=(ref==1)`、`isext=(ref==2)`、`arraylength = ∏dims`。 - -**形态由 `kind` 与 `ndim` 表达,无独立 flag**:compact 形态 head 的 kind 是元素类型(`int32`…)、`ndim≥1`;stringkeymap 形态 head 的 kind 是 `stringkeymap`、`ndim=1`、body 只记形状而元素散落子 key;标量 `ndim=0`。 - -**kindexp 字符串 ↔ 二进制 head 的映射**: - -| kindexp 字符串 | ref | ndim | dims | kind | -|---------------|-----|------|------|------| -| `int32` | 0 | 0 | — | `int32` | -| `[10]int32` | 0 | 1 | [10] | `int32` | -| `[256,256]uint8` | 0 | 2 | [256,256] | `uint8` | -| `*[]int32` | 1 | 1 | — | `int32` | -| `@[256,256]uint8` | 2 | 2 | [256,256] | `uint8` | - -## 转换与变长:`array·scatter` / `array·compact` / `array·append` / `array·slice` - -| rwir | 方向 | 语义 | -|------|------|------| -| `array·scatter(arr) -> dst` | compact → stringkeymap | 连续数组 `arr` 拆成 `dst·0..dst·N-1` 标量 key,不动 `arr` | -| `array·compact(arr) -> dst` | stringkeymap → compact | 读 `arr·0..arr·N-1` 打包成连续数组 `dst`,不动 `arr` | -| `array·append(arr, elem) -> arr` | 变长 +1 | 追加 `elem`;若 `arr` 为 compact 先自动 scatter 成 stringkeymap | -| `array·slice(arr, lo, hi) -> arr` | 变长截取 | 切片 `arr[lo:hi]`;若 `arr` 为 compact 先自动 scatter 成 stringkeymap | - -## 待定项 - -- stringkeymap 形态 body schema(shape/元素类型/稀疏索引)细化。 -- compact 多维 `[256,256]uint8` 的 row-major 排布约定(与 op-gpu tensor 连续布局对齐)。 -- stringkeymap 形态随机访问:元素散落 `base·i` 子 key,`for-in`/`kv·get` 可遍历,但 `arr[i]`/`ndarray·numel` 随机访问尚未打通;需随机访问的数值数组用 compact 形态。 -- 扩展存储句柄 body 的位置 schema(device 枚举、shm_name/offset、fd、GPU ptr 的统一编码)。 -"# -> /lib/kvlang/deepdive/kvspace/05_key_system_and_array_access -} diff --git a/stdlib/kvlang/deepdive/layout/01-instruction-architecture.kv b/stdlib/kvlang/deepdive/layout/01-instruction-architecture.kv deleted file mode 100644 index fa3b4113..00000000 --- a/stdlib/kvlang/deepdive/layout/01-instruction-architecture.kv +++ /dev/null @@ -1,139 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/layout/01_instruction_architecture { - r#"# Instruction Architecture(指令架构) - -## 指令分类 - -指令分两层,kvcpu 只见执行层: - -**执行层**(kvcpu 直接处理): -1. **原子 rwir**:`writes <- opcode(reads...)` 或 `opcode(reads...) -> writes`。读/写角色由箭头方向决定,无隐式栈、无匿名寄存器。`writes = expr` 是 `<-` 的等价书写;`=` 不是表达式,不可嵌套在条件中。控制流原语 `call`/`return`/`br`/`goto` 是 rwir 的子集。 -2. **复合 rwir(rwfunc)**:`rwfunc funca(ra,rb) -> (wa,wb) { … }` 把多条 rwir 打包为命名单元,对外暴露相同的 `(reads) -> (writes)` 箭头接口。HandleCall 将实参绑入子帧读槽,HandleReturn 将子帧写槽写回父帧——没有"返回值",只有槽位间的数据流动。调用时**必须匹配全部写参**,不需要的用 `_` 丢弃(与 Go/Rust `_` 约定一致,写入 `_` 帧槽,不影响语义)。 - -**源码层**(lower 后消失):`if`/`else`/`while`/`for`——lower pass 将其降级为 ScopeStmt + `br`/`goto`,kvcpu 不感知。 - -### `->` / `<-` / `=` 与写槽 - -三种书写形态,写槽约束完全一致: - -| 形态 | 写槽位置 | 例 | -|------|---------|-----| -| `expr -> writes` | 右 | `A + B -> C` | -| `writes <- expr` | 左 | `C <- A + B` | -| `writes = expr` | 左(≡ `<-`) | `C = A + B`、`p·val = 8` | - -**铁律**:`->` 右侧 / `<-`、`=` 左侧必须是**位置**(裸名、`/abs`、`base·名`);字面量在写槽位置是语法错误,layout 报错拒绝。 - -调用时**写参 arity 强制匹配**(对齐 Go/Rust 阵营):`f() -> s` 对多写参函数是 layout 错误;不需要的写参用 `_` 接收。 - -## 指令的二维空间模型:`[s0, s1]` - -### 两个轴的含义 - -每条指令在 KV 树中占据一个**二维坐标** `[s0, s1]`: - -``` -s0 轴(横轴) — 执行顺序轴:第几条指令 -s1 轴(纵轴) — 参数轴:该槽的角色 - - s1 < 0 s1 = 0 s1 > 0 - (读参,输入) (操作码) (写参,输出) - ←─────────────── 0 ───────────────→ -s0 = 0 │ [0,-2] [0,-1] [0,0] [0,1] [0,2] -s0 = 1 │ [1,-2] [1,-1] [1,0] [1,1] -s0 = 2 │ [2,-1] [2,0] [2,1] - ... -``` - -**铁律**: -- `[s0, 0]` **永远是 opcode**(操作符或被调用函数名) -- `[s0, -1], [s0, -2], ...` **读参**(read slots),负号代表"消费数据" -- `[s0, 1], [s0, 2], ...` **写参**(write slots),正号代表"产出数据" - -### 具体示例 - -```kv -rwfunc add(A: int64, B: int64) -> (C: int64) { - A + B -> C -} -``` - -lower + layoutrwir 后写入 KV: - -``` -/lib/main·add/[0,0] = "+" ← s1=0: opcode -/lib/main·add/[0,-1] = "A" ← s1=-1: 第1个读参(left operand) -/lib/main·add/[0,-2] = "B" ← s1=-2: 第2个读参(right operand) -/lib/main·add/[0,1] = "C" ← s1=1: 第1个写参(result destination) - -/lib/main·add/[1,0] = "return" ← s1=0: opcode(隐式 return,appendReturn 补充) -``` - -图示为一张二维表格: - -``` - s1=-2 s1=-1 s1=0 s1=1 -s0=0 │ "B" "A" "+" "C" -s0=1 │ "return" -``` - -数据流方向清晰:**从负轴读入,在零轴执行,向正轴写出**。 - - -### 多参数指令与扇出 - -```kv -print("hello", x, y) -``` - -``` -/lib/main·foo/[3,0] = "print" ← opcode -/lib/main·foo/[3,-1] = "\"hello" ← 第1读参(字符串字面量," 前缀编码) -/lib/main·foo/[3,-2] = "x" ← 第2读参 -/lib/main·foo/[3,-3] = "y" ← 第3读参 -(无写参,副作用是输出) - -`print` 多参数直接拼接(无空格分隔),末尾不追加换行。`println` 空格分隔参数,末尾追加换行(Go 风格)。 -``` - -写参扇出(同一结果写入多个槽): - -```kv -a + b -> sum, backup -``` - -``` -/lib/main·foo/[2,0] = "+" -/lib/main·foo/[2,-1] = "a" -/lib/main·foo/[2,-2] = "b" -/lib/main·foo/[2,1] = "sum" ← 第1写参 -/lib/main·foo/[2,2] = "backup" ← 第2写参(扇出) -``` - -拷贝指令(叶表达式 → 写槽)编码为显式操作码 `=`,值引用放读槽: - -```kv -a -> b # [s0,0]="=" [s0,-1]="a" [s0,1]="b" -42 -> x # [s0,0]="=" [s0,-1]="42" [s0,1]="x" -``` - -opcode 位**永远是操作码**,从不放变量引用——`=` 使拷贝与零参函数调用(`greet() -> x`,opcode="greet")在 KV 层无歧义。 - - -### 解码与写函数约束 - -kvcpu 沿 s1 轴向两侧扩展,直到遇到空 key 停止,参数数量**隐式编码**,无需在 opcode 中存储 arity。 - -**WriteFunc 正确性约束**:写新函数前必须先 `del_tree` 清除旧数据。若旧函数在 `[0,-1]` 有遗留值,Decode 会误读为真实读参。 - -### 与传统字节码的对比 - -| 维度 | 传统字节码 | kvlang `[s0, s1]` | -|------|-----------|-------------------| -| 指令存储 | 线性数组 `code[PC]` | KV 树 `kv·get(prefix + "[s0,0]")` | -| 操作数位置 | opcode 后紧跟操作数 | `s1<0`(读)/ `s1>0`(写)分离 | -| 参数数量 | opcode 内编码 arity | 隐式:扫描到空 key 停止 | -| 数据流方向 | 单向(操作数 → 结果) | 符号编码(负=读, 正=写) | -| 可观察性 | 字节不可独立寻址 | 每个槽是独立 KV key,可单独 Get/Watch | -"# -> /lib/kvlang/deepdive/layout/01_instruction_architecture -} diff --git a/stdlib/kvlang/deepdive/layout/02-functions.kv b/stdlib/kvlang/deepdive/layout/02-functions.kv deleted file mode 100644 index f459e099..00000000 --- a/stdlib/kvlang/deepdive/layout/02-functions.kv +++ /dev/null @@ -1,53 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/layout/02_functions { - r#"# 函数 - -> 签名参数/返回值的 `type` 用签名类型表达式(并集 `A|B`、通配 `any`、高维 shape `[2,3]`、动态维 `?`),见 `grammar.bnf`。 - -## 函数无返回值:只有读参与写参 - -kvlang 函数**没有返回值**,只有两种参数: - -| 参数类型 | KV 槽位 | 方向 | -|---------|---------|------| -| **读参** | `[s0, -1], [s0, -2], ...` | 调用方 → 被调方 | -| **写参** | `[s0, +1], [s0, +2], ...` | 被调方 → 调用方 | - -`-> result` 是**写参的跨帧路径映射**:HandleReturn 将子帧写参直写调用方帧目标路径。没有"返回值",只有槽位间的数据流动。 - -### 写槽即位置 - -写槽(`->` 右侧 / `<-`、`=` 左侧)必须是**位置**:裸名、`/abs`、`base·名`。字面量在写槽位置是语法错误。多写参逗号分隔,与签名写参一一对应: - -```kv -divmod(17, 5) -> q, r # q←商, r←余数 -add(a, b) -> _ # _ 丢弃(约定,写入 _ 帧槽) -``` - -### 读参只读 - -读参是调用方 → 被调方的输入绑定。函数体内将读参裸名放入写槽(含 `for` 迭代变量)破坏 rwir 数据流方向——layout `check_read_only_params` 报 error 拒绝装载。成员写(`A·x = v`,脱糖为 set 本体回写)不写本体,豁免。 - -**签名诚实原则**:若函数体内写了某参数,该参数必须在写参侧: - -```kv -# ❌ 签名撒谎:acc 被写但在读参侧 → 报错 -rwfunc sum(arr, acc:int64) -> (r:int64) { acc + arr[0] -> acc } - -# ✅ 签名诚实:acc 在写参侧,体内可读可写 -rwfunc sum(arr) -> (acc:int64) { acc + arr[0] -> acc } -``` - -### 定义与调用的参数同名规则 - -**定义时**:读参与写参不得同名——`rwfunc f(A:int64) -> (A:int64)` 非法,`check_param_dup` 报错拒绝。 - -**调用时**:同变量可同时出现在读槽和写槽——`inc(x) -> x` 合法,HandleCall 对两者独立解析,不冲突。 - -### layout 写槽校验(`collect_write_list`) - -- 字面量写槽(数字/引号串开头)→ warn「unexpected token in write slot position」 -- 写槽后紧跟 `(` → warn「function call on same line as write slot」 -- 合法写槽:裸名、`/abs`、`base·名` -"# -> /lib/kvlang/deepdive/layout/02_functions -} diff --git a/stdlib/kvlang/deepdive/layout/03-control-flow.kv b/stdlib/kvlang/deepdive/layout/03-control-flow.kv deleted file mode 100644 index 4b95434f..00000000 --- a/stdlib/kvlang/deepdive/layout/03-control-flow.kv +++ /dev/null @@ -1,141 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/layout/03_control_flow { - r"# 控制流:Scope 帧模型 - -# Part 1: Layout - -## Lower Pass(`lower_func`) - -`lower_func` 将 `if`/`while`/`for` 降级为 ScopeStmt + `br`/`goto`,goto/br 目标用短名(如 `_while_2`)。 - -``` -# 源 # lower 后 -while (found == 0) { goto(_while_2) - s = lo + hi _while_2: { - int(s/2) -> mid found == 0 -> _1 - tries = tries + 1 br(_1, _do_3, _exit_4) - mid == target -> hit } - if (hit) { ... } _do_3: { - else { lo + hi -> s - too_low = mid < target tries + 1 -> tries - if (too_low) { mid == target -> hit - mid + 1 -> lo goto(_if_5) - } } -} _if_5: { - br(hit, _then_6, _else_7) - } - _then_6: { ... } - _else_7: { - mid < target -> too_low - goto(_if_9) - } - _if_9: { - br(too_low, _then_10, _else_11) - } - _then_10: { mid + 1 -> lo; goto(_merge_12) } -``` - -- 所有 ScopeStmt 为函数体平级兄弟节点 -- `inject_goto_blocks` 补全提升后缺失的 goto -- break/continue → `goto exitLabel` / `goto condLabel` - -## 指令布局 - -**函数指令**:`/lib//[i,j]`(标准格式,供 extindex 解析) - -**Scope 指令**:`/lib//[i,j]`(flat key,scope 名直接拼在 coord 前) - -``` -/lib/sum_to/[0,0] ← 函数体指令 -/lib/sum_to/[1,0] ← goto _while_2 -/lib/sum_to/_while_2[0,0] ← scope 条件块 -/lib/sum_to/_while_2[1,0] ← br(bool, _do_3, _exit_4) -/lib/sum_to/_do_3[0,0] ← scope 循环体 -``` - ---- - -# Part 2: Runtime - -## 帧类型 - -| | rwfunc 帧 | scope 帧 | -|--|----------|---------| -| 创建者 | `call(func, args...)` | `goto(label)` / `br(cond, t, f)` | -| extindex | ✅ → /lib// | ❌ 不建 extindex | -| `.rparam` / `.wparam` | ✅ | ❌ | -| `.lib` 标记 | ✅ | ❌ | -| `.returnpc` / `.callpc` | ✅ | ✅ | -| 隐式 return | DelTree 整帧 | DelTree 自身,变量不可被父 scope 访问 | -| 帧位置 | callPC | rwfunc 帧下平级子目录 | - -``` -/vthread/1/[0,0]/ ← rwfunc 帧(call 创建) -├── .lib = /lib/sum_to ← rwfunc 标记(funcFrameRoot 检测用) -├── .rparam/n → ... ← 读参零拷贝 -├── .wparam/total → ... ← 写参零拷贝 -├── .returnpc / .callpc -├── total = 0, i = 1 ← 局部变量(所有变量均在 rwfunc 帧) -├── _while_2/ ← scope 帧(goto/br 创建,平级) -│ ├── .callpc ← 每次重入更新 -│ └── .returnpc ← 首次设置,不覆写 -├── _do_3/ ← scope 帧(平级) -│ ├── .callpc -│ └── .returnpc -└── _exit_4/ ← scope 帧(平级) -``` - -## 变量作用域 - -**所有变量存储于 rwfunc 帧 Stack**。`funcFrameRoot` 沿帧树向上查找 `.lib` 标记识别 rwfunc 帧。 - -- 写:`resolveWriteSlot` 先查 `.wparam` 重定向,否则写 rwfunc Stack -- 读:`resolveReadValue` 先查 `.rparam` 重定向,再查 rwfunc Stack -- scope 帧不存储变量;scope 退出 DelTree 自身 -- 子 scope 可访问父变量(都在 rwfunc 帧),scope 退出后变量不可被父访问(scope 帧已删除) - -## Decode:scope 感知 key 构造 - -scope 帧不建 extindex,Decode 手动拼接 scope 前缀 key(如 `_while_2[0,0]`),经父帧 extindex 解析到 `/lib/func/_while_2[0,0]`。 - -## HandleScope(goto/br) - -`HandleScope` 在 rwfunc 帧下创建 scope 子目录,不建 extindex。`.returnpc` 仅首次设置(防止 while 循环退出路径错误),`.callpc` 每次更新。 - -## HandleScopeReturn - -scope 帧隐式 return:读 `.returnpc` → DelTree 自身 → 返回父上下文。 - -## 系统变量 - -| 变量 | 位置 | 职责 | -|------|------|------| -| `.pc` | vthread 级 | 外部视图 | -| `.callpc` | 每帧 | 本帧执行进度 | -| `.returnpc` | 每帧 | 返回地址 | -| `.lib` | rwfunc 帧 | extindex 目标路径(funcFrameRoot 检测用) | - -## HandleCall - -1. frameRoot = callPC,ExtIndex → /lib// -2. 写 `.lib` = funcKey,`.returnpc`,`.callpc` -3. 读参零拷贝:`.rparam/` → 调用方值位置 -4. 写参零拷贝:从 inst.Writes 直接读 write target(不从帧路径查) - -## Bootstrap - -虚线程根 `/vthread/1/`,extindex → 入口函数。写 `.lib` = funcKey。 - -## 与传统 VM 的关键差异 - -| | 传统 VM | kvlang | -|--|---------|--------| -| 代码传递 | copy 字节码 | **ExtIndex**(共享 /lib/ 指令树) | -| 帧模型 | 一种帧 | **两种帧**:rwfunc + scope | -| rwfunc 帧检测 | — | **`.lib` 标记**(避免 extindex 层叠干扰) | -| 变量作用域 | 词法作用域 | **rwfunc 帧集中存储**,funcFrameRoot 定位 | -| scope 指令查找 | — | **Decode scope 前缀 key** + 父帧 extindex | -| 返回地址 | 硬件栈 | **`.returnpc`** 显式记录 | -| 崩溃恢复 | 内存栈,死即全失 | PC + frameRoot 落 KV——重启续跑 | -" -> /lib/kvlang/deepdive/layout/03_control_flow -} diff --git a/stdlib/kvlang/deepdive/layout/04-layout-pipeline.kv b/stdlib/kvlang/deepdive/layout/04-layout-pipeline.kv deleted file mode 100644 index 1406a97a..00000000 --- a/stdlib/kvlang/deepdive/layout/04-layout-pipeline.kv +++ /dev/null @@ -1,37 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/layout/04_layout_pipeline { - r#"# Layout Pipeline(layout 流水线) - -## kvlang 流水线 - -``` -源代码 → scanner(scan)→ []Token → parser(parse_code)→ ast::File - → lower(lower_func) 结构化控制流 → ScopeStmt + br/goto - → write_func AST → KV 指令树(/lib/·/[i,j]) -``` - -layout 产物写入 `/lib/` 后由 kvcpu 执行。 - -关键特征: -- **PC 是 KV 路径字符串**,不是整数 -- **指令存储在 KV 树**中,通过 `kv·get` 获取 -- **控制流块(ScopeStmt)**不建 extindex,由 Decode 拼接 scope 前缀 key 解析 - -## 与传统流水线的差异 - -Python(CPython):AST → 基本块(整数跳转偏移)→ .pyc 字节码 → ceval 线性递增 PC。 - -kvlang:无字节码数组,无整数 PC,控制流通过 KV 路径跳转;结构化控制流在 lower pass 降级为 ScopeStmt。 - -## 前端设计要点 - -- 块结构由消费 `LBrace`/`RBrace` Token 自然追踪,禁止 `strings.Count/Index` 做语法判断 -- 换行是语句分隔符(`Newline` token),`{ }` 负责块结构 -- `parser` 以 `tokens[]` + `pos` + `peek/advance/expect` 递归下降驱动 -- 错误收集不首错即止:`parser.errors: Vec` 累积全量诊断 - -## AST 类型标记——`quote` 字段 - -`Expr.quote` 区分字符串字面量和变量名(替代旧的 `"` 前缀 hack)。`flat()` 在 KV 传输层对字符串字面量加 `"` 前缀,数字字面量不加引号。 -"# -> /lib/kvlang/deepdive/layout/04_layout_pipeline -} diff --git a/stdlib/kvlang/deepdive/layout/05-diagnostic-output.kv b/stdlib/kvlang/deepdive/layout/05-diagnostic-output.kv deleted file mode 100644 index 75de20e7..00000000 --- a/stdlib/kvlang/deepdive/layout/05-diagnostic-output.kv +++ /dev/null @@ -1,26 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/layout/05_diagnostic_output { - r"# Diagnostic Output(诊断输出规范) - -## 诊断输出架构(Rust layout + C runtime) - -诊断输出分两层,格式约定一致:无时间戳、无 key=value,对齐 GCC/Go/Rust 编译器风格。 - -**Rust layout**:`Diagnostic.string()` 输出 `{kind}: {src}:{line}:{col}: {msg}`,kind 为 `error`/`warn`/`info`。`parser.errors: Vec` 累积全量诊断,不首错即止。 - -**C runtime(`logx.c`)**:`kvlangLogDebug/Info/Error` 以 `{exe}: ` 或 `{exe}: error: ` 前缀输出到 stderr,由 `LOG_LEVEL` 环境变量控制(默认 `warn`,可选 `debug`/`info`/`error`)。 - -## LOG_LEVEL 级别 - -| 级别 | layout 可见 | runtime 可见 | -|------|------------|-------------| -| `debug` | info/warn/error 全量 | debug/info/warn/error | -| `info` | info/warn/error | info/warn/error | -| `warn`(默认) | warn/error | warn/error | -| `error` | error | error | - -## 原则 - -诊断(layout 报错/运行时错误)走各层的诊断接口;stdout 结果(`print` 输出、格式化内容、命令成功状态)不加前缀直接输出。 -" -> /lib/kvlang/deepdive/layout/05_diagnostic_output -} diff --git a/stdlib/kvlang/deepdive/layout/06-layout-rwir.kv b/stdlib/kvlang/deepdive/layout/06-layout-rwir.kv deleted file mode 100644 index 161a5ede..00000000 --- a/stdlib/kvlang/deepdive/layout/06-layout-rwir.kv +++ /dev/null @@ -1,53 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/layout/06_layout_rwir { - r#"# Layoutrwir: 装载与执行 - -## lib 树与装载 - -`write_func` 将多个 `.kv` 文件 parse → lower → 写入 `/lib/`。每个 `lib name { }` 块形成一个 lib 节点,每个 lib 有且仅有一个 `init` 函数(init 体 + 顶层代码合并)。 - -**CLI 入口**: -- `kvlang ` → layout 后运行约定入口 `test` -- `kvlang -c ""` → layout 内存源码 → 运行 `init` -- `kvlang layout ` → 仅 layout,打印 `ENTRY=` - -**跨 lib 调用**:全路径 `/lib/{childlib}·{func}()`,无 `import` 关键字。 - -## `init` 函数 - -裸顶层代码自动封装为 `rwfunc init() -> () { }`。`lib name { }` 中各函数注册在 `/lib/·{func}`,无 lib 包裹的属匿名 lib(路径 `/lib/·{func}`)。源码副本写入 `/lib/·‥src`。 - -## XValue Kind - -`/lib/` 下槽位的 XValue kind: - -| Kind | 示例 | 写入者 | -|------|------|--------| -| `rwir` | `/lib/func/[1,0] = "+"` | `write_body`(指令槽) | -| `defrwfunc` | `/lib/func/[0,0]` | `write_func`(函数签名:`[nr\|nw][kindexp…]`) | -| `ptr` | `/lib/func/A = →[0,-1]` | `write_func`(命名参数→slot 指针) | - -帧类型判定:`func_frame_root` 沿帧树向上查找 `‥lib` 标记 → rwfunc 帧;无则为 scope 帧。 - -## `write_func`:函数布局 - -1. `funcDir = /lib/·/` -2. `[0,0]` → `defrwfunc` 签名(nr, nw, num_insts, kindexp 列表) -3. 读参 `p`: `funcDir/p` → `Ptr("[0,-j]")`;写参 `r`: `funcDir/r` → `Ptr("[0,+j]")` -4. `write_body(..., offset=1)` → 指令从 `[1,0]` 开始 - -**EntryPC**:`root/[1,0]`(函数签名占 row 0)。 - -## HandleCall:运行时调用 - -1. 读 `[0,0]`(defrwfunc)→ nr, nw -2. ExtIndex(frameRoot, funcDir)(帧→指令树) -3. 写 `‥returnpc`、`‥callpc`、`‥lib` -4. 读参绑入 `[0,-(i+1)]`,写参路径写入 `[0,+(i+1)]` -5. 返回 EntryPC = `[1,0]` - -## `=` 操作码 - -`a -> b` 编码为 `[s0,0]="="`(值拷贝)。函数调用 opcode 位为 `call`。opcode 槽位永远不放变量引用。 -"# -> /lib/kvlang/deepdive/layout/06_layout_rwir -} diff --git a/stdlib/kvlang/deepdive/layout/07-rwir-and-lib-data.kv b/stdlib/kvlang/deepdive/layout/07-rwir-and-lib-data.kv deleted file mode 100644 index 837bda41..00000000 --- a/stdlib/kvlang/deepdive/layout/07-rwir-and-lib-data.kv +++ /dev/null @@ -1,86 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/layout/07_rwir_and_lib_data { - r#"# rwir 与 lib 数据:注册布局 - -> 签名参数/返回值的 `type` 用签名类型表达式(并集 `A|B`、通配 `any`、高维 shape `[2,3]`、动态维 `?`),见 `grammar.bnf`。 - -## 三类 KV 内容 - -`/lib/` 下统一三类内容: - -| 类别 | 语义 | 有指令体? | KV Kind | 布局路径 | -|------|------|-----------|---------|---------| -| **rwfunc** | 用户函数 | ✅ 指令树 | `defrwfunc` | `/lib/·/` | -| **lib 数据** | 常量 | ❌ 单个 XValue | 值类型 | `/lib/` | -| **rwir 声明** | 扩展/用户声明 rwir | ❌ 仅签名 | `defrwir` | `/lib/` | - -## 铁律 - -- **三类内容同存 `/lib/`**:无独立 `/rwir/{runtime}/` 命名空间。 -- **`defrwir` 与 `defrwfunc` 同定义体格式**:`[nr:u16 LE][nw:u16 LE][kindexp 以 "\n" 连接]`,区别仅在 KV kind 与有无指令体子节点(`[i,j]`)。 -- **native rwir 不落盘**:`+`/`==`/`array`/`sqrt` 等内建在 C runtime `builtins[]`(`opcode → bi_fn`),不写 kvspace、运行时不回查。 - -## rwir:`/lib/` - -**扩展 rwir**(宿主注入,如 `numpy.add`)经 `kvlang_rwirextRegister(opcode, nr, nw, sig)` 写入 `/lib/`(kind=`defrwir`),写入前 `kvlang_rwirextTypeValid` 校验各参 kindexp。**用户 rwir 声明**(`rwir myop(A:int64) -> (C:int64)`)由 layout `write_rwir_decl` 写入同路径。 - -**native rwir** 数值多类型算子在 `builtins[]` 中融合为单条(`add` 一条覆盖 int8…float64),派发前 `strip_num_kind` 剥掉 `·` 前缀,输出 kind 由实参决定。 - -## lib 数据:`/lib/` - -`/lib/` 存单个 XValue 常量,任意 kvlang 代码以绝对路径读写: - -``` -/lib/math·Pi float64 3.141592653589793 -/lib/math·E float64 2.718281828459045 -``` - -```kv -lib math { - /lib/math·Pi = 3.141592653589793 - /lib/math·E = 2.718281828459045 -} -``` - -`lib` body 内的绝对路径赋值即写常量;kv 代码直接以绝对路径访问 `print(/lib/math·Pi)`。裸名(`Pi = 3.14`)是 `/vthread` 帧局部变量、跑完回收,不入 `/lib/`。 - -> 注:Go/oldhero 曾用 `//go:embed stdlib/*.kv` + 启动 layout+run 各 lib `init` 自举内置常量。当前 Rust layout + C runtime **不内置 stdlib 自举**;常量由用户/宿主显式 layout 写入 `/lib/`。 - -## 落盘流程(Rust layout + C runtime) - -| 步骤 | 内容 | -|------|------| -| 建目录 | `init_dirs`:`mkindex("/lib/")`、`mkindex("/vthread/")` | -| 写 rwfunc | `write_func` → `/lib/·/`:`[0,0]` 签名(defrwfunc)、参数 Ptr、`[i,j]` 指令(rwir,i≥1)、`‥src` 源码 | -| 写用户 rwir | `write_rwir_decl` → `/lib/`(kind=defrwir,无指令体) | -| 注册 native | runtime `builtins[]`,不落盘 | -| 注入扩展 rwir | `kvlang_rwirextRegister(opcode, nr, nw, sig)` → `/lib/`(kind=defrwir) | - -## 示例 - -宿主注入扩展 rwir(各参 kindexp 以 `\n` 连接): - -```c -kvlang_rwirextRegister(c, "numpy.add", 2, 1, "int64|float64\nint64|float64\nint64|float64"); -``` - -用户在 kvlang 源码声明 rwir(无体,仅签名)→ layout 落 `/lib/myop`: - -```kv -rwir myop(A:int64, B:int64) -> (C:int64) -``` - -## 实现要点(相关文件) - -| 文件 | 职责 | -|------|------| -| `layout/src/keytree.rs` | `LIB_ROOT`/`RWIR_ROOT` = `/lib`;`lib_func`/`rwir`/`lib_src` 路径构造 | -| `layout/src/code.rs` | `init_dirs`、`write_func`(rwfunc→`/lib`)、`write_rwir_decl`(rwir→`/lib/`) | -| `layout/src/kvkind.rs` | `new_rwfunc` / `new_rwir` 定义体编码(`nr`u16 + `nw`u16 + `\n` 连接 kindexp) | -| `runtime/src/keytree.c` | `kt_rwir(opcode)` = `/lib/` | -| `runtime/src/builtin.c` | native `builtins[]`(opcode→`bi_fn`,数字多类型融合)、`strip_num_kind` 派发 | -| `runtime/src/rwirext.c` | `kvlang_rwirextRegister(opcode, nr, nw, sig)` → `/lib/` | -| `runtime/src/kvcpu.c` | execute 循环 `load_def_reads` + `check_read_types` 内联类型匹配 | -| `runtime/src/type_expr.c` | `kvlang_rwirextTypeValid`(装载校验)+ `kvlang_rwirextTypeMatch`(运行时匹配) | -"# -> /lib/kvlang/deepdive/layout/07_rwir_and_lib_data -} diff --git a/stdlib/kvlang/deepdive/reference/01-how-to-design-a-language.kv b/stdlib/kvlang/deepdive/reference/01-how-to-design-a-language.kv deleted file mode 100644 index 8174d64b..00000000 --- a/stdlib/kvlang/deepdive/reference/01-how-to-design-a-language.kv +++ /dev/null @@ -1,121 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/reference/01_how_to_design_a_language { - r#"# reference篇-01: 如何设计编程语言 - -编程语言设计:核心准则、设计约束与系统性避坑规范 - ---- - -## 一、顶层设计核心原则:语言是自洽的取舍体系 - -语言设计的核心价值不在于特性堆砌,而在于贯穿全栈的统一设计哲学与权衡体系。所有底层模块必须遵循统一约束,不存在孤立设计。 - -必须统一对齐的核心维度: - -- 类型系统模型(强/弱类型、隐式转换规则、空值语义) -- 求值执行策略(eager/lazy、执行顺序、指令语义) -- 内存与状态模型(变量生命周期、作用域规则、状态存储方式) -- 控制流模型(跳转、调用、返回、异常、恢复语义) -- 错误与崩溃语义(故障恢复、状态一致性) -- 调试与可观测性模型(状态可见、可回溯、可快照) -- 版本与兼容策略 - -每项特性的收益、代价、替代方案、边界场景必须可完整答辩;语言设计是长期迭代工程。 - ---- - -## 二、语言创新的两层分级 - -### 1. 表层创新(增量改良) - -仅优化语法糖、关键字、API封装,不改动底层执行与语义模型。绝大多数自研脚本语言停留在该层级。 - -### 2. 深层创新(范式突破) - -重构底层执行范式、状态存储模型、控制流抽象、类型语义体系。典型例子:全新PC寻址模型、明文可持久化VM、源码即数据无独立中间层设计。 - -学术社区(PLDI/POPL)只认可深层语义与执行模型创新,表层语法优化不构成核心贡献。 - ---- - -## 三、可落地语言的硬性标准 - -合格的编程语言/VM系统必须满足: - -- 具备可运行的完整原型,而非文字构想 -- 配套成体系测试用例:基础语法、算法案例、边界用例、负面异常用例 -- 拥有明确的错误诊断与报错机制 -- 设计语义可形式化、可与主流工作横向对比 - ---- - -## 四、拒绝"全能杂糅设计" - -成熟语言遵循"有舍才有得",强行融合对立设计会导致语义冲突、维护成本爆炸。 - -高危杂糅陷阱: - -- 同时混用动态/静态类型规则,无统一类型约束 -- 同时兼容隐式类型转换与强类型校验 -- 混合多种内存管理模型(GC+手动内存+所有权无规范混用) -- 堆砌多种范式语法,无统一执行语义 - ---- - -## 五、可观测性与故障语义 - -顶级语言设计必须提前定义异常与崩溃体系,这是工业级系统与玩具项目的核心分水岭: - -- 程序崩溃后,执行状态是否可保留、可恢复、可重启 -- 运行时状态是否可观测、可遍历、可快照 -- 是否支持无侵入调试,无需附着进程、无需底层调试器 -- 状态持久化、指令回放、断点续算的底层语义 - -这是现代分布式、训练推理、长时运行系统的核心刚需,也是传统脚本语言缺失的关键能力。 - ---- - -## 六、学术级语言设计的四大评审标准 - -新语言/VM获得学术认可,必须完整满足: - -1. **明确的动机(Motivation)**:精准指出现有语言/VM无法解决的痛点与固有架构瓶颈 -2. **清晰的执行语义(Semantics)**:类型、求值、控制流、状态变换规则可精确定义,无模糊歧义 -3. **可复现的工程实现**:可运行原型、完整测试体系、性能与功能评估 -4. **充分的横向对比**:与现有PL、VM、IR体系明确差异,论证创新性与优势 - ---- - -## 七、语言架构设计的底层约束 - -语言顶层设计需要长程一致性、工程约束感知、系统性取舍: - -- **拒绝折中妥协**:主动做艰难取舍,而非兼容所有方案、回避冲突 -- **坚持范式原创**:不能依赖整合现有特性,必须内生全新执行范式与底层架构 -- **全局一致性优先**:所有语法、语义、底层机制必须服从同一套顶层设计规范 -- **完备的边界推理**:穷举极端case、嵌套语义、异常交互、并发冲突 -- **贴合工程落地约束**:考虑存储开销、序列化、调度、硬件适配、分布式一致性 -- **可量化论证Trade-off**:能清晰阐述每项设计的长期代价、适用场景、替代方案 - ---- - -## 八、工具与设计者的角色边界 - -自动化工具可用于:样板代码生成、文档撰写、测试用例辅助、语法草稿、代码重构。 - -顶层架构、执行模型、语义规范、核心取舍、体系一致性,必须由设计者独立完成与维护。 - ---- - -## 九、范式创新型VM语言的设计要点(以kvlang为例) - -区别于传统语法改良型语言,范式创新型VM语言的核心设计体现在: - -- 重构程序状态存储与PC寻址范式(kvlang以KV路径字符串作PC,而非整数计数器) -- 将代码与数据统一在一棵KV树,消除运行时独立内存模型与中间表示层的隔离 -- 内置全明文可观测、可持久化、支持崩溃恢复的原生语义 -- 通过扩展机制(rwir/rwext)实现算子融合;layout层只做检查与布局,runtime层只做解释执行 - -该类项目的核心价值在于重新定义程序执行与状态存储的底层范式。 -"# -> /lib/kvlang/deepdive/reference/01_how_to_design_a_language -} diff --git a/stdlib/kvlang/deepdive/runtime/01-type-system.kv b/stdlib/kvlang/deepdive/runtime/01-type-system.kv deleted file mode 100644 index de7e1b85..00000000 --- a/stdlib/kvlang/deepdive/runtime/01-type-system.kv +++ /dev/null @@ -1,136 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/runtime/01_type_system { - r#"# Type System(类型系统) - -## 类型系统 - -kvlang 是**严格类型语言**。所有变量、参数、返回值必须有确定的类型——不允许无类型变量,不允许运行时类型隐式改变。 - -### 数字类型:无 float/int,只有具体位宽类型 - -**kvlang 没有 `float`、没有 `int`。** 只存在携带具体位数的数字类型: - -| 类别 | 类型 | -|------|------| -| 有符号整数 | `int8` `int16` `int32` `int64` | -| 无符号整数 | `uint8` `uint16` `uint32` `uint64` | -| IEEE 754 浮点 | `float32` `float64` | - -**设计理由**:位数是类型契约的核心部分。`int64` 跨 kvspace-go → kvspace-c → kvregion shm → op-gpu tensor dtype 传播时,每一层都依赖精确的位数。`float` / `int` 在不同语言/平台映射到不同位数,破坏跨语言类型契约。 - -**实现约束**:XValue kindexpr 只接受上表所列 10 个精确名称。`"int"`、`"float"` 等短名在任何代码路径中均为非法 kind。 - -### 类型归属规则 - -| 归属 | 存放位置 | -|------|---------| -| **函数签名** | `/lib/·` — `defrwfunc` 值,kindexpr 列表 | -| **指令槽引用** | `/lib/·/[s0,s1]` — `rwir` 文本引用 | -| **运行时值** | `/vthread///` — 携带 kindexpr 的 XValue | - -**铁律**: -- `def` 签名中每个参数和返回值**必须声明类型**(`name:type`) -- 签名缺类型的 rwfunc **拒绝装载** -- 指令槽 `[s0,s1]` 是槽位描述符,类型信息在签名中 -- 运行时通过 kindexpr 标签自描述,无需查签名 -- 与五语言对齐:C/Rust/Go 必须声明——kvlang 选此阵营 - -### XValue 与 kindexpr - -XValue 是 TLV 编码的字节串,编解码由链接的 kvspace .so(`kvspaceTlvEncode`/`kvspaceDecodeHead`)负责。解码后的 head(`kvspaceHead_t`)包含: - -- `kindexpr`:NUL 终止字符串,格式 `[*|@][dims]base_kind` - - 可选前缀 `*`(ptr)或 `@`(扩展句柄) - - 可选 `[d,d,...]` 维度列表 - - 紧跟 base kind 字符串 -- `body_offset` / `body_len`:值数据在字节串中的位置 - -**kind 完整清单**: - -| kind 字符串 | 说明 | -|------------|------| -| `"int8"` `"int16"` `"int32"` `"int64"` | 有符号整数,小端编码 | -| `"uint8"` `"uint16"` `"uint32"` `"uint64"` | 无符号整数 | -| `"float32"` `"float64"` | IEEE 754 浮点 | -| `"bool"` | 1 字节:0=false, 1=true | -| `"char/utf32"` | 码点,4B×N,**默认字符串**,定宽(可索引)| -| `"char/utf8"` | UTF-8 字节串,1B×N,变宽(存储/交换,禁索引)| -| `"char/ascii"` | ASCII 字节串,1B×N,定宽(可索引)| -| `"object"` | 异构命名成员容器(类 struct/record),成员经 `·` 访问 | -| `"stringkeymap"` | map 容器,kindexpr `key·value`(如 `[]char/utf8·int64`) | -| `"scope"` | 词法作用域帧目录 | -| `"index"` | 通用目录索引(尾 `/` 的 key) | -| `"extindex"` | 扩展索引 | -| `"rwir"` | 指令槽文本引用 | -| `"defrwir"` | 扩展 rwir 定义(签名,存 `/lib/`) | -| `"rwfunc"` | rwir 文本中的函数引用 | -| `"defrwfunc"` | 用户函数定义(签名,存 `/lib/·`,kindexpr 列表) | - -kvkind 枚举即上表(源码 `layout/src/kvkind.rs`)。**None 不是 kvkind**——它是 kindexpr 为空的 XValue;**ptr 也不是 kvkind**——软链接以 kindexpr 前缀 `*` 表示、body 为目标 key 路径。 - -**字符家族(`char/<编码>`)**:定宽编码(`utf32` 4B、`ascii` 1B)支持 O(1) 码点索引;变宽编码 `utf8` **拒绝索引操作**,仅用于存储/交换。判定:`IsCharKind(kind) = HasPrefix(kind, "char/")`。字符串字面量默认落 `char/utf32`。 - -**kind 铁律——禁止别名**。`"int"`、`"float"` 等短名在任何代码路径中均非法。 - -### 变量名即地址,未赋值即 None - -kvlang 的每个变量名即 kvspace 中的相对地址。声明变量即在当前帧分配一个 KV slot(如 `/vthread///x`): - -- **有变量名 ⇒ 有地址** -- **有地址 ⇒ 有 XValue** -- **未赋值 ⇒ XValue 为 None**(kindexpr 为空,`None()` 编码) - -None 参与算术/比较直接 TypeError,迫使代码显式初始化。 - -**访问器分级**: -- **宽容读取器**:`AsInt64()` 按 kind 实际宽度解码 + 符号扩展,`AsUint64()` 同理。算术/比较走宽容读取器。 -- **精确访问器**:`Int8()` `Float32()` 等严格校验 kind,不匹配返回零值。 - -### 数字类型算子(构造器兼转换器) - -十个数字类型算子,**既是构造器也是转换器**: - -```kv -f = float32(3) # kind=float32 -i <- int8(0.1) # 0(float→int 截断向零) -int8(300) -> w # 44(窄化 = 补码回绕) -``` - -| 语义 | 对齐阵营 | -|------|---------| -| float→int 截断向零 | 五语言一致 | -| 窄化 = 补码回绕(`uint8(-1)`=255) | Go / Rust `as` / C | -| None 输入直接 TypeError | strict None | - -### 字符编码转换 - -字符家族三个 kind **既是构造器也是转换器**: - -```kv -s:char/utf8 = "hello" -t <- char/utf32(s) # 转定宽(可索引) -u <- char/ascii(t) # 拒非 ASCII 码点 → TypeError -``` - -### 数值运算域 - -1. **int ∧ int → 原生 int64 运算**,溢出 = 补码回绕 -2. **任一侧 float → float64 提升**;混合比较 `3 == 3.0` 为 true -3. **None 参与算术直接 TypeError** - -### Bool 铁律 - -**bool 只能是 `true`/`false`,禁止一切隐式 coerce。** - -| 输入 | kvlang | -|------|--------| -| `true`/`false` | ✅ | -| `0`/`1` 整数 | panic — 必须写 `!= 0` | -| 非空字符串 | panic — 必须写 `!= ""` | -| None | panic | - -隐式 coerce 会使控制流条件取决于运行时 kind,agent 无法静态判定控制流走向。强制显式比较使 br 指令的 cond 槽永远是 bool。 - -**实现**:`kvlangXvalueAsBool`(`xvalue.c`)仅接受 `kind=="bool"`,其他 kind 直接 panic。 -"# -> /lib/kvlang/deepdive/runtime/01_type_system -} diff --git a/stdlib/kvlang/deepdive/runtime/02-member-access-and-data-structures.kv b/stdlib/kvlang/deepdive/runtime/02-member-access-and-data-structures.kv deleted file mode 100644 index 82abe618..00000000 --- a/stdlib/kvlang/deepdive/runtime/02-member-access-and-data-structures.kv +++ /dev/null @@ -1,77 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/runtime/02_member_access_and_data_structures { - r#"# Member Access and Data Structures(成员访问与数据结构) - -## 两个索引符号:`[]` 与 `·` - -kvlang 用两个符号访问复合数据,语义严格分离: - -| 符号 | 目标 | 说明 | -|------|------|------| -| `a[i]` | **数组下标** | `[]T` 数组的元素访问,`i` 为整数 | -| `base·name` | **容器成员 / lib 函数** | `·`(U+00B7 中点)是成员访问符 | - -`·` 之所以取代旧的 `.`:`.` 被释放给小数字面量与浮点 key。成员分隔符在 `layout/src/keytree.rs` 定义为 `MEMBER_SEP = "·"`,成员键 = `base·name`(`keytree::member`)。 - -**铁律**:容器成员一律走 `·`(或 `kv·get`/`kv·set`),**绝不用 `[]`**;`[]` 只索引 `[]T` 数组。 - -## 容器类型:object 与 stringkeymap - -kvlang 没有 `dict`。复合命名数据只有两种 kind: - -- **object**:异构命名成员,类 struct / record。成员名固定写在源码里。 -- **stringkeymap**(`kvkind.rs` KIND_MAP):一等 map 类型,kindexpr 用 `·` 分隔 `key·value`——`[]char/utf8·int64` 即 `map[string]int64`;value 可递归嵌套(`[]char/utf8·[]char/utf8·int64`)。 - -容器 = 值 + memindex,`·` 是唯一的 memindex 标记:`base` 持容器值,`base·name` 是其成员,成员在 kvspace 中作为独立 key 落库。 - -### 构造与写成员 - -```kv -rec:object = {} # 空容器字面量必须带类型标注(layout 生成 obj 构造指令) -rec·name = "kv" # 成员写:= 值拷贝,写槽是完整成员键 rec·name -rec·ver = 1 -m:[]char/utf8·int64 = {} # 空 map 同样须标注 -m·a = 10 # 命名成员写 -kv·set(m, "b", 20) -> _ # 动态键写 -``` - -### 读成员 - -```kv -rec·ver -> v # 命名成员读,直接取 key rec·ver -kv·get(m, "a") -> x # 动态键读;缺失返回 None -k = "a"; m·*k -> y # 动态成员名:k 的值作为成员名(读 m·a) -``` - -`·*k` 动态成员语法用于**局部**容器变量。 - -## 指针解引用:按值优先,按名回退 - -`base·name`(读写同规则)中 base 的解析: - -1. **按值解引用**:base 持有非空字符串值(路径指针)→ 成员键 = `值(base)·name`。如 `"/n0" -> p` 后 `p·next` 解析为 `/n0·next`。 -2. **按名回退**:base 无值(或非字符串)→ 成员键 = `解析(base)·name`,`解析()` 帧感知:裸名 → `帧根/base`,`/` 开头 → 直通。 - -该规则让"局部结构体"与"指针解引用"共用一套语法:结构体的 base 保持无值(按名),指针变量存路径字符串(触发按值)。 - -```kv -/n1:object = {} # 跨函数共享数据放绝对路径 -/n1·val = 1; /n1·next = "/n2" -"/n1" -> p; p·val -> v # 引号=路径串(指针),按值解引用 -``` - -## 数组元素读写(compact 形态) - -数组有两种存储形态:**compact**(`[...]` 字面量,元素连续打包进单个 XValue,head kind=元素类型)与 **stringkeymap**(`{...}` 字面量,散 key,每元素落 `base·i` 独立子 key、head kind=`stringkeymap`)。下标 `a[i]` / `xv·at` / `xv·set` 用于 **compact 形态**;stringkeymap 形态用 `for-in` / `kv·get` 遍历访问。compact 形态: - -```kv -a:[]int64 = [7, 2, 9] -a[1] -> e # 2(下标读,layout desugar 为 kv·get) -a[2] = 99 # 下标写,a = [7, 2, 99] -xv·at(a, 1) -> e # 显式读,等价 -xv·set(a, 2, 99) -> a # 显式写,等价 -``` - -下标写用赋值式 `a[i] = v`;`->` 形式的写槽不能带下标(`v -> a[i]` 被 layout 拒绝),改用 `a[i] = v` 或 `xv·set`。 -"# -> /lib/kvlang/deepdive/runtime/02_member_access_and_data_structures -} diff --git a/stdlib/kvlang/deepdive/runtime/03-debugging-and-observability.kv b/stdlib/kvlang/deepdive/runtime/03-debugging-and-observability.kv deleted file mode 100644 index 64bf110b..00000000 --- a/stdlib/kvlang/deepdive/runtime/03-debugging-and-observability.kv +++ /dev/null @@ -1,99 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/runtime/03_debugging_and_observability { - r#"# Debugging and Observability(调试与可观测性) - -## 调试模型:暂停即落盘,观察即读 kvspace - -kvlang 没有独立调试器进程,也没有 `--debug` 开关。调试就是**让 vthread 停在某处、把整个执行状态留在 kvspace,再用 kvspace 命令静态读取**。因为 PC 和栈帧本就是 kvspace 里的 KV 路径与值,暂停后进程可以直接退出——状态不丢,可崩溃恢复。 - -### 暂停:`debugger()` - -`debugger()` 是 native builtin,等价于把当前 vthread 的 `‥status` 置 `paused`(`vthread·setstatus("paused")` 同义)。execute 循环只在 `init`/`running`/`wait` 状态下继续,读到 `paused` 即停机,进程正常退出。 - -**关键:暂停时 PC 已前进到 `debugger()` 的下一条指令**——恢复后从下一条继续,不会重复执行断点。 - -```kv -rwfunc g() -> (s:int64) { debugger(); 30 -> s } -rwfunc f() -> (s:int64) { g() -> s } -rwfunc test() -> () { f() -> r; println(r) } # 70 -``` - -```bash -kvlang prog.kv # 跑到 debugger() 暂停,进程退出;输出 vthread 号(如 vthread 3) -``` - -### 观察 PC 与状态 - -vthread 的运行时保留字段以 `‥`(U+2025)为前缀,不进普通目录索引,故用 `kvspace get` 直接按 key 读,不用 `tree`: - -```bash -kvspace get /vthread/3/‥status # char/utf8:paused -kvspace get /vthread/3/‥pc # char/utf8:/vthread/3/[1,0]/[1,0]/[2,0] -``` - -### PC 字符串即调用栈 - -PC = `/vthread/3/[1,0]/[1,0]/[2,0]`,逐段解读(坐标 `[s0,s1]`:s0=指令行,行 0 为签名、指令从 1 起;s1=参数轴,0 为 opcode): - -| 路径段 | 含义 | -|--------|------| -| `/vthread/3/` | 根帧(test) | -| `[1,0]/` | test 指令行 1(`f() -> r`)发起的调用 → f 帧 | -| `[1,0]/` | f 指令行 1(`g() -> s`)发起的调用 → g 帧 | -| `[2,0]` | g 指令行 2(`30 -> s`)——`debugger()` 在行 1,PC 已前进到此 | - -嵌套帧路径即调用链,无需另存栈结构。单帧程序 PC 形如 `/vthread/3/[3,0]`。 - -### 栈帧字段 - -每个调用帧(`/vthread//[s0,s1]/...`)的运行时字段,均 `‥` 前缀: - -| 字段 | 含义 | -|------|------| -| `‥lib` | 软链接到 `/lib/·`——指令树零拷贝共享,全帧只存一份 | -| `‥callpc` | 发起本帧调用的指令 PC | -| `‥returnpc` | 返回后继续的 PC | -| `‥ro` | 只读读参绑定 | -| `‥pc` / `‥status` | 仅根帧持有,记录整个 vthread 的当前 PC 与状态 | - -无 `rootfunc` 字段,也无 `.debugger` JSON 控制协议——这些是上一代设计的残留。 - -```bash -kvspace head /vthread/3/[1,0]/‥lib # 子帧 f 的指令树软链 -kvspace head /vthread/3/[1,0]/[1,0]/[2,0] # 当前指令槽 -``` - -### 恢复执行 - -暂停状态已落盘,恢复只需把状态改回 `running` 再驱动该 vid: - -```bash -kvspace set /vthread/3/‥status string:running -kvlang run 3 # 从持久化的 ‥pc 继续;子进程/重启后亦可,PC 可崩溃恢复 -``` - -`kvlang run ` 接 vid(整数句柄,非函数名),从帧里的 `‥pc` 续跑到结束。 - -### 观察源码:`kvlang dump` - -`/lib` 顶层无目录索引,`tree /lib` 为空。查看已 layout 的函数用 `dump`——它把 `/lib` 子树逆向重建为可运行 kvlang,并以 `#` 注释标出每条指令的 `[s0,s1]` 槽位: - -```bash -kvlang dump prog.kv # 先 layout prog.kv,再把整个 /lib dump 为源码 -kvlang dump prog.kv /lib/g # 只 dump 某函数子树 -``` - -签名槽 `[0,0]`(kind=`defrwfunc`)、读参绑定 `[0,-1]..`、写参绑定 `[0,1]..`、指令从行 1 起——dump 输出与此坐标系一一对应,是核对 layout 结果与定位坐标 bug 的首选透视镜。 - -### 完整调试闭环 - -``` -kvlang prog.kv # 1. debugger() 暂停,进程退出 -kvspace get /vthread//‥pc / ‥status # 2. 读 PC、状态 -kvspace head /vthread//<帧路径> # 3. 读栈帧变量槽 -kvlang dump prog.kv # 4. 对照源码与槽位 -kvspace set /vthread//‥status string:running -kvlang run # 5. 恢复到结束 -``` -"# -> /lib/kvlang/deepdive/runtime/03_debugging_and_observability -} diff --git a/stdlib/kvlang/deepdive/runtime/04-function-call-builtin.kv b/stdlib/kvlang/deepdive/runtime/04-function-call-builtin.kv deleted file mode 100644 index 6af05ee7..00000000 --- a/stdlib/kvlang/deepdive/runtime/04-function-call-builtin.kv +++ /dev/null @@ -1,80 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/runtime/04_function_call_builtin { - r#"# 函数调用(layout → runtime) - -## 三种调用形态,收敛到同一 KV key - -用户函数的调用有三种源码形态,最终都解析为 `/lib/·`: - -| 源码形态 | 示例 | 多级 pkg? | -|---------|------|-----------| -| 全路径 | `/lib/a/b·add(3, 4) -> s` | ✅ 段用 `/` | -| 限定名 | `math·sum(3, 4) -> s` / `a/b·add(3, 4) -> s` | ✅ | -| 裸名 | `sum(3, 4) -> s` | 同 pkg 内 | - -`·`(U+00B7)是成员/包分隔符(`const.h` `MEMBER_SEP`);`/` 是路径段分隔符。多级 pkg 的段用 `/`,末段用 `·` 接函数名。 - -## Scanner(layout/src/scanner.rs) - -- `·` → `Kind::Dot`:成员/包限定符。 -- `/` 三态:`//` 行注释;后跟路径起始字符 → **绝对路径字面量**(`Literal`,其中 `·` 与 `.` 作路径字符读入,不再切分);否则 **除法算子**。因此 `/lib/a/b·add` 是单个 Literal token,`a/b·add` 中 `/` 也并入标识符。 - -opcode 生成:`math·sum` → opcode `"math·sum"`;`/lib/a/b·add` → 原样;裸名 `sum` → `"sum"`。 - -## Execute 分发(runtime kvcpu.c) - -派发优先级: - -1. `op_is_control` — call / return / br / goto -2. `kvlangBuiltinIsNative` — native 算子(静态注册表) -3. `is_copy_op` — opcode 为 `=` 的值拷贝 -4. `isothersrwir` — `/lib/` kind=`defrwir`(扩展 rwir) -5. 否则 → 用户 rwfunc:opcode 移到 `reads[0]`,改写为 call 指令 - -## HandleCall:函数名 → KV key(kvcpu.c `handle_call`) - -``` -if HasPrefix(fn, "/lib/"): 剥 "/lib/",rest 里 rfind("·") → pkg, name -elif fn 含 "·": rfind("·") → pkg, name -else (裸名,无 /lib/ 无 ·): - 同 pkg 优先——从帧的 ‥lib 槽取调用者 pkg, - 补成 /lib/·(lib a/b/math 内 sum → /lib/a/b/math·sum), - 否则退回 /lib/ -funcKey = "/lib/" + pkg + "·" + name (pkg 空时 "/lib/" + name) -``` - -所有形态收敛到同一 key: - -| 源码 | KV key | -|------|--------| -| `/lib/a/b·add(3,4)` | `/lib/a/b·add` | -| `a/b·add(3,4)` | `/lib/a/b·add` | -| `math·sum(3,4)` | `/lib/math·sum` | - -查找失败(`[0,0]` 签名槽为 None):`NameError: rwfunc not found: `(或 `rwir 未注册/签名不匹配`、`不是 rwfunc (kind=…)`)。 - -## Resolve:Ptr 链变量解析(kvcpu.c `resolve_read_path`) - -调用后指令的读写槽引用走 Ptr 链解析: - -``` -1. 绝对路径 → 直取 -2. 由 PC 定帧根 -3. 命名 Ptr key → Ptr 目标(参数地址)→ 值(最多三跳) -4. fallback:帧根/名 → 局部变量 -``` - -`·rparam`/`·wparam` 重定向表已移除,改由 Ptr 链路径别名实现零拷贝跨帧传参。 - -## native builtin 与 kind - -native 算子按类别注册于 `runtime/src/builtin*.c`(无独立包目录): - -- 算术/比较/逻辑:`add sub mul div mod ... eq lt gt ... and or not`(源码 `+ - × ÷ % == < …` 映射到这些 opcode) -- 容器/数组:`obj`、`map`、`array` -- 类型构造兼转换:`int8..uint64 float32 float64 bool char/utf8 char/utf32 char/ascii` -- 带 `·` 的方法:`string·* xv·* kv·* ndarray·* array·* vthread·* time·* random·intn`、`debugger`、`print`/`println` - -kind 的字节宽由 `kvlangXvalueElemSize(kind)` 决定(char/utf8=1,int32/float32=4,int64/float64=8 …),数组与字符串的元素访问据此零拷贝定位。 -"# -> /lib/kvlang/deepdive/runtime/04_function_call_builtin -} diff --git a/stdlib/kvlang/deepdive/runtime/05-rwirext-extension-runtime.kv b/stdlib/kvlang/deepdive/runtime/05-rwirext-extension-runtime.kv deleted file mode 100644 index c20f0668..00000000 --- a/stdlib/kvlang/deepdive/runtime/05-rwirext-extension-runtime.kv +++ /dev/null @@ -1,142 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/runtime/05_rwirext_extension_runtime { - r#"# runtime篇-05: rwirext 扩展运行时 - -> 状态:与实现同步。代码:`runtime/src/rwirext.c`(C 扩展 ABI)、`runtime-rwirext_example/rust/term`(Rust term)、`runtime-rwirext_example/go/json`(Go json)。C runtime 侧实现见 [[runtime篇-06-C运行时与后端抽象]]。 - -## 一、核心模型 - -kvlang runtime 是**中央调度 runtime**,默认执行全部 native rwir / rwfunc / 控制流。某些 rwir(tensor 计算、LLM 调用、agent 循环)不由它求值,而是交给**扩展运行时**。 - -**扩展运行时 = 只解析己方 opcode 的迷你 kvcpu**:从中央 runtime 拿到 PC,连续执行己方 rwir,直到遇到非己方指令,把最终 PC 交还中央 runtime。 - -| 类别 | rwir 示例 | 扩展运行时 | -|------|----------|-----------| -| 计算委托 | `tensor·matmul(a,b) -> c` | op-gpu | -| API 封装 | `llm·chat(prompt) -> resp` | llm worker | -| Agent 封装 | `agent·search(q) -> r` | sub-agent | - -**核心公理**: - -1. **rwir 自描述**:opcode + 读写参已含执行所需全部信息。 -2. **注册即签名**:扩展运行时写 `/lib/`(kind=`defrwir`,含签名),不再有 `/sys/`。 -3. **批量执行**:扩展运行时连续执行己方 rwir 到非己方指令,把最终 PC 写回 `/vthread//pc`。 -4. **被动消费**:扩展运行时只监控己方 `/lib//vids` 队列。 - -## 二、注册与发现 - -### 签名注册 - -扩展运行时把己方每个 opcode 的签名写到 `/lib/`(kind=`defrwir`,空函数体,仅签名): - -定义体格式 `[nr:u16 LE][nw:u16 LE][读参+写参 kindexpr,"\n" 连接]`(详见 [[runtime篇-07-签名类型表达式]] §八): - -``` -/lib/json·to → defrwir(nr=1, nw=1, "char/utf8\n[]char/utf8") -/lib/print → defrwir(nr=1, nw=0, "any...") -``` - -**与 native builtin 的区分**:native 签名不落盘——内建在 C 注册表 `builtins[]`(`opcode → bi_fn`),直接查表;扩展签名落 `/lib/`(kind=`defrwir`),C runtime 通过 `isothersrwir` 识别:读 `/lib/` 的 kind,等于 `defrwir` 即为扩展 rwir。 - -### 全局标记 - -`kvlang_rwirextRegister` 标记裸 opcode(如 `print`/`input`)在 layout 阶段不被补 `pkg·` 前缀。带 `·` 的限定 opcode(`json·to`)本就不被补前缀。 - -term 在**进程启动时**注册全局:与中央 runtime 同进程,`vet`/`format` 也会 layout 用户代码,若不在启动期注册,裸 opcode 会被补 `pkg·` 前缀。 - -### 幂等重注册 - -`kvlang_rwirextRegister` 幂等。若 vids 队列已存在则跳过——否则重复注册时 Set 经路径穿透会把首队列改成自指 Ptr,令 `resolve_path` 死循环(共享 redis 时的挂起根因)。 - -## 三、handoff 协议 - -### 键布局 - -``` -/lib//vids/ = pc # 中央 runtime 写,扩展运行时消费(strkeymap 队列) -``` - -第一个 rwir 的 vids 是真实 strkeymap;后续同 opcode 的 vids 是 Ptr 指向第一个(共享同一队列)。 - -### 时序 - -``` -kvlang runtime(C) 扩展运行时 -───────────────── ────────── -1. isothersrwir(opcode) 命中 -2. handoff_external_rwir: - Set /lib//vids/=pc - 3. 发现队列中的 vtid - RunSeq(pc):逐条执行己方 rwir 到非己方指令 - WriteFinalPC:Set /vthread//pc = finalPC - Del /lib//vids/ -4. Watch 直到 vtid 从队列消失(30s 超时) -5. 继续(PC 已由扩展运行时写回) -``` - -中央 runtime **不推进 PC**——扩展运行时批量执行后已写回最终 PC,中央 runtime 直接读新 PC 继续。 - -### RunSeq(顺序执行) - -扩展运行时从 PC 起逐条解码,只要 opcode ∈ 己方集合就执行,遇第一条非己方指令即停。**顺序执行,非并行 batch**:ext rwir 之间存在顺序依赖。 - -## 四、kvcpu 集成(C dispatch 链) - -`kvcpu.c:kvlangRuntimeExecuteVthread` 的 dispatch 链: - -``` -1. op_is_control → handle_control (call/return/br/goto) -2. kvlangBuiltinIsNative → kvlangBuiltinNative (~50 native 算子,静态注册表) -3. is_copy_op → kvlangBuiltinExecuteCopy (= 赋值) -4. isothersrwir → handoff_external_rwir (/lib/ kind=defrwir) -5. else → 重写为 call(用户函数) -``` - -`isothersrwir(kv, opcode)`:`opcode[0]=='/' → false`;否则读 `/lib/`,kind==`defrwir` → true。 -`handoff_external_rwir`:Set 共享队列、Watch 直到消费完成(30s 超时),不推进 PC。 - -Rust runtime(runtime-rs)中,**就地 rwir**直接在 `run_vid` 驱动循环里派发(`rwir::is_inproc(op)` 命中 → `rwir::dispatch`),无 handoff、无 watch、与中央 runtime 同进程。当前就地集合(`rwir/mod.rs::is_inproc`): - -``` -term print / println / cerr / input -json json·to / json·from -http http·call -layout kvlanglayout·vet / ·format / ·layout / ·dump -networld networld/proc·exec - networld/fs·size / ·read / ·write / ·append / ·list / ·del / ·mkdir / ·exists -``` - -非就地 rwir(如 numpy tensor 计算)经 `kvlang_rwirextHandoff` 移交独立进程扩展。**注意**:就地与独立进程只是**执行位置**之别,职责线一致——两者都只经 `kvlang_rwirext*` 解析参数、只经 `kvspace*` 正典 ABI 读写与编解码(详见 [[runtime篇-06-C运行时与后端抽象]] §七)。 - -## 五、标杆:term 与 json - -| | term | json | -|--|------|------| -| op | print/println/cerr/input | json·to/json·from | -| 进程 | 与中央 runtime 同进程 | 独立进程 | -| opcode | 裸名(启动期注册全局) | 带 `·` 限定(`json·to`) | - -两者结构统一:`kvlang_rwirextRegister` + `Serve` 循环 + `exec` 分发。livebyte/op-gpu 扩展照此模板。 - -## 六、与 /ext/ 拓扑的关系 - -| 目录 | 回答 | 内容 | -|------|------|------| -| `/lib/` | 有什么 op(签名) | `defrwir` 签名(读参/写参/类型) | -| `/ext/` | 在哪、怎么执行(拓扑) | 存储/计算/通信注册 | - -无状态扩展运行时(term/json)只写 `/lib/`;有状态扩展引擎(op-gpu/heap-plat)写 `/lib/` + `/ext/`。handoff 协议一致,后者多一次 `/ext/` 查表定位引擎。 - -## 七、扩展的职责与不可越界 - -扩展回答的唯一问题是「**算什么、怎么算**」——tensor 怎么乘、LLM 怎么调、JSON 怎么按类型分发写回子树。它**不回答**「值长什么样、指令怎么解码、类型怎么校验」——这些是 runtime-c 的值语义,扩展只**调用**、不**复刻**。 - -扩展只有三个合法动作,一步都不许多做: - -1. **拿 PC**:从 handoff 队列 / drive 循环取当前 PC,跑己方 rwir,`NextPc` 求后继。 -2. **解析参数**:**只经** `kvlang_rwirext*`(`Params` / `ResolveRead` / `ResolveReadPath` / `ResolveWrite`)。不自行解析 rwir 槽位。 -3. **读写 kvspace**:**只经** `kvspace*` 正典 ABI(`Get` 借用读、`WriteInPlace`/`WriteNewPlace` 写、`TlvEncode`/`DecodeHead` 编解码)。**绝不自造 kindexpr 构造器、绝不手写 TLV/head。** - -**曾越界、已拉回**:runtime-rs 一度有 Rust 版 `build_kindexpr`(与 `frontend.c` 逐字节等价)——对编解码器的复刻,违反职责线,**已删**,编码统一走 `kvspaceTlvEncode`。唯一保留的例外是 json 反序列化里的 kindexpr 串**解析器**(`json.rs::parse_kindexpr`):kvspace 只导出编码器不导出串解析器,而「按类型/形状把外部文本分发写回」是扩展**自身的反序列化职责**,故留在扩展侧,且只读 head、不造 head。runtime-c 侧的权威边界见 [[runtime篇-06-C运行时与后端抽象]] §七。 -"# -> /lib/kvlang/deepdive/runtime/05_rwirext_extension_runtime -} diff --git a/stdlib/kvlang/deepdive/runtime/06-c-runtime-and-backend-abstraction.kv b/stdlib/kvlang/deepdive/runtime/06-c-runtime-and-backend-abstraction.kv deleted file mode 100644 index c9ae8418..00000000 --- a/stdlib/kvlang/deepdive/runtime/06-c-runtime-and-backend-abstraction.kv +++ /dev/null @@ -1,102 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/runtime/06_c_runtime_and_backend_abstraction { - r#"# runtime篇-06: C 运行时与后端抽象 - -> 状态:与实现同步。代码:`kvlang/runtime/`(纯 C)、`kvlang/runtime-rwirext_example/rust/term/`、`kvlang/runtime-rwirext_example/go/json/`、`kvlang/runtime-rwirext_example/py/numpy/`、`array2d/kvspace-c/`(SHM)、`array2d/kvspace-durable/`(fs/redis)。 - -## 一、小 runtime 大扩展 - -kvlang 是**小 runtime 大扩展**架构。中央 runtime 只实现最核心的 execute 大循环(fetch-decode-execute、call/return/br/goto、native rwir 派发、`=` 拷贝、用户函数调用),其余能力(tensor 计算、LLM、agent、print/println/cerr)全部交给**扩展 runtime**。 - -扩展 runtime 从中央 runtime 拿到 PC,连续执行己方 rwir 到非己方指令,再把最终 PC 交还。op-gpu 扩展收到 handoff 后连续跑完整段 tensor 计算,不必每算一个 op 就回报中央。 - -## 二、C 运行时(纯 C + extern "C" ABI) - -`kvlang/runtime/` 是通用嵌入式执行器,嵌入到 rwirext 项目(op-gpu、agent、livebyte)。 - -**两组对外 C ABI**: -- `kvlang_runtime.h`(`kvlangRuntimeConnect/Disconnect/Execute/Bootstrap/...`):核心执行器 -- `kvlang_rwirext.h`:扩展 runtime 语义,**只暴露 kvspace 不提供的部分**。KV 存取(connect/get/write/del/list/tlv)**不在此**——扩展宿主自己连 kvspace ABI,把 kvspace 句柄(`void *`)传进带句柄的 rwirext 函数。 - -`kvlang_rwirext.h` 现有 9 个符号:`Register`(写 `/lib/` 签名)、`Handoff`(把非己方 rwir 移交外部执行器)、`NextPc`(纯函数,下一条 PC)、`Params`(opcode + 读/写参名,\n 分隔)、`ResolveRead`/`ResolveReadPath`/`ResolveWrite`(读/写参解析,`ResolveReadPath` 供零拷贝读整块 ndarray)、`KindexprValid`/`KindexprMatch`(签名类型表达式校验与值判定)。**print/println/cerr 不在此**——term 扩展与中央 runtime 同进程,在 drive 循环里就地 `ResolveRead` + display,直写宿主 stdout/stderr,无专用打印 ABI。 - -**模块**(内部实现统一 `kvlang`/`kvspace` 前缀):`xvalue.c`(TLV 编解码)、`kv.c`(kvspace ABI 封装)、`keytree.c`(路径构造)、`rwir.c`(PC 解析 + Decode)、`vthread.c`、`builtin.c`(~50 native 算子 + 静态注册表)、`kvcpu.c`(Execute 循环)、`runtime.c`(ABI 层)、`rwirext.c`(扩展 ABI,含 `isothersrwir`)。 - -**注册表**:静态数组 + 线性查找(C 版,对应 Go 原 `map[string]nativeRwir`)。 - -**print/println/cerr 是扩展 rwir**(不在 builtin),在 `/lib/`(kind=`defrwir`)注册,dispatch 经 `isothersrwir` 命中。执行分两种模式: -- **嵌入式**(`KVMODE_RETURN`):runtime-rwirext 的 Rust `term` 与 runtime 同进程,execute 循环经 `out_pc` 把控制交回扩展,扩展直接写宿主进程 stdout/stderr,零轮询。 -- **handoff**:`handoff_external_rwir` 写 `/lib//vids/`,watch 消费完成(30s 超时),供独立进程扩展(Go json、Py numpy)。 - -## 三、kvspace 统一 C ABI(去特化) - -layout 与 runtime **都不对 kvspace 做特化处理**:不 dlopen、不双后端、不解析 scheme。二者只走一套 `kvspace*` 兼容 C ABI(无下划线 camelCase): - -``` -kvspaceConnect / Close -kvspaceGet // 借用读:*out 指向后端常驻/回收空间,调用方不得 free -kvspaceWriteInPlace // key 已存在且 body_len 相等 → 返回原 box body 偏移指针 -kvspaceWriteNewPlace // 否则按 (kindexpr, body_len) 分配新 box、写 head,返回 body 指针 -kvspaceListLen / ListAt / Del / DelTree / Cp / CpTree -kvspaceMkindex / MkindexExt / RmindexExt / Watch -kvspaceTlvEncode / TlvEncodeMode / DecodeHead -kvspaceNewPtr / NewChar / NewBool / NewInt64 / NewFloat64 -``` - -**零拷贝写读模型**:读走 `kvspaceGet`——返回后端常驻内存的**借用偏移指针**,调用方只读、**不 free**(旧 `kvspaceBytesFree` 已删)。写不再有「先 encode 整条 TLV 再交后端」的 `kvspaceSet`(已删):改为向后端**要一个可写 body 偏移指针**再直接写字节——`kvspaceWriteInPlace`(key 已存在且 body 尺寸不变,原 box 就地改)与 `kvspaceWriteNewPlace`(新建 / 换 kind / 换尺寸,分配新 box 并写 head)两条原语,各自唯一、无中转 buffer。前缀枚举同样不再一次性返回整段名单缓冲(旧 `kvspaceList` 已删,连同批量读的 `kvspaceGetBatch`):`kvspaceListLen` 只返子项计数、无缓冲无需释放,`kvspaceListAt(idx)` 借用回收缓冲逐项取名——遍历即「先 ListLen 定计数、再逐 idx ListAt」,与借用读模型一致、绝不外泄大块名单。 - -**后端由链接决定**:`KVLANG_KVSPACE_LIB` 环境变量在**构建期**选择链接哪个 kvspace 库(`kvspace-c` 或 `kvspace_durable`),两者导出同一 ABI。runtime 的 `kvlangKv_t` 只保留 `void *h`,代码零后端分支。 - -**kvspace-c 补齐 ABI**:`kvspace-c/src/durable_abi.c` 导出完整符号(内部复用 `kvspaceShm*` + `kvspaceXvalue*`),其中 `Watch`(轮询到值==target 或超时)为补齐的手工实现。 - -## 四、三个后端 - -| 后端 | 库 | 存储 | 场景 | -|------|-----|------|------| -| shm | kvspace-c(C) | ART 树 + slotsboxmalloc + file-backed mmap | 高性能(训练/推理) | -| redis | kvspace-durable(Rust) | Redis | 基线验证 | -| fs | kvspace-durable(Rust) | 文件系统 | 本地持久化 | - -`conn(dsn)` 按 scheme 选后端(`shm://`、`redis://`、`fs://`),DSN 是唯一的后端区分。 - -## 五、numpy 扩展(零拷贝 buffer 在 kvspace) - -`runtime-rwirext_example/py/numpy/` 是第一个 tensor 类 rwirext(CPU 侧,为 op-gpu 铺垫)。tensor 本体较大,**走 kvspace-c SHM**(redis/fs 有 I/O 拷贝)。 - -**零拷贝核心**(`numpy.py` 的 `Engine.view`):tensor 存 kvspace 为 XValue 定长数组(`float64`/`int64` 等)。`view(key)` 经 `kvspaceShmGet` 拿 TLV 指针,`kvspaceDecodeHead` 解出 `kvspaceHead_t` 定位 body 段,用 `numpy.ctypeslib.as_array` 在该地址建 ndarray——ndarray 的 buffer 就是 SHM 的 raw data,读写 ndarray 即读写 kvspace,无拷贝;写走 `kvspaceTlvEncode` 直接 N 维落盘。 - -**扩展宿主自连 kvspace**:`Engine` 自己 `kvspaceConnect(dsn)` 拿句柄,rwir handoff 只用 `kvlang_rwirext*` 的 runtime 语义(`Register/Params/ResolveRead/ResolveReadPath/ResolveWrite/NextPc`)。Go json 扩展同构。 - -**测试**:`tutorial/14-numpy/`(六例:creation/elementwise/linalg/reduce/manipulation/pipeline,shm 全流程)。 - -## 六、验证(Rust term 扩展 + C runtime) - -`tutorial/test.py --runtime c`(Rust layout → kvspace → C runtime),全量 tutorial: - -| 后端 | 结果 | -|------|------| -| shm | **185 PASS / 0 FAIL / 3 SKIP** | -| redis | **185 PASS / 0 FAIL / 3 SKIP** | -| fs | **185 PASS / 0 FAIL / 3 SKIP** | - -三后端**语义完全一致**——早期 redis/fs 上 `prime_sieve` 的超时与 `time.kv` 的 RTT/文件 I/O 时序偏差已随指令重读稠密探查修复(`RwirDecode` 遇空槽即停,不再每步固定读满 257 槽),不再有后端专属失败。3 个 SKIP 为跨进程扩展 / WIP 用例,三后端一致跳过。 - -**后端存储布局差异(非语义 bug)**:fs 后端(`kvspace-durable/src/fs`)把成员分隔符 `·`(OBJ_SEP)编码为 `·/`——尾中点目录 + `/` 目录边界,故限定名 `/lib/math·sum` 在 fs 上呈嵌套目录 `lib/math·/sum`,而 redis/shm 后端存为扁平单键 `/lib/math·sum`。读写经对称编解码还原(`·/`↔`·`),三后端语义一致。 - -## 七、runtime-c 的边界与扩展的职责(单一正确路径) - -「小 runtime 大扩展」不是模糊分工,而是一条**不可越界**的职责线。runtime-c 独占三件事,扩展一件都不许重做: - -**runtime-c 拥有(唯一实现,扩展不得复刻)**: -1. **执行核心**:fetch-decode-execute 大循环、call/return/br/goto、vthread 调度、native builtin 派发(~50 基础算子)、`=` 拷贝语义。 -2. **值语义与编解码**:TLV / kindexpr 的**唯一编解码器**在 kvspace 正典 codec(`kvspaceTlvEncode` / `kvspaceDecodeHead` / `kvspaceNewPtr/NewChar/NewBool/NewInt64/NewFloat64`),runtime-c 的 `xvalue.c` 只是其薄封装。kindexpr 串是类型的唯一真相,怎么拼、怎么解,只此一处。 -3. **指令解码与签名校验**:`rwir.c` 的 `RwirDecode`(稠密探查、遇空即停)、`/lib/` 签名的读写码规范、`KindexprValid` / `KindexprMatch` 类型判定。 - -**扩展只许做三件事(无论就地 rwir 还是独立进程)**: -1. **拿 PC**:经 handoff / drive 循环从 runtime 拿到当前 PC 与后继 PC(`NextPc`),连续跑己方 rwir。 -2. **解析参数**:**只经 `kvlang_rwirext*` 宿主 ABI**(`Params` / `ResolveRead` / `ResolveReadPath` / `ResolveWrite`)拿读/写参名与值。不许自行解析 rwir 槽位。 -3. **读写 kvspace**:**只经上文的 `kvspace*` 正典 ABI**。要落一个类型化值,走 `kvspaceTlvEncode` + `WriteInPlace/WriteNewPlace`;**绝不自造 kindexpr 构造器、绝不手写 TLV/head**。 - -**一条曾越界、已拉回的实例**:runtime-rs 早期在 Rust 侧有一份 `build_kindexpr`,与 `frontend.c` 逐字节等价——这是对编解码器的复刻,违反职责线,**已删除**,所有编码统一路由到 `kvspaceTlvEncode`(见 `ffi.rs::tlv_encode` 注释「唯一编码入口,不在 Rust 侧复刻 kindexpr 构造」)。**唯一保留的例外**是 json 扩展反序列化里的 kindexpr 串**解析器**(`json.rs::parse_kindexpr`):kvspace 只导出编码器、不导出串解析器,而「把外部 JSON 文本按类型/形状分发写回子树」是扩展**自己的反序列化职责**,不属于 runtime-c 的值语义——故这份解析留在扩展侧,且只读 head 不造 head。这条例外恰恰印证边界:**能调用 kvspace 的,一律调用;kvspace 没提供、且属扩展自身语义的,才自理**。 -"# -> /lib/kvlang/deepdive/runtime/06_c_runtime_and_backend_abstraction -} diff --git a/stdlib/kvlang/deepdive/runtime/07-signature-type-expression.kv b/stdlib/kvlang/deepdive/runtime/07-signature-type-expression.kv deleted file mode 100644 index 4f8e19d8..00000000 --- a/stdlib/kvlang/deepdive/runtime/07-signature-type-expression.kv +++ /dev/null @@ -1,135 +0,0 @@ -// 欢迎加入kvspace世界 -lib kvlang/deepdive/runtime/07_signature_type_expression { - r#"# runtime篇-07: 签名类型表达式(多态 + 高维数组) - -> 状态:设计定稿。代码:签名字符串存 `/lib/·`(kind=`defrwfunc`/`defrwir`),参数类型即本文件的「类型表达式」。 - -## 一、动机 - -函数签名里参数/返回值的类型需要支持**多态**(一个参数声明时接受多种类型,含高维数组),需要一个: - -- 人和 AI 都容易读、容易生成的类型定义表达式; -- 不比正则长; -- 不撞 kvlang 的 rwir/rwfunc 语法分隔符(`(` `)` `,` `:` `->` 空格)。 - -**kind 铁律**:`charbyte` 是旧名,一律作废,字符类型用 `char/utf32` / `char/utf8` / `char/ascii`。 - -## 二、文法 - -``` -type := union ( "..." )? # 末参可带 "..." 变参:0..N 个同型实参 -union := atom ( "|" atom )* # 并集:A|B 表 A 或 B -atom := dims ( any | kind ) -dims := ε # 无 [] = 标量(0 维) - | "[]" # 空 [] = 1 维、长度任意(等价 [?]) - | "[" dim ("," dim)* "]" -dim := int | "?" # 精确大小 或 动态维 -any := "any" # 通配,匹配任意 kind -kind := 精确 kind 串(bool、int8..int64、uint8..uint64、float32/64、char/utf32|utf8|ascii、object、stringkeymap、scope、index、extindex、rwir、defrwir、rwfunc、defrwfunc) -``` - -**变参 `...`**:仅**末**读参可带尾缀 `...`(如 `A:any...`、`A:int64|float64...`),表「0..N 个同型实参」。用于 `print`/`println`/`cerr`/`min`/`max` 等 arity 开放的 rwir。非末参带 `...` 是装载期错误。 - -**铁律:无家族简写**。`int`/`uint`/`float`/`num` 这类简写**不提供**——位宽是开放集合(未来有 int4、fp8、fp16…),封闭枚举会漏。`char` 编码简写同样不提供。多态靠显式 `|` 枚举:`int8|int16|int32|int64`。仅保留一个与「位宽/编码枚举」无关的简写: - -| 简写 | 展开 | 说明 | -|------|------|------| -| `any` | 全部 kind | 通配,不是位宽/编码聚族 | - -## 三、实例 - -``` -A:int64 # 标量 int64 -A:int64|float64 # 标量并集(多态核心) -A:int8|int16|int32|int64 # 任意整数(显式枚举) -A:char/utf8 # 单个 utf8 字符(标量);字符串用 []char/utf8 - -A:[]float32 # 1-D float32,长度任意(= [?]float32) -A:[2]float32 # 1-D,恰好 2 元素 -A:[2,3]float32 # 2-D,恰好 2×3 矩阵 -A:[?,768]float32 # 2-D,第一维动态、第二维 768(weight 场景) -A:[]float32|[]float64 # 多精度 1-D - -A:[2,3]float32|float32 # shape 或标量 -A:bool|char/utf8 # bool 或 单个 utf8 字符 -A:index|object # 目录或命名成员容器 -``` - -## 四、匹配语义 - -一个值(kind=`k`,ndim=`n`,dims=`d`)匹配类型表达式 `E` 的判定: - -``` -match(E, k, n, d): - 对 E 按 "|" 拆成若干 atom,任一 atom 命中即 true - matchAtom(atom, k, n, d): - atom == "any" → true - 有 "[" 前缀 → 解析出 shape 与剩余 type: - matchShape(shape, n, d) 且 base(type) 匹配 k - 无 "[" → 单值:n == 0 且 kind 匹配 k - matchShape(shape, n, d): - shape == "" → n == 1 # "[]" 等价 "[?]":恰一维、任意长 - len(拆逗号) != n → false - 逐维:p=="?" 跳过;否则 d[i] == int(p) -``` - -## 五、语法冲突规避(`[` `]` 感知切分) - -签名用 `,` 分隔参数,shape 里也用 `,` 分隔维: - -``` -matmul(A:[2,3]float32, B:[3,4]float32) -> (C:[2,4]float32) -``` - -解法:签名解析器**跟踪方括号深度**——见 `[` 进 shape 态、见 `]` 退出;shape 态内的 `,` 归维、shape 态外的 `,` 归参数。类型表达式可出现的字符里,`(` `)` `:` `->` 空格**都不出现**,故与签名外层结构无歧义。 - -## 六、匹配器实现 - -Rust 侧在 `layout/src/kindexpr.rs`;C 侧在 `runtime/src/kindexpr.c`(`kvlang_rwirextKindexprValid` / `kvlang_rwirextKindexprMatch` / `kvlang_rwirextKindexprVariadic`)。 - -```go -func match(expr, kind string, ndim int, dims []int32) bool { - for _, atom := range strings.Split(expr, "|") { - if matchAtom(atom, kind, ndim, dims) { return true } - } - return false -} -func matchAtom(atom, kind string, ndim int, dims []int32) bool { - if strings.HasPrefix(atom, "[") { - end := strings.Index(atom, "]") - if !matchShape(atom[1:end], ndim, dims) { return false } - return matchAtom(atom[end+1:], kind, -1, nil) - } - if ndim >= 0 && ndim != 0 { return false } - switch atom { - case "any": return true - default: return atom == kind - } -} -``` - -`any` 是唯一简写,`kind` 走精确字符串相等。 - -## 七、多态派发 - -- native 算子(`+`)内建多态,签名只做类型检查。 -- rwirext 扩展(numpy·add)经 `kvlang_rwirextResolveRead` 拿实参实际 kind,按精确 kind 分派;高维 shape 由扩展自行处理。 -- rwfunc 用户函数:并集声明接受集,函数体 `kind(x)` 分支。 - -## 八、参数 kindexpr 的落盘与运行时内联匹配 - -**铁律**:rwir/rwfunc 每个参数落盘的 **kindexpr 与源码里的类型定义表达式逐字节相同**(同一文法,含 `...`)。 - -**统一落盘于 `/lib/`**。rwir 定义(扩展算子)与 rwfunc(用户函数)**同一存储位置**——不再有独立的 `/rwir/{runtime}//` 槽位树。定义体(`char/utf8` 值)布局: - -``` -[nr : u16 LE][nw : u16 LE][读参 kindexpr × nr, 再 写参 kindexpr × nw,全部以 "\n" 连接] -``` - -**native op 不落盘**:其 kindexpr 内联在 C 注册表中,是唯一权威来源,**运行时匹配直接读 C 表,不回查 kvspace**。数值多类型算子在 C 表中融合为单条(如 `add(a:int64|int32|…, b:…)`)。 - -**扩展 op** 经 `kvlang_rwirextRegister(opcode, nr, nw, sig)` 注册,runtime 把 `sig` 按方括号感知切分为各参 kindexpr、做 `kvlang_rwirextKindexprValid` 校验(拒绝 `path` 等非 kind),并按上述定义体格式写入 `/lib/`(kind=`defrwir`)。 - -**运行时内联匹配**:execute 循环走到 `isothersrwir` 派发前,按 opcode 从 `/lib/` 取定义体、解出读参 kindexpr,对每个已解析实参(kind/ndim/dims)跑 `kvlang_rwirextKindexprMatch`;变参 `...` 把尾随实参全部对末 kindexpr 判定。任一失配 → `TypeError` 写 vthread 错误并停机。 -"# -> /lib/kvlang/deepdive/runtime/07_signature_type_expression -} diff --git "a/stdlib/kvlang/spec/00-\345\257\274\350\250\200/01-\350\214\203\345\233\264\344\270\216\344\270\200\350\207\264\346\200\247.kv" "b/stdlib/kvlang/spec/00-\345\257\274\350\250\200/01-\350\214\203\345\233\264\344\270\216\344\270\200\350\207\264\346\200\247.kv" new file mode 100644 index 00000000..95601a7f --- /dev/null +++ "b/stdlib/kvlang/spec/00-\345\257\274\350\250\200/01-\350\214\203\345\233\264\344\270\216\344\270\200\350\207\264\346\200\247.kv" @@ -0,0 +1,37 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/导言/范围与一致性 { + r####"# 范围与一致性 + +## 本规范的地位 + +本规范是 kvlang 语言的**唯一事实源**。语言核心的任何设计或实现调整,一律**规范驱动**:先改本规范的相应条款及其锚定示例,再改实现,直至锚例在三后端全绿。规范与实现不符时,以规范为准,实现视为缺陷。 + +规范以 kvlang 自身书写(`.kv`),layout&run 后落进 `/lib/kvlang/spec/…`——规范即 KV 树里的数据,可被 kvlang 自解析与自校验。 + +## 范围 + +本规范界定 kvlang 语言本体: + +- **词法**(卷 01)——源码表示、词法单元、注释、标识符、字面量、运算符。 +- **kvspace 模型**(卷 02)——地址空间、寻址与命名、键系统与数组访问、指令布局格式、系统变量。kvspace 既是寻址空间也是内存空间,是 kvlang 的第一性前置概念,故前置于语义两卷。 +- **类型系统**(卷 03)——种类与定宽类型、kindexpr 签名类型表达式、数组形态、容器。类型跨 layout 与 runtime 两阶段,故独立成卷。 +- **layout 语义**(卷 04)——静态语义:语法检查与布局。layout 是 **o0 编译器**:做检查、布局与降级,**不做任何优化**(一旦优化即丢失高层语义,使扩展编译器无法按原语义在异构硬件上优化),可报诊断。 +- **runtime 语义**(卷 05)——动态语义:纯解释执行。 + +附录给出唯一权威文法;设计理由卷为非规范(non-normative)的动机说明。 + +## 核心与扩展边界 + +kvlang 是**小核心 + 扩展主导**的语言。本规范只界定语言核心必须保证的语义,以及核心与 rwir 兑现方之间的契约(def rwir 路由头与各 runtime 的 myrwircaps)。具体 runtime(numpy、fs、networld、gpu 等)提供的 rwir 实现不属本规范,由各 runtime 文档界定;kv 源码**不声明** rwir——`def rwir` 路由头由各 runtime 注册到 `/lib`,本规范只规定 kvlang 如何定位与调用 rwir、经 def rwir 路由兑现(见卷 05)。 + +## 一致性 + +**tutorial 套件即一致性测试。** 一个实现是否符合本规范,由 `tutorial/` 下的可运行 `.kv` 示例判定:三后端(shm / fs / redis)全部通过、输出逐字节一致,即为符合。 + +本规范每条 normative 条款应锚定至 tutorial 中至少一条示例。修改语言的流程恒为: + +1. 修改本规范条款; +2. 增改其锚定的 tutorial 示例(新增条款须补锚例,不改动既有冻结示例的语义); +3. 修改 layout / runtime 实现,直至锚例三后端全绿。 + "#### -> /lib/kvlang/spec/导言/范围与一致性 +} diff --git "a/stdlib/kvlang/spec/00-\345\257\274\350\250\200/02-\350\247\204\350\214\203\347\224\250\350\257\255\344\270\216\351\230\205\350\257\273\347\272\246\345\256\232.kv" "b/stdlib/kvlang/spec/00-\345\257\274\350\250\200/02-\350\247\204\350\214\203\347\224\250\350\257\255\344\270\216\351\230\205\350\257\273\347\272\246\345\256\232.kv" new file mode 100644 index 00000000..ac38e8ca --- /dev/null +++ "b/stdlib/kvlang/spec/00-\345\257\274\350\250\200/02-\350\247\204\350\214\203\347\224\250\350\257\255\344\270\216\351\230\205\350\257\273\347\272\246\345\256\232.kv" @@ -0,0 +1,21 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/导言/规范用语与阅读约定 { + r####"# 规范用语与阅读约定 + +## 规范用语 + +规范用语对齐 RFC 2119 语义: + +- **必须 / 不得**(must / must not / shall)——绝对要求 / 绝对禁止。违反即不符合规范。 +- **应 / 不应**(should / should not)——存在正当理由时可偏离,但须权衡后果。 +- **可**(may)——纯可选。 + +未加此类限定词的陈述句为**事实性描述**(语言如此这般),同样是 normative 的。设计动机、历史与取舍一律归入非规范的**设计理由**卷,不混入正文条款。 + +## 阅读约定 + +- 文法元记法与本规范其余用语约定见 [[文法]]。 +- 完整权威文法汇总于 [[文法]](附录);正文各章只引用其片段,不各自另立文法。 +- 章节间引用以 `[[章节名]]` 链接。 + "#### -> /lib/kvlang/spec/导言/规范用语与阅读约定 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/01-\346\272\220\347\240\201\347\273\223\346\236\204.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/01-\346\272\220\347\240\201\347\273\223\346\236\204.kv" new file mode 100644 index 00000000..519980bf --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/01-\346\272\220\347\240\201\347\273\223\346\236\204.kv" @@ -0,0 +1,27 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/源码结构 { + r####"# 源码结构 + +## 锚例 + +- `tutorial/01-basics/hello.kv`——最小源文件与 `Ident`/`Literal`/调用记号。 +- `tutorial/01-basics/vars.kv`——换行作语句分隔、`=`/`->` 记号、行注释。 + +## 源码表示 + +kvlang 源文件是一段 **UTF-8 字节序列**。词法分析按字节推进:ASCII 字节触发语法结构,其余多字节序列(如中文、`é`)作为普通字符原样穿过,出现在标识符、字符串与路径内部。 + +一处**行**由换行符 `\n`(U+000A)界定;`\r`(U+000D)在词法阶段被直接跳过,不产生任何词法单元,故 `\r\n` 与 `\n` 等价。位置以 1 起始的 `line`、`col` 计。 + +## 空白与语句分隔 + +空格 ` ` 与制表符 `\t` 是词法单元之间的分隔符,本身不产生词法单元;除用于分隔相邻词法单元外不具意义。 + +**语句分隔**由换行符 `\n` 与分号 `;` 表达,二者语义等价,均产生一个 `Newline` 词法单元。词法器执行两条归并规则: + +- 连续的语句分隔(多个换行/分号、含其间空白)**折叠**为单个 `Newline`; +- 词法单元流开头的语句分隔被**抑制**,不产生 `Newline`。 + +字符串与原始字符串字面量内部的物理换行**不**产生 `Newline`(它是字面量内容的一部分),但行号仍随之前进。 + "#### -> /lib/kvlang/spec/词法/源码结构 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/02-\350\257\215\346\263\225\345\215\225\345\205\203.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/02-\350\257\215\346\263\225\345\215\225\345\205\203.kv" new file mode 100644 index 00000000..8256e504 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/02-\350\257\215\346\263\225\345\215\225\345\205\203.kv" @@ -0,0 +1,39 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/词法单元 { + r####"# 词法单元 + +## 词法单元种类 + +词法分析把源码切分为下列种类的词法单元,末尾附一个 `EOF` 哨兵: + +- `Ident`——标识符,以及未被单独归类的符号运算符(见 [[运算符基础]]); +- `Literal`——字面量:数值、字符串、原始字符串、绝对路径(见 [[字面量基础]]、[[标识符]]); +- `Arrow`——两种赋值/方向记号 `=`、`->`(见 [[运算符基础]]); +- `Dot`——成员分隔符 `·`(U+00B7,见 [[标识符]]); +- `LParen` `RParen` `LBrace` `RBrace` `LBrack` `RBrack`——`( ) { } [ ]`; +- `Comma` `Colon`——`,` `:`; +- `Newline`——语句分隔(`\n` 或 `;`,见上); +- `Comment`——注释(见 [[注释]]); +- 关键字词法单元 `Return` `If` `Else` `For` `While` `Break` `Continue`; +- `EOF`——源码结束哨兵。 + +## 关键字 + +kvlang 只有 **7 个词法关键字**,它们在词法阶段即被识别,不得用作标识符: + +``` +return if else for while break continue +``` + +其余在语言中具有特殊含义的词(`lib`、`rwir`、`rwfunc`、`in`、`any`、`None`、`true`、`false`、种类名如 `int64`/`float32`/`struct` 等)**不是**词法关键字:它们一律词法化为 `Ident`,其含义由 layout 阶段依上下文赋予。这类词称为**上下文词**。 + +`def` **不是** kvlang 关键字,kv 源码中永不出现:`def rwir`/`def struct`/`def kindexpr` 是 head 的 langtype 元层「定义节点」标记,只在 layout 之后(`def struct`、`def kindexpr`)或 runtime 注册时(`def rwir`)产生,与源码词法无关(见 [[langtype语义类型]])。 + +## 最长匹配 + +词法分析遵循**最长匹配**(maximal munch):在每个位置,词法器取能构成合法词法单元的最长字符序列。特别地: + +- 以数字开头的序列必为数值字面量,不会被识别为标识符; +- `-` 后紧跟 `>` 构成 `->`,`<` 后紧跟 `<` 构成 `<<`,依此类推——双字符运算符优先于其单字符成分。 + "#### -> /lib/kvlang/spec/词法/词法单元 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/03-\346\263\250\351\207\212.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/03-\346\263\250\351\207\212.kv" new file mode 100644 index 00000000..2ba251a3 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/03-\346\263\250\351\207\212.kv" @@ -0,0 +1,42 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/注释 { + r####"# 注释 + +kvlang 有两种注释,语法与 Rust 一致。**不存在** `#` 注释。 + +## 锚例 + +- `tutorial/01-basics/vars.kv`、`tutorial/01-basics/arith.kv`——行注释(含期望输出注释块与行尾 )。 + +## 行注释 + +行注释以 `//` 开始,延伸至(但**不含**)该行的换行符: + +``` +x = 42 // 这是行注释 +``` + +行注释不消费其后的换行符,故一行末尾的行注释之后仍产生一个 `Newline` 语句分隔。行注释不嵌套:其内部的 `//`、`/*` 均无特殊含义。 + +## 块注释 + +块注释由 `/*` 与 `*/` 界定,**可跨行**: + +``` +/* 这一段 + 跨越多行 */ +``` + +块注释**可嵌套**:词法器对 `/*` 计数、对 `*/` 递减,仅当计数归零才结束。因此下例整体是一条注释: + +``` +/* 外层 /* 内层 */ 仍在注释内 */ +``` + +嵌套能力使得注释掉一段本身含块注释的代码是安全的。若源码结束时块注释仍未闭合,注释延伸至文件末尾。 + +## 注释的地位 + +注释被词法化为 `Comment` 词法单元,在词法流中保留,但对程序语义无贡献——layout 阶段将其忽略。注释不是语句分隔符:其内部的换行不产生 `Newline`(块注释),行注释后的换行照常产生 `Newline`。 + "#### -> /lib/kvlang/spec/词法/注释 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/04-\346\240\207\350\257\206\347\254\246.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/04-\346\240\207\350\257\206\347\254\246.kv" new file mode 100644 index 00000000..4e1158a6 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/04-\346\240\207\350\257\206\347\254\246.kv" @@ -0,0 +1,42 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/标识符 { + r####"# 标识符 + +## 锚例 + +- `tutorial/01-basics/vars.kv`——标识符 `x`、`y`。 +- `tutorial/01-basics/kv_tree.kv`——绝对路径 `/tmp/kvt`、子键路径 `/tmp/kvt·data`、层级路径 `/tmp/kvt/a`,以及成员/方法 `kv·set`、`kv·get`。 + +## 标识符 + +标识符命名帧内变量、参数、lib 段、rwir/rwfunc、成员键等。其规范形态为: + +``` +letter = unicode_letter | "_" +ident = letter { letter | digit } +``` + +其中 `unicode_letter` 为任意 Unicode 字母。因此标识符**可含非 ASCII 字母**,中文标识符合法: + +``` +变量数 = 42 +println("值 =", 变量数) +``` + +此决策使 kvlang 与 C99、Go、Python、Rust、TypeScript 在「允许 Unicode 标识符」上对齐。 + +7 个词法关键字(`return if else for while break continue`)为保留字,不得用作标识符;其余上下文词(`lib`、`None`、`true`、`false`、种类名等)不保留,但用作普通标识符会遮蔽其上下文含义,不应如此使用。 + +> 实现说明(非规范):当前词法器以「最长非分隔符字节串」切分标识符,未逐字符校验上述字母表,故比本节所述更宽松。分隔符集合为空白、`; , ( ) { } [ ] :`、成员分隔符 `·`,以及运算符字符 `+ - * % ! = < > & | ^`。 + +## 成员分隔符 + +`·`(U+00B7,中点)是 kvlang 唯一的成员/方法分隔符,词法化为独立的 `Dot` 词法单元。它出现于: + +- 成员访问 `base·field`; +- 方法/限定调用 `base·method(...)`、`pkg·func(...)`; +- 坐标访问 `base·[i,j]`。 + +kvlang **不使用** ASCII 句点 `.` 作成员分隔符(`.` 仅作路径子键,见下)。 + "#### -> /lib/kvlang/spec/词法/标识符 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/05-\347\273\235\345\257\271\350\267\257\345\276\204\345\255\227\351\235\242\351\207\217.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/05-\347\273\235\345\257\271\350\267\257\345\276\204\345\255\227\351\235\242\351\207\217.kv" new file mode 100644 index 00000000..6af8fba0 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/05-\347\273\235\345\257\271\350\267\257\345\276\204\345\255\227\351\235\242\351\207\217.kv" @@ -0,0 +1,20 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/绝对路径字面量 { + r####"# 绝对路径字面量 + +绝对路径直接寻址 kvspace 树中的节点,是一类字面量(词法化为以 `/` 起始的 `Literal`)。路径以 `/` 开头,后随一个 ASCII 字母、数字或下划线,随后是由下列成分构成的最长序列: + +- `/`——段分隔符,划分树的层级; +- `.`——子键分隔符,寻址同一节点下的子键; +- `·[i,j,…]`——坐标段,按整数下标寻址 strkeymap/容器的坐标; +- 其余非分隔符字符构成段名(同样允许非 ASCII)。 + +``` +kv·set("/tmp/kvt·data", 42) +kv·get("/tmp/kvt", "data") -> x +kv·set("/tmp/kvt/a", 1) +``` + +路径的完整寻址语义见 [[寻址与命名]];本节只界定其词法形态。裸 `/`(其后不接字母/数字/下划线,且非 `//`、`/*`)不构成路径,见 [[运算符基础]]。 + "#### -> /lib/kvlang/spec/词法/绝对路径字面量 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/06-\345\255\227\351\235\242\351\207\217\345\237\272\347\241\200.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/06-\345\255\227\351\235\242\351\207\217\345\237\272\347\241\200.kv" new file mode 100644 index 00000000..1a956a60 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/06-\345\255\227\351\235\242\351\207\217\345\237\272\347\241\200.kv" @@ -0,0 +1,30 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/字面量基础 { + r####"# 字面量基础 + +字面量是源码中直接书写的常量值。kvlang 有四类字面量:数值、字符串、绝对路径(词法见 [[标识符]]),以及上下文空/布尔字面量。 + +## 锚例 + +- `tutorial/01-basics/float_literal.kv`——浮点字面量 `3.14`、结果 `6.28`。 +- `tutorial/01-basics/arith.kv`、`tutorial/01-basics/numtypes.kv`——整数字面量与定宽落定。 +- `tutorial/01-basics/strings.kv`——双引号串、`+` 拼接、单字符字符串。 +- `tutorial/11-string/07-multiline.kv`——字符串三形式(`"…"` 跨行、`r"…"`、`r#"…"#`)。 +- `tutorial/01-basics/none.kv`——`None` 空值。 +- `tutorial/01-basics/kv_tree.kv`、`tutorial/03-control/if.kv`——`true`/`false` 布尔字面量。 + +## 数值字面量 + +``` +digit = "0" … "9" +integer = digit { digit } +float = integer [ "." integer ] [ ("e" | "E") [ "+" | "-" ] integer ] +``` + +- 整数为一串十进制数字。**无**十六进制、八进制、二进制前缀,**无**下划线数字分隔符。 +- 浮点数为整数部分后可选跟小数部分 `.digits` 与可选指数 `(e|E)[±]digits`。小数点前必须有数字(不支持 `.5` 这类前导点写法)。 +- 数值字面量本身**无符号**;负数由前缀一元运算符 `-` 与字面量组合而成(见 [[运算符基础]])。 + +`3` 是整数字面量,`3.14`、`6.28`、`1e3`、`2.5E-4` 是浮点字面量。字面量到具体定宽种类的落定由类型系统与上下文决定(见 [[种类与定宽类型]])。 + "#### -> /lib/kvlang/spec/词法/字面量基础 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/07-\345\255\227\347\254\246\344\270\262\344\270\216\347\251\272\345\200\274\345\270\203\345\260\224\345\255\227\351\235\242\351\207\217.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/07-\345\255\227\347\254\246\344\270\262\344\270\216\347\251\272\345\200\274\345\270\203\345\260\224\345\255\227\351\235\242\351\207\217.kv" new file mode 100644 index 00000000..667f99a6 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/07-\345\255\227\347\254\246\344\270\262\344\270\216\347\251\272\345\200\274\345\270\203\345\260\224\345\255\227\351\235\242\351\207\217.kv" @@ -0,0 +1,44 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/字符串与空值布尔字面量 { + r####"# 字符串与空值布尔字面量 + +## 字符串字面量 + +kvlang 有三种字符串书写形式,与 Rust 一致;**无**三引号串,**无**反引号原始串。 + +### 转义串 `"…"` + +以双引号界定,**可跨行**(内部的物理换行成为内容中的换行字符)。反斜杠 `\` 引入转义: + +- `\n`→换行、`\t`→制表符、`\r`→回车、`\0`→空字符; +- `\<其他字符>`→该字符本身,故 `\"`→`"`、`\\`→`\`。 + +``` +s = "line1 +line2" // 跨行,含一个真实换行 +t = "one\ntwo" // 转义换行 +``` + +### 原始串 `r"…"` / `r#"…"#` / `r##"…"##` … + +以 `r` 引导,**零转义**、可跨行。可在 `r` 与首个 `"` 之间加 `N` 个井号 `#`,此时须以 `"` 后接同样 `N` 个 `#` 闭合;井号越多可嵌套越深,内容因而能包含 `"`: + +``` +r = r"raw\nliteral" // 输出含反斜杠与 n,非换行 +q = r#"he said "hi""# // 内容含双引号 +``` + +### 单引号形式 `'…'` + +以单引号界定,用于 char / keypath 场景,与转义串共享同一套转义规则。其内容作为裸字面量交由后续阶段依上下文(char 种类、键路径)解释。 + +kvlang 的动态字符串阵营中,单个字符通常以**长度为 1 的字符串**表示(如 `s[i]` 读返单字符字符串),char 的定宽种类见 [[种类与定宽类型]]。 + +## 空值与布尔字面量 + +- `None`——空值字面量。kvlang 的空值**只有** `None`;书写 `null` 是错误,layout **必须**显式拒绝,**不得**当作裸标识符静默放行。未赋值的变量地址即读作 `None`。 +- `true` / `false`——布尔字面量。 + +三者均为**上下文词**:词法阶段化为标识符,由 layout/runtime 依类型上下文解释为对应值。 + "#### -> /lib/kvlang/spec/词法/字符串与空值布尔字面量 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/08-\350\277\220\347\256\227\347\254\246\345\237\272\347\241\200.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/08-\350\277\220\347\256\227\347\254\246\345\237\272\347\241\200.kv" new file mode 100644 index 00000000..3dd99554 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/08-\350\277\220\347\256\227\347\254\246\345\237\272\347\241\200.kv" @@ -0,0 +1,57 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/运算符基础 { + r####"# 运算符基础 + +本节界定运算符与方向记号的**词法**:有哪些符号、各写作什么字形。其求值与结合语义见 [[指令架构]]与 [[执行模型]]。符号表是权威的双向查找表,其余部分不得另行硬编码符号字符串。 + +## 锚例 + +- `tutorial/01-basics/arith.kv`——`+ - × ÷ %` 与一元 `√`(`√(144)`)、`=`/`->` 二记号。 +- `tutorial/01-basics/vars.kv`——`=` 与 `+`。 +- `tutorial/03-control/if.kv`——比较与逻辑运算符。 +- `tutorial/01-basics/kv_tree.kv`——`!= None` 比较。 + +## 赋值与方向记号 + +kvlang 有两种赋值/方向记号,均词法化为 `Arrow`: + +- `=`——写入左侧; +- `->`——写入右侧(数据从左流向右)。 + +``` +x = 42 +42 -> x // 写入右侧 +``` + +`=` 仅为写入记号,kvlang **无**独立的等号赋值语义之外用法;相等比较写作 `==`。**无 `<-`**:历史上有第三种 `<-`(写入左侧),与 `=` 完全重复,已删除——写入左侧一律写 `=`。 + +## 算术运算符 + +| 字形 | 含义 | 一元 | +|------|------|------| +| `+` | 加 | 是(一元正) | +| `-` | 减 | 是(一元负) | +| `×` | 乘(U+00D7) | 否 | +| `÷` | 除(U+00F7) | 否 | +| `%` | 取模 | 否 | + +**乘法是 `×`、除法是 `÷`**,非 ASCII 的 `*` 与 `/`: + +- `*`(U+002A)**不是**乘号。它在符号表中登记为 `pointer`,非算术运算符。 +- `/`(U+002F)**不是**除号。它只用于绝对路径字面量与注释(`//`、`/*`);出现在其他位置的裸 `/` 不承载算术除法语义。 + +一元 `+`、`-` 作前缀;`√`(U+221A,平方根)亦为一元前缀运算符。 + +## 比较运算符 + +``` +== != < > <= >= +``` + +其中三者另有等价的 Unicode 字形别名,词法上与 ASCII 形式同义: + +- `!=` ≡ `≠`(U+2260) +- `<=` ≡ `≤`(U+2264) +- `>=` ≡ `≥`(U+2265) + "#### -> /lib/kvlang/spec/词法/运算符基础 +} diff --git "a/stdlib/kvlang/spec/01-\350\257\215\346\263\225/09-\351\200\273\350\276\221\344\275\215\350\277\220\347\256\227\344\270\216\344\274\230\345\205\210\347\272\247.kv" "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/09-\351\200\273\350\276\221\344\275\215\350\277\220\347\256\227\344\270\216\344\274\230\345\205\210\347\272\247.kv" new file mode 100644 index 00000000..23491f22 --- /dev/null +++ "b/stdlib/kvlang/spec/01-\350\257\215\346\263\225/09-\351\200\273\350\276\221\344\275\215\350\277\220\347\256\227\344\270\216\344\274\230\345\205\210\347\272\247.kv" @@ -0,0 +1,42 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/词法/逻辑位运算与优先级 { + r####"# 逻辑位运算与优先级 + +## 逻辑运算符 + +``` +&& || ! +``` + +`!` 为一元前缀(逻辑非)。 + +## 位运算符 + +``` +& | ^ << >> +``` + +分别为按位与、按位或、按位异或、左移、右移。 + +## 优先级 + +中缀运算符的优先级如下(数值越大结合越紧);一元前缀运算符(`+ - ! √`)优先级高于全部中缀。 + +| 运算符 | 优先级 | +|--------|--------| +| `\|\|` | 10 | +| `&&` | 20 | +| `==` `!=`(`≠`) | 30 | +| `<` `>` `<=`(`≤`) `>=`(`≥`) | 40 | +| `+` `-` | 50 | +| `×` `÷` `%` | 60 | +| `<<` `>>` | 70 | +| `&` | 80 | +| `^` | 90 | +| `\|` | 100 | + +## 运算符成员分隔符 + +`·`(U+00B7)是成员/方法分隔符,非算术运算符,词法化为独立的 `Dot` 词法单元,其词法与用途见 [[标识符]]。 + "#### -> /lib/kvlang/spec/词法/逻辑位运算与优先级 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/01-\345\234\260\345\235\200\347\251\272\351\227\264.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/01-\345\234\260\345\235\200\347\251\272\351\227\264.kv" new file mode 100644 index 00000000..eb52022f --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/01-\345\234\260\345\235\200\347\251\272\351\227\264.kv" @@ -0,0 +1,33 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/地址空间 { + r####"# 地址空间 + +kvspace 是 kvlang 的第一性前置概念:它**既是寻址空间也是内存空间**。程序计数器 PC 是一条 kvspace 路径字符串,指令是路径上的值,变量是路径上的值,代码与数据统一在同一棵 KV 树上。本卷其余各章([[寻址与命名]]、[[键系统与数组访问]]、[[指令布局格式]]、[[系统变量]])均以本章界定的地址空间为基础。 + +## 存储铁律 + +kvspace 是文件系统风格的键值抽象。任一条目由 key 与 value 构成: + +- key **必须**是绝对、规范的字符串路径:以 `/` 开头,`/` 分隔层级;不得为相对路径。 +- value **必须**是一个 XValue 序列化后的字节串(TLV 编码,格式见 [[键系统与数组访问]])。 +- **不得**向 kvspace 直接写入裸基础类型字节;所有值必须经 XValue 编解码。违反此铁律的写入,读取方在解码非法字节时行为未定义。 + +目录以尾随 `/` 标记:key `/a/` 是目录(其 value 为 `index`),key `/a` 是普通值。同一路径的有尾斜杠与无尾斜杠形态可独立共存。 + +## 扩展存储 + +kvspace 元存中的 value 分两类:**内联数据**(基础类型、容器元数据)与**扩展存储句柄**。句柄 value 的 ref=2(@ext,见 [[键系统与数组访问]]),其 body 只记录目标位置描述符,真实字节位于元存之外: + +| 扩展位置 | 典型数据 | +|---------|---------| +| 集群/本机共享内存 | 大张量、激活值 | +| GPU 显存 | 计算张量 | +| 文件系统 / 对象存储 | 模型权重、检查点、数据集、外部进程输出 | + +读取一个 ref=2 扩展句柄 value 时,运行时按 body 定位符前缀路由到对应兑现器还原真实字节(`runtime-rs/src/engine.rs`)。 + +## 后端与前端 + +kvspace 的 C ABI 由前端 dispatch 层(`kvspace` 仓 `src/frontend.c`)统一导出,运行期按连接串 scheme 选择后端:`shm://` 走 kvspace-c,`redis`/`fs`/`s3` 等走 kvspace-durable。三后端**必须**实现同一 ABI 与同一线格式(byte-identical),语义一致;一致性由 tutorial 三后端全绿判定(见 [[范围与一致性]])。ABI 符号与 XValue 线格式见 [[寻址与命名]] 与 [[键系统与数组访问]]。 + "#### -> /lib/kvlang/spec/kvspace模型/地址空间 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/02-\347\273\223\346\236\204\345\237\237.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/02-\347\273\223\346\236\204\345\237\237.kv" new file mode 100644 index 00000000..a33ad3b4 --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/02-\347\273\223\346\236\204\345\237\237.kv" @@ -0,0 +1,31 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/结构域 { + r####"# 结构域 + +kvspace 的路径中,只有以下前缀由语言核心赋予固定结构语义;其余 `/` 路径全部自由,由用户代码定义,核心不预设 schema。 + +| 前缀 | 语义 | +|------|------| +| `/lib` | 函数与扩展指令的单一事实源:rwfunc 的 layout 产物(签名 + 指令树)、`.src` 源码副本、各 runtime 注册的 def rwir 路由头均落于此 | +| `/vthread` | 虚线程运行时状态:每 vthread 一棵子树,栈帧、PC、状态等随身系统变量暴露执行现场 | +| `/networld` | 与外部世界交互的名册域:本机与外部进程、文件等以 `/networld/{host}/…` 登记,供 rwir(经 def rwir 路由)定位与兑现 | + +`/lib` 与 `/vthread` 的前缀常量由运行时与 layout 各自定义(C 运行时 `runtime/src/runtime_internal.h` 的 `LIB_ROOT`/`VTHREAD_ROOT`,Rust layout `layout/src/keytree.rs` 的 `LIB_ROOT`/`VTHREAD_ROOT`)。 + +### `/lib` + +`/lib` 是全局命名空间,无 `import`——lib 树本身即命名空间。`lib name { … }` 块声明包;跨包调用走全路径 `/lib/{pkg}·{func}`。多文件经引导拼接后统一 layout 进 `/lib`,已加载条目去重。函数在 `/lib` 下只读,帧调用经 extindex 复用同一份指令树(见 [[指令布局格式]])。 + +### `/vthread` + +每个虚线程 `vid` 占一棵子树 `/vthread/{vid}/`,借用 Unix `/proc//` 思想:栈帧为其下的 KV 子树,`‥pc`、`‥status` 等系统变量(前缀 `‥`,见 [[系统变量]])暴露执行状态。PC 为路径字符串、帧根落 KV,故进程崩溃后可从 kvspace 续跑。 + +### `/networld` + +`/networld` 是本机在网络命名空间下的身份域,`host` 取裸 hostname。外部进程执行、宿主文件系统读写等**不**经设备文件,而是登记在 `/networld/{host}/…` 下并由 rwir(经 def rwir 路由)兑现:例如外部进程的 stdout/stderr 捕获为扩展存储句柄,其逻辑定位符形如 `/networld/{host}/proc/{pid}/{stream}`(`runtime-rs/src/rwir/networld/`)。 + +### 没有设备域 + +kvspace 世界中没有终端、没有设备文件,只有 key 与 value。`print`/`println` 等 I/O 不是地址空间的一个域,也不是核心内建,而是 rwir(以 def rwir 路由头注册于 `/lib/`,见 [[指令布局格式]]),执行时直接作用于宿主 I/O。 + "#### -> /lib/kvlang/spec/kvspace模型/结构域 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/03-\345\257\273\345\235\200\344\270\216\345\221\275\345\220\215.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/03-\345\257\273\345\235\200\344\270\216\345\221\275\345\220\215.kv" new file mode 100644 index 00000000..07ffdf76 --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/03-\345\257\273\345\235\200\344\270\216\345\221\275\345\220\215.kv" @@ -0,0 +1,55 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/寻址与命名 { + r####"# 寻址与命名 + +本章界定 kvlang 如何在 [[地址空间]] 上寻址:PC 的形态、变量名与指针的关系、路径分隔符语义,以及访问 kvspace 的 C ABI。 + +## PC 是路径字符串 + +传统 VM 的程序计数器是一维线性整数(内存地址),跳转与调用是整数算术。kvlang 的 PC 是一条 kvspace 路径字符串,跳转与调用是路径拼接与子树导航。 + +| 维度 | 传统 VM | kvlang | +|------|---------|--------| +| PC 类型 | `uint64` / 指令指针 | `string`(KV 路径) | +| 指令获取 | 解引用内存地址 | 读 PC 路径处的 XValue | +| 跳转 | 改寄存器 | PC 置为新路径 | +| 调用 | 压返回地址、跳入口 | 建帧子树、帧根 extindex 指向 `/lib` 指令树 | +| 栈帧 | 连续内存 | `/vthread/{vid}/…` KV 子树 | + +PC 落在 kvspace、帧根落在 kvspace,故执行现场可观测、可崩溃恢复。指令布局与帧机制见 [[指令布局格式]]。 + +## 变量名即指针 + +kvlang 没有取址运算符——**代码中变量的名字,本身就是该变量的指针**(一条 kvspace 路径)。指令槽里存的从来不是值,而是指针文本;求值恒经一次指针间接。指针有两种形态: + +| 形态 | 写法 | 语义 | 解析 | +|------|------|------|------| +| 相对指针 | 裸标识符 `x` | 相对当前栈帧 | 运行时与帧根拼接 | +| 绝对指针 | `/counter` | kvspace 全局绝对路径 | 零拼接,直接读写 | + +局部变量的名字即相对指针。运行时解析公式: + +``` +绝对路径 = FrameRoot(PC) + "/" + 相对指针 +``` + +`FrameRoot(PC)` 从 PC 截去末尾的 `/[coord]` 指令坐标段即得帧根(`layout/src/keytree.rs` 的 `frame_root`:PC 无 `/[` 坐标段则为非法)。这与 C 的 `rbp + offset` 同构:帧根对应帧基址,相对指针对应栈偏移,绝对指针对应固定地址。 + +`/lib` 下的函数模板中只有相对指针,因此天然可重入:每次调用产生不同的帧根,同一份相对指针拼接出互不干扰的绝对指针——递归与尾调用无需额外机制。 + +**参数不得同名**:变量名即指针,同一帧内两个同名参数将指向同一 kvspace 位置。读参列表内、写参列表内、以及读写列表之间均**不得**同名;layout 静态阻断,运行时兜底。 + +## 路径分隔符 + +kvspace 路径与 XValue 的 kindexpr 共用一套分隔符语义,每个分隔符承载固定含义(常量定义处:`kvspace/include/kvspace/const.h`、`kvspace-durable/src/const.rs`): + +| 分隔符 | 码位 | 语义 | +|--------|------|------| +| `/` | U+002F | 层级;目录以尾随 `/` 标记 | +| `·` | U+00B7 | **两种独立含义,正交**(见 [[总体方案]]):①**memindex 标记**——值容器成员索引 `{key}·`(仅 stringkeymap/struct 用),以及成员访问 `obj·field`、`m·[i]`;②**包·函数命名分隔符**——`/lib/{pkg}·{func}` 中路径段名字的一部分,rwfunc 目录成员仍走 `/` 子路径,与 memindex 无关 | +| `‥` | U+2025 | 运行时系统变量前缀(`X/‥pc`,见 [[系统变量]]) | +| `…` | U+2026 | extindex 句柄 body 的首元素前缀 | + +标识符**不得**以 `‥` 开头,故用户代码永远造不出 `‥` 前缀段,系统变量命名空间与用户命名空间零交集(见 [[键系统与数组访问]] 的键形态三分)。 + "#### -> /lib/kvlang/spec/kvspace模型/寻址与命名 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/04-CABI.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/04-CABI.kv" new file mode 100644 index 00000000..f337b2e8 --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/04-CABI.kv" @@ -0,0 +1,26 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/CABI { + r####"# C ABI + +访问 kvspace 的唯一 C ABI 由前端 dispatch 静态导出(`kvspace/include/kvspace/kvspace.h`)。读写采用**借用指针模型**:读返回指向后端常驻空间的指针,调用方不得释放;写返回 body 偏移指针供调用方直接填。核心符号: + +| 符号 | 语义 | +|------|------| +| `kvspaceGet` | 借用读:`*out` 指向后端常驻空间;`resolve=1` 穿透 link;不存在/空值 → `*out=NULL`、返回 0 | +| `kvspaceWriteInPlace` | 就地写:key 已存在、kind 不变、body 长度不变;返回原 box body 偏移指针,违反前置条件即报错,绝不静默重分配 | +| `kvspaceWriteNewPlace` | 新位置写:按 `(kindexpr, body_len)` 分配新 box、写好 head,返回 body 偏移指针;用于新建或 kind/尺寸变化 | +| `kvspaceListLen` | 返回前缀下直接子项计数 | +| `kvspaceListAt` | 取前缀下第 `idx` 个子项名,写入调用方自备缓冲;配合 `kvspaceListLen` 遍历 | +| `kvspaceDel` | 批量删除给定 key 数组 | +| `kvspaceDelTree` | 删除前缀子树 | +| `kvspaceCp` / `kvspaceCpTree` / `kvspaceCpList` | 复制单值 / 子树 / 直接子项 | +| `kvspaceMkindex` | 建目录,带 `capacity` 预留容量 | +| `kvspaceMkindexExt` / `kvspaceRmindexExt` | 建立 / 撤销 extindex 叠加层 | +| `kvspaceClear` | 清空 | +| `kvspaceWatch` | 一次性等待某 key 变为目标值 | + +无句柄的编解码符号(前端静态实现,byte-identical):`kvspaceTlvEncode` / `kvspaceTlvEncodeMode` / `kvspaceDecodeHead`,以及构造器 `kvspaceNewPtr`(存目标完整 kindexpr + 目标 key)、`kvspaceNewChar` / `kvspaceNewBool` / `kvspaceNewInt64` / `kvspaceNewFloat64`。 + +读写各带 `resolve` 参数:`resolve=1` 时透明穿透 ref=1 指针(link);`kvspaceListLen`/`kvspaceListAt` 另有 `expand_ext` 参数控制是否展开 extindex 子项。head 线格式与 kindexpr 编码见 [[键系统与数组访问]]。 + "#### -> /lib/kvlang/spec/kvspace模型/CABI +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/05-\351\224\256\347\263\273\347\273\237\344\270\216\346\225\260\347\273\204\350\256\277\351\227\256.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/05-\351\224\256\347\263\273\347\273\237\344\270\216\346\225\260\347\273\204\350\256\277\351\227\256.kv" new file mode 100644 index 00000000..9a177aec --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/05-\351\224\256\347\263\273\347\273\237\344\270\216\346\225\260\347\273\204\350\256\277\351\227\256.kv" @@ -0,0 +1,40 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/键系统与数组访问 { + r####"# 键系统与数组访问 + +本章界定 kvspace 键的三种形态、XValue 的 kindexpr 类型表达与线格式、数组的两种物理存储形态及其访问方式。 + +## 键形态三分 + +任一 key 的形态唯一确定其性质与所有权: + +| 形态 | 例 | 性质 | 所有权 | +|------|----|------|--------| +| `X/名`(`/` + 普通名) | `/vthread/7/[3,0]`、`/lib/f/[1,0]` | 结构:帧、指令槽、目录 | 核心 | +| `X·名`(`·` 中点键) | `/c0·next`、`m·[0]` | 用户数据成员 | 用户 | +| `X/‥名`(`/` + `‥` 名) | `/vthread/7/‥pc` | 系统变量(影子元数据) | 核心 | + +标识符禁以 `‥` 开头,故三类零交集。系统变量清单见 [[系统变量]]。 + +## kind 与 kindexpr + +| 概念 | 内容 | 例 | +|------|------|----| +| **kind** | 基础类型字符串(标量或元素类型),是 kindexpr 的叶子 | `uint8` `int32` `float64` `bool` `char/utf32` `stringkeymap` `rwir` `rwfunc` … | +| **kindexpr** | 完整类型表达式(即 langtype 串):可选 `[dims]` + kind;ref 已独立为 head 字节字段,不再是 kindexpr 前缀 | `int8` `[10]int32` `[256,256]uint8` `[]char/utf8·int64` | + +kind 常量集中定义于 `kvspace/include/kvspace/const.h`(C)与 `kvspace-durable/src/const.rs`(Rust),两侧同名同值。剥掉 kindexpr 的所有前缀与 `[dims]` 修饰即得 kind。 + +### kindexpr 文法 + +kindexpr(即 langtype 串)**全前缀**书写,最左符号为最外层构造器,无优先级歧义(对齐 Go/Rust 的 `[]int`): + +``` +kindexpr ::= [ dims ] kind +dims ::= '[' ']' # 变长一维 + | '[' INT ( ',' INT )* ']' # 定长 / 多维(compact 连续布局) +``` + +维数与各维长度编码进 kindexpr 串本身;ref(存储位置)已独立为 head 中的 ref u8 字段(见 [[XValueHead线格式]]),不再是 kindexpr 前缀——storetype 物理字段携带对应的 ndim/dims 结构。完整签名类型表达式(并集 `A|B`、通配 `any`、动态维 `?`、map 键值 `key·value`)见 [[文法]] 与类型系统卷。**源码**类型标注可在最前写 `*`/`@` 前缀表示 ref(如 `*int64`、`@[256,256]uint8`),layout 解析时剥离前缀落成 head `ref` 独立字节;wire 层 langtype 串不含 `*`/`@` 前缀(见 [[总体方案]])。 + "#### -> /lib/kvlang/spec/kvspace模型/键系统与数组访问 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/06-XValueHead\347\272\277\346\240\274\345\274\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/06-XValueHead\347\272\277\346\240\274\345\274\217.kv" new file mode 100644 index 00000000..2a7df57e --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/06-XValueHead\347\272\277\346\240\274\345\274\217.kv" @@ -0,0 +1,28 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/XValueHead线格式 { + r####"# XValueHead 线格式 + +XValue = head + body。head 由三正交字段(ref × storetype × langtype)与辅助字段构成,langtype kindexpr 串为语义类型真相(`kvspace/include/kvspace/kvspace.h`、`kvspace-c/src/xvalue.h`、`kvspace-durable/src/xvalue.rs`,三处 byte-identical): + +``` +head = [headlen u16 LE][ref u8][storetype u8][ro u8][vid u32 LE][body_len u32 LE] + [storetype 物理字段(变长,按 storetype)][langtype kindexpr 串(占至 headlen)] +body = [body_len B raw] +``` + +| 字段 | 大小 | 含义 | +|------|------|------| +| `headlen` | 2B LE | head 总字节数;body 起于偏移 `headlen` | +| `ref` | 1B | 存储位置:0=inline、1=ptr(body=目标 key 路径)、2=@ext(body=扩展世界定位符) | +| `storetype` | 1B | 物理布局(codec 唯一分派):NONE/ATOM/ARRAYND/index/extindex | +| `ro` | 1B | 1=只读,0=可写 | +| `vid` | 4B LE | vthread id(默认 0) | +| `body_len` | 4B LE | body 字节数 | +| (storetype 物理字段) | 变 | ARRAYND:ndim u8 + dims[ndim] u32 LE;index/extindex:成员名 m×n 对齐字符串数组 dims=[len,cap,M](len=成员数、M align8、cap 预留容量);主槽 body 恒空 | +| `langtype` | 变长 | kindexpr 串,占至 `headlen`(长度 = headlen − 当前偏移,无独立长度字段) | + +`langtype` 恒为 head 最后一段,无独立长度字段;body 起始偏移 = `headlen`。ref 与 storetype 各为独立 u8 字节,不再编码进 kindexpr 串。ARRAYND 的 ndim、dims 直接读自 storetype 物理字段,与 langtype 串内 `[dims]` 保持同步(由 codec 保证)。`char/*` kind 恒为 ARRAYND(ndim=1)。None 编码:storetype=NONE、langtype=""、body 空。 + +指针(ref=1)的 head langtype = 目标完整 langtype(kindexpr,含 `[dims]`),storetype = 目标 storetype,body = 目标 key 路径;由 `kvspaceNewPtr(target_kindexpr, target)` 构造,Set 时据此单跳类型检查。 + "#### -> /lib/kvlang/spec/kvspace模型/XValueHead线格式 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" new file mode 100644 index 00000000..ff271e50 --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" @@ -0,0 +1,53 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/数组两种物理形态 { + r####"# 数组的两种物理形态 + +理论上的数组(含多维)在 kvlang 有两种物理落盘形态: + +| 形态 | langtype | 触发写法 | 元素位置 | XValue 数 | +|------|----------|---------|---------|----------| +| **compact** | 元素 kind(`int64`…) | `[1,2,3]`、`[N]T`、`[d0,d1]T` | 连续打包进单个 XValue 的 body | 1 | +| **stringkeymap** | `stringkeymap` | `{v0,v1,…}`、`array·scatter` 等 | 每元素落独立子 key `base·[i]` | N+1 | + +- **compact**:元素连续打包进一个 XValue,`raw_len = ∏dims × 元素字节宽`,head kind 即元素类型、ndim≥1。对齐 C `int[10]`、Go `[10]int`、Rust `[i32;10]`;多维即连续布局的 ndarray/tensor。要求元素定长同类型——含变长字符串字面量的 `[…]` 在 layout 阶段报错。支持 `arr[i]` 随机访问(下标读经 `xv·at` 在 body 内定位),可零拷贝整块读。 +- **stringkeymap**:head kind=`stringkeymap`、ndim≥1,body 只记形状;每个元素是独立子 key,成员名为**坐标段**。变长、可增长、允许变长元素——字符串数组、可追加数组走此形态。stringkeymap **必须** ndim≥1(无维度的键值容器应为 `struct`),且成员名**必须**是坐标段(`kvspace-durable/src/const.rs` 的 `ERR_MAP_NDIM`、`ERR_MAP_COORD`)。 + +## 坐标段 + +stringkeymap 元素的成员名是坐标段,物理 key 形如 `m·[i]`(一维)或 `m·[i,j]`(多维)——`·` 后紧跟方括号坐标,**不是** `m·0` 这类裸后缀。坐标段格式为 `[s0,s1,…]`:十进制、逗号分隔、无空格(`kvspace-durable/src/coord.rs` 的 `format_coord`)。 + +- 结构判定 `is_coord`:`[` 开头、`]` 结尾、内部不含嵌套 `[`/`]`;判定不要求整数(允许含点或字符串坐标)。 +- 排序 `cmp_coord`(row-major):坐标段恒排在非坐标段之前;两个可按整数解析的坐标段按数值升序,否则字典序。 +- 访问:源码 `m·[i]` 对散 key 元素走 `kv·get`(member 名 `[i]`);compact 的 `arr[i]` 走 `xv·at`。二者由括号与形态区分。 + +## 字面量括号即形态 + +| 字面量 | 形态 | 约束 | +|--------|------|------| +| `[1, 2, 3]` | compact(head kind=元素类型) | 元素须定长同 kind;含变长字符串成员时 layout 报错 | +| `{1, 2, 3}` | stringkeymap(散 key) | 变长 / 可增长;字符串数组必须走此形式 | +| `{k = v; …}` | struct 或 stringkeymap | 由 `=` 键值对与散 key 数组区分;空 `{}` 须带类型标注 | + +## 形态转换 + +| rwir | 方向 | 语义 | +|------|------|------| +| `array·scatter(arr) -> dst` | compact → stringkeymap | 连续数组拆成 `dst·[0]…dst·[N-1]` 独立 key | +| `array·compact(arr) -> dst` | stringkeymap → compact | 读散 key 打包成连续数组 | +| `array·append` / `array·slice` | 变长 | 作用于 compact 时先自动 scatter | + +## ref(存储位置维)与扩展句柄 + +| ref | 语义 | body | +|-----|------|------| +| 0(inline) | 内联值 | 数据本体 | +| 1(ptr) | kvspace 内软链接 | 目标 key 路径 | +| 2(@ext) | 扩展存储句柄 | 元存外位置描述符 | + +ref=2 表示值本体在扩展世界(SHM/GPU/文件),head 中 langtype 记录形态(如 `[256,256]uint8`),body 记位置描述符。@ext 只表**单个值**存于扩展世界(compact 形态),与 storetype=index/extindex 无关。读取时按 body 前缀路由兑现(见 [[地址空间]] 扩展存储)。 + +## memindex:定宽目录矩阵 + +storetype=index/extindex 的 head 物理字段记成员名 m×n 对齐字符串数组,参数 `dims=[len, cap, M]`,`M` 向上 8 对齐;`len` 为当前项数、`cap` 为预留容量、`M` 为行宽;主槽 body 恒空。`kvspaceMkindex` 带 `capacity` 参数、构造器 `kvspaceXvalueNewIndexGrow(children, count, cap_hint, m_hint)`(`kvspace-c/src/xvalue.h`)据此保留容量与行宽,使增删可就地覆写、不重分配。 + "#### -> /lib/kvlang/spec/kvspace模型/数组两种物理形态 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" new file mode 100644 index 00000000..ed4942dd --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/08-\346\214\207\344\273\244\345\270\203\345\261\200\346\240\274\345\274\217.kv" @@ -0,0 +1,45 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/指令布局格式 { + r####"# 指令布局格式 + +本章界定函数经 layout 后在 `/lib` 下的 KV 布局:指令如何以坐标键落盘、签名与命名参数如何编码、调用如何经 extindex 复用指令树、参数如何经指针链解析。 + +## 空间布局,非线性字节码 + +传统 VM 编译产线性字节码,调用即压返回地址后跳入口。kvlang 的 layout 不产线性序列,而是**空间布局**:每条指令展开为一组 `[s0,s1]` 坐标键,可逐槽 `kv·get`/`kv·list` 寻址,无需反汇编器。函数体永不被复制——调用时经 extindex 令帧根指向 `/lib` 下同一份指令树(见下 [[调用extindex]] 调用机制)。 + +## 坐标键 `[s0,s1]` + +函数 `/lib/{pkg}·{name}/` 目录下,每个槽以 `[s0,s1]` 坐标键寻址。约定: + +- `s0` = 行号:`[0,·]` 为签名行,`[1,·]` 起为指令行(指令 0 落 `[1,·]`)。 +- `s1` = 列:`0` 为 opcode 槽,负号 `-j` 为第 j 个读槽,正号 `+j` 为第 j 个写槽。 + +入口 PC 为 `…/[1,0]`(`layout/src/keytree.rs` 的 `is_entry_pc`:PC 以 `/[1,0]` 结尾即入口)。 + +### 签名行 + +函数目录 `/lib/{pkg}·{name}/` 自身(主槽):storetype = index、langtype = `rwfunc`(裸种类名,**不含签名**),body = `[nr:u16 LE][nw:u16 LE][dynamic:u8]`(读参个数 `nr`、写参个数 `nw`、是否变参 `dynamic`)。各参数真实类型**不**写进主槽 langtype,而落在**签名行 `[0,x]` 子槽**:`x<0` 读参、`x>0` 写参、`[0,0]` 为锚点;每个 `[0,x]`(`x≠0`)是 storetype=ARRAYND、langtype=`def kindexpr` 的 xvalue,body 为该参数完整 kindexpr 串(见 [[总体方案]])。同理,`def rwir` 目录主槽 langtype = `def rwir`(裸种类名),body 同格式,签名行 `[0,x]` 亦为 `def kindexpr` 子槽,无实现指令行。 + +- 命名参数 → 槽映射:`/lib/{pkg}·{name}/{param}` 是一个 char/utf32 值,内容为该参数的槽坐标串(如 `x` → `"[0,-1]"`、返回值 `R` → `"[0,1]"`)。读参映射到 `[0,-j]`、写参映射到 `[0,+j]`。 + +### 指令行 + +以 `id_val(x:int64) -> (R:int64) { int64·add(x, 0) -> R }` 为例,`/lib/id_val/` 下: + +``` +/lib/id_val/ storetype=index langtype=rwfunc body=[nr=1,nw=1,dyn=0] ← 主槽;langtype 为裸种类名,不含签名 +[0,0] (签名行锚点 s0=0) +[0,-1] storetype=ARRAYND langtype=def kindexpr body="int64" ← 读参1 x 的类型 +[0,1] storetype=ARRAYND langtype=def kindexpr body="int64" ← 写参1 R 的类型 +x char/utf32→[0,-1] ← 命名读参 → 槽坐标(查表用) +R char/utf32→[0,1] ← 命名写参 → 槽坐标(查表用) +[1,0] rwir:int64·add ← 指令0 opcode +[1,-1] rwir:x ← 指令0 读槽1 +[1,-2] int64:0 ← 指令0 读槽2(内联字面量) +[1,1] rwir:R ← 指令0 写槽1 +``` + +opcode 槽 langtype 为 `rwir`(可为 `rwfunc` 引用),body 记 opcode 名。读/写槽的值有两类:`rwir:name` 是对变量的引用(存名字文本,即相对指针),或内联字面量(如 `int64:0`、`char/utf32:"…"`)。opcode 槽永不存变量引用,故值拷贝 `=` 与函数调用 `call` 在 KV 层无歧义。 + "#### -> /lib/kvlang/spec/kvspace模型/指令布局格式 +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" new file mode 100644 index 00000000..84011802 --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/09-\350\260\203\347\224\250extindex.kv" @@ -0,0 +1,31 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/调用extindex { + r####"# 调用:extindex 复用指令树 + +调用一个函数时,运行时**不**拷贝指令,而是为本次调用建帧子树,令帧根成为指向 `/lib/{pkg}·{name}/` 指令树的 extindex 叠加层,再把实参 / 结果地址写进帧根下的 `[0,±j]` 槽: + +- `/lib` 下只有命名参数键(如 `x`、`R`),坐标键 `[0,-j]` / `[0,+j]` 不存在;故运行时向 `frameRoot/[0,±j]` 写入不与只读指令树冲突。 +- 所有帧共享 `/lib` 下同一份指令树,零拷贝;帧根下叠加各自的实参槽与局部变量。 + +崩溃恢复由此自然成立:PC 是路径字符串、帧根落 KV、返回点落 KV——进程重启后可从 kvspace 续跑。 + +## 参数解析:指针链 + +指令读到 `rwir:x` 时,`x` 是相对当前帧的名字。运行时经指针链解析到实参真实位置: + +``` +1. 读 frameRoot/x → "[0,-1]" 名字 → 槽坐标 +2. 读 frameRoot/[0,-1] → 实参 key 路径 槽 → 实参地址 +3. 读该路径 → 值 解引用 +``` + +第 1 跳的 `frameRoot/x` 由 `/lib` 指令树经 extindex 透出(layout 写入、只读),第 2 跳的 `frameRoot/[0,-1]` 由运行时在调用点写入。读参地址可指向调用方值以实现零拷贝重定向(见 [[系统变量]] 的 `‥rparam` / `‥wparam`)。 + +## init 与标签 + +- lib 内的裸指令收集为隐式 `init` 函数(无 `init` 关键字),同样以上述布局落 `/lib/{pkg}·init/`。 +- 具名块的标签落 `/lib/{pkg}·{name}/‥labels/{label}` → 指令序号(`layout/src/code.rs`、`layout/src/keytree.rs`)。 + +签名类型表达式文法见 [[文法]];帧的系统变量见 [[系统变量]];坐标键与 kindexpr 的分隔符语义见 [[键系统与数组访问]]。 + "#### -> /lib/kvlang/spec/kvspace模型/调用extindex +} diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" new file mode 100644 index 00000000..2d2f70ab --- /dev/null +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/10-\347\263\273\347\273\237\345\217\230\351\207\217.kv" @@ -0,0 +1,53 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/kvspace模型/系统变量 { + r####"# 系统变量 + +运行时为它管理的对象附带**系统变量**(影子元数据),以 `{对象key}/‥{名}` 形式存放:宿主对象 `/` 下探一层、键名以 `‥`(U+2025)开头。标识符禁以 `‥` 开头,故所有 `‥` 前缀键均为核心保留,用户代码无法直接读写——类比 Unix 隐藏文件,默认视图不显示、引擎可见。 + +系统变量(`X/‥名`,`/` + `‥` 前缀)与用户成员(`X·名`,`·` 中点键)正交,零交集(见 [[键系统与数组访问]] 键形态三分)。生命周期绑定:`X/‥*` 落在 X 的 `/` 子树内,删除 X 子树即连带清除其全部系统变量。 + +## vthread 系统变量 + +宿主 = `/vthread/{vid}`(常量:`runtime/src/keytree.c`、`layout/src/keytree.rs`): + +| 键 | 语义 | +|----|------| +| `‥pc` | 当前执行 PC(绝对路径字符串) | +| `‥status` | 运行状态:`init` / `running` / `wait` / `paused`;进入终态时删除并 Notify 返回值 | +| `‥status/msg` | 状态附带消息;错误时形如 `‥error/msg` 承载错误文本 | +| `‥debugger` | 调试控制:空为正常,`paused` 为暂停;`debugger()` 内建写此键 | +| `‥ctime` | 创建时刻 | + +## 帧系统变量 + +宿主 = 帧根(rwfunc 帧或块帧的根路径): + +| 键 | 语义 | +|----|------| +| *(帧根 extindex)* | rwfunc 帧根经 extindex 指向 `/lib/{pkg}·{name}/` 只读指令树;块帧不建 extindex | +| `‥lib` | rwfunc 帧的 lib 路径,用于识别 rwfunc 帧边界 | +| `‥callpc` | 帧内执行进度(每 op 更新;块帧重入亦更新) | +| `‥returnpc` | 返回地址(帧创建时固化;块帧仅首次设置,重入不覆写) | +| `‥ro` | 只读参数名单,写槽检查用 | +| `‥rparam/{name}` | 读参重定向:存调用方值的绝对路径,读参从此路径直读(零拷贝) | +| `‥wparam/{name}` | 写参重定向:存调用方写目标的绝对路径,写参直写此路径 | + +调用点如何写实参 / 结果槽、如何经指针链解析读到重定向路径,见 [[指令布局格式]]。 + +## 函数标签 + +具名作用域的标签不落在帧上,而落在函数的 `/lib` 定义处:`/lib/{pkg}·{name}/‥labels/{label}` → 指令序号(`layout/src/keytree.rs`、`layout/src/code.rs`)。 + +## 语法层保留名 `._` + +`._` 是源码层的丢弃槽占位符:写目标为 `._` 时不落 kvspace(帧槽键构造遇此名返回空路径)。它是语法占位符,**不**是 `‥` 系统变量,不落盘。 + +## `debugger()` + +`debugger()` 内建对齐 V8/TypeScript 的 `debugger;` 语句:源码内联暂停点。非调试模式下为 no-op;调试模式下将 `‥status` 置 `paused`,由外部驱动改回 `running` 恢复执行。 + +## 域与变量之别 + +注意区分:[[地址空间]] 的结构**域**(`/lib`、`/vthread`、`/networld` 顶层树)与对象随身的 `‥` 系统**变量**是两种机制。域是路径前缀赋予的固定结构语义,系统变量是任一对象 `/` 子树内的影子元数据。 + "#### -> /lib/kvlang/spec/kvspace模型/系统变量 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/01-\347\247\215\347\261\273\344\270\216\345\256\232\345\256\275\347\261\273\345\236\213.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/01-\347\247\215\347\261\273\344\270\216\345\256\232\345\256\275\347\261\273\345\236\213.kv" new file mode 100644 index 00000000..9883c354 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/01-\347\247\215\347\261\273\344\270\216\345\256\232\345\256\275\347\261\273\345\236\213.kv" @@ -0,0 +1,45 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/种类与定宽类型 { + r####"# 种类与定宽类型 + +kvlang 是**严格定宽类型语言**。 +每个变量、参数、返回值都有确定的种类(kind);无无类型变量,运行时种类不隐式改变。 +类型信息随值自描述:每个 XValue 的类别由 head 三个正交字段表达(见 [[总体方案]] 与 [[XValueHead线格式]]): +- `ref`(存储位置):0=inline、1=ptr(软链)、2=@ext(单值在扩展世界); +- `storetype`(物理布局,codec 视角,闭合集:NONE/ATOM/ARRAYND/index/extindex); +- `langtype`(语义类型,即完整 kindexpr 串,变长,见 [[kindexpr签名类型表达式]])。 + +## 锚例 + +- 定宽构造与转换、float→int 截断、窄化回绕:`tutorial/01-basics/cast.kv`、`tutorial/01-basics/numtypes.kv`。 +- 算术保宽与混合提升:`tutorial/01-basics/type_narrowing.kv`。 +- 字符编码与索引:`tutorial/11-string/05-encodings.kv`、`06-convert.kv`、`02-char-ord.kv`。 +- 时间种类:`tutorial/01-basics/time.kv`、`tutorial/13-stdlib/time/`、`13-stdlib/duration/`。 +- 类型错误(bool 算术、跨种类比较、无符号取负、None、compact 变长串):`tutorial/error_cases/type_error/`、`error_cases/none_type_error/`。 + +## 数值运算域 + +1. **同种类算术产生同种类结果**,溢出按补码回绕:`int8(127)+int8(1)`=-128、`uint8(255)+uint8(1)`=0、`int32(100)×int32(100)`=10000(kind 保持 `int32`)。float32 运算不升 float64:`float32(16777216)+float32(1)`=16777216.0。 +2. **混合宽度整数向更宽种类提升**:`int8(100)×int16(2)`=200(结果 `int16`);`int8(7)%int16(3)`=1(结果 `int16`)。 +3. **任一侧浮点则提升为浮点**:`int8(120)÷float32(10)`=12.0(结果 `float32`);两整数 `÷` 为整除(`int8(120)÷int8(10)`=12)。 +4. **无符号取负** `-(uint8 值)` TypeError(对齐 C 警告 / Rust、Go 编译错误)。 +5. **bool 不参与算术**:`true + false` TypeError。 +6. **None 参与算术** TypeError;**None 参与分支条件** TypeError。 +7. **跨种类比较** TypeError(`int64(5) == "hello"` 报错);数值种类之间可跨宽度比较(`3 == 3.0` 为 true)。 + +## bool 铁律 + +`bool` 值**只能**为 `true`/`false`,**禁止**一切隐式布尔化: + +| 输入 | kvlang | +|------|--------| +| `true` / `false` | 合法 | +| `0` / `1` 整数 | TypeError——须显式写 `!= 0` | +| 非空字符串 | TypeError——须显式写 `!= ""` | +| None | TypeError | + +分支条件(`if`/`while`/`br`)的条件槽**必须**是 `bool`。强制显式比较使控制流走向可被静态判定,不依赖运行时种类。 + +逻辑运算 `&&`/`||`/`!` 的操作数**必须**是 `bool`,非 bool 即 TypeError;`bool(x)` 构造器只接受 `true`/`false`,其余种类 TypeError(对整数须写 `!= 0`)。 +"#### -> /lib/kvlang/spec/类型系统/种类与定宽类型 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" new file mode 100644 index 00000000..0e36255e --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/02-\347\247\215\347\261\273-\346\240\207\351\207\217\346\227\266\351\227\264\345\256\271\345\231\250.kv" @@ -0,0 +1,52 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/种类_标量时间容器 { + r####"# 种类(kind) + +**种类**是值的运行时类别。种类字符串是 kindexpr 的基(base)部分(见 [[kindexpr签名类型表达式]])。 + +### 定宽数字种类 + +kvlang 的数字种类**必须携带精确位宽**: + +| 类别 | 种类 | +|------|------| +| 有符号整数 | `int8` `int16` `int32` `int64` | +| 无符号整数 | `uint8` `uint16` `uint32` `uint64` | +| IEEE 754 浮点 | `float32` `float64` | + +整数按小端补码编码,浮点按 IEEE 754 编码。 + +**铁律——无家族简写**。`int`、`uint`、`float`、`num`、`char` 这类不含位宽/编码的简写**不是**合法种类,在任何类型标注中均**不得**出现。位宽与字符编码是**开放集合**(未来可扩 `int4`、`fp8`、`fp16` 等),封闭的家族枚举必然漏项;多态**必须**靠 [[kindexpr签名类型表达式]] 的显式 `|` 枚举表达(如 `int8|int16|int32|int64`)。 + +### bool 与字符种类 + +| 种类 | 说明 | +|------|------| +| `bool` | 1 字节:`0`=false、`1`=true | +| `char/utf32` | 码点序列,4B×N,定宽、可索引;**字符串字面量的默认种类** | +| `char/ascii` | ASCII 字节序列,1B×N,定宽、可索引 | +| `char/utf8` | UTF-8 字节序列,1B×N,变宽、**禁索引**(仅存储与交换) | + +字符三种编码的判定为前缀相等:某种类是字符种类当且仅当它以 `char/` 起始。定宽编码(`utf32`、`ascii`)**必须**支持 O(1) 码点索引;变宽编码 `utf8` 的按码点索引操作(`string·char`、`s[i]` 等)**必须**报 TypeError。单个字符标注为 `char/<编码>`(标量),字符串标注为 `[]char/<编码>`(序列);字符种类的值恒为一维序列。 + +### 容器与结构种类 + +| 种类 | 说明 | +|------|------| +| `stringkeymap` | map 容器,kindexpr 为 `key·value`(如 `[]char/utf8·int64`) | +| `struct` | 结构原型实例的种类(见 [[容器]] 与结构声明) | +| `index` | **storetype**:索引型节点(map/struct 值容器的 `{key}·` memindex、lib/rwfunc 目录节点);物理=m×n 对齐字符串数组,dims=[len,cap,M](M align8) | +| `extindex` | **storetype**:可扩展索引型节点(vthread 运行栈等,cap 可增长);与 `ref`/`@ext` 无关,extindex 是 storetype 不是 ref=2 | + +容器的物理组成(值 + memindex)见 [[容器]]。 + +### 时间种类 + +| 种类 | 说明 | +|------|------| +| `time` | 时刻,由 `time·now()` 等产生 | +| `duration` | 时长,由 `time/duration·*` 产生 | + +`time`/`duration` 是不透明标量种类,其运算由 time 标准库提供,字节布局不由本卷规定。 +"#### -> /lib/kvlang/spec/类型系统/种类_标量时间容器 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" new file mode 100644 index 00000000..b300d6c9 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" @@ -0,0 +1,22 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/种类_代码与特殊值 { + r####"# 种类(kind)—— 代码节点与特殊值 + +### 代码种类 + +下列 langtype 描述 KV 树中的代码与目录节点(见 [[指令布局格式]]): + +- `rwir`:无 kvlang 实现体,指令名文本,storetype=ARRAYND 叶子;由 runtime 经 def rwir 路由直接兑现。 +- `def rwir`:**路由头**,仅存于 `/lib/`,是 rwir 统一调度入口,storetype=index(定义节点),**无 kvlang 实现体**;**不叫"扩展算子"**,**不得**出现在用户书写的类型标注中。每个 runtime 只声明 **myrwircaps**(该 runtime 能兑现的 rwir 能力集);rwir 若不在本 runtime myrwircaps 内须经 `/lib/` 的 def rwir 路由给能兑现它的其它 runtime。 +- `rwfunc`:有 kvlang 实现体(`/` 目录下的指令槽序列),storetype=index(静态定义)或 extindex(vthread 运行栈),可被任意 main-runtime 直接解释执行。 + +**故有 def rwir 而无 def rwfunc**:rwfunc 有 kvlang 实现体(一串 rwir),任意 main-runtime 逐条解释即可,无需路由头;rwir 无实现体,不在本 runtime myrwircaps 内时必须路由,故需 def rwir。 + +### None 与 ptr 不是种类 + +- **None** 不是种类:它是 `storetype=NONE` 的空结构体 XValue。任何有名变量都有地址、都有 XValue;未赋值即 None。None 参与算术、比较、分支条件、类型构造一律 TypeError(见下文与 [[执行模型]])。 +- **ptr(软链接)** 不是种类:它是 head 的 `ref=1`(存储位置维),head 的 storetype/langtype 描述目标的完整形态,body 存目标 key 路径(见 [[容器]])。 + +> ptr(`ref=1`)是 head 的 ref 字段(存储位置维);kvlang **源码**类型标注最前仍可书写 `*`/`@` 前缀(如 `*int64`、`@[256,256]uint8`),表 ref,layout 解析时剥离该前缀落成 head `ref` 独立字节;**wire 层 langtype 串不含 `*`/`@` 前缀**,存储位置一律经 head `ref` 字节表达。extindex 是 storetype(物理布局维),与 ref=2 无关(见 [[总体方案]])。 +"#### -> /lib/kvlang/spec/类型系统/种类_代码与特殊值 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/04-\346\236\204\351\200\240\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/04-\346\236\204\351\200\240\345\231\250.kv" new file mode 100644 index 00000000..b7c4decf --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/04-\346\236\204\351\200\240\345\231\250.kv" @@ -0,0 +1,26 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/构造器 { + r####"# 构造器兼转换器 + +十个定宽数字种类名与三个字符编码名**既是构造器也是转换器**——同名一元调用把实参转换为该种类: + +```kv +f = float32(3) // kind=float32,值 3.0 +i = int8(0.1) // 0(float→int 向零截断) +int8(300) -> w // 44(窄化 = 补码回绕) +t = char/utf32(s) // 变宽 utf8 转定宽 utf32(转后可索引) +u = char/ascii(t) // 非 ASCII 码点 → TypeError +``` + +转换规则(对齐 C / Go / Rust `as`): + +| 规则 | 语义 | +|------|------| +| float → int | 向零截断(`int32(3.9)`=3、`int32(-2.7)`=-2、`int64(0.5)`=0) | +| 整数窄化 | 补码回绕(`int8(300)`=44、`uint8(-1)`=255、`int16(70000)`=4464、`int32(2147483648)`=-2147483648) | +| float32 精度 | 收窄至单精度(`float32(3.141592653589793)`=3.1415927、`float32(16777217)`=16777216.0) | +| None 输入 | TypeError(`int64(None)` 报错,不得静默产生 0) | + +构造器接受字面量与变量表达式(`42.9 -> x; int8(x)`)。种类构造的结果 XValue 以该种类名为 kindexpr 落盘,精度信息随值保留。 +"#### -> /lib/kvlang/spec/类型系统/构造器 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-kindexpr\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-kindexpr\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" new file mode 100644 index 00000000..ccf91c4f --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-kindexpr\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" @@ -0,0 +1,15 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/kindexpr签名类型表达式 { + r####"# kindexpr 签名类型表达式 + +**类型表达式**是函数(rwfunc)与 def rwir 签名中参数、返回值的类型标注语法。它既供人和工具读写,也逐字节落盘为运行时匹配的依据。同一套表达式贯穿 layout(静态校验)与 runtime(值匹配),故本卷统称 **kindexpr**。 + +完整权威文法见 [[文法]](附录)的「类型表达式」小节;本章只界定其语义。 + +## 锚例 + +- 标量并集、多维形状、动态维、字符串标注:`tutorial/10-types/`(`01-typed-map.kv`~`05-tuple-key.kv`)。 +- 嵌套 mapexpr 与元组键落盘 round-trip:`tutorial/10-types/04-nested-type.kv`、`05-tuple-key.kv`。 +- 变参 `...`:`print`/`println`/`min`/`max` 全 tutorial 广泛使用。 +"#### -> /lib/kvlang/spec/类型系统/kindexpr签名类型表达式 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/06-\345\275\222\345\261\236\344\270\216\350\220\275\347\233\230.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/06-\345\275\222\345\261\236\344\270\216\350\220\275\347\233\230.kv" new file mode 100644 index 00000000..20c85528 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/06-\345\275\222\345\261\236\344\270\216\350\220\275\347\233\230.kv" @@ -0,0 +1,22 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/归属与落盘 { + r####"# 归属与落盘 + +| 归属 | 存放位置 | 种类 | +|------|---------|------| +| 用户函数签名 | `/lib/·` | `rwfunc` | +| def rwir 路由头 | `/lib/` | `def rwir` | +| native 算子签名 | C 注册表(不落盘) | —— | + +**铁律**:`rwfunc` 每个参数落盘的 kindexpr 与源码里的类型标注**逐字节相同**(同一文法,含 `...`);`def rwir` 源码不声明,其参数 kindexpr 由各 runtime 注册时按同一文法写入。签名**不写进 langtype 串**,也不塞进主槽 body,分两处存(详见 [[总体方案]]「签名不入 langtype 串」): + +1. **主槽计数头**:主槽(如 `/lib/pkg·funca`)body 只记 `[nr:u16 LE][nw:u16 LE][dynamic:u8]`——读参个数 `nr`、写参个数 `nw`、是否变参 `dynamic`(末位读参 `...`);**不含任何参数 kindexpr 串**。 +2. **签名行 `[0,x]` 各槽**:每个参数的 kindexpr 串落在签名行坐标 `[0,x]`(`x<0` 读参、`x>0` 写参、`[0,0]` 签名行锚点),每槽为一个 `def kindexpr` 类型的 xvalue,body 存该参数完整 kindexpr 串。 + +故 rwfunc/def rwir 主槽的 langtype 就是裸种类名 `rwfunc`/`def rwir` 本身,不再是「签名 kindexpr 串」。native 算子的 kindexpr 内联在 C 注册表中,是唯一权威来源,运行时匹配**直接读 C 表、不回查 kvspace**;数值多类型算子在 C 表中融合为单条(如 `add(a:int8|…|float64, b:…)`)。 + +## 强制类型标注 + +`rwfunc` 签名中每个参数与每个返回值**必须**声明类型(`name:type_expr`)。缺标注的签名**拒绝装载**——layout 在语法检查期报错,指出该参数/返回值无类型标注。类型标注须通过 kindexpr 合法性校验(拒绝非种类名,见下)。`def rwir` 路由头由 runtime 注册(源码不声明),其参数类型在注册接口处按同一合法性规则校验。 +"#### -> /lib/kvlang/spec/类型系统/归属与落盘 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/07-\346\226\207\346\263\225\344\270\216\345\220\210\346\263\225\346\200\247.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/07-\346\226\207\346\263\225\344\270\216\345\220\210\346\263\225\346\200\247.kv" new file mode 100644 index 00000000..66549b68 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/07-\346\226\207\346\263\225\344\270\216\345\220\210\346\263\225\346\200\247.kv" @@ -0,0 +1,33 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/文法与合法性 { + r####"# 文法与合法性 + +类型表达式的构成(详见 [[文法]]): + +- **并集** `A|B`——以 `|` 拆分为若干 atom,表「A 或 B」。多态**必须**用显式并集表达。 +- **atom** = 形状(shape)或 map 表达式(mapexpr)。 +- **shape** = 可选维度 `dims` 后跟 `any` 或精确种类名。 +- **dims** = `[]`(无内容)或 `[dim,dim,…]`;`dim` 为整数或 `?`(动态维)。 +- **mapexpr** = `key·value`,`value` 递归为完整类型表达式(故 map 可无限嵌套)。 +- **key** = `[]char/<编码>`(字符串键)或 `[scalar,…]`(标量元组键);`scalar` 限 `bool` 与十个定宽数字种类。 +- **any** = 通配,匹配任意种类;是**唯一**简写。 +- **structref** = 以 `/` 起始的原型路径(如 `/lib/Point`),作为 atom 标注结构类型(见 [[容器]] 的 struct)。 +- **变参** `...`——见下。 + +**合法性校验**(layout 与 runtime 一致):每个 atom 的基须为 `known` 种类名或 `any`;键须为 `[]char/<编码>` 或全标量元组。非法示例及拒因: + +| 非法 | 拒因 | +|------|------| +| `int` `float` `char` | 家族简写,非种类名 | +| `int64*` | 后缀 `*` 位置非法;`*`/`@` 前缀**仅**可书写在源码类型标注最前,layout 剥离落 `ref` 字节,wire langtype 不含前缀 | +| `char/utf8·int64` | map 键未加方括号 | +| `[]int32·int64` | map 字符串键的编码非 `char/*` | +| `[?]·int64` | map 键含维度 | +| `[foo,int32]·int64` | 元组键含非标量元素 | +| 裸 `[]` / 裸 `[?]` | 无基种类 | + +源码类型标注最前的 `*`/`@` 前缀(如 `*int64`、`@[256,256]uint8`)**合法**,layout 解析时剥离前缀落成 head `ref` 字节,再校验剩余 kindexpr;wire 层 langtype 串不含前缀(见 [[总体方案]])。 + +`def rwir` 是路由头 langtype(仅落于 `/lib/`),**不得**作为类型标注书写;`rwfunc` 有 kvlang 实现体,可被任意 main-runtime 直接解释,**无需也没有** def rwfunc(见 [[种类与定宽类型]])。 +"#### -> /lib/kvlang/spec/类型系统/文法与合法性 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/08-\345\214\271\351\205\215\350\257\255\344\271\211\344\270\216\345\217\230\345\217\202.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/08-\345\214\271\351\205\215\350\257\255\344\271\211\344\270\216\345\217\230\345\217\202.kv" new file mode 100644 index 00000000..b85adac4 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/08-\345\214\271\351\205\215\350\257\255\344\271\211\344\270\216\345\217\230\345\217\202.kv" @@ -0,0 +1,48 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/匹配语义与变参 { + r####"# 匹配语义与变参 + +## 变参 `...` + +变参尾缀 `...` **仅**允许加在**末位读参**的类型表达式上(如 `A:any...`、`A:int64|float64...`),表「0..N 个同型实参」,用于 `print`/`println`/`min`/`max` 等 arity 开放的算子。 + +- 非末位参数带 `...`:装载期错误。 +- 写参(返回值)带 `...`:装载期错误。 + +匹配时,变参把所有尾随实参逐个按去掉 `...` 后的类型表达式判定。 + +## 匹配语义 + +一个值(种类 `k`、维数 `n`、维列表 `d`)匹配类型表达式 `E`:按 `|` 拆成若干 atom,**任一** atom 命中即匹配。单个 atom 的判定: + +1. atom 为 `any` → 命中。 +2. atom 含 `·`(mapexpr)→ 命中当且仅当 `k == stringkeymap`。键/值结构在**匹配期不再校验**(已在装载期校验合法性)。 +3. atom 有 `[` 前缀(shape)→ 先按维度段做形状匹配,再以剩余基种类匹配 `k`。 +4. atom 无 `[` 前缀(标量)→ 命中当且仅当 `n == 0` 且基种类等于 `k`(`any` 除外走精确字符串相等)。 + +**形状匹配**(维度段 → `n`,`d`): + +| 维度段 | 命中条件 | +|--------|---------| +| 空(`[]`)或 `[?]` | 恰 `n == 1`,长度任意 | +| `[d0,d1,…]` | 逗号项数 `== n`,且逐维:`?` 跳过、否则 `d[i] == di` | + +`[]` **等价** `[?]`——均为「恰一维、任意长」。字符串标注 `[]char/<编码>` 即一维字符序列。 + +> 规范注记:`[]` 恒为「恰一维」是 normative 规则。runtime 匹配器(`runtime/src/kindexpr.c`)已一致;layout 匹配器(`layout/src/kindexpr.rs`)当前放宽为「任意 rank ≥1」,为已裁决待修的实现偏差,须收敛至「恰一维」。 + +任一失配即 TypeError:装载期由 layout 报诊断,运行期写 vthread 错误并停机。 + +XValue head 三正交维编码见 [[总体方案]](本卷只界定 langtype 作为 kindexpr 串的文法与匹配语义)。 + +## 多态派发 + +- **native 算子**(如 `+`)内建多态,签名只做类型检查;分派在 C 算子内部按实参种类完成。 +- **rwir 指令**(如 `numpy·add`)经 def rwir 路由到有对应 myrwircaps 的 runtime,按实参实际种类分派;高维形状由该 runtime 自行处理。 +- **用户函数**(rwfunc)以并集声明接受集,函数体内用种类分支处理各分支。 + +## 语法冲突规避 + +签名以 `,` 分隔参数,形状维度段内也用 `,` 分隔维。签名解析器**跟踪方括号深度**:见 `[` 进形状态、见 `]` 退出;形状态内的 `,` 归维、态外的 `,` 归参数。类型表达式内不出现 `(` `)` `:` `->` 空格,故与签名外层结构无歧义。 +"#### -> /lib/kvlang/spec/类型系统/匹配语义与变参 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/09-\346\225\260\347\273\204\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/09-\346\225\260\347\273\204\345\275\242\346\200\201.kv" new file mode 100644 index 00000000..c1a4892d --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/09-\346\225\260\347\273\204\345\275\242\346\200\201.kv" @@ -0,0 +1,28 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/数组形态 { + r####"# 数组形态 + +kvlang 的数组有**两种物理形态**:**compact**(稠密:全量元素连续打包进单个 XValue)与 **stringkeymap**(稀疏:散 key,每元素独立落库,未 set 的 key 不存在)。两形态由字面量括号选定,访问算子不同,但都被 `ndarray·*` 元数据算子统一观测。 + +## 锚例 + +- compact 建/读/写/求和:`tutorial/04-ndarray/continuous.kv`。 +- head 种类与 kindexpr、reinterpret:`tutorial/04-ndarray/xv_meta.kv`、`xv_reinterpret.kv`。 +- numel/dim/shape、xv·at/set:`tutorial/04-ndarray/xv_shape.kv`。 +- 多维下标写、reshape:`tutorial/04-ndarray/subscript_write.kv`。 +- stringkeymap 字面量与遍历:`tutorial/04-ndarray/sparse_literal.kv`、`string_array.kv`。 +- 两形态互转:`tutorial/04-ndarray/separated.kv`。 +- 多值坐标段:`tutorial/04-ndarray/geo_coord.kv`。 +- 括号规约错误(compact 容变长串):`tutorial/error_cases/type_error/compact_string_array.kv`。 + +## 括号规约 + +| 字面量 | 形态 | 落库 | 约束 | +|--------|------|------|------| +| `[e0, e1, …]` | compact | 元素连续打包进单个 XValue,head 的 langtype(kindexpr)= 元素种类(如 `[4]int64`),storetype=ARRAYND | 定长、同种类;元素**不得**为变长(字符串字面量禁入) | +| `{v0, v1, …}` | stringkeymap | 每元素落 `base·i` 独立子 key,容器值 langtype=map kindexpr(如 `[]char/utf8·int64`),storetype=index,主槽 body 空,成员名索引落 `base·` memindex(m×n 对齐字符串数组) | 变长/可增长;字符串数组等变长集合用此形式 | + +- compact 字面量降级为 `array(…)` 构造。变长字符串元素进 compact `[…]` 在 layout 语法检查期报错(`compact array [...] cannot hold variable-length string element`)。 +- stringkeymap 字面量降级为 `map(…)` 构造,只作赋值右值或 `for-in` 源。 +"#### -> /lib/kvlang/spec/类型系统/数组形态 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/10-compact\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/10-compact\345\275\242\346\200\201.kv" new file mode 100644 index 00000000..c4634903 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/10-compact\345\275\242\346\200\201.kv" @@ -0,0 +1,46 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/compact形态 { + r####"# compact 形态 + +compact 数组是**单个 XValue**:head 的 langtype(kindexpr)形如 `[4]int64`(含维度+元素种类),storetype=ARRAYND,body 是连续的定宽元素字节。 + +```kv +a:[]int64 = [10, 20, 30, 40] // head kindexpr = [4]int64,body = 4×8 字节 +``` + +- head 的 langtype = kindexpr 串形如 `[4]int64`(元素种类为 `int64`,**非** `array`);storetype=ARRAYND(见 `tutorial/04-ndarray/xv_meta.kv`)。 +- 字符串是 compact 的字符序列:`s:char/utf8 = "hi"` 的 kindexpr 为 `[2]char/utf8`。单个字符串是 compact;**字符串的数组**是变长集合,必须 stringkeymap。 + +### compact 元素访问 + +下标 `a[i]` 是 compact 元素访问:读降级为 `xv·at`,写(`a[i]=v` 或 `v -> a[i]`)降级为 `xv·set`。 + +```kv +a[1] -> e // 等价 xv·at(a, 1) -> e +a[2] = 99 // 等价 xv·set(a, 2, 99) -> a +99 -> a[0] // 右箭头下标写,等价 xv·set(a, 0, 99) -> a +``` + +`xv·at` / `xv·set` **仅接受 compact 数组**(维数非 0):对 stringkeymap 或标量报 TypeError。索引个数**必须**等于维数,按 row-major 折算扁平偏移;越界报 IndexError。写回保持 dims 不变。 + +### 多维 compact(tensor) + +多维 compact 由 `xv·reshape` 从一维铺开: + +```kv +m = xv·reshape([1, 2, 3, 4], 2, 2) // dims = [2,2] +7 -> m[1, 0] // 多维下标写 +m[0, 1] -> x // 多维下标读 +``` + +多维下标个数须等于维数(此例 2),按 row-major 折算(见 `tutorial/04-ndarray/subscript_write.kv`)。 + +### head 观测与重解释 + +| 算子 | 语义 | +|------|------| +| `xv·kindexpr(a)` | 返回 head 的 kindexpr 串(如 `[4]int64`) | +| `xv·bodylen(a)` | 返回 body 字节数 | +| `xv·reinterpret(a, "[]uint8")` | 原样保留 body 字节,只替换 kindexpr(`[3]int64` 24 字节 → `[24]uint8`) | +"#### -> /lib/kvlang/spec/类型系统/compact形态 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" new file mode 100644 index 00000000..c95840cd --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/11-stringkeymap\345\275\242\346\200\201.kv" @@ -0,0 +1,24 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/stringkeymap形态 { + r####"# stringkeymap 形态 + +stringkeymap 数组的**容器值**是一个 storetype=index 的 XValue(langtype 为 map kindexpr,如 `[]char/utf8·int64`;body 空,成员名索引落 `base·` memindex,dims=[len,cap,M]),**每个元素**是独立子 key `base·i`: + +```kv +nums = {10, 20, 30, 40} // 每元素落 nums·0 … nums·3 +for (x in nums) { … } // 遍历散 key 元素 +names = {"apple", "banana", "cherry"} // 变长字符串数组必须 stringkeymap +``` + +散 key 元素以坐标段 `base·[i]` 访问(经 `kv·get`),或以 `for-in` / `kv·listlen` / `kv·listn` 遍历。坐标段支持多值键(含小数):`geo·[39.9,116.4] = "Beijing"`,物理以字符串格式落 key(见 `tutorial/04-ndarray/geo_coord.kv`)。 + +## 两形态互转 + +| 算子 | 语义 | +|------|------| +| `array·scatter(a)` | compact → stringkeymap(散 key) | +| `array·compact(b)` | stringkeymap → compact | + +字面量初始化默认按括号定形态;`scatter`/`compact` 显式转换(见 `tutorial/04-ndarray/separated.kv`)。 +"#### -> /lib/kvlang/spec/类型系统/stringkeymap形态 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/12-\347\250\240\345\257\206vs\347\250\200\347\226\217.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/12-\347\250\240\345\257\206vs\347\250\200\347\226\217.kv" new file mode 100644 index 00000000..68a5e482 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/12-\347\250\240\345\257\206vs\347\250\200\347\226\217.kv" @@ -0,0 +1,25 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/稠密vs稀疏 { + r####"# 稠密 compact vs 稀疏 stringkeymap:同一逻辑形状的两种落库 + +同一个逻辑上的 `[1024,1024]int32` 二维数组,可按稀疏度选两种截然不同的物理存储: + +| 维度 | compact(稠密) | stringkeymap(稀疏) | +|------|----------------|---------------------| +| langtype | `[1024,1024]int32` | 坐标元组键 map,如 `[int32,int32]·int32` | +| storetype | ARRAYND | index | +| 落库 | 全量 1024×1024 元素连续打包进**单个** XValue 的 body(恒 1024×1024×4 = 4 MiB,未写元素亦占位) | 每个写过的坐标落一个独立子 key `base·[i,j]`;**未 set 的坐标子 key 根本不存在**,不占空间 | +| 缺失元素 | 无「缺失」——全部就位(默认 0) | 读未写坐标返回 None(`kv·get` 缺失语义) | +| 随机访问 | `a[i,j]` 按 row-major O(1) 偏移 | `base·[i,j]` / `kv·get` 按 key 查找 | +| 适用 | 稠密张量、绝大多数元素有值、需连续内存与 O(1) 下标 | 稀疏矩阵、坐标空间极大而实际有值项稀少 | + +```kv +dense:[]int32 = xv·reshape(array(…), 1024, 1024) // 4 MiB 稠密块,storetype=ARRAYND +sparse:[int32,int32]·int32 = {} // 稀疏,storetype=index +99 -> sparse·[3,7] // 仅此一项落子 key sparse·[3,7] +sparse·[0,0] -> x // 未写坐标 → None(子 key 不存在) +``` + +两种落库共享 `ndarray·*` 观测(`numel` 对稀疏形态返回**实存成员数**,非坐标空间容量),并经下节 `array·scatter`/`array·compact` 互转。 +"#### -> /lib/kvlang/spec/类型系统/稠密vs稀疏 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/13-ndarray\345\205\203\346\225\260\346\215\256\347\256\227\345\255\220.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/13-ndarray\345\205\203\346\225\260\346\215\256\347\256\227\345\255\220.kv" new file mode 100644 index 00000000..0a4bb121 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/13-ndarray\345\205\203\346\225\260\346\215\256\347\256\227\345\255\220.kv" @@ -0,0 +1,20 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/ndarray元数据算子 { + r####"# ndarray 元数据算子 + +三个算子统一观测数组形状,对两形态均适用: + +| 算子 | 语义 | +|------|------| +| `ndarray·numel(a)` | 元素总数(compact 读 head array_len;stringkeymap 读成员数) | +| `ndarray·dim(a)` | 维数(head ndim) | +| `ndarray·shape(a)` | 各维长度,返回一维 `int64` compact 数组 | + +```kv +a:[]int64 = [10, 20, 30, 40] +ndarray·numel(a) -> n // 4 +ndarray·dim(a) -> d // 1 +ndarray·shape(a) -> s // [4];s[0] = 4 +``` +"#### -> /lib/kvlang/spec/类型系统/ndarray元数据算子 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" new file mode 100644 index 00000000..1d9f925e --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" @@ -0,0 +1,40 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/容器 { + r####"# 容器 + +kvlang 的复合命名数据只有两种容器语义类型:**stringkeymap**(键-值映射)与 **struct**(异构命名成员)。kvlang **没有** `dict` 种类,也没有 `object` 或 `scope` 种类(见 [[总体方案]])。容器物理落 storetype=index,成员经 `{key}·` memindex 兄弟槽枚举(与 lib/rwfunc 目录节点用 `/` 子路径不同)。此外 **ptr**(软链接)是 ref=1 的值形态,指向另一 key。 + +## 锚例 + +- stringkeymap 字面量与访问:`tutorial/10-types/01-typed-map.kv`、`tutorial/05-dict/literal.kv`。 +- stringkeymap 与动态键、缺失返 None:`tutorial/10-types/02-string-map.kv`、`05-dict/kv_has_string_key.kv`。 +- 嵌套 map 与元组键签名:`tutorial/10-types/04-nested-type.kv`、`05-tuple-key.kv`。 +- struct 声明/实例化/默认值/多类型字段:`tutorial/12-struct/00-point.kv`、`01-fields.kv`。 +- 指针解引用、局部结构体、链表/树/图:`tutorial/12-struct/03-linked-list.kv`~`07-list-local.kv`。 + +## 容器结构 + +容器在 kvspace 中分两部分落库,`·`(U+00B7 中点)是值容器唯一的 memindex 标记(注:`/lib/{pkg}·{func}` 中的 `·` 是包·函数命名分隔符,与 memindex 无关,详见 [[总体方案]]): + +- **容器值**:落在裸 base 键,storetype=index,body 空;langtype 为 map kindexpr(如 `[]char/utf8·int64`)或 struct 原型路径(如 `/lib/Point`)。 +- **成员**:`base·name` 是独立子 key,各自持成员值。 +- **memindex**:落在 `base·` 键(尾中点),storetype=index,其 body 是成员名索引矩阵,是成员枚举的权威来源。 + +memindex 的线格式为 dims = `[len, cap, M]` 的矩阵:`len` = 有效成员数、`cap` = 预留行数(`cap ≥ len`,满则翻倍扩容)、`M` = 成员名 UTF-8 字节最大长向上 8 对齐后的**行宽**;body 为 `cap × M` 字节,前 `len` 行是有序成员名(NUL 补齐),其余行全 NUL。容量内增删成员 body 长恒定、就地覆写。`M` 是行宽(行 stride),`cap` 是可翻倍的容量。 + +## stringkeymap + +**stringkeymap** 是一等 map 类型,kindexpr 为 `key·value`(见 [[kindexpr签名类型表达式]]):`[]char/utf8·int64` 即 `map[string]int64`,value 可递归嵌套(`[]char/utf8·[]char/utf8·int64`)。 + +```kv +m:[]char/utf8·int64 = {} // 空 map 同样须带类型标注 +m·a = 10 // 命名成员写 +kv·set(m, "b", 20) // 动态键写 +kv·get(m, "a") -> x // 动态键读;缺失返回 None +``` + +## 空容器字面量须带类型标注 + +`{}` 空 stringkeymap 字面量**必须**在赋值目标处带类型标注(如 `x:[]char/utf8·int64 = {}`);无标注时 layout 报错——空字面量本身无法推断 key/value 类型。 +"#### -> /lib/kvlang/spec/类型系统/容器 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" new file mode 100644 index 00000000..236fb718 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" @@ -0,0 +1,22 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/成员访问 { + r####"# 成员访问 + +| 语法 | 语义 | +|------|------| +| `base·name` | 静态键成员读/写 | +| `base·*k` | 动态键:变量 `k` 的值作成员名(用于局部容器) | +| `kv·get(base, k)` / `kv·set(base, k, v)` | 动态字符串键读/写 | + +容器成员一律走 `·`(或 `kv·get`/`kv·set`),**不得**用 `[]`;`[]` 只索引 compact 数组(见 [[数组形态]])。 + +### `base·name` 的 base 解析:按值优先、按名回退 + +`base·name`(读写同规则)中 base 的解析: + +1. **按值解引用**:base 持有非空字符串值(路径指针)→ 成员键 = `值(base)·name`。如 `"/n1" -> p` 后 `p·next` 解析为 `/n1·next`。 +2. **按名回退**:base 无值(或非字符串)→ 成员键 = `解析(base)·name`;解析帧感知:裸名 → `帧根/base`,`/` 开头 → 直通。 + +该规则让「局部结构体」(base 无值,按名)与「指针解引用」(base 存路径字符串,按值)共用一套语法。 +"#### -> /lib/kvlang/spec/类型系统/成员访问 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" new file mode 100644 index 00000000..7550fa17 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" @@ -0,0 +1,17 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/ptr { + r####"# ptr(软链接) + +**ptr** 不是种类,而是 head 的 `ref=1`(存储位置维):ptr 值的 head storetype/langtype 描述**目标的完整形态**,body 存**目标 key 路径**。源码类型标注最前可写 `*` 前缀(如 `*int64`)表 ref,layout 剥离后落成 head `ref` 独立字节;wire 层 langtype 串不含前缀(见 [[总体方案]]);赋值(Set)时据 head 的 storetype/langtype 做单跳类型检查。 + +跨函数共享的数据放绝对路径,指针变量存其路径字符串: + +```kv +/n1 = Node{} // Node 为 struct 原型(含 val:int64、next:[]char/utf8 字段) +/n1·val = 1 +/n1·next = "/n2" // next 存目标路径(指针) +"/n1" -> p // 引号 = 路径串(指针) +p·val -> v // 按值解引用:读 /n1·val +``` +"#### -> /lib/kvlang/spec/类型系统/ptr +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/17-struct.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/17-struct.kv" new file mode 100644 index 00000000..6e601ba0 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/17-struct.kv" @@ -0,0 +1,24 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/struct { + r####"# struct + +`struct` 声明注册一个原型节点于 `/lib/`(storetype=index,langtype 为原型路径如 `/lib/Point`),字段带类型标注与默认值: + +```kv +struct Point { + x:float64=0.0 + y:float64=0.0 +} +``` + +实例化 `Name{f=v}` 克隆原型子树、覆盖给定字段(其余取默认),并对每个字段做类型校验;实例是 struct 类型命名成员容器(storetype=index,成员在 `{key}·` memindex),成员经 `·` 访问。字段类型不符或写不存在的字段在 runtime 报 TypeError。 + +```kv +p = Point{x=3.0 y=4.0} +p·x -> a // 3.0 +q = Point{} // 全取默认:0.0, 0.0 +``` + +在签名中,结构类型以原型路径 `/lib/`(**structref** 类型表达式 atom)标注(见 [[kindexpr签名类型表达式]])。结构值可存进 map(int 键、struct 值)、以绝对路径指针连成链表/树/图(见 `tutorial/12-struct/`)。 +"#### -> /lib/kvlang/spec/类型系统/struct +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/18-\346\200\273\344\275\223\346\226\271\346\241\210.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/18-\346\200\273\344\275\223\346\226\271\346\241\210.kv" new file mode 100644 index 00000000..07d5edd3 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/18-\346\200\273\344\275\223\346\226\271\346\241\210.kv" @@ -0,0 +1,15 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/总体方案 { + r####"# 类型系统总体方案:xvalue head 三正交维 + +> **本卷为新方案,待敲定。** 敲定后据此重构 [[种类与定宽类型]]、[[kindexpr签名类型表达式]]、[[数组形态]]、[[容器]]。 + +每个 XValue 落盘时,其类别由 head 三个**正交**字段表达,彻底取代旧的单一 `kind` 分类与 kindexpr 首字符 `*`/`@` 前缀。 + +- **`ref`(存储位置)** — 值放在哪:inline / 指针 / 扩展世界。1 字节。 +- **`storetype`(物理布局,codec 视角)** — 字节怎么切。**闭合小集**,1 字节。 +- **`langtype`(语义类型,语言视角)** — 值是什么类型。**即旧的 kindexpr 串**,变长,保留高级语言的全部类型定义。 + +**核心分层**:codec([[XValueHead线格式]] 的编解码层)**只按 `storetype` 这 1 字节分派切字节**,完全不解析 `langtype` 串(对它只是一段透传字节);body 对 codec 恒为不透明字节。语言层(layout/runtime 类型系统)只读 `langtype` kindexpr 串做类型检查、匹配、显示。这把 kvspace(寻址+内存空间,只认 storetype 1 字节)与 kvlang 类型系统(只认 langtype 串)彻底解耦。 +"#### -> /lib/kvlang/spec/类型系统/总体方案 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/19-head\345\255\227\346\256\265.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/19-head\345\255\227\346\256\265.kv" new file mode 100644 index 00000000..f2e85819 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/19-head\345\255\227\346\256\265.kv" @@ -0,0 +1,18 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/head字段 { + r####"# 通用 head 字段(一切 xvalue 皆有,按序) + +| 字段 | 宽 | 义 | +|------|----|----| +| `headlen` | u16 LE | head 总字节数;body 起于偏移 `headlen` | +| `ref` | u8 | 存储位置(见下) | +| `storetype` | u8 | 物理布局(见下) | +| `ro` | u8 | `1`=只读、`0`=可写 | +| `vid` | u32 LE | vthread id(默认 0) | +| `body_len` | u32 LE | body 字节数 | +| (storetype 物理字段) | 变 | 按 storetype 追加,见下表 | +| `langtype` | 变长 | kindexpr 串,占至 `headlen`(长度 = `headlen` − 当前偏移,无需独立长度字段) | + +`langtype` 恒为 head 最后一段(body 之前)。旧 1 字节 kindcode 方案作废——语义类型回归完整 kindexpr 串,方能承载 `[dims]`、map 的 `key·value`、struct 原型路径、def 族定义节点等高级定义。 +"#### -> /lib/kvlang/spec/类型系统/head字段 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" new file mode 100644 index 00000000..0e82b0b8 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" @@ -0,0 +1,15 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/ref存储位置 { + r####"# ref(存储位置维) + +只决定 body 语义与是否间接寻址;**不改变** storetype/langtype 描述——head 恒携带完整形态。 + +| ref | body | 义 | +|-----|------|----| +| 0 inline | 值 raw 本体 | 就地落盘 | +| 1 ptr | 目标 key 路径 | 软链接,单跳指向同型目标(head 的 storetype/langtype 即目标形态);`ptr → rwfunc` 即函数调用链 | +| 2 @ext | 扩展句柄/定位符 | 单个值本体在扩展世界(fs 文件 / gpu tensordata),head 存元描述 | + +**源码书写与 wire 的分工**:kvlang **源码**里,指针/扩展引用仍以 `*`/`@` 前缀写在类型**最前**(`*int64`、`@[256,256]uint8`,见 tutorial 例),这是源码类型标注文法的一部分。layout 解析源码时把该前缀**剥离**并落成 head 的 `ref` 独立字节;故 **wire 层 langtype 串不含 `*`/`@` 前缀**,存储位置一律经 `ref` 字节表达。`ref=2` 只表**单值**存于扩展世界,与目录类型 `index`/`extindex` 无关。 +"#### -> /lib/kvlang/spec/类型系统/ref存储位置 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/21-storetype\347\211\251\347\220\206\345\270\203\345\261\200.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/21-storetype\347\211\251\347\220\206\345\270\203\345\261\200.kv" new file mode 100644 index 00000000..39421155 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/21-storetype\347\211\251\347\220\206\345\270\203\345\261\200.kv" @@ -0,0 +1,56 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/storetype物理布局 { + r####"# storetype(物理布局维,codec 视角) + +决定 head 追加的物理字段与 body 切法。**codec 仅凭此 1 字节分派**。 + +| storetype | 追加物理字段 | body(ref=0) | 承载的 langtype | +|-----------|-------------|--------------|----------------| +| `NONE` | 无 | 空 | None | +| `ATOM` | 无 | 定宽 raw | 标量:bool、int/uint/float、单 char、time、duration | +| `ARRAYND` | `ndim` u8 + `dims[ndim]` u32 LE | 稠密等宽元素数组 | 张量、`[]char/*` 字符串、rwir 指令行文本 | +| `index` | 成员名 **m×n 对齐字符串数组**,`dims=[len,cap,M]`(len=成员数、M=对齐宽度 align8、cap 保留容量) | 见下(目录节点主槽 body 记计数头或空) | stringkeymap、struct(值容器);lib、rwfunc、def 族(目录/定义节点) | +| `extindex` | 同 index,`cap` 可增长(栈帧/运行栈扩容) | 同上 | rwfunc(vthread 运行栈等可扩展目录) | + +数值 `dims` 供 codec O(1) 切字节,与 langtype 串内的 `[dims]` 语法一致(由 codec 保证同步)。index/extindex 的物理载体沿用今日敲定的 m×n 对齐字符串数组(每成员名 padding 至 M,`cap` 预留扩容),存成员/子项名。 + +### 成员如何枚举:`/` 目录 vs `·` memindex + +storetype=index/extindex 只表「这是有成员的索引节点」,成员的**组织方式按 langtype 分两类**: + +| 类别 | langtype | 成员载体 | 例 | +|------|----------|---------|----| +| 目录节点 | lib、rwfunc、def rwir | **`/` 子路径**(kvspace 目录枚举) | `/lib/funca/[1,0]`、`/lib/funca/[1,1]` 为 `/lib/funca/` 的指令槽成员 | +| 值容器 | stringkeymap、struct | **`{key}·` memindex 兄弟槽** | `p·` 存 `p` 的成员名索引 | + +`{key}·` 中的 **memindex `·`** 是值容器(stringkeymap/struct)唯一的成员索引标记;rwfunc/lib/def 族是真目录,成员即 `/` 子路径,绝不用 memindex `·`。 + +**注意 `·` 的两种独立含义**——两者正交、勿混: + +1. **memindex 标记**:值容器主槽 `{key}` 的兄弟槽 `{key}·`,存该容器的成员名索引。**仅** stringkeymap/struct 用。 +2. **包·函数命名分隔符**:`/lib/{pkg}·{func}`(如 `/lib/time·now`)中的 `·` 只是路径名里分隔包名与函数名的字符,是 rwfunc 所在**目录的名字的一部分**,与 memindex 无关。故 `/lib/time·now/` 仍是纯 `/` 目录,其成员是 `/` 子路径而非兄弟 `·` 槽。 + +两类值容器底层皆为同一 m×n 对齐字符串数组:目录节点由 kvspace 目录直接持有并按 `/` 枚举,值容器把它放在 `{key}·` 兄弟槽。 + +### rwir 无实现体(叶子)vs rwfunc 有实现体(目录);def 族定义节点 + +本质区别在「有无 kvlang 实现体」: + +| langtype | 有 kvlang 实现体? | 位置 | 谁实现 | 形态 | +|----------|------------------|------|--------|------| +| `rwir` | 无 | `/vthread//` 指令行 | runtime 直接兑现(经 def rwir 路由,myrwircaps) | ARRAYND 指令行叶子 | +| `def rwir` | 无(仅签名,无指令行) | **仅 `/lib/`** | runtime 直接兑现 | index **定义节点**(有签名行 `[0,x]`,无实现指令行) | +| `rwfunc` | **有**(一串 rwir 指令行) | `/lib/·/` | kvlang 指令序列自身 | index/extindex **目录**(签名行 `[0,x]` + 实现指令行 `[s0>0,·]`) | + +**def rwir 是路由头**(原 `defrwir`,现规范书写为带空格的 **`def rwir`**,见下「def 族」):它只存在于 `/lib/`,是每个 rwir 指令的统一声明入口。`/vthread/` 下执行的每条 rwir 都**引用** `/lib/` 的 def rwir——为「rwir 统一调度」而设。def rwir 没有 kvlang 实现体(无 `s0>0` 的实现指令行),由 runtime 直接兑现。 + +**没有「扩展 rwir」这个说法**:def rwir 不专指扩展算子。每个 runtime 只声明自己的 **myrwircaps**(该 runtime 能兑现的 rwir 能力集),`/lib/` 的 def rwir 即这些能力对外的路由头。 + +rwfunc 与之相反:它**有** kvlang 实现体——就是它 `/` 目录下的实现指令行子成员(`/lib/pkg·funca/[1,0]`…)。这正是 rwir/def rwir(无实现指令行、runtime 兑现)与 rwfunc(有实现指令行、`/` 目录)的本质区别。 + +**为何有 def rwir 却无 def rwfunc**——根源在能力调度的不对称: + +- **rwfunc 可被任意 main-runtime 直接解释执行**:其实现体就是一串 rwir,main-runtime 逐条解释即可,不需路由头。故 rwfunc 无需、也没有 `def rwfunc`。 +- **rwir 若不在本 runtime 的 myrwircaps 内就执行不了**,必须依赖调度转发给**能兑现它的其它 runtime**。这份「按能力路由到其它 runtime」的需求,正由 `/lib/` 的 **def rwir 路由头**承载——这就是 def rwir 存在的根本原因。 +"#### -> /lib/kvlang/spec/类型系统/storetype物理布局 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" new file mode 100644 index 00000000..ced02590 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/22-storetype-def\346\227\217\344\270\216\347\255\276\345\220\215\350\241\214.kv" @@ -0,0 +1,39 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/storetype_def族与签名行 { + r####"# storetype——签名行、def 族与目录节点 + +### 签名不入 langtype 串,而入签名行 `[0,x]` 槽 + +rwfunc / def rwir 的**签名(各参数类型)不写进 langtype 串**,也不塞进主槽 body。分两处存: + +1. **主槽计数头**:目录节点主槽 `{fn}`(如 `/lib/pkg·funca`)的 body 只记 `[nr:u16 LE][nw:u16 LE][dynamic:u8]`——读参个数 `nr`、写参个数 `nw`、是否变参 `dynamic`(末位读参 `...`)。**不含任何参数 kindexpr 串**。 +2. **签名行 `[0,x]` 各槽**:每个参数的真实类型定义落在该目录的**签名行**坐标 `[0,x]`——`x<0` 为读参、`x>0` 为写参、`[0,0]` 为 opcode/签名行锚点(见 [[指令布局格式]] 的二元坐标 `[s0,s1]`:`s0=0` 即签名行)。每个 `[0,x]` 槽是一个 **`def kindexpr`** 类型的 xvalue,其 body 存该参数的完整 kindexpr 串(一个参数一槽)。 + +于是 rwfunc/def rwir 主槽的 langtype 就是裸种类名 `rwfunc` / `def rwir` 本身,**不再是「签名 kindexpr 串」**。签名分散在 `[0,x]` 签名行槽里,实现指令行(若有)在 `[s0>0,·]`。 + +### def 族:定义节点的 langtype(带空格书写) + +「某类型的定义节点」自身的 langtype 用 **`def `** 家族名,**中间保留空格**(读作「`base` 的定义」): + +| langtype | 落点 | 表示 | +|----------|------|------| +| `def rwir` | `/lib/` | 一条 rwir 的路由头/签名定义(原 `defrwir`,废连写) | +| `def struct` | `/lib/` | 一个 struct 原型的定义节点(字段类型定义在其子槽) | +| `def kindexpr` | 签名行 `[0,x]` 槽 | 一个参数的类型定义(body=该参数 kindexpr 串) | + +`def` 与 base 间的空格是**规范强制**:连写形式 `defrwir`/`defstruct`/`defkindexpr` 一律废除。 + +### 目录节点即索引型容器 + +KV 树中的目录路径(尾 `/`)本身是 xvalue,storetype ∈ {index, extindex},langtype 表达目录语义: + +| 路径 | langtype | storetype | 成员 | +|------|----------|-----------|------| +| `/lib/` | `lib` | index | `/lib/` 各包目录(`/` 子路径) | +| `/lib/pkg·funca/` | `rwfunc` | index(静态函数定义目录) | 签名行 `[0,x]`(def kindexpr)+ 实现指令行 `[s0>0,·]` | +| `/lib/` | `def rwir` | index(路由头,只有签名行) | 签名行 `[0,x]`(def kindexpr),无实现指令行 | +| `/vthread/1/` | `rwfunc` | extindex(运行栈,可扩展) | `/vthread/1/<坐标>` 栈帧/指令 | + +同为 rwfunc,静态定义落 `index`、运行栈落 `extindex`——langtype 一致、物理载体按场景选。 +"#### -> /lib/kvlang/spec/类型系统/storetype_def族与签名行 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/23-langtype\350\257\255\344\271\211\347\261\273\345\236\213.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/23-langtype\350\257\255\344\271\211\347\261\273\345\236\213.kv" new file mode 100644 index 00000000..56ea947e --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/23-langtype\350\257\255\344\271\211\347\261\273\345\236\213.kv" @@ -0,0 +1,28 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/langtype语义类型 { + r####"# langtype(语义类型维,语言视角)= kindexpr 串 + +langtype 恢复为完整 kindexpr 串,逐字节即旧类型标注文法(见 [[kindexpr签名类型表达式]]),天然含 base 种类、`[dims]`、map 键值、struct 原型路径。各 langtype 的串形态与其落定的 storetype: + +| langtype(kindexpr 串例) | storetype | 说明 | +|--------------------------|-----------|------| +| `int64` `bool` `time` `duration` | ATOM | 定宽标量 | +| `[]char/utf8` | ARRAYND(elem 1B) | **utf8 变宽 → 物理即 [n]uint8**;禁码点索引是 langtype 层规则,物理按字节切 | +| `[]char/utf32` `[]char/ascii` | ARRAYND | 定宽,物理可 O(1) 码点索引 | +| `[3,4]float32` | ARRAYND | 张量 | +| `rwir`(指令行,`/vthread/`) | ARRAYND | 指令行叶子,**无实现体,由 runtime 经 def rwir 路由直接兑现(myrwircaps)** | +| `def rwir`(路由头,仅 `/lib/`) | index | 定义节点:签名行 `[0,x]`,无实现指令行 | +| `[]char/utf8·int64`(map) | index | **stringkeymap**,键值经 `·` mapexpr;成员在 `{key}·` | +| `/lib/Point`(struct 原型路径) | index | 结构实例,字段固定;成员在 `{key}·` | +| `def struct`(原型定义节点) | index | struct 原型自身;字段类型定义在其子槽 | +| `rwfunc` | index / extindex | 函数即 `/` 目录:签名行 `[0,x]` + 实现指令行;主槽 body 只记 nr/nw/dynamic | +| `def kindexpr`(签名行 `[0,x]` 槽) | ARRAYND | 单个参数类型定义,body=该参数 kindexpr 串 | +| `lib`(包目录) | index | `/lib/` 及各 pkg 目录;成员为 `/` 子路径 | + +**def 族的来源**(`def` 不是 kvlang 关键字、不在 kv 源码书写):`def struct`(源码 `struct Name{}`,layout 落原型定义节点)、`def kindexpr`(源码参数标注 `x:int64`,layout 铺签名行时逐参生成)由 **layout** 产生;`def rwir`(`/lib/` 路由头)**由各 runtime 注册**——kv 源码不声明 rwir,layout 也不产生 def rwir(见 [[rwfunc布局与def_rwir]])。 + +`object`/`scope` **彻底不存在**——无此种类名、无此关键字;异构命名容器一律是 `struct`(见 [[容器]])。容器**语义类型**只剩 `stringkeymap`、`struct`(口语称「map」指的即 stringkeymap,规范一律书写种类名 `stringkeymap`)。 + +**一对多正是拆分的价值**:同为 rwfunc,静态函数定义落 `index`、vthread 运行栈落 `extindex`;语言层看到的是同一 langtype,物理层按场景选载体。 +"#### -> /lib/kvlang/spec/类型系统/langtype语义类型 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/24-wire\345\270\203\345\261\200\347\244\272\344\276\213.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/24-wire\345\270\203\345\261\200\347\244\272\344\276\213.kv" new file mode 100644 index 00000000..fa765808 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/24-wire\345\270\203\345\261\200\347\244\272\344\276\213.kv" @@ -0,0 +1,24 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/wire布局示例 { + r####"# wire 布局示例 + +通用前缀 `U = [headlen:u16][ref][storetype][ro][vid:u32][body_len:u32]`,其后按 storetype 追加物理字段,再接 langtype kindexpr 串(占至 headlen),末接 body: + +``` +None : U(store=NONE)[lang=""] | 空 +atom int64 : U(store=ATOM,body_len=8)[lang="int64"] | 8B raw +[]char/utf8 : U(store=ARRAYND)[ndim=1][dims=n][lang="[]char/utf8"] | n 字节 +[3,4]f32 : U(store=ARRAYND)[ndim=2][dims=3,4][lang="[3,4]float32"] | 48B raw +map : U(store=index)[count/cap…][lang="[]char/utf8·int64"] | 主槽空;成员在 {key}· +struct 实例 : U(store=index)[count/cap…][lang="/lib/Point"] | 主槽空;成员在 {key}· +def struct : U(store=index)[count/cap…][lang="def struct"] | Point 原型;字段定义在子槽 +/lib/ : U(store=index)[count/cap…][lang="lib"] | 成员为 /lib/ 子路径 +/lib/pkg·funca : U(store=index)[count/cap…][lang="rwfunc"] | body=[nr][nw][dyn];签名在 [0,x],实现在 [s0>0,·] +/lib/ : U(store=index)[count/cap…][lang="def rwir"] | 路由头;body=[nr][nw][dyn];签名在 [0,x],无实现行 +签名行 [0,x]: U(store=ARRAYND)[lang="def kindexpr"] | body=该参数 kindexpr 串 +/vthread/1/ : U(store=extindex)[ext 元数据][lang="rwfunc"] | 运行栈,/vthread/1/<坐标> 子成员 +ptr→int64 : U(ref=1,store=ATOM)[lang="int64"] | body="/x/y" +ptr→rwfunc : U(ref=1,store=index)[lang="rwfunc"] | body="/lib/pkg·funca"(调用链) +``` +"#### -> /lib/kvlang/spec/类型系统/wire布局示例 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" new file mode 100644 index 00000000..34d88660 --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/25-\345\267\262\346\225\262\345\256\232.kv" @@ -0,0 +1,28 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/已敲定 { + r####"# 已敲定 + +- 三维正交:`ref` × `storetype` × `langtype`,codec 只认 storetype(1 字节)、语言层只认 langtype(kindexpr 串)。 +- **langtype = 旧 kindexpr 串**(变长),保留高级语言全部类型定义;1 字节 kindcode 方案作废。 +- storetype 集:`NONE`/`ATOM`/`ARRAYND`/`index`/`extindex`(index/extindex 是 storetype,非 langtype)。 +- 容器语义类型只剩 `stringkeymap`/`struct`(口语「map」= stringkeymap;`object`/`scope` **彻底删除**,无此种类/关键字);物理落 index。 +- 字符串:ascii/utf32 → ARRAYND;utf8 → ARRAYND(elem 1B)。 +- time/duration → ATOM;rwir 指令行 → ARRAYND 叶子(无实现体)。 +- **rwir vs rwfunc 的本质区别**:rwir 无实现指令行、由 runtime 直接兑现(myrwircaps);rwfunc 有 kvlang 实现指令行(`/` 目录下的 `[s0>0,·]`)→ 目录。 +- **def rwir 是路由头**(原 defrwir):仅存于 `/lib/`,是 rwir 统一调度入口;`/vthread/` 的每条 rwir 引用 `/lib/` 的 def rwir。**无「扩展 rwir」之说**,只有各 runtime 的 myrwircaps。 +- **签名不入 langtype 串**:rwfunc/def rwir 主槽 langtype 即裸名 `rwfunc`/`def rwir`;主槽 body 只记 `[nr][nw][dynamic]`;各参数真实类型落在签名行 `[0,x]` 槽(`x<0` 读参、`x>0` 写参、`[0,0]` 锚点),每槽为 `def kindexpr` xvalue(body=该参数 kindexpr 串)。 +- **def 族带空格书写**:`def rwir`、`def struct`、`def kindexpr`——`def` 与 base 间强制空格,连写形式废除。 +- **rwfunc/lib 是目录节点**:成员即 `/` 子路径;rwfunc 静态定义 → index、vthread 运行栈 → extindex。 +- **index/extindex 物理载体 = m×n 对齐字符串数组**(`dims=[len,cap,M]`,M align8、cap 扩容),沿用今日敲定方案,存成员/子项名。 +- **`·` 双义正交**:① memindex 标记 `{key}·` 仅 stringkeymap/struct 用;② `/lib/{pkg}·{func}` 里的 `·` 是包·函数命名分隔符(路径名一部分),与 memindex 无关,rwfunc 目录成员仍走 `/`。 +- 索引型容器(stringkeymap/struct)主槽 `{key}` body 恒空。 +- **源码 `*`/`@` 前缀保留**:kvlang 源码类型标注最前可写 `*`/`@`(表 ref),layout 剥离前缀落成 head `ref` 字节;wire 层 langtype 串不含前缀。 +- `headlen` u16 LE;`body_len` 保留为显式 u32 LE 字段;langtype 串占至 headlen,无独立长度字段。 +- `ref=2`(@ext)与 index/extindex 无关。 +- in-memory head struct 按 storetype 拆分——**属实现层,本规范只定 wire 与字段语义**。 + +## 仍待敲定 + +(本卷所有类型分歧已敲定。剩余仅实现层细节——如 index/extindex m×n 数组各字段精确偏移、签名行 `[0,x]` 坐标与 [[指令布局格式]] 二元坐标的具体编码、extindex 扩容策略——随 01-04 重构与 [[容器]] memindex 线格式对齐后落定,不属本卷规范分歧。) +"#### -> /lib/kvlang/spec/类型系统/已敲定 +} diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/26-\345\255\227\347\254\246\344\270\262\347\274\226\347\240\201.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/26-\345\255\227\347\254\246\344\270\262\347\274\226\347\240\201.kv" new file mode 100644 index 00000000..de1a94dc --- /dev/null +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/26-\345\255\227\347\254\246\344\270\262\347\274\226\347\240\201.kv" @@ -0,0 +1,49 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/类型系统/字符串编码 { + r####"# 字符串编码 + +char 种类([[种类与定宽类型]]、[[种类_标量时间容器]])承载三种编码:utf8、utf32、ascii。 +本章规定这三种编码在 **key/value 存储**、**用户 kv 代码**与 **string 标准库**三个层面的分工。 + +## 一、存储与交换默认 utf8 + +kvlang 体系内,**key 与 value 中的字符串默认 utf8**。 + +- key 侧:字符串键(`[]char/utf8·value` 中 key 侧的 `[]char/`)与路径名,utf8 是默认。 +- value 侧:落到 KV 树的字符串值,未显式标注时以 utf8 承载。 + +utf8 是变宽字节序列,**字节取向、空间高效**,适合作为持久/交换形态;utf8 **禁码点索引**(O(1) 索引须定宽编码)。 + +## 二、utf32 开放给用户 kv 代码 + +utf32 是码点序列(4B×N),**定宽、可索引**,供用户 kv 代码在需要处理字符串内容时显式使用: + +``` +// 存储读出的串默认 utf8;要逐码点处理,先显式转 utf32 +kv·get(d, "name") -> s8 // s8: []char/utf8 +char/utf32(s8) -> s32 // s32: []char/utf32,定宽可索引 +s32[0] -> c // O(1) 码点取 +``` + +utf8 → utf32 的转换是**显式**的(编码纪律,见 [[字符串与空值布尔字面量]] 与 char 转换 builtin);跨编码不隐式混拼。 + +## 三、string 标准库只处理定宽(utf32) + +string 包的处理函数(`string·len`、`string·find`、`string·slice`、`string·concat` 等) +**通常只能处理定宽编码的字符串**——utf32(及 utf8 转换后的工作形态): + +- 传入 utf8 串必须先转 utf32 再交 string 包处理; +- utf8 直接喂 string 包函数是 TypeError(变宽无法 O(1) 定位,函数不隐式转码)。 + +> 例外:字节层面、与编码无关的操作(如原样搬运、`x` 编码无关比较)可接受 utf8; +> 语义上按"string 包 = 定宽 utf32 工作面"理解,utf8 是它的存储/交换输入,先转后用。 + +## 关系小结 + +| 层面 | 默认编码 | 说明 | +|------|---------|------| +| key/value 存储与交换 | utf8 | 变宽字节序列,禁索引,空间高效 | +| 用户 kv 代码处理 | utf32(显式) | 定宽可索引;utf8 先显式转 utf32 | +| string 标准库 | utf32 | 只处理定宽;utf8 输入须先转 | + "#### -> /lib/kvlang/spec/类型系统/字符串编码 +} diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" new file mode 100644 index 00000000..921b1600 --- /dev/null +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" @@ -0,0 +1,50 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/layout语义/指令架构 { + r####"# 指令架构 + +layout 把每个函数体布进 kvspace 的一棵子树:**指令即路径**,每条指令占据一个二维坐标 `[s0, s1]`。本章界定该坐标系、opcode 与读写槽的角色、三种赋值书写形态,以及槽值的 XValue 编码。二维坐标的线格式(TLV、head kindexpr)见 [[指令布局格式]](kvspace 模型卷);本章只界定 layout 产出的槽位语义。 + +## 二维坐标系 `[s0, s1]` + +函数目录(`/lib/·/`,见 [[lib与rwir数据]])下,每条指令的每个槽是一个独立 KV key,key 名恒为坐标串 `[s0,s1]`。两轴含义: + +- **s0 轴(执行顺序轴)**——第几条指令。`s0 = 0` 保留给函数签名行;指令从 `s0 = 1` 起顺序编号(称 irseq)。 +- **s1 轴(参数轴)**——该槽的角色: + +| s1 | 角色 | 方向 | +|----|------|------| +| `s1 = 0` | **opcode**(操作码) | —— | +| `s1 = -1, -2, …` | **读参**(read slot,输入) | 调用方 → 被调方 | +| `s1 = +1, +2, …` | **写参**(write slot,输出) | 被调方 → 调用方 | + +铁律: + +- `[s0, 0]` **必须**是 opcode(操作符名或被调用的 rwir/rwfunc 名),**不得**放变量引用。 +- 读参在负轴、写参在正轴,符号即数据流方向:从负轴读入、在零轴执行、向正轴写出。 +- 参数数量**隐式编码**:runtime 沿 s1 轴向两侧扩展,遇空 key 即停,opcode 中不存 arity(见 [[执行模型]])。 + +示例:`A + B -> C` 布为 + +``` +[s0,0] = "+" opcode +[s0,-1] = "A" 第 1 读参 +[s0,-2] = "B" 第 2 读参 +[s0,1] = "C" 第 1 写参 +``` + +写参扇出(同一结果写入多个位置)以多个正轴槽表示:`a + b -> sum, backup` 布出 `[s0,1]="sum"`、`[s0,2]="backup"`。 + +## 三种赋值书写形态 + +赋值有三种等价书写,写槽约束完全一致: + +| 形态 | 写槽位置 | 例 | +|------|---------|-----| +| `expr -> writes` | 右 | `A + B -> C` | +| `writes = expr` | 左 | `C = A + B` | + +`=` 与 `->` **不是**表达式,**不得**嵌套于条件或实参中;相等比较写作 `==`。两种方向记号写槽位置不同(`->` 右写、`=` 左写),同一次赋值只取一种方向。 + +**写槽必须是位置(location)**:裸名(帧内变量)、绝对路径(`/abs`)、成员写(`base·field` / `base·*key`)、下标写(`arr[idx]`)。字面量出现在写槽位置是错误,layout 报诊断(见 [[诊断]])。 + "#### -> /lib/kvlang/spec/layout语义/指令架构 +} diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/02-\346\214\207\344\273\244\346\247\275\347\274\226\347\240\201.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/02-\346\214\207\344\273\244\346\247\275\347\274\226\347\240\201.kv" new file mode 100644 index 00000000..5509ff2d --- /dev/null +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/02-\346\214\207\344\273\244\346\247\275\347\274\226\347\240\201.kv" @@ -0,0 +1,34 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/layout语义/指令槽编码 { + r####"# 指令槽编码 + +## `=` 拷贝 opcode + +叶表达式写入写槽(拷贝)**必须**编码为显式 opcode `=`,被拷贝的值放读槽: + +``` +a -> b [s0,0]="=" [s0,-1]="a" [s0,1]="b" +42 -> x [s0,0]="=" [s0,-1]=42 [s0,1]="x" +``` + +opcode 位恒为操作码,故拷贝(opcode=`=`)与零参函数调用(`greet() -> x`,opcode=`greet`)在 KV 层无歧义。 + +## opcode 槽的 langtype + +opcode 槽 `[s0,0]` 的 langtype 由 layout 按 opcode 类别决定: + +- 控制流与拷贝原语(`return` / `goto` / `br` / `call` / `=`)——langtype `rwir`。 +- 其余(运算符、函数/rwir 调用目标)——langtype `rwir|rwfunc`。layout **不**静态判定该目标是否在本 runtime 的 myrwircaps 内(rwir)或为 rwfunc;判定推迟到 runtime 查 `/lib/` 分派(见 [[lib与rwir数据]])。 + +## 读写槽的槽值编码 + +读/写槽的槽值按内容编码为 XValue: + +- 字面量(整数、浮点、`true`/`false`、字符串、绝对路径)——按其类型编码为对应 kind 的 XValue(`int64` / `float64` / `bool` / `char/*` …);字符串字面量在 KV 传输层以 `"` 前缀区分于变量名。 +- 变量名、标签等引用——langtype `rwir`,body 即该名字符串。 + +## 增量覆盖 + +写函数前 layout **必须**先 `del_tree` 清除该函数子树,再重写:残留的旧槽会被 runtime 沿 s1 轴误读为真实参数。清除**仅限本函数子树**——多次 layout 各自覆盖其函数,**不得**整库删除,以支持增量布局(文件夹复制式合并)。 + "#### -> /lib/kvlang/spec/layout语义/指令槽编码 +} diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" new file mode 100644 index 00000000..50270079 --- /dev/null +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/03-\345\207\275\346\225\260.kv" @@ -0,0 +1,57 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/layout语义/函数 { + r####"# 函数 + +kvlang 源码只有一种命名单元:**rwfunc**(有指令体的用户函数),暴露 `(读参) -> (写参)` 箭头接口。函数**没有返回值**,只有读参与写参。rwir 是 runtime 兑现的原子能力,源码**不声明**、只调用;其 `def rwir` 路由头由各 runtime 注册到 `/lib`(见 [[rwfunc布局与def_rwir]])。 + +## 读参与写参 + +| 参数 | 布局槽位 | 方向 | +|------|---------|------| +| **读参** | `[0,-1], [0,-2], …` | 调用方 → 被调方 | +| **写参** | `[0,+1], [0,+2], …` | 被调方 → 调用方 | + +`-> result` 是**写参的跨帧路径映射**,不是返回值:runtime 将子帧写参直写调用方目标位置(见 [[函数调用与内建]])。 + +调用时**写参 arity 必须全部匹配**(对齐 Go/Rust):`f() -> s` 对多写参函数是错误;不需要的写参**必须**用 `_` 显式丢弃。 + +## 参数类型必须标注 + +每个读参与写参**必须**声明类型(`name:type_expr`,type_expr 见 [[kindexpr签名类型表达式]])。type_expr 最前可写 `*`/`@` 前缀(如 `*int64`、`@[256,256]uint8`)表示 ptr/ext 存储位置;layout 解析时剥离该前缀并落成 head `ref` 字节,wire 层 kindexpr 串不含前缀(见 [[总体方案]])。缺类型标注是错误: + +``` +error: func f: param "x" has no type annotation — every parameter must declare its type +error: func f: return value "r" has no type annotation +``` + +末读参的 type_expr **可**加 `...` 声明变参(0..N 个同型实参)。变参**必须**是最后一个读参;写参**不得**变参。 + +## 写参初值 None + +写参在被调方帧内初值为 **None**(strict null)。函数体内**必须**先显式初始化写参再参与运算——对 None 做算术在 runtime 被拒。惯例以拷贝置零值起步(`0 -> acc`),此后写参在体内可读可写,退出时经箭头映射回调用方。 + +## 读参只读 + +读参是单向输入绑定。函数体内把读参裸名放入写槽(含 `for … in` 的迭代变量)会破坏数据流方向,layout 报错拒绝装载: + +``` +error: func f: read param "A" cannot be used as write slot (read params are read-only) +``` + +**签名诚实原则**:函数体内被写的参数**必须**声明在写参侧。 + +``` +// 非法:acc 被写却在读参侧 +rwfunc sum(arr:[]int64, acc:int64) -> (r:int64) { acc + arr[0] -> acc } +// 合法:acc 在写参侧,体内可读可写 +rwfunc sum(arr:[]int64) -> (acc:int64) { acc + arr[0] -> acc } +``` + +判定规则(实现):写槽含 `/`、`[`、`·` 者视为路径/下标/成员写,不计入本检查;但 `kv·set(base, …)` 的 base 命中读参裸名同样拒绝(成员目录被改写)。 + +## 参数同名规则 + +- **定义时**:读参与写参**不得**同名——`rwfunc f(A:int64) -> (A:int64)` 非法,报 `param "A" appears in both read-params and write-params`。 +- **调用时**:同一变量**可**同时出现在读槽与写槽——`inc(x) -> x` 合法,读写独立解析、互不冲突。 + "#### -> /lib/kvlang/spec/layout语义/函数 +} diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" new file mode 100644 index 00000000..132b8da7 --- /dev/null +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/04-\345\207\275\346\225\260\345\206\231\346\247\275\344\270\216\345\270\203\345\261\200.kv" @@ -0,0 +1,35 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/layout语义/函数写槽与布局 { + r####"# 函数写槽与布局 + +## 写槽校验 + +写槽(`->` 右 / `=` 左)解析时,非法写槽产生警告(`warn`,不拒装载): + +- 字面量(数字/引号串开头)出现在写槽位置 → `unexpected token … in write slot position`。 +- 写槽后紧跟 `(`(同行函数调用)→ `function call … on same line as write slot`。 + +合法写槽:裸名、`/abs`、`base·名`、`base·*key`、`arr[idx]`。 + +## rwfunc 布局 + +rwfunc 布进 `/lib/·/`: + +1. `[0,0]`——签名行锚点,langtype `rwfunc`,body `[nr:u16 LE][nw:u16 LE][dynamic:u8]`(读参数/写参数/是否变参);各参数类型落在签名行 `[0,x]` 槽(`x<0` 读参、`x>0` 写参),每槽为 `def kindexpr` xvalue,body 即该参数 kindexpr 串(见 [[总体方案]])。 +2. `/lib/·.src`——源码副本(kind `char`)。 +3. 每个命名参数——`funcDir/` 存 Ptr(kind `char`),指向其槽坐标串(读参 `[0,-j]`、写参 `[0,+j]`)。 +4. 指令体从 `[1,0]` 起(`[0,*]` 为签名行占用)。 + +写参不需要值的调用以 `_` 接收,`_` 写入帧内 `_` 槽、不影响语义。 + +## rwfunc 声明位置 + +rwfunc 应包裹在 `lib pkg { }` 内。裸顶层 rwfunc **可**声明,但产生提示(`info`)并登记到 `/lib/`(无 pkg): + +``` +info: rwfunc outside lib block — registering under /lib/; consider wrapping in 'lib pkgname { }' +``` + +裸顶层 rwfunc 名与 native builtin 同名是错误(`function "…" shadows builtin`)——须包裹进 lib 或改名。 + "#### -> /lib/kvlang/spec/layout语义/函数写槽与布局 +} diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/05-\346\216\247\345\210\266\346\265\201.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/05-\346\216\247\345\210\266\346\265\201.kv" new file mode 100644 index 00000000..97dafb30 --- /dev/null +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/05-\346\216\247\345\210\266\346\265\201.kv" @@ -0,0 +1,44 @@ +// 欢迎加入kvspace世界 +lib kvlang/spec/layout语义/控制流 { + r####"# 控制流 + +kvlang 的结构化控制流有 `if`/`else`/`while`/`for … in`/具名 block/`break`/`continue`/`return`。它们**只能**出现在 rwfunc 体内;顶层出现是错误: + +``` +error: top-level while is not supported — wrap in main() +``` + +`return` **无参数**——输出经写参传递;`return x` 报错。 + +## 降级为 goto/br(lower) + +layout 的 lower 阶段把结构化控制流降级为两条控制原语加具名作用域块(ScopeStmt): + +- `goto(label)`——无条件跳转。 +- `br(cond, then_label, else_label)`——按 cond 二路分支。 +- `break` → `goto` 到循环 exit 块;`continue` → `goto` 到循环 cond 块。 + +每个 `if`/`while`/`for` 展开为一组具名块(如 `_while_2`、`_do_3`、`_exit_4`、`_if_5`、`_then_6`、`_else_7`、`_merge_8`)。`else if` 链按嵌套 `if` 展开。开放路径尾部补 `return`(layout 保证每块以终结符 `return`/`goto`/`br` 收尾)。 + +## 布局为线性 irseq + +lower 之后,layout 把**所有**块体展平进函数目录下的**单一线性指令序列** `[1,0]…[N,0]`——**不建 scope 子目录**。goto/br 的跳转目标在布局时解析为**整数 irseq**(目标块首指令的 s0 序号),写入其读槽。 + +标签到 irseq 的映射另存于 `/lib/·/‥labels/