diff --git a/layout/src/code.rs b/layout/src/code.rs index a1cadbad..f2adda7e 100644 --- a/layout/src/code.rs +++ b/layout/src/code.rs @@ -715,6 +715,11 @@ pub fn write_struct_decl(kv: &mut Kv, decl: &StructDecl) { pairs.push((base.clone(), kvkind::new_struct(&ftypes))); pairs.push((keytree::member(&base, ""), kvkind::new_memindex(&fnames))); for fld in &decl.fields { + // *T 字段的默认值是空指针 None——不落键。None 在 kvspace 即"无值"(键不存在), + // 故原型不带该字段、实例读回 None(见 [[ptr]]);不造"空 Ptr"这第二种空值表示。 + if fld.ty.starts_with('*') { + continue; + } pairs.push(( keytree::member(&base, &fld.name), field_default(&fld.ty, fld.default.as_ref()), @@ -726,10 +731,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 { - // *T 指针字段:默认空指针(ref=1、langtype=目标 kindexpr、body 空)。 - if let Some(target) = ty.strip_prefix('*') { - return ffi::new_ptr(target, ""); - } + // *T 指针字段不在此列——write_struct_decl 已按"空指针 = None = 不落键"跳过。 let (dims, base) = kvkind::parse_langtype(ty); let s = default.map(|e| e.val.clone()).unwrap_or_default(); if base.starts_with("char/") { diff --git a/layout/src/parser.rs b/layout/src/parser.rs index 25628417..54a96202 100644 --- a/layout/src/parser.rs +++ b/layout/src/parser.rs @@ -132,7 +132,7 @@ impl Parser { fn parse_file(&mut self) -> ast::File { let mut f = ast::File::default(); loop { - let comments = self.collect_leading_comments(); + let mut comments = self.collect_leading_comments(); if self.peek().kind == Kind::EOF { break; } @@ -203,11 +203,15 @@ impl Parser { self.advance(); } else { let prev_pos = self.pos; - let inst = self.parse_inst(); - if let Some(mut inst) = inst { - if inst.expr.is_some() { - inst.comments = comments; - f.top_level_calls.push(inst); + let insts = self.parse_inst(); + if !insts.is_empty() { + for (k, mut inst) in insts.into_iter().enumerate() { + if inst.expr.is_some() { + if k == 0 { + inst.comments = comments.clone(); + } + f.top_level_calls.push(inst); + } } } else if self.pos == prev_pos { if self.peek().kind != Kind::EOF { @@ -259,7 +263,7 @@ impl Parser { self.expect(Kind::LBrace); let mut body: Vec = Vec::new(); loop { - let comments = self.collect_leading_comments(); + let mut comments = self.collect_leading_comments(); if self.peek().kind == Kind::RBrace || self.peek().kind == Kind::EOF { break; } @@ -295,9 +299,13 @@ impl Parser { f.funcs.push(func); continue; } - match self.parse_stmt() { - Some(st) => body.push(attach_comments(st, comments)), - None => break, + let sts = self.parse_stmt(); + if sts.is_empty() { + break; + } + for st in sts { + let cs = std::mem::take(&mut comments); + body.push(attach_comments(st, cs)); } } self.expect(Kind::RBrace); @@ -718,15 +726,15 @@ impl Parser { fn parse_body(&mut self) -> Vec { let mut stmts = Vec::new(); loop { - let comments = self.collect_leading_comments(); + let mut comments = self.collect_leading_comments(); let t = self.peek(); if t.kind == Kind::RBrace || t.kind == Kind::EOF { break; } let before = self.pos; - if let Some(st) = self.parse_stmt() { - let st = attach_comments(st, comments); - stmts.push(st); + for st in self.parse_stmt() { + let cs = std::mem::take(&mut comments); + stmts.push(attach_comments(st, cs)); } // panic-mode 恢复:一轮没消费任何 token(如错误恢复后游标停在 parse_primary_expr // 不消费即返回 None 的 token 上)——报诊断并跳过一个 token,保证前进性,杜绝死循环。 @@ -746,7 +754,9 @@ impl Parser { stmts } - fn parse_stmt(&mut self) -> Option { + /// 解析一条语句,返回**一或 N 条** Stmt(多赋值展开为多条指令,其余语句恒 1 条)。 + /// 空 Vec = 解析不出语句(调用方据此结束块 / break)。 + fn parse_stmt(&mut self) -> Vec { // 块标签检测(优先级最高)。排除类型注解写槽:`x:Type`(Ident)、`x:[…]`(LBrack)、 // `x:/lib/Name`(structref langtype,以 / 起头的路径 Literal)。 let t2 = self.peek_at(2); @@ -756,27 +766,27 @@ impl Parser { && t2.kind != Kind::LBrack && !t2_structref { - return Some(self.parse_block_label()); + return vec![self.parse_block_label()]; } match self.peek().kind { - Kind::If => Some(self.parse_if()), - Kind::For => Some(self.parse_for()), - Kind::While => Some(self.parse_while()), + Kind::If => vec![self.parse_if()], + Kind::For => vec![self.parse_for()], + Kind::While => vec![self.parse_while()], Kind::Break => { self.advance(); self.eat(Kind::Newline); - Some(Stmt::Break(ast::BreakStmt { + vec![Stmt::Break(ast::BreakStmt { comments: Vec::new(), - })) + })] } Kind::Continue => { self.advance(); self.eat(Kind::Newline); - Some(Stmt::Continue(ast::ContinueStmt { + vec![Stmt::Continue(ast::ContinueStmt { comments: Vec::new(), - })) + })] } - _ => self.parse_inst().map(Stmt::Instruction), + _ => self.parse_inst().into_iter().map(Stmt::Instruction).collect(), } } @@ -1016,31 +1026,44 @@ impl Parser { // ── 指令级(Pratt) ──────────────────────────────────────────── - fn parse_inst(&mut self) -> Option { + /// 解析一条「语句级指令」,返回**一或 N 条** Instruction:读槽侧写顶层逗号列表时, + /// 按位置配对 `writes`、展开为 N 条独立单赋值(见 spec「多赋值」)。个数不等 → error。 + fn parse_inst(&mut self) -> Vec { let mut inst = Instruction::default(); + let mut multi: Vec = Vec::new(); match self.find_top_level_arrow() { Some(v) if v == "=" => { inst.arrow_left = true; let (writes, wtypes) = self.collect_writes_until_arrow(); - inst.writes = writes; - inst.write_types = wtypes; self.advance(); // consume = - inst.expr = self.parse_pratt(0); - self.dispatch_obj_by_type(&mut inst); - self.lower_array_fill(&mut inst); - self.desugar_subscript_write(&mut inst); - self.desugar_member_write(&mut inst); + let reads = self.parse_read_list(); + if reads.len() > 1 { + multi = self.expand_multi(reads, &writes, &wtypes, true); + } else { + inst.writes = writes; + inst.write_types = wtypes; + inst.expr = reads.into_iter().next().flatten(); + self.dispatch_obj_by_type(&mut inst); + self.lower_array_fill(&mut inst); + self.desugar_subscript_write(&mut inst); + self.desugar_member_write(&mut inst); + } } Some(_) => { - inst.expr = self.parse_pratt(0); + let reads = self.parse_read_list(); self.advance(); // consume -> let (writes, wtypes) = self.collect_write_list(); - inst.writes = writes; - inst.write_types = wtypes; - self.dispatch_obj_by_type(&mut inst); - self.desugar_subscript_write(&mut inst); - self.desugar_member_write(&mut inst); + if reads.len() > 1 { + multi = self.expand_multi(reads, &writes, &wtypes, false); + } else { + inst.expr = reads.into_iter().next().flatten(); + inst.writes = writes; + inst.write_types = wtypes; + self.dispatch_obj_by_type(&mut inst); + self.desugar_subscript_write(&mut inst); + self.desugar_member_write(&mut inst); + } } None => { if self.peek().kind == Kind::Ident && self.peek_at(1).kind == Kind::Colon { @@ -1058,17 +1081,82 @@ impl Parser { self.advance(); } self.eat(Kind::Newline); - self.check_write_type_match(&inst); - self.check_empty_container_typed(&inst); - // 散 key 字面量 `{...}` 仅允许作赋值右值(单一写目标);其余位置报错。 - let top_legal = inst.writes.len() == 1; - if let Some(e) = &inst.expr { - self.check_sparse_usage(e, top_legal); + + let mut out = if multi.is_empty() { vec![inst] } else { multi }; + // 逐条检查:先 mem::take 移出以避开 &mut self 与 &inst 的借用冲突。 + for k in 0..out.len() { + let i = std::mem::take(&mut out[k]); + self.check_write_type_match(&i); + self.check_empty_container_typed(&i); + // 散 key 字面量 `{...}` 仅允许作赋值右值(单一写目标);其余位置报错。 + let top_legal = i.writes.len() == 1; + if let Some(e) = &i.expr { + self.check_sparse_usage(e, top_legal); + } + out[k] = i; + } + out.retain(|i| i.expr.is_some() || !i.writes.is_empty()); + out + } + + /// 读槽侧:顶层逗号分隔的表达式列表(多赋值)。单表达式即普通赋值。 + fn parse_read_list(&mut self) -> Vec> { + let mut reads = Vec::new(); + loop { + reads.push(self.parse_pratt(0)); + if self.peek().kind == Kind::Comma { + self.advance(); + } else { + break; + } } - if inst.expr.is_none() && inst.writes.is_empty() { - return None; + reads + } + + /// 多赋值展开:reads 与 writes 按位置配对,各成一条单赋值指令;个数不等 → error。 + fn expand_multi( + &mut self, + reads: Vec>, + writes: &[String], + wtypes: &[String], + arrow_left: bool, + ) -> Vec { + if reads.len() != writes.len() { + let pos = self.peek().pos; + self.errors.push(Diagnostic { + pos, + warn: false, + info: false, + message: format!( + "multi-assignment arity mismatch: {} reads vs {} writes", + reads.len(), + writes.len() + ), + source: String::new(), + src_file: String::new(), + src_name: String::new(), + }); + return Vec::new(); } - Some(inst) + let mut out = Vec::with_capacity(reads.len()); + for (r, (w, wt)) in reads + .into_iter() + .zip(writes.iter().cloned().zip(wtypes.iter().cloned())) + { + let mut i = Instruction { + comments: Vec::new(), + expr: r, + writes: vec![w], + write_types: vec![wt], + arrow_left, + }; + self.dispatch_obj_by_type(&mut i); + self.lower_array_fill(&mut i); + self.desugar_subscript_write(&mut i); + self.desugar_member_write(&mut i); + out.push(i); + } + out } fn find_top_level_arrow(&self) -> Option { diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index 07c53b63..3fcdfde3 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -354,18 +354,18 @@ static int kvlangBuiltinCmp(kvlangFrame_t *f, cmp_op op) { bool av = kvlangScalarI64(a) != 0, bv = kvlangScalarI64(b) != 0; r = op == CMP_EQ ? av == bv : op == CMP_NEQ ? av != bv : op == CMP_LT ? av < bv : op == CMP_GT ? av > bv : op == CMP_LE ? av <= bv : av >= bv; } else if (kvlangXvalueIsPtr(&in[0]) || kvlangXvalueIsPtr(&in[1])) { - // 指针比较恒判身份(target 串)。空指针 target="";另一方须是 Ptr 或 char 哨兵。 + // 指针比较恒判身份(target 串),且只允许 Ptr 对 Ptr——空指针即 None,已在上方 + // None 分支处理;不设 char 哨兵(`p == ""` 是非法比较,正是被砍掉的歧义分支)。 if (op != CMP_EQ && op != CMP_NEQ) { kvlangBuiltinSetErr(f, "TypeError: cannot order ptr with %s", kvlangXvalueKind(&in[0])); kvlangBuiltinFreeInputs(in, n); return -1; } - if (!kvlangXvalueIsPtr(&in[0]) && !kvlangLtIsChar(a.id)) { - kvlangBuiltinSetErr(f, "TypeError: cannot compare %s with ptr", kvlangXvalueKind(&in[0])); kvlangBuiltinFreeInputs(in, n); return -1; + if (!kvlangXvalueIsPtr(&in[0]) || !kvlangXvalueIsPtr(&in[1])) { + kvlangBuiltinSetErr(f, "TypeError: cannot compare %s with ptr; use None for a null pointer", + kvlangXvalueIsPtr(&in[0]) ? kvlangXvalueKind(&in[1]) : kvlangXvalueKind(&in[0])); + kvlangBuiltinFreeInputs(in, n); return -1; } - if (!kvlangXvalueIsPtr(&in[1]) && !kvlangLtIsChar(b.id)) { - kvlangBuiltinSetErr(f, "TypeError: cannot compare %s with ptr", kvlangXvalueKind(&in[1])); kvlangBuiltinFreeInputs(in, n); return -1; - } - char *as = kvlangXvalueIsPtr(&in[0]) ? kvlangXvaluePtrTarget(&in[0]) : kvlangXvalueValueString(&in[0]); - char *bs = kvlangXvalueIsPtr(&in[1]) ? kvlangXvaluePtrTarget(&in[1]) : kvlangXvalueValueString(&in[1]); + char *as = kvlangXvaluePtrTarget(&in[0]); + char *bs = kvlangXvaluePtrTarget(&in[1]); int c = strcmp(as, bs); r = op == CMP_EQ ? c == 0 : c != 0; free(as); free(bs); diff --git a/runtime/src/builtin_collection.c b/runtime/src/builtin_collection.c index 7862e219..601e8168 100644 --- a/runtime/src/builtin_collection.c +++ b/runtime/src/builtin_collection.c @@ -568,10 +568,11 @@ int kvlangBuiltinStructNew(kvlangFrame_t *f) { const char *vk = kvlangXvalueKind(&in[i + 1]); kvspaceHead_t vh; kvlangXvalueHead(&in[i + 1], &vh); kvlangLangtype vkx; kvlangLangtypeParse(vh.langtype, &vkx); - // *T 指针字段:字段类型剥离前导 * 后与值的 langtype 比对,并校验值 ref=1。 + // *T 指针字段:字段类型剥离前导 * 后与值的 langtype 比对,并校验值 ref=1; + // None 是合法空指针(见 [[ptr]]),不算类型不符。 const char *fx = (ftype[0] == '*') ? ftype + 1 : ftype; bool type_ok = ftype[0] ? kvlangLangtypeMatch(fx, vk, vkx.ndim, vkx.dims) : true; - if (ftype[0] == '*' && vh.ref != KVSPACE_REF_PTR) + if (ftype[0] == '*' && !kvlangXvalueNone(&in[i + 1]) && vh.ref != KVSPACE_REF_PTR) type_ok = false; if (ftype[0] && !type_ok) { rc = kvlangBuiltinSetErr(f, "TypeError: field %s: expected %s, got %s", fname, ftype, vk[0] ? vk : "None"); diff --git "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" index ec468386..796a4ce3 100644 --- "a/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" +++ "b/stdlib/kvlang/spec/02-kvspace\346\250\241\345\236\213/07-\346\225\260\347\273\204\344\270\244\347\247\215\347\211\251\347\220\206\345\275\242\346\200\201.kv" @@ -52,7 +52,7 @@ stringkeymap 元素的成员名是坐标段,物理 key 形如 `m·[i]`(一 | ref | 语义 | body | |-----|------|------| | 0(inline) | 内联值 | 数据本体 | -| 1(ptr) | kvspace 内软链接 | 目标 key 路径 | +| 1(ptr) | kvspace 内指针 | 目标 key 路径 | | 2(@ext) | 扩展存储句柄 | 元存外位置描述符 | ref=2 表示值本体在扩展世界(SHM/GPU/文件),head 中 langtype 记录形态(如 `[256,256]uint8`),body diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" index 9fd6874e..2a9b28dc 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/03-\347\247\215\347\261\273-\344\273\243\347\240\201\344\270\216\347\211\271\346\256\212\345\200\274.kv" @@ -22,8 +22,9 @@ lib kvlang/spec/类型系统/种类_代码与特殊值 { - **None** 不是种类:它是 `storetype=NONE` 的空结构体 XValue。任何有名变量都有地址、都有 XValue;未 赋值即 None。None 参与算术、比较、分支条件、类型构造一律 TypeError(见下文与 [[执行模型]])。 -- **ptr(软链接)** 不是种类:它是 head 的 `ref=1`(存储位置维),head 的 storetype/langtype 描述目 - 标的完整形态,body 存目标 key 路径(见 [[容器]])。 +- **ptr(指针)** 不是种类:它是 head 的 `ref=1`(存储位置维),head 的 storetype/langtype 描述目 + 标的完整形态,body 存目标 key 路径(见 [[容器]])。**空指针就是 None**(无值),不设"空 Ptr"、也 + 不用 `""` 冒充(见 [[ptr]])。 > ptr(`ref=1`)是 head 的 ref 字段(存储位置维);kvlang **源码**类型标注最前仍可书写 `*`/`@` 前缀 (如 `*int64`、`@[256,256]uint8`),表 ref,layout 解析时剥离该前缀落成 head `ref` 独立字节; diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" index 4fd5eb8f..9f005982 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/14-\345\256\271\345\231\250.kv" @@ -5,7 +5,7 @@ lib kvlang/spec/类型系统/容器 { kvlang 的复合命名数据只有两种容器语义类型:**stringkeymap**(键-值映射)与 **struct**(异构命名成 员)。kvlang **没有** `dict` 种类,也没有 `object` 或 `scope` 种类(见 [[总体方案]])。容器物理落 storetype=index,成员经 `{key}·` memindex 兄弟槽枚举(与 lib/rwfunc 目录节点用 `/` 子路径不同)。此 -外 **ptr**(软链接)是 ref=1 的值形态,指向另一 key。 +外 **ptr**(指针)是 ref=1 的值形态,指向另一 key。 ## 锚例 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" index 8c288d50..13094e53 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/15-\346\210\220\345\221\230\350\256\277\351\227\256.kv" @@ -11,15 +11,16 @@ lib kvlang/spec/类型系统/成员访问 { 容器成员一律走 `·`(或 `kv·get`/`kv·set`),**不得**用 `[]`;`[]` 只索引 compact 数组(见 [[数组形 态]])。 -### `base·name` 的 base 解析:按值优先、按名回退 +### `base·name` 的 base 解析:按指针优先、按名回退 `base·name`(读写同规则)中 base 的解析: -1. **按值解引用**:base 持有非空字符串值(路径指针)→ 成员键 = `值(base)·name`。如 `"/n1" -> p` 后 - `p·next` 解析为 `/n1·next`。 -2. **按名回退**:base 无值(或非字符串)→ 成员键 = `解析(base)·name`;解析帧感知:裸名 → +1. **按指针解引用**:base 是 ref=1 Ptr → 成员键 = `target(base)·name`。如 `&n1 -> p` 后 + `p·next` 解析为 `/…/n1·next`。 +2. **按名回退**:base 非 Ptr(局部 struct 实例等)→ 成员键 = `解析(base)·name`;解析帧感知:裸名 → `帧根/base`,`/` 开头 → 直通。 -该规则让「局部结构体」(base 无值,按名)与「指针解引用」(base 存路径字符串,按值)共用一套语法。 +该规则让「局部结构体」(base 非 Ptr,按名)与「指针成员访问」(base 是 Ptr,按其 target)共用一套语 +法。 "#### -> /lib/kvlang/spec/类型系统/成员访问 } diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" index 0b79b1ba..0cb7bb63 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/16-ptr.kv" @@ -9,29 +9,41 @@ lib kvlang/spec/类型系统/ptr { 做单跳类型检查。 指针是**值**:读一个 `*T` 变量得到**地址值本身**(ref=1 Ptr,不解引用),解引用是**显式**的——用一元 -前缀 `*`。三者与 C 对齐: +前缀 `*`。四项与 C 对齐: | 形态 | 语义 | |------|------| | `&x` | 取地址 → ref=1 Ptr(`&x ≡ kv·abs(x)`,返回绝对路径串) | | `p`(`*T` 变量读值) | 得地址值本身(不穿透) | | `*p` | 解引用 → 读 body 目标地址的值 | +| `None` | **空指针**(唯一的空指针字面量;即键不存在=无值) | -`&` 对**裸名** `&x` 取帧槽地址(`≡ kv·abs(x)`)、对**成员链** `&t·2` 取成员路径地址(`≡ -kv·abs(t, "2")`),均产 ref=1 Ptr、body 为目标 key 路径。 +## 空指针即 None + +`*T` 的槽只有两种取值:指向某地址的 Ptr,或 **None**。None 在 kvspace 即「无值」——键不存在、读回 +None。**不设**「空 Ptr」这第二种空值表示,也不用 `""` 之类字符哨兵冒充空指针(对齐五语言:C `NULL`、 +Go `nil`、Python `None`、Rust `None`、TS `null`,无一用空字符串)。 + +- 字段默认空指针:`left:*TNode=None`;原型不落该键,实例读回 None。 +- 判定:`if (p != None) { … }`——`p == None` / `p != None` 是**唯一**的空指针判定写法。 +- `p == ""` 是**非法**比较(字符串不是指针),运行期报错。 ```kv struct Node { val:int64=0 - next:*Node="" // 指针字段:ref=1 + langtype=/lib/Node,body 存目标路径 + next:*Node=None // 空指针:原型不落该键 } -/n1 = Node{val=1} // 绝对路径建节点 -/n2 = Node{val=2 next=/n1} // next 存 /n1 绝对路径(指针) -/n2 -> p // p 为指针变量(存路径串) +/n1 = Node{val=1} +/n2 = Node{val=2 next=&/n1} // next = /n1 的地址(Ptr) +/n2 -> p // p 为指针变量 *p -> q // q = 解引用 p = n2 的实例值 p·val -> v // 成员访问经指针自动解引用:读 /n1·val +if (p != None) { … } // 空指针判定 ``` +`&` 对**裸名** `&x` 取帧槽地址(`≡ kv·abs(x)`)、对**成员链** `&t·2` 取成员路径地址(`≡ +kv·abs(t, "2")`),均产 ref=1 Ptr、body 为目标 key 路径。 + 成员访问 `p·field` 经指针**自动解引用**(对齐 Go/Rust 的 `p.field`);需要「指针指向的整值」(如指针 指向标量)时用显式 `*p`。二者不冲突:`*p` 取整值、`p·field` 取成员。 diff --git "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" index dfcdbcd5..74cad2ce 100644 --- "a/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" +++ "b/stdlib/kvlang/spec/03-\347\261\273\345\236\213\347\263\273\347\273\237/20-ref\345\255\230\345\202\250\344\275\215\347\275\256.kv" @@ -7,7 +7,7 @@ lib kvlang/spec/类型系统/ref存储位置 { | ref | body | 义 | |-----|------|----| | 0 inline | 值 raw 本体 | 就地落入 kvspace | -| 1 ptr | 目标 key 路径 | 软链接,单跳指向同型目标(head 的 storetype/langtype 即目标形态);`ptr → rwfunc` 即函数调用链 | +| 1 ptr | 目标 key 路径 | 指针,单跳指向同型目标(head 的 storetype/langtype 即目标形态);`ptr → rwfunc` 即函数调用链。**空指针即 None**(无值、不落键),非"空 Ptr"(见 [[ptr]]) | | 2 @ext | 扩展句柄/定位符 | 单个值本体在扩展世界(fs 文件 / gpu tensordata),head 存元描述 | **源码书写与 wire 的分工**:kvlang **源码**里,指针/扩展引用仍以 `*`/`@` 前缀写在类型**最前** diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" index ea48e4f6..5abd4ad9 100644 --- "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" +++ "b/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/01-\346\214\207\344\273\244\346\236\266\346\236\204.kv" @@ -64,6 +64,41 @@ layout 把每个函数体布进 kvspace 的一棵子树:**指令即路径**, `[0,·]` 只用 `->` 形态表达,没有 `=` 形式的签名(见 [[函数]])。`=` 从不改变槽位布局,仅决定同一条指 令里写槽写在源码的哪一侧。 +## 多赋值(顶层逗号列表) + +读槽侧写**顶层逗号列表**时,一次书写展开为 **N 条单赋值指令**:左右按位置一一配对,个数**必须相等**, +从左到右保序展开。 + +```kv +d·name, d·age, d·active -> dn, da, dac +``` + +等价于三条独立赋值: + +```kv +d·name -> dn +d·age -> da +d·active -> dac +``` + +`=` 形态同理:`x, y = a, b` ≡ `a -> x`、`b -> y`。 + +**与「写参扇出」的区别**(判据是**读槽侧顶层是否含逗号**): + +| 形态 | 含义 | 指令数 | +|------|------|--------| +| `a + b -> sum, backup` | **扇出**:一个表达式写多个位置 | 1 条(多写槽) | +| `x, y -> sum, backup` | **多赋值**:多个表达式各写一个位置 | 2 条 | + +`f(a, b) -> x, y` 的逗号在**括号内**(仍是单个表达式),故为扇出、非多赋值。 + +- **个数不等是 layout 错误**(左 2 右 3 报 `multi-assignment arity mismatch`),不静默截断、不补 None。 +- 写槽写 `_` 即丢弃该位置(与多写参调用的 `_` 同义,落帧内 `_` 槽):`_, a, b = 1, 2, 3` ≡ + `1 -> _`、`2 -> a`、`3 -> b`。 +- 展开是纯语法糖:不引入新 opcode,落 kvspace 后与逐条书写**逐字节等价**;dump 回来即 N 条单赋值(不保 + 留原书写形态)。 +- 中间含函数调用时按 lower 的常规嵌套调用摊平(临时槽 `_N`),展开保序见 [[控制流脱糖]]。 + **写槽必须是位置(location)**:裸名(帧内变量)、绝对路径(`/abs`)、成员写(`base·field` / `base·*key`)、下标写(`arr[idx]`)。字面量出现在写槽位置是错误,layout 报诊断(见 [[诊断]])。 "#### -> /lib/kvlang/spec/layout语义/指令架构 diff --git "a/stdlib/kvlang/spec/04-layout\350\257\255\344\271\211/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 fb20d557..ee094c2d 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" @@ -28,8 +28,8 @@ kvlang 源码只有一种命名单元:**rwfunc**(有指令体的用户函数 每个读参与写参**必须**声明类型(`name:type_expr`,type_expr 见 [[langtype签名类型表达式]])。 type_expr 最前可写 `*`/`@` 前缀(如 `*int64`、`@[256,256]uint8`)表示 ptr/ext 存储位置;layout 解析 -时剥离该前缀并落成 head `ref` 字节,wire 层 langtype 串不含前缀(见 [[总体方案]])。缺类型标注是错 -误: +时剥离该前缀并落成 head `ref` 字节,wire 层 langtype 串不含前缀(见 [[总体方案]])。`*T` 参数可接受 +**None**(空指针),判定恒写 `p != None`(见 [[ptr]])。缺类型标注是错误: ``` error: func f: param "x" has no type annotation — every parameter must declare its type diff --git "a/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" "b/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" index 03ce76f5..0682ffc8 100644 --- "a/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" +++ "b/stdlib/kvlang/spec/05-runtime\350\257\255\344\271\211/03-\346\210\220\345\221\230\350\256\277\351\227\256.kv" @@ -27,17 +27,17 @@ kvlang 用两个符号访问复合数据,语义严格分离,runtime **不得 - **写成员**:`rec·name = 值` 的写槽是完整成员键 `rec·name`,`=` 为值拷贝。 - **读成员**:`rec·name -> v` 直接取 key `rec·name`;缺失成员的动态读(`kv·get`)返回 `None`。 -## `·` 的解引用:按值优先,按名回退 +## `·` 的解引用:按指针优先,按名回退 `base·name`(读写同规则)中 `base` 的解析在 runtime 按以下顺序确定成员键: -1. **按值解引用**:`base` 持有非空字符串值(路径指针)时,成员键 = `值(base)·name`。例:`"/n1" -> p` - 后 `p·val` 解析为 `/n1·val`。 -2. **按名回退**:`base` 无值(或非字符串)时,成员键 = `解析(base)·name`;`解析()` 帧感知——裸名 → - `帧根/base`,`/` 开头 → 直通绝对路径。 +1. **按指针解引用**:`base` 是 ref=1 Ptr(`base` 的 head `ref` 字节为 1)时,成员键 = + `target(base)·name`。例:`&n1 -> p` 后 `p·val` 解析为 `/…/n1·val`。 +2. **按名回退**:`base` 非 Ptr(局部 struct 实例等)时,成员键 = `解析(base)·name`;`解析()` 帧感知 + ——裸名 → `帧根/base`,`/` 开头 → 直通绝对路径。 -该规则让"局部结构体"与"指针解引用"共用一套语法:结构体的 `base` 保持无值(走按名),指针变量存路径字 -符串(触发按值)。跨函数共享的数据**应**放绝对路径(`/n1`),使不同帧的指针都能解引用到同一 key。 +该规则让"局部结构体"与"指针成员访问"共用一套语法:结构体的 `base` 非 Ptr(走按名),指针变量持 Ptr(按 +其 target)。跨函数共享的数据**应**放绝对路径(`/n1`),使不同帧的指针都解引用到同一 key。 ## 动态成员名 `·*` 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 f3ba048d..0b87198d 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" @@ -7,7 +7,7 @@ lib kvlang/spec/附录/类型表达式文法 { ``` type_expr = [ ref_prefix ] atom { "|" atom } (* ref 前缀可选;layout 剥离前缀落 head ref 字节,wire langtype 不含前缀;见 [[总体方案]] *) -ref_prefix = "*" | "@" (* * → ref=ptr(软链接);@ → ref=@ext(扩展世界) *) +ref_prefix = "*" | "@" (* * → ref=ptr(指针);@ → ref=@ext(扩展世界) *) atom = shape | mapexpr | structref shape = [ dims ] ( any | kind ) mapexpr = key "·" type_expr (* stringkeymap;value 可递归嵌套 *) diff --git "a/stdlib/kvlang/spec/\351\231\204\345\275\225/04-\346\214\207\344\273\244.kv" "b/stdlib/kvlang/spec/\351\231\204\345\275\225/04-\346\214\207\344\273\244.kv" index cab02746..ef2fc6ab 100644 --- "a/stdlib/kvlang/spec/\351\231\204\345\275\225/04-\346\214\207\344\273\244.kv" +++ "b/stdlib/kvlang/spec/\351\231\204\345\275\225/04-\346\214\207\344\273\244.kv" @@ -3,9 +3,11 @@ lib kvlang/spec/附录/指令 { r##"# 指令 ``` -instruction = [ writes "=" ] expr (* 左写:writes = expr *) - | expr [ "->" writes ] (* 右写:expr -> writes *) - | expr (* 纯副作用调用 *) +instruction = [ writes "=" read_list ] (* 左写:writes = read_list *) + | read_list [ "->" writes ] (* 右写:read_list -> writes *) + | expr (* 纯副作用调用 *) + +read_list = expr { "," expr } (* 顶层逗号=多赋值:展开为 N 条单赋值;单 expr 即普通赋值 *) writes = write_slot { "," write_slot } write_slot = ident (* 裸名:帧内变量 *) @@ -14,5 +16,9 @@ write_slot = ident (* 裸名:帧内变量 *) | ident "·" "*" ident (* 动态键写:base·*key *) | ident "[" expr "]" (* 下标写:仅限 "=" 形式;-> 侧不接受带下标写槽 *) ``` + +多赋值:左右逗号列表按位置配对、个数必须相等,展开为 N 条独立赋值(见 [[指令架构]])。`->` 右侧的逗 +号列表若左侧是**单个**表达式,则是写参扇出(一条指令多写槽),不是多赋值——判据是读槽侧顶层是否含逗 +号。 "## -> /lib/kvlang/spec/附录/指令 } diff --git a/tutorial/08-leetcode/002_add_two_numbers.kv b/tutorial/08-leetcode/002_add_two_numbers.kv index cd4156ff..7f2e777a 100644 --- a/tutorial/08-leetcode/002_add_two_numbers.kv +++ b/tutorial/08-leetcode/002_add_two_numbers.kv @@ -6,7 +6,7 @@ // 8 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } lib lc { @@ -16,16 +16,16 @@ rwfunc add(a:*Node, b:*Node) -> () { 0 -> ri pa = a pb = b - while (pa != "" || pb != "" || carry != 0) { + while (pa != None || pb != None || carry != 0) { sum = carry - if (pa != "") { + if (pa != None) { pa·val -> av sum + av -> sum pa = pa·next } - if (pb != "") { + if (pb != None) { pb·val -> bv bv + sum -> sum pb·next -> pb diff --git a/tutorial/08-leetcode/021_merge_two_lists.kv b/tutorial/08-leetcode/021_merge_two_lists.kv index e51e757b..6d34bf43 100644 --- a/tutorial/08-leetcode/021_merge_two_lists.kv +++ b/tutorial/08-leetcode/021_merge_two_lists.kv @@ -9,15 +9,15 @@ // 4 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc merge(a:*Node, b:*Node) -> () { pa = a pb = b - while (pa != "") { + while (pa != None) { pa·val -> av - pick_a = pb == "" + pick_a = pb == None if (pick_a) { println(av) @@ -36,7 +36,7 @@ rwfunc merge(a:*Node, b:*Node) -> () { } } } - while (pb != "") { + while (pb != None) { bv = pb·val println(bv) pb = pb·next diff --git a/tutorial/08-leetcode/083_remove_dup_list.kv b/tutorial/08-leetcode/083_remove_dup_list.kv index 03b71e6b..a227af4d 100644 --- a/tutorial/08-leetcode/083_remove_dup_list.kv +++ b/tutorial/08-leetcode/083_remove_dup_list.kv @@ -8,16 +8,16 @@ // 3 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc dedup(head:*Node) -> () { head -> cur - while (cur != "") { + while (cur != None) { cur·next -> nxt advance = true - if (nxt != "") { + if (nxt != None) { cur·val -> v nxt·val -> nv @@ -35,7 +35,7 @@ rwfunc dedup(head:*Node) -> () { rwfunc show(head:*Node) -> () { head -> p - while (p != "") { + while (p != None) { p·val -> v println(v) p·next -> p diff --git a/tutorial/08-leetcode/094_inorder_traversal.kv b/tutorial/08-leetcode/094_inorder_traversal.kv index bc78a3c7..a558445b 100644 --- a/tutorial/08-leetcode/094_inorder_traversal.kv +++ b/tutorial/08-leetcode/094_inorder_traversal.kv @@ -6,8 +6,8 @@ // 3 struct TNode { val:int64=0 - left:*TNode="" - right:*TNode="" + left:*TNode=None + right:*TNode=None } rwfunc inorder(root:*TNode) -> () { @@ -15,8 +15,8 @@ rwfunc inorder(root:*TNode) -> () { si = 0 stack:[int64]·*TNode = {} cur = root - while (cur != "" || si > 0) { - while (cur != "") { + while (cur != None || si > 0) { + while (cur != None) { _ = kv·set(stack, si, cur) si + 1 -> si cur·left -> cur diff --git a/tutorial/08-leetcode/100_same_tree.kv b/tutorial/08-leetcode/100_same_tree.kv index 8da02f29..d9a92aa5 100644 --- a/tutorial/08-leetcode/100_same_tree.kv +++ b/tutorial/08-leetcode/100_same_tree.kv @@ -5,12 +5,12 @@ // false struct TNode { val:int64=0 - left:*TNode="" - right:*TNode="" + left:*TNode=None + right:*TNode=None } rwfunc same(a:*TNode, b:*TNode) -> (ok:int64) { - if (a == "" || b == "") { + if (a == None || b == None) { if (a == b) { 1 -> ok } else { 0 -> ok } } else { if (a·val == b·val) { diff --git a/tutorial/08-leetcode/104_max_tree_depth.kv b/tutorial/08-leetcode/104_max_tree_depth.kv index 14544dcc..da24daea 100644 --- a/tutorial/08-leetcode/104_max_tree_depth.kv +++ b/tutorial/08-leetcode/104_max_tree_depth.kv @@ -4,12 +4,12 @@ // 3 struct TNode { val:int64=0 - left:*TNode="" - right:*TNode="" + left:*TNode=None + right:*TNode=None } rwfunc max_depth(root:*TNode) -> (depth:int64) { - if (root == "") { 0 -> depth } + if (root == None) { 0 -> depth } else { ld = max_depth(root·left) rd = max_depth(root·right) diff --git a/tutorial/08-leetcode/141_linked_list_cycle.kv b/tutorial/08-leetcode/141_linked_list_cycle.kv index 073b9dd4..25259a3d 100644 --- a/tutorial/08-leetcode/141_linked_list_cycle.kv +++ b/tutorial/08-leetcode/141_linked_list_cycle.kv @@ -7,20 +7,20 @@ rwfunc has_cycle(head:*Node) -> (result:int64) { result = 0 head -> slow fast = head - while (fast != "") { + while (fast != None) { slow = slow·next fast·next -> n1 - at_end = n1 == "" + at_end = n1 == None if (at_end) { - fast = "" + fast = None } else { n1·next -> fast match = slow == fast if (match) { result = 1 - "" -> fast + None -> fast } } } @@ -28,7 +28,7 @@ rwfunc has_cycle(head:*Node) -> (result:int64) { struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc test() -> () { diff --git a/tutorial/08-leetcode/160_intersection_list.kv b/tutorial/08-leetcode/160_intersection_list.kv index 33fc8cff..f7770bc6 100644 --- a/tutorial/08-leetcode/160_intersection_list.kv +++ b/tutorial/08-leetcode/160_intersection_list.kv @@ -5,7 +5,7 @@ // 8 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc get_intersection(ha:*Node, hb:*Node) -> () { @@ -15,29 +15,29 @@ rwfunc get_intersection(ha:*Node, hb:*Node) -> () { switch_b = 0 while (pa != pb) { pa·next -> nxt - if (nxt == "") { + if (nxt == None) { if (switch_a == 0) { hb -> pa 1 -> switch_a } else { - "" -> pa + None -> pa } } else { nxt -> pa } pb·next -> nxt2 - if (nxt2 == "") { + if (nxt2 == None) { if (switch_b == 0) { ha -> pb switch_b = 1 } else { - "" -> pb + None -> pb } } else { nxt2 -> pb } } - if (pa != "") { + if (pa != None) { pa·val -> v println(v) } diff --git a/tutorial/08-leetcode/203_remove_linked_elements.kv b/tutorial/08-leetcode/203_remove_linked_elements.kv index e00c95f9..f56e9910 100644 --- a/tutorial/08-leetcode/203_remove_linked_elements.kv +++ b/tutorial/08-leetcode/203_remove_linked_elements.kv @@ -8,12 +8,12 @@ // 5 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc remove(head:*Node, val:int64) -> () { head -> p - while (p != "") { + while (p != None) { v = p·val v == val -> skip diff --git a/tutorial/08-leetcode/206_reverse_linked_list.kv b/tutorial/08-leetcode/206_reverse_linked_list.kv index 75290aed..1d0e243a 100644 --- a/tutorial/08-leetcode/206_reverse_linked_list.kv +++ b/tutorial/08-leetcode/206_reverse_linked_list.kv @@ -8,20 +8,20 @@ // 1 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc reverse(head:*Node) -> () { - prev = "" + prev = None head -> cur - while (cur != "") { + while (cur != None) { nxt = cur·next cur·next = prev cur -> prev cur = nxt } p = prev - while (p != "") { + while (p != None) { p·val -> v println(v) p = p·next diff --git a/tutorial/08-leetcode/234_palindrome_list.kv b/tutorial/08-leetcode/234_palindrome_list.kv index 1119f893..baaa3b5c 100644 --- a/tutorial/08-leetcode/234_palindrome_list.kv +++ b/tutorial/08-leetcode/234_palindrome_list.kv @@ -5,14 +5,14 @@ // false struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc is_pal(head:*Node) -> () { arr:[int64]·int64 = {} 0 -> n head -> p - while (p != "") { + while (p != None) { p·val -> v kv·set(arr, n, v) -> _ n = n + 1 diff --git a/tutorial/12-struct/00-point.kv b/tutorial/12-struct/00-point.kv index be26ae7a..41a42f27 100644 --- a/tutorial/12-struct/00-point.kv +++ b/tutorial/12-struct/00-point.kv @@ -11,11 +11,9 @@ struct Point { rwfunc test() -> () { p:Point = {x=3.0 y=4.0} - p·x -> a - p·y -> b + p·x, p·y -> a, b println("x=", a, " y=", b) q:Point = {} - q·x -> c - q·y -> d + q·x, q·y -> c, d println("default x=", c, " y=", d) } diff --git a/tutorial/12-struct/01-fields.kv b/tutorial/12-struct/01-fields.kv index 6d701e72..2044c950 100644 --- a/tutorial/12-struct/01-fields.kv +++ b/tutorial/12-struct/01-fields.kv @@ -12,13 +12,9 @@ struct User { rwfunc test() -> () { u:User = {name="alice" age=30 active=true} - u·name -> n - u·age -> a - u·active -> ac + u·name, u·age, u·active -> n, a, ac println(n, a, ac) d:User = {} - d·name -> dn - d·age -> da - d·active -> dac + d·name, d·age, d·active -> dn, da, dac println(dn, da, dac) } diff --git a/tutorial/12-struct/02-mutate.kv b/tutorial/12-struct/02-mutate.kv index 887aa3eb..96ec7f73 100644 --- a/tutorial/12-struct/02-mutate.kv +++ b/tutorial/12-struct/02-mutate.kv @@ -10,11 +10,9 @@ struct P { rwfunc test() -> () { p:P = {x=1 y=2} - p·x -> a - p·y -> b + p·x, p·y -> a, b println("before x=", a, " y=", b) 5 -> p·x - p·x -> c - p·y -> d + p·x, p·y -> c, d println("after x=", c, " y=", d) } diff --git a/tutorial/12-struct/03-linked-list.kv b/tutorial/12-struct/03-linked-list.kv index 6d7957d9..c34a6a5b 100644 --- a/tutorial/12-struct/03-linked-list.kv +++ b/tutorial/12-struct/03-linked-list.kv @@ -1,13 +1,13 @@ // linked-list: 局部 struct 节点变量 + & 取址串成链表 // 语义: 每个节点是帧内 struct 局部变量;next 存下一节点地址(&node ≡ kv·abs(node)); -// ""=尾;cur·field 按 next 路径解引用遍历。尾节点先建,&node 恒指向已存在节点。 +// None=尾;cur·field 按 next 路径解引用遍历。尾节点先建,&node 恒指向已存在节点。 // 期望输出: // 10 // 20 // 30 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc test() -> () { @@ -15,7 +15,7 @@ rwfunc test() -> () { n2:Node = {val=20 next=&n3} n1:Node = {val=10 next=&n2} cur = &n1 - while (cur != "") { + while (cur != None) { cur·val -> v println(v) cur·next -> cur diff --git a/tutorial/12-struct/04-tree.kv b/tutorial/12-struct/04-tree.kv index ba2f41a9..ec4194f1 100644 --- a/tutorial/12-struct/04-tree.kv +++ b/tutorial/12-struct/04-tree.kv @@ -1,18 +1,18 @@ -// tree: 二叉树存进 map(int 键、struct 值),左右孩子用绝对路径指针 -// 语义: /tree 是 [int64]·TNode 的 map;left/right 存孩子绝对路径(""=空); -// inorder 递归中序遍历,node 是路径串,node·field 解引用。 +// tree: 二叉树存进 map(int 键、struct 值),左右孩子用 *TNode 指针 +// 语义: /tree 是 [int64]·TNode 的 map;left/right 是 *TNode(None=空指针); +// inorder 递归中序遍历,node 是指针,node·field 经指针解引用。 // 期望输出: // 3 // 5 // 8 struct TNode { val:int64=0 - left:[]char/utf32="" - right:[]char/utf32="" + left:*TNode=None + right:*TNode=None } -rwfunc inorder(node:[]char/utf32) -> () { - if (node != "") { +rwfunc inorder(node:*TNode) -> () { + if (node != None) { node·left -> l inorder(l) node·val -> v @@ -27,7 +27,7 @@ rwfunc test() -> () { /tree·1:TNode = {val=5} /tree·2:TNode = {val=3} /tree·3:TNode = {val=8} - "/tree·2" -> /tree·1·left - "/tree·3" -> /tree·1·right - inorder("/tree·1") + &/tree·2 -> /tree·1·left + &/tree·3 -> /tree·1·right + inorder(&/tree·1) } diff --git a/tutorial/12-struct/05-graph.kv b/tutorial/12-struct/05-graph.kv index a5741948..06166edd 100644 --- a/tutorial/12-struct/05-graph.kv +++ b/tutorial/12-struct/05-graph.kv @@ -1,6 +1,6 @@ -// graph: 有向图存进 map(int 键、struct 值),邻居用绝对路径指针 -// 语义: /graph 是 [int64]·GNode 的 map;e0/e1 是两条出边的绝对路径(""=无); -// seen 标记去重;dfs 深度优先,node 是路径串,本地拷贝 n 后经 n·seen 回写。 +// graph: 有向图存进 map(int 键、struct 值),邻居用 *GNode 指针 +// 语义: /graph 是 [int64]·GNode 的 map;e0/e1 是两条出边(None=无); +// seen 标记去重;dfs 深度优先,node 是指针,本地拷贝 n 后经 n·seen 回写。 // 边: 1→2 1→3 2→3 // 期望输出: // 1 @@ -8,13 +8,13 @@ // 3 struct GNode { val:int64=0 - e0:[]char/utf32="" - e1:[]char/utf32="" + e0:*GNode=None + e1:*GNode=None seen:bool=false } -rwfunc dfs(node:[]char/utf32) -> () { - if (node != "") { +rwfunc dfs(node:*GNode) -> () { + if (node != None) { node·seen -> s if (!s) { n = node @@ -34,8 +34,8 @@ rwfunc test() -> () { /graph·1:GNode = {val=1} /graph·2:GNode = {val=2} /graph·3:GNode = {val=3} - "/graph·2" -> /graph·1·e0 - "/graph·3" -> /graph·1·e1 - "/graph·3" -> /graph·2·e0 - dfs("/graph·1") + &/graph·2 -> /graph·1·e0 + &/graph·3 -> /graph·1·e1 + &/graph·3 -> /graph·2·e0 + dfs(&/graph·1) } diff --git a/tutorial/12-struct/06-list-reverse.kv b/tutorial/12-struct/06-list-reverse.kv index ef341586..dc18f4fb 100644 --- a/tutorial/12-struct/06-list-reverse.kv +++ b/tutorial/12-struct/06-list-reverse.kv @@ -8,23 +8,23 @@ // 10 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc test() -> () { n3:Node = {val=30} n2:Node = {val=20 next=&n3} n1:Node = {val=10 next=&n2} - prev = "" + prev = None cur = &n1 - while (cur != "") { + while (cur != None) { cur·next -> nxt prev -> cur·next cur -> prev nxt -> cur } cur = prev - while (cur != "") { + while (cur != None) { cur·val -> v println(v) cur·next -> cur diff --git a/tutorial/12-struct/07-list-local.kv b/tutorial/12-struct/07-list-local.kv index 37b7135d..d59345a2 100644 --- a/tutorial/12-struct/07-list-local.kv +++ b/tutorial/12-struct/07-list-local.kv @@ -1,12 +1,12 @@ // list-local: 局部 struct 节点变量 + & 建链,遍历求和 // 语义: 节点是帧内 struct 局部变量,next 存下一节点地址(&node ≡ kv·abs(node)); -// ""=尾;尾节点先建,&node 恒指向已存在节点。遍历累加各节点 val。 +// None=尾;尾节点先建,&node 恒指向已存在节点。遍历累加各节点 val。 // 链表: 10→20→30 // 期望输出: // 60 struct Node { val:int64=0 - next:*Node="" + next:*Node=None } rwfunc test() -> () { @@ -15,7 +15,7 @@ rwfunc test() -> () { n1:Node = {val=10 next=&n2} cur = &n1 sum = 0 - while (cur != "") { + while (cur != None) { cur·val -> v sum + v -> sum cur·next -> cur