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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions layout/src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
Expand All @@ -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<u8> {
// *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/") {
Expand Down
182 changes: 135 additions & 47 deletions layout/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -259,7 +263,7 @@ impl Parser {
self.expect(Kind::LBrace);
let mut body: Vec<Stmt> = 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;
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -718,15 +726,15 @@ impl Parser {
fn parse_body(&mut self) -> Vec<Stmt> {
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,保证前进性,杜绝死循环。
Expand All @@ -746,7 +754,9 @@ impl Parser {
stmts
}

fn parse_stmt(&mut self) -> Option<Stmt> {
/// 解析一条语句,返回**一或 N 条** Stmt(多赋值展开为多条指令,其余语句恒 1 条)。
/// 空 Vec = 解析不出语句(调用方据此结束块 / break)。
fn parse_stmt(&mut self) -> Vec<Stmt> {
// 块标签检测(优先级最高)。排除类型注解写槽:`x:Type`(Ident)、`x:[…]`(LBrack)、
// `x:/lib/Name`(structref langtype,以 / 起头的路径 Literal)。
let t2 = self.peek_at(2);
Expand All @@ -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(),
}
}

Expand Down Expand Up @@ -1016,31 +1026,44 @@ impl Parser {

// ── 指令级(Pratt) ────────────────────────────────────────────

fn parse_inst(&mut self) -> Option<Instruction> {
/// 解析一条「语句级指令」,返回**一或 N 条** Instruction:读槽侧写顶层逗号列表时,
/// 按位置配对 `writes`、展开为 N 条独立单赋值(见 spec「多赋值」)。个数不等 → error。
fn parse_inst(&mut self) -> Vec<Instruction> {
let mut inst = Instruction::default();
let mut multi: Vec<Instruction> = 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 {
Expand All @@ -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<Option<Expr>> {
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<Option<Expr>>,
writes: &[String],
wtypes: &[String],
arrow_left: bool,
) -> Vec<Instruction> {
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<String> {
Expand Down
16 changes: 8 additions & 8 deletions runtime/src/builtin.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions runtime/src/builtin_collection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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` 独立字节;
Expand Down
2 changes: 1 addition & 1 deletion stdlib/kvlang/spec/03-类型系统/14-容器.kv
Original file line number Diff line number Diff line change
Expand Up @@ -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。

## 锚例

Expand Down
11 changes: 6 additions & 5 deletions stdlib/kvlang/spec/03-类型系统/15-成员访问.kv
Original file line number Diff line number Diff line change
Expand Up @@ -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/类型系统/成员访问
}
Loading
Loading