From 40cffa9cb54ea7b5bf350c5ad63fc04935095924 Mon Sep 17 00:00:00 2001 From: zzy <2450266535@qq.com> Date: Tue, 29 Sep 2026 21:11:15 +0800 Subject: [PATCH] =?UTF-8?q?stage0=20=E6=96=B0=E5=A2=9E=E5=86=85=E5=AD=98?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E6=8C=87=E4=BB=A4=20=E9=87=8D=E6=9E=84vm?= =?UTF-8?q?=E4=BA=A4=E4=BA=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- stage0/spl_mcode.h | 1 + stage0/spl_rt.h | 28 ------ stage0/spl_syscall.c | 91 ++++++++++++------- stage0/spl_vm.c | 23 +++++ stage0/spl_vm.h | 3 + stage1/spl_ast.c | 5 ++ stage1/spl_ast.h | 7 +- stage1/spl_ast2ir.c | 136 ++++++++++++++++++++++++---- stage1/spl_ir2vm.c | 5 +- stage1/spl_parser.c | 14 +++ stage1/spl_sema.c | 12 +++ stage2/ast.spl | 5 +- stage2/ast2ir.spl | 141 ++++++++++++++++++++++++++--- stage2/attr.spl | 3 +- stage2/ir2c.spl | 29 ------ stage2/ir2vm.spl | 6 +- stage2/ir_builder.spl | 6 +- stage2/mcode.spl | 7 ++ stage2/parser.spl | 89 +++++++++++++----- stage2/sema.spl | 9 ++ stage2/sys/vm.spl | 9 +- tests/stage1/036_lvalue_ref.spl | 155 ++++++++++++++++++++++++++++++++ 22 files changed, 626 insertions(+), 158 deletions(-) create mode 100644 tests/stage1/036_lvalue_ref.spl diff --git a/stage0/spl_mcode.h b/stage0/spl_mcode.h index 741aed6..29f0e08 100644 --- a/stage0/spl_mcode.h +++ b/stage0/spl_mcode.h @@ -97,6 +97,7 @@ typedef enum { /* 间接内存访问 */ \ X(SPL_LOAD, "load", 0, 1, 1, "load sizeof(type)-bits zero-extended") \ X(SPL_STORE, "store", 0, 2, 0, "store low sizeof(type)-bits") \ + X(SPL_MEMCPY, "memcpy", 0, 3, 0, "copy n bytes: pop dst, src, n (VM address space)") \ /* 原生接口 */ \ X(SPL_NCALL, "ncall", 1, 0, 1, "call native function by index") \ X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \ diff --git a/stage0/spl_rt.h b/stage0/spl_rt.h index 3ce5e34..1c7bc32 100644 --- a/stage0/spl_rt.h +++ b/stage0/spl_rt.h @@ -87,32 +87,4 @@ SPL_FN u64 vm_timestamp(u64 kind) { #define SPL_VM_EMBED_IMPLIMENTATION #include "spl_vm_embed.h" -/* 配置层外部函数处理器(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; - spl_vm_set_foreign((spl_vm_t *)p, spl_native_foreign, ctx); -} - #endif /* __SPL_RT_H__ */ diff --git a/stage0/spl_syscall.c b/stage0/spl_syscall.c index e8f673e..16b779e 100644 --- a/stage0/spl_syscall.c +++ b/stage0/spl_syscall.c @@ -615,33 +615,51 @@ static spl_vm_val_t vm_timestamp(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { * nested-VM syscalls * ================================================================= */ -/* ---- 配置层的“外部函数处理器”:把某个 VM 未解析的 native 交给调用者 VM。 - * 这是配置策略,不属于 VM 库 API;VM 本身只有通用 foreign 钩子。 ---- */ +/* 通用双向桥(配置层策略,非 VM 平台 API): + * 在子 VM 上安装「未解析 native 处理器」,把子 VM 的未解析调用按名转发到 + * 宿主(调用者)VM 中由 `entry` 指定的函数。宿主函数收到: + * (ctx, name_off, name_len, nested_vm, arg_base, nargs) + * 平台/C 侧**纯转发**:不复制实参、不限元数、不了解类型;聚合读写由宿主用 + * vm_stack_get / vm_mem_read / vm_mem_alloc / vm_mem_write 自行完成。 + * `entry` 由宿主显式给出,平台不预设任何函数名。 */ typedef struct { - spl_vm_t *caller; + spl_vm_t *host; void *ctx; -} spl_foreign_t; + char entry[128]; +} spl_bridge_t; -static spl_vm_val_t spl_foreign_reenter(spl_vm_t *vm, const spl_vm_call_t *call, void *user) { - spl_foreign_t *f = (spl_foreign_t *)user; - spl_vm_val_t tmp[20]; - spl_vm_call_t c2; - int i; - (void)vm; - if (!f || !f->caller || !call || !call->name) +static spl_vm_val_t spl_bridge_forward(spl_vm_t *nested, const spl_vm_call_t *call, void *user) { + spl_bridge_t *b = (spl_bridge_t *)user; + spl_vm_val_t name_off; + spl_vm_val_t args[6]; + spl_vm_call_t back; + usize n; + usize arg_base; + if (!b || !b->host || !call || !call->name || b->entry[0] == '\0') return 0; - if (call->nargs + 1 > (usize)(sizeof(tmp) / sizeof(tmp[0]))) + n = strlen(call->name); + /* 名拷进宿主 VM 内存,宿主按偏移读取。 */ + name_off = spl_vm_mem_alloc(b->host, n + 1); + if (!name_off) return 0; - tmp[0] = (spl_vm_val_t)(uintptr_t)f->ctx; - for (i = 0; i < (int)call->nargs; i++) - tmp[1 + i] = call->args ? call->args[i] : 0; - c2.name = call->name; - c2.args = tmp; - c2.nargs = call->nargs + 1; - c2.ret = 0; - if (spl_vm_invoke(f->caller, &c2) != 0) + if (spl_vm_mem_write(b->host, name_off, call->name, n + 1) != 0) return 0; - return c2.ret; + arg_base = spl_vm_call_arg_base(nested, call); + if (arg_base == (usize)-1) + return 0; + args[0] = (spl_vm_val_t)(uintptr_t)b->ctx; + args[1] = name_off; + args[2] = (spl_vm_val_t)n; + args[3] = (spl_vm_val_t)(uintptr_t)nested; + args[4] = (spl_vm_val_t)arg_base; + args[5] = (spl_vm_val_t)call->nargs; + back.name = b->entry; + back.args = args; + back.nargs = 6; + back.ret = 0; + if (spl_vm_invoke(b->host, &back) != 0) + return 0; + return back.ret; } static spl_vm_val_t vm_new(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { @@ -655,25 +673,32 @@ static spl_vm_val_t vm_new(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { return (spl_vm_val_t)(uintptr_t)vm; } -/* 配置入口:vm_set_foreign(vm, handler, ctx)。handler=0:把未解析 native 重入 - * 当前调用者(_vm),ctx 透传前置。名与语义由配置层定义;VM 库不认识 foreign handler。 */ +/* vm_set_foreign(vm, entry, ctx):宿主显式指定处理器函数名。 */ static spl_vm_val_t sc_vm_set_foreign(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { spl_vm_t *vm; - spl_foreign_t *f; + spl_bridge_t *b; + const spl_vm_val_t *entry; + const char *eptr; + usize elen; CHECK_NARGS("vm_set_foreign", 3); vm = (spl_vm_t *)(uintptr_t)args[0]; if (!vm || !_vm) return -1; - if (args[1] != 0) /* 仅注册 handler 0(重入调用者) */ + entry = (const spl_vm_val_t *)VM_XN(_vm, args[1], 2 * sizeof(spl_vm_val_t)); + if (!entry) return -1; - if (vm->foreign == spl_foreign_reenter && vm->foreign_ctx) - free(vm->foreign_ctx); - f = (spl_foreign_t *)malloc(sizeof(*f)); - if (!f) + eptr = (const char *)VM_X(_vm, entry[0]); + elen = (usize)entry[1]; + if (!eptr || elen == 0 || elen >= sizeof(((spl_bridge_t *)0)->entry)) return -1; - f->caller = _vm; - f->ctx = (void *)(uintptr_t)args[2]; - spl_vm_set_foreign(vm, spl_foreign_reenter, f); + b = (spl_bridge_t *)malloc(sizeof(*b)); + if (!b) + return -1; + b->host = _vm; + b->ctx = (void *)(uintptr_t)args[2]; + memcpy(b->entry, eptr, elen); + b->entry[elen] = '\0'; + spl_vm_set_foreign(vm, spl_bridge_forward, b); return 0; } @@ -683,7 +708,7 @@ static spl_vm_val_t vm_drop(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) { spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0]; if (!vm) return 0; - if (vm->foreign == spl_foreign_reenter && vm->foreign_ctx) + if (vm->foreign == spl_bridge_forward && vm->foreign_ctx) free(vm->foreign_ctx); if (vm->prog) { spl_prog_drop(vm->prog); diff --git a/stage0/spl_vm.c b/stage0/spl_vm.c index 204e8bb..cb4bb9c 100644 --- a/stage0/spl_vm.c +++ b/stage0/spl_vm.c @@ -785,6 +785,14 @@ int spl_vm_stack_set(spl_vm_t *vm, usize index, spl_vm_val_t val) { return 0; } +usize spl_vm_call_arg_base(spl_vm_t *vm, const spl_vm_call_t *call) { + if (!vm || !call || !call->args || !vm->stacks.data) + return (usize)-1; + if (call->args < vm->stacks.data) + return (usize)-1; + return (usize)(call->args - vm->stacks.data); +} + spl_vm_val_t spl_vm_mem_alloc(spl_vm_t *vm, usize size) { return spl_vm_asp_alloc(vm, size); } int spl_vm_mem_read(spl_vm_t *vm, spl_vm_val_t off, void *dst, usize n) { @@ -1358,6 +1366,7 @@ static int vm_loop(spl_vm_t *vm, size_t limit) { DT(SPL_CALLI), DT(SPL_RET), DT(SPL_ALLOC), DT(SPL_LADDR), DT(SPL_GADDR), DT(SPL_LOAD), DT(SPL_STORE), DT(SPL_NCALL), DT(SPL_NLIB), + DT(SPL_MEMCPY), DT(SPL_BK), DT(SPL_DBG), [SPL_ERROR] = &&L_default, }; @@ -1679,6 +1688,20 @@ L_SPL_STORE: memcpy(_addr, &_v, spl_type_size(ins->type)); DISPATCH(); } +L_SPL_MEMCPY: + { + spl_vm_val_t _n = POP(); + spl_vm_val_t _src = POP(); + spl_vm_val_t _dst = POP(); + if (_n) { + void *_d = spl_vm_xlate(vm, _dst, (usize)_n); + void *_s = spl_vm_xlate(vm, _src, (usize)_n); + if (!_d || !_s) + VM_ERROR("MEMCPY: invalid address"); + memmove(_d, _s, (usize)_n); + } + DISPATCH(); + } /* ========== Type Conversion ========== */ L_SPL_TRUNC: diff --git a/stage0/spl_vm.h b/stage0/spl_vm.h index eeab3a8..b09c2d0 100644 --- a/stage0/spl_vm.h +++ b/stage0/spl_vm.h @@ -127,6 +127,9 @@ usize spl_vm_sp(spl_vm_t *vm); usize spl_vm_fp(spl_vm_t *vm); spl_vm_val_t spl_vm_stack_get(spl_vm_t *vm, usize index); int spl_vm_stack_set(spl_vm_t *vm, usize index, spl_vm_val_t val); +/* 本次调用实参在 VM 栈中的起始下标(外部处理器可按需用 stack_get 逐个读取, + * 无需在 C 侧了解参数类型/元数上限)。失败返回 (usize)-1。 */ +usize spl_vm_call_arg_base(spl_vm_t *vm, const spl_vm_call_t *call); spl_vm_val_t spl_vm_mem_alloc(spl_vm_t *vm, usize size); int spl_vm_mem_read(spl_vm_t *vm, spl_vm_val_t off, void *dst, usize n); int spl_vm_mem_write(spl_vm_t *vm, spl_vm_val_t off, const void *src, usize n); diff --git a/stage1/spl_ast.c b/stage1/spl_ast.c index ee7fb1f..259869b 100644 --- a/stage1/spl_ast.c +++ b/stage1/spl_ast.c @@ -424,6 +424,11 @@ static void dump_node(spl_ast_t *ast, spl_ast_node_ref_t node_ref, dump_stack_t dump_node(ast, node->pattern_field.pattern, stack, true); break; + case SPL_AST_PATTERN_REF: + if (node->pattern_ref.pat) + dump_node(ast, node->pattern_ref.pat, stack, true); + break; + default: break; } diff --git a/stage1/spl_ast.h b/stage1/spl_ast.h index 18a4a43..f40ebaa 100644 --- a/stage1/spl_ast.h +++ b/stage1/spl_ast.h @@ -73,7 +73,8 @@ X(SPL_AST_PATTERN_TUPLE, V0, pattern_tuple) \ X(SPL_AST_PATTERN_LIST, V0, pattern_list) \ X(SPL_AST_PATTERN_AGGREGATE, V0, pattern_aggregate) \ - X(SPL_AST_PATTERN_FIELD, V0, pattern_field) + X(SPL_AST_PATTERN_FIELD, V0, pattern_field) \ + X(SPL_AST_PATTERN_REF, V0, pattern_ref) /* clang-format on */ typedef enum { @@ -452,6 +453,10 @@ struct spl_ast_node { const char *name; spl_ast_node_ref_t pattern; } pattern_field; + + struct { + spl_ast_node_ref_t pat; + } pattern_ref; }; }; diff --git a/stage1/spl_ast2ir.c b/stage1/spl_ast2ir.c index 9e6edc5..c0da738 100644 --- a/stage1/spl_ast2ir.c +++ b/stage1/spl_ast2ir.c @@ -51,7 +51,8 @@ static void emit_deferred_until(spl_ast2ir_t *a, usize base) { static void emit_decl(spl_ast2ir_t *a, spl_ast_node_ref_t ref); static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n); static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, - spl_ir_node_ref_t src_ptr, spl_type_id_t tid); + spl_ir_node_ref_t src_ptr, spl_type_id_t tid, int place_mode); +static int pattern_has_ref(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref); static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, spl_type_id_t scrut_ir, spl_ir_node_ref_t scrut_slot); @@ -140,14 +141,52 @@ static spl_ir_node_ref_t emit_zero_value(spl_ast2ir_t *a, spl_type_id_t tid) { } } +/* 判断 pattern 是否包含 `&` 引用绑定(PATTERN_REF)。 */ +static int pattern_has_ref(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref) { + if (!pat_ref) + return 0; + spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref); + if (!pat) + return 0; + switch (pat->kind) { + case SPL_AST_PATTERN_REF: + return 1; + case SPL_AST_PATTERN_LIST: + for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) + if (pattern_has_ref(a, vec_at(pat->pattern_list.patterns, i))) + return 1; + return 0; + case SPL_AST_PATTERN_TUPLE: + for (usize i = 0; i < vec_size(pat->pattern_tuple.patterns); ++i) + if (pattern_has_ref(a, vec_at(pat->pattern_tuple.patterns, i))) + return 1; + return 0; + case SPL_AST_PATTERN_DOT: + return pattern_has_ref(a, pat->pattern_dot.sub_pattern); + case SPL_AST_PATTERN_AGGREGATE: + for (usize i = 0; i < vec_size(pat->pattern_aggregate.fields); ++i) { + spl_ast_node_t *f = + spl_ast_node(a->ast, vec_at(pat->pattern_aggregate.fields, i)); + if (f && f->kind == SPL_AST_PATTERN_FIELD && + pattern_has_ref(a, f->pattern_field.pattern)) + return 1; + } + return 0; + default: + return 0; + } +} + /* 把 pattern 里的 ident 绑到 src_ptr 指向内存的对应位置。 - - PATTERN_IDENT: def2ir_map[def_id] = src_ptr + - PATTERN_IDENT: def2ir_map[def_id] = src_ptr(place_mode=0,值绑定别名源); + place_mode=1 时强制拷贝到新 slot(源为真实左值,值绑定必须拷贝)。 + - PATTERN_REF: src_ptr 是该子位置的地址;分配 *T slot 存该地址,内层名绑到 slot。 - PATTERN_DOT: src_ptr 指向 enum,payload 在 mem_field(tid, src_ptr, vidx+1) - PATTERN_TUPLE / PATTERN_AGGREGATE: 递归 mem_field UB 语义:不做 tag 检查,调用者保证变体正确。 */ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, - spl_ir_node_ref_t src_ptr, spl_type_id_t tid) { + spl_ir_node_ref_t src_ptr, spl_type_id_t tid, int place_mode) { if (!pat_ref || !src_ptr) return; spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref); @@ -156,8 +195,30 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, switch (pat->kind) { case SPL_AST_PATTERN_IDENT: { - if (pat->resolved_def_id) + if (!pat->resolved_def_id) + return; + if (place_mode) { + spl_ir_node_ref_t slot = + spl_ir_builder_mem_alloca(a->ir, tid, const_usize(a, 1)); + spl_ir_node_ref_t v = spl_ir_builder_mem_load(a->ir, tid, src_ptr); + spl_ir_builder_mem_store(a->ir, tid, slot, v); + map_put(a->def2ir_map, pat->resolved_def_id, slot); + } else { map_put(a->def2ir_map, pat->resolved_def_id, src_ptr); + } + return; + } + + case SPL_AST_PATTERN_REF: { + spl_ast_node_ref_t inner = pat->pattern_ref.pat; + spl_ast_node_t *in = spl_ast_node(a->ast, inner); + spl_type_id_t ptr_tid = in ? node_ir(a, in) : 0; + if (!ptr_tid) + ptr_tid = spl_type_builder_ptr(&a->sema->type->ir, tid); + spl_ir_node_ref_t slot = + spl_ir_builder_mem_alloca(a->ir, ptr_tid, const_usize(a, 1)); + spl_ir_builder_mem_store(a->ir, ptr_tid, slot, src_ptr); + emit_pattern_bind(a, inner, slot, ptr_tid, 0); return; } @@ -174,7 +235,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, case SPL_AST_PATTERN_LIST: for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) - emit_pattern_bind(a, vec_at(pat->pattern_list.patterns, i), src_ptr, tid); + emit_pattern_bind(a, vec_at(pat->pattern_list.patterns, i), src_ptr, tid, place_mode); return; case SPL_AST_PATTERN_TUPLE: { @@ -188,7 +249,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, for (usize i = 0; i < n; ++i) { spl_type_id_t ft = vec_at(tn->agg_type.variants, i).type_id; spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, i); - emit_pattern_bind(a, vec_at(pat->pattern_tuple.patterns, i), fp, ft); + emit_pattern_bind(a, vec_at(pat->pattern_tuple.patterns, i), fp, ft, place_mode); } return; } @@ -206,7 +267,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, spl_type_id_t payload_ir = vec_at(tn->agg_type.variants, vidx).type_id; spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, tid, src_ptr, vidx + 1); if (pat->pattern_dot.sub_pattern) - emit_pattern_bind(a, pat->pattern_dot.sub_pattern, pptr, payload_ir); + emit_pattern_bind(a, pat->pattern_dot.sub_pattern, pptr, payload_ir, place_mode); return; } @@ -224,7 +285,7 @@ static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref, spl_type_id_t ft = vec_at(tn->agg_type.variants, idx).type_id; spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, idx); if (f->pattern_field.pattern) - emit_pattern_bind(a, f->pattern_field.pattern, fp, ft); + emit_pattern_bind(a, f->pattern_field.pattern, fp, ft, place_mode); } return; } @@ -297,12 +358,32 @@ static spl_ir_node_ref_t emit_match_check(spl_ast2ir_t *a, spl_ast_node_ref_t pa } /* 分配 slot → store init 值 → bind pattern。 - 返回指向变量内存的指针(*tid),调用者可用于读取初值。 */ + 返回指向变量内存的指针(*tid),调用者可用于读取初值。 + 若 pattern 含 `&` 引用绑定:源必须是可寻址左值,直接在源 place 上绑定 + (值绑定拷贝子值,引用绑定取子位置地址),返回该 place。 */ static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n) { spl_type_id_t var_ir = node_ir(a, n); if (!var_ir) return 0; + spl_ast_node_ref_t pat_ref = n->var_const_decl.pattern; + if (pattern_has_ref(a, pat_ref)) { + if (!n->var_const_decl.init_expr) { + spl_diag_error(a->diag, SPL_DIAG_KIND_NO_LVALUE, &n->dbg, + "reference binding requires an initialized place"); + return 0; + } + spl_ir_node_ref_t place = emit_lvalue(a, n->var_const_decl.init_expr); + if (!place) { + spl_ast_node_t *in = spl_ast_node(a->ast, n->var_const_decl.init_expr); + spl_diag_error(a->diag, SPL_DIAG_KIND_NO_LVALUE, in ? &in->dbg : &n->dbg, + "cannot bind by reference: initializer is not an lvalue"); + return 0; + } + emit_pattern_bind(a, pat_ref, place, var_ir, 1); + return place; + } + spl_ir_node_ref_t slot = spl_ir_builder_mem_alloca(a->ir, var_ir, const_usize(a, 1)); if (n->var_const_decl.init_expr) { @@ -311,7 +392,7 @@ static spl_ir_node_ref_t emit_var_decl(spl_ast2ir_t *a, spl_ast_node_t *n) { spl_ir_builder_mem_store(a->ir, var_ir, slot, v); } - emit_pattern_bind(a, n->var_const_decl.pattern, slot, var_ir); + emit_pattern_bind(a, pat_ref, slot, var_ir, 0); return slot; } @@ -1396,12 +1477,32 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref) if (!matched_ir) return 0; - spl_ir_node_ref_t scrut_slot = - spl_ir_builder_mem_alloca(a->ir, matched_ir, const_usize(a, 1)); - spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref); - if (!scrut_val) - return 0; - spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val); + spl_ir_node_ref_t scrut_slot = 0; + int matched_place_mode = 0; + for (usize i = 0; i < vec_size(n->match_expr.arms); ++i) { + spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i)); + if (arm && arm->kind == SPL_AST_MATCH_ARM && + pattern_has_ref(a, arm->match_arm.pattern)) { + matched_place_mode = 1; + break; + } + } + + if (matched_place_mode) { + scrut_slot = emit_lvalue(a, matched_ref); + if (!scrut_slot) { + spl_diag_error(a->diag, SPL_DIAG_KIND_NO_LVALUE, + matched_node ? &matched_node->dbg : &n->dbg, + "cannot bind by reference: matched value is not an lvalue"); + return 0; + } + } else { + scrut_slot = spl_ir_builder_mem_alloca(a->ir, matched_ir, const_usize(a, 1)); + spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref); + if (!scrut_val) + return 0; + spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val); + } usize narms = vec_size(n->match_expr.arms); spl_ir_node_ref_vec_t arm_branches; @@ -1448,7 +1549,8 @@ static spl_ir_node_ref_t emit_expr_raw(spl_ast2ir_t *a, spl_ast_node_ref_t ref) spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i)); if (arm && arm->kind == SPL_AST_MATCH_ARM) { - emit_pattern_bind(a, arm->match_arm.pattern, scrut_slot, matched_ir); + emit_pattern_bind(a, arm->match_arm.pattern, scrut_slot, matched_ir, + matched_place_mode); if (arm->match_arm.body) emit_expr(a, arm->match_arm.body); } diff --git a/stage1/spl_ir2vm.c b/stage1/spl_ir2vm.c index 05084c0..42c0a96 100644 --- a/stage1/spl_ir2vm.c +++ b/stage1/spl_ir2vm.c @@ -1016,10 +1016,7 @@ static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) { CG_LOAD(cg, SPL_PTR); cg_load_slot(cg, n->control_ret.val, SPL_PTR); CG_PUSH(cg, SPL_U64, sz); - int nat = get_or_add_native(ctx, "vm_memcpy"); - CG_PUSH(cg, SPL_U64, (spl_vm_val_t)nat); - emit(cg, SPL_NCALL, SPL_VOID, 3); - emit(cg, SPL_DROP, SPL_VOID, 0); + emit(cg, SPL_MEMCPY, SPL_VOID, 0); } emit(cg, SPL_RET, SPL_VOID, 0); break; diff --git a/stage1/spl_parser.c b/stage1/spl_parser.c index d9222a7..e1f3322 100644 --- a/stage1/spl_parser.c +++ b/stage1/spl_parser.c @@ -417,6 +417,20 @@ static spl_ast_node_ref_t parse_pattern_atom(spl_parser_t *p) { return ref; } + case TOK_AND: { + /* `&name`:解包左值引用绑定。仅允许作用于绑定名。 */ + advance(p); + const spl_tok_t *nx = peek(p); + if (!nx || nx->type != TOK_IDENT) { + PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_EXPECTED, nx, "expected binding name after '&'"); + return 0; + } + spl_ast_node_ref_t inner = parse_pattern_atom(p); + spl_ast_node_ref_t ref = new_node(p, SPL_AST_PATTERN_REF, t); + node_at(p->ast, ref)->pattern_ref.pat = inner; + return ref; + } + default: PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unexpected token in pattern"); return 0; diff --git a/stage1/spl_sema.c b/stage1/spl_sema.c index ba42ac5..b32ccda 100644 --- a/stage1/spl_sema.c +++ b/stage1/spl_sema.c @@ -1136,6 +1136,18 @@ static void sema_bind_pattern(spl_sema_t *sema, spl_ast_node_ref_t pat_ref, case SPL_AST_PATTERN_UNDERSCORE: return; + case SPL_AST_PATTERN_REF: { + if (!matched_tid) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &pat->dbg, + "ref pattern requires a concrete type"); + return; + } + spl_lang_type_id_t ptr = spl_lang_make_ptr(sema->type, matched_tid); + sema_bind_pattern(sema, pat->pattern_ref.pat, ptr); + pat->resolved_lang_tid = ptr; + return; + } + case SPL_AST_PATTERN_LIST: { for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i) sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), matched_tid); diff --git a/stage2/ast.spl b/stage2/ast.spl index 533108b..57057df 100644 --- a/stage2/ast.spl +++ b/stage2/ast.spl @@ -45,7 +45,7 @@ type Body = enum { type_expr: usize, init_expr: usize, }, - ParamDecl: struct { attr_list: Vec, name: ByteView, type_expr: usize }, + ParamDecl: struct { attr_list: Vec, is_comptime: bool, name: ByteView, type_expr: usize }, StatementRet: struct { expr: usize }, StatementBreak: struct { label: usize, expr: usize }, StatementContinue: struct { label: usize }, @@ -124,6 +124,7 @@ type Body = enum { PatternList: struct { items: Vec }, PatternAggregate: struct { items: Vec }, PatternField: struct { name: ByteView, pattern: usize }, + PatternRef: struct { pat: usize }, ExprComptime: struct { body: usize }, ExprComptimeInit: struct { name: ByteView, args: Vec, fields: Vec }, } @@ -201,6 +202,7 @@ fn kind_name(body: Body): []u8 { .PatternList(_) => { ret "pattern_list"; }, .PatternAggregate(_) => { ret "pattern_aggregate"; }, .PatternField(_) => { ret "pattern_field"; }, + .PatternRef(_) => { ret "pattern_ref"; }, } ret "?"; } @@ -590,6 +592,7 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool): .PatternList(v) => { ok = dump_list(ast, buf, stack, v.items); }, .PatternAggregate(v) => { ok = dump_list(ast, buf, stack, v.items); }, .PatternField(p) => { ok = dump_opt(ast, buf, stack, p.pattern, true); }, + .PatternRef(p) => { ok = dump_opt(ast, buf, stack, p.pat, true); }, } stack.len = stack.len - 1; diff --git a/stage2/ast2ir.spl b/stage2/ast2ir.spl index a8ee609..2605814 100644 --- a/stage2/ast2ir.spl +++ b/stage2/ast2ir.spl @@ -279,14 +279,77 @@ fn variant_at(v: Vec, idx: usize): usize { // ---- 模式绑定 ---- -fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize) { +fn pattern_has_ref(self: *Ast2Ir, pat_ref: usize): bool { + if pat_ref == 0 { ret false; } + var pat = Ast.node(self.ast, pat_ref); + if pat == null { ret false; } + match pat.body { + .PatternRef(_) => { ret true; }, + .PatternList(x) => { + var i: usize = 0; + while i < x.items.len { + if pattern_has_ref(self, list_at(x.items, i)) { ret true; } + i = i + 1; + } + ret false; + }, + .PatternTuple(x) => { + var i: usize = 0; + while i < x.items.len { + if pattern_has_ref(self, list_at(x.items, i)) { ret true; } + i = i + 1; + } + ret false; + }, + .PatternDot(x) => { ret pattern_has_ref(self, x.sub_pattern); }, + .PatternAggregate(x) => { + var i: usize = 0; + while i < x.items.len { + var f = Ast.node(self.ast, list_at(x.items, i)); + if f != null { + match f.body { + .PatternField(pf) => { + if pattern_has_ref(self, pf.pattern) { ret true; } + }, + _ => {}, + } + } + i = i + 1; + } + ret false; + }, + _ => { ret false; }, + } + ret false; +} + +fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize, place_mode: bool) { if pat_ref == 0 || src_ptr == 0 { ret; } var pat = Ast.node(self.ast, pat_ref); if pat == null { ret; } match pat.body { .PatternIdent(x) => { - if pat.resolved_def_id != 0 { ir_map_put(self, pat.resolved_def_id, src_ptr); } + if pat.resolved_def_id != 0 { + if place_mode { + var slot = IrBuilder.mem_alloca(self.ir, tid, const_usize(self, 1)); + var v = IrBuilder.mem_load(self.ir, tid, src_ptr); + ignore_usize(IrBuilder.mem_store(self.ir, tid, slot, v)); + ir_map_put(self, pat.resolved_def_id, slot); + } else { + ir_map_put(self, pat.resolved_def_id, src_ptr); + } + } + ret; + }, + .PatternRef(x) => { + var in = Ast.node(self.ast, x.pat); + var ptr_tid: usize = 0; + if in != null { ptr_tid = node_ir(self, in); } + if ptr_tid == 0 { ptr_tid = TypeTable.make_ptr(&self.sema.types.ir, tid); } + var slot = IrBuilder.mem_alloca(self.ir, ptr_tid, const_usize(self, 1)); + ignore_usize(IrBuilder.mem_store(self.ir, ptr_tid, slot, src_ptr)); + emit_pattern_bind(self, x.pat, slot, ptr_tid, false); ret; }, .ExprIntLit(x) => { ret; }, @@ -299,7 +362,7 @@ fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize) .PatternList(x) => { var i: usize = 0; while i < x.items.len { - emit_pattern_bind(self, list_at(x.items, i), src_ptr, tid); + emit_pattern_bind(self, list_at(x.items, i), src_ptr, tid, place_mode); i = i + 1; } ret; @@ -316,7 +379,7 @@ fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize) while i < n { var ft = variant_at(variants, i); var fp = IrBuilder.mem_field(self.ir, tid, src_ptr, i); - emit_pattern_bind(self, list_at(x.items, i), fp, ft); + emit_pattern_bind(self, list_at(x.items, i), fp, ft, place_mode); i = i + 1; } ret; @@ -333,7 +396,7 @@ fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize) var payload_ir = variant_at(variants, vidx); var pptr = IrBuilder.mem_field(self.ir, tid, src_ptr, vidx + 1); if x.sub_pattern != 0 { - emit_pattern_bind(self, x.sub_pattern, pptr, payload_ir); + emit_pattern_bind(self, x.sub_pattern, pptr, payload_ir, place_mode); } ret; }, @@ -357,7 +420,7 @@ fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize) var ft = variant_at(variants, idx); var fp = IrBuilder.mem_field(self.ir, tid, src_ptr, idx); if pf.pattern != 0 { - emit_pattern_bind(self, pf.pattern, fp, ft); + emit_pattern_bind(self, pf.pattern, fp, ft, place_mode); } } }, @@ -429,7 +492,6 @@ fn match_check_literal(self: *Ast2Ir, pat_ref: usize, scrut_ir: usize, scrut_slo fn emit_var_decl(self: *Ast2Ir, n: *Node): usize { var var_ir = node_ir(self, n); if var_ir == 0 { ret 0; } - var slot = IrBuilder.mem_alloca(self.ir, var_ir, const_usize(self, 1)); var iref: usize = 0; var pat_ref: usize = 0; @@ -438,11 +500,35 @@ fn emit_var_decl(self: *Ast2Ir, n: *Node): usize { .ConstDecl(x) => { iref = x.init_expr; pat_ref = x.pattern; }, _ => {}, } + + if pattern_has_ref(self, pat_ref) { + if iref == 0 { + Diag.err(self.diag, n.span, .NoLvalue, + "reference binding requires an initialized place"); + ret 0; + } + var place = emit_lvalue(self, iref); + if place == 0 { + var in = Ast.node(self.ast, iref); + if in != null { + Diag.err(self.diag, in.span, .NoLvalue, + "cannot bind by reference: initializer is not an lvalue"); + } else { + Diag.err(self.diag, n.span, .NoLvalue, + "cannot bind by reference: initializer is not an lvalue"); + } + ret 0; + } + emit_pattern_bind(self, pat_ref, place, var_ir, true); + ret place; + } + + var slot = IrBuilder.mem_alloca(self.ir, var_ir, const_usize(self, 1)); if iref != 0 { var v = emit_expr(self, iref); if v != 0 { IrBuilder.mem_store(self.ir, var_ir, slot, v); } } - emit_pattern_bind(self, pat_ref, slot, var_ir); + emit_pattern_bind(self, pat_ref, slot, var_ir, false); ret slot; } @@ -1428,10 +1514,39 @@ fn emit_match(self: *Ast2Ir, n: *Node, matched_ref: usize, arms: Vec): usize { var matched_lang = node_lang(matched_node); var matched_ir = to_ir_type(self, matched_lang); if matched_ir == 0 { ret 0; } - var scrut_slot = IrBuilder.mem_alloca(self.ir, matched_ir, const_usize(self, 1)); - var scrut_val = emit_expr(self, matched_ref); - if scrut_val == 0 { ret 0; } - ignore_usize(IrBuilder.mem_store(self.ir, matched_ir, scrut_slot, scrut_val)); + + var matched_place_mode = false; + var scan: usize = 0; + while scan < arms.len && !matched_place_mode { + var sa = Ast.node(self.ast, list_at(arms, scan)); + if sa != null { + var spat: usize = 0; + match sa.body { .MatchArm(ma) => { spat = ma.pattern; }, _ => {} } + if pattern_has_ref(self, spat) { matched_place_mode = true; } + } + scan = scan + 1; + } + + var scrut_slot: usize = 0; + if matched_place_mode { + scrut_slot = emit_lvalue(self, matched_ref); + if scrut_slot == 0 { + var mn = Ast.node(self.ast, matched_ref); + if mn != null { + Diag.err(self.diag, mn.span, .NoLvalue, + "cannot bind by reference: matched value is not an lvalue"); + } else { + Diag.err(self.diag, n.span, .NoLvalue, + "cannot bind by reference: matched value is not an lvalue"); + } + ret 0; + } + } else { + scrut_slot = IrBuilder.mem_alloca(self.ir, matched_ir, const_usize(self, 1)); + var scrut_val = emit_expr(self, matched_ref); + if scrut_val == 0 { ret 0; } + ignore_usize(IrBuilder.mem_store(self.ir, matched_ir, scrut_slot, scrut_val)); + } var narms = arms.len; var arm_branches = Vec.blank(@sizeof(usize), @alignof(usize)); @@ -1469,7 +1584,7 @@ fn emit_match(self: *Ast2Ir, n: *Node, matched_ref: usize, arms: Vec): usize { var pat: usize = 0; var body: usize = 0; match arm.body { .MatchArm(ma) => { pat = ma.pattern; body = ma.body; }, _ => {} } - emit_pattern_bind(self, pat, scrut_slot, matched_ir); + emit_pattern_bind(self, pat, scrut_slot, matched_ir, matched_place_mode); if body != 0 { ignore_usize(emit_expr(self, body)); } } var j: usize = 0; diff --git a/stage2/attr.spl b/stage2/attr.spl index a644e6f..99e464e 100644 --- a/stage2/attr.spl +++ b/stage2/attr.spl @@ -202,6 +202,7 @@ fn register_builtins(self: *Attr) { registry_add(self, make_info("", "align", .Layout, .Type, .Required, 1, 1)); registry_add(self, make_info("", "volatile", .Ir, .Expr, .Required, 0, 0)); registry_add(self, make_info("", "atomic", .Ir, .Expr, .Required, 0, 1)); + registry_add(self, make_info("", "export", .Ir, .Decl, .Optional, 1, 1)); } // ---- dump(镜像 stage1 spl_attr_dump / spl_attr_dump_id)---- @@ -360,7 +361,7 @@ fn test_registry() { } defer { drop(&at); } - @assert(at.registry.len == 7, "1 sentinel + 6 builtins"); + @assert(at.registry.len == 8, "1 sentinel + 7 builtins"); var i = lookup(&at, ByteView.empty(), ByteView.from_slice("extern")); @assert(i != 0, "extern found"); diff --git a/stage2/ir2c.spl b/stage2/ir2c.spl index 269b682..4e8702d 100644 --- a/stage2/ir2c.spl +++ b/stage2/ir2c.spl @@ -1283,7 +1283,6 @@ 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); @@ -1311,34 +1310,6 @@ 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, diff --git a/stage2/ir2vm.spl b/stage2/ir2vm.spl index 5cbc33c..049ced5 100644 --- a/stage2/ir2vm.spl +++ b/stage2/ir2vm.spl @@ -638,6 +638,7 @@ fn cg_const(cg: *Cgen, ref: usize, n: *IrEntry) { } }, .Int(x) => { imm = const_usize(n); }, + .Any => { imm = const_usize(n); }, .Float => { var fv: f64 = 0.0; match n.body { @@ -915,10 +916,7 @@ fn cg_control(cg: *Cgen, ref: usize, n: *IrEntry) { cg_load(cg, .Ptr); cg_load_slot(cg, r.val, .Ptr); cg_push(cg, .U64, sz); - var nat = Mcode.get_or_add_native(&ctx.prog, ByteView.from_slice("vm_memcpy")); - cg_push(cg, .U64, nat as usize); - emit(cg, .Ncall, .Void, 3); - emit(cg, .Drop, .Void, 0); + emit(cg, .Memcpy, .Void, 0); } emit(cg, .Ret, .Void, 0); } else { diff --git a/stage2/ir_builder.spl b/stage2/ir_builder.spl index f919eca..6eba8a7 100644 --- a/stage2/ir_builder.spl +++ b/stage2/ir_builder.spl @@ -122,11 +122,11 @@ fn fn_end(self: *IrBuilder) { if fx.block < self.blocks.len { target = list_at(self.blocks, fx.block); } if target != 0 { if @enum2int(fx.slot) == @enum2int(FixKind.BrTrue) { - match e.body { .Br(b) => { b.true_label = target; e.body = .Br(b); }, _ => {} } + match e.body { .Br(&b) => { b.true_label = target; }, _ => {} } } else if @enum2int(fx.slot) == @enum2int(FixKind.BrFalse) { - match e.body { .Br(b) => { b.false_label = target; e.body = .Br(b); }, _ => {} } + match e.body { .Br(&b) => { b.false_label = target; }, _ => {} } } else { - match e.body { .Jmp(j) => { j.label = target; e.body = .Jmp(j); }, _ => {} } + match e.body { .Jmp(&j) => { j.label = target; }, _ => {} } } } } diff --git a/stage2/mcode.spl b/stage2/mcode.spl index b25b75b..096ddab 100644 --- a/stage2/mcode.spl +++ b/stage2/mcode.spl @@ -72,6 +72,7 @@ type Op = enum { Gaddr, Load, Store, + Memcpy, Ncall, Nlib, Bk, @@ -177,6 +178,7 @@ fn op_name(op: Op): []u8 { .Gaddr => { ret "gaddr"; }, .Load => { ret "load"; }, .Store => { ret "store"; }, + .Memcpy => { ret "memcpy"; }, .Ncall => { ret "ncall"; }, .Nlib => { ret "nlib"; }, .Bk => { ret "breakpoint"; }, @@ -693,6 +695,11 @@ fn store(self: *Prog, path: ByteView): Status { ret .Ok; } +// 序列化到内存缓冲(供内嵌 VM 直接装载)。 +fn serialize(self: *Prog, out: *ByteBuffer): Status { + ret encode(self, out); +} + fn ignore_ptr(p: *any) {} fn ignore_usize(v: usize) {} diff --git a/stage2/parser.spl b/stage2/parser.spl index 88c25bd..02e7c9c 100644 --- a/stage2/parser.spl +++ b/stage2/parser.spl @@ -460,9 +460,8 @@ fn parse_pattern_atom(self: *Parser): usize { var sub = parse_pattern(self); var pn = Ast.node(self.ast, ref); match pn.body { - .PatternDot(pd) => { + .PatternDot(&pd) => { pd.sub_pattern = sub; - pn.body = .PatternDot(pd); }, _ => {}, } @@ -508,6 +507,24 @@ fn parse_pattern_atom(self: *Parser): usize { } ret ref; }, + .And => { + // `&name`:解包左值引用绑定。仅允许作用于绑定名。 + advance(self); + var nx = peek(self); + if nx == null { + parse_error(self, nx, "expected binding name after '&'"); + ret 0; + } + match nx.kind { + .Ident => {}, + _ => { + parse_error(self, nx, "expected binding name after '&'"); + ret 0; + }, + } + var inner = parse_pattern_atom(self); + ret new_node(self, .PatternRef(.{ .pat = inner }), t); + }, _ => {}, } }, @@ -547,7 +564,7 @@ fn parse_pattern(self: *Parser): usize { // ---- 声明 ---- -fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) { +fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool, bang_only: bool) { while !self.failed { var t = peek(self); if t == null { ret; } @@ -556,14 +573,21 @@ fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) { else if kind_is_punct(t.kind, .Sharp) { is_at = false; } else { ret; } if !allow_at && is_at { ret; } - advance(self); - if is_external { - t = peek(self); - if t == null || !kind_is_punct(t.kind, .Not) { - parse_error(self, t, "expected '!' in external tag"); - ret; - } + if bang_only { + // 仅消费 `#!`/`@!` 形式的当前层 tag;遇到普通 `#name` 即停。 + var save = self.pos; advance(self); + var nt = peek(self); + var has_bang = nt != null && kind_is_punct(nt.kind, .Not); + self.pos = save; + if !has_bang { ret; } + } + advance(self); + t = peek(self); + var ext_here = is_external; + if t != null && kind_is_punct(t.kind, .Not) { + advance(self); + ext_here = true; } t = peek(self); if t == null { ret; } @@ -572,14 +596,14 @@ fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) { // stage1 先分配 tag 节点,再解析参数。 var body: Body = .TagItem(.{ .is_at = is_at, - .is_external = is_external, + .is_external = ext_here, .name = t.text, .args = ref_vec(), }); - if is_external { + if ext_here { body = .ExternalTagItem(.{ .is_at = is_at, - .is_external = is_external, + .is_external = ext_here, .name = t.text, .args = ref_vec(), }); @@ -608,7 +632,13 @@ fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) { fn parse_param_decl_impl(self: *Parser): usize { var attrs = ref_vec(); - parse_tag_list(self, &attrs, false, true); + var is_comptime = false; + var c0 = peek(self); + if c0 != null && kind_is_keyword(c0.kind, .Comptime) { + advance(self); + is_comptime = true; + } + parse_tag_list(self, &attrs, false, true, false); var name = peek(self); if name == null { ret 0; } var ok = false; @@ -620,10 +650,10 @@ fn parse_param_decl_impl(self: *Parser): usize { if !ok { ret 0; } advance(self); if !expect_punct(self, .Colon, "':' in parameter declaration") { ret 0; } - parse_tag_list(self, &attrs, false, false); + parse_tag_list(self, &attrs, false, false, false); var ty = parse_type_expr(self); ret new_node(self, - .ParamDecl(.{ .attr_list = attrs, .name = name.text, .type_expr = ty }), + .ParamDecl(.{ .attr_list = attrs, .is_comptime = is_comptime, .name = name.text, .type_expr = ty }), name); } @@ -651,11 +681,24 @@ fn parse_param_list(self: *Parser): Vec { if at_punct(self, .Comma) { advance(self); continue; } break; }, + .Keyword(k) => { + if @enum2int(k) == @enum2int(Keyword.Comptime) { + var pd2 = parse_param_decl_impl(self); + if pd2 != 0 { + match Vec.push(&out, &pd2 as *u8) { .Ok => {}, .Err(e) => { ret out; } } + } + if at_punct(self, .Comma) { advance(self); continue; } + break; + } + parse_error(self, t, "unexpected token in parameter list"); + break; + }, .Punct(p) => { if @enum2int(p) == @enum2int(Punct.Ellipsis) { advance(self); var pd = new_node(self, .ParamDecl(.{ .attr_list = ref_vec(), + .is_comptime = false, .name = ByteView.empty(), .type_expr = 0, }), t); @@ -825,7 +868,7 @@ fn parse_member_decl(self: *Parser, tags: Vec): usize { var t = peek(self); if t != null && kind_is_punct(t.kind, .Colon) { advance(self); - parse_tag_list(self, &tags, false, false); + parse_tag_list(self, &tags, false, false, false); te = parse_type_expr(self); t = peek(self); if t != null && kind_is_punct(t.kind, .Assign) { @@ -864,7 +907,7 @@ fn parse_comptime_decl(self: *Parser): usize { fn parse_container_declaration(self: *Parser): usize { var tags = ref_vec(); - parse_tag_list(self, &tags, false, false); + parse_tag_list(self, &tags, false, false, false); var t = peek(self); if t == null { ret 0; } match t.kind { @@ -1282,9 +1325,9 @@ fn parse_prefix(self: *Parser): usize { if ref == 0 { ret 0; } var n = Ast.node(self.ast, ref); match n.body { - .ExprWhile(w) => { w.label = label; n.body = .ExprWhile(w); }, - .ExprLoop(l) => { l.label = label; n.body = .ExprLoop(l); }, - .ExprFor(f) => { f.label = label; n.body = .ExprFor(f); }, + .ExprWhile(&w) => { w.label = label; }, + .ExprLoop(&l) => { l.label = label; }, + .ExprFor(&f) => { f.label = label; }, _ => {}, } ret ref; @@ -2021,7 +2064,7 @@ fn parse_statement(self: *Parser): usize { @enum2int(k) == @enum2int(Keyword.Const) || @enum2int(k) == @enum2int(Keyword.Type) { var tags = ref_vec(); - parse_tag_list(self, &tags, false, false); + parse_tag_list(self, &tags, false, false, false); var decl: usize = 0; if @enum2int(k) == @enum2int(Keyword.Var) { decl = parse_var_const_decl(self, tags, false); @@ -2188,7 +2231,7 @@ fn parse(self: *Parser) { var t2 = peek(self); self.pos = save; if t2 != null && kind_is_punct(t2.kind, .Not) { - parse_tag_list(self, &external_tags, true, true); + parse_tag_list(self, &external_tags, true, true, true); continue; } } diff --git a/stage2/sema.spl b/stage2/sema.spl index f9a98b4..4c771ab 100644 --- a/stage2/sema.spl +++ b/stage2/sema.spl @@ -1268,6 +1268,15 @@ fn bind_pattern(self: *Sema, pat_ref: usize, matched_tid: usize) { pat.resolved_lang_tid = matched_tid; }, .PatternUnderscore => {}, + .PatternRef(x) => { + if matched_tid == 0 { + diag(self, pat.span, .TypeMismatch, "ref pattern requires a concrete type"); + ret; + } + var ptr = LangType.make_ptr(&self.types, matched_tid); + bind_pattern(self, x.pat, ptr); + pat.resolved_lang_tid = ptr; + }, .PatternList(x) => { var i: usize = 0; while i < x.items.len { diff --git a/stage2/sys/vm.spl b/stage2/sys/vm.spl index caa10f6..00c9605 100644 --- a/stage2/sys/vm.spl +++ b/stage2/sys/vm.spl @@ -8,6 +8,13 @@ type spl_vm_val_t = u64; // usize #extern(vm) fn vm_load_from_buffer(vm: *any, buf: *any, len: usize): i32; #extern(vm) fn vm_push(vm: *any, val: spl_vm_val_t): void; -// TODO +#extern(vm) fn vm_call(vm: *any, name: *u8, nargs: u64): i32; +#extern(vm) fn vm_run(vm: *any): i32; +#extern(vm) fn vm_ret_val(vm: *any): spl_vm_val_t; +#extern(vm) fn vm_mem_alloc(vm: *any, size: usize): spl_vm_val_t; +#extern(vm) fn vm_mem_read(vm: *any, off: spl_vm_val_t, dst: *any, n: usize): i32; +#extern(vm) fn vm_mem_write(vm: *any, off: spl_vm_val_t, src: *any, n: usize): i32; +#extern(vm) fn vm_stack_get(vm: *any, index: usize): spl_vm_val_t; +#extern(vm) fn vm_set_foreign(vm: *any, entry: []u8, ctx: u64): i32; fn main() {} diff --git a/tests/stage1/036_lvalue_ref.spl b/tests/stage1/036_lvalue_ref.spl new file mode 100644 index 0000000..398c24e --- /dev/null +++ b/tests/stage1/036_lvalue_ref.spl @@ -0,0 +1,155 @@ +// Expect retcode = 0 + +type Pair = struct { + x: i32, + y: i32, +} + +type Option = enum { + None, + Some: i32, +} + +type Nested = struct { + inner: Pair, + tag: i32, +} + +// 对照:无 & 的值绑定是拷贝,写回不作用于源。 +fn test_value_binding_is_copy() { + var p: Pair = .{ .x = 1, .y = 2 }; + var .{ .x = a, .y = b } = p; + a = 10; + b = 20; + @assert(a == 10, "value binding a is local copy"); + @assert(p.x == 1, "value binding does not write back x"); + @assert(p.y == 2, "value binding does not write back y"); +} + +// 普通结构体解构:& 引用绑定。 +fn test_struct_ref_binding() { + var p: Pair = .{ .x = 1, .y = 2 }; + var .{ .x = &a, .y = &b } = p; + @assert(a.* == 1, "struct ref reads x"); + @assert(b.* == 2, "struct ref reads y"); + a.* = 10; + b.* = 20; + @assert(p.x == 10, "struct ref writes back x"); + @assert(p.y == 20, "struct ref writes back y"); +} + +// 混合:值绑定 + 引用绑定。 +fn test_struct_mixed_binding() { + var p: Pair = .{ .x = 1, .y = 2 }; + var .{ .x = val, .y = &ref } = p; + @assert(val == 1, "mixed value reads x"); + val = 100; + ref.* = 200; + @assert(p.x == 1, "mixed value does not write back"); + @assert(p.y == 200, "mixed ref writes back"); +} + +// 部分字段 & 引用绑定。 +fn test_struct_partial_ref() { + var p: Pair = .{ .x = 1, .y = 2 }; + var .{ .y = &b } = p; + b.* = 99; + @assert(p.y == 99, "partial ref writes back y"); + @assert(p.x == 1, "partial ref leaves x untouched"); +} + +// 嵌套结构体解构:& 引用绑定。 +fn test_nested_ref() { + var n: Nested = .{ .inner = .{ .x = 1, .y = 2 }, .tag = 3 }; + var .{ .inner = .{ .x = &a, .y = &b }, .tag = &t } = n; + a.* = 7; + b.* = 8; + t.* = 9; + @assert(n.inner.x == 7, "nested ref writes back inner.x"); + @assert(n.inner.y == 8, "nested ref writes back inner.y"); + @assert(n.tag == 9, "nested ref writes back tag"); +} + +// 源本身是左值子表达式(字段):& 引用绑定。 +fn test_field_source_ref() { + var n: Nested = .{ .inner = .{ .x = 1, .y = 2 }, .tag = 3 }; + var .{ .x = &a, .y = &b } = n.inner; + a.* = 11; + b.* = 12; + @assert(n.inner.x == 11, "field source ref writes back x"); + @assert(n.inner.y == 12, "field source ref writes back y"); +} + +// 枚举解构:& 引用绑定。 +fn test_enum_ref() { + var o: Option = .Some(1); + var .Some(&n) = o; + @assert(n.* == 1, "enum ref reads payload"); + n.* = 42; + match o { + .None => { @assert(false, "enum ref: expected Some"); }, + .Some(m) => { @assert(m == 42, "enum ref writes back payload"); }, + } +} + +// 元组解构:& 引用绑定。 +fn test_tuple_ref() { + var t: (i32, i32) = (1, 2); + var (&a, &b) = t; + @assert(a.* == 1, "tuple ref reads [0]"); + @assert(b.* == 2, "tuple ref reads [1]"); + a.* = 10; + b.* = 20; + @assert(t[0] == 10, "tuple ref writes back [0]"); + @assert(t[1] == 20, "tuple ref writes back [1]"); +} + +// if var:& 引用绑定(枚举)。 +fn test_ifvar_enum_ref() { + var o: Option = .Some(1); + if var .Some(&n) = o { + n.* = 77; + } + match o { + .None => { @assert(false, "if var ref: expected Some"); }, + .Some(m) => { @assert(m == 77, "if var ref writes back payload"); }, + } +} + +// if var:& 引用绑定(元组)。 +fn test_ifvar_tuple_ref() { + var t: (i32, i32) = (1, 2); + if var (&a, &b) = t { + a.* = 5; + b.* = 6; + } + @assert(t[0] == 5, "if var tuple ref writes back [0]"); + @assert(t[1] == 6, "if var tuple ref writes back [1]"); +} + +// match arm:& 引用绑定。 +fn test_match_enum_ref() { + var o: Option = .Some(1); + match o { + .None => { @assert(false, "match ref: expected Some"); }, + .Some(&n) => { n.* = 55; }, + } + match o { + .None => { @assert(false, "match ref: expected Some after"); }, + .Some(m) => { @assert(m == 55, "match arm ref writes back payload"); }, + } +} + +fn main() { + test_value_binding_is_copy(); + test_struct_ref_binding(); + test_struct_mixed_binding(); + test_struct_partial_ref(); + test_nested_ref(); + test_field_source_ref(); + test_enum_ref(); + test_tuple_ref(); + test_ifvar_enum_ref(); + test_ifvar_tuple_ref(); + test_match_enum_ref(); +}