stage2 修复全局变量未完成的问题 提供错误处理解析

This commit is contained in:
zzy committed 2026-09-25 13:46:07 +08:00
1 parent c0a86ea552
commit 0b8f649818
11 files changed
+317 -18

No files matched your search

+6 -6
View File
@@ -96,15 +96,15 @@ static const char* splc1_deps[] = {
static const char* splc2_deps[] = {
/* stage3 */
"stage3/ast2ir.spl",
"stage3/ast_clone.spl",
"stage3/ctvm.spl",
"stage3/eval.spl",
"stage3/intrin.spl",
"stage3/builtin.spl",
"stage3/hir.spl",
"stage3/hir2ir.spl",
"stage3/lang_type.spl",
"stage3/lower.spl",
"stage3/scope.spl",
"stage3/sema.spl",
"stage3/splc2.spl",
"stage3/splrt.spl",
"stage3/tag.spl",
/* stage2 */
"stage2/sys/exit.spl",
"stage2/sys/file.spl",
+24 -4
View File
@@ -87,12 +87,32 @@ SPL_FN u64 vm_timestamp(u64 kind) {
#define SPL_VM_EMBED_IMPLIMENTATION
#include "spl_vm_embed.h"
/* 配置层外部函数处理器(native 端)。VM 模式的实现见 spl_syscall.c。
* native 的 foreign 分发尚未接入(后续),这里先提供可链接入口。 */
SPL_FN void vm_set_foreign(void *vm, unsigned long long handler, void *ctx) {
/* 配置层外部函数处理器(native 端):把 nested VM 未解析的 native 按名分发到
* 编译器生成的 `ct_*` 函数(表由 ir2c 生成,见 stage2/ir2c.spl)。 */
extern void *spl_ct_lookup(const char *name);
typedef spl_vm_val_t (*spl_native_ct_fn_t)(void *, spl_vm_val_t, spl_vm_val_t, spl_vm_val_t);
SPL_FN spl_vm_val_t spl_native_foreign(spl_vm_t *vm, const spl_vm_call_t *call, void *user) {
void *fn;
spl_vm_val_t a0 = 0, a1 = 0;
(void)vm;
if (!call)
return 0;
fn = spl_ct_lookup(call->name);
if (!fn)
return 0;
if (call->nargs > 0)
a0 = call->args[0];
if (call->nargs > 1)
a1 = call->args[1];
/* ct_* 统一形状:返回 u64,参数 (ctx, u64, u64);多余参数被忽略。 */
return ((spl_native_ct_fn_t)fn)(user, a0, a1, 0);
}
SPL_FN void vm_set_foreign(void *p, unsigned long long handler, void *ctx) {
(void)handler;
(void)ctx;
spl_vm_set_foreign((spl_vm_t *)p, spl_native_foreign, ctx);
}
#endif /* __SPL_RT_H__ */
+1 -4
View File
@@ -1674,11 +1674,8 @@ L_SPL_STORE:
spl_vm_val_t _v = POP();
spl_vm_val_t _off = POP();
void *_addr = spl_vm_xlate(vm, _off, spl_type_size(ins->type));
if (!_addr) {
fprintf(stderr, "vm: STORE bad off=%zu sz=%d\n", (size_t)_off, (int)spl_type_size(ins->type));
spl_vm_backtrace(vm, vm->fp);
if (!_addr)
VM_ERROR("STORE: invalid address");
}
memcpy(_addr, &_v, spl_type_size(ins->type));
DISPATCH();
}
+13
View File
@@ -50,6 +50,7 @@ type Body = enum {
StatementBreak: struct { label: usize, expr: usize },
StatementContinue: struct { label: usize },
StatementDefer: struct { body: usize },
StatementErrdefer: struct { body: usize }, // errdefer:仅错误退出时执行
StatementComptime: struct { body: usize },
StatementDecl: struct { decl: usize },
StatementExpr: struct { expr: usize },
@@ -87,6 +88,8 @@ type Body = enum {
},
ExprAggregateLit: struct { name: ByteView, fields: Vec },
ExprComptimeCall: struct { name: ByteView, args: Vec },
ExprTry: struct { value: usize }, // `try expr`
ExprCatch: struct { value: usize, pattern: usize, handler: usize }, // `expr catch <pat> { }`
ExprBlock: struct { stmts: Vec, tail_expr: usize },
ExprIf: struct { cond: usize, then_block: usize, else_branch: usize },
ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize },
@@ -144,6 +147,7 @@ fn kind_name(body: Body): []u8 {
.StatementBreak(_) => { ret "statement_break"; },
.StatementContinue(_) => { ret "statement_continue"; },
.StatementDefer(_) => { ret "statement_defer"; },
.StatementErrdefer(_) => { ret "statement_errdefer"; },
.StatementComptime(_) => { ret "statement_comptime"; },
.ExprComptime(_) => { ret "expr_comptime"; },
.StatementDecl(_) => { ret "statement_decl"; },
@@ -171,6 +175,8 @@ fn kind_name(body: Body): []u8 {
.ExprArrayLit(_) => { ret "array_literal"; },
.ExprAggregateLit(_) => { ret "aggregate_literal"; },
.ExprComptimeCall(_) => { ret "comptime_call"; },
.ExprTry(_) => { ret "try_expr"; },
.ExprCatch(_) => { ret "catch_expr"; },
.ExprComptimeInit(_) => { ret "comptime_init"; },
.ExprBlock(_) => { ret "block"; },
.ExprIf(_) => { ret "if_expr"; },
@@ -466,6 +472,7 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
},
.StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); },
.StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
.StatementErrdefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
.StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
.ExprComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
.StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); },
@@ -519,6 +526,12 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
},
.ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); },
.ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); },
.ExprTry(t2) => { ok = dump_opt(ast, buf, stack, t2.value, true); },
.ExprCatch(c2) => {
ok = dump_opt(ast, buf, stack, c2.value, false) &&
dump_opt(ast, buf, stack, c2.pattern, false) &&
dump_opt(ast, buf, stack, c2.handler, true);
},
.ExprComptimeInit(x) => { ok = dump_list(ast, buf, stack, x.fields); },
.ExprBlock(b) => {
ok = dump_list(ast, buf, stack, b.stmts) &&
+78
View File
@@ -44,6 +44,7 @@ sema: *Sema,
ir: *IrBuilder,
ast: *Ast,
def2ir_map: Map,
glob_gidx: Map, // def_id -> gidx+1(全局变量)
loop_stack: Vec, // LoopCtx
defer_stack: Vec, // ast node ref
cur_ret_lang: usize,
@@ -105,6 +106,62 @@ fn ir_map_put(self: *Ast2Ir, def_id: usize, val: usize) {
match Map.put(&self.def2ir_map, &def_id as *u8, &val as *u8) { .Ok => {}, .Err(e) => {} }
}
fn glob_get(self: *Ast2Ir, def_id: usize, out: *usize): bool {
match Map.get(&self.glob_gidx, &def_id as *u8, out as *u8) {
.Found => { ret true; },
.NotFound => { ret false; },
}
ret false;
}
fn glob_put(self: *Ast2Ir, def_id: usize, val: usize) {
match Map.put(&self.glob_gidx, &def_id as *u8, &val as *u8) { .Ok => {}, .Err(e) => {} }
}
// 顶层 var/const → 模块全局(alloc 于静态存储;引用是 *T 指针常量)。
fn emit_global_var(self: *Ast2Ir, d: *Node) {
if d == null || d.resolved_def_id == 0 { ret; }
var def_id = d.resolved_def_id;
var existing: usize = 0;
if glob_get(self, def_id, &existing) { ret; }
var lang = LangType.def_lang_id(&self.sema.types, def_id);
var ir = to_ir_type(self, lang);
if ir == 0 { ret; }
var init_ref: usize = 0;
match d.body {
.VarDecl(x) => { init_ref = x.init_expr; },
.ConstDecl(x) => { init_ref = x.init_expr; },
_ => {},
}
var zero = true;
var bytes = Vec.blank(1, 1);
if init_ref != 0 {
var inode = Ast.node(self.ast, init_ref);
if inode != null {
match inode.body {
.ExprIntLit(bi) => {
var v: u64 = 0;
match BigInt.to_u64(&v, bi.value) { .Ok => {}, .Err(e) => {} }
var bits = LangType.int_bits(&self.sema.types, ir);
var size = bits / 8;
if size != 0 {
zero = false;
var i: usize = 0;
while i < size {
var b = ((v >> (8 * i)) & 255) as u8;
match Vec.push(&bytes, &b as *u8) { .Ok => {}, .Err(e) => {} }
i = i + 1;
}
}
},
_ => {},
}
}
}
var gidx = IrBuilder.add_global(self.ir, ir, zero, bytes);
glob_put(self, def_id, gidx + 1);
}
fn kind_is(self: *Ast2Ir, lang_id: usize, want: LangKind): bool {
var e = ltn(self, lang_id);
if e == null { ret false; }
@@ -608,6 +665,13 @@ fn emit_lvalue(self: *Ast2Ir, ref: usize): usize {
.ExprIdent(x) => {
var d = defn(self, n.resolved_def_id);
if d != null && @enum2int(d.kind) == @enum2int(DefKind.Fn) { ret 0; }
var g: usize = 0;
if glob_get(self, n.resolved_def_id, &g) {
var lang = node_lang(n);
var ir = to_ir_type(self, lang);
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, ir);
ret IrBuilder.type_const_global(self.ir, ptr_ir, g - 1);
}
var slot: usize = 0;
if !ir_map_get(self, n.resolved_def_id, &slot) { ret 0; }
ret slot;
@@ -935,6 +999,13 @@ fn emit_ident(self: *Ast2Ir, n: *Node): usize {
if !ir_map_get(self, n.resolved_def_id, &fn_ref) { ret 0; }
ret IrBuilder.type_const_fn(self.ir, node_ir(self, n), fn_ref);
}
var g: usize = 0;
if glob_get(self, n.resolved_def_id, &g) {
var ir = node_ir(self, n);
var ptr_ir = TypeTable.make_ptr(&self.sema.types.ir, ir);
var ptr = IrBuilder.type_const_global(self.ir, ptr_ir, g - 1);
ret IrBuilder.mem_load(self.ir, ir, ptr);
}
var slot: usize = 0;
if !ir_map_get(self, n.resolved_def_id, &slot) { ret 0; }
ret IrBuilder.mem_load(self.ir, node_ir(self, n), slot);
@@ -1961,6 +2032,8 @@ fn emit_container_reserve(self: *Ast2Ir, ref: usize) {
}
},
.TopLevelBlock(x) => { emit_container_reserve(self, dref); },
.VarDecl(_) => { emit_global_var(self, d); },
.ConstDecl(_) => { emit_global_var(self, d); },
_ => {},
}
}
@@ -1997,6 +2070,8 @@ fn emit_container_body(self: *Ast2Ir, ref: usize) {
}
},
.TopLevelBlock(x) => { emit_container_body(self, dref); },
.VarDecl(_) => {},
.ConstDecl(_) => {},
_ => {},
}
}
@@ -2070,6 +2145,8 @@ fn new(builder: *IrBuilder, sema: *Sema, diag: *Diag): Ast2Ir {
.ast = sema.ast,
.def2ir_map = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
hash_kind_usize()),
.glob_gidx = Map.new(@sizeof(usize), @alignof(usize), @sizeof(usize), @alignof(usize),
hash_kind_usize()),
.loop_stack = Vec.blank(@sizeof(LoopCtx), @alignof(LoopCtx)),
.defer_stack = Vec.blank(@sizeof(usize), @alignof(usize)),
.cur_ret_lang = 0,
@@ -2089,6 +2166,7 @@ fn drop(self: *Ast2Ir) {
Vec.drop(&self.loop_stack);
Vec.drop(&self.defer_stack);
Map.drop(&self.def2ir_map);
Map.drop(&self.glob_gidx);
var k: usize = 0;
while k < self.owned.len {
match Vec.get(&self.owned, k) {
+37 -1
View File
@@ -82,6 +82,14 @@ type ConstLit = enum {
Float: f64,
Char: u8,
Fn: usize,
Global: usize, // 全局变量下标(Ir.globals);值 = 指向该全局的 *T
}
// 模块级全局变量:编译期分配的静态存储,引用时是一个 *T 指针常量(同 LLVM GlobalVariable)。
type Global = struct {
tid: usize,
zero: bool, // true = 零初始化(BSS,不携带字节)
data: Vec, // u8 初始字节(zero=false 时使用)
}
type IrBody = enum {
@@ -128,6 +136,7 @@ type IrFunc = struct {
type Ir = @self;
funcs: Vec, // IrFunc
globals: Vec, // Global
// ---- 生命周期 ----
@@ -141,7 +150,8 @@ fn new_entry(): IrEntry {
}
fn new(): Ir {
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)) };
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)),
.globals = Vec.blank(@sizeof(Global), @alignof(Global)) };
var sentinel = IrFunc {
.name = ByteView.empty(),
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
@@ -180,6 +190,25 @@ fn drop(self: *Ir) {
i = i + 1;
}
Vec.drop(&self.funcs);
var g: usize = 0;
while g < self.globals.len {
var ge = (self.globals.data as *Global) + g;
Vec.drop(&ge.data);
g = g + 1;
}
Vec.drop(&self.globals);
}
// 追加一个全局变量;返回其下标(gidx)。bytes 的所有权转移进表。
fn add_global(self: *Ir, tid: usize, zero: bool, bytes: Vec): usize {
var g = Global { .tid = tid, .zero = zero, .data = bytes };
match Vec.push(&self.globals, &g as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
ret self.globals.len - 1;
}
fn global_at(self: *Ir, gidx: usize): *Global {
if gidx >= self.globals.len { ret null; }
ret (self.globals.data as *Global) + gidx;
}
fn alloc_fn(self: *Ir): usize {
@@ -394,6 +423,13 @@ fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
},
.Const(c) => {
if !dump_type(out, ty, c.tid) { ret false; }
match c.lit {
.Global(g) => {
if !out_str(out, " global#") { ret false; }
ret out_u(out, g);
},
_ => {},
}
var te = TypeTable.node(ty, c.tid);
var is_float = false;
var is_fn = false;
+57 -1
View File
@@ -18,7 +18,7 @@ type .{ .Layout } = @import("./layout.spl");
type AttrM = @import("./attr.spl");
type .{ .Attr } = @import("./attr.spl");
type IrM = @import("./ir.spl");
type .{ .Ir, .IrFunc, .IrEntry } = @import("./ir.spl");
type .{ .Ir, .IrFunc, .IrEntry, .Global } = @import("./ir.spl");
type Ir2C = @self;
@@ -570,6 +570,7 @@ fn emit_const(self: *Ir2C, ref: usize, n: *IrEntry) {
}
},
.Ptr(v) => { put(self, "(void*)"); put_u(self, v); },
.Global(g) => { put(self, "(void*)spl_g_"); put_u(self, g); },
.Float(v) => { put(self, "0.0"); },
.Fn(fid) => { put(self, "(void*)&"); c_fn_name(self, fid); },
}
@@ -1240,6 +1241,32 @@ fn find_main(self: *Ir2C): usize {
fn run(self: *Ir2C) {
put(self, "#include \"spl_rt.h\"\n");
// 模块级全局变量(Zero → BSS:无初始化式)。
var gi: usize = 0;
while gi < self.ir.globals.len {
var g = (self.ir.globals.data as *Global) + gi;
var sz = type_size(self, g.tid);
if sz == 0 { sz = 1; }
put(self, "static unsigned char spl_g_");
put_u(self, gi);
put(self, "[");
put_u(self, sz);
put(self, "]");
if !g.zero {
put(self, " = {");
var k: usize = 0;
while k < g.data.len {
if k > 0 { put(self, ", "); }
put_u(self, ((g.data.data as *u8) + k).* as usize);
k = k + 1;
}
if g.data.len == 0 { put(self, "0"); }
put(self, "}");
}
put(self, ";\n");
gi = gi + 1;
}
var i: usize = 1;
while i < self.ir.funcs.len {
var f = IrM.func(self.ir, i);
@@ -1256,6 +1283,7 @@ fn run(self: *Ir2C) {
}
i = i + 1;
}
emit_ct_table(self);
var m = find_main(self);
if m != 0 {
var f = IrM.func(self.ir, m);
@@ -1283,6 +1311,34 @@ fn run(self: *Ir2C) {
}
}
// compiler function(`ct_` 前缀)回调表:供 native 端 vm_set_foreign 按名分发。
fn emit_ct_table(self: *Ir2C) {
put(self, "static const struct { const char *name; void *fn; } spl_ct_tab[] = {\n");
var n: usize = 0;
var i: usize = 1;
while i < self.ir.funcs.len {
var f = IrM.func(self.ir, i);
if f != null && !fn_is_extern(self, f) &&
ByteView.starts_with(f.name, ByteView.from_slice("ct_")) {
put(self, " {\"");
put_bv(self, f.name);
put(self, "\", (void*)f");
put_u(self, i);
put(self, "},\n");
n = n + 1;
}
i = i + 1;
}
if n == 0 { put(self, " {0, 0},\n"); }
put(self, "};\n");
put(self, "void *spl_ct_lookup(const char *name) {\n");
put(self, " unsigned long i;\n");
put(self, " for (i = 0; i < sizeof(spl_ct_tab)/sizeof(spl_ct_tab[0]); i++)\n");
put(self, " if (spl_ct_tab[i].name && strcmp(spl_ct_tab[i].name, name) == 0) return spl_ct_tab[i].fn;\n");
put(self, " return 0;\n");
put(self, "}\n");
}
fn new(ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr, out: *ByteBuffer): Ir2C {
ret Ir2C {
.ir = ir,
+37 -1
View File
@@ -16,7 +16,7 @@ type .{ .Layout } = @import("./layout.spl");
type AttrM = @import("./attr.spl");
type .{ .Attr, .AttrArg } = @import("./attr.spl");
type IrM = @import("./ir.spl");
type .{ .Ir, .IrFunc, .IrEntry } = @import("./ir.spl");
type .{ .Ir, .IrFunc, .IrEntry, .Global } = @import("./ir.spl");
type Mcode = @import("./mcode.spl");
type .{ .Prog, .Op, .VmKind, .VmIns, .VmFunc } = @import("./mcode.spl");
type Mem = @import("sys/mem.spl");
@@ -28,6 +28,7 @@ ty: *TypeTable,
attr: *Attr,
layout: *Layout,
extern_native: Vec, // isize
global_gaddr: Vec, // usize:每个全局变量在 prog.gdata 的下标
prog: Prog,
// ---- 类型/布局查询 ----
@@ -589,6 +590,23 @@ fn cg_const(cg: *Cgen, ref: usize, n: *IrEntry) {
var te = TypeTable.node(ctx.ty, tid);
if te == null { ret; }
// 全局变量引用:Const{ lit = Global(gidx) } → GADDR(该全局的运行时地址)。
match n.body {
.Const(c) => {
match c.lit {
.Global(g) => {
var di: usize = 0;
if g < ctx.global_gaddr.len { di = (ctx.global_gaddr.data as *usize)[g]; }
emit(cg, .Gaddr, .Void, di);
cg_store_slot(cg, ref, .Ptr);
ret;
},
_ => {},
}
},
_ => {},
}
match te.node {
.Fn(f) => {
var cref = const_fn(n);
@@ -1213,6 +1231,7 @@ fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) {
ctx.attr = attr;
ctx.layout = layout;
ctx.extern_native = Vec.blank(@sizeof(isize), @alignof(isize));
ctx.global_gaddr = Vec.blank(@sizeof(usize), @alignof(usize));
match Mcode.new() {
.Ok(v) => { ctx.prog = v; },
@@ -1223,6 +1242,7 @@ fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) {
fn drop(ctx: *Ir2Vm) {
Mcode.drop(&ctx.prog);
Vec.drop(&ctx.extern_native);
Vec.drop(&ctx.global_gaddr);
}
fn run_func(ctx: *Ir2Vm, ref: usize): i32 {
@@ -1253,6 +1273,22 @@ fn run(ctx: *Ir2Vm): i32 {
i = i + 1;
}
// 登记全局变量到 prog.gdata(Zero→零填/BSS,Bytes→拷贝),记下运行时下标。
var gi: usize = 0;
while gi < ctx.ir.globals.len {
var g = (ctx.ir.globals.data as *Global) + gi;
var gsize = ir_type_size(ctx, g.tid);
var didx: usize = 0;
if g.zero {
didx = Mcode.add_zero_data(&ctx.prog, gsize);
} else {
didx = Mcode.add_data(&ctx.prog, g.data.data as usize, gsize);
}
var dv = didx;
match Vec.push(&ctx.global_gaddr, &dv as *u8) { .Ok => {}, .Err(error) => {} }
gi = gi + 1;
}
// 哨兵:保证真实函数入口地址非零。
ignore_usize(Mcode.add_instr(&ctx.prog, .Halt, .Void, 0));
+6
View File
@@ -285,6 +285,12 @@ fn type_const_float(self: *IrBuilder, tid: usize, val: f64): usize { ret const_n
fn type_const_char(self: *IrBuilder, tid: usize, val: u8): usize { ret const_node(self, tid, .Char(val)); }
fn type_const_fn(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Fn(val)); }
fn type_const_bytes(self: *IrBuilder, tid: usize, data: usize): usize { ret const_node(self, tid, .Ptr(data)); }
fn type_const_global(self: *IrBuilder, tid: usize, gidx: usize): usize { ret const_node(self, tid, .Global(gidx)); }
// 追加模块级全局变量;返回 gidx。bytes 所有权转移进 Ir。
fn add_global(self: *IrBuilder, tid: usize, zero: bool, bytes: Vec): usize {
ret Ir.add_global(&self.ir, tid, zero, bytes);
}
// ---- type ops ----
+20
View File
@@ -314,6 +314,26 @@ fn add_data(self: *Prog, ptr: usize, size: usize): usize {
ret self.gdata.len - 1;
}
// 追加一条 size 字节的零初始化全局数据(BSS 语义);返回其索引。
fn add_zero_data(self: *Prog, size: usize): usize {
var entry = ByteBuffer.new();
match ByteBuffer.reserve(&entry, size) {
.Ok => {},
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
}
if size != 0 {
entry.storage.len = size;
var p = entry.storage.data as *u8;
var i: usize = 0;
while i < size { p[i] = 0; i = i + 1; }
}
match Vec.push(&self.gdata, &entry as *u8) {
.Ok => {},
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
}
ret self.gdata.len - 1;
}
// 返回 name 对应的 native 索引;不存在则登记。name 为空指针时返回 -1。
fn get_or_add_native(self: *Prog, name: ByteView): isize {
if name.ptr == null { ret -1; }
+38 -1
View File
@@ -282,6 +282,7 @@ fn can_start_expr(kind: TokenKind): bool {
.For => { ret true; },
.Match => { ret true; },
.Comptime => { ret true; },
.Try => { ret true; },
_ => { ret false; },
}
},
@@ -321,6 +322,7 @@ fn can_start_statement(kind: TokenKind): bool {
.Break => { ret true; },
.Continue => { ret true; },
.Defer => { ret true; },
.Errdefer => { ret true; },
.Var => { ret true; },
.Const => { ret true; },
.Type => { ret true; },
@@ -1237,7 +1239,19 @@ fn parse_expr_prec(self: *Parser, min_prec: i32): usize {
}
fn parse_expr(self: *Parser): usize {
ret parse_expr_prec(self, 1);
var e = parse_expr_prec(self, 1);
// `expr catch <pattern> <block>`:catch 用解包语法绑定错误。
while !self.failed {
var t = peek(self);
if t == null { break; }
if !kind_is_keyword(t.kind, .Catch) { break; }
advance(self);
var pat = parse_pattern(self);
if pat == 0 { ret e; }
var handler = parse_block(self);
e = new_node(self, .ExprCatch(.{ .value = e, .pattern = pat, .handler = handler }), t);
}
ret e;
}
fn parse_cast(self: *Parser): usize {
@@ -1444,6 +1458,12 @@ fn parse_primary(self: *Parser): usize {
if @enum2int(k) == @enum2int(Keyword.Loop) { ret parse_loop_expr(self); }
if @enum2int(k) == @enum2int(Keyword.For) { ret parse_for_expr(self); }
if @enum2int(k) == @enum2int(Keyword.Match) { ret parse_match_expr(self); }
// `try <expr>`:错误上抛前缀。
if @enum2int(k) == @enum2int(Keyword.Try) {
advance(self);
var tv = parse_expr(self);
ret new_node(self, .ExprTry(.{ .value = tv }), t);
}
// `comptime <expr>`:编译期表达式(可调用普通函数、返回类型等)。
if @enum2int(k) == @enum2int(Keyword.Comptime) {
advance(self);
@@ -1958,6 +1978,22 @@ fn parse_defer_stmt(self: *Parser): usize {
ret new_node(self, .StatementDefer(.{ .body = body }), kw);
}
fn parse_errdefer_stmt(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Errdefer) { ret 0; }
advance(self);
var body: usize = 0;
var t = peek(self);
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
else if t != null && can_start_statement(t.kind) { body = parse_statement(self); }
else {
body = parse_expr(self);
t = peek(self);
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
}
ret new_node(self, .StatementErrdefer(.{ .body = body }), kw);
}
fn parse_comptime_stmt(self: *Parser): usize {
var kw = peek(self);
if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; }
@@ -1979,6 +2015,7 @@ fn parse_statement(self: *Parser): usize {
if @enum2int(k) == @enum2int(Keyword.Break) { ret parse_break_stmt(self); }
if @enum2int(k) == @enum2int(Keyword.Continue) { ret parse_continue_stmt(self); }
if @enum2int(k) == @enum2int(Keyword.Defer) { ret parse_defer_stmt(self); }
if @enum2int(k) == @enum2int(Keyword.Errdefer) { ret parse_errdefer_stmt(self); }
if @enum2int(k) == @enum2int(Keyword.Comptime) { ret parse_comptime_stmt(self); }
if @enum2int(k) == @enum2int(Keyword.Var) ||
@enum2int(k) == @enum2int(Keyword.Const) ||