From a2e05907c1a162bc6acec6d4d303c17ab689443f Mon Sep 17 00:00:00 2001 From: "peng.li24" <734991033@qq.com> Date: Thu, 20 Aug 2026 01:20:25 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20dict=20kind=20=E6=8B=86=E5=88=86?= =?UTF-8?q?=E4=B8=BA=20obj=20+=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - layout/kvkind.rs、type_expr.rs、parser.rs、ast.rs:KIND_DICT → KIND_OBJ + KIND_MAP。 - runtime/builtin.c、type_expr.c、xvalue.c 同步。 - tutorial 05-dict 等用例同步。 Co-Authored-By: Claude --- layout/src/ast.rs | 4 +- layout/src/builtin.rs | 4 +- layout/src/kvkind.rs | 3 +- layout/src/lower.rs | 178 +++++++---- layout/src/parser.rs | 29 +- layout/src/type_expr.rs | 10 +- runtime/src/builtin.c | 299 +++++------------- runtime/src/runtime_internal.h | 3 +- runtime/src/type_expr.c | 2 +- runtime/src/xvalue.c | 4 +- tutorial/01-basics/kv_tree.kv | 4 +- tutorial/05-dict/kv_has_string_key.kv | 2 +- tutorial/05-dict/literal.kv | 2 +- tutorial/08-leetcode/001_two-sum-hash.kv | 6 +- tutorial/08-leetcode/003_longest_substring.kv | 4 +- tutorial/08-leetcode/020_valid_parentheses.kv | 6 +- tutorial/08-leetcode/038_count_and_say.kv | 10 +- tutorial/08-leetcode/056_merge_intervals.kv | 18 +- tutorial/08-leetcode/062_unique_paths.kv | 4 +- tutorial/08-leetcode/094_inorder_traversal.kv | 2 +- tutorial/08-leetcode/118_pascal_triangle.kv | 10 +- tutorial/08-leetcode/217_contains_dup_hash.kv | 8 +- tutorial/08-leetcode/219_contains_dup_ii.kv | 4 +- tutorial/08-leetcode/234_palindrome_list.kv | 2 +- tutorial/08-leetcode/242_valid_anagram.kv | 4 +- .../error_cases/array_param/mutate_correct.kv | 8 +- .../error_cases/array_param/mutate_literal.kv | 2 +- tutorial/error_cases/index_error/at_oob.kv | 2 +- .../index_error/typed_array_string_key.kv | 2 +- .../error_cases/key_error/kv_at_missing.kv | 2 +- 30 files changed, 277 insertions(+), 361 deletions(-) diff --git a/layout/src/ast.rs b/layout/src/ast.rs index cd767ed2..ec5bc174 100644 --- a/layout/src/ast.rs +++ b/layout/src/ast.rs @@ -392,8 +392,8 @@ impl fmt::Display for Instruction { } return write!(f, "{s}"); } - // dict("k1", v1, ...) → { k1=v1; k2=v2 } - if e.op == "dict" { + // obj("k1", v1, ...) → { k1=v1; k2=v2 } + if e.op == "obj" { let mut pairs = Vec::new(); let mut j = 0; while j + 1 < e.args.len() { diff --git a/layout/src/builtin.rs b/layout/src/builtin.rs index 1249d84c..5bcf00ef 100644 --- a/layout/src/builtin.rs +++ b/layout/src/builtin.rs @@ -156,7 +156,7 @@ pub fn try_parse_number(s: &str) -> Option> { fn native_rwir_set() -> &'static [&'static str] { &[ // 单字 builtin - "array", "at", "set", "has", "dict", "debugger", + "array", "obj", "map", "debugger", // cast kind "bool", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float32", "float64", "char/utf8", "char/ascii", "char/utf32", @@ -168,7 +168,7 @@ fn native_rwir_set() -> &'static [&'static str] { "sqrt", "√", "neg", "abs", "sign", "max", "min", "pow", "exp", "log", // kv.* 树操作 "kv.get", "kv.set", "kv.del", "kv.deltree", "kv.list", "kv.mkindex", - "kv.extindex", "kv.rmindexext", "kv.watch", "kv.has", "kv.at", + "kv.extindex", "kv.rmindexext", "kv.watch", // xv.* 形状内省与多维元素访问 "xv.numel", "xv.dim", "xv.shape", "xv.at", "xv.set", ] diff --git a/layout/src/kvkind.rs b/layout/src/kvkind.rs index 6e9aae5f..65b4aa47 100644 --- a/layout/src/kvkind.rs +++ b/layout/src/kvkind.rs @@ -14,7 +14,8 @@ pub const KIND_CHAR_ASCII: &str = "char/ascii"; pub const KIND_BOOL: &str = "bool"; pub const KIND_INT64: &str = "int64"; pub const KIND_FLOAT64: &str = "float64"; -pub const KIND_DICT: &str = "dict"; +pub const KIND_OBJ: &str = "obj"; +pub const KIND_MAP: &str = "map"; pub const KIND_INDEX: &str = "index"; pub const KIND_EXT_INDEX: &str = "extindex"; diff --git a/layout/src/lower.rs b/layout/src/lower.rs index 39c845cb..1eb81c9e 100644 --- a/layout/src/lower.rs +++ b/layout/src/lower.rs @@ -29,12 +29,13 @@ impl LabelGen { /// 将函数体中 if/while 控制流降级为 ScopeStmt + br/goto,展开复合表达式。 pub fn lower_func(fn_: &Func) -> Func { + let tm = infer_types(fn_); let mut lg = LabelGen { n: 0 }; - let body = lower_body(&fn_.body, &mut lg, None); + let body = lower_body(&fn_.body, &mut lg, None, &tm); Func { comments: Vec::new(), sig: fn_.sig.clone(), body, pkg: String::new() } } -fn lower_body(stmts: &[Stmt], lg: &mut LabelGen, lc: Option<&LoopCtx>) -> Vec { +fn lower_body(stmts: &[Stmt], lg: &mut LabelGen, lc: Option<&LoopCtx>, tm: &HashMap) -> Vec { if stmts.is_empty() { return Vec::new(); } @@ -45,7 +46,7 @@ fn lower_body(stmts: &[Stmt], lg: &mut LabelGen, lc: Option<&LoopCtx>) -> Vec) -> Vec { - let cont = lower_body(&stmts[i + 1..], lg, lc); - return lower_if_with_cont(&preamble, s, cont, lg, lc); + let cont = lower_body(&stmts[i + 1..], lg, lc, tm); + return lower_if_with_cont(&preamble, s, cont, lg, lc, tm); } Stmt::While(s) => { - let cont = lower_body(&stmts[i + 1..], lg, lc); - return lower_while_with_cont(&preamble, s, cont, lg, lc); + let cont = lower_body(&stmts[i + 1..], lg, lc, tm); + return lower_while_with_cont(&preamble, s, cont, lg, lc, tm); } Stmt::Scope(s) => { let mut s = s.clone(); - s.body = lower_body(&s.body, lg, lc); + s.body = lower_body(&s.body, lg, lc, tm); if !preamble_ends_with_terminator(&preamble) { preamble.push(goto_label(&s.label)); } let mut out = preamble; out.push(Stmt::Scope(s)); - out.extend(lower_body(&stmts[i + 1..], lg, lc)); + out.extend(lower_body(&stmts[i + 1..], lg, lc, tm)); return out; } Stmt::Break(_) => { @@ -93,8 +94,8 @@ fn lower_body(stmts: &[Stmt], lg: &mut LabelGen, lc: Option<&LoopCtx>) -> Vec { - let cont = lower_body(&stmts[i + 1..], lg, lc); - return lower_for_with_cont(&preamble, s, cont, lg, lc); + let cont = lower_body(&stmts[i + 1..], lg, lc, tm); + return lower_for_with_cont(&preamble, s, cont, lg, lc, tm); } } } @@ -107,6 +108,7 @@ fn lower_if_with_cont( cont: Vec, lg: &mut LabelGen, lc: Option<&LoopCtx>, + tm: &HashMap, ) -> Vec { let (cond_eval, cond_slot) = eval_cond(s.cond.as_ref(), lg); let if_label = lg.next("if"); @@ -117,8 +119,8 @@ fn lower_if_with_cont( let mut cond_body = cond_eval; cond_body.push(br_inst(&cond_slot, &then_label, &else_label)); - let then_ = inject_goto(lower_body(&s.then_, lg, lc), &merge_label); - let else_ = inject_goto(lower_body(&s.else_, lg, lc), &merge_label); + let then_ = inject_goto(lower_body(&s.then_, lg, lc, tm), &merge_label); + let else_ = inject_goto(lower_body(&s.else_, lg, lc, tm), &merge_label); let (then_insts, mut then_blocks) = split_insts_and_blocks(then_); let (else_insts, mut else_blocks) = split_insts_and_blocks(else_); let (cont_insts, cont_blocks) = split_insts_and_blocks(cont); @@ -144,6 +146,7 @@ fn lower_while_with_cont( cont: Vec, lg: &mut LabelGen, _lc: Option<&LoopCtx>, + tm: &HashMap, ) -> Vec { let (cond_eval, cond_slot) = eval_cond(s.cond.as_ref(), lg); let cond_label = lg.next("while"); @@ -154,7 +157,7 @@ fn lower_while_with_cont( cond_body.push(br_inst(&cond_slot, &body_label, &exit_label)); let body_lc = LoopCtx { break_label: exit_label.clone(), continue_label: cond_label.clone() }; - let body_ = inject_goto(lower_body(&s.body, lg, Some(&body_lc)), &cond_label); + let body_ = inject_goto(lower_body(&s.body, lg, Some(&body_lc), tm), &cond_label); let (body_insts, mut body_blocks) = split_insts_and_blocks(body_); inject_goto_blocks(&mut body_blocks, &cond_label); let (cont_insts, cont_blocks) = split_insts_and_blocks(cont); @@ -175,6 +178,7 @@ fn lower_for_with_cont( cont: Vec, lg: &mut LabelGen, _lc: Option<&LoopCtx>, + tm: &HashMap, ) -> Vec { let init_label = lg.next("for_init"); let cond_label = lg.next("for_cond"); @@ -183,12 +187,17 @@ fn lower_for_with_cont( let idx_slot = lg.tmp(); let cond_slot = lg.tmp(); + let len_slot = lg.tmp(); + let keys_slot = lg.tmp(); + let key_slot = lg.tmp(); + let is_map_obj = s.iter.op == "map" || s.iter.op == "obj" + || (s.iter.is_leaf() && matches!(tm.get(&s.iter.val), Some(t) if t == "obj" || t == "map")); // 迭代源:裸标识符直接原地遍历;表达式(如数组字面量)先物化到临时槽。 let mut init_body = Vec::new(); let iter_slot = if s.iter.is_leaf() { s.iter.val.clone() - } else if s.iter.op == "sparsearray" { + } else if s.iter.op == "map" { let slot = lg.tmp(); init_body.extend(expand_sparse(&slot, &s.iter, lg)); slot @@ -205,6 +214,33 @@ fn lower_for_with_cont( slot }; init_body.push(make_copy_inst("-1", &idx_slot)); + if is_map_obj { + init_body.push(Stmt::Instruction(Instruction { + comments: Vec::new(), + expr: Some(ast::call("kv.list", vec![ast::leaf(&iter_slot)])), + writes: vec![keys_slot.clone()], + write_types: Vec::new(), + arrow_left: false, + eq: false, + })); + init_body.push(Stmt::Instruction(Instruction { + comments: Vec::new(), + expr: Some(ast::call("xv.numel", vec![ast::leaf(&keys_slot)])), + writes: vec![len_slot.clone()], + write_types: Vec::new(), + arrow_left: false, + eq: false, + })); + } else { + init_body.push(Stmt::Instruction(Instruction { + comments: Vec::new(), + expr: Some(ast::call("xv.numel", vec![ast::leaf(&iter_slot)])), + writes: vec![len_slot.clone()], + write_types: Vec::new(), + arrow_left: false, + eq: false, + })); + } init_body.push(goto_label(&cond_label)); let add_inst = Instruction { @@ -215,9 +251,9 @@ fn lower_for_with_cont( arrow_left: false, eq: false, }; - let kv_has_inst = Instruction { + let lt_inst = Instruction { comments: Vec::new(), - expr: Some(ast::call("kv.has", vec![ast::leaf(&iter_slot), ast::leaf(&idx_slot)])), + expr: Some(ast::call("<", vec![ast::leaf(&idx_slot), ast::leaf(&len_slot)])), writes: vec![cond_slot.clone()], write_types: Vec::new(), arrow_left: false, @@ -225,21 +261,40 @@ fn lower_for_with_cont( }; let cond_body = vec![ Stmt::Instruction(add_inst), - Stmt::Instruction(kv_has_inst), + Stmt::Instruction(lt_inst), br_inst(&cond_slot, &body_label, &exit_label), ]; - let kv_at_inst = Instruction { - comments: Vec::new(), - expr: Some(ast::call("kv.at", vec![ast::leaf(&iter_slot), ast::leaf(&idx_slot)])), - writes: vec![s.var.clone()], - write_types: Vec::new(), - arrow_left: false, - eq: false, - }; let body_lc = LoopCtx { break_label: exit_label.clone(), continue_label: cond_label.clone() }; - let body_inner = lower_body(&s.body, lg, Some(&body_lc)); - let mut body_insts = vec![Stmt::Instruction(kv_at_inst)]; + let body_inner = lower_body(&s.body, lg, Some(&body_lc), tm); + let mut body_insts = Vec::new(); + if is_map_obj { + body_insts.push(Stmt::Instruction(Instruction { + comments: Vec::new(), + expr: Some(ast::call("xv.at", vec![ast::leaf(&keys_slot), ast::leaf(&idx_slot)])), + writes: vec![key_slot.clone()], + write_types: Vec::new(), + arrow_left: false, + eq: false, + })); + body_insts.push(Stmt::Instruction(Instruction { + comments: Vec::new(), + expr: Some(ast::call("kv.get", vec![ast::leaf(&iter_slot), ast::leaf(&key_slot)])), + writes: vec![s.var.clone()], + write_types: Vec::new(), + arrow_left: false, + eq: false, + })); + } else { + body_insts.push(Stmt::Instruction(Instruction { + comments: Vec::new(), + expr: Some(ast::call("xv.at", vec![ast::leaf(&iter_slot), ast::leaf(&idx_slot)])), + writes: vec![s.var.clone()], + write_types: Vec::new(), + arrow_left: false, + eq: false, + })); + } body_insts.extend(body_inner); let body_insts = inject_goto(body_insts, &cond_label); @@ -316,30 +371,18 @@ fn goto_label(label: &str) -> Stmt { }) } -/// 散 key 数组字面量(parser 产出的 sparsearray)展开为逐元素散 key 写入: -/// `base = {a, b}` → `set(base,"0",a); set(base,"1",b)`——每个元素落在 base. -/// 独立 key(变长/字符串数组的存储形态),与 compact `[...]`(单打包 XValue)相对。 -/// 元素为复合表达式时先 flatten 展开子调用。 -fn expand_sparse(base: &str, e: &Expr, lg: &mut LabelGen) -> Vec { - let mut out = Vec::new(); - for (i, el) in e.args.iter().enumerate() { - let inst = Instruction { - comments: Vec::new(), - expr: Some(ast::call("set", vec![ast::leaf(base), ast::str_lit(&i.to_string()), el.clone()])), - writes: vec![base.to_string()], - write_types: Vec::new(), - arrow_left: true, - eq: false, - }; - if all_args_leaf(inst.expr.as_ref().unwrap()) { - out.push(Stmt::Instruction(inst)); - } else { - let (flat, extra) = flatten_nested_calls(&inst, lg); - out.extend(extra); - out.push(Stmt::Instruction(flat)); - } - } - out +/// 散 key 数组字面量(parser 产出的 "map")→ 单条 `map(v0, v1, ...) -> base`, +/// 运行时 map builtin 创建 map 根(kind=map)+ 子键 `base/[i]`(相对 key,方括号索引串)。 +fn expand_sparse(base: &str, e: &Expr, _lg: &mut LabelGen) -> Vec { + let inst = Instruction { + comments: Vec::new(), + expr: Some(ast::call("map", e.args.clone())), + writes: vec![base.to_string()], + write_types: Vec::new(), + arrow_left: true, + eq: false, + }; + vec![Stmt::Instruction(inst)] } fn make_copy_inst(val: &str, dest: &str) -> Stmt { @@ -557,11 +600,20 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap= 3 { + tm.entry(reads[0].clone()).or_insert_with(|| "obj".to_string()); + } + return String::new(); } match opcode { - "kv.has" => return "bool".to_string(), "kvlen" | "xv.numel" | "xv.dim" | "string.len" | "string.ord" | "string.cmp" | "string.find" => { return "int64".to_string(); } @@ -575,7 +627,7 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap { return if !reads.is_empty() { slot_type(&reads[0], tm) } else { String::new() }; } - "kv.at" | "xv.at" | "at" => { + "xv.at" => { if !reads.is_empty() { if let Some(t) = tm.get(&format!("{}.0", reads[0])) { return t.clone(); @@ -588,9 +640,9 @@ fn infer_op_type(opcode: &str, reads: &[String], tm: &mut HashMap { - if reads.len() >= 3 { - let t = slot_type(&reads[2], tm); + "xv.set" => { + if let Some(last) = reads.last() { + let t = slot_type(last, tm); if !t.is_empty() { let key = format!("{}.0", reads[0]); tm.entry(key).or_insert(t); @@ -676,6 +728,14 @@ fn specialize_inst(inst: &mut Instruction, tm: &HashMap) { if e.is_leaf() { return; } + // xv.at/xv.set 基座是 obj/map → 降为 kv.get/kv.set(成员目录访问)。 + if (e.op == "xv.at" || e.op == "xv.set") && !e.args.is_empty() { + if let Some(t) = tm.get(&e.args[0].val) { + if t == "obj" || t == "map" { + e.op = if e.op == "xv.at" { "kv.get".to_string() } else { "kv.set".to_string() }; + } + } + } let opcode = e.op.clone(); let mut word = symbol::lookup(&opcode).word.to_string(); if word.is_empty() { diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 56897a99..607c1ba8 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -684,7 +684,7 @@ impl Parser { /// 校验散 key 字面量 `{...}` 的出现位置。`top_legal` 表示当前上下文允许顶层出现 /// (赋值右值 / for-in 源);无论如何,其元素内部都不得再嵌套散 key 字面量。 fn check_sparse_usage(&mut self, e: &Expr, top_legal: bool) { - let top_is_sparse = e.op == "sparsearray"; + let top_is_sparse = e.op == "map"; let bad = if top_is_sparse && top_legal { e.args.iter().any(expr_contains_sparse) } else { @@ -752,7 +752,8 @@ impl Parser { let arr = s[..br].to_string(); let idx = s[br + 1..s.len().saturating_sub(1)].to_string(); let e = inst.expr.take(); - inst.expr = Some(ast::call("set", vec![ast::leaf(&arr), ast::leaf(&idx), e.unwrap_or(ast::leaf(""))])); + let op = if arr.starts_with('/') { "kv.set" } else { "xv.set" }; + inst.expr = Some(ast::call(op, vec![ast::leaf(&arr), ast::leaf(&idx), e.unwrap_or(ast::leaf(""))])); inst.writes = vec![arr]; inst.write_types = Vec::new(); } @@ -833,13 +834,13 @@ impl Parser { self.advance(); // consume * if self.peek().kind == Kind::Ident { let key = self.advance().value; - left = ast::call("at", vec![left, ast::leaf(&key)]); + left = ast::call("kv.get", vec![left, ast::leaf(&key)]); continue; } } if self.peek().kind == Kind::Ident || self.peek().kind == Kind::Literal { let field = self.advance().value; - left = ast::call("at", vec![left, ast::str_lit(&field)]); + left = ast::call("kv.get", vec![left, ast::str_lit(&field)]); continue; } } @@ -856,9 +857,11 @@ impl Parser { } } self.expect(Kind::RBrack); + let is_path = left.is_leaf() && left.val.starts_with('/'); let mut args = vec![left]; args.extend(indices); - left = ast::call("at", args); + // 路径字面量 + [idx] → kv.get(KV 路径成员访问);否则 xv.at(compact 数组元素)。 + left = ast::call(if is_path { "kv.get" } else { "xv.at" }, args); continue; } let t = self.peek(); @@ -970,7 +973,7 @@ impl Parser { } } self.expect(Kind::RBrace); - return Some(ast::call("sparsearray", elems)); + return Some(ast::call("map", elems)); } self.advance(); // consume { let mut args = Vec::new(); @@ -986,7 +989,7 @@ impl Parser { self.errors.push(Diagnostic { pos: t.pos, warn: true, - message: format!("dict literal: expected member name, got {:?}", t.value), + message: format!("obj literal: expected member name, got {:?}", t.value), info: false, source: String::new(), src_file: String::new(), @@ -1000,7 +1003,7 @@ impl Parser { self.errors.push(Diagnostic { pos: t.pos, warn: true, - message: format!("dict literal: expected '=' after {key:?}"), + message: format!("obj literal: expected '=' after {key:?}"), info: false, source: String::new(), src_file: String::new(), @@ -1017,7 +1020,7 @@ impl Parser { args.push(val); } self.expect(Kind::RBrace); - return Some(ast::call("dict", args)); + return Some(ast::call("obj", args)); } // 括号分组 @@ -1354,7 +1357,7 @@ impl Parser { ast::str_lit(&field) }; let e = inst.expr.take(); - inst.expr = Some(ast::call("set", vec![ast::leaf(&base), key, e.unwrap_or(ast::leaf(""))])); + inst.expr = Some(ast::call("kv.set", vec![ast::leaf(&base), key, e.unwrap_or(ast::leaf(""))])); inst.writes = vec![base]; inst.write_types = Vec::new(); } @@ -1425,9 +1428,9 @@ fn is_float_literal(v: &str) -> bool { v.contains('.') || v.contains('e') || v.contains('E') } -/// 表达式树中任意节点是否为散 key 字面量 `{...}`(parser 产出的 sparsearray)。 +/// 表达式树中任意节点是否为散 key 字面量 `{...}`(parser 产出的 "map")。 fn expr_contains_sparse(e: &Expr) -> bool { - e.op == "sparsearray" || e.args.iter().any(expr_contains_sparse) + e.op == "map" || e.args.iter().any(expr_contains_sparse) } fn attach_comments(st: Stmt, comments: Vec) -> Stmt { @@ -1452,7 +1455,7 @@ fn is_array_kindexp(t: &str) -> bool { } 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() + "unknown type — valid: int8/16/32/64, uint8/16/32/64, float32/64, bool, char/utf32, obj, map, index, char, any, []T, [2,3]T, [?,N]T, A|B".to_string() } fn walk_read_only( diff --git a/layout/src/type_expr.rs b/layout/src/type_expr.rs index 5f365492..adb9aa00 100644 --- a/layout/src/type_expr.rs +++ b/layout/src/type_expr.rs @@ -15,7 +15,7 @@ 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" + | "float32" | "float64" | "char/utf32" | "char/utf8" | "char/ascii" | "obj" | "index" | "extindex" | "rwir" | "rwfunc" | "scope" | "time" | "duration" ) } @@ -131,11 +131,11 @@ mod tests { fn valid() { for e in [ "int64", "uint8", "float32", "bool", "any", - "char/utf8", "char/utf32", "char/ascii", "dict", "index", + "char/utf8", "char/utf32", "char/ascii", "obj", "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", + "bool|char/utf8", "index|obj", "any...", "int64|float64...", "[]float32...", ] { assert!(valid_type_expr(e), "{e} should be valid"); @@ -170,7 +170,7 @@ mod tests { let cases = [ ("int64", "int64", 0, &[][..], true), ("int64", "float64", 0, &[], false), - ("any", "dict", 0, &[], true), + ("any", "obj", 0, &[], true), ("any", "int4", 0, &[], true), ("char/utf8", "char/utf8", 0, &[], true), ("char/utf8", "char/utf32", 0, &[], false), @@ -185,7 +185,7 @@ mod tests { ("[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), + ("index|obj", "index", 0, &[], true), ]; for (expr, kind, ndim, dims, want) in cases { let got = match_type(expr, kind, ndim, dims); diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index 54363daa..b9d9d5ef 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -7,9 +7,8 @@ typedef int (*kvlangBuiltinFn)(kvlangFrame_t *f); /* collection 模块 handler(builtin_coll.c) */ int kvlangBuiltinArray(kvlangFrame_t *f), kvlangBuiltinXvNumel(kvlangFrame_t *f), kvlangBuiltinXvDim(kvlangFrame_t *f), kvlangBuiltinXvShape(kvlangFrame_t *f), kvlangBuiltinXvAt(kvlangFrame_t *f), kvlangBuiltinXvSet(kvlangFrame_t *f), - kvlangBuiltinAt(kvlangFrame_t *f), kvlangBuiltinSet(kvlangFrame_t *f), - kvlangBuiltinHas(kvlangFrame_t *f), kvlangBuiltinScatter(kvlangFrame_t *f), kvlangBuiltinCompact(kvlangFrame_t *f), - kvlangBuiltinAppend(kvlangFrame_t *f), kvlangBuiltinSlice(kvlangFrame_t *f), kvlangBuiltinDict(kvlangFrame_t *f), kvlangBuiltinStringSet(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), 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), kvlangBuiltinTimeNow(kvlangFrame_t *f), kvlangBuiltinTimeSub(kvlangFrame_t *f), @@ -19,7 +18,7 @@ int kvlangBuiltinArray(kvlangFrame_t *f), kvlangBuiltinXvNumel(kvlangFrame_t *f) kvlangBuiltinKvGet(kvlangFrame_t *f), kvlangBuiltinKvSet(kvlangFrame_t *f), kvlangBuiltinKvDel(kvlangFrame_t *f), kvlangBuiltinKvDelTree(kvlangFrame_t *f), kvlangBuiltinKvList(kvlangFrame_t *f), kvlangBuiltinKvMkindex(kvlangFrame_t *f), kvlangBuiltinKvExtIndex(kvlangFrame_t *f), kvlangBuiltinKvRmIndexExt(kvlangFrame_t *f), kvlangBuiltinKvWatch(kvlangFrame_t *f), - kvlangBuiltinKvHas(kvlangFrame_t *f), kvlangBuiltinKvAt(kvlangFrame_t *f), kvlangBuiltinDebugger(kvlangFrame_t *f); + kvlangBuiltinDebugger(kvlangFrame_t *f); /* ── 类型 helper(对齐 Go isIntKind 含 uint)────────────────────── */ @@ -589,10 +588,10 @@ static const struct { const char *op; kvlangBuiltinFn fn; } builtins[] = { {"float32", kvlangBuiltinCastF32}, {"float64", kvlangBuiltinCastF64}, {"char/utf32", kvlangBuiltinCastChar32}, {"char/utf8", kvlangBuiltinCastChar8}, {"char/ascii", kvlangBuiltinCastCharAscii}, /* collection */ - {"array", kvlangBuiltinArray}, {"at", kvlangBuiltinAt}, {"set", kvlangBuiltinSet}, {"has", kvlangBuiltinHas}, + {"array", kvlangBuiltinArray}, {"array.scatter", kvlangBuiltinScatter}, {"array.compact", kvlangBuiltinCompact}, {"array.append", kvlangBuiltinAppend}, {"array.slice", kvlangBuiltinSlice}, - {"dict", kvlangBuiltinDict}, + {"obj", kvlangBuiltinObj}, {"map", kvlangBuiltinMap}, {"xv.numel", kvlangBuiltinXvNumel}, {"xv.dim", kvlangBuiltinXvDim}, {"xv.shape", kvlangBuiltinXvShape}, {"xv.at", kvlangBuiltinXvAt}, {"xv.set", kvlangBuiltinXvSet}, {"string.set", kvlangBuiltinStringSet}, {"string.char", kvlangBuiltinStringChar}, {"string.ord", kvlangBuiltinStringOrd}, @@ -612,7 +611,6 @@ static const struct { const char *op; kvlangBuiltinFn fn; } builtins[] = { {"kv.get", kvlangBuiltinKvGet}, {"kv.set", kvlangBuiltinKvSet}, {"kv.del", kvlangBuiltinKvDel}, {"kv.deltree", kvlangBuiltinKvDelTree}, {"kv.list", kvlangBuiltinKvList}, {"kv.mkindex", kvlangBuiltinKvMkindex}, {"kv.extindex", kvlangBuiltinKvExtIndex}, {"kv.rmindexext", kvlangBuiltinKvRmIndexExt}, {"kv.watch", kvlangBuiltinKvWatch}, - {"kv.has", kvlangBuiltinKvHas}, {"kv.at", kvlangBuiltinKvAt}, {"debugger", kvlangBuiltinDebugger}, }; @@ -907,6 +905,19 @@ int kvlangBuiltinXvAt(kvlangFrame_t *f) { int nidx = f->inst->nr - 1; if (nidx < 1) return set_err(f, "TypeError: xv.at requires array and indices"); kvlangXvalue_t in[MAX_PARAMS]; int n = read_inputs(f, in, MAX_PARAMS); + /* 散 key 数组(kv.list 产出的 key 列表):base[idx] 平方括号寻址 */ + if (nidx == 1 && kvlangXvalueNone(&in[0]) && f->inst->nr > 0) { + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *base = separated_base(f->kv, fr, f->inst->reads[0].name); + if (base) { + int idx = (int)kvlangXvalueAsInt64(&in[1]); + kvlangStrbuf_t k; kvlangStrbufInit(&k); kvlangStrbufPrintf(&k, "%s[%d]", base, idx); + kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, k.p, &v); + int rc = write_result(f, &v); kvlangXvalueFree(&v); kvlangStrbufFree(&k); + free(base); free(fr); free_inputs(in, n); return rc; + } + free(fr); + } const char *k = kvlangXvalueKind(&in[0]); int sz = kvlangXvalueElemSize(k); kvspaceHead_t h; kvspaceDecodeHead(in[0].data, in[0].len, &h); @@ -948,165 +959,6 @@ int kvlangBuiltinXvSet(kvlangFrame_t *f) { next_pc(f); return 0; } -int kvlangBuiltinAt(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = read_inputs(f, in, 2); - if (n < 2) { free_inputs(in, n); return set_err(f, "TypeError: at requires array and index"); } - const char *k0 = kvlangXvalueKind(&in[0]), *k1 = kvlangXvalueKind(&in[1]); - /* 字符串非路径 → 码点索引 */ - if (kvlangXvalueIsCharKind(k0)) { - char *s = kvlangXvalueValueString(&in[0]); - bool is_path = s[0] == '/'; - free(s); - if (!is_path) { - if (var_len_char_err(k0)) { free_inputs(in, n); return set_err(f, "%s", var_len_char_err(k0)); } - if (!is_int_kind(k1)) { kvlangXvalue_t e; kvlangXvalueNewCharUtf32(&e, ""); int rc = write_result(f, &e); kvlangXvalueFree(&e); free_inputs(in, n); return rc; } - int idx = (int)kvlangXvalueAsInt64(&in[1]); - int rn; uint32_t *r = string_runes(&in[0], &rn); - if (idx < 0 || idx >= rn) { free(r); kvlangXvalue_t e; kvlangXvalueNewCharUtf32(&e, ""); int rc = write_result(f, &e); kvlangXvalueFree(&e); free_inputs(in, n); return rc; } - kvlangXvalue_t e; new_char32_cp(&e, r[idx]); - int rc = write_result(f, &e); kvlangXvalueFree(&e); free(r); free_inputs(in, n); return rc; - } - } - /* 路径访问 */ - bool path_access = strcmp(k0, KVSPACE_KIND_DICT) == 0 || kvlangXvalueIsCharKind(k0) || kvlangXvalueIsCharKind(k1) || - (f->inst->nr > 0 && (f->inst->reads[0].name[0] == '/' || (f->inst->reads[0].name[0] == '"' && f->inst->reads[0].name[1] == '/'))); - if (path_access) { - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *ff = kvlangBuiltinFuncFrameRoot(f->kv, fr); - kvlangXvalue_t base; kvlangXvalueZero(&base); - kvlangBuiltinResolveReadValue(f->kv, ff, f->inst->reads[0].name, &f->inst->reads[0].val, &base); - char *bp = kvlangXvalueNone(&base) || (strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_DICT) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_INDEX) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_EXT_INDEX) == 0) ? kvlangBuiltinResolveWriteSlot(f->kv, ff, f->inst->reads[0].name) : kvlangXvalueValueString(&base); - char *kk = kvlangKvKey(&in[1]); - char *path = kvlangKeytreeMember(bp, kk); - kvlangXvalue_t v; kvlangXvalueZero(&v); - kvlangKvGetOne(f->kv, path, &v); - int rc = write_result(f, &v); kvlangXvalueFree(&v); - free(path); free(kk); free(bp); kvlangXvalueFree(&base); free(ff); free(fr); - free_inputs(in, n); return rc; - } - if (kvlangXvalueElemSize(k0) > 0 && kvlangXvalueIsCharKind(k1)) { free_inputs(in, n); return set_err(f, "IndexError: at: index must be integer for typed array"); } - if (kvlangXvalueNone(&in[0]) && f->inst->nr > 0) { - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *base = separated_base(f->kv, fr, f->inst->reads[0].name); - if (base) { - int idx = (int)kvlangXvalueAsInt64(&in[1]); - kvlangStrbuf_t k; kvlangStrbufInit(&k); kvlangStrbufPrintf(&k, "%s[%d]", base, idx); - kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, k.p, &v); - int rc = write_result(f, &v); kvlangXvalueFree(&v); kvlangStrbufFree(&k); - free(base); free(fr); free_inputs(in, n); return rc; - } - free(fr); - } - if (kvlangXvalueNone(&in[0])) { free_inputs(in, n); return set_err(f, "IndexError: at: base is None; help: declare a key-family first or pass a path string"); } - int idx = (int)kvlangXvalueAsInt64(&in[1]); - kvlangXvalue_t e; xvalue_at(&in[0], idx, &e); - int rc = write_result(f, &e); kvlangXvalueFree(&e); free_inputs(in, n); - return rc; -} - -int kvlangBuiltinHas(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = read_inputs(f, in, 2); - if (n < 2) { kvlangXvalue_t r; kvlangXvalueNewBool(&r, false); int rc = write_result(f, &r); kvlangXvalueFree(&r); free_inputs(in, n); return rc; } - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *ff = kvlangBuiltinFuncFrameRoot(f->kv, fr); - kvlangXvalue_t base; kvlangXvalueZero(&base); - kvlangBuiltinResolveReadValue(f->kv, ff, f->inst->reads[0].name, &f->inst->reads[0].val, &base); - char *bp = kvlangXvalueNone(&base) || (strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_DICT) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_INDEX) == 0) ? kvlangBuiltinResolveWriteSlot(f->kv, ff, f->inst->reads[0].name) : kvlangXvalueValueString(&base); - char *kk = kvlangKvKey(&in[1]); - char *path = kvlangKeytreeMember(bp, kk); - kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, path, &v); - bool exists = !kvlangXvalueNone(&v); - kvlangXvalue_t r; kvlangXvalueNewBool(&r, exists); - int rc = write_result(f, &r); kvlangXvalueFree(&r); kvlangXvalueFree(&v); - free(path); free(kk); free(bp); kvlangXvalueFree(&base); free(ff); free(fr); - free_inputs(in, n); return rc; -} - -int kvlangBuiltinSet(kvlangFrame_t *f) { - kvlangXvalue_t in[3]; int n = read_inputs(f, in, 3); - if (n < 3) { free_inputs(in, n); return set_err(f, "TypeError: set requires array, index, value"); } - const char *k0 = kvlangXvalueKind(&in[0]); - /* 字符串非路径 → 替换码点 */ - if (kvlangXvalueIsCharKind(k0)) { - char *s = kvlangXvalueValueString(&in[0]); bool is_path = s[0] == '/'; free(s); - if (!is_path) { - if (var_len_char_err(k0)) { free_inputs(in, n); return set_err(f, "%s", var_len_char_err(k0)); } - int idx = (int)kvlangXvalueAsInt64(&in[1]); - int rn; uint32_t *r = string_runes(&in[0], &rn); - if (idx < 0 || idx >= rn) { free(r); free_inputs(in, n); return set_err(f, "IndexError: set: string index %d out of bounds (len=%d)", idx, rn); } - int rn2; uint32_t *repl = string_runes(&in[2], &rn2); - if (rn2 == 0) { free(repl); free(r); free_inputs(in, n); return set_err(f, "TypeError: set: replacement char is empty"); } - r[idx] = repl[0]; - int rc = write_char32(f, r, rn); - free(repl); free(r); free_inputs(in, n); - return rc; - } - } - /* 路径写入 */ - bool path_access = strcmp(k0, KVSPACE_KIND_DICT) == 0 || kvlangXvalueIsCharKind(k0) || kvlangXvalueIsCharKind(kvlangXvalueKind(&in[1])) || - (f->inst->nr > 0 && (f->inst->reads[0].name[0] == '/' || (f->inst->reads[0].name[0] == '"' && f->inst->reads[0].name[1] == '/'))); - if (path_access) { - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *ff = kvlangBuiltinFuncFrameRoot(f->kv, fr); - kvlangXvalue_t base; kvlangXvalueZero(&base); - kvlangBuiltinResolveReadValue(f->kv, ff, f->inst->reads[0].name, &f->inst->reads[0].val, &base); - char *bp = kvlangXvalueNone(&base) || (strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_DICT) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_INDEX) == 0) ? kvlangBuiltinResolveWriteSlot(f->kv, ff, f->inst->reads[0].name) : kvlangXvalueValueString(&base); - char *kk = kvlangKvKey(&in[1]); - char *path = kvlangKeytreeMember(bp, kk); - kvlangKvPair_t p = { path, in[2] }; - char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - if (f->inst->nw > 0 && !kvlangXvalueNone(&in[0])) { - char *ok = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[0].name); - kvlangKvPair_t p2 = { ok, in[0] }; kvlangKvSet(f->kv, &p2, 1, err, sizeof err); - free(ok); - } - free(path); free(kk); free(bp); kvlangXvalueFree(&base); free(ff); free(fr); - next_pc(f); free_inputs(in, n); return 0; - } - if (kvlangXvalueElemSize(k0) > 0 && kvlangXvalueIsCharKind(kvlangXvalueKind(&in[1]))) { free_inputs(in, n); return set_err(f, "IndexError: set: index must be integer for typed array"); } - if (kvlangXvalueNone(&in[0]) && f->inst->nr > 0) { - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *base = separated_base(f->kv, fr, f->inst->reads[0].name); - if (base) { - int idx = (int)kvlangXvalueAsInt64(&in[1]); - kvlangStrbuf_t k; kvlangStrbufInit(&k); kvlangStrbufPrintf(&k, "%s[%d]", base, idx); - kvlangKvPair_t p = { k.p, in[2] }; - char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - kvlangStrbufFree(&k); free(base); free(fr); - next_pc(f); free_inputs(in, n); return 0; - } - free(fr); - } - if (kvlangXvalueNone(&in[0])) { free_inputs(in, n); return set_err(f, "IndexError: set: base is None"); } - if (kvlangXvalueElemSize(k0) > 0) { - /* 整存整取改元素 */ - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *key = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->reads[0].name); - kvlangXvalue_t arr; kvlangXvalueZero(&arr); kvlangKvGetOne(f->kv, key, &arr); - if (!kvlangXvalueNone(&arr)) { - int idx = (int)kvlangXvalueAsInt64(&in[1]); - int sz = kvlangXvalueElemSize(k0); - kvspaceHead_t h; kvspaceDecodeHead(arr.data, arr.len, &h); - const uint8_t *body = arr.data + h.body_offset; - int al = kvlangXvalueArrayLen(&arr); - if (idx >= 0 && idx < al) { - uint8_t *nb = malloc((size_t)h.body_len); - memcpy(nb, body, (size_t)h.body_len); - const uint8_t *vb; int32_t vbl; kvspaceHead_t vh; kvspaceDecodeHead(in[2].data, in[2].len, &vh); vb = in[2].data + vh.body_offset; vbl = vh.body_len; - int c = vbl < sz ? vbl : sz; - memcpy(nb + idx * sz, vb, (size_t)c); - kvlangXvalue_t nv; kvlangXvalueNewTlv(&nv, k0, nb, (uint32_t)h.body_len, al); - kvlangKvPair_t p = { key, nv }; - char err[256]; kvlangKvSet(f->kv, &p, 1, err, sizeof err); - kvlangXvalueFree(&nv); free(nb); - } - } - kvlangXvalueFree(&arr); free(key); free(fr); - next_pc(f); free_inputs(in, n); return 0; - } - free_inputs(in, n); return set_err(f, "IndexError: set: unsupported array kind %s", k0); -} - int kvlangBuiltinScatter(kvlangFrame_t *f) { if (f->inst->nw == 0) return set_err(f, "TypeError: array.scatter requires a write param"); kvlangXvalue_t in[2]; int n = read_inputs(f, in, 2); @@ -1187,12 +1039,12 @@ int kvlangBuiltinSlice(kvlangFrame_t *f) { /* ── dict ─────────────────────────────────────────────────────────── */ -int kvlangBuiltinDict(kvlangFrame_t *f) { +int kvlangBuiltinObj(kvlangFrame_t *f) { kvlangXvalue_t in[64]; int n = read_inputs(f, in, 64); char *fr = kvlangKeytreeFrameRoot(f->pc); for (int w = 0; w < f->inst->nw; w++) { char *ok = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); - kvlangXvalue_t mark; kvlangXvalueNewTlv(&mark, KVSPACE_KIND_DICT, (const uint8_t *)"", 0, 1); + kvlangXvalue_t mark; kvlangXvalueNewTlv(&mark, KVSPACE_KIND_OBJ, (const uint8_t *)"", 0, 1); kvlangKvPair_t p0 = { ok, mark }; char err[256]; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); kvlangXvalueFree(&mark); @@ -1210,6 +1062,28 @@ int kvlangBuiltinDict(kvlangFrame_t *f) { next_pc(f); free_inputs(in, n); return 0; } +int kvlangBuiltinMap(kvlangFrame_t *f) { + kvlangXvalue_t in[64]; int n = read_inputs(f, in, 64); + char *fr = kvlangKeytreeFrameRoot(f->pc); + for (int w = 0; w < f->inst->nw; w++) { + char *ok = kvlangBuiltinResolveWriteSlot(f->kv, fr, f->inst->writes[w].name); + kvlangXvalue_t mark; kvlangXvalueNewTlv(&mark, KVSPACE_KIND_MAP, (const uint8_t *)"", 0, 1); + kvlangKvPair_t p0 = { ok, mark }; + char err[256]; kvlangKvSet(f->kv, &p0, 1, err, sizeof err); + kvlangXvalueFree(&mark); + for (int i = 0; i < n; i++) { + char key[32]; snprintf(key, sizeof key, "[%d]", i); + char *mk = kvlangKeytreeMember(ok, key); + kvlangKvPair_t p = { mk, in[i] }; + kvlangKvSet(f->kv, &p, 1, err, sizeof err); + free(mk); + } + free(ok); + } + free(fr); + next_pc(f); free_inputs(in, n); return 0; +} + /* ── string 系列 ──────────────────────────────────────────────────── */ int kvlangBuiltinStringSet(kvlangFrame_t *f) { @@ -1458,10 +1332,22 @@ static char *path_arg(kvlangFrame_t *f, int idx, const kvlangXvalue_t *in) { return NULL; } +static char *member_path(kvlangFrame_t *f, const kvlangXvalue_t *in) { + const kvlangXvalue_t *base = &in[0]; + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *ff = kvlangBuiltinFuncFrameRoot(f->kv, fr); + 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, ff, f->inst->reads[0].name) : kvlangXvalueValueString(base); + free(ff); free(fr); + char *kk = kvlangKvKey(&in[1]); + char *path = kvlangKeytreeMember(bp, kk); + free(kk); free(bp); + return path; +} + int kvlangBuiltinKvGet(kvlangFrame_t *f) { - kvlangXvalue_t in[1]; int n = read_inputs(f, in, 1); - char *key = n >= 1 ? path_arg(f, 0, in) : NULL; - if (!key) { free_inputs(in, n); return set_err(f, "TypeError: kv.get requires 1 path arg"); } + kvlangXvalue_t in[2]; int n = read_inputs(f, in, 2); + char *key = f->inst->nr >= 2 ? member_path(f, in) : (n >= 1 ? path_arg(f, 0, in) : NULL); + if (!key) { free_inputs(in, n); return set_err(f, "TypeError: kv.get requires a path"); } kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, key, &v); int rc = write_result(f, &v); kvlangXvalueFree(&v); @@ -1469,10 +1355,12 @@ int kvlangBuiltinKvGet(kvlangFrame_t *f) { } int kvlangBuiltinKvSet(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = read_inputs(f, in, 2); - char *key = n >= 1 ? path_arg(f, 0, in) : NULL; - if (!key || n < 2) { free(key); free_inputs(in, n); return set_err(f, "TypeError: kv.set requires key and value"); } - kvlangKvPair_t p = { key, in[1] }; + kvlangXvalue_t in[3]; int n = read_inputs(f, in, 3); + char *key; kvlangXvalue_t *val; + if (f->inst->nr >= 3) { key = member_path(f, in); val = &in[2]; } + else { key = n >= 1 ? path_arg(f, 0, in) : NULL; val = &in[1]; } + if (!key || (f->inst->nr < 3 && n < 2)) { free(key); free_inputs(in, n); return set_err(f, "TypeError: kv.set requires path and value"); } + kvlangKvPair_t p = { key, *val }; char err[256]; int rc = kvlangKvSet(f->kv, &p, 1, err, sizeof err); free(key); free_inputs(in, n); if (rc != 0) return set_err(f, "%s", err); @@ -1498,6 +1386,18 @@ int kvlangBuiltinKvList(kvlangFrame_t *f) { if (f->inst->nw == 0) return set_err(f, "TypeError: kv.list requires a write param"); kvlangXvalue_t in[1]; int n = read_inputs(f, in, 1); char *key = n >= 1 ? path_arg(f, 0, in) : NULL; + if (!key && f->inst->nr >= 1) { + /* 裸变量(obj/map 成员目录):解析为 /. 目录。 */ + const char *name = f->inst->reads[0].name; + if (name[0] != '/') { + char *fr = kvlangKeytreeFrameRoot(f->pc); + char *ff = kvlangBuiltinFuncFrameRoot(f->kv, fr); + char *base = kvlangBuiltinResolveWriteSlot(f->kv, ff, name); + free(ff); free(fr); + key = kvlangKeytreeMember(base, ""); + free(base); + } + } if (!key) { free_inputs(in, n); return set_err(f, "TypeError: kv.list requires 1 path arg"); } char **names = NULL; int count = 0; kvlangKvList(f->kv, key, false, false, &names, &count); @@ -1546,55 +1446,6 @@ int kvlangBuiltinKvWatch(kvlangFrame_t *f) { free(key); free_inputs(in, n); return rc; } -int kvlangBuiltinKvHas(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = read_inputs(f, in, 2); - if (n < 2) { free_inputs(in, n); return set_err(f, "TypeError: kv.has requires 2 args"); } - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *ff = kvlangBuiltinFuncFrameRoot(f->kv, fr); - kvlangXvalue_t base; kvlangXvalueZero(&base); - kvlangBuiltinResolveReadValue(f->kv, ff, f->inst->reads[0].name, &f->inst->reads[0].val, &base); - if (!kvlangXvalueNone(&base) && kvlangXvalueElemSize(kvlangXvalueKind(&base)) > 0 && !kvlangXvalueIsCharKind(kvlangXvalueKind(&base)) - && f->inst->reads[0].name[0] != '/' && kvlangXvalueIsIntKind(kvlangXvalueKind(&in[1]))) { - int idx = (int)kvlangXvalueAsInt64(&in[1]); - kvlangXvalue_t e; kvlangXvalueNewBool(&e, idx >= 0 && idx < kvlangXvalueArrayLen(&base)); - int rc = write_result(f, &e); kvlangXvalueFree(&e); - kvlangXvalueFree(&base); free(ff); free(fr); free_inputs(in, n); return rc; - } - char *bp = kvlangXvalueNone(&base) || (strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_DICT) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_INDEX) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_EXT_INDEX) == 0) ? kvlangBuiltinResolveWriteSlot(f->kv, ff, f->inst->reads[0].name) : kvlangXvalueValueString(&base); - char *kk = kvlangKvKey(&in[1]); - char *path = kvlangKeytreeMember(bp, kk); - kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, path, &v); - kvlangXvalue_t e; kvlangXvalueNewBool(&e, !kvlangXvalueNone(&v)); - int rc = write_result(f, &e); kvlangXvalueFree(&e); kvlangXvalueFree(&v); - free(path); free(kk); free(bp); kvlangXvalueFree(&base); free(ff); free(fr); - free_inputs(in, n); return rc; -} - -int kvlangBuiltinKvAt(kvlangFrame_t *f) { - kvlangXvalue_t in[2]; int n = read_inputs(f, in, 2); - if (n < 2) { free_inputs(in, n); return set_err(f, "TypeError: kv.at requires 2 args"); } - char *fr = kvlangKeytreeFrameRoot(f->pc); - char *ff = kvlangBuiltinFuncFrameRoot(f->kv, fr); - kvlangXvalue_t base; kvlangXvalueZero(&base); - kvlangBuiltinResolveReadValue(f->kv, ff, f->inst->reads[0].name, &f->inst->reads[0].val, &base); - if (!kvlangXvalueNone(&base) && kvlangXvalueElemSize(kvlangXvalueKind(&base)) > 0 && !kvlangXvalueIsCharKind(kvlangXvalueKind(&base)) - && f->inst->reads[0].name[0] != '/' && kvlangXvalueIsIntKind(kvlangXvalueKind(&in[1]))) { - int idx = (int)kvlangXvalueAsInt64(&in[1]); - if (idx < 0 || idx >= kvlangXvalueArrayLen(&base)) { kvlangXvalueFree(&base); free(ff); free(fr); free_inputs(in, n); return set_err(f, "KeyError: kv.at: index out of range: %d", idx); } - kvlangXvalue_t e; xvalue_at(&base, idx, &e); - int rc = write_result(f, &e); kvlangXvalueFree(&e); - kvlangXvalueFree(&base); free(ff); free(fr); free_inputs(in, n); return rc; - } - char *bp = kvlangXvalueNone(&base) || (strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_DICT) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_INDEX) == 0 || strcmp(kvlangXvalueKind(&base), KVSPACE_KIND_EXT_INDEX) == 0) ? kvlangBuiltinResolveWriteSlot(f->kv, ff, f->inst->reads[0].name) : kvlangXvalueValueString(&base); - char *kk = kvlangKvKey(&in[1]); - char *path = kvlangKeytreeMember(bp, kk); - kvlangXvalue_t v; kvlangXvalueZero(&v); kvlangKvGetOne(f->kv, path, &v); - if (kvlangXvalueNone(&v)) { free(path); free(kk); free(bp); kvlangXvalueFree(&base); free(ff); free(fr); free_inputs(in, n); return set_err(f, "KeyError: kv.at: key not found: %s", path); } - int rc = write_result(f, &v); kvlangXvalueFree(&v); - free(path); free(kk); free(bp); kvlangXvalueFree(&base); free(ff); free(fr); - free_inputs(in, n); return rc; -} - /* ── debugger ─────────────────────────────────────────────────────── */ int kvlangBuiltinDebugger(kvlangFrame_t *f) { diff --git a/runtime/src/runtime_internal.h b/runtime/src/runtime_internal.h index 2e1ad447..5e8a9ad6 100644 --- a/runtime/src/runtime_internal.h +++ b/runtime/src/runtime_internal.h @@ -66,7 +66,8 @@ extern int kvspaceNewFloat64(double v, uint8_t **out, uint32_t *out_len); #define KVSPACE_KIND_CHAR "char/utf32" #define KVSPACE_KIND_CHAR_UTF8 "char/utf8" #define KVSPACE_KIND_CHAR_ASCII "char/ascii" -#define KVSPACE_KIND_DICT "dict" +#define KVSPACE_KIND_OBJ "obj" +#define KVSPACE_KIND_MAP "map" #define KVSPACE_KIND_INDEX "index" #define KVSPACE_KIND_EXT_INDEX "extindex" #define KVSPACE_KIND_RWIR "rwir" diff --git a/runtime/src/type_expr.c b/runtime/src/type_expr.c index 91e1d386..31321c6e 100644 --- a/runtime/src/type_expr.c +++ b/runtime/src/type_expr.c @@ -28,7 +28,7 @@ static bool known_kind(const char *s, size_t len) { kind_eq(s, len, "float32") || kind_eq(s, len, "float64") || kind_eq(s, len, "char/utf32") || kind_eq(s, len, "char/utf8") || kind_eq(s, len, "char/ascii") || - kind_eq(s, len, "dict") || kind_eq(s, len, "index") || + kind_eq(s, len, "obj") || kind_eq(s, len, "index") || kind_eq(s, len, "extindex") || kind_eq(s, len, "rwir") || kind_eq(s, len, "rwfunc") || kind_eq(s, len, "scope") || kind_eq(s, len, "time") || diff --git a/runtime/src/xvalue.c b/runtime/src/xvalue.c index 2687b517..49cb7f48 100644 --- a/runtime/src/xvalue.c +++ b/runtime/src/xvalue.c @@ -270,9 +270,9 @@ char *kvlangXvalueValueString(const kvlangXvalue_t *v) { if (blen > 0) n++; kvlangStrbuf_t b; kvlangStrbufInit(&b); kvlangStrbufPrintf(&b, "(%d)", n); return kvlangStrbufDetach(&b); } - if (strcmp(k, KVSPACE_KIND_DICT) == 0) { + if (strcmp(k, KVSPACE_KIND_OBJ) == 0) { kvlangStrbuf_t b; kvlangStrbufInit(&b); - if (blen == 0) kvlangStrbufPuts(&b, "dict"); + if (blen == 0) kvlangStrbufPuts(&b, "obj"); else { int n = 1; for (int32_t i = 0; i < blen; i++) if (body[i] == '\n') n++; kvlangStrbufPrintf(&b, "{%d}", n); } return kvlangStrbufDetach(&b); } diff --git a/tutorial/01-basics/kv_tree.kv b/tutorial/01-basics/kv_tree.kv index ad53ae40..8b1ae268 100644 --- a/tutorial/01-basics/kv_tree.kv +++ b/tutorial/01-basics/kv_tree.kv @@ -9,9 +9,9 @@ rwfunc main() -> () { kv.set("/tmp/kvt.data", 42) kv.get("/tmp/kvt.data") -> v println("v=", v) - kv.has("/tmp/kvt", "data") -> h + kv.get("/tmp/kvt", "data") != None -> h println("has_data=", h) - kv.at("/tmp/kvt", "data") -> x + kv.get("/tmp/kvt", "data") -> x println("x=", x) kv.mkindex("/tmp/kvt/") kv.set("/tmp/kvt/a", 1) diff --git a/tutorial/05-dict/kv_has_string_key.kv b/tutorial/05-dict/kv_has_string_key.kv index 7f18f909..6d281eaf 100644 --- a/tutorial/05-dict/kv_has_string_key.kv +++ b/tutorial/05-dict/kv_has_string_key.kv @@ -2,7 +2,7 @@ # true rwfunc main() -> () { /tmp/kvhas_test.data = 42 - kv.has(/tmp/kvhas_test, "data") -> ok + kv.get(/tmp/kvhas_test, "data") != None -> ok println(ok) } main() diff --git a/tutorial/05-dict/literal.kv b/tutorial/05-dict/literal.kv index ee1cec89..775ff3b3 100644 --- a/tutorial/05-dict/literal.kv +++ b/tutorial/05-dict/literal.kv @@ -10,7 +10,7 @@ rwfunc main() -> () { println("h.x =", v) 42 -> h.z println("h.z =", h.z) - has(h, "x") -> ok + kv.get(h, "x") != None -> ok println("has x:", ok) } diff --git a/tutorial/08-leetcode/001_two-sum-hash.kv b/tutorial/08-leetcode/001_two-sum-hash.kv index ee221eea..6775d4ef 100644 --- a/tutorial/08-leetcode/001_two-sum-hash.kv +++ b/tutorial/08-leetcode/001_two-sum-hash.kv @@ -9,16 +9,16 @@ rwfunc two_sum(nums:[]int64, target:int64) -> () { while (i < n) { x <- nums[i] need = target - x # = 等价于 <- - has(h, need) -> exists + kv.get(h, need) != None -> exists if (exists) { - h[need] -> j + kv.get(h, need) -> j k = j - 1 # = 等价于 <- println("[", k, ",", i, "]") n -> i } else { v <- i + 1 - _ = set(h, x, v) # = 等价于 <- + _ = kv.set(h, x, v) # = 等价于 <- i + 1 -> i } } diff --git a/tutorial/08-leetcode/003_longest_substring.kv b/tutorial/08-leetcode/003_longest_substring.kv index c49e6802..70ec87c3 100644 --- a/tutorial/08-leetcode/003_longest_substring.kv +++ b/tutorial/08-leetcode/003_longest_substring.kv @@ -12,13 +12,13 @@ rwfunc length_of_longest(s:[]char/utf32) -> (max_len:int64) { right <- 0 while (right < n) { s[right] -> c - exists <- has(last, c) + exists <- kv.get(last, c) != None if (exists) { prev = last[c] # = 等价于 <- cand <- prev + 1 if (cand > left) { left <- cand } } - _ = set(last, c, right) # = 等价于 <- + _ = kv.set(last, c, right) # = 等价于 <- cur <- right - left + 1 if (cur > max_len) { cur -> max_len } right + 1 -> right diff --git a/tutorial/08-leetcode/020_valid_parentheses.kv b/tutorial/08-leetcode/020_valid_parentheses.kv index 4058dca3..c8fb2e83 100644 --- a/tutorial/08-leetcode/020_valid_parentheses.kv +++ b/tutorial/08-leetcode/020_valid_parentheses.kv @@ -13,15 +13,15 @@ rwfunc is_valid(s:[]char/utf32) -> (ok:int64) { c = s[i] # = 等价于 <- if (c == "(") { si + 1 -> si - _ <- set("/tmp/vp", si, 1) + _ <- kv.set("/tmp/vp", si, 1) } if (c == "[") { si = si + 1 # = 等价于 <- - set("/tmp/vp", si, 2) -> _ + kv.set("/tmp/vp", si, 2) -> _ } if (c == "{") { si + 1 -> si - _ = set("/tmp/vp", si, 3) # = 等价于 <- + _ = kv.set("/tmp/vp", si, 3) # = 等价于 <- } if (c == ")") { if (si < 0) { 0 -> ok ; n -> i } diff --git a/tutorial/08-leetcode/038_count_and_say.kv b/tutorial/08-leetcode/038_count_and_say.kv index 67b4339f..6a99a338 100644 --- a/tutorial/08-leetcode/038_count_and_say.kv +++ b/tutorial/08-leetcode/038_count_and_say.kv @@ -19,8 +19,8 @@ rwfunc next_seq(src_len:int64) -> (dst_len:int64) { if (v == cur) { count + 1 -> count ; j + 1 -> j } else { src_len -> j } } - _ = set("/tmp/s2", di, count) - _ = set("/tmp/s2", di+1, cur) + _ = kv.set("/tmp/s2", di, count) + _ = kv.set("/tmp/s2", di+1, cur) di + 2 -> di i + count -> i } @@ -46,14 +46,14 @@ rwfunc copy_from_s2_to_s(s2_len:int64) -> () { i = 0 # = 等价于 <- while (i < s2_len) { /tmp/s2[i] -> v - _ = set("/tmp/s", i, v) # = 等价于 <- + _ = kv.set("/tmp/s", i, v) # = 等价于 <- i + 1 -> i } } rwfunc count_and_say(N:int64) -> () { - _ = set("/tmp/s", 0, 1) # count=1 of digit 1 - _ = set("/tmp/s", 1, 1) + _ = kv.set("/tmp/s", 0, 1) # count=1 of digit 1 + _ = kv.set("/tmp/s", 1, 1) cur_len = 2 # = 等价于 <- round = 1 # = 等价于 <- while (round < N) { diff --git a/tutorial/08-leetcode/056_merge_intervals.kv b/tutorial/08-leetcode/056_merge_intervals.kv index 5071797d..20e85223 100644 --- a/tutorial/08-leetcode/056_merge_intervals.kv +++ b/tutorial/08-leetcode/056_merge_intervals.kv @@ -16,8 +16,8 @@ rwfunc bubble_sort(n:int64) -> () { # swap start /tmp/mi[j][1] -> a1 /tmp/mi[j+1][1] -> b1 - _ = set("/tmp/mi", j, [b, b1]) # = 等价于 <- - _ = set("/tmp/mi", j+1, [a, a1]) # = 等价于 <- + _ = kv.set("/tmp/mi", j, [b, b1]) # = 等价于 <- + _ = kv.set("/tmp/mi", j+1, [a, a1]) # = 等价于 <- } j + 1 -> j } @@ -38,26 +38,26 @@ rwfunc merge(n:int64) -> (m:int64) { nxt[1] -> nxt_end if (cur_end >= nxt_start) { if (nxt_end > cur_end) { - _ = set("/tmp/mr", ki, [cur[0], nxt_end]) # = 等价于 <- + _ = kv.set("/tmp/mr", ki, [cur[0], nxt_end]) # = 等价于 <- /tmp/mr[ki] -> cur } } else { - _ = set("/tmp/mr", ki, cur) + _ = kv.set("/tmp/mr", ki, cur) ki = ki + 1 # = 等价于 <- nxt -> cur } idx + 1 -> idx } - _ = set("/tmp/mr", ki, cur) + _ = kv.set("/tmp/mr", ki, cur) ki + 1 -> m } rwfunc main() -> () { # init intervals in tmp storage - _ = set("/tmp/mi", 0, [1, 3]) - _ = set("/tmp/mi", 1, [2, 6]) - _ = set("/tmp/mi", 2, [8, 10]) - _ = set("/tmp/mi", 3, [15, 18]) + _ = kv.set("/tmp/mi", 0, [1, 3]) + _ = kv.set("/tmp/mi", 1, [2, 6]) + _ = kv.set("/tmp/mi", 2, [8, 10]) + _ = kv.set("/tmp/mi", 3, [15, 18]) cnt <- merge(4) i = 0 # = 等价于 <- while (i < cnt) { diff --git a/tutorial/08-leetcode/062_unique_paths.kv b/tutorial/08-leetcode/062_unique_paths.kv index 808623b2..23c69bcf 100644 --- a/tutorial/08-leetcode/062_unique_paths.kv +++ b/tutorial/08-leetcode/062_unique_paths.kv @@ -6,7 +6,7 @@ rwfunc dp(m:int64, n:int64) -> (result:int64) { i = 0 # = 等价于 <- while (i < n) { - _ = set("/tmp/up", i, 1) # = 等价于 <- + _ = kv.set("/tmp/up", i, 1) # = 等价于 <- i + 1 -> i } j <- 1 @@ -15,7 +15,7 @@ rwfunc dp(m:int64, n:int64) -> (result:int64) { while (k < n) { /tmp/up[k] -> v /tmp/up[k-1] -> prev - _ = set("/tmp/up", k, prev + v) # = 等价于 <- + _ = kv.set("/tmp/up", k, prev + v) # = 等价于 <- k + 1 -> k } j + 1 -> j diff --git a/tutorial/08-leetcode/094_inorder_traversal.kv b/tutorial/08-leetcode/094_inorder_traversal.kv index 56fec466..2c9107dc 100644 --- a/tutorial/08-leetcode/094_inorder_traversal.kv +++ b/tutorial/08-leetcode/094_inorder_traversal.kv @@ -17,7 +17,7 @@ rwfunc inorder(root:[]char/utf32) -> () { cur <- root while (cur != "" || si > 0) { while (cur != "") { - _ = set(stack, si, cur) + _ = kv.set(stack, si, cur) si + 1 -> si cur.left -> cur } diff --git a/tutorial/08-leetcode/118_pascal_triangle.kv b/tutorial/08-leetcode/118_pascal_triangle.kv index 5556b091..d0d8e9c5 100644 --- a/tutorial/08-leetcode/118_pascal_triangle.kv +++ b/tutorial/08-leetcode/118_pascal_triangle.kv @@ -19,29 +19,29 @@ # 1 rwfunc generate(num_rows:int64) -> () { # store prev row in dict - _ = set("/tmp/pt", 0, 1) + _ = kv.set("/tmp/pt", 0, 1) println(1) if (num_rows == 1) { } r = 1 # = 等价于 <- while (r < num_rows) { println(1) - _ = set("/tmp/cur", 0, 1) + _ = kv.set("/tmp/cur", 0, 1) i = 1 # = 等价于 <- while (i < r) { /tmp/pt[i-1] -> a /tmp/pt[i] -> b v <- a + b println(v) - _ = set("/tmp/cur", i, v) + _ = kv.set("/tmp/cur", i, v) i + 1 -> i } println(1) - _ = set("/tmp/cur", r, 1) + _ = kv.set("/tmp/cur", r, 1) # copy cur to pt k = 0 # = 等价于 <- while (k <= r) { /tmp/cur[k] -> val - _ = set("/tmp/pt", k, val) + _ = kv.set("/tmp/pt", k, val) k + 1 -> k } r + 1 -> r diff --git a/tutorial/08-leetcode/217_contains_dup_hash.kv b/tutorial/08-leetcode/217_contains_dup_hash.kv index 3680b425..6cdc9a93 100644 --- a/tutorial/08-leetcode/217_contains_dup_hash.kv +++ b/tutorial/08-leetcode/217_contains_dup_hash.kv @@ -10,13 +10,13 @@ rwfunc has_dup(a:[]int64) -> () { i = 0 # = 等价于 <- while (i < n) { a[i] -> x - has(seen, x) -> exists + kv.get(seen, x) != None -> exists if (exists) { true -> found i <- n } else { - _ = set(seen, x, 1) # = 等价于 <- + _ = kv.set(seen, x, 1) # = 等价于 <- i + 1 -> i } } @@ -37,13 +37,13 @@ rwfunc no_dup() -> () { i = 0 # = 等价于 <- while (i < n) { a[i] -> x - has(seen, x) -> exists + kv.get(seen, x) != None -> exists if (exists) { true -> found i <- n } else { - _ = set(seen, x, 1) # = 等价于 <- + _ = kv.set(seen, x, 1) # = 等价于 <- i + 1 -> i } } diff --git a/tutorial/08-leetcode/219_contains_dup_ii.kv b/tutorial/08-leetcode/219_contains_dup_ii.kv index 5a480fd4..198b374c 100644 --- a/tutorial/08-leetcode/219_contains_dup_ii.kv +++ b/tutorial/08-leetcode/219_contains_dup_ii.kv @@ -10,7 +10,7 @@ rwfunc near_dup(nums:[]int64, k:int64) -> () { 0 -> i while (i < n) { nums[i] -> x - has(h, x) -> exists + kv.get(h, x) != None -> exists if (exists) { h.*x -> j @@ -24,7 +24,7 @@ rwfunc near_dup(nums:[]int64, k:int64) -> () { } } if (found == false) { - set(h, x, i + 1) -> _ + kv.set(h, x, i + 1) -> _ } i <- i + 1 } diff --git a/tutorial/08-leetcode/234_palindrome_list.kv b/tutorial/08-leetcode/234_palindrome_list.kv index b2dc7bf7..aa4a2ea8 100644 --- a/tutorial/08-leetcode/234_palindrome_list.kv +++ b/tutorial/08-leetcode/234_palindrome_list.kv @@ -9,7 +9,7 @@ rwfunc is_pal(head:[]char/utf32) -> () { head -> p while (p != "") { p.val -> v - set(arr, n, v) -> _ + kv.set(arr, n, v) -> _ n <- n + 1 p.next -> p } diff --git a/tutorial/08-leetcode/242_valid_anagram.kv b/tutorial/08-leetcode/242_valid_anagram.kv index 11b3ecd7..b0a520b5 100644 --- a/tutorial/08-leetcode/242_valid_anagram.kv +++ b/tutorial/08-leetcode/242_valid_anagram.kv @@ -15,7 +15,7 @@ rwfunc is_anagram(s:[]char/utf32, t:[]char/utf32) -> () { 0 -> i while (i < ns) { string.char(s, i) -> c - has(h, c) -> exists + kv.get(h, c) != None -> exists if (exists) { h.*c + 1 -> h.*c } else { @@ -26,7 +26,7 @@ rwfunc is_anagram(s:[]char/utf32, t:[]char/utf32) -> () { 0 -> i while (i < nt) { string.char(t, i) -> c - has(h, c) -> exists + kv.get(h, c) != None -> exists if (exists) { h.*c -> v v - 1 -> h.*c diff --git a/tutorial/error_cases/array_param/mutate_correct.kv b/tutorial/error_cases/array_param/mutate_correct.kv index 98bc83e2..e06e9c2d 100644 --- a/tutorial/error_cases/array_param/mutate_correct.kv +++ b/tutorial/error_cases/array_param/mutate_correct.kv @@ -3,14 +3,14 @@ # has no type annotation rwfunc mutate() -> (a) { arr:[]int64 = [10, 20] - 99 -> set(arr, 0, 99) # set 返回修改后的数组 - 1 -> set(arr, 1, 1) + 99 -> xv.set(arr, 0, 99) # set 返回修改后的数组 + 1 -> xv.set(arr, 1, 1) arr -> a # 返回修改后的数组给调用方 } rwfunc init() -> () { mutate() -> result - at(result, 0) -> v0 - at(result, 1) -> v1 + xv.at(result, 0) -> v0 + xv.at(result, 1) -> v1 println(v0, v1) # 99 1 } diff --git a/tutorial/error_cases/array_param/mutate_literal.kv b/tutorial/error_cases/array_param/mutate_literal.kv index 6bcfb787..b915d8ff 100644 --- a/tutorial/error_cases/array_param/mutate_literal.kv +++ b/tutorial/error_cases/array_param/mutate_literal.kv @@ -4,7 +4,7 @@ # kvlang 铁律:读参只读。要修改数组,必须放写参位置。 # 正确: rwfunc mutate() -> (a) { array(10,20) -> a; 99 -> a[0]; ... } rwfunc mutate(a) -> () { - 99 -> a[0] # ❌ a 是读参,a[0] 展开为 set(a,0,99) -> a,写入读参槽被拒绝 + 99 -> a[0] # ❌ a 是读参,a[0] 展开为 kv.set(a,0,99) -> a,写入读参槽被拒绝 } a:[]int64 = [10, 20] diff --git a/tutorial/error_cases/index_error/at_oob.kv b/tutorial/error_cases/index_error/at_oob.kv index 45a31953..7d2eaf42 100644 --- a/tutorial/error_cases/index_error/at_oob.kv +++ b/tutorial/error_cases/index_error/at_oob.kv @@ -1,3 +1,3 @@ # expected: # error: KeyError: kv.at: key not found -kv.at([1, 2], 5) -> _ +kv.get([1, 2], 5) -> _ diff --git a/tutorial/error_cases/index_error/typed_array_string_key.kv b/tutorial/error_cases/index_error/typed_array_string_key.kv index b668a685..d8754f97 100644 --- a/tutorial/error_cases/index_error/typed_array_string_key.kv +++ b/tutorial/error_cases/index_error/typed_array_string_key.kv @@ -1,4 +1,4 @@ # expected: # error: KeyError: kv.at: key not found arr:[]int32 = [1, 2, 3] -kv.at(arr, "key") -> _ +kv.get(arr, "key") -> _ diff --git a/tutorial/error_cases/key_error/kv_at_missing.kv b/tutorial/error_cases/key_error/kv_at_missing.kv index 3d21eaaa..69267c56 100644 --- a/tutorial/error_cases/key_error/kv_at_missing.kv +++ b/tutorial/error_cases/key_error/kv_at_missing.kv @@ -1,3 +1,3 @@ # expected: # error: KeyError: kv.at: key not found -kv.at("/x", "y") -> _ +kv.get("/x", "y") -> _