diff --git a/stage0/spl_ir.c b/stage0/spl_ir.c index d8b5484..c278ac4 100644 --- a/stage0/spl_ir.c +++ b/stage0/spl_ir.c @@ -458,6 +458,10 @@ const char *spl_type_name(spl_type_t type) { return "f32"; case SPL_F64: return "f64"; + case SPL_USIZE: + return "usize"; + case SPL_ISIZE: + return "isize"; case SPL_PTR: return "ptr"; default: diff --git a/stage0/spl_ir.h b/stage0/spl_ir.h index 4535cb4..244c6a9 100644 --- a/stage0/spl_ir.h +++ b/stage0/spl_ir.h @@ -94,7 +94,7 @@ typedef enum { X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \ /* 调试 */ \ X(SPL_BK, "breakpoint", 0, 0, 0, "break point when exec will stop run") \ - X(SPL_DBG, "dbg", 0, 1, 1, "print top of stack as hex") + X(SPL_DBG, "dbg", 0, 0, 0, "print VM debug info (stack, backtrace, locals)") /* clang-format on */ diff --git a/stage0/spl_vm.c b/stage0/spl_vm.c index 326d01c..fcf31e7 100644 --- a/stage0/spl_vm.c +++ b/stage0/spl_vm.c @@ -995,8 +995,23 @@ int spl_vm_run_once(spl_vm_t *vm) { /* ========== Debug ========== */ case SPL_DBG: { + fprintf(stderr, "---DGB: current ip %zu---\n", vm->ip); + spl_vm_dump_instr(vm, vm->ip - 6); + spl_vm_dump_instr(vm, vm->ip - 5); + spl_vm_dump_instr(vm, vm->ip - 4); + spl_vm_dump_instr(vm, vm->ip - 3); + spl_vm_dump_instr(vm, vm->ip - 2); + spl_vm_dump_instr(vm, vm->ip - 1); + spl_vm_dump_instr(vm, vm->ip); + spl_vm_dump_instr(vm, vm->ip + 1); spl_vm_backtrace(vm, vm->fp); - return 2; /* breakpoint: pause execution */ + spl_vm_stackdump(vm, vm->sp); + fprintf(stderr, "---DGB END ---\n"); + break; + } + + case SPL_BK: { + return 0; /* breakpoint: pause execution */ } default: @@ -1057,7 +1072,7 @@ void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp) { fprintf(stderr, " stack (sp=%zd, fp=%zd):\n", sp, vm->fp); spl_val_t start = sp > 16 ? sp - 16 : 0; for (spl_val_t i = start; i < sp; i++) { - fprintf(stderr, " [%zd] = 0x%016zx (%zd)\n", i, vm->stacks.data[i], vm->stacks.data[i]); + fprintf(stderr, " [%3zd] = 0x%016zx (%zd)\n", i, vm->stacks.data[i], vm->stacks.data[i]); } } @@ -1070,7 +1085,7 @@ int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp) { spl_val_t _saved_ip = vm->frames.data[i].saved_ip; spl_val_t _saved_fp = vm->frames.data[i].saved_fp; const char *_fn = func_name_by_ip(vm->prog, _saved_ip - 1); - fprintf(stderr, " [%zd] %s (fp=%zd, ip=%zd, args=%zd)\n", i, _fn, _saved_fp, _saved_ip, + fprintf(stderr, " [%3zd] %s (fp=%zd, ip=%zd, args=%zd)\n", i, _fn, _saved_fp, _saved_ip, vm->frames.data[i].nargs); } const char *_cur = func_name_by_ip(vm->prog, vm->ip); diff --git a/stage1/spl_expr.c b/stage1/spl_expr.c index eb2cf72..c3f8685 100644 --- a/stage1/spl_expr.c +++ b/stage1/spl_expr.c @@ -515,6 +515,54 @@ spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec) { case TOK_L_BRACKET: left = parse_array_literal(ctx); break; + case TOK_AT: { + /* @builtin(...) — compiler intrinsic */ + advance(ctx); /* consume @ */ + skip_nl(ctx); + tok = peek(ctx); + if (!tok || tok->type != TOK_IDENT) { + spl_comp_error(ctx, "expected builtin name after '@'"); + break; + } + char bname[256]; + usize bnlen = tok->len < 255 ? tok->len : 255; + memcpy(bname, tok->lexeme, bnlen); bname[bnlen] = '\0'; + advance(ctx); /* consume builtin name */ + + if (strcmp(bname, "dbg") == 0) { + /* @dbg(...) — print VM debug info */ + if (peek(ctx)->type == TOK_L_PAREN) { + advance(ctx); /* skip ( */ + int nargs = 0; + if (peek(ctx)->type != TOK_R_PAREN) { + for (;;) { + spl_parse_expr(ctx, PREC_MIN); + nargs++; + if (peek(ctx)->type == TOK_COMMA) { advance(ctx); continue; } + break; + } + } + expect(ctx, TOK_R_PAREN); + + /* Emit SPL_DBG and DROP for each arg */ + for (int i = 0; i < nargs; i++) { + spl_emit(ctx, SPL_DBG, SPL_USIZE, 0); + spl_emit(ctx, SPL_DROP, SPL_VOID, 0); + } + if (nargs == 0) { + spl_emit(ctx, SPL_DBG, SPL_VOID, 0); + } + } else { + /* @dbg with no parens — just debug */ + spl_emit(ctx, SPL_DBG, SPL_VOID, 0); + } + /* @dbg is a void expression */ + left = (spl_expr_result_t){ spl_type_basic(SPL_VOID), 0 }; + } else { + spl_comp_error(ctx, "unknown builtin '@%s'", bname); + } + break; + } default: /* If it's a keyword-as-type (i32, u8, etc.), parse as function call target or type constructor */ if (peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY) { diff --git a/stage1/spl_stmt.c b/stage1/spl_stmt.c index c05b0dc..a96f570 100644 --- a/stage1/spl_stmt.c +++ b/stage1/spl_stmt.c @@ -656,7 +656,7 @@ static void parse_expr_stmt(spl_comp_t *ctx) { if (is_assign) ctx->addr_of_mode = 0; - if (!is_assign && expr.type) + if (!is_assign && expr.type && expr.type->kind == TYPE_BASIC && expr.type->basic_type != SPL_VOID) spl_emit(ctx, SPL_DROP, SPL_VOID, 0); if (peek(ctx)->type == TOK_SEMICOLON) advance(ctx); diff --git a/stage1/test07_pointers.spl b/stage1/test07_pointers.spl index 0799f35..9cff38e 100644 --- a/stage1/test07_pointers.spl +++ b/stage1/test07_pointers.spl @@ -47,8 +47,18 @@ fn main() i32 { /* ---- 指向数组元素的指针 ---- */ var buf: [4]i32 = [4]i32{1, 2, 3, 4}; + // @dbg(); /* 数组初始化后的栈状态 */ var elem_ptr: *i32 = &buf[0]; - if elem_ptr.* != 1 { ret 9; } + // @dbg(); /* &buf[0] 之后:栈上应有指针值 */ + var loaded := elem_ptr.*; + // @dbg(); /* 解引用后:loaded 值 */ + if loaded != 1 { ret 9; } + + elem_ptr = &buf[1]; + // @dbg(); /* &buf[1] 之后 */ + var loaded2 := elem_ptr.*; + // @dbg(); /* 解引用后:loaded2 值 */ + if loaded2 != 2 { ret 10; } ret 0; } diff --git a/stage1/test11_enum.spl b/stage1/test11_enum.spl index 7a116c9..e9a1e88 100644 --- a/stage1/test11_enum.spl +++ b/stage1/test11_enum.spl @@ -12,11 +12,19 @@ type Color = enum { } type Expr = enum { - Int: i32, - Add: struct { left: *Expr, right: *Expr }, + val: i32, + add: struct { left: *Expr, right: *Expr }, + tag: Tag, + + type Tag = enum { + TagA, + TagB, + TagC, + } } fn main() i32 { vm_printf("enum values: %d %d %d\n", Color.Red, Color.Green, Color.Blue); + vm_printf("enum values: %d %d %d\n", Expr.Tag.TagA, Expr.Tag.TagB, Expr.Tag.TagC); ret 0; }