Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,21 @@
# make layout Rust layout(layout_file)
# make json Go json 扩展(json-rwirext 可执行文件)
# make oldhero Go 旧 runtime(kvlang,兼容保留)
# make test 全量 tutorial 回归(C runtime + Rust term,递归跑所有子目录 kv)
# make all 全部
# make clean 清理 bin/ 与各构建目录

BIN := bin
KVSPACE_LIB ?= kvspace-c

.PHONY: all runtime term run layout json oldhero clean
.PHONY: all runtime term run layout json oldhero test clean

all: runtime term run layout json

test: KVSPACE_LIB := kvspace_durable
test: runtime term run layout
python3 tutorial/test.py --no-build

runtime:
cmake -S runtime -B build/runtime -DCMAKE_BUILD_TYPE=Release -DKVSPACE_LIB=$(KVSPACE_LIB)
cmake --build build/runtime --target kvlang_runtime -j
Expand Down
1 change: 1 addition & 0 deletions layout/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod ast;
pub mod scanner;
pub mod parser;
pub mod builtin;
pub mod type_expr;
pub mod lower;
pub mod code;

Expand Down
54 changes: 4 additions & 50 deletions layout/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ impl Parser {

fn check_param_types(&mut self, sig: &FuncSig) {
for param in &sig.params {
if !valid_kindexp(&param.ty) {
if !crate::type_expr::valid_type_expr(&param.ty) {
self.errors.push(Diagnostic {
pos: Pos { line: 0, col: 0 },
message: format!("func {}: param {:?}: {} (got {:?})", sig.name, param.name, type_error(&param.ty), param.ty),
Expand All @@ -413,7 +413,7 @@ impl Parser {
}
}
for ret in &sig.returns {
if !valid_kindexp(&ret.ty) {
if !crate::type_expr::valid_type_expr(&ret.ty) {
self.errors.push(Diagnostic {
pos: Pos { line: 0, col: 0 },
message: format!("func {}: return value {:?}: {} (got {:?})", sig.name, ret.name, type_error(&ret.ty), ret.ty),
Expand Down Expand Up @@ -1327,58 +1327,12 @@ fn attach_comments(st: Stmt, comments: Vec<String>) -> Stmt {
st
}

fn valid_kinds() -> &'static [&'static str] {
&[
"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float32", "float64",
"bool", "char/utf32", "char/utf8", "char/ascii", "any",
]
}

fn valid_kindexp(t: &str) -> bool {
let mut t = t;
while !t.is_empty() {
match t.as_bytes()[0] {
b'*' | b'@' => t = &t[1..],
b'[' => {
let end = match t.find(']') {
Some(e) => e,
None => return false,
};
if !t[1..end].is_empty() && !valid_dims(&t[1..end]) {
return false;
}
t = &t[end + 1..];
}
_ => return valid_kinds().contains(&t),
}
}
false
}

fn valid_dims(s: &str) -> bool {
for d in s.split(',') {
if d.is_empty() {
return false;
}
if !d.bytes().all(|b| b.is_ascii_digit()) {
return false;
}
}
true
}

fn is_array_kindexp(t: &str) -> bool {
t.contains('[')
}

fn type_error(kind: &str) -> String {
if kind == "int" || kind == "float" {
return "ambiguous type — use int64 or float64 instead".to_string();
}
if kind == "string" || kind == "bytes" {
return "unknown type — use char/utf32 instead".to_string();
}
"unknown type — valid: int8/16/32/64, uint8/16/32/64, float32/64, bool, char/utf32, any, []T, [N]T, *T".to_string()
fn type_error(_kind: &str) -> String {
"unknown type — valid: int8/16/32/64, uint8/16/32/64, float32/64, bool, char/utf32, dict, index, char, any, []T, [2,3]T, [?,N]T, A|B".to_string()
}

fn walk_read_only(
Expand Down
170 changes: 170 additions & 0 deletions layout/src/type_expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
//! 签名类型表达式(runtime篇-07,修订:无家族简写):语法校验 + 值匹配。
//!
//! type = atom ("|" atom)*
//! atom = [dims] ( any | kind )
//! dims = "[]" | "[" dim ("," dim)* "]"
//! dim = integer | "?"
//! any = "any" # 通配,匹配任意 kind
//! kind = 精确 kind 串 # 见 [`known_kind`]
//!
//! 铁律:不提供 int/uint/float/num 数值家族(位宽开放,int4/fp8/fp16…),
//! 也不提供 char 编码简写(编码须写明确,如 char/utf8、char/utf32)。多态靠显式 "|" 枚举。

/// 精确 kind 集合(对齐 runtime kind 常量,不含 None)。
fn known_kind(k: &str) -> bool {
matches!(
k,
"bool" | "int8" | "int16" | "int32" | "int64" | "uint8" | "uint16" | "uint32" | "uint64"
| "float32" | "float64" | "char/utf32" | "char/utf8" | "char/ascii" | "dict" | "index"
| "extindex" | "rwir" | "rwfunc" | "scope" | "time" | "duration"
)
}

fn valid_base(s: &str) -> bool {
if s.is_empty() {
return false;
}
s == "any" || known_kind(s)
}

fn valid_dim(s: &str) -> bool {
if s.is_empty() {
return false;
}
s == "?" || s.bytes().all(|b| b.is_ascii_digit())
}

fn valid_dims(s: &str) -> bool {
s.is_empty() || s.split(',').all(valid_dim)
}

fn valid_atom(s: &str) -> bool {
if s.is_empty() {
return false;
}
if let Some(rest) = s.strip_prefix('[') {
let end = match rest.find(']') {
Some(e) => e,
None => return false,
};
if !valid_dims(&rest[..end]) {
return false;
}
let base = &rest[end + 1..];
return !base.is_empty() && valid_base(base);
}
valid_base(s)
}

/// 类型表达式语法校验(装载期)。
pub fn valid_type_expr(expr: &str) -> bool {
!expr.is_empty() && expr.split('|').all(valid_atom)
}

fn base_match(s: &str, kind: &str) -> bool {
match s {
"any" => true,
_ => s == kind,
}
}

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;
}
if p.parse::<i32>().ok() != Some(dims[i]) {
return false;
}
}
true
}

/// ndim = -1 表示「已消费 dims,不再判 ndim」(递归哨兵)。
fn match_atom(s: &str, kind: &str, ndim: i32, dims: &[i32]) -> bool {
if let Some(rest) = s.strip_prefix('[') {
let end = match rest.find(']') {
Some(e) => e,
None => return false,
};
if !match_shape(&rest[..end], ndim, dims) {
return false;
}
return match_atom(&rest[end + 1..], kind, -1, &[]);
}
if ndim >= 0 && ndim != 0 {
return false;
}
base_match(s, kind)
}

/// 值(kind/ndim/dims)是否匹配类型表达式:任一 atom 命中即 true。
pub fn match_type(expr: &str, kind: &str, ndim: i32, dims: &[i32]) -> bool {
!expr.is_empty() && expr.split('|').any(|atom| match_atom(atom, kind, ndim, dims))
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn valid() {
for e in [
"int64", "uint8", "float32", "bool", "any",
"char/utf8", "char/utf32", "char/ascii", "dict", "index",
"[]float32", "[2]float32", "[2,3]float32", "[2,3,4]float64",
"[?,768]float32", "[?,?]int8",
"int64|float64", "[2,3]float32|float32", "[]float32|[]float64",
"bool|char/utf8", "index|dict",
] {
assert!(valid_type_expr(e), "{e} should be valid");
}
}

#[test]
fn invalid() {
for e in [
"", "int|", "|int", "int||float64", "|", "[]", "[2]", "[?]",
"[2", "2]", "[2,]float32", "[,2]float32", "[2 3]float32",
"*int64", "@int64", "int64*", "float64|", "int ", "float32,float64",
"int", "uint", "float", "num", "char", "int4", "fp8", "fp16", "string", "charbyte",
] {
assert!(!valid_type_expr(e), "{e} should be invalid");
}
}

#[test]
fn matching() {
let cases = [
("int64", "int64", 0, &[][..], true),
("int64", "float64", 0, &[], false),
("any", "dict", 0, &[], true),
("any", "int4", 0, &[], true),
("char/utf8", "char/utf8", 0, &[], true),
("char/utf8", "char/utf32", 0, &[], false),
("int64|float64", "float64", 0, &[], true),
("int64|float64", "bool", 0, &[], false),
("[]float32", "float32", 1, &[5], true),
("[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),
("[2,3]float32|float32", "float32", 0, &[], true),
("[2,3]float32|float32", "float64", 0, &[], false),
("[]float32|[]float64", "float64", 1, &[10], true),
("bool|char/utf8", "char/utf8", 0, &[], true),
("index|dict", "index", 0, &[], true),
];
for (expr, kind, ndim, dims, want) in cases {
let got = match_type(expr, kind, ndim, dims);
assert_eq!(got, want, "match_type({expr}, {kind}, ndim={ndim}, dims={dims:?})");
}
}
}
23 changes: 23 additions & 0 deletions layout/tests/type_expr_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use kvlang_layout::parser::{self};

#[test]
fn parse_type_expression_signature() {
let src = "rwfunc f(A:int64|float64, B:[2,3]float32, C:[?,768]float32) -> (D:[]float32) {\n A -> D\n}\n";
let (file, diags) = parser::parse_code(src).unwrap();
let msgs: Vec<String> = diags.iter().map(|d| d.message.clone()).collect();
assert!(!parser::has_errors(&diags), "unexpected errors: {:?}", msgs);

let sig = &file.funcs[0].sig;
assert_eq!(sig.name, "f");
let tys: Vec<&str> = sig.params.iter().map(|p| p.ty.as_str()).collect();
assert_eq!(tys, vec!["int64|float64", "[2,3]float32", "[?,768]float32"]);
let rets: Vec<&str> = sig.returns.iter().map(|p| p.ty.as_str()).collect();
assert_eq!(rets, vec!["[]float32"]);
}

#[test]
fn reject_malformed_type_expression() {
let src = "rwfunc f(A:[2,3) -> () {\n}\n";
let (_, diags) = parser::parse_code(src).unwrap();
assert!(parser::has_errors(&diags), "expected errors for malformed type");
}
61 changes: 4 additions & 57 deletions oldhero/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"oldhero/ast"
"oldhero/keytree"
"oldhero/rwir"
"oldhero/symbol"
)

Expand Down Expand Up @@ -275,54 +276,6 @@ func (p *parser) parseFunc() ast.Func {
return fn
}

// validKinds kvlang 合法基础类型名(权威来源,与 kvspace.XValue.Kind() 对齐)。
// num / any 是类型类(多态):num = int|float 联合,any = 任意类型;非真实落盘 kind,仅签名声明。
var validKinds = map[string]bool{
"int8": true, "int16": true, "int32": true, "int64": true,
"uint8": true, "uint16": true, "uint32": true, "uint64": true,
"float32": true, "float64": true,
"bool": true, "char/utf32": true, "char/utf8": true, "char/ascii": true,
"any": true,
}

// validKindexp 校验类型表达式(kindexp):前缀修饰符序列 + 基础 kind。
// 文法:kindexp ::= kind | '*' kindexp | '@' kindexp | '[' ']' kindexp | '[' dims ']' kindexp
func validKindexp(t string) bool {
for t != "" {
switch t[0] {
case '*', '@':
t = t[1:]
case '[':
end := strings.IndexByte(t, ']')
if end < 0 {
return false
}
if inner := t[1:end]; inner != "" && !validDims(inner) {
return false
}
t = t[end+1:]
default:
return validKinds[t]
}
}
return false
}

// validDims 校验维度列表(逗号分隔的非负整数)。
func validDims(s string) bool {
for _, d := range strings.Split(s, ",") {
if d == "" {
return false
}
for i := 0; i < len(d); i++ {
if d[i] < '0' || d[i] > '9' {
return false
}
}
}
return true
}

// isArrayKindexp 判断 kindexp 是否含数组修饰符([] [N])。
func isArrayKindexp(t string) bool {
return strings.Contains(t, "[")
Expand All @@ -331,23 +284,17 @@ func isArrayKindexp(t string) bool {
// checkParamTypes 确保所有参数和返回值都有显式类型标注且类型名合法。
func (p *parser) checkParamTypes(sig *ast.FuncSig) {
typeError := func(kind string) string {
if kind == "int" || kind == "float" {
return "ambiguous type — use int64 or float64 instead"
}
if kind == "string" || kind == "bytes" {
return "unknown type — use char/utf32 instead"
}
return "unknown type — valid: int8/16/32/64, uint8/16/32/64, float32/64, bool, char/utf32, any, []T, [N]T, *T"
return "unknown type — valid: int8/16/32/64, uint8/16/32/64, float32/64, bool, char/utf32, dict, index, char, any, []T, [2,3]T, [?,N]T, A|B"
}
for _, param := range sig.Params {
if !validKindexp(param.Type) {
if !rwir.ValidTypeExpr(param.Type) {
p.errors = append(p.errors, Diagnostic{Message: fmt.Sprintf(
"func %s: param %q: %s (got %q)",
sig.Name, param.Name, typeError(param.Type), param.Type)})
}
}
for _, ret := range sig.Returns {
if !validKindexp(ret.Type) {
if !rwir.ValidTypeExpr(ret.Type) {
p.errors = append(p.errors, Diagnostic{Message: fmt.Sprintf(
"func %s: return value %q: %s (got %q)",
sig.Name, ret.Name, typeError(ret.Type), ret.Type)})
Expand Down
Loading
Loading