From 5fa818e989808b70340d6e1dc9191498acfcdc0f Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Wed, 9 Sep 2026 13:10:42 +0800 Subject: [PATCH] =?UTF-8?q?layout:=20kindexpr.rs=20=E2=86=92=20langtype.rs?= =?UTF-8?q?=EF=BC=88=E4=B8=8E=20runtime=20langtype=20=E5=BD=92=E5=B9=B6?= =?UTF-8?q?=EF=BC=89+=20spec=20=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit layout kindexpr 模块改名 langtype.rs(对齐 runtime langtype.c/术语),导出 ABI 不变(6 符号含 kvlangKindexprParse,nm 实测一致);capi/code/ffi/kvkind/lower/ parser/ast 同步;spec 03/04/06/附录 更新(编译器定位、算子版本化)。 --- layout/src/ast.rs | 14 +- layout/src/capi.rs | 16 +- layout/src/code.rs | 9 +- layout/src/ffi.rs | 8 +- layout/src/kvkind.rs | 68 +++--- layout/src/{kindexpr.rs => langtype.rs} | 193 ++++++++++++------ layout/src/lib.rs | 2 +- layout/src/lower.rs | 4 +- layout/src/parser.rs | 24 ++- layout/tests/issue116.rs | 2 +- layout/tests/pipeline_test.rs | 4 +- runtime/src/const.h | 4 + runtime/src/kvcpu.c | 31 +-- runtime/src/langtype.c | 101 +++++---- runtime/src/runtime_internal.h | 1 - runtime/src/xvalue.c | 28 ++- ...66\351\227\264\345\256\271\345\231\250.kv" | 15 +- ...13\350\241\250\350\276\276\345\274\217.kv" | 8 +- ...16\345\220\210\346\263\225\346\200\247.kv" | 20 +- ...11\344\270\216\345\217\230\345\217\202.kv" | 27 +-- ...60\347\273\204\345\275\242\346\200\201.kv" | 2 +- ...16\347\255\276\345\220\215\350\241\214.kv" | 2 +- .../03-\345\207\275\346\225\260.kv" | 8 +- ...21\345\231\250\345\256\232\344\275\215.kv" | 13 ++ ...10\346\234\254\344\274\230\345\205\210.kv" | 12 +- ...76\345\274\217\346\226\207\346\263\225.kv" | 11 +- 26 files changed, 409 insertions(+), 218 deletions(-) rename layout/src/{kindexpr.rs => langtype.rs} (60%) diff --git a/layout/src/ast.rs b/layout/src/ast.rs index 0e4bed1f..85b1c9f2 100644 --- a/layout/src/ast.rs +++ b/layout/src/ast.rs @@ -109,15 +109,21 @@ impl FuncSig { self.params.iter().map(|p| p.name.clone()).collect() } - /// 参数 kindexp 列表(读参在前、写参在后,源文法逐字节),落盘于 rwir/rwfunc body。 - pub fn kindexp_list(&self) -> Vec { + /// 参数 langtype 列表(读参在前、写参在后),落盘于 rwir/rwfunc body。 + /// 末读参尾缀 `...` 是签名层变参标记:此处剥离,langtype 串保持纯净(变参落 dynamic 字节)。 + pub fn langtype_list(&self) -> Vec { self.params .iter() .chain(self.returns.iter()) - .map(|p| p.ty.clone()) + .map(|p| p.ty.strip_suffix("...").unwrap_or(&p.ty).to_string()) .collect() } + /// 末读参是否变参(源尾缀 `...`)→ 落成主槽 body 的 dynamic 字节。 + pub fn dynamic(&self) -> bool { + self.params.last().is_some_and(|p| p.ty.ends_with("...")) + } + pub fn num_reads(&self) -> i32 { self.params.len() as i32 } @@ -185,7 +191,7 @@ impl RwirDecl { #[derive(Clone)] pub struct Field { pub name: String, - pub ty: String, // kindexpr(字段类型) + pub ty: String, // langtype(字段类型) pub default: Option, // 默认值字面量(None = 未给) } diff --git a/layout/src/capi.rs b/layout/src/capi.rs index dda220d6..47c0255f 100644 --- a/layout/src/capi.rs +++ b/layout/src/capi.rs @@ -183,12 +183,12 @@ pub extern "C" fn kvlangLayoutDump( } } -// ── kindexpr 解析 ABI ────────────────────────────────────────────────── -// kindexpr 语法唯一事实源在 layout(kindexpr.rs);解析能力导出为 C ABI, +// ── langtype 解析 ABI ────────────────────────────────────────────────── +// langtype 语法唯一事实源在 layout(langtype.rs);解析能力导出为 C ABI, // 供 runtime 之外的消费方(扩展宿主 term/numpy/json、byteseek…)读取 XValue head 时 // 复用,杜绝各处手写 head 结构/解析造成的 ABI 漂移(#70 遗留的旧 kind[32] 结构即此类)。 -/// kindexpr 解析结果(repr(C),内存布局 = i32,i32,[i32;8],i32,[u8;64])。 +/// langtype 解析结果(repr(C),内存布局 = i32,i32,[i32;8],i32,[u8;64])。 #[repr(C)] pub struct kvlangKindexpr { pub ref_: i32, // 0=内联 1=指针(*) 2=扩展句柄(@) @@ -198,18 +198,18 @@ pub struct kvlangKindexpr { pub kind: [u8; 64], // base kind,NUL 终止(如 "float64"、"char/utf8"、"rwir|rwfunc") } -/// 解析 XValue head 的 kindexpr 内容(NUL 终止串,含 */@ 前缀与 [dims])。 +/// 解析 XValue head 的 langtype 内容(NUL 终止串,含 */@ 前缀与 [dims])。 /// 成功返回 0,失败(空指针/空串)返回 -1。 #[no_mangle] -pub extern "C" fn kvlangKindexprParse(kindexpr: *const c_char, out: *mut kvlangKindexpr) -> i32 { - if kindexpr.is_null() || out.is_null() { +pub extern "C" fn kvlangKindexprParse(langtype: *const c_char, out: *mut kvlangKindexpr) -> i32 { + if langtype.is_null() || out.is_null() { return -1; } - let s = cstr(kindexpr); + let s = cstr(langtype); if s.is_empty() { return -1; } - let (dims, kind) = kvkind::parse_kindexpr(s); + let (dims, kind) = kvkind::parse_langtype(s); let out = unsafe { &mut *out }; out.ref_ = 0; out.ndim = dims.len() as i32; diff --git a/layout/src/code.rs b/layout/src/code.rs index 08585afe..24c10672 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -390,12 +390,12 @@ pub fn write_func(kv: &mut Kv, pkg: &str, fn_: &mut Func) { let nr = fn_.sig.num_reads(); let nw = fn_.sig.num_writes(); - let param_types: Vec = fn_.sig.kindexp_list(); + let param_types: Vec = fn_.sig.langtype_list(); let mut pairs: Vec<(String, Vec)> = Vec::new(); pairs.push(( format!("{func_dir}/[0,0]"), - kvkind::new_rwfunc(seq.len() as i32, nr, nw, ¶m_types), + kvkind::new_rwfunc(seq.len() as i32, nr, nw, fn_.sig.dynamic(), ¶m_types), )); pairs.push(( keytree::lib_src(pkg, &fn_.sig.name), @@ -465,7 +465,7 @@ pub fn write_struct_decl(kv: &mut Kv, decl: &StructDecl) { /// 字段默认值 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 (dims, base) = kvkind::parse_langtype(ty); let s = default.map(|e| e.val.clone()).unwrap_or_default(); if base.starts_with("char/") { return ffi::new_char(&base, &s); @@ -501,7 +501,8 @@ pub fn write_rwir_decl(kv: &mut Kv, decl: &RwirDecl) { let v = kvkind::new_defrwir( decl.sig.num_reads(), decl.sig.num_writes(), - &decl.sig.kindexp_list().join("\n"), + decl.sig.dynamic(), + &decl.sig.langtype_list().join("\n"), ); let _ = kv.set(&[(keytree::rwir(&opcode), v)]); } diff --git a/layout/src/ffi.rs b/layout/src/ffi.rs index 2f075abe..45dd9363 100644 --- a/layout/src/ffi.rs +++ b/layout/src/ffi.rs @@ -108,7 +108,7 @@ extern "C" { fn kvspaceDecodeHead(data: *const u8, data_len: u32, out: *mut kvspaceHead_t) -> c_int; fn kvspaceNewPtr( - target_kindexpr: *const c_char, + target_langtype: *const c_char, target: *const c_char, out: *mut *mut u8, out_len: *mut u32, @@ -190,7 +190,7 @@ impl Kv { Kv { h } } - /// 写:pairs 的值为预编码 TLV;逐条解 head 取 (kindexpr, body),经 WriteNewPlace + /// 写:pairs 的值为预编码 TLV;逐条解 head 取 (langtype, body),经 WriteNewPlace /// 向 kvspace 要 body 偏移指针后直接写入 body 字节(新建/换 kind/换尺寸唯一原语)。 pub fn set(&mut self, pairs: &[(String, Vec)]) -> Result<(), String> { for (key, tlv) in pairs { @@ -379,8 +379,8 @@ pub fn decode_head(data: &[u8]) -> kvspaceHead_t { // ── 标准标量构造器 ─────────────────────────────────────────────────── -pub fn new_ptr(target_kindexpr: &str, target: &str) -> Vec { - let ck = CString::new(target_kindexpr).expect("no NUL"); +pub fn new_ptr(target_langtype: &str, target: &str) -> Vec { + let ck = CString::new(target_langtype).expect("no NUL"); let ct = CString::new(target).expect("no NUL"); call_codec(|out, out_len| unsafe { kvspaceNewPtr(ck.as_ptr(), ct.as_ptr(), out, out_len) }) } diff --git a/layout/src/kvkind.rs b/layout/src/kvkind.rs index 79baa833..eec272ae 100644 --- a/layout/src/kvkind.rs +++ b/layout/src/kvkind.rs @@ -38,8 +38,8 @@ pub fn head(data: &[u8]) -> ffi::kvspaceHead_t { ffi::decode_head(data) } -/// 解析 kindexpr 内容 → (dims, base kind)。kindexpr 无前缀(ref/ptr 归 head.ref)。 -pub fn parse_kindexpr(kx: &str) -> (Vec, String) { +/// 解析 langtype 内容 → (dims, base kind)。langtype 无前缀(ref/ptr 归 head.ref)。 +pub fn parse_langtype(kx: &str) -> (Vec, String) { if kx.starts_with('[') { match kx.find(']') { Some(end) => ( @@ -57,8 +57,8 @@ pub fn parse_kindexpr(kx: &str) -> (Vec, String) { } } -/// 读 head 的 kindexpr 内容(去 NUL)。 -pub fn kindexpr(data: &[u8]) -> String { +/// 读 head 的 langtype 内容(去 NUL)。 +pub fn langtype(data: &[u8]) -> String { if data.is_empty() { return String::new(); } @@ -75,7 +75,7 @@ pub fn kind(data: &[u8]) -> String { if data.is_empty() { return String::new(); } - parse_kindexpr(&kindexpr(data)).1 + parse_langtype(&langtype(data)).1 } pub fn is_ptr(data: &[u8]) -> bool { @@ -86,7 +86,7 @@ pub fn array_len(data: &[u8]) -> i32 { if data.is_empty() { return 0; } - let dims = parse_kindexpr(&kindexpr(data)).0; + let dims = parse_langtype(&langtype(data)).0; if dims.is_empty() { 1 } else { @@ -104,7 +104,7 @@ pub fn body<'a>(data: &'a [u8], h: &ffi::kvspaceHead_t) -> &'a [u8] { &data[off..off + len] } -/// 指针目标 key(Ptr 的 body 即目标 key 路径;head 去 * 为目标完整 kindexpr)。 +/// 指针目标 key(Ptr 的 body 即目标 key 路径;head 去 * 为目标完整 langtype)。 pub fn ptr_target(data: &[u8]) -> String { let h = ffi::decode_head(data); String::from_utf8_lossy(body(data, &h)).into_owned() @@ -122,7 +122,7 @@ pub fn display(data: &[u8]) -> String { if data.is_empty() { return "None".to_string(); } - let (_, k) = parse_kindexpr(&kindexpr(data)); + let (_, k) = parse_langtype(&langtype(data)); if k.is_empty() { return "None".to_string(); } @@ -187,21 +187,23 @@ fn plain_value(k: &str, b: &[u8]) -> String { .map(|c| char::from_u32(le_u32(c)).unwrap_or('\u{FFFD}')) .collect(), "index" => format!("({})", count_names(b)), - // kvlang 自有 kind:body = [2B nr][2B nw][sig];槽值/调用目标 nr=nw=0,取 sig 即可。 + // kvlang 自有 kind:body = [2B nr][2B nw][1B dynamic][sig];槽值/调用目标 nr=nw=0,取 sig 即可。 "rwir" | "rwir|rwfunc" | "rwfunc" | "defrwir" => { - let (nr, nw) = if b.len() >= 4 { + let (nr, nw, dynamic) = if b.len() >= 5 { ( u16::from_le_bytes([b[0], b[1]]), u16::from_le_bytes([b[2], b[3]]), + b[4] != 0, ) } else { - (0, 0) + (0, 0, false) }; - let sig = String::from_utf8_lossy(&b[4.min(b.len())..]).into_owned(); + let sig = String::from_utf8_lossy(&b[5.min(b.len())..]).into_owned(); + let var = if dynamic { "..." } else { "" }; if nr == 0 && nw == 0 { sig } else { - format!("(nr={nr},nw={nw}) {sig}") + format!("(nr={nr},nw={nw}{var}) {sig}") } } _ => String::from_utf8_lossy(b).into_owned(), @@ -214,34 +216,35 @@ pub fn is_char_kind(k: &str) -> bool { // ── kvlang 自有 kind:rwir / defrwir ──────────────────────────────── // -// body = [2B nr LE][2B nw LE][sig],array_len=1。 -// rwir=槽值(引用串/opcode),defrwir=定义(签名)。 +// body = [2B nr LE][2B nw LE][1B dynamic][sig],array_len=1。 +// rwir=槽值(引用串/opcode),defrwir=定义(签名)。dynamic=末读参变参(arity,非 langtype)。 -fn rwir_body(nr: i32, nw: i32, sig: &str) -> Vec { - let mut raw = Vec::with_capacity(4 + sig.len()); +fn rwir_body(nr: i32, nw: i32, dynamic: bool, sig: &str) -> Vec { + let mut raw = Vec::with_capacity(5 + sig.len()); raw.extend_from_slice(&(nr as u16).to_le_bytes()); raw.extend_from_slice(&(nw as u16).to_le_bytes()); + raw.push(dynamic as u8); raw.extend_from_slice(sig.as_bytes()); raw } pub fn new_rwir(nr: i32, nw: i32, sig: &str) -> Vec { - ffi::tlv_encode(KIND_RWIR, &rwir_body(nr, nw, sig), 1) + ffi::tlv_encode(KIND_RWIR, &rwir_body(nr, nw, false, sig), 1) } -/// 调用目标(看起来像函数调用的 opcode)→ kindexpr `rwir|rwfunc` 并列。 +/// 调用目标(看起来像函数调用的 opcode)→ langtype `rwir|rwfunc` 并列。 /// 静态无法判定是扩展 rwir 还是用户 rwfunc,交 runtime 查 /lib/ 的 XValue kind 分派。 pub fn new_rwir_union(sig: &str) -> Vec { - ffi::tlv_encode(KIND_RWIR_OR_RWFUNC, &rwir_body(0, 0, sig), 1) + ffi::tlv_encode(KIND_RWIR_OR_RWFUNC, &rwir_body(0, 0, false, sig), 1) } -pub fn new_defrwir(nr: i32, nw: i32, sig: &str) -> Vec { - ffi::tlv_encode(KIND_DEF_RWIR, &rwir_body(nr, nw, sig), 1) +pub fn new_defrwir(nr: i32, nw: i32, dynamic: bool, sig: &str) -> Vec { + ffi::tlv_encode(KIND_DEF_RWIR, &rwir_body(nr, nw, dynamic, sig), 1) } // ── struct 原型(对齐 runtime kvlangBuiltinMemindex)───────────────── // -// /lib/Name kind=struct,body="name:kindexpr\n..."(字段声明类型,供实例化类型校验) +// /lib/Name kind=struct,body="name:langtype\n..."(字段声明类型,供实例化类型校验) // /lib/Name· kind=index,body=[4B count LE][name\n...](字段名唯一权威) pub fn new_struct(fields: &[(String, String)]) -> Vec { @@ -261,12 +264,19 @@ pub fn new_memindex(names: &[String]) -> Vec { // ── kvlang 自有 kind:rwfunc ──────────────────────────────────────── // -// body = [2B nr LE][2B nw LE][param_types 以 \n 连接],array_len=num_insts。 - -pub fn new_rwfunc(num_insts: i32, nr: i32, nw: i32, param_types: &[String]) -> Vec { - let mut raw = Vec::with_capacity(4 + param_types.iter().map(|s| s.len()).sum::()); +// body = [2B nr LE][2B nw LE][1B dynamic][param_types 以 \n 连接],array_len=num_insts。 + +pub fn new_rwfunc( + num_insts: i32, + nr: i32, + nw: i32, + dynamic: bool, + param_types: &[String], +) -> Vec { + let mut raw = Vec::with_capacity(5 + param_types.iter().map(|s| s.len()).sum::()); raw.extend_from_slice(&(nr as u16).to_le_bytes()); raw.extend_from_slice(&(nw as u16).to_le_bytes()); + raw.push(dynamic as u8); raw.extend_from_slice(param_types.join("\n").as_bytes()); ffi::tlv_encode(KIND_RWFUNC, &raw, num_insts) } @@ -287,10 +297,10 @@ pub fn rwfunc_num_writes(body: &[u8]) -> i32 { } pub fn rwfunc_param_types(body: &[u8]) -> Vec { - if body.len() <= 4 { + if body.len() <= 5 { return Vec::new(); } - String::from_utf8_lossy(&body[4..]) + String::from_utf8_lossy(&body[5..]) .split('\n') .map(|s| s.to_string()) .collect() diff --git a/layout/src/kindexpr.rs b/layout/src/langtype.rs similarity index 60% rename from layout/src/kindexpr.rs rename to layout/src/langtype.rs index d6e28b46..9ef19ee3 100644 --- a/layout/src/kindexpr.rs +++ b/layout/src/langtype.rs @@ -1,4 +1,4 @@ -//! 签名 kindexpr(runtime篇-07,修订:无家族简写):语法校验 + 值匹配。 +//! 签名 langtype(runtime篇-07,修订:无家族简写):语法校验 + 值匹配。 //! //! type = atom ("|" atom)* //! atom = shape | mapexpr @@ -6,13 +6,15 @@ //! mapexpr= key "·" type # stringkeymap;value 递归 → 嵌套 map[string]map[…] //! key = "[]" char-enc # 字符串键(= Go map[string]V) //! | "[" scalar ("," scalar)* "]" # 标量元组键(物理以字符串格式落 key) -//! dims = "[]" | "[" dim ("," dim)* "]" -//! dim = integer | "?" +//! dims = "[]" | "[" elem ("," elem)* "]" # 至多一个变元量词 ?/*/+ +//! elem = integer | "." | "?" | "*" | "+" # 轴量词(借鉴正则:. .? .* .+) //! any = "any" # 通配,匹配任意 kind //! kind = 精确 kind 串 # 见 [`known_kind`] //! -//! 铁律:不提供 int/uint/float/num 数值家族(位宽开放,int4/fp8/fp16…), +//! 铁律:不提供 int/uint/float/num 数值家族简写(位宽开放,纳入须逐个显式命名, +//! 如低精度浮点 float16/bfloat16/float8/e4m3/float8/e5m2;int4 等仍未纳入), //! 也不提供 char 编码简写(编码须写明确,如 char/utf8、char/utf32)。多态靠显式 "|" 枚举。 +//! 变参 "..." 是签名层 arity(吸收 0..N 个实参),不是 langtype 的一部分——由主槽 dynamic 字节承载。 /// 精确 kind 集合(对齐 runtime kind 常量,不含 None)。 fn known_kind(k: &str) -> bool { @@ -29,6 +31,10 @@ fn known_kind(k: &str) -> bool { | "uint64" | "float32" | "float64" + | "float16" + | "bfloat16" + | "float8/e4m3" + | "float8/e5m2" | "char/utf32" | "char/utf8" | "char/ascii" @@ -45,7 +51,7 @@ fn known_kind(k: &str) -> bool { ) } -/// structref = "/" path:指向 /lib 下类型定义节点的完整路径(实例 kindexpr、struct 字段类型)。 +/// structref = "/" path:指向 /lib 下类型定义节点的完整路径(实例 langtype、struct 字段类型)。 /// 仅做语法承认(`/` + 合法路径段),不解析 /lib 是否存在该类型 —— 存在性/字段一致性留给 runtime。 fn valid_structref(s: &str) -> bool { let rest = match s.strip_prefix('/') { @@ -65,15 +71,36 @@ fn valid_base(s: &str) -> bool { s == "any" || known_kind(s) } -fn valid_dim(s: &str) -> bool { +fn elem_is_variable(s: &str) -> bool { + s == "?" || s == "*" || s == "+" +} + +/// elem = integer | "." | "?" | "*" | "+"(借鉴正则的轴量词)。 +fn valid_elem(s: &str) -> bool { if s.is_empty() { return false; } - s == "?" || s.bytes().all(|b| b.is_ascii_digit()) + matches!(s, "." | "?" | "*" | "+") || s.bytes().all(|b| b.is_ascii_digit()) } +/// dims = ε(空 [] = 1 维任意,等价 [.]) | elem ("," elem)*;至多一个变元量词 ?/*/+。 fn valid_dims(s: &str) -> bool { - s.is_empty() || s.split(',').all(valid_dim) + if s.is_empty() { + return true; + } + let mut nvar = 0; + for e in s.split(',') { + if !valid_elem(e) { + return false; + } + if elem_is_variable(e) { + nvar += 1; + if nvar > 1 { + return false; + } + } + } + true } fn valid_shape(s: &str) -> bool { @@ -138,25 +165,14 @@ fn valid_atom(s: &str) -> bool { return valid_structref(s); } if let Some(i) = s.find('·') { - return valid_key(&s[..i]) && valid_kindexpr(&s[i + '·'.len_utf8()..]); + return valid_key(&s[..i]) && valid_langtype(&s[i + '·'.len_utf8()..]); } valid_shape(s) } -/// 末参变参标记:`A:any...` 表 0..N 个同型实参。 -pub fn is_variadic(expr: &str) -> bool { - expr.ends_with("...") -} - -/// 去掉尾缀 `...`(非变参原样返回)。 -pub fn strip_variadic(expr: &str) -> &str { - expr.strip_suffix("...").unwrap_or(expr) -} - -/// 类型表达式语法校验(装载期)。允许末参尾缀 `...` 变参。 -pub fn valid_kindexpr(expr: &str) -> bool { - let e = strip_variadic(expr); - !e.is_empty() && e.split('|').all(valid_atom) +/// 类型表达式语法校验(装载期)。变参 `...` 是签名层 arity、不入 langtype 串,此处永不见。 +pub fn valid_langtype(expr: &str) -> bool { + !expr.is_empty() && expr.split('|').all(valid_atom) } fn base_match(s: &str, kind: &str) -> bool { @@ -166,23 +182,56 @@ fn base_match(s: &str, kind: &str) -> bool { } } +/// 定长元(整数或 `.`)匹配单轴:`.` 任意长,整数须精确相等。 +fn fixed_elem_match(e: &str, d: i32) -> bool { + e == "." || e.parse::().ok() == Some(d) +} + +/// 轴量词序列 → (ndim,dims)。空串等价 `[.]`(恰一维)。至多一个变元量词,切分唯一无回溯。 fn match_shape(s: &str, ndim: i32, dims: &[i32]) -> bool { if s.is_empty() { return ndim == 1; } - let parts: Vec<&str> = s.split(',').collect(); - if parts.len() as i32 != ndim { - return false; - } - for (i, p) in parts.iter().enumerate() { - if *p == "?" { - continue; + let elems: Vec<&str> = s.split(',').collect(); + let m = elems.len(); + let vpos = elems.iter().position(|e| elem_is_variable(e)); + match vpos { + None => { + if ndim as usize != m { + return false; + } + elems + .iter() + .enumerate() + .all(|(i, e)| fixed_elem_match(e, dims[i])) } - if p.parse::().ok() != Some(dims[i]) { - return false; + Some(vpos) => { + let f = (m - 1) as i32; // 定长元个数 + let rem = ndim - f; // 变元量词吸收的轴数 + let ok_rem = match elems[vpos] { + "?" => rem == 0 || rem == 1, + "*" => rem >= 0, + _ => rem >= 1, // "+" + }; + if !ok_rem { + return false; + } + // 前缀定长元 → dims[0..vpos] + for (i, e) in elems.iter().take(vpos).enumerate() { + if !fixed_elem_match(e, dims[i]) { + return false; + } + } + // 后缀定长元 → dims 末 suf 轴 + let suf = m - 1 - vpos; + for k in 0..suf { + if !fixed_elem_match(elems[vpos + 1 + k], dims[ndim as usize - suf + k]) { + return false; + } + } + true } } - true } /// ndim = -1 表示「已消费 dims,不再判 ndim」(递归哨兵)。 @@ -208,10 +257,12 @@ fn match_atom(s: &str, kind: &str, ndim: i32, dims: &[i32]) -> bool { } /// 单值(kind/ndim/dims)是否匹配类型表达式:任一 atom 命中即 true。 -/// 变参 `...` 按单元素判定(去尾缀后匹配),重复由派发循环处理。 -pub fn match_kindexpr(expr: &str, kind: &str, ndim: i32, dims: &[i32]) -> bool { - let e = strip_variadic(expr); - !e.is_empty() && e.split('|').any(|atom| match_atom(atom, kind, ndim, dims)) +/// 变参 arity 不在此判(靠主槽 dynamic 字节 + 派发循环)。 +pub fn match_langtype(expr: &str, kind: &str, ndim: i32, dims: &[i32]) -> bool { + !expr.is_empty() + && expr + .split('|') + .any(|atom| match_atom(atom, kind, ndim, dims)) } #[cfg(test)] @@ -236,16 +287,19 @@ mod tests { "[2]float32", "[2,3]float32", "[2,3,4]float64", - "[?,768]float32", - "[?,?]int8", + "[.,768]float32", + "[.,.]int8", + "[.]float32", + "[*]float32", + "[?,3]float32", + "[+,3]float32", + "[2,*]float32", + "[512,+]float32", "int64|float64", "[2,3]float32|float32", "[]float32|[]float64", "bool|char/utf8", "index|object", - "any...", - "int64|float64...", - "[]float32...", "[]char/utf8·int64", "[]char/utf32·[]char/utf8", "[]char/utf8·[]char/utf8·int64", @@ -258,7 +312,7 @@ mod tests { "/lib/Node", "/lib/Point|/lib/Node", ] { - assert!(valid_kindexpr(e), "{e} should be valid"); + assert!(valid_langtype(e), "{e} should be valid"); } for e in [ "·int64", @@ -267,32 +321,47 @@ mod tests { "int·int64", "[]char/utf8·nope", "[]int32·int64", - "[?]·int64", + "[.]·int64", "[int32,]·int64", "[foo,int32]·int64", + "any...", + "int64|float64...", + "[]float32...", + "[*,+]int64", + "[?,*]int64", "/", "/lib/", "/lib//Point", "lib/Point", "/lib/Po int", ] { - assert!(!valid_kindexpr(e), "{e} should be invalid"); + assert!(!valid_langtype(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, &[])); + assert!(match_langtype("/lib/Point", "/lib/Point", 0, &[])); + assert!(!match_langtype("/lib/Point", "/lib/Node", 0, &[])); + assert!(match_langtype("[]char/utf8·int64", "stringkeymap", 1, &[3])); + assert!(!match_langtype("[]char/utf8·int64", "object", 0, &[])); } #[test] - fn variadic() { - assert!(is_variadic("any...")); - assert!(!is_variadic("any")); - assert_eq!(strip_variadic("int64|float64..."), "int64|float64"); - // 变参 kindexp 按单元素匹配 - assert!(match_kindexpr("any...", "int64", 0, &[])); - assert!(match_kindexpr("int64|float64...", "float64", 0, &[])); - assert!(!match_kindexpr("int64...", "bool", 0, &[])); + fn quantifiers() { + // "." 恰 1 轴任意长 + assert!(match_langtype("[.]int64", "int64", 1, &[5])); + assert!(!match_langtype("[.]int64", "int64", 2, &[2, 3])); + // "*" 0+ 轴 + assert!(match_langtype("[*]int64", "int64", 0, &[])); + assert!(match_langtype("[*]int64", "int64", 3, &[2, 3, 4])); + // "+" 1+ 轴 + assert!(!match_langtype("[+]int64", "int64", 0, &[])); + assert!(match_langtype("[+]int64", "int64", 2, &[2, 3])); + // "?" 0 或 1 轴 + assert!(match_langtype("[?,3]int64", "int64", 1, &[3])); + assert!(match_langtype("[?,3]int64", "int64", 2, &[7, 3])); + assert!(!match_langtype("[?,3]int64", "int64", 3, &[7, 8, 3])); + // 前缀定长 + 尾量词 + assert!(match_langtype("[512,*]int64", "int64", 1, &[512])); + assert!(match_langtype("[512,*]int64", "int64", 3, &[512, 8, 8])); + assert!(!match_langtype("[512,*]int64", "int64", 2, &[7, 8])); } #[test] @@ -328,7 +397,7 @@ mod tests { "string", "charbyte", ] { - assert!(!valid_kindexpr(e), "{e} should be invalid"); + assert!(!valid_langtype(e), "{e} should be invalid"); } } @@ -348,8 +417,8 @@ mod tests { ("[]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), - ("[?,768]float32", "float32", 2, &[100, 512], false), + ("[.,768]float32", "float32", 2, &[100, 768], true), + ("[.,768]float32", "float32", 2, &[100, 512], false), ("[2,3]float32|float32", "float32", 0, &[], true), ("[2,3]float32|float32", "float64", 0, &[], false), ("[]float32|[]float64", "float64", 1, &[10], true), @@ -357,10 +426,10 @@ mod tests { ("index|object", "index", 0, &[], true), ]; for (expr, kind, ndim, dims, want) in cases { - let got = match_kindexpr(expr, kind, ndim, dims); + let got = match_langtype(expr, kind, ndim, dims); assert_eq!( got, want, - "match_kindexpr({expr}, {kind}, ndim={ndim}, dims={dims:?})" + "match_langtype({expr}, {kind}, ndim={ndim}, dims={dims:?})" ); } } diff --git a/layout/src/lib.rs b/layout/src/lib.rs index 93089b17..de86a836 100644 --- a/layout/src/lib.rs +++ b/layout/src/lib.rs @@ -9,8 +9,8 @@ pub mod capi; pub mod code; pub mod ffi; pub mod keytree; -pub mod kindexpr; pub mod kvkind; +pub mod langtype; pub mod lower; pub mod parser; pub mod scanner; diff --git a/layout/src/lower.rs b/layout/src/lower.rs index 1bb74eca..80dc238a 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -7,7 +7,7 @@ use super::scanner::{Diagnostic, Pos}; use super::{builtin, keytree, symbol}; /// 容器类型(object / stringkeymap / mapexpr)不可用 `[]` 下标访问成员—— -/// `[]` 仅限 compact array(shaped kindexpr,含字符串 `[]char/*`)。 +/// `[]` 仅限 compact array(shaped langtype,含字符串 `[]char/*`)。 fn is_container_type(t: &str) -> bool { t == "object" || t == "stringkeymap" || t.contains(keytree::MEMBER_SEP) } @@ -852,7 +852,7 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap { return "int64".to_string(); } - "xv·kindexpr" => return "[]char/utf8".to_string(), + "xv·langtype" => return "[]char/utf8".to_string(), "ndarray·shape" => return "[]int64".to_string(), "kv·list" => return "[]char/utf8".to_string(), "string·char" | "string·set" | "string·slice" | "string·concat" | "string·formatint" diff --git a/layout/src/parser.rs b/layout/src/parser.rs index ae362c32..c3009b10 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -536,7 +536,9 @@ impl Parser { fn check_param_types(&mut self, sig: &FuncSig) { for param in &sig.params { - if !crate::kindexpr::valid_kindexpr(¶m.ty) { + // 末读参 `...` 是签名层变参标记,校验前剥离(变参落 dynamic 字节,见 [[函数]])。 + let ty = param.ty.strip_suffix("...").unwrap_or(¶m.ty); + if !crate::langtype::valid_langtype(ty) { self.errors.push(Diagnostic { pos: Pos { line: 0, col: 0 }, message: format!( @@ -555,7 +557,9 @@ impl Parser { } } for ret in &sig.returns { - if !crate::kindexpr::valid_kindexpr(&ret.ty) { + // 变参标记剥离后校验;变参写参的非法性由 check_variadic 专门报错。 + let ty = ret.ty.strip_suffix("...").unwrap_or(&ret.ty); + if !crate::langtype::valid_langtype(ty) { self.errors.push(Diagnostic { pos: Pos { line: 0, col: 0 }, message: format!( @@ -604,7 +608,7 @@ impl Parser { fn check_variadic(&mut self, sig: &FuncSig) { let last = sig.params.len().saturating_sub(1); for (i, p) in sig.params.iter().enumerate() { - if crate::kindexpr::is_variadic(&p.ty) && i != last { + if p.ty.ends_with("...") && i != last { self.errors.push(Diagnostic { pos: Pos { line: 0, col: 0 }, message: format!( @@ -620,7 +624,7 @@ impl Parser { } } for r in &sig.returns { - if crate::kindexpr::is_variadic(&r.ty) { + if r.ty.ends_with("...") { self.errors.push(Diagnostic { pos: Pos { line: 0, col: 0 }, message: format!( @@ -884,7 +888,7 @@ impl Parser { /// 校验散 key 字面量 `{...}` 的出现位置。`top_legal` 表示当前上下文允许顶层出现 /// (赋值右值 / for-in 源);无论如何,其元素内部都不得再嵌套散 key 字面量。 - // 空容器字面量 `{}` 不含任何类型信息,必须由写目标显式标注 kindexpr: + // 空容器字面量 `{}` 不含任何类型信息,必须由写目标显式标注 langtype: // 禁 `d = {}`,须 `d:[]char/utf8·int64 = {}`(非空 `{a=…}` 可由成员推断,放行)。 fn check_empty_container_typed(&mut self, inst: &Instruction) { let Some(e) = &inst.expr else { return }; @@ -911,12 +915,12 @@ impl Parser { src_file: String::new(), src_name: String::new(), }); - } else if !super::kindexpr::valid_kindexpr(ty) { + } else if !super::langtype::valid_langtype(ty) { self.errors.push(Diagnostic { pos: t.pos, warn: false, info: false, - message: format!("invalid kindexpr {ty:?} on empty container literal target"), + message: format!("invalid langtype {ty:?} on empty container literal target"), source: String::new(), src_file: String::new(), src_name: String::new(), @@ -1985,7 +1989,7 @@ impl Parser { continue; } let name = inst.writes.get(j).cloned().unwrap_or_default(); - if !is_array_kindexp(wt) && expr_is_array { + if !is_array_langtype(wt) && expr_is_array { self.errors.push(Diagnostic { pos: Pos { line: 0, col: 0 }, message: format!("write {name:?} declared scalar {wt} but assigned an array literal — use []{wt} instead"), @@ -1995,7 +1999,7 @@ impl Parser { src_file: String::new(), src_name: String::new(), }); - } else if is_array_kindexp(wt) && expr_is_scalar_lit { + } else if is_array_langtype(wt) && expr_is_scalar_lit { self.errors.push(Diagnostic { pos: Pos { line: 0, col: 0 }, message: format!("write {name:?} declared {wt} but assigned a scalar literal"), @@ -2055,7 +2059,7 @@ fn attach_comments(st: Stmt, comments: Vec) -> Stmt { st } -fn is_array_kindexp(t: &str) -> bool { +fn is_array_langtype(t: &str) -> bool { t.contains('[') } diff --git a/layout/tests/issue116.rs b/layout/tests/issue116.rs index c5418c1c..b35cb9cc 100644 --- a/layout/tests/issue116.rs +++ b/layout/tests/issue116.rs @@ -10,7 +10,7 @@ fn body(data: &[u8]) -> &[u8] { fn sig(data: &[u8]) -> String { let b = body(data); - String::from_utf8_lossy(&b[4.min(b.len())..]).into_owned() + String::from_utf8_lossy(&b[5.min(b.len())..]).into_owned() } fn slot_text(data: &[u8]) -> String { diff --git a/layout/tests/pipeline_test.rs b/layout/tests/pipeline_test.rs index ada355ca..0076191b 100644 --- a/layout/tests/pipeline_test.rs +++ b/layout/tests/pipeline_test.rs @@ -7,7 +7,7 @@ fn body(data: &[u8]) -> &[u8] { fn sig(data: &[u8]) -> String { let b = body(data); - String::from_utf8_lossy(&b[4.min(b.len())..]).into_owned() + String::from_utf8_lossy(&b[5.min(b.len())..]).into_owned() } fn fresh_kv() -> Kv { @@ -30,7 +30,7 @@ fn compile_simple_func() { let b = body(&sig_val); assert_eq!(kvkind::rwfunc_num_reads(b), 2); assert_eq!(kvkind::rwfunc_num_writes(b), 1); - // kindexp 列表:读参在前(nr=2)、写参在后(nw=1) + // langtype 列表:读参在前(nr=2)、写参在后(nw=1) assert_eq!( kvkind::rwfunc_param_types(b), vec!["int64", "int64", "int64"] diff --git a/runtime/src/const.h b/runtime/src/const.h index 4e5d5ad7..d518ac66 100644 --- a/runtime/src/const.h +++ b/runtime/src/const.h @@ -17,6 +17,10 @@ #define KVSPACE_KIND_UINT64 "uint64" #define KVSPACE_KIND_FLOAT32 "float32" #define KVSPACE_KIND_FLOAT64 "float64" +#define KVSPACE_KIND_FLOAT16 "float16" +#define KVSPACE_KIND_BFLOAT16 "bfloat16" +#define KVSPACE_KIND_FLOAT8_E4M3 "float8/e4m3" +#define KVSPACE_KIND_FLOAT8_E5M2 "float8/e5m2" #define KVSPACE_KIND_CHAR "char/utf32" #define KVSPACE_KIND_CHAR_UTF8 "char/utf8" #define KVSPACE_KIND_CHAR_ASCII "char/ascii" diff --git a/runtime/src/kvcpu.c b/runtime/src/kvcpu.c index e34e3736..eb967501 100644 --- a/runtime/src/kvcpu.c +++ b/runtime/src/kvcpu.c @@ -45,11 +45,12 @@ typedef struct opmeta_ent { int notinmyrwircaps; /* 1 = 不在本 runtime myrwircaps、须经 def rwir 路由;0 = 用户 rwfunc */ char *def_sig; /* notinmyrwircaps 时的读参 langtype 签名(owned,可 NULL) */ int def_nr; + int def_dyn; /* 末读参变参(主槽 body 的 dynamic 字节) */ struct opmeta_ent *next; } opmeta_ent_t; static opmeta_ent_t *g_opmeta_cache[RWIR_CACHE_BUCKETS]; -static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr); +static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, int *out_dyn); static opmeta_ent_t *opmeta_get(kvlangKv_t *kv, const char *opcode) { size_t b = rwir_hash(opcode, 0); @@ -60,9 +61,10 @@ static opmeta_ent_t *opmeta_get(kvlangKv_t *kv, const char *opcode) { e->notinmyrwircaps = notinmyrwircaps(kv, opcode) ? 1 : 0; e->def_sig = NULL; e->def_nr = 0; + e->def_dyn = 0; if (e->notinmyrwircaps) { char *rk = kvlangKeytreeRwir(opcode); - e->def_sig = load_def_reads(kv, rk, &e->def_nr); + e->def_sig = load_def_reads(kv, rk, &e->def_nr, &e->def_dyn); free(rk); } e->next = g_opmeta_cache[b]; @@ -129,11 +131,11 @@ static bool is_literal(const char *s) { /* 派发期读参类型校验(runtime篇-07 第八节):把每个实参的 kind 逐一匹配 * rwir/rwfunc 定义的读参 kindexp。def_sig 为读参 kindexp 在前的 \n 分隔列表, - * def_nr 为定义读参数。空 kindexp / any 跳过;末读参 "..." 变参吸收其后全部实参。 + * def_nr 为定义读参数,dynamic=1 表末读参变参吸收其后全部实参。空 kindexp / any 跳过。 * XValue 头只携带 array_len 不含多维 shape,故仅校验 kind 层。 * 不匹配 → 置 TypeError,返回 -1;通过返回 0。 */ static int check_read_types(kvlangKv_t *kv, const char *vtid, const char *pc, - const char *opcode, const char *def_sig, int def_nr, + const char *opcode, const char *def_sig, int def_nr, int dynamic, kvlangParam_t *args, int nargs) { if (def_nr <= 0 || !def_sig || !*def_sig) return 0; char *dup = strdup(def_sig); @@ -145,7 +147,7 @@ static int check_read_types(kvlangKv_t *kv, const char *vtid, const char *pc, if (!nl) break; *nl = 0; s = nl + 1; } - bool var_last = rn > 0 && kvlangLangtypeVariadic(reads[rn - 1]); + bool var_last = rn > 0 && dynamic; int min_args = var_last ? rn - 1 : rn; char *fr = kvlangKeytreeFrameRoot(pc); int rc = 0; @@ -186,18 +188,20 @@ static int check_read_types(kvlangKv_t *kv, const char *vtid, const char *pc, /* 读取 rwir/rwfunc 定义体的 kindexp-list(nr/nw 前缀后的 \n 分隔串)。 * 返回 malloc 串(调用方 free)并置 *out_nr;无定义返回 NULL。 */ -static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr) { +static char *load_def_reads(kvlangKv_t *kv, const char *key, int *out_nr, int *out_dyn) { *out_nr = 0; + *out_dyn = 0; kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(kv, key, &v); if (kvlangXvalueNone(&v)) { kvlangXvalueFree(&v); return NULL; } kvspaceHead_t h; kvlangXvalueHead(&v, &h); int32_t bl; const uint8_t *b = kvlangXvalueBody(&v, &h, &bl); - if (bl < 4) { kvlangXvalueFree(&v); return NULL; } + if (bl < 5) { kvlangXvalueFree(&v); return NULL; } *out_nr = b[0] | (b[1] << 8); - size_t sl = (size_t)(bl - 4); + *out_dyn = b[4]; + size_t sl = (size_t)(bl - 5); char *sig = malloc(sl + 1); - memcpy(sig, b + 4, sl); sig[sl] = 0; + memcpy(sig, b + 5, sl); sig[sl] = 0; kvlangXvalueFree(&v); return sig; } @@ -366,12 +370,13 @@ static char *handle_call(kvlangKv_t *kv, const char *pc, kvlangRwirInst_t *inst) const uint8_t *sbody = sig.data + h.body_offset; int nr = sbody[0] | (sbody[1] << 8); int nw = sbody[2] | (sbody[3] << 8); + int dyn = h.body_len >= 5 ? sbody[4] : 0; { /* 读参类型校验:reads[0]=函数名,实参从 reads[1] 起 */ - size_t sl = h.body_len >= 4 ? (size_t)(h.body_len - 4) : 0; + size_t sl = h.body_len >= 5 ? (size_t)(h.body_len - 5) : 0; char *ds = malloc(sl + 1); - memcpy(ds, sbody + 4, sl); ds[sl] = 0; - int crc = check_read_types(kv, vtid, pc, fn, ds, nr, inst->reads + 1, inst->nr - 1); + memcpy(ds, sbody + 5, sl); ds[sl] = 0; + int crc = check_read_types(kv, vtid, pc, fn, ds, nr, dyn, inst->reads + 1, inst->nr - 1); free(ds); if (crc != 0) goto fail; } @@ -723,7 +728,7 @@ int kvlangKvcpuExecuteMode(kvlangKv_t *kv, const char *pc, kvmode_t mode, char * } else if (opmeta_get(kv, inst->opcode)->notinmyrwircaps) { opmeta_ent_t *m = opmeta_get(kv, inst->opcode); if (m->def_sig) - exec_err = check_read_types(kv, vtid, cur, inst->opcode, m->def_sig, m->def_nr, inst->reads, inst->nr); + exec_err = check_read_types(kv, vtid, cur, inst->opcode, m->def_sig, m->def_nr, m->def_dyn, inst->reads, inst->nr); if (exec_err == 0 && mode == KVMODE_RETURN) { if (out_pc) *out_pc = strdup(cur); free(fr); if (tmp_owned) kvlangRwirInstFree(&tmp); diff --git a/runtime/src/langtype.c b/runtime/src/langtype.c index dd3d3b5d..c286fb3d 100644 --- a/runtime/src/langtype.c +++ b/runtime/src/langtype.c @@ -3,12 +3,16 @@ /* ── 签名 langtype(runtime篇-07,修订:无家族简写)────────────────── * type = atom ("|" atom)* * atom = [dims] ( any | kind ) - * dims = "[]" | "[" dim ("," dim)* "]" - * dim = integer | "?" + * dims = "[]" | "[" elem ("," elem)* "]" # 至多一个变元量词 ? 星 加 + * elem = integer | "." | "?" | 星 | 加 # 轴量词(借鉴正则的 . 及量词) * any = "any" # 通配,匹配任意 kind * kind = 精确 kind 串 # 见 known_kind * - * 铁律:不提供 int/uint/float/num 数值家族(位宽开放集合,int4/fp8/fp16…), + * 变参 "..." 是签名层 arity(吸收 0..N 个实参),不是 langtype 的一部分—— + * 由主槽 body 的 dynamic 字节承载,本文件的 valid/match 永不见 "..."。 + * + * 铁律:不提供 int/uint/float/num 数值家族简写(位宽开放,纳入须逐个显式命名, + * 如低精度浮点 float16/bfloat16/float8/e4m3/float8/e5m2;int4 等仍未纳入), * 也不提供 char 编码简写(编码须写明确,如 char/utf8、char/utf32)。 * 多态靠显式 "|" 枚举(如 int8|int16|int32|int64)。 */ @@ -26,6 +30,8 @@ static bool known_kind(const char *s, size_t len) { kind_eq(s, len, KVSPACE_KIND_UINT8) || kind_eq(s, len, KVSPACE_KIND_UINT16) || kind_eq(s, len, KVSPACE_KIND_UINT32) || kind_eq(s, len, KVSPACE_KIND_UINT64) || kind_eq(s, len, KVSPACE_KIND_FLOAT32) || kind_eq(s, len, KVSPACE_KIND_FLOAT64) || + kind_eq(s, len, KVSPACE_KIND_FLOAT16) || kind_eq(s, len, KVSPACE_KIND_BFLOAT16) || + kind_eq(s, len, KVSPACE_KIND_FLOAT8_E4M3) || kind_eq(s, len, KVSPACE_KIND_FLOAT8_E5M2) || kind_eq(s, len, KVSPACE_KIND_CHAR) || kind_eq(s, len, KVSPACE_KIND_CHAR_UTF8) || kind_eq(s, len, KVSPACE_KIND_CHAR_ASCII) || kind_eq(s, len, KVSPACE_KIND_OBJ) || kind_eq(s, len, KVSPACE_KIND_MAP) || @@ -63,22 +69,29 @@ static bool valid_base(const char *s, size_t len) { return known_kind(s, len); } -static bool valid_dim(const char *s, size_t len) { +/* elem = integer | "." | "?" | "*" | "+"(借鉴正则的轴量词)。 */ +static bool elem_is_variable(const char *s, size_t len) { + return len == 1 && (s[0] == '?' || s[0] == '*' || s[0] == '+'); +} + +static bool valid_elem(const char *s, size_t len) { if (len == 0) return false; - if (len == 1 && s[0] == '?') return true; + if (len == 1 && (s[0] == '.' || s[0] == '?' || s[0] == '*' || s[0] == '+')) return true; for (size_t i = 0; i < len; i++) if (s[i] < '0' || s[i] > '9') return false; return true; } -/* dims = ε(空 [] = 1 维任意,等价 [?]) | dim ("," dim)* */ +/* dims = ε(空 [] = 1 维任意,等价 [.]) | elem ("," elem)*;至多一个变元量词。 */ static bool valid_dims(const char *s, size_t len) { if (len == 0) return true; const char *p = s, *end = s + len; + int nvar = 0; while (p < end) { const char *comma = memchr(p, ',', (size_t)(end - p)); size_t seg = comma ? (size_t)(comma - p) : (size_t)(end - p); - if (!valid_dim(p, seg)) return false; + if (!valid_elem(p, seg)) return false; + if (elem_is_variable(p, seg) && ++nvar > 1) return false; p += seg + (comma ? 1 : 0); } return true; @@ -99,26 +112,11 @@ static bool valid_atom(const char *s, size_t len) { return valid_base(p, (size_t)(s + len - p)); } -/* 末参变参标记:A:any... 表 0..N 个同型实参。 */ -bool kvlangLangtypeVariadic(const char *expr) { - if (!expr) return false; - size_t n = strlen(expr); - return n >= 3 && memcmp(expr + n - 3, "...", 3) == 0; -} - -/* 去掉尾缀 "..." 后的有效长度。 */ -static size_t effective_len(const char *expr) { - size_t n = strlen(expr); - return (n >= 3 && memcmp(expr + n - 3, "...", 3) == 0) ? n - 3 : n; -} - -/* 类型表达式语法校验(装载期)。允许末参尾缀 "..." 变参。 */ +/* 类型表达式语法校验(装载期)。变参 "..." 是签名层 arity、不入 langtype 串,此处永不见。 */ bool kvlangLangtypeValid(const char *expr) { if (!expr || !*expr) return false; - size_t total = effective_len(expr); - if (total == 0) return false; const char *p = expr; - const char *end = expr + total; + const char *end = expr + strlen(expr); for (;;) { const char *pipe = memchr(p, '|', (size_t)(end - p)); size_t len = pipe ? (size_t)(pipe - p) : (size_t)(end - p); @@ -136,25 +134,48 @@ static bool base_match(const char *s, size_t len, const char *kind) { return kind_eq(s, len, kind); } -/* match_shape:shape="" 等价 "[?]" → 恰一维(ndim==1);否则维数须一致且逐维 ?(跳过)或精确相等。 */ +/* 定长元(整数或 ".")匹配单轴:. 任意长,整数须精确相等。 */ +static bool fixed_elem_match(const char *s, size_t len, int32_t d) { + if (len == 1 && s[0] == '.') return true; + long v = 0; + for (size_t j = 0; j < len; j++) v = v * 10 + (s[j] - '0'); + return v == d; +} + +/* match_shape:轴量词序列 → (ndim,dims)。空串等价 "[.]"(恰一维)。 + * 至多一个变元量词,切分唯一无回溯:设定长元共 f 个,rem = ndim - f。 */ static bool match_shape(const char *s, size_t len, int32_t ndim, const int32_t *dims) { if (len == 0) return ndim == 1; - int count = 1; - for (size_t i = 0; i < len; i++) if (s[i] == ',') count++; - if (count != ndim) return false; + /* 切分为 elem 段(最多 64 维)。 */ + const char *seg[64]; size_t seglen[64]; int m = 0; const char *p = s, *end = s + len; - int i = 0; - while (p < end && i < ndim) { + int vpos = -1; + while (p < end && m < 64) { const char *comma = memchr(p, ',', (size_t)(end - p)); - size_t seg = comma ? (size_t)(comma - p) : (size_t)(end - p); - if (!(seg == 1 && p[0] == '?')) { - long v = 0; - for (size_t j = 0; j < seg; j++) v = v * 10 + (p[j] - '0'); - if (v != dims[i]) return false; - } - p += seg + (comma ? 1 : 0); - i++; + size_t l = comma ? (size_t)(comma - p) : (size_t)(end - p); + seg[m] = p; seglen[m] = l; + if (elem_is_variable(p, l)) vpos = m; + m++; + p += l + (comma ? 1 : 0); + } + if (vpos < 0) { + if (ndim != m) return false; + for (int i = 0; i < m; i++) + if (!fixed_elem_match(seg[i], seglen[i], dims[i])) return false; + return true; } + int f = m - 1; /* 定长元个数 */ + int rem = ndim - f; /* 变元量词吸收的轴数 */ + char q = seg[vpos][0]; + if (q == '?' && !(rem == 0 || rem == 1)) return false; + if (q == '*' && rem < 0) return false; + if (q == '+' && rem < 1) return false; + for (int i = 0; i < vpos; i++) /* 前缀定长元 → dims[0..vpos] */ + if (!fixed_elem_match(seg[i], seglen[i], dims[i])) return false; + int suf = m - 1 - vpos; /* 后缀定长元 → dims 末 suf 轴 */ + for (int k = 0; k < suf; k++) + if (!fixed_elem_match(seg[vpos + 1 + k], seglen[vpos + 1 + k], dims[ndim - suf + k])) + return false; return true; } @@ -172,10 +193,10 @@ static bool match_atom(const char *s, size_t len, const char *kind, int32_t ndim } /* 单值(kind/ndim/dims)是否匹配类型表达式:任一 atom 命中即 true。 - * 变参 "..." 按单元素判定(去尾缀后匹配),重复由派发循环处理。 */ + * 变参 arity 不在此判(靠主槽 dynamic 字节 + 派发循环)。 */ bool kvlangLangtypeMatch(const char *expr, const char *kind, int32_t ndim, const int32_t *dims) { if (!expr || !kind) return false; - const char *end = expr + effective_len(expr); + const char *end = expr + strlen(expr); const char *p = expr; while (p < end) { const char *pipe = memchr(p, '|', (size_t)(end - p)); diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 2e6c76ba..a89ed0f5 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -177,7 +177,6 @@ static inline int kvlangLtElemSize(int id) { /* 签名 langtype(runtime篇-07)校验/匹配 */ bool kvlangLangtypeValid(const char *expr); bool kvlangLangtypeMatch(const char *expr, const char *kind, int32_t ndim, const int32_t *dims); -bool kvlangLangtypeVariadic(const char *expr); /* 标量 0copy 视图(取代 kvlangXvalueAsInt64 等按值转换):decode head 一次, * 持 langtype id + 指向 body 首字节的借用指针,热路径按 id 直读 body。 */ typedef struct { diff --git a/runtime/src/xvalue.c b/runtime/src/xvalue.c index 378ecdd8..ac77ac7c 100644 --- a/runtime/src/xvalue.c +++ b/runtime/src/xvalue.c @@ -442,7 +442,7 @@ char *kvlangXvalueValueString(const kvlangXvalue_t *v) { if (strcmp(k, KVSPACE_KIND_CHAR) == 0) return utf32_to_utf8(body, blen); if (strcmp(k, KVSPACE_KIND_RWIR) == 0 || strcmp(k, KVSPACE_KIND_RWIR_OR_RWFUNC) == 0) - return strndup2(body + (blen >= 4 ? 4 : 0), blen >= 4 ? blen - 4 : 0); + return strndup2(body + (blen >= 5 ? 5 : 0), blen >= 5 ? blen - 5 : 0); if (strcmp(k, KVSPACE_KIND_RWFUNC) == 0) { kvlangStrbuf_t b; kvlangStrbufInit(&b); @@ -556,15 +556,35 @@ void kvlangXvalueNewPtr(kvlangXvalue_t *v, const char *target_langtype, const ch v->len = len; v->borrowed = 0; } +/* body = [nr:u16 LE][nw:u16 LE][dynamic:u8][clean_sig]。 + * 末读参尾缀 "..." 是签名层变参标记:此处剥离,置 dynamic=1,langtype 串保持纯净。 */ void kvlangXvalueNewRwir(kvlangXvalue_t *v, int32_t nr, int32_t nw, const char *sig) { size_t sl = strlen(sig); - uint8_t *raw = malloc(4 + sl); + uint8_t *raw = malloc(5 + sl); raw[0] = nr & 0xFF; raw[1] = (nr >> 8) & 0xFF; raw[2] = nw & 0xFF; raw[3] = (nw >> 8) & 0xFF; - memcpy(raw + 4, sig, sl); - kvlangXvalueNewTlv(v, KVSPACE_KIND_DEF_RWIR, raw, (uint32_t)(4 + sl), 1); + raw[4] = 0; + memcpy(raw + 5, sig, sl); + /* 末读参尾缀 "..." 是签名层变参标记:此处剥离,置 dynamic=1,langtype 串保持纯净。 */ + if (nr > 0) { + char *base = (char *)raw + 5; + char *p = base; + for (int i = 0; i < nr - 1; i++) { + char *nl = strchr(p, '\n'); + if (!nl) { p = base + sl; break; } + p = nl + 1; + } + char *nl = strchr(p, '\n'); + size_t seg = nl ? (size_t)(nl - p) : (size_t)(base + sl - p); + if (seg >= 3 && memcmp(p + seg - 3, "...", 3) == 0) { + raw[4] = 1; + memmove(p + seg - 3, p + seg, (size_t)(base + sl - (p + seg))); + sl -= 3; + } + } + kvlangXvalueNewTlv(v, KVSPACE_KIND_DEF_RWIR, raw, (uint32_t)(5 + sl), 1); free(raw); } 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" index 2e3526af..32eef7a5 100644 --- "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" @@ -16,7 +16,20 @@ kvlang 的数字种类**必须携带精确位宽**: 整数按小端补码编码,浮点按 IEEE 754 编码。 -**铁律——无家族简写**。`int`、`uint`、`float`、`num`、`char` 这类不含位宽/编码的简写**不是**合法种类,在任何类型标注中均**不得**出现。位宽与字符编码是**开放集合**(未来可扩 `int4`、`fp8`、`fp16` 等),封闭的家族枚举必然漏项;多态**必须**靠 [[langtype签名类型表达式]] 的显式 `|` 枚举表达(如 `int8|int16|int32|int64`)。 +### 低精度浮点种类 + +面向张量的低精度浮点种类。**位布局各不相同**(fp8 更有两种 OCP 格式),故与字符编码同理,种类名**必须显式带出具体格式**,**禁**裸 `float8`/`fp8`: + +| 种类 | 位布局 | 元素字节 | +|------|--------|---------| +| `float16` | IEEE 754 binary16(1-5-10) | 2B | +| `bfloat16` | 截断 float32(1-8-7) | 2B | +| `float8/e4m3` | OCP FP8 E4M3 | 1B | +| `float8/e5m2` | OCP FP8 E5M2 | 1B | + +这些种类用于张量的**类型标注**:layout 认作合法基种类、可写进签名 langtype;runtime 只搬字节、按种类串相等匹配,**不实现**其算术——低精度运算由 deepx 系 runtime/后端兑现(fp8 的 per-tensor/block scale 亦不由 langtype 表达,属算子/张量元数据)。 + +**铁律——无家族简写**。`int`、`uint`、`float`、`num`、`char` 这类不含位宽/编码的简写**不是**合法种类,在任何类型标注中均**不得**出现。位宽与字符编码是**开放集合**(如 `int4` 等仍未纳入),封闭的家族枚举必然漏项;纳入新位宽须逐个**显式命名**(如上表低精度浮点),多态则靠 [[langtype签名类型表达式]] 的显式 `|` 枚举表达(如 `int8|int16|int32|int64`、`float16|bfloat16`)。 ### bool 与字符种类 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\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-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" index ac79e782..f91d3175 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/05-langtype\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-langtype\347\255\276\345\220\215\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217.kv" @@ -10,20 +10,20 @@ lib kvlang/spec/类型系统/langtype签名类型表达式 { | 型 | 出现位置 | 角色 | 表达力 | |----|---------|------|--------| -| **def langtype** | rwfunc / `def rwir` 签名行 `[0,x]` 槽(storetype=`def langtype`,见 [[storetype_def族与签名行]]) | **模式**:描述一个参数「可接受的一类值」 | 完整——并集 `A\|B`、变参 `...`、动态维 `[?]`、`any`、mapexpr(见 [[匹配语义与变参]]) | -| **value langtype** | 一个具体 XValue 的 head langtype(见 [[langtype语义类型]]) | **实例**:描述这个值**是什么** | 具体固定——单一种类 + 确定 `[dims]`,不含并集、变参、`?` | +| **def langtype** | rwfunc / `def rwir` 签名行 `[0,x]` 槽(storetype=`def langtype`,见 [[storetype_def族与签名行]]) | **模式**:描述一个参数「可接受的一类值」 | 完整——并集 `A\|B`、轴量词 `.`/`?`/`*`/`+`、`any`、mapexpr(见 [[匹配语义与变参]])。参数变参 `...` 是签名层 arity、**不在** langtype 内(见 [[函数]]) | +| **value langtype** | 一个具体 XValue 的 head langtype(见 [[langtype语义类型]]) | **实例**:描述这个值**是什么** | 具体固定——单一种类 + 确定 `[dims]`(每维具体整数),不含并集与任何轴量词 | def langtype 是「值域」,value langtype 是值域里「一个点」;类型检查即判定后者是否落进前者(匹配语义见 [[匹配语义与变参]])。 ## kindexpr(简写) -一段**具体的 langtype 串**——无论是某个 value 的 langtype,还是某条 def 签名里某参数的 langtype——统称 **kindexpr**。故 kindexpr 与 langtype 指同一文法产物:`langtype` 强调 head 的语义类型维,`kindexpr` 强调「一条具体的类型表达式串」。def langtype 是允许模式构造(`|`/`...`/`?`)的 kindexpr,value langtype 是不含模式构造的 kindexpr。下文与各卷凡书「kindexpr」即指一条具体 langtype 串。 +一段**具体的 langtype 串**——无论是某个 value 的 langtype,还是某条 def 签名里某参数的 langtype——统称 **kindexpr**。故 kindexpr 与 langtype 指同一文法产物:`langtype` 强调 head 的语义类型维,`kindexpr` 强调「一条具体的类型表达式串」。def langtype 是允许模式构造(并集 `|` 与轴量词 `.`/`?`/`*`/`+`)的 kindexpr,value langtype 是不含模式构造的 kindexpr。下文与各卷凡书「kindexpr」即指一条具体 langtype 串。 ## 两条 runtime-c ABI(供外部调用) runtime-c 对外开放两组 langtype 能力,一一对应两型(`kvlang_myrwir.h` 与正典 codec,见 [[cruntime与后端abi]]),供扩展运行时与嵌入方调用: -- **模式侧(def langtype)**:`LangtypeValid` 校验一条 def kindexpr 合法、`LangtypeMatch` 把一个值的 `(kind,ndim,dims)` 对一条 def kindexpr 作模式匹配(含变参判定)。用于签名校验与派发。 +- **模式侧(def langtype)**:`LangtypeValid` 校验一条 def kindexpr 合法、`LangtypeMatch` 把一个值的 `(kind,ndim,dims)` 对一条 def kindexpr 作**逐值无状态**模式匹配。用于签名校验与派发(参数变参判定不在此,靠主槽 `dynamic` 字节,见 [[函数]])。 - **具体侧(value langtype)**:把一条具体 kindexpr 解析/解码为 `(kind, ndim, dims, 元素字节宽)`——唯一事实源为正典 codec `kvspaceDecodeHead`(读 head 的 value langtype)与 layout 的 `kvlangLangtypeParse`(见 [[layout流水线接口]])。用于零拷贝定位、显示、round-trip。 两 ABI 同源同文法(都吃一条 kindexpr 串),差别只在角色:**模式匹配** vs **具体解析**。runtime-c **不得**为二者各造一份编解码——文法与 codec 只此一处。 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" index a5c3e69c..0fd877e1 100644 --- "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" @@ -7,12 +7,23 @@ lib kvlang/spec/类型系统/文法与合法性 { - **并集** `A|B`——以 `|` 拆分为若干 atom,表「A 或 B」。多态**必须**用显式并集表达。 - **atom** = 形状(shape)或 map 表达式(mapexpr)。 - **shape** = 可选维度 `dims` 后跟 `any` 或精确种类名。 -- **dims** = `[]`(无内容)或 `[dim,dim,…]`;`dim` 为整数或 `?`(动态维)。 +- **dims** = `[]` 或 `[elem,elem,…]`;每个 `elem` 是一个**轴量词**,逗号分隔(借鉴正则,一个 dims 就是一段「轴序列」的模式): + + | `elem` | 匹配的轴数 | 每轴长度 | 正则类比 | + |--------|-----------|---------|---------| + | 整数 `N` | 恰 1 | 恰 `N` | 具体字符 | + | `.` | 恰 1 | 任意 | `.` | + | `?` | 0 或 1 | 任意 | `.?` | + | `*` | 0+ | 任意 | `.*` | + | `+` | 1+ | 任意 | `.+` | + + 空 `[]` **等价** `[.]`(恰一维、任意长)——字符串标注 `[]char/<编码>` 即此形。一个 dims 内**至多一个**变元量词(`?`/`*`/`+`),其余须为定长元(整数或 `.`)——保证匹配 O(n) 确定、无回溯(见 [[匹配语义与变参]])。 - **mapexpr** = `key·value`,`value` 递归为完整类型表达式(故 map 可无限嵌套)。 - **key** = `[]char/<编码>`(字符串键)或 `[scalar,…]`(标量元组键);`scalar` 限 `bool` 与十个定宽数字种类。 - **any** = 通配,匹配任意种类;是**唯一**简写。 - **structref** = 以 `/` 起始的原型路径(如 `/lib/Point`),作为 atom 标注结构类型(见 [[容器]] 的 struct)。 -- **变参** `...`——见下。 + +> 轴量词 `.`/`?`/`*`/`+` 与正则一致,**只描述单个值的形状**。而**参数变参**(一个参数位吸收 0..N 个实参,旧写法末读参尾缀 `...`)是**签名**层的 arity 构造,**不属于类型表达式**,见 [[函数]];它落成函数主槽的 `dynamic` 字节,参数 langtype 子槽保持纯净(不含 `...`)。 **合法性校验**(layout 与 runtime 一致):每个 atom 的基须为 `known` 种类名或 `any`;键须为 `[]char/<编码>` 或全标量元组。非法示例及拒因: @@ -22,9 +33,10 @@ lib kvlang/spec/类型系统/文法与合法性 { | `int64*` | 后缀 `*` 位置非法;`*`/`@` 前缀**仅**可书写在源码类型标注最前,layout 剥离落 `ref` 字节,wire langtype 不含前缀 | | `char/utf8·int64` | map 键未加方括号 | | `[]int32·int64` | map 字符串键的编码非 `char/*` | -| `[?]·int64` | map 键含维度 | +| `[.]·int64` | map 键含维度 | | `[foo,int32]·int64` | 元组键含非标量元素 | -| 裸 `[]` / 裸 `[?]` | 无基种类 | +| 裸 `[]` / 裸 `[.]` | 无基种类 | +| `[*,+]int64`、`[?,*]int64` | 一个 dims 含多于一个变元量词(`?`/`*`/`+`) | 源码类型标注最前的 `*`/`@` 前缀(如 `*int64`、`@[256,256]uint8`)**合法**,layout 解析时剥离前缀落成 head `ref` 字节,再校验剩余 langtype;wire 层 langtype 串不含前缀(见 [[总体方案]])。 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" index 38e1e019..d4aeb948 100644 --- "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" @@ -2,16 +2,7 @@ lib kvlang/spec/类型系统/匹配语义与变参 { r####"# 匹配语义与变参 -本章匹配语义作用于 **def langtype**(模式):把一个值的 value langtype `(kind,ndim,dims)` 与签名里的 def langtype 判定是否命中(两型见 [[langtype签名类型表达式]])。 - -## 变参 `...` - -变参尾缀 `...` **仅**允许加在**末位读参**的类型表达式上(如 `A:any...`、`A:int64|float64...`),表「0..N 个同型实参」,用于 `print`/`println`/`min`/`max` 等 arity 开放的算子。 - -- 非末位参数带 `...`:装载期错误。 -- 写参(返回值)带 `...`:装载期错误。 - -匹配时,变参把所有尾随实参逐个按去掉 `...` 后的类型表达式判定。 +本章匹配语义作用于 **def langtype**(模式):把一个值的 value langtype `(kind,ndim,dims)` 与签名里的 def langtype 判定是否命中(两型见 [[langtype签名类型表达式]])。匹配**恒逐值、无状态**:只看单个值,不在参数间携带任何环境(跨操作数的维相等/输出推断不在类型层,归 op 的 shape function,见 [[函数调用与内建]])。 ## 匹配语义 @@ -22,19 +13,23 @@ lib kvlang/spec/类型系统/匹配语义与变参 { 3. atom 有 `[` 前缀(shape)→ 先按维度段做形状匹配,再以剩余基种类匹配 `k`。 4. atom 无 `[` 前缀(标量)→ 命中当且仅当 `n == 0` 且基种类等于 `k`(`any` 除外走精确字符串相等)。 -**形状匹配**(维度段 → `n`,`d`): +**形状匹配**(维度段的轴量词序列 → `n`,`d`,轴量词见 [[文法与合法性]])。一个 dims 至多一个变元量词(`?`/`*`/`+`),故切分唯一、无回溯:设定长元(整数或 `.`)共 `f` 个,令 `rem = n - f`: | 维度段 | 命中条件 | |--------|---------| -| 空(`[]`)或 `[?]` | 恰 `n == 1`,长度任意 | -| `[d0,d1,…]` | 逗号项数 `== n`,且逐维:`?` 跳过、否则 `d[i] == di` | +| 无变元(全定长元) | `n == f`;逐轴:整数元须 `d[i] == 该整数`,`.` 任意 | +| 含一个 `?` | `rem ∈ {0,1}`;`?` 吸收 `rem` 个轴,其余定长元按位(`?` 前后各就各位)逐轴判定 | +| 含一个 `*` | `rem ≥ 0`;`*` 吸收 `rem` 个轴,其余定长元按位逐轴判定 | +| 含一个 `+` | `rem ≥ 1`;`+` 吸收 `rem` 个轴,其余定长元按位逐轴判定 | -`[]` **等价** `[?]`——均为「恰一维、任意长」。字符串标注 `[]char/<编码>` 即一维字符序列。 - -> 规范注记:`[]` 恒为「恰一维」是 normative 规则。runtime 匹配器(`runtime/src/langtype.c`)已一致;layout 匹配器(`layout/src/langtype.rs`)当前放宽为「任意 rank ≥1」,为已裁决待修的实现偏差,须收敛至「恰一维」。 +空 `[]` **等价** `[.]`——「恰一维、任意长」。字符串标注 `[]char/<编码>` 即一维字符序列。例:`[.]`↔恰 1 维;`[.,.]`↔恰 2 维;`[*]`↔任意秩;`[*,.]`↔秩≥1、末维任意;`[512,*]`↔首维 512、秩≥1。 任一失配即 TypeError:装载期由 layout 报诊断,运行期写 vthread 错误并停机。 +## 参数变参(签名层,非类型) + +一个**参数位**吸收 0..N 个同型实参(`print`/`println`/`min`/`max` 等 arity 开放算子)是**签名**的 arity 属性,**不属于类型表达式**——源码在末读参尾缀 `...` 声明,由签名解析器剥离,落成函数主槽的 `dynamic` 字节,该参数 langtype 子槽保持纯净(见 [[函数]]、[[指令布局格式]])。派发时:主槽 `dynamic=1` → 末读参对**每个**尾随实参按其(纯净)langtype 逐个判定。`LangtypeValid`/`LangtypeMatch` 永不见 `...`。 + XValue head 三正交维编码见 [[总体方案]](本卷只界定 langtype 串的文法与匹配语义)。 ## 多态派发 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" index 71fbd894..d1714474 100644 --- "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" @@ -36,7 +36,7 @@ kvlang 的数组有**两种物理形态**:**compact**(稠密:全量元素 | `b:[256,256]float32 = [1...]` | 填充初始化:按声明维度分配,每元素填 `1`(填充值按元素种类编码,`1`→`float32 1.0`) | - 两者降级为 `array·fill(langtype[, v])`:`langtype` 即写类型(维度来源),`v` 为填充值(`[]` 省略即全零)。runtime 一条指令批量填充,不展开成 N 个字面量参数。 -- `[v...]` 只允许**单一**填充值,且写类型必须是定长数组(`[]T` 动态一维、`[?,N]T` 含未知维均报错)。 +- `[v...]` 只允许**单一**填充值,且写类型必须是定长数组(`[]T` 动态一维、`[.,N]T` 含任意长维均报错)。(此处 `[v...]` 是**值字面量**的填充展开,与类型表达式无关。) - 动态一维 `a:[]int32 = []` 仍是**空数组**(0 长度),不触发填充。 "#### -> /lib/kvlang/spec/类型系统/数组形态 } 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" index 4b46284e..387e5c73 100644 --- "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" @@ -7,7 +7,7 @@ lib kvlang/spec/类型系统/storetype_def族与签名行 { rwfunc / def rwir 的**签名(各参数类型)不写进 langtype 串**,也不塞进主槽 body。分两处存: 1. **主槽计数头**:目录节点主槽 `{fn}`(如 `/lib/pkg·funca`)的 body 只记 `[nr:u16 LE][nw:u16 LE][dynamic:u8]`——读参个数 `nr`、写参个数 `nw`、是否变参 `dynamic`(末位读参 `...`)。**不含任何参数 langtype 串**。 -2. **签名行 `[0,x]` 各槽**:每个参数的真实类型定义落在该目录的**签名行**坐标 `[0,x]`——`x<0` 为读参、`x>0` 为写参、`[0,0]` 为 opcode/签名行锚点(见 [[指令布局格式]] 的二元坐标 `[s0,s1]`:`s0=0` 即签名行)。每个 `[0,x]` 槽是一个 **`def langtype`** 类型的 xvalue,其 body 存该参数的完整 langtype 串(一个参数一槽)。 +2. **签名行 `[0,x]` 各槽**:每个参数的真实类型定义落在该目录的**签名行**坐标 `[0,x]`——`x<0` 为读参、`x>0` 为写参、`[0,0]` 为 opcode/签名行锚点(见 [[指令布局格式]] 的二元坐标 `[s0,s1]`:`s0=0` 即签名行)。每个 `[0,x]` 槽是一个 **`def langtype`** 类型的 xvalue,其 body 存该参数的完整 langtype 串(一个参数一槽),**纯净、不含参数变参 `...`**——变参是 arity,只落主槽 `dynamic` 字节(见 [[函数]])。 于是 rwfunc/def rwir 主槽的 langtype 就是裸种类名 `rwfunc` / `def rwir` 本身,**不再是「签名 langtype 串」**。签名分散在 `[0,x]` 签名行槽里,实现指令行(若有)在 `[s0>0,·]`。 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" index 5cabe927..70197697 100644 --- "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" @@ -26,7 +26,13 @@ error: func f: param "x" has no type annotation — every parameter must declare error: func f: return value "r" has no type annotation ``` -末读参的 type_expr **可**加 `...` 声明变参(0..N 个同型实参)。变参**必须**是最后一个读参;写参**不得**变参。 +## 参数变参(arity,非类型) + +末读参**可**在参数上尾缀 `...` 声明变参(吸收 0..N 个同型实参,如 `A:int64|float64...`)。`...` 是**签名层 arity 构造,不是 type_expr 的一部分**(轴量词见 [[langtype签名类型表达式]]):layout 解析时把它从参数上**剥离**,给该函数主槽置 `dynamic=1`(见 [[指令布局格式]]),而参数的 langtype 子槽 `[0,x]` 存**纯净** langtype(不含 `...`)。故 `LangtypeValid`/`LangtypeMatch` 永不见 `...`。 + +- 变参**必须**是最后一个读参;非末位读参带 `...`:装载期错误。 +- 写参(返回值)**不得**变参:装载期错误。 +- 派发时,主槽 `dynamic=1` 则末读参对每个尾随实参按其纯净 langtype 逐个判定(见 [[匹配语义与变参]])。 ## 写参初值 None diff --git "a/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/01-\347\274\226\350\257\221\345\231\250\345\256\232\344\275\215.kv" "b/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/01-\347\274\226\350\257\221\345\231\250\345\256\232\344\275\215.kv" index 4974c3eb..2afb3e09 100644 --- "a/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/01-\347\274\226\350\257\221\345\231\250\345\256\232\344\275\215.kv" +++ "b/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/01-\347\274\226\350\257\221\345\231\250\345\256\232\344\275\215.kv" @@ -29,6 +29,19 @@ kvlang 的三阶段:**layout(o0)→ 编译器(优化)→ runtime(纯 2. **复合算子分解/融合**——把由基础算子组合的复合 rwfunc(如 `softmax` = exp/sum/div)融合成单条自解释 rwir。 3. **算子版本化**——融合产物作为版本递增并存,runtime 调用时优先高版本,见 [[算子版本化与高版本优先]]。 +## 融合的层级:编排器 vs kernel 编译器 + +融合不是一步,分**两层**,职责必须切开: + +- **kvlang 编译器(编排器)**:读整棵 `/lib`,做后端绑定、复合算子分解、**识别可融合的相邻 rwir 组**、编排、并把结果**回写**——把融合产物注册为 `/lib/` def rwir + myrwircaps、改写调用点 opcode、登记版本。回写是编排器的职责。 +- **kernel 编译器(叶子 codegen)**:把「一组 rwir + shape + dtype」codegen 成**单个硬件 kernel**(如 op-gpu / TileLang,GPU 上多个 `deepx·{rwir/rwfunc}` 融成一次 kernel 调用 = 一条 rwir)。它是**纯函数式服务**:吃 `op序列 + shape + dtype`,吐 `.so + meta`,**不回写 kvspace、不感知调度/分布式**,便于独立测试与对拍。 + +对照 PyTorch:编排器 ≈ Inductor 的 lowering + Scheduler(识别融合组);kernel 编译器 ≈ Inductor 的 codegen 后端(Triton / C++)。 + +**融合匹配须是子序列/滑窗匹配**(在 rwir 线性序列上),不可退化为整段 opcode 元组精确匹配(否则每种顺序/shape 变体都要新增一条规则、不可扩展)。切分边界的硬启发式:**重算子(GEMM/conv 一类)计数**——一个融合组内重算子超阈值(典型 ≥2 GEMM)即拆成多 kernel。融合规则宜用「规则即数据」的表 + 四态标签(可融单核 / 缺模板 / 须拆多核 / 不可融)。 + +**GPU 融合产物走 AOT→`.so`→`dlopen`**(调用约定 `fn(ptr..., stream)`),运行时侧零 Python 依赖——这条约束决定必须 AOT 而非进程内 JIT,须作为独立可验收里程碑,不可停在 JIT 原型。 + ## 非规范对照(PyTorch) 本编译器的「编译期读 `/lib` 静态绑定后端」≈ TorchInductor 的 lowering(编译期查表把抽象 ATen op 静态映射到具体 kernel),**不是** `c10::Dispatcher` 的运行期按 device 位图派发。后端绑定表 ≈ Inductor lowering 表;复合算子分解 ≈ `torch/_decomp`;融合 ≈ Inductor Scheduler 合并算子成单 kernel;版本择优 ≈ autotune 缓存最优(但 kvlang 把「谁更优」显式化为可声明、可审计的静态版本号)。 diff --git "a/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/03-\347\256\227\345\255\220\347\211\210\346\234\254\345\214\226\344\270\216\351\253\230\347\211\210\346\234\254\344\274\230\345\205\210.kv" "b/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/03-\347\256\227\345\255\220\347\211\210\346\234\254\345\214\226\344\270\216\351\253\230\347\211\210\346\234\254\344\274\230\345\205\210.kv" index 660a0531..c0d7875c 100644 --- "a/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/03-\347\256\227\345\255\220\347\211\210\346\234\254\345\214\226\344\270\216\351\253\230\347\211\210\346\234\254\344\274\230\345\205\210.kv" +++ "b/stdlib/kvlang/spec/06-\347\274\226\350\257\221\345\231\250\350\257\255\344\271\211/03-\347\256\227\345\255\220\347\211\210\346\234\254\345\214\226\344\270\216\351\253\230\347\211\210\346\234\254\344\274\230\345\205\210.kv" @@ -30,10 +30,18 @@ lib kvlang/spec/编译器语义/算子版本化与高版本优先 { - **后端绑定**改**调用点 opcode**(前端算子 → 后端算子,见 [[前端算子与后端算子]])。 - **版本化**改**被调函数的实现选择**(基 → 高版本)。 -二者正交,可叠加:`deepx·softmax` 先绑定为某后端组合,其融合版再登记为 `softmax·1`。 +二者正交,可叠加:`deepx·softmax` 先绑定为某后端组合,其融合版再登记为 `softmax.1`。 + +## 版本号与 shape 特化正交 + +**语义版本号 `.`(哪个实现更优)** 与 **shape 特化(为哪个具体 shape/dtype 编译的 kernel)** 是两个维度,不可混同: + +- runtime 的高版本优先解析**只认版本号** `.`——它是可声明、可审计的语义择优。 +- 具体后端 / 融合 kernel 的 shape 特化多产物(同一 op 序列 × 不同 shape → 不同 kernel)由计算引擎 / kernel 编译器按 **shape-hash 缓存**管理,不进 runtime 的版本解析。 +- 完整的实现选择键是复合的:`(基 op, 版本 .N, shape-hash, dtype, author/backend)`。**别把 shape 特化冒充成版本号**。 ## 锚例(待补) -- `tutorial/16-compiler/op_version.kv`——编译前调 `softmax` 走基版;编译生成 `softmax·1` 后,同一 `softmax(...)` 调用走 `softmax·1`,输出与基版逐字节一致。 +- `tutorial/16-compiler/op_version.kv`——编译前调 `softmax` 走基版;编译生成 `softmax.1` 后,同一 `softmax(...)` 调用走 `softmax.1`,输出与基版逐字节一致。 "#### -> /lib/kvlang/spec/编译器语义/算子版本化与高版本优先 } diff --git "a/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" "b/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" index 24f61b50..62d10020 100644 --- "a/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" +++ "b/stdlib/kvlang/spec/\351\231\204\345\275\225/03-\347\261\273\345\236\213\350\241\250\350\276\276\345\274\217\346\226\207\346\263\225.kv" @@ -18,9 +18,14 @@ scalar = "bool" | "int8" | "int16" | "int32" | "int64" | "uint8" | "uint16" | "uint32" | "uint64" | "float32" | "float64" -dims = "[]" (* 恰一维,长度任意,等价 [?];Rust layout 放宽为 rank≥1,已裁决待修 *) - | "[" dim { "," dim } "]" (* 逗号分割的维;rank = 项数 *) -dim = integer | "?" (* 精确大小或动态维 *) +dims = "[]" (* 等价 [.]:恰一维、长度任意 *) + | "[" elem { "," elem } "]" (* 逗号分割的轴量词序列;至多一个变元量词 ?/*/+ *) +elem = integer (* 恰 1 轴、长度=integer *) + | "." (* 恰 1 轴、任意长(≈正则 . ) *) + | "?" (* 0 或 1 轴(≈正则 .? ) *) + | "*" (* 0+ 轴(≈正则 .* ) *) + | "+" (* 1+ 轴(≈正则 .+ ) *) + (* 参数变参 "..." 不在类型表达式内——它是签名层 arity 构造,见 [[函数]] / [[程序结构]] *) any = "any" (* 通配,匹配任意 kind *) kind = "bool" | "int8" | "int16" | "int32" | "int64"