stage0 新增内存复制指令 重构vm交互
This commit is contained in:
1 parent
0b8f649818
commit
40cffa9cb5
22 files changed
+626
-158
No files matched your search
@@ -97,6 +97,7 @@ typedef enum {
|
|||||||
/* 间接内存访问 */ \
|
/* 间接内存访问 */ \
|
||||||
X(SPL_LOAD, "load", 0, 1, 1, "load sizeof(type)-bits zero-extended") \
|
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_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_NCALL, "ncall", 1, 0, 1, "call native function by index") \
|
||||||
X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \
|
X(SPL_NLIB, "nlib", 1, 0, 0, "dlopen library (name idx)") \
|
||||||
|
|||||||
@@ -87,32 +87,4 @@ SPL_FN u64 vm_timestamp(u64 kind) {
|
|||||||
#define SPL_VM_EMBED_IMPLIMENTATION
|
#define SPL_VM_EMBED_IMPLIMENTATION
|
||||||
#include "spl_vm_embed.h"
|
#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__ */
|
#endif /* __SPL_RT_H__ */
|
||||||
+58
-33
@@ -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
|
* nested-VM syscalls
|
||||||
* ================================================================= */
|
* ================================================================= */
|
||||||
|
|
||||||
/* ---- 配置层的“外部函数处理器”:把某个 VM 未解析的 native 交给调用者 VM。
|
/* 通用双向桥(配置层策略,非 VM 平台 API):
|
||||||
* 这是配置策略,不属于 VM 库 API;VM 本身只有通用 foreign 钩子。 ---- */
|
* 在子 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 {
|
typedef struct {
|
||||||
spl_vm_t *caller;
|
spl_vm_t *host;
|
||||||
void *ctx;
|
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) {
|
static spl_vm_val_t spl_bridge_forward(spl_vm_t *nested, const spl_vm_call_t *call, void *user) {
|
||||||
spl_foreign_t *f = (spl_foreign_t *)user;
|
spl_bridge_t *b = (spl_bridge_t *)user;
|
||||||
spl_vm_val_t tmp[20];
|
spl_vm_val_t name_off;
|
||||||
spl_vm_call_t c2;
|
spl_vm_val_t args[6];
|
||||||
int i;
|
spl_vm_call_t back;
|
||||||
(void)vm;
|
usize n;
|
||||||
if (!f || !f->caller || !call || !call->name)
|
usize arg_base;
|
||||||
|
if (!b || !b->host || !call || !call->name || b->entry[0] == '\0')
|
||||||
return 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;
|
return 0;
|
||||||
tmp[0] = (spl_vm_val_t)(uintptr_t)f->ctx;
|
if (spl_vm_mem_write(b->host, name_off, call->name, n + 1) != 0)
|
||||||
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)
|
|
||||||
return 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) {
|
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;
|
return (spl_vm_val_t)(uintptr_t)vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 配置入口:vm_set_foreign(vm, handler, ctx)。handler=0:把未解析 native 重入
|
/* vm_set_foreign(vm, entry, ctx):宿主显式指定处理器函数名。 */
|
||||||
* 当前调用者(_vm),ctx 透传前置。名与语义由配置层定义;VM 库不认识 foreign handler。 */
|
|
||||||
static spl_vm_val_t sc_vm_set_foreign(spl_vm_t *_vm, int nargs, spl_vm_val_t *args) {
|
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_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);
|
CHECK_NARGS("vm_set_foreign", 3);
|
||||||
vm = (spl_vm_t *)(uintptr_t)args[0];
|
vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||||
if (!vm || !_vm)
|
if (!vm || !_vm)
|
||||||
return -1;
|
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;
|
return -1;
|
||||||
if (vm->foreign == spl_foreign_reenter && vm->foreign_ctx)
|
eptr = (const char *)VM_X(_vm, entry[0]);
|
||||||
free(vm->foreign_ctx);
|
elen = (usize)entry[1];
|
||||||
f = (spl_foreign_t *)malloc(sizeof(*f));
|
if (!eptr || elen == 0 || elen >= sizeof(((spl_bridge_t *)0)->entry))
|
||||||
if (!f)
|
|
||||||
return -1;
|
return -1;
|
||||||
f->caller = _vm;
|
b = (spl_bridge_t *)malloc(sizeof(*b));
|
||||||
f->ctx = (void *)(uintptr_t)args[2];
|
if (!b)
|
||||||
spl_vm_set_foreign(vm, spl_foreign_reenter, f);
|
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;
|
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];
|
spl_vm_t *vm = (spl_vm_t *)(uintptr_t)args[0];
|
||||||
if (!vm)
|
if (!vm)
|
||||||
return 0;
|
return 0;
|
||||||
if (vm->foreign == spl_foreign_reenter && vm->foreign_ctx)
|
if (vm->foreign == spl_bridge_forward && vm->foreign_ctx)
|
||||||
free(vm->foreign_ctx);
|
free(vm->foreign_ctx);
|
||||||
if (vm->prog) {
|
if (vm->prog) {
|
||||||
spl_prog_drop(vm->prog);
|
spl_prog_drop(vm->prog);
|
||||||
|
|||||||
@@ -785,6 +785,14 @@ int spl_vm_stack_set(spl_vm_t *vm, usize index, spl_vm_val_t val) {
|
|||||||
return 0;
|
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); }
|
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) {
|
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_CALLI), DT(SPL_RET), DT(SPL_ALLOC),
|
||||||
DT(SPL_LADDR), DT(SPL_GADDR), DT(SPL_LOAD),
|
DT(SPL_LADDR), DT(SPL_GADDR), DT(SPL_LOAD),
|
||||||
DT(SPL_STORE), DT(SPL_NCALL), DT(SPL_NLIB),
|
DT(SPL_STORE), DT(SPL_NCALL), DT(SPL_NLIB),
|
||||||
|
DT(SPL_MEMCPY),
|
||||||
DT(SPL_BK), DT(SPL_DBG), [SPL_ERROR] = &&L_default,
|
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));
|
memcpy(_addr, &_v, spl_type_size(ins->type));
|
||||||
DISPATCH();
|
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 ========== */
|
/* ========== Type Conversion ========== */
|
||||||
L_SPL_TRUNC:
|
L_SPL_TRUNC:
|
||||||
|
|||||||
@@ -127,6 +127,9 @@ usize spl_vm_sp(spl_vm_t *vm);
|
|||||||
usize spl_vm_fp(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);
|
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);
|
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);
|
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_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);
|
int spl_vm_mem_write(spl_vm_t *vm, spl_vm_val_t off, const void *src, usize n);
|
||||||
|
|||||||
@@ -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);
|
dump_node(ast, node->pattern_field.pattern, stack, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SPL_AST_PATTERN_REF:
|
||||||
|
if (node->pattern_ref.pat)
|
||||||
|
dump_node(ast, node->pattern_ref.pat, stack, true);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -73,7 +73,8 @@
|
|||||||
X(SPL_AST_PATTERN_TUPLE, V0, pattern_tuple) \
|
X(SPL_AST_PATTERN_TUPLE, V0, pattern_tuple) \
|
||||||
X(SPL_AST_PATTERN_LIST, V0, pattern_list) \
|
X(SPL_AST_PATTERN_LIST, V0, pattern_list) \
|
||||||
X(SPL_AST_PATTERN_AGGREGATE, V0, pattern_aggregate) \
|
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 */
|
/* clang-format on */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -452,6 +453,10 @@ struct spl_ast_node {
|
|||||||
const char *name;
|
const char *name;
|
||||||
spl_ast_node_ref_t pattern;
|
spl_ast_node_ref_t pattern;
|
||||||
} pattern_field;
|
} pattern_field;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
spl_ast_node_ref_t pat;
|
||||||
|
} pattern_ref;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+119
-17
@@ -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 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 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,
|
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,
|
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);
|
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 绑到 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_DOT: src_ptr 指向 enum,payload 在 mem_field(tid, src_ptr, vidx+1)
|
||||||
- PATTERN_TUPLE / PATTERN_AGGREGATE: 递归 mem_field
|
- PATTERN_TUPLE / PATTERN_AGGREGATE: 递归 mem_field
|
||||||
UB 语义:不做 tag 检查,调用者保证变体正确。
|
UB 语义:不做 tag 检查,调用者保证变体正确。
|
||||||
*/
|
*/
|
||||||
static void emit_pattern_bind(spl_ast2ir_t *a, spl_ast_node_ref_t pat_ref,
|
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)
|
if (!pat_ref || !src_ptr)
|
||||||
return;
|
return;
|
||||||
spl_ast_node_t *pat = spl_ast_node(a->ast, pat_ref);
|
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) {
|
switch (pat->kind) {
|
||||||
case SPL_AST_PATTERN_IDENT: {
|
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);
|
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;
|
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:
|
case SPL_AST_PATTERN_LIST:
|
||||||
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
|
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;
|
return;
|
||||||
|
|
||||||
case SPL_AST_PATTERN_TUPLE: {
|
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) {
|
for (usize i = 0; i < n; ++i) {
|
||||||
spl_type_id_t ft = vec_at(tn->agg_type.variants, i).type_id;
|
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);
|
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;
|
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_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);
|
spl_ir_node_ref_t pptr = spl_ir_builder_mem_field(a->ir, tid, src_ptr, vidx + 1);
|
||||||
if (pat->pattern_dot.sub_pattern)
|
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;
|
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_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);
|
spl_ir_node_ref_t fp = spl_ir_builder_mem_field(a->ir, tid, src_ptr, idx);
|
||||||
if (f->pattern_field.pattern)
|
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;
|
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。
|
/* 分配 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) {
|
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);
|
spl_type_id_t var_ir = node_ir(a, n);
|
||||||
if (!var_ir)
|
if (!var_ir)
|
||||||
return 0;
|
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));
|
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) {
|
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);
|
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;
|
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)
|
if (!matched_ir)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
spl_ir_node_ref_t scrut_slot =
|
spl_ir_node_ref_t scrut_slot = 0;
|
||||||
spl_ir_builder_mem_alloca(a->ir, matched_ir, const_usize(a, 1));
|
int matched_place_mode = 0;
|
||||||
spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref);
|
for (usize i = 0; i < vec_size(n->match_expr.arms); ++i) {
|
||||||
if (!scrut_val)
|
spl_ast_node_t *arm = spl_ast_node(a->ast, vec_at(n->match_expr.arms, i));
|
||||||
return 0;
|
if (arm && arm->kind == SPL_AST_MATCH_ARM &&
|
||||||
spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val);
|
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);
|
usize narms = vec_size(n->match_expr.arms);
|
||||||
spl_ir_node_ref_vec_t arm_branches;
|
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));
|
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) {
|
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)
|
if (arm->match_arm.body)
|
||||||
emit_expr(a, arm->match_arm.body);
|
emit_expr(a, arm->match_arm.body);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-4
@@ -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(cg, SPL_PTR);
|
||||||
cg_load_slot(cg, n->control_ret.val, SPL_PTR);
|
cg_load_slot(cg, n->control_ret.val, SPL_PTR);
|
||||||
CG_PUSH(cg, SPL_U64, sz);
|
CG_PUSH(cg, SPL_U64, sz);
|
||||||
int nat = get_or_add_native(ctx, "vm_memcpy");
|
emit(cg, SPL_MEMCPY, SPL_VOID, 0);
|
||||||
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_RET, SPL_VOID, 0);
|
emit(cg, SPL_RET, SPL_VOID, 0);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -417,6 +417,20 @@ static spl_ast_node_ref_t parse_pattern_atom(spl_parser_t *p) {
|
|||||||
return ref;
|
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:
|
default:
|
||||||
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unexpected token in pattern");
|
PARSE_ERROR(p, SPL_DIAG_KIND_PARSE_UNRECOGNIZED, t, "unexpected token in pattern");
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -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:
|
case SPL_AST_PATTERN_UNDERSCORE:
|
||||||
return;
|
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: {
|
case SPL_AST_PATTERN_LIST: {
|
||||||
for (usize i = 0; i < vec_size(pat->pattern_list.patterns); ++i)
|
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);
|
sema_bind_pattern(sema, vec_at(pat->pattern_list.patterns, i), matched_tid);
|
||||||
|
|||||||
+4
-1
@@ -45,7 +45,7 @@ type Body = enum {
|
|||||||
type_expr: usize,
|
type_expr: usize,
|
||||||
init_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 },
|
StatementRet: struct { expr: usize },
|
||||||
StatementBreak: struct { label: usize, expr: usize },
|
StatementBreak: struct { label: usize, expr: usize },
|
||||||
StatementContinue: struct { label: usize },
|
StatementContinue: struct { label: usize },
|
||||||
@@ -124,6 +124,7 @@ type Body = enum {
|
|||||||
PatternList: struct { items: Vec },
|
PatternList: struct { items: Vec },
|
||||||
PatternAggregate: struct { items: Vec },
|
PatternAggregate: struct { items: Vec },
|
||||||
PatternField: struct { name: ByteView, pattern: usize },
|
PatternField: struct { name: ByteView, pattern: usize },
|
||||||
|
PatternRef: struct { pat: usize },
|
||||||
ExprComptime: struct { body: usize },
|
ExprComptime: struct { body: usize },
|
||||||
ExprComptimeInit: struct { name: ByteView, args: Vec, fields: Vec },
|
ExprComptimeInit: struct { name: ByteView, args: Vec, fields: Vec },
|
||||||
}
|
}
|
||||||
@@ -201,6 +202,7 @@ fn kind_name(body: Body): []u8 {
|
|||||||
.PatternList(_) => { ret "pattern_list"; },
|
.PatternList(_) => { ret "pattern_list"; },
|
||||||
.PatternAggregate(_) => { ret "pattern_aggregate"; },
|
.PatternAggregate(_) => { ret "pattern_aggregate"; },
|
||||||
.PatternField(_) => { ret "pattern_field"; },
|
.PatternField(_) => { ret "pattern_field"; },
|
||||||
|
.PatternRef(_) => { ret "pattern_ref"; },
|
||||||
}
|
}
|
||||||
ret "?";
|
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); },
|
.PatternList(v) => { ok = dump_list(ast, buf, stack, v.items); },
|
||||||
.PatternAggregate(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); },
|
.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;
|
stack.len = stack.len - 1;
|
||||||
|
|||||||
+128
-13
@@ -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; }
|
if pat_ref == 0 || src_ptr == 0 { ret; }
|
||||||
var pat = Ast.node(self.ast, pat_ref);
|
var pat = Ast.node(self.ast, pat_ref);
|
||||||
if pat == null { ret; }
|
if pat == null { ret; }
|
||||||
|
|
||||||
match pat.body {
|
match pat.body {
|
||||||
.PatternIdent(x) => {
|
.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;
|
ret;
|
||||||
},
|
},
|
||||||
.ExprIntLit(x) => { ret; },
|
.ExprIntLit(x) => { ret; },
|
||||||
@@ -299,7 +362,7 @@ fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize)
|
|||||||
.PatternList(x) => {
|
.PatternList(x) => {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while i < x.items.len {
|
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;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
ret;
|
ret;
|
||||||
@@ -316,7 +379,7 @@ fn emit_pattern_bind(self: *Ast2Ir, pat_ref: usize, src_ptr: usize, tid: usize)
|
|||||||
while i < n {
|
while i < n {
|
||||||
var ft = variant_at(variants, i);
|
var ft = variant_at(variants, i);
|
||||||
var fp = IrBuilder.mem_field(self.ir, tid, src_ptr, 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;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
ret;
|
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 payload_ir = variant_at(variants, vidx);
|
||||||
var pptr = IrBuilder.mem_field(self.ir, tid, src_ptr, vidx + 1);
|
var pptr = IrBuilder.mem_field(self.ir, tid, src_ptr, vidx + 1);
|
||||||
if x.sub_pattern != 0 {
|
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;
|
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 ft = variant_at(variants, idx);
|
||||||
var fp = IrBuilder.mem_field(self.ir, tid, src_ptr, idx);
|
var fp = IrBuilder.mem_field(self.ir, tid, src_ptr, idx);
|
||||||
if pf.pattern != 0 {
|
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 {
|
fn emit_var_decl(self: *Ast2Ir, n: *Node): usize {
|
||||||
var var_ir = node_ir(self, n);
|
var var_ir = node_ir(self, n);
|
||||||
if var_ir == 0 { ret 0; }
|
if var_ir == 0 { ret 0; }
|
||||||
var slot = IrBuilder.mem_alloca(self.ir, var_ir, const_usize(self, 1));
|
|
||||||
|
|
||||||
var iref: usize = 0;
|
var iref: usize = 0;
|
||||||
var pat_ref: 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; },
|
.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 {
|
if iref != 0 {
|
||||||
var v = emit_expr(self, iref);
|
var v = emit_expr(self, iref);
|
||||||
if v != 0 { IrBuilder.mem_store(self.ir, var_ir, slot, v); }
|
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;
|
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_lang = node_lang(matched_node);
|
||||||
var matched_ir = to_ir_type(self, matched_lang);
|
var matched_ir = to_ir_type(self, matched_lang);
|
||||||
if matched_ir == 0 { ret 0; }
|
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);
|
var matched_place_mode = false;
|
||||||
if scrut_val == 0 { ret 0; }
|
var scan: usize = 0;
|
||||||
ignore_usize(IrBuilder.mem_store(self.ir, matched_ir, scrut_slot, scrut_val));
|
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 narms = arms.len;
|
||||||
var arm_branches = Vec.blank(@sizeof(usize), @alignof(usize));
|
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 pat: usize = 0;
|
||||||
var body: usize = 0;
|
var body: usize = 0;
|
||||||
match arm.body { .MatchArm(ma) => { pat = ma.pattern; body = ma.body; }, _ => {} }
|
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)); }
|
if body != 0 { ignore_usize(emit_expr(self, body)); }
|
||||||
}
|
}
|
||||||
var j: usize = 0;
|
var j: usize = 0;
|
||||||
|
|||||||
+2
-1
@@ -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("", "align", .Layout, .Type, .Required, 1, 1));
|
||||||
registry_add(self, make_info("", "volatile", .Ir, .Expr, .Required, 0, 0));
|
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("", "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)----
|
// ---- dump(镜像 stage1 spl_attr_dump / spl_attr_dump_id)----
|
||||||
@@ -360,7 +361,7 @@ fn test_registry() {
|
|||||||
}
|
}
|
||||||
defer { drop(&at); }
|
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"));
|
var i = lookup(&at, ByteView.empty(), ByteView.from_slice("extern"));
|
||||||
@assert(i != 0, "extern found");
|
@assert(i != 0, "extern found");
|
||||||
|
|||||||
@@ -1283,7 +1283,6 @@ fn run(self: *Ir2C) {
|
|||||||
}
|
}
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
emit_ct_table(self);
|
|
||||||
var m = find_main(self);
|
var m = find_main(self);
|
||||||
if m != 0 {
|
if m != 0 {
|
||||||
var f = IrM.func(self.ir, m);
|
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 {
|
fn new(ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr, out: *ByteBuffer): Ir2C {
|
||||||
ret Ir2C {
|
ret Ir2C {
|
||||||
.ir = ir,
|
.ir = ir,
|
||||||
|
|||||||
+2
-4
@@ -638,6 +638,7 @@ fn cg_const(cg: *Cgen, ref: usize, n: *IrEntry) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Int(x) => { imm = const_usize(n); },
|
.Int(x) => { imm = const_usize(n); },
|
||||||
|
.Any => { imm = const_usize(n); },
|
||||||
.Float => {
|
.Float => {
|
||||||
var fv: f64 = 0.0;
|
var fv: f64 = 0.0;
|
||||||
match n.body {
|
match n.body {
|
||||||
@@ -915,10 +916,7 @@ fn cg_control(cg: *Cgen, ref: usize, n: *IrEntry) {
|
|||||||
cg_load(cg, .Ptr);
|
cg_load(cg, .Ptr);
|
||||||
cg_load_slot(cg, r.val, .Ptr);
|
cg_load_slot(cg, r.val, .Ptr);
|
||||||
cg_push(cg, .U64, sz);
|
cg_push(cg, .U64, sz);
|
||||||
var nat = Mcode.get_or_add_native(&ctx.prog, ByteView.from_slice("vm_memcpy"));
|
emit(cg, .Memcpy, .Void, 0);
|
||||||
cg_push(cg, .U64, nat as usize);
|
|
||||||
emit(cg, .Ncall, .Void, 3);
|
|
||||||
emit(cg, .Drop, .Void, 0);
|
|
||||||
}
|
}
|
||||||
emit(cg, .Ret, .Void, 0);
|
emit(cg, .Ret, .Void, 0);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -122,11 +122,11 @@ fn fn_end(self: *IrBuilder) {
|
|||||||
if fx.block < self.blocks.len { target = list_at(self.blocks, fx.block); }
|
if fx.block < self.blocks.len { target = list_at(self.blocks, fx.block); }
|
||||||
if target != 0 {
|
if target != 0 {
|
||||||
if @enum2int(fx.slot) == @enum2int(FixKind.BrTrue) {
|
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) {
|
} 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 {
|
} else {
|
||||||
match e.body { .Jmp(j) => { j.label = target; e.body = .Jmp(j); }, _ => {} }
|
match e.body { .Jmp(&j) => { j.label = target; }, _ => {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ type Op = enum {
|
|||||||
Gaddr,
|
Gaddr,
|
||||||
Load,
|
Load,
|
||||||
Store,
|
Store,
|
||||||
|
Memcpy,
|
||||||
Ncall,
|
Ncall,
|
||||||
Nlib,
|
Nlib,
|
||||||
Bk,
|
Bk,
|
||||||
@@ -177,6 +178,7 @@ fn op_name(op: Op): []u8 {
|
|||||||
.Gaddr => { ret "gaddr"; },
|
.Gaddr => { ret "gaddr"; },
|
||||||
.Load => { ret "load"; },
|
.Load => { ret "load"; },
|
||||||
.Store => { ret "store"; },
|
.Store => { ret "store"; },
|
||||||
|
.Memcpy => { ret "memcpy"; },
|
||||||
.Ncall => { ret "ncall"; },
|
.Ncall => { ret "ncall"; },
|
||||||
.Nlib => { ret "nlib"; },
|
.Nlib => { ret "nlib"; },
|
||||||
.Bk => { ret "breakpoint"; },
|
.Bk => { ret "breakpoint"; },
|
||||||
@@ -693,6 +695,11 @@ fn store(self: *Prog, path: ByteView): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 序列化到内存缓冲(供内嵌 VM 直接装载)。
|
||||||
|
fn serialize(self: *Prog, out: *ByteBuffer): Status {
|
||||||
|
ret encode(self, out);
|
||||||
|
}
|
||||||
|
|
||||||
fn ignore_ptr(p: *any) {}
|
fn ignore_ptr(p: *any) {}
|
||||||
fn ignore_usize(v: usize) {}
|
fn ignore_usize(v: usize) {}
|
||||||
|
|
||||||
|
|||||||
+66
-23
@@ -460,9 +460,8 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
var sub = parse_pattern(self);
|
var sub = parse_pattern(self);
|
||||||
var pn = Ast.node(self.ast, ref);
|
var pn = Ast.node(self.ast, ref);
|
||||||
match pn.body {
|
match pn.body {
|
||||||
.PatternDot(pd) => {
|
.PatternDot(&pd) => {
|
||||||
pd.sub_pattern = sub;
|
pd.sub_pattern = sub;
|
||||||
pn.body = .PatternDot(pd);
|
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
@@ -508,6 +507,24 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
}
|
}
|
||||||
ret ref;
|
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 {
|
while !self.failed {
|
||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t == null { ret; }
|
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 if kind_is_punct(t.kind, .Sharp) { is_at = false; }
|
||||||
else { ret; }
|
else { ret; }
|
||||||
if !allow_at && is_at { ret; }
|
if !allow_at && is_at { ret; }
|
||||||
advance(self);
|
if bang_only {
|
||||||
if is_external {
|
// 仅消费 `#!`/`@!` 形式的当前层 tag;遇到普通 `#name` 即停。
|
||||||
t = peek(self);
|
var save = self.pos;
|
||||||
if t == null || !kind_is_punct(t.kind, .Not) {
|
|
||||||
parse_error(self, t, "expected '!' in external tag");
|
|
||||||
ret;
|
|
||||||
}
|
|
||||||
advance(self);
|
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);
|
t = peek(self);
|
||||||
if t == null { ret; }
|
if t == null { ret; }
|
||||||
@@ -572,14 +596,14 @@ fn parse_tag_list(self: *Parser, out: *Vec, is_external: bool, allow_at: bool) {
|
|||||||
// stage1 先分配 tag 节点,再解析参数。
|
// stage1 先分配 tag 节点,再解析参数。
|
||||||
var body: Body = .TagItem(.{
|
var body: Body = .TagItem(.{
|
||||||
.is_at = is_at,
|
.is_at = is_at,
|
||||||
.is_external = is_external,
|
.is_external = ext_here,
|
||||||
.name = t.text,
|
.name = t.text,
|
||||||
.args = ref_vec(),
|
.args = ref_vec(),
|
||||||
});
|
});
|
||||||
if is_external {
|
if ext_here {
|
||||||
body = .ExternalTagItem(.{
|
body = .ExternalTagItem(.{
|
||||||
.is_at = is_at,
|
.is_at = is_at,
|
||||||
.is_external = is_external,
|
.is_external = ext_here,
|
||||||
.name = t.text,
|
.name = t.text,
|
||||||
.args = ref_vec(),
|
.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 {
|
fn parse_param_decl_impl(self: *Parser): usize {
|
||||||
var attrs = ref_vec();
|
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);
|
var name = peek(self);
|
||||||
if name == null { ret 0; }
|
if name == null { ret 0; }
|
||||||
var ok = false;
|
var ok = false;
|
||||||
@@ -620,10 +650,10 @@ fn parse_param_decl_impl(self: *Parser): usize {
|
|||||||
if !ok { ret 0; }
|
if !ok { ret 0; }
|
||||||
advance(self);
|
advance(self);
|
||||||
if !expect_punct(self, .Colon, "':' in parameter declaration") { ret 0; }
|
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);
|
var ty = parse_type_expr(self);
|
||||||
ret new_node(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);
|
name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,11 +681,24 @@ fn parse_param_list(self: *Parser): Vec {
|
|||||||
if at_punct(self, .Comma) { advance(self); continue; }
|
if at_punct(self, .Comma) { advance(self); continue; }
|
||||||
break;
|
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) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.Ellipsis) {
|
if @enum2int(p) == @enum2int(Punct.Ellipsis) {
|
||||||
advance(self);
|
advance(self);
|
||||||
var pd = new_node(self, .ParamDecl(.{
|
var pd = new_node(self, .ParamDecl(.{
|
||||||
.attr_list = ref_vec(),
|
.attr_list = ref_vec(),
|
||||||
|
.is_comptime = false,
|
||||||
.name = ByteView.empty(),
|
.name = ByteView.empty(),
|
||||||
.type_expr = 0,
|
.type_expr = 0,
|
||||||
}), t);
|
}), t);
|
||||||
@@ -825,7 +868,7 @@ fn parse_member_decl(self: *Parser, tags: Vec): usize {
|
|||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t != null && kind_is_punct(t.kind, .Colon) {
|
if t != null && kind_is_punct(t.kind, .Colon) {
|
||||||
advance(self);
|
advance(self);
|
||||||
parse_tag_list(self, &tags, false, false);
|
parse_tag_list(self, &tags, false, false, false);
|
||||||
te = parse_type_expr(self);
|
te = parse_type_expr(self);
|
||||||
t = peek(self);
|
t = peek(self);
|
||||||
if t != null && kind_is_punct(t.kind, .Assign) {
|
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 {
|
fn parse_container_declaration(self: *Parser): usize {
|
||||||
var tags = ref_vec();
|
var tags = ref_vec();
|
||||||
parse_tag_list(self, &tags, false, false);
|
parse_tag_list(self, &tags, false, false, false);
|
||||||
var t = peek(self);
|
var t = peek(self);
|
||||||
if t == null { ret 0; }
|
if t == null { ret 0; }
|
||||||
match t.kind {
|
match t.kind {
|
||||||
@@ -1282,9 +1325,9 @@ fn parse_prefix(self: *Parser): usize {
|
|||||||
if ref == 0 { ret 0; }
|
if ref == 0 { ret 0; }
|
||||||
var n = Ast.node(self.ast, ref);
|
var n = Ast.node(self.ast, ref);
|
||||||
match n.body {
|
match n.body {
|
||||||
.ExprWhile(w) => { w.label = label; n.body = .ExprWhile(w); },
|
.ExprWhile(&w) => { w.label = label; },
|
||||||
.ExprLoop(l) => { l.label = label; n.body = .ExprLoop(l); },
|
.ExprLoop(&l) => { l.label = label; },
|
||||||
.ExprFor(f) => { f.label = label; n.body = .ExprFor(f); },
|
.ExprFor(&f) => { f.label = label; },
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
@@ -2021,7 +2064,7 @@ fn parse_statement(self: *Parser): usize {
|
|||||||
@enum2int(k) == @enum2int(Keyword.Const) ||
|
@enum2int(k) == @enum2int(Keyword.Const) ||
|
||||||
@enum2int(k) == @enum2int(Keyword.Type) {
|
@enum2int(k) == @enum2int(Keyword.Type) {
|
||||||
var tags = ref_vec();
|
var tags = ref_vec();
|
||||||
parse_tag_list(self, &tags, false, false);
|
parse_tag_list(self, &tags, false, false, false);
|
||||||
var decl: usize = 0;
|
var decl: usize = 0;
|
||||||
if @enum2int(k) == @enum2int(Keyword.Var) {
|
if @enum2int(k) == @enum2int(Keyword.Var) {
|
||||||
decl = parse_var_const_decl(self, tags, false);
|
decl = parse_var_const_decl(self, tags, false);
|
||||||
@@ -2188,7 +2231,7 @@ fn parse(self: *Parser) {
|
|||||||
var t2 = peek(self);
|
var t2 = peek(self);
|
||||||
self.pos = save;
|
self.pos = save;
|
||||||
if t2 != null && kind_is_punct(t2.kind, .Not) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1268,6 +1268,15 @@ fn bind_pattern(self: *Sema, pat_ref: usize, matched_tid: usize) {
|
|||||||
pat.resolved_lang_tid = matched_tid;
|
pat.resolved_lang_tid = matched_tid;
|
||||||
},
|
},
|
||||||
.PatternUnderscore => {},
|
.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) => {
|
.PatternList(x) => {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while i < x.items.len {
|
while i < x.items.len {
|
||||||
|
|||||||
+8
-1
@@ -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_load_from_buffer(vm: *any, buf: *any, len: usize): i32;
|
||||||
|
|
||||||
#extern(vm) fn vm_push(vm: *any, val: spl_vm_val_t): void;
|
#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() {}
|
fn main() {}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
Reference in new issue
Block a user