Compare commits
10
Commits
51d408d524
..
dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc2d8f8db4 | ||
|
|
76f980654c | ||
|
|
0e38858785 | ||
|
|
40cffa9cb5 | ||
|
|
0b8f649818 | ||
|
|
c0a86ea552 | ||
|
|
fae017b52d | ||
|
|
9ecb92cb03 | ||
|
|
70085355db | ||
|
|
d10578d267 |
No files matched your search
@@ -5,9 +5,9 @@
|
|||||||
#define nob_cc_flags(cmd) nob_cmd_append(cmd, SPL_CFLAGS)
|
#define nob_cc_flags(cmd) nob_cmd_append(cmd, SPL_CFLAGS)
|
||||||
#undef UNICODE
|
#undef UNICODE
|
||||||
#define NOB_IMPLEMENTATION
|
#define NOB_IMPLEMENTATION
|
||||||
#include "nob.h"
|
#include "nob/nob.h"
|
||||||
#undef NOB_IMPLEMENTATION
|
#undef NOB_IMPLEMENTATION
|
||||||
#include "nob_popen.h"
|
#include "nob/nob_popen.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <consoleapi2.h>
|
#include <consoleapi2.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -19,93 +19,7 @@
|
|||||||
// object files get rebuilt.
|
// object files get rebuilt.
|
||||||
static const char *CFLAGS_STAMP = BUILD_FOLDER ".cflags";
|
static const char *CFLAGS_STAMP = BUILD_FOLDER ".cflags";
|
||||||
|
|
||||||
/* clang-format off */
|
#include "nob/nob_data.h"
|
||||||
#define VM_SRCS "stage0/spl_mcode.c", "stage0/spl_syscall.c", "stage0/spl_vm.c",
|
|
||||||
static const char *spl_cli_srcs[] = {"stage0/spl_cli.c", VM_SRCS};
|
|
||||||
static const char *vm_test_srcs[] = {"stage0/test_spl_vm.c", VM_SRCS};
|
|
||||||
static const char *splc0_srcs[] = {
|
|
||||||
"stage1/splc0.c",
|
|
||||||
"stage1/spl_diag.c",
|
|
||||||
"stage1/spl_tok.c",
|
|
||||||
"stage1/spl_lexer.c",
|
|
||||||
"stage1/spl_ast.c",
|
|
||||||
"stage1/spl_parser.c",
|
|
||||||
"stage1/spl_dumptree.c",
|
|
||||||
"stage1/spl_type.c",
|
|
||||||
"stage1/spl_fold.c",
|
|
||||||
"stage1/spl_lang_type.c",
|
|
||||||
"stage1/spl_attr.c",
|
|
||||||
"stage1/spl_scope.c",
|
|
||||||
"stage1/spl_sema.c",
|
|
||||||
"stage1/spl_intrin.c",
|
|
||||||
"stage1/spl_ir.c",
|
|
||||||
"stage1/spl_ast2ir.c",
|
|
||||||
"stage1/spl_layout.c",
|
|
||||||
"stage1/spl_ir2vm.c",
|
|
||||||
"stage1/ir_opt.c",
|
|
||||||
"stage1/vm_opt.c",
|
|
||||||
"stage0/spl_mcode.c",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* splc1_deps[] = {
|
|
||||||
"stage2/sys/exit.spl",
|
|
||||||
"stage2/sys/file.spl",
|
|
||||||
"stage2/sys/mem.spl",
|
|
||||||
"stage2/sys/spl_rt.h",
|
|
||||||
"stage2/sys/spl_vm_embed.h",
|
|
||||||
"stage2/sys/time.spl",
|
|
||||||
"stage2/sys/vm.spl",
|
|
||||||
"stage2/utils/algorithm.spl",
|
|
||||||
"stage2/utils/arena.spl",
|
|
||||||
"stage2/utils/ascii.spl",
|
|
||||||
"stage2/utils/byte_buffer.spl",
|
|
||||||
"stage2/utils/byte_view.spl",
|
|
||||||
"stage2/utils/error.spl",
|
|
||||||
"stage2/utils/fmt.spl",
|
|
||||||
"stage2/utils/hash.spl",
|
|
||||||
"stage2/utils/io.spl",
|
|
||||||
"stage2/utils/json.spl",
|
|
||||||
"stage2/utils/log.spl",
|
|
||||||
"stage2/utils/lsp_frame.spl",
|
|
||||||
"stage2/utils/map.spl",
|
|
||||||
"stage2/utils/memory.spl",
|
|
||||||
"stage2/utils/mp.spl",
|
|
||||||
"stage2/utils/path.spl",
|
|
||||||
"stage2/utils/profile.spl",
|
|
||||||
"stage2/utils/set.spl",
|
|
||||||
"stage2/utils/source.spl",
|
|
||||||
"stage2/utils/str.spl",
|
|
||||||
"stage2/utils/utf8.spl",
|
|
||||||
"stage2/utils/value.spl",
|
|
||||||
"stage2/utils/vec.spl",
|
|
||||||
"stage2/ast.spl",
|
|
||||||
"stage2/ast2ir.spl",
|
|
||||||
"stage2/attr.spl",
|
|
||||||
"stage2/def.spl",
|
|
||||||
"stage2/diag.spl",
|
|
||||||
"stage2/eval.spl",
|
|
||||||
"stage2/intrin.spl",
|
|
||||||
"stage2/ir.spl",
|
|
||||||
"stage2/ir2c.spl",
|
|
||||||
"stage2/ir2vm.spl",
|
|
||||||
"stage2/ir_builder.spl",
|
|
||||||
"stage2/lang_type.spl",
|
|
||||||
"stage2/layout.spl",
|
|
||||||
"stage2/lexer.spl",
|
|
||||||
"stage2/mcode.spl",
|
|
||||||
"stage2/parser.spl",
|
|
||||||
"stage2/scope.spl",
|
|
||||||
"stage2/sema.spl",
|
|
||||||
"stage2/splc1.spl",
|
|
||||||
"stage2/token.spl",
|
|
||||||
"stage2/type_table.spl",
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* splc2_deps[] = {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
/* clang-format on */
|
|
||||||
|
|
||||||
static const char *obj_path(const char *src) {
|
static const char *obj_path(const char *src) {
|
||||||
// "stage0/spl_vm.c" -> "build/stage0_spl_vm.o"
|
// "stage0/spl_vm.c" -> "build/stage0_spl_vm.o"
|
||||||
@@ -162,47 +76,12 @@ static bool prepare_build(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct build {
|
|
||||||
const char *name;
|
|
||||||
enum {
|
|
||||||
BUILD_EXE,
|
|
||||||
BUILD_EXE_WITH_OPTIMIZATION,
|
|
||||||
BUILD_SIR,
|
|
||||||
BUILD_NATIVE,
|
|
||||||
} kind;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
const char **srcs;
|
|
||||||
size_t srcs_count;
|
|
||||||
const char **headers;
|
|
||||||
size_t headers_count;
|
|
||||||
} exe;
|
|
||||||
struct {
|
|
||||||
const char *src;
|
|
||||||
const char *compiler;
|
|
||||||
const char **deps;
|
|
||||||
size_t deps_count;
|
|
||||||
} sir;
|
|
||||||
struct {
|
|
||||||
const char *src;
|
|
||||||
const char *compiler;
|
|
||||||
const char **deps;
|
|
||||||
size_t deps_count;
|
|
||||||
} native;
|
|
||||||
};
|
|
||||||
} build_t;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char **items;
|
const char **items;
|
||||||
size_t count;
|
size_t count;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
} str_da_t;
|
} str_da_t;
|
||||||
|
|
||||||
typedef struct run {
|
|
||||||
const char *name;
|
|
||||||
const char *interpreter;
|
|
||||||
} run_t;
|
|
||||||
|
|
||||||
bool run_target(const char *name, Nob_Cmd *args);
|
bool run_target(const char *name, Nob_Cmd *args);
|
||||||
const char *build_target(const char *name);
|
const char *build_target(const char *name);
|
||||||
|
|
||||||
@@ -271,143 +150,6 @@ defer:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *build_native(const char *name) {
|
|
||||||
const char *exe = exe_path(name);
|
|
||||||
const char *cli = build_target("spl_cli");
|
|
||||||
const char *sir = build_target("splc1");
|
|
||||||
if (!cli || !sir) {
|
|
||||||
nob_log(NOB_ERROR, "failed to build prerequisites for `%s`", name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
const char *cpath = BUILD_FOLDER "splc1.c";
|
|
||||||
const char *inputs[] = {sir,
|
|
||||||
"stage2/sys/spl_rt.h",
|
|
||||||
"stage2/sys/spl_vm_embed.h",
|
|
||||||
"stage2/sys/spl_vm_embed.c",
|
|
||||||
"stage0/spl_vm.c",
|
|
||||||
"stage0/spl_mcode.c",
|
|
||||||
"stage0/spl_syscall.c",
|
|
||||||
"stage0/spl_vm.h",
|
|
||||||
"stage0/spl_mcode.h"};
|
|
||||||
int needs = nob_needs_rebuild(exe, inputs, NOB_ARRAY_LEN(inputs));
|
|
||||||
if (needs < 0)
|
|
||||||
return NULL;
|
|
||||||
if (needs == 0)
|
|
||||||
return exe;
|
|
||||||
|
|
||||||
Nob_Cmd cmd = {0};
|
|
||||||
if (nob_file_exists(exe)) {
|
|
||||||
// 已有原生编译器:用原生自举重新生成 C(0.1s 级)
|
|
||||||
nob_cmd_append(&cmd, exe, "--dump", "c", "stage2/splc1.spl", cpath);
|
|
||||||
} else {
|
|
||||||
// 冷启动:用 VM 版 splc1 生成 C
|
|
||||||
nob_cmd_append(&cmd, cli, sir, "--dump", "c", "stage2/splc1.spl", cpath);
|
|
||||||
}
|
|
||||||
if (!nob_cmd_run(&cmd)) {
|
|
||||||
nob_log(NOB_ERROR, "ir2c C generation failed");
|
|
||||||
nob_cmd_free(cmd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
cmd.count = 0;
|
|
||||||
nob_cc(&cmd);
|
|
||||||
nob_cc_inputs(&cmd, cpath);
|
|
||||||
nob_cc_inputs(&cmd, "stage2/sys/spl_vm_embed.c");
|
|
||||||
nob_cc_inputs(&cmd, "stage0/spl_vm.c");
|
|
||||||
nob_cc_inputs(&cmd, "stage0/spl_mcode.c");
|
|
||||||
nob_cc_inputs(&cmd, "stage0/spl_syscall.c");
|
|
||||||
nob_cc_output(&cmd, exe);
|
|
||||||
if (!nob_cmd_run(&cmd)) {
|
|
||||||
nob_log(NOB_ERROR, "native link failed");
|
|
||||||
nob_cmd_free(cmd);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
nob_cmd_free(cmd);
|
|
||||||
return exe;
|
|
||||||
}
|
|
||||||
|
|
||||||
static build_t targets[] = {
|
|
||||||
{
|
|
||||||
"spl_cli",
|
|
||||||
BUILD_EXE_WITH_OPTIMIZATION,
|
|
||||||
.exe = {.srcs = spl_cli_srcs,
|
|
||||||
.srcs_count = NOB_ARRAY_LEN(spl_cli_srcs),
|
|
||||||
.headers = NULL,
|
|
||||||
.headers_count = 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"spl_cli_g",
|
|
||||||
BUILD_EXE,
|
|
||||||
.exe = {.srcs = spl_cli_srcs,
|
|
||||||
.srcs_count = NOB_ARRAY_LEN(spl_cli_srcs),
|
|
||||||
.headers = NULL,
|
|
||||||
.headers_count = 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"vm_test",
|
|
||||||
BUILD_EXE,
|
|
||||||
.exe = {.srcs = vm_test_srcs,
|
|
||||||
.srcs_count = NOB_ARRAY_LEN(vm_test_srcs),
|
|
||||||
.headers = NULL,
|
|
||||||
.headers_count = 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"splc0",
|
|
||||||
BUILD_EXE,
|
|
||||||
.exe = {.srcs = splc0_srcs,
|
|
||||||
.srcs_count = NOB_ARRAY_LEN(splc0_srcs),
|
|
||||||
.headers = NULL,
|
|
||||||
.headers_count = 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"splc1",
|
|
||||||
BUILD_SIR,
|
|
||||||
.sir = {.src = "stage2/splc1.spl",
|
|
||||||
.compiler = "splc0",
|
|
||||||
.deps = splc1_deps,
|
|
||||||
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"splc1s",
|
|
||||||
BUILD_SIR,
|
|
||||||
.sir = {.src = "stage2/splc1.spl",
|
|
||||||
.compiler = "splc1",
|
|
||||||
.deps = splc1_deps,
|
|
||||||
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"splc1r",
|
|
||||||
BUILD_SIR,
|
|
||||||
.sir = {.src = "stage2/splc1.spl",
|
|
||||||
.compiler = "splc1s",
|
|
||||||
.deps = splc1_deps,
|
|
||||||
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"splc1n",
|
|
||||||
BUILD_NATIVE,
|
|
||||||
.native = {.src = "stage2/splc1.spl",
|
|
||||||
.compiler = "splc1",
|
|
||||||
.deps = splc1_deps,
|
|
||||||
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"splc2",
|
|
||||||
BUILD_SIR,
|
|
||||||
.sir = {.src = "stage3/splc2.spl",
|
|
||||||
.compiler = "splc1n",
|
|
||||||
.deps = splc2_deps,
|
|
||||||
.deps_count = NOB_ARRAY_LEN(splc2_deps)},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"splc2n",
|
|
||||||
BUILD_NATIVE,
|
|
||||||
.native = {.src = "stage3/splc2.spl",
|
|
||||||
.compiler = "splc1n",
|
|
||||||
.deps = splc2_deps,
|
|
||||||
.deps_count = NOB_ARRAY_LEN(splc2_deps)},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *build_target(const char *name) {
|
const char *build_target(const char *name) {
|
||||||
if (!prepare_build())
|
if (!prepare_build())
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -439,13 +181,10 @@ const char *build_target(const char *name) {
|
|||||||
const char *target_c = c_path(name);
|
const char *target_c = c_path(name);
|
||||||
|
|
||||||
// TODO need_rebuild
|
// TODO need_rebuild
|
||||||
int needs =
|
int needs = nob_needs_rebuild(target_c, target->native.deps, target->native.deps_count);
|
||||||
nob_needs_rebuild(target_exe, target->native.deps, target->native.deps_count);
|
|
||||||
if (needs < 0)
|
if (needs < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (needs == 0)
|
if (needs != 0) {
|
||||||
return target_exe;
|
|
||||||
|
|
||||||
nob_cmd_append(&cmd, "--dump", "c", target->native.src, target_c);
|
nob_cmd_append(&cmd, "--dump", "c", target->native.src, target_c);
|
||||||
if (!run_target(target->native.compiler, &cmd)) {
|
if (!run_target(target->native.compiler, &cmd)) {
|
||||||
nob_log(NOB_ERROR, "failed to build `%s`", name);
|
nob_log(NOB_ERROR, "failed to build `%s`", name);
|
||||||
@@ -453,8 +192,17 @@ const char *build_target(const char *name) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
cmd.count = 0;
|
cmd.count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
needs = nob_needs_rebuild(target_exe, target->native.crt_deps,
|
||||||
|
target->native.crt_deps_count);
|
||||||
|
if (needs < 0)
|
||||||
|
return NULL;
|
||||||
|
if (needs == 0)
|
||||||
|
return target_exe;
|
||||||
|
|
||||||
nob_cc(&cmd);
|
nob_cc(&cmd);
|
||||||
nob_cmd_append(&cmd, "-I./stage2/sys", "-O2", "-D_CRT_SECURE_NO_WARNINGS");
|
nob_cmd_append(&cmd, "-I./stage0", "-O2", "-D_CRT_SECURE_NO_WARNINGS");
|
||||||
nob_cc_inputs(&cmd, target_c);
|
nob_cc_inputs(&cmd, target_c);
|
||||||
nob_cc_output(&cmd, target_exe);
|
nob_cc_output(&cmd, target_exe);
|
||||||
if (!nob_cmd_run(&cmd)) {
|
if (!nob_cmd_run(&cmd)) {
|
||||||
@@ -473,12 +221,6 @@ const char *build_target(const char *name) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static run_t runs[] = {
|
|
||||||
{"spl_cli", NULL}, {"vm_test", NULL}, {"splc0", NULL},
|
|
||||||
{"splc1", "spl_cli"}, {"splc1r", "spl_cli"}, {"splc1s", "spl_cli"},
|
|
||||||
{"splc1n", NULL}, {"splc2", "spl_cli"}, {"splc2n", NULL},
|
|
||||||
};
|
|
||||||
|
|
||||||
bool run_target_dry(const char *name, Nob_Cmd *args, Nob_Cmd *result) {
|
bool run_target_dry(const char *name, Nob_Cmd *args, Nob_Cmd *result) {
|
||||||
for (size_t i = 0; i < NOB_ARRAY_LEN(runs); ++i) {
|
for (size_t i = 0; i < NOB_ARRAY_LEN(runs); ++i) {
|
||||||
if (strcmp(runs[i].name, name) != 0) {
|
if (strcmp(runs[i].name, name) != 0) {
|
||||||
@@ -788,7 +530,7 @@ int main(int argc, char **argv) {
|
|||||||
SetConsoleOutputCP(CP_UTF8);
|
SetConsoleOutputCP(CP_UTF8);
|
||||||
SetConsoleCP(CP_UTF8);
|
SetConsoleCP(CP_UTF8);
|
||||||
#endif
|
#endif
|
||||||
NOB_GO_REBUILD_URSELF_PLUS(argc, argv, "nob.h", "nob_popen.h");
|
NOB_GO_REBUILD_URSELF_PLUS(argc, argv, "nob/nob.h", "nob/nob_popen.h", "nob/nob_data.h");
|
||||||
set_log_handler(nob_cancer_log_handler);
|
set_log_handler(nob_cancer_log_handler);
|
||||||
|
|
||||||
const char *program_name = nob_shift(argv, argc);
|
const char *program_name = nob_shift(argv, argc);
|
||||||
|
|||||||
File renamed without changes.
+298
@@ -0,0 +1,298 @@
|
|||||||
|
#ifndef __NOB_DATA_H__
|
||||||
|
#define __NOB_DATA_H__
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
#define VM_SRCS "stage0/spl_mcode.c", "stage0/spl_syscall.c", "stage0/spl_vm.c",
|
||||||
|
|
||||||
|
static const char *stage0_headers[] = {
|
||||||
|
"stage0/spl_asp.h", "stage0/spl_mcode.h", "stage0/spl_syscall.h",
|
||||||
|
"stage0/spl_vm.h", "stage0/spl_rt_core.h", "stage0/spl_rt.h",
|
||||||
|
"stage0/spl_vm_embed.h",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *spl_cli_srcs[] = {"stage0/spl_cli.c", VM_SRCS};
|
||||||
|
static const char *vm_test_srcs[] = {"stage0/test_spl_vm.c", VM_SRCS};
|
||||||
|
static const char *splc0_srcs[] = {
|
||||||
|
"stage1/splc0.c",
|
||||||
|
"stage1/spl_diag.c",
|
||||||
|
"stage1/spl_tok.c",
|
||||||
|
"stage1/spl_lexer.c",
|
||||||
|
"stage1/spl_ast.c",
|
||||||
|
"stage1/spl_parser.c",
|
||||||
|
"stage1/spl_dumptree.c",
|
||||||
|
"stage1/spl_type.c",
|
||||||
|
"stage1/spl_fold.c",
|
||||||
|
"stage1/spl_lang_type.c",
|
||||||
|
"stage1/spl_attr.c",
|
||||||
|
"stage1/spl_scope.c",
|
||||||
|
"stage1/spl_sema.c",
|
||||||
|
"stage1/spl_intrin.c",
|
||||||
|
"stage1/spl_ir.c",
|
||||||
|
"stage1/spl_ast2ir.c",
|
||||||
|
"stage1/spl_layout.c",
|
||||||
|
"stage1/spl_ir2vm.c",
|
||||||
|
"stage1/ir_opt.c",
|
||||||
|
"stage1/vm_opt.c",
|
||||||
|
"stage0/spl_mcode.c",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *splc1_crt_deps[] ={
|
||||||
|
"stage0/spl_rt.h",
|
||||||
|
"stage0/spl_vm_embed.h",
|
||||||
|
// TODO
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* splc1_deps[] = {
|
||||||
|
"stage2/sys/exit.spl",
|
||||||
|
"stage2/sys/file.spl",
|
||||||
|
"stage2/sys/mem.spl",
|
||||||
|
"stage2/sys/time.spl",
|
||||||
|
"stage2/sys/vm.spl",
|
||||||
|
"stage2/utils/algorithm.spl",
|
||||||
|
"stage2/utils/arena.spl",
|
||||||
|
"stage2/utils/ascii.spl",
|
||||||
|
"stage2/utils/byte_buffer.spl",
|
||||||
|
"stage2/utils/byte_view.spl",
|
||||||
|
"stage2/utils/error.spl",
|
||||||
|
"stage2/utils/fmt.spl",
|
||||||
|
"stage2/utils/hash.spl",
|
||||||
|
"stage2/utils/io.spl",
|
||||||
|
"stage2/utils/json.spl",
|
||||||
|
"stage2/utils/log.spl",
|
||||||
|
"stage2/utils/lsp_frame.spl",
|
||||||
|
"stage2/utils/map.spl",
|
||||||
|
"stage2/utils/memory.spl",
|
||||||
|
"stage2/utils/mp.spl",
|
||||||
|
"stage2/utils/path.spl",
|
||||||
|
"stage2/utils/profile.spl",
|
||||||
|
"stage2/utils/set.spl",
|
||||||
|
"stage2/utils/source.spl",
|
||||||
|
"stage2/utils/str.spl",
|
||||||
|
"stage2/utils/utf8.spl",
|
||||||
|
"stage2/utils/value.spl",
|
||||||
|
"stage2/utils/vec.spl",
|
||||||
|
"stage2/ast.spl",
|
||||||
|
"stage2/ast2ir.spl",
|
||||||
|
"stage2/attr.spl",
|
||||||
|
"stage2/def.spl",
|
||||||
|
"stage2/diag.spl",
|
||||||
|
"stage2/eval.spl",
|
||||||
|
"stage2/intrin.spl",
|
||||||
|
"stage2/ir.spl",
|
||||||
|
"stage2/ir2c.spl",
|
||||||
|
"stage2/ir2vm.spl",
|
||||||
|
"stage2/ir_builder.spl",
|
||||||
|
"stage2/lang_type.spl",
|
||||||
|
"stage2/layout.spl",
|
||||||
|
"stage2/lexer.spl",
|
||||||
|
"stage2/mcode.spl",
|
||||||
|
"stage2/parser.spl",
|
||||||
|
"stage2/scope.spl",
|
||||||
|
"stage2/sema.spl",
|
||||||
|
"stage2/splc1.spl",
|
||||||
|
"stage2/token.spl",
|
||||||
|
"stage2/type_table.spl",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char* splc2_deps[] = {
|
||||||
|
/* stage3 */
|
||||||
|
"stage3/builtin.spl",
|
||||||
|
"stage3/hir.spl",
|
||||||
|
"stage3/hir2ir.spl",
|
||||||
|
"stage3/lang_type.spl",
|
||||||
|
"stage3/lower.spl",
|
||||||
|
"stage3/scope.spl",
|
||||||
|
"stage3/sema.spl",
|
||||||
|
"stage3/splc2.spl",
|
||||||
|
"stage3/tag.spl",
|
||||||
|
/* stage2 */
|
||||||
|
"stage2/sys/exit.spl",
|
||||||
|
"stage2/sys/file.spl",
|
||||||
|
"stage2/sys/mem.spl",
|
||||||
|
"stage2/sys/time.spl",
|
||||||
|
"stage2/sys/vm.spl",
|
||||||
|
"stage2/utils/algorithm.spl",
|
||||||
|
"stage2/utils/arena.spl",
|
||||||
|
"stage2/utils/ascii.spl",
|
||||||
|
"stage2/utils/byte_buffer.spl",
|
||||||
|
"stage2/utils/byte_view.spl",
|
||||||
|
"stage2/utils/error.spl",
|
||||||
|
"stage2/utils/fmt.spl",
|
||||||
|
"stage2/utils/hash.spl",
|
||||||
|
"stage2/utils/io.spl",
|
||||||
|
"stage2/utils/json.spl",
|
||||||
|
"stage2/utils/log.spl",
|
||||||
|
"stage2/utils/lsp_frame.spl",
|
||||||
|
"stage2/utils/map.spl",
|
||||||
|
"stage2/utils/memory.spl",
|
||||||
|
"stage2/utils/mp.spl",
|
||||||
|
"stage2/utils/path.spl",
|
||||||
|
"stage2/utils/profile.spl",
|
||||||
|
"stage2/utils/set.spl",
|
||||||
|
"stage2/utils/source.spl",
|
||||||
|
"stage2/utils/str.spl",
|
||||||
|
"stage2/utils/utf8.spl",
|
||||||
|
"stage2/utils/value.spl",
|
||||||
|
"stage2/utils/vec.spl",
|
||||||
|
"stage2/ast.spl",
|
||||||
|
"stage2/ast2ir.spl",
|
||||||
|
"stage2/attr.spl",
|
||||||
|
"stage2/def.spl",
|
||||||
|
"stage2/diag.spl",
|
||||||
|
"stage2/eval.spl",
|
||||||
|
"stage2/intrin.spl",
|
||||||
|
"stage2/ir.spl",
|
||||||
|
"stage2/ir2c.spl",
|
||||||
|
"stage2/ir2vm.spl",
|
||||||
|
"stage2/ir_builder.spl",
|
||||||
|
"stage2/lang_type.spl",
|
||||||
|
"stage2/layout.spl",
|
||||||
|
"stage2/lexer.spl",
|
||||||
|
"stage2/mcode.spl",
|
||||||
|
"stage2/parser.spl",
|
||||||
|
"stage2/scope.spl",
|
||||||
|
"stage2/sema.spl",
|
||||||
|
"stage2/splc1.spl",
|
||||||
|
"stage2/token.spl",
|
||||||
|
"stage2/type_table.spl",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *splc2_crt_deps[] = {
|
||||||
|
"stage0/spl_rt.h", "stage0/spl_rt_core.h", "stage0/spl_asp.h",
|
||||||
|
"stage0/spl_mcode.h", "stage0/spl_vm.h", "stage0/spl_syscall.h",
|
||||||
|
"stage0/spl_vm_embed.h",
|
||||||
|
};
|
||||||
|
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
typedef struct build {
|
||||||
|
const char *name;
|
||||||
|
enum {
|
||||||
|
BUILD_EXE,
|
||||||
|
BUILD_EXE_WITH_OPTIMIZATION,
|
||||||
|
BUILD_SIR,
|
||||||
|
BUILD_NATIVE,
|
||||||
|
} kind;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
const char **srcs;
|
||||||
|
size_t srcs_count;
|
||||||
|
const char **headers;
|
||||||
|
size_t headers_count;
|
||||||
|
} exe;
|
||||||
|
struct {
|
||||||
|
const char *src;
|
||||||
|
const char *compiler;
|
||||||
|
const char **deps;
|
||||||
|
size_t deps_count;
|
||||||
|
} sir;
|
||||||
|
struct {
|
||||||
|
const char *src;
|
||||||
|
const char *compiler;
|
||||||
|
const char **deps;
|
||||||
|
size_t deps_count;
|
||||||
|
const char **crt_deps;
|
||||||
|
size_t crt_deps_count;
|
||||||
|
} native;
|
||||||
|
};
|
||||||
|
} build_t;
|
||||||
|
|
||||||
|
typedef struct run {
|
||||||
|
const char *name;
|
||||||
|
const char *interpreter;
|
||||||
|
} run_t;
|
||||||
|
|
||||||
|
static build_t targets[] = {
|
||||||
|
{
|
||||||
|
"spl_cli",
|
||||||
|
BUILD_EXE_WITH_OPTIMIZATION,
|
||||||
|
.exe = {.srcs = spl_cli_srcs,
|
||||||
|
.srcs_count = NOB_ARRAY_LEN(spl_cli_srcs),
|
||||||
|
.headers = stage0_headers,
|
||||||
|
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"spl_cli_g",
|
||||||
|
BUILD_EXE,
|
||||||
|
.exe = {.srcs = spl_cli_srcs,
|
||||||
|
.srcs_count = NOB_ARRAY_LEN(spl_cli_srcs),
|
||||||
|
.headers = stage0_headers,
|
||||||
|
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"vm_test",
|
||||||
|
BUILD_EXE,
|
||||||
|
.exe = {.srcs = vm_test_srcs,
|
||||||
|
.srcs_count = NOB_ARRAY_LEN(vm_test_srcs),
|
||||||
|
.headers = stage0_headers,
|
||||||
|
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"splc0",
|
||||||
|
BUILD_EXE,
|
||||||
|
.exe = {.srcs = splc0_srcs,
|
||||||
|
.srcs_count = NOB_ARRAY_LEN(splc0_srcs),
|
||||||
|
.headers = stage0_headers,
|
||||||
|
.headers_count = NOB_ARRAY_LEN(stage0_headers)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"splc1",
|
||||||
|
BUILD_SIR,
|
||||||
|
.sir = {.src = "stage2/splc1.spl",
|
||||||
|
.compiler = "splc0",
|
||||||
|
.deps = splc1_deps,
|
||||||
|
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"splc1s",
|
||||||
|
BUILD_SIR,
|
||||||
|
.sir = {.src = "stage2/splc1.spl",
|
||||||
|
.compiler = "splc1",
|
||||||
|
.deps = splc1_deps,
|
||||||
|
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"splc1r",
|
||||||
|
BUILD_SIR,
|
||||||
|
.sir = {.src = "stage2/splc1.spl",
|
||||||
|
.compiler = "splc1s",
|
||||||
|
.deps = splc1_deps,
|
||||||
|
.deps_count = NOB_ARRAY_LEN(splc1_deps)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"splc1n",
|
||||||
|
BUILD_NATIVE,
|
||||||
|
.native = {.src = "stage2/splc1.spl",
|
||||||
|
.compiler = "splc1",
|
||||||
|
.deps = splc1_deps,
|
||||||
|
.deps_count = NOB_ARRAY_LEN(splc1_deps),
|
||||||
|
.crt_deps = splc1_crt_deps,
|
||||||
|
.crt_deps_count = NOB_ARRAY_LEN(splc1_crt_deps)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"splc2",
|
||||||
|
BUILD_SIR,
|
||||||
|
.sir = {.src = "stage3/splc2.spl",
|
||||||
|
.compiler = "splc1n",
|
||||||
|
.deps = splc2_deps,
|
||||||
|
.deps_count = NOB_ARRAY_LEN(splc2_deps)},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"splc2n",
|
||||||
|
BUILD_NATIVE,
|
||||||
|
.native = {.src = "stage3/splc2.spl",
|
||||||
|
.compiler = "splc1n",
|
||||||
|
.deps = splc2_deps,
|
||||||
|
.deps_count = NOB_ARRAY_LEN(splc2_deps),
|
||||||
|
.crt_deps = splc2_crt_deps,
|
||||||
|
.crt_deps_count = NOB_ARRAY_LEN(splc2_crt_deps)},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static run_t runs[] = {
|
||||||
|
{"spl_cli", NULL}, {"vm_test", NULL}, {"splc0", NULL},
|
||||||
|
{"splc1", "spl_cli"}, {"splc1r", "spl_cli"}, {"splc1s", "spl_cli"},
|
||||||
|
{"splc1n", NULL}, {"splc2", "spl_cli"}, {"splc2n", NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __NOB_DATA_H__ */
|
||||||
File renamed without changes.
@@ -9,8 +9,16 @@
|
|||||||
#ifndef nullptr
|
#ifndef nullptr
|
||||||
#define nullptr NULL
|
#define nullptr NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
typedef uintptr_t usize;
|
typedef uintptr_t usize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPL_ISIZE_TYPEDEF
|
||||||
|
#define SPL_ISIZE_TYPEDEF
|
||||||
typedef intptr_t isize;
|
typedef intptr_t isize;
|
||||||
|
#endif
|
||||||
|
|
||||||
#define MAP_TYPEOF __typeof__
|
#define MAP_TYPEOF __typeof__
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,10 @@
|
|||||||
#ifndef nullptr
|
#ifndef nullptr
|
||||||
#define nullptr NULL
|
#define nullptr NULL
|
||||||
#endif
|
#endif
|
||||||
typedef size_t usize;
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
|
typedef uintptr_t usize;
|
||||||
|
#endif
|
||||||
#define __vec_realloc realloc
|
#define __vec_realloc realloc
|
||||||
#define __vec_free free
|
#define __vec_free free
|
||||||
#define __vec_memcpy memcpy
|
#define __vec_memcpy memcpy
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef __SPL_ASP_H__
|
||||||
|
#define __SPL_ASP_H__
|
||||||
|
|
||||||
|
/* spl_asp.h - 地址空间抽象(Address SPace)
|
||||||
|
*
|
||||||
|
* SPL 侧指针语义由 spl_asp_t 统一描述:
|
||||||
|
* - native(编译器自身):identity,指针就是宿主地址。
|
||||||
|
* - VM:句柄/偏移,offset = (handle<<32) | 区内字节偏移,0 为 null。
|
||||||
|
*
|
||||||
|
* runtime(spl_rt_core.h)的解引用/分配一律经此接口,从而同一份 SPL
|
||||||
|
* 源的 Vec/Map/mem 在两种后端下都自洽。
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
|
typedef uintptr_t usize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct spl_asp spl_asp_t;
|
||||||
|
|
||||||
|
struct spl_asp {
|
||||||
|
void *ctx;
|
||||||
|
/* offset -> 宿主地址;size>0 时做边界检查;失败返回 NULL。 */
|
||||||
|
unsigned char *(*xlate)(void *ctx, uintptr_t off, uintptr_t size);
|
||||||
|
/* 分配 size 字节,返回 offset(0=失败)。 */
|
||||||
|
uintptr_t (*alloc)(void *ctx, uintptr_t size);
|
||||||
|
/* 重新分配 offset@old,返回新 offset(0=失败)。 */
|
||||||
|
uintptr_t (*realloc)(void *ctx, uintptr_t off, uintptr_t size);
|
||||||
|
/* 释放 offset。 */
|
||||||
|
void (*free)(void *ctx, uintptr_t off);
|
||||||
|
/* 宿主地址 -> offset;size>0 时校验范围。不属于该空间返回 0。 */
|
||||||
|
uintptr_t (*to_off)(void *ctx, const void *host, uintptr_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* __SPL_ASP_H__ */
|
||||||
+8
-2
@@ -9,8 +9,15 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
typedef uintptr_t usize;
|
typedef uintptr_t usize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPL_ISIZE_TYPEDEF
|
||||||
|
#define SPL_ISIZE_TYPEDEF
|
||||||
typedef intptr_t isize;
|
typedef intptr_t isize;
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SPL_VOID,
|
SPL_VOID,
|
||||||
@@ -90,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)") \
|
||||||
@@ -192,8 +200,6 @@ void spl_prog_init(spl_prog_t *prog);
|
|||||||
void spl_prog_drop(spl_prog_t *prog);
|
void spl_prog_drop(spl_prog_t *prog);
|
||||||
void spl_prog_dump(spl_prog_t *prog);
|
void spl_prog_dump(spl_prog_t *prog);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog);
|
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog);
|
||||||
int spl_prog_load_from_buffer(const char *buffer, usize len, spl_prog_t *prog);
|
int spl_prog_load_from_buffer(const char *buffer, usize len, spl_prog_t *prog);
|
||||||
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog);
|
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog);
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#ifndef __SPL_RT_H__
|
||||||
|
#define __SPL_RT_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||||
|
#define SPL_FN static inline
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define SPL_FN static __inline__
|
||||||
|
#else
|
||||||
|
#define SPL_FN static
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
typedef uint8_t u8;
|
||||||
|
typedef int8_t i8;
|
||||||
|
typedef uint16_t u16;
|
||||||
|
typedef int16_t i16;
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef int32_t i32;
|
||||||
|
typedef uint64_t u64;
|
||||||
|
typedef int64_t i64;
|
||||||
|
|
||||||
|
#ifndef SPL_USIZE_TYPEDEF
|
||||||
|
#define SPL_USIZE_TYPEDEF
|
||||||
|
typedef u64 usize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPL_ISIZE_TYPEDEF
|
||||||
|
#define SPL_ISIZE_TYPEDEF
|
||||||
|
typedef i64 isize;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPL_FN void spl_panic(const char *msg) {
|
||||||
|
fprintf(stderr, "%s\n", msg ? msg : "panic");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
SPL_FN void spl_trap(void) { abort(); }
|
||||||
|
|
||||||
|
#include "spl_rt_core.h"
|
||||||
|
|
||||||
|
SPL_FN void *vm_fopen(void *path, void *mode) {
|
||||||
|
return (void *)fopen((const char *)path, (const char *)mode);
|
||||||
|
}
|
||||||
|
SPL_FN i32 vm_fclose(void *f) { return f ? fclose((FILE *)f) : 0; }
|
||||||
|
SPL_FN u64 vm_fread(void *buf, u64 sz, u64 n, void *f) {
|
||||||
|
return f ? (u64)fread(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
||||||
|
}
|
||||||
|
SPL_FN u64 vm_fwrite(void *buf, u64 sz, u64 n, void *f) {
|
||||||
|
return f ? (u64)fwrite(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
||||||
|
}
|
||||||
|
SPL_FN u64 vm_fsize(void *f) {
|
||||||
|
long cur, end;
|
||||||
|
if (!f)
|
||||||
|
return 0;
|
||||||
|
cur = ftell((FILE *)f);
|
||||||
|
if (cur < 0)
|
||||||
|
return 0;
|
||||||
|
if (fseek((FILE *)f, 0, SEEK_END) != 0)
|
||||||
|
return 0;
|
||||||
|
end = ftell((FILE *)f);
|
||||||
|
fseek((FILE *)f, cur, SEEK_SET);
|
||||||
|
return end < 0 ? 0 : (u64)end;
|
||||||
|
}
|
||||||
|
SPL_FN void *vm_stdin(void) { return (void *)stdin; }
|
||||||
|
SPL_FN void *vm_stdout(void) { return (void *)stdout; }
|
||||||
|
SPL_FN void *vm_stderr(void) { return (void *)stderr; }
|
||||||
|
SPL_FN i32 vm_putchar(i32 c) { return fputc(c, stdout); }
|
||||||
|
SPL_FN i32 vm_getchar(void) { return fgetc(stdin); }
|
||||||
|
|
||||||
|
SPL_FN void vm_exit(i32 code) { exit(code); }
|
||||||
|
SPL_FN u64 vm_timestamp(u64 kind) {
|
||||||
|
time_t sec = time(NULL);
|
||||||
|
if (kind == 1)
|
||||||
|
return (u64)sec;
|
||||||
|
if (kind == 2)
|
||||||
|
return 0;
|
||||||
|
if (kind == 4)
|
||||||
|
return (u64)sec * 1000000000ull;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SPL_VM_EMBED_IMPLIMENTATION
|
||||||
|
#include "spl_vm_embed.h"
|
||||||
|
|
||||||
|
#endif /* __SPL_RT_H__ */
|
||||||
@@ -0,0 +1,691 @@
|
|||||||
|
#ifndef __SPL_RT_CORE_H__
|
||||||
|
#define __SPL_RT_CORE_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "spl_asp.h"
|
||||||
|
|
||||||
|
#ifndef SPL_CORE_FN
|
||||||
|
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||||
|
#define SPL_CORE_FN static inline
|
||||||
|
#elif defined(__GNUC__) || defined(__clang__)
|
||||||
|
#define SPL_CORE_FN static __inline__
|
||||||
|
#else
|
||||||
|
#define SPL_CORE_FN static
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 地址空间:identity(native/编译器自身)
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
SPL_CORE_FN unsigned char *spl_asp_id_xlate(void *ctx, uintptr_t off, uintptr_t size) {
|
||||||
|
(void)ctx;
|
||||||
|
(void)size;
|
||||||
|
return (unsigned char *)off;
|
||||||
|
}
|
||||||
|
SPL_CORE_FN uintptr_t spl_asp_id_alloc(void *ctx, uintptr_t size) {
|
||||||
|
(void)ctx;
|
||||||
|
return (uintptr_t)malloc((size_t)size);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN uintptr_t spl_asp_id_realloc(void *ctx, uintptr_t off, uintptr_t size) {
|
||||||
|
(void)ctx;
|
||||||
|
return (uintptr_t)realloc((void *)off, (size_t)size);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN void spl_asp_id_free(void *ctx, uintptr_t off) {
|
||||||
|
(void)ctx;
|
||||||
|
free((void *)off);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN uintptr_t spl_asp_id_to_off(void *ctx, const void *host, uintptr_t size) {
|
||||||
|
(void)ctx;
|
||||||
|
(void)size;
|
||||||
|
return (uintptr_t)host;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const spl_asp_t spl_asp_identity = {
|
||||||
|
NULL, spl_asp_id_xlate, spl_asp_id_alloc, spl_asp_id_realloc, spl_asp_id_free,
|
||||||
|
spl_asp_id_to_off,
|
||||||
|
};
|
||||||
|
#define SPL_ASP_ID ((spl_asp_t *)&spl_asp_identity)
|
||||||
|
|
||||||
|
/* 偏移 -> 宿主(不做边界检查)。 */
|
||||||
|
SPL_CORE_FN unsigned char *spl_x(spl_asp_t *asp, uintptr_t off) {
|
||||||
|
return asp->xlate(asp->ctx, off, 0);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN void *spl_xp(spl_asp_t *asp, uintptr_t off, uintptr_t size) {
|
||||||
|
return asp->xlate(asp->ctx, off, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- mem ---------------- */
|
||||||
|
|
||||||
|
SPL_CORE_FN void *spl_alloc_ex(spl_asp_t *asp, uintptr_t sz) {
|
||||||
|
if (sz == 0)
|
||||||
|
return NULL;
|
||||||
|
return (void *)asp->alloc(asp->ctx, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN void spl_free_ex(spl_asp_t *asp, void *p) {
|
||||||
|
if (p)
|
||||||
|
asp->free(asp->ctx, (uintptr_t)p);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN void *spl_realloc_ex(spl_asp_t *asp, void *p, uintptr_t sz) {
|
||||||
|
if (!p)
|
||||||
|
return spl_alloc_ex(asp, sz);
|
||||||
|
if (sz == 0) {
|
||||||
|
asp->free(asp->ctx, (uintptr_t)p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return (void *)asp->realloc(asp->ctx, (uintptr_t)p, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN void *vm_memcpy(void *dst, const void *src, uintptr_t n) {
|
||||||
|
if (n)
|
||||||
|
memcpy(dst, src, (size_t)n);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN void *vm_memmove(void *dst, const void *src, uintptr_t n) {
|
||||||
|
if (n)
|
||||||
|
memmove(dst, src, (size_t)n);
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN intptr_t vm_memcmp(const void *a, const void *b, uintptr_t n) {
|
||||||
|
if (n == 0)
|
||||||
|
return 0;
|
||||||
|
return (intptr_t)memcmp(a, b, (size_t)n);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_bytes_eq_ex(spl_asp_t *asp, const void *a, uintptr_t alen, const void *b,
|
||||||
|
uintptr_t blen) {
|
||||||
|
(void)asp;
|
||||||
|
if (alen != blen)
|
||||||
|
return 0;
|
||||||
|
if (alen == 0)
|
||||||
|
return 1;
|
||||||
|
if (!a || !b)
|
||||||
|
return 0;
|
||||||
|
return memcmp(a, b, (size_t)alen) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- Vec ---------------- */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uintptr_t raw;
|
||||||
|
uintptr_t data;
|
||||||
|
uintptr_t len;
|
||||||
|
uintptr_t cap;
|
||||||
|
uintptr_t elem_size;
|
||||||
|
uintptr_t elem_align;
|
||||||
|
} spl_vec_t;
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_align_ok(uintptr_t a) { return a != 0 && (a & (a - 1)) == 0; }
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_is_valid(const spl_vec_t *v) {
|
||||||
|
uintptr_t a;
|
||||||
|
if (v->elem_size == 0)
|
||||||
|
return 0;
|
||||||
|
a = v->elem_align;
|
||||||
|
if (a == 0 || (a & (a - 1)) != 0)
|
||||||
|
return 0;
|
||||||
|
if (v->len > v->cap)
|
||||||
|
return 0;
|
||||||
|
if (v->cap == 0)
|
||||||
|
return v->raw == 0 && v->data == 0 && v->len == 0;
|
||||||
|
if (v->raw == 0 || v->data == 0)
|
||||||
|
return 0;
|
||||||
|
return (v->data & (a - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN void spl_grow_capacity(uintptr_t current, uintptr_t min_cap, uintptr_t need,
|
||||||
|
uintptr_t *out) {
|
||||||
|
uintptr_t cap = current < min_cap ? min_cap : current;
|
||||||
|
uintptr_t limit = ~(uintptr_t)0;
|
||||||
|
while (cap < need) {
|
||||||
|
if (cap > limit / 2) {
|
||||||
|
cap = need;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cap = cap * 2;
|
||||||
|
}
|
||||||
|
*out = cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_allocate_ex(spl_asp_t *asp, spl_vec_t *out, uintptr_t capacity,
|
||||||
|
uintptr_t elem_size, uintptr_t elem_align) {
|
||||||
|
uintptr_t bytes, mask, addr;
|
||||||
|
uintptr_t raw;
|
||||||
|
if (elem_size == 0)
|
||||||
|
return 1;
|
||||||
|
if (!spl_align_ok(elem_align))
|
||||||
|
return 4;
|
||||||
|
out->raw = 0;
|
||||||
|
out->data = 0;
|
||||||
|
out->len = 0;
|
||||||
|
out->cap = 0;
|
||||||
|
out->elem_size = elem_size;
|
||||||
|
out->elem_align = elem_align;
|
||||||
|
if (capacity == 0)
|
||||||
|
return 0;
|
||||||
|
if (capacity > ~(uintptr_t)0 / elem_size)
|
||||||
|
return 3;
|
||||||
|
bytes = capacity * elem_size;
|
||||||
|
mask = elem_align - 1;
|
||||||
|
if (bytes > ~(uintptr_t)0 - mask)
|
||||||
|
return 3;
|
||||||
|
raw = asp->alloc(asp->ctx, bytes + mask);
|
||||||
|
if (!raw)
|
||||||
|
return 3;
|
||||||
|
addr = raw;
|
||||||
|
if (addr > ~(uintptr_t)0 - mask) {
|
||||||
|
asp->free(asp->ctx, raw);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
out->raw = raw;
|
||||||
|
out->data = (addr + mask) & ~mask;
|
||||||
|
out->cap = capacity;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_reserve_raw_ex(spl_asp_t *asp, spl_vec_t *v, uintptr_t need) {
|
||||||
|
uintptr_t new_cap = 0, used = 0, old;
|
||||||
|
spl_vec_t fresh;
|
||||||
|
int rc;
|
||||||
|
if (need <= v->cap)
|
||||||
|
return 0;
|
||||||
|
spl_grow_capacity(v->cap, 8, need, &new_cap);
|
||||||
|
rc = spl_vec_allocate_ex(asp, &fresh, new_cap, v->elem_size, v->elem_align);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
if (v->len != 0) {
|
||||||
|
if (v->len > ~(uintptr_t)0 / v->elem_size) {
|
||||||
|
asp->free(asp->ctx, fresh.raw);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
used = v->len * v->elem_size;
|
||||||
|
}
|
||||||
|
if (used)
|
||||||
|
memcpy(spl_x(asp, fresh.data), spl_x(asp, v->data), used);
|
||||||
|
fresh.len = v->len;
|
||||||
|
old = v->raw;
|
||||||
|
*v = fresh;
|
||||||
|
if (old)
|
||||||
|
asp->free(asp->ctx, old);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN void spl_vec_release_ex(spl_asp_t *asp, spl_vec_t *v) {
|
||||||
|
uintptr_t es = v->elem_size, ea = v->elem_align;
|
||||||
|
if (v->raw)
|
||||||
|
asp->free(asp->ctx, v->raw);
|
||||||
|
v->raw = 0;
|
||||||
|
v->data = 0;
|
||||||
|
v->len = 0;
|
||||||
|
v->cap = 0;
|
||||||
|
v->elem_size = es;
|
||||||
|
v->elem_align = ea;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_valid_ex(const void *vv) {
|
||||||
|
return vv ? spl_vec_is_valid((const spl_vec_t *)vv) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_push_raw_ex(spl_asp_t *asp, void *vv, const void *elem) {
|
||||||
|
spl_vec_t *v = (spl_vec_t *)vv;
|
||||||
|
if (!v || !elem)
|
||||||
|
return 0;
|
||||||
|
if (v->len < v->cap) {
|
||||||
|
memcpy(spl_x(asp, v->data + v->len * v->elem_size), elem, v->elem_size);
|
||||||
|
v->len++;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_push_ex(spl_asp_t *asp, void *vv, const void *elem) {
|
||||||
|
spl_vec_t *v = (spl_vec_t *)vv;
|
||||||
|
int rc;
|
||||||
|
if (!v || !elem || !spl_vec_is_valid(v))
|
||||||
|
return 1;
|
||||||
|
if (v->len < v->cap) {
|
||||||
|
memcpy(spl_x(asp, v->data + v->len * v->elem_size), elem, v->elem_size);
|
||||||
|
v->len++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
rc = spl_vec_reserve_raw_ex(asp, v, v->len + 1);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
memmove(spl_x(asp, v->data + v->len * v->elem_size), elem, v->elem_size);
|
||||||
|
v->len++;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_get_ex(spl_asp_t *asp, const void *vv, uintptr_t index, void **out) {
|
||||||
|
const spl_vec_t *v = (const spl_vec_t *)vv;
|
||||||
|
unsigned char *p;
|
||||||
|
if (!v || !out)
|
||||||
|
return 1;
|
||||||
|
if (!spl_vec_is_valid(v))
|
||||||
|
return 1;
|
||||||
|
if (index >= v->len)
|
||||||
|
return 2;
|
||||||
|
p = spl_x(asp, v->data) + index * v->elem_size;
|
||||||
|
*out = (void *)(uintptr_t)asp->to_off(asp->ctx, p, v->elem_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_allocate_out_ex(spl_asp_t *asp, void *out, uintptr_t capacity,
|
||||||
|
uintptr_t elem_size, uintptr_t elem_align) {
|
||||||
|
if (!out)
|
||||||
|
return 1;
|
||||||
|
return spl_vec_allocate_ex(asp, (spl_vec_t *)out, capacity, elem_size, elem_align);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_reserve_raw_void_ex(spl_asp_t *asp, void *vv, uintptr_t need) {
|
||||||
|
if (!vv)
|
||||||
|
return 1;
|
||||||
|
return spl_vec_reserve_raw_ex(asp, (spl_vec_t *)vv, need);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_append_ex(spl_asp_t *asp, void *vv, const void *elems, uintptr_t count) {
|
||||||
|
spl_vec_t *v = (spl_vec_t *)vv;
|
||||||
|
uintptr_t copy_bytes, new_len, used, source_offset = 0;
|
||||||
|
int source_inside = 0, rc;
|
||||||
|
if (!v || !spl_vec_is_valid(v))
|
||||||
|
return 1;
|
||||||
|
if (count == 0)
|
||||||
|
return 0;
|
||||||
|
if (!elems)
|
||||||
|
return 1;
|
||||||
|
if (count > ~(uintptr_t)0 / v->elem_size)
|
||||||
|
return 3;
|
||||||
|
copy_bytes = count * v->elem_size;
|
||||||
|
if (v->len > ~(uintptr_t)0 - count)
|
||||||
|
return 3;
|
||||||
|
new_len = v->len + count;
|
||||||
|
used = v->len * v->elem_size;
|
||||||
|
if (used) {
|
||||||
|
unsigned char *base = spl_x(asp, v->data);
|
||||||
|
uintptr_t source = (uintptr_t)elems;
|
||||||
|
if (source >= (uintptr_t)base && source - (uintptr_t)base < used) {
|
||||||
|
source_offset = source - (uintptr_t)base;
|
||||||
|
if (copy_bytes > used - source_offset)
|
||||||
|
return 2;
|
||||||
|
source_inside = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc = spl_vec_reserve_raw_ex(asp, v, new_len);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
if (source_inside)
|
||||||
|
elems = (const void *)(spl_x(asp, v->data) + source_offset);
|
||||||
|
memmove(spl_x(asp, v->data + used), elems, copy_bytes);
|
||||||
|
v->len = new_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_set_ex(spl_asp_t *asp, void *vv, uintptr_t index, const void *elem) {
|
||||||
|
spl_vec_t *v = (spl_vec_t *)vv;
|
||||||
|
if (!v || !elem || !spl_vec_is_valid(v))
|
||||||
|
return 1;
|
||||||
|
if (index >= v->len)
|
||||||
|
return 2;
|
||||||
|
memmove(spl_x(asp, v->data + index * v->elem_size), elem, v->elem_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_pop_ex(spl_asp_t *asp, void *vv, void *out) {
|
||||||
|
spl_vec_t *v = (spl_vec_t *)vv;
|
||||||
|
if (!v || !out || !spl_vec_is_valid(v))
|
||||||
|
return 1;
|
||||||
|
if (v->len == 0)
|
||||||
|
return 2;
|
||||||
|
memmove(out, spl_x(asp, v->data + (v->len - 1) * v->elem_size), v->elem_size);
|
||||||
|
v->len--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_clone_ex(spl_asp_t *asp, void *outp, const void *srcp) {
|
||||||
|
spl_vec_t *out = (spl_vec_t *)outp;
|
||||||
|
const spl_vec_t *src = (const spl_vec_t *)srcp;
|
||||||
|
spl_vec_t fresh;
|
||||||
|
uintptr_t bytes;
|
||||||
|
int rc;
|
||||||
|
if (!out || !src || !spl_vec_is_valid(out) || !spl_vec_is_valid(src))
|
||||||
|
return 1;
|
||||||
|
if (out->elem_size != src->elem_size || out->elem_align != src->elem_align)
|
||||||
|
return 1;
|
||||||
|
fresh.raw = 0;
|
||||||
|
fresh.data = 0;
|
||||||
|
fresh.len = 0;
|
||||||
|
fresh.cap = 0;
|
||||||
|
fresh.elem_size = src->elem_size;
|
||||||
|
fresh.elem_align = src->elem_align;
|
||||||
|
rc = spl_vec_reserve_raw_ex(asp, &fresh, src->len);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
bytes = src->len * src->elem_size;
|
||||||
|
if (bytes)
|
||||||
|
memcpy(spl_x(asp, fresh.data), spl_x(asp, src->data), bytes);
|
||||||
|
fresh.len = src->len;
|
||||||
|
if (out->raw)
|
||||||
|
asp->free(asp->ctx, out->raw);
|
||||||
|
*out = fresh;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_vec_drop_ex(spl_asp_t *asp, void *vv) {
|
||||||
|
if (vv)
|
||||||
|
spl_vec_release_ex(asp, (spl_vec_t *)vv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------------- Map ---------------- */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
spl_vec_t keys;
|
||||||
|
spl_vec_t vals;
|
||||||
|
spl_vec_t states;
|
||||||
|
uintptr_t size;
|
||||||
|
uintptr_t key_size;
|
||||||
|
uintptr_t val_size;
|
||||||
|
uintptr_t hash_kind;
|
||||||
|
} spl_map_t;
|
||||||
|
|
||||||
|
SPL_CORE_FN uintptr_t spl_hash_usize_value(uintptr_t value) {
|
||||||
|
uintptr_t prime = 1099511628211ull;
|
||||||
|
uintptr_t h = value;
|
||||||
|
h = (h ^ (h >> 30)) * prime;
|
||||||
|
h = h ^ (h >> 27);
|
||||||
|
h = h * prime;
|
||||||
|
h = h ^ (h >> 31);
|
||||||
|
if (h == 0)
|
||||||
|
return prime;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN uintptr_t spl_hash_bytes(const void *data, uintptr_t len) {
|
||||||
|
const unsigned char *p = (const unsigned char *)data;
|
||||||
|
uintptr_t h = 14695981039346656037ull;
|
||||||
|
uintptr_t prime = 1099511628211ull;
|
||||||
|
uintptr_t i;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
h = (h ^ (uintptr_t)p[i]) * prime;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN uintptr_t spl_hash_bytes_ex(spl_asp_t *asp, const void *data, uintptr_t len) {
|
||||||
|
return spl_hash_bytes(spl_x(asp, (uintptr_t)data), len);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN uintptr_t spl_map_hash_ex(spl_asp_t *asp, const spl_map_t *m, const void *key) {
|
||||||
|
const uintptr_t *v;
|
||||||
|
if (m->hash_kind == 0)
|
||||||
|
return spl_hash_usize_value(*(const uintptr_t *)key);
|
||||||
|
if (m->hash_kind == 2)
|
||||||
|
return 3;
|
||||||
|
v = (const uintptr_t *)key;
|
||||||
|
return spl_hash_bytes_ex(asp, (const void *)v[0], v[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_eq_ex(spl_asp_t *asp, const spl_map_t *m, const void *a, const void *b) {
|
||||||
|
const uintptr_t *x;
|
||||||
|
const uintptr_t *y;
|
||||||
|
if (m->hash_kind == 0 || m->hash_kind == 2)
|
||||||
|
return *(const uintptr_t *)a == *(const uintptr_t *)b;
|
||||||
|
x = (const uintptr_t *)a;
|
||||||
|
y = (const uintptr_t *)b;
|
||||||
|
if (x[1] != y[1])
|
||||||
|
return 0;
|
||||||
|
if (x[1] == 0)
|
||||||
|
return 1;
|
||||||
|
return memcmp(spl_x(asp, x[0]), spl_x(asp, y[0]), (size_t)x[1]) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_valid(const spl_map_t *m) {
|
||||||
|
if (m->key_size == 0 || m->val_size == 0)
|
||||||
|
return 0;
|
||||||
|
if (m->keys.len != m->states.len || m->vals.len != m->states.len)
|
||||||
|
return 0;
|
||||||
|
if (m->size > m->states.len)
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN uintptr_t spl_map_find_slot_ex(spl_asp_t *asp, const spl_map_t *m, const void *key) {
|
||||||
|
uintptr_t mask, i, probes;
|
||||||
|
unsigned char *st;
|
||||||
|
if (m->states.cap == 0)
|
||||||
|
return ~(uintptr_t)0;
|
||||||
|
mask = m->states.cap - 1;
|
||||||
|
i = spl_map_hash_ex(asp, m, key) & mask;
|
||||||
|
st = spl_x(asp, m->states.data);
|
||||||
|
probes = 0;
|
||||||
|
while (probes < m->states.cap && st[i] != 0) {
|
||||||
|
if (st[i] == 1 &&
|
||||||
|
spl_map_eq_ex(asp, m, spl_x(asp, m->keys.data + i * m->key_size), key))
|
||||||
|
return i;
|
||||||
|
i = (i + 1) & mask;
|
||||||
|
probes++;
|
||||||
|
}
|
||||||
|
return ~(uintptr_t)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN void spl_map_place_ex(spl_asp_t *asp, spl_map_t *m, const void *key, const void *val) {
|
||||||
|
uintptr_t mask = m->states.cap - 1;
|
||||||
|
uintptr_t i = spl_map_hash_ex(asp, m, key) & mask;
|
||||||
|
unsigned char *st = spl_x(asp, m->states.data);
|
||||||
|
uintptr_t first_deleted = ~(uintptr_t)0;
|
||||||
|
uintptr_t probes = 0;
|
||||||
|
while (probes < m->states.cap && st[i] != 0) {
|
||||||
|
if (st[i] == 2 && first_deleted == ~(uintptr_t)0)
|
||||||
|
first_deleted = i;
|
||||||
|
i = (i + 1) & mask;
|
||||||
|
probes++;
|
||||||
|
}
|
||||||
|
if (first_deleted != ~(uintptr_t)0)
|
||||||
|
i = first_deleted;
|
||||||
|
memcpy(spl_x(asp, m->keys.data + i * m->key_size), key, m->key_size);
|
||||||
|
memcpy(spl_x(asp, m->vals.data + i * m->val_size), val, m->val_size);
|
||||||
|
st[i] = 1;
|
||||||
|
m->size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_reserve_ex(spl_asp_t *asp, spl_map_t *m, uintptr_t need) {
|
||||||
|
uintptr_t new_cap = 0, i;
|
||||||
|
spl_map_t fresh;
|
||||||
|
int rc;
|
||||||
|
if (!spl_map_valid(m))
|
||||||
|
return 1;
|
||||||
|
if (need <= m->states.cap)
|
||||||
|
return 0;
|
||||||
|
spl_grow_capacity(m->states.cap, 8, need, &new_cap);
|
||||||
|
fresh = *m;
|
||||||
|
fresh.keys.raw = 0;
|
||||||
|
fresh.keys.data = 0;
|
||||||
|
fresh.keys.len = 0;
|
||||||
|
fresh.keys.cap = 0;
|
||||||
|
fresh.vals.raw = 0;
|
||||||
|
fresh.vals.data = 0;
|
||||||
|
fresh.vals.len = 0;
|
||||||
|
fresh.vals.cap = 0;
|
||||||
|
fresh.states.raw = 0;
|
||||||
|
fresh.states.data = 0;
|
||||||
|
fresh.states.len = 0;
|
||||||
|
fresh.states.cap = 0;
|
||||||
|
fresh.size = 0;
|
||||||
|
rc = spl_vec_reserve_raw_ex(asp, &fresh.keys, new_cap);
|
||||||
|
if (rc)
|
||||||
|
goto fail;
|
||||||
|
rc = spl_vec_reserve_raw_ex(asp, &fresh.vals, new_cap);
|
||||||
|
if (rc)
|
||||||
|
goto fail;
|
||||||
|
rc = spl_vec_reserve_raw_ex(asp, &fresh.states, new_cap);
|
||||||
|
if (rc)
|
||||||
|
goto fail;
|
||||||
|
fresh.keys.len = new_cap;
|
||||||
|
fresh.vals.len = new_cap;
|
||||||
|
fresh.states.len = new_cap;
|
||||||
|
memset(spl_x(asp, fresh.states.data), 0, new_cap);
|
||||||
|
for (i = 0; i < m->states.len; i++) {
|
||||||
|
if (spl_x(asp, m->states.data)[i] == 1)
|
||||||
|
spl_map_place_ex(asp, &fresh, spl_x(asp, m->keys.data + i * m->key_size),
|
||||||
|
spl_x(asp, m->vals.data + i * m->val_size));
|
||||||
|
}
|
||||||
|
spl_vec_release_ex(asp, &m->keys);
|
||||||
|
spl_vec_release_ex(asp, &m->vals);
|
||||||
|
spl_vec_release_ex(asp, &m->states);
|
||||||
|
*m = fresh;
|
||||||
|
return 0;
|
||||||
|
fail:
|
||||||
|
spl_vec_release_ex(asp, &fresh.keys);
|
||||||
|
spl_vec_release_ex(asp, &fresh.vals);
|
||||||
|
spl_vec_release_ex(asp, &fresh.states);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_valid_ex(const void *mm) {
|
||||||
|
return mm ? spl_map_valid((const spl_map_t *)mm) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_reserve_void_ex(spl_asp_t *asp, void *mm, uintptr_t need) {
|
||||||
|
if (!mm)
|
||||||
|
return 1;
|
||||||
|
return spl_map_reserve_ex(asp, (spl_map_t *)mm, need);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_put_ex(spl_asp_t *asp, void *mm, const void *key, const void *val) {
|
||||||
|
spl_map_t *m = (spl_map_t *)mm;
|
||||||
|
uintptr_t existing;
|
||||||
|
if (!m || !key || !val || !spl_map_valid(m))
|
||||||
|
return 1;
|
||||||
|
existing = spl_map_find_slot_ex(asp, m, key);
|
||||||
|
if (existing != ~(uintptr_t)0) {
|
||||||
|
memcpy(spl_x(asp, m->vals.data + existing * m->val_size), val, m->val_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (m->states.cap == 0) {
|
||||||
|
int rc = spl_map_reserve_ex(asp, m, 8);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
} else {
|
||||||
|
uintptr_t next_size, scaled;
|
||||||
|
if (m->size > ~(uintptr_t)0 - 1)
|
||||||
|
return 3;
|
||||||
|
next_size = m->size + 1;
|
||||||
|
if (next_size > ~(uintptr_t)0 / 128)
|
||||||
|
return 3;
|
||||||
|
scaled = next_size * 128;
|
||||||
|
if (scaled / m->states.cap >= 70) {
|
||||||
|
int rc;
|
||||||
|
if (m->states.cap > ~(uintptr_t)0 / 2)
|
||||||
|
return 3;
|
||||||
|
rc = spl_map_reserve_ex(asp, m, m->states.cap * 2);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_map_place_ex(asp, m, key, val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_get_ex(spl_asp_t *asp, void *mm, const void *key, void *out) {
|
||||||
|
spl_map_t *m = (spl_map_t *)mm;
|
||||||
|
uintptr_t i;
|
||||||
|
if (!m || !key || !out || !spl_map_valid(m))
|
||||||
|
return 1;
|
||||||
|
i = spl_map_find_slot_ex(asp, m, key);
|
||||||
|
if (i == ~(uintptr_t)0)
|
||||||
|
return 1;
|
||||||
|
memcpy(out, spl_x(asp, m->vals.data + i * m->val_size), m->val_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_del_ex(spl_asp_t *asp, void *mm, const void *key) {
|
||||||
|
spl_map_t *m = (spl_map_t *)mm;
|
||||||
|
uintptr_t i;
|
||||||
|
if (!m || !key || !spl_map_valid(m))
|
||||||
|
return 1;
|
||||||
|
i = spl_map_find_slot_ex(asp, m, key);
|
||||||
|
if (i == ~(uintptr_t)0)
|
||||||
|
return 1;
|
||||||
|
spl_x(asp, m->states.data)[i] = 2;
|
||||||
|
m->size--;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_clear_ex(spl_asp_t *asp, void *mm) {
|
||||||
|
spl_map_t *m = (spl_map_t *)mm;
|
||||||
|
if (!m)
|
||||||
|
return 0;
|
||||||
|
if (m->states.data && m->states.len)
|
||||||
|
memset(spl_x(asp, m->states.data), 0, m->states.len);
|
||||||
|
m->size = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int spl_map_drop_ex(spl_asp_t *asp, void *mm) {
|
||||||
|
spl_map_t *m = (spl_map_t *)mm;
|
||||||
|
if (!m)
|
||||||
|
return 0;
|
||||||
|
spl_vec_release_ex(asp, &m->keys);
|
||||||
|
spl_vec_release_ex(asp, &m->vals);
|
||||||
|
spl_vec_release_ex(asp, &m->states);
|
||||||
|
m->size = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 公开 C 符号(identity):供 native 编译器直接调用
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
SPL_CORE_FN void *vm_alloc(uintptr_t sz) { return spl_alloc_ex(SPL_ASP_ID, sz); }
|
||||||
|
SPL_CORE_FN void vm_free(void *p) { spl_free_ex(SPL_ASP_ID, p); }
|
||||||
|
SPL_CORE_FN void *vm_realloc(void *p, uintptr_t sz) { return spl_realloc_ex(SPL_ASP_ID, p, sz); }
|
||||||
|
SPL_CORE_FN int vm_bytes_eq(const void *a, uintptr_t alen, const void *b, uintptr_t blen) {
|
||||||
|
return spl_bytes_eq_ex(SPL_ASP_ID, a, alen, b, blen);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPL_CORE_FN int vm_vec_valid(const void *v) { return spl_vec_valid_ex(v); }
|
||||||
|
SPL_CORE_FN int vm_vec_push_raw(void *v, const void *e) {
|
||||||
|
return spl_vec_push_raw_ex(SPL_ASP_ID, v, e);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_vec_push(void *v, const void *e) { return spl_vec_push_ex(SPL_ASP_ID, v, e); }
|
||||||
|
SPL_CORE_FN int vm_vec_get(const void *v, uintptr_t i, void **o) {
|
||||||
|
return spl_vec_get_ex(SPL_ASP_ID, v, i, o);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_vec_allocate(void *o, uintptr_t c, uintptr_t es, uintptr_t ea) {
|
||||||
|
return spl_vec_allocate_out_ex(SPL_ASP_ID, o, c, es, ea);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_vec_reserve_raw(void *v, uintptr_t n) {
|
||||||
|
return spl_vec_reserve_raw_void_ex(SPL_ASP_ID, v, n);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_vec_append(void *v, const void *e, uintptr_t n) {
|
||||||
|
return spl_vec_append_ex(SPL_ASP_ID, v, e, n);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_vec_set(void *v, uintptr_t i, const void *e) {
|
||||||
|
return spl_vec_set_ex(SPL_ASP_ID, v, i, e);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_vec_pop(void *v, void *o) { return spl_vec_pop_ex(SPL_ASP_ID, v, o); }
|
||||||
|
SPL_CORE_FN int vm_vec_clone(void *o, const void *s) { return spl_vec_clone_ex(SPL_ASP_ID, o, s); }
|
||||||
|
SPL_CORE_FN int vm_vec_drop(void *v) { return spl_vec_drop_ex(SPL_ASP_ID, v); }
|
||||||
|
|
||||||
|
SPL_CORE_FN int vm_map_valid(const void *m) { return spl_map_valid_ex(m); }
|
||||||
|
SPL_CORE_FN int vm_map_reserve(void *m, uintptr_t n) {
|
||||||
|
return spl_map_reserve_void_ex(SPL_ASP_ID, m, n);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_map_put(void *m, const void *k, const void *v) {
|
||||||
|
return spl_map_put_ex(SPL_ASP_ID, m, k, v);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_map_get(void *m, const void *k, void *o) {
|
||||||
|
return spl_map_get_ex(SPL_ASP_ID, m, k, o);
|
||||||
|
}
|
||||||
|
SPL_CORE_FN int vm_map_del(void *m, const void *k) { return spl_map_del_ex(SPL_ASP_ID, m, k); }
|
||||||
|
SPL_CORE_FN int vm_map_clear(void *m) { return spl_map_clear_ex(SPL_ASP_ID, m); }
|
||||||
|
SPL_CORE_FN int vm_map_drop(void *m) { return spl_map_drop_ex(SPL_ASP_ID, m); }
|
||||||
|
|
||||||
|
#endif /* __SPL_RT_CORE_H__ */
|
||||||
+566
-825
File diff suppressed because it is too large.
Load diff
+424
-28
@@ -568,6 +568,18 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
|||||||
vec_realloc(vm->frames, (usize)vm->config.max_call_depth);
|
vec_realloc(vm->frames, (usize)vm->config.max_call_depth);
|
||||||
vec_init(vm->breakpoints);
|
vec_init(vm->breakpoints);
|
||||||
vec_init(vm->fn_breakpoints);
|
vec_init(vm->fn_breakpoints);
|
||||||
|
vec_init(vm->gdata_off);
|
||||||
|
vec_init(vm->regions);
|
||||||
|
|
||||||
|
/* regions[0] 保留;regions[1] = 栈区。 */
|
||||||
|
vec_push(vm->regions, ((spl_vm_region_t){NULL, 0, 0}));
|
||||||
|
vec_push(vm->regions, ((spl_vm_region_t){
|
||||||
|
(unsigned char *)vm->stacks.data,
|
||||||
|
(usize)vm->config.max_stack_depth * sizeof(spl_vm_val_t), 1}));
|
||||||
|
vm->stdio_off[0] = vm->stdio_off[1] = vm->stdio_off[2] = 0;
|
||||||
|
vm->ret_val = 0;
|
||||||
|
vm->foreign = NULL;
|
||||||
|
vm->foreign_ctx = NULL;
|
||||||
|
|
||||||
vm->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0;
|
vm->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0;
|
||||||
vm->steps = 0;
|
vm->steps = 0;
|
||||||
@@ -593,9 +605,214 @@ void spl_vm_drop(spl_vm_t *vm) {
|
|||||||
free(vm->fn_breakpoints.data[i]);
|
free(vm->fn_breakpoints.data[i]);
|
||||||
vec_free(vm->fn_breakpoints);
|
vec_free(vm->fn_breakpoints);
|
||||||
vec_free(vm->breakpoints);
|
vec_free(vm->breakpoints);
|
||||||
|
vec_free(vm->gdata_off);
|
||||||
|
for (usize i = 2; i < vec_size(vm->regions); i++) {
|
||||||
|
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||||
|
if (r->used && r->base)
|
||||||
|
free(r->base);
|
||||||
|
}
|
||||||
|
vec_free(vm->regions);
|
||||||
spl_vm_profile_free(vm);
|
spl_vm_profile_free(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 统一偏移地址空间:offset = (handle<<32) | 区内字节偏移
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
#define SPL_ASP_HANDLE_OF(off) ((usize)((spl_vm_val_t)(off) >> 32))
|
||||||
|
#define SPL_ASP_BYTE_OF(off) ((usize)((spl_vm_val_t)(off) & 0xFFFFFFFFull))
|
||||||
|
|
||||||
|
static spl_vm_region_t *spl_vm_region_at(spl_vm_t *vm, usize handle) {
|
||||||
|
if (handle == 0 || handle >= vec_size(vm->regions))
|
||||||
|
return NULL;
|
||||||
|
return &vec_at(vm->regions, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *spl_vm_xlate(spl_vm_t *vm, spl_vm_val_t off, usize size) {
|
||||||
|
usize handle, byte;
|
||||||
|
spl_vm_region_t *r;
|
||||||
|
if (!vm || off == SPL_OFF_NULL)
|
||||||
|
return NULL;
|
||||||
|
handle = SPL_ASP_HANDLE_OF(off);
|
||||||
|
byte = SPL_ASP_BYTE_OF(off);
|
||||||
|
r = spl_vm_region_at(vm, handle);
|
||||||
|
if (!r || !r->used)
|
||||||
|
return NULL;
|
||||||
|
if (byte > r->size || (size > 0 && byte + size > r->size))
|
||||||
|
return NULL;
|
||||||
|
return (void *)(r->base + byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_vm_val_t spl_vm_to_off(spl_vm_t *vm, const void *ptr) {
|
||||||
|
if (!vm || !ptr)
|
||||||
|
return SPL_OFF_NULL;
|
||||||
|
{
|
||||||
|
unsigned char *p = (unsigned char *)ptr;
|
||||||
|
usize i;
|
||||||
|
for (i = 1; i < vec_size(vm->regions); i++) {
|
||||||
|
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||||
|
if (r->used && r->base && p >= r->base && p < r->base + r->size)
|
||||||
|
return SPL_ASP_OFF(i, (usize)(p - r->base));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SPL_OFF_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 返回用于存放该偏移的 handle 槽;找不到空闲/空间不足返回 0。 */
|
||||||
|
static usize spl_vm_region_new(spl_vm_t *vm, void *base, usize size) {
|
||||||
|
usize i;
|
||||||
|
for (i = 2; i < vec_size(vm->regions); i++) {
|
||||||
|
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||||
|
if (!r->used) {
|
||||||
|
r->base = (unsigned char *)base;
|
||||||
|
r->size = size;
|
||||||
|
r->used = 1;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (vec_size(vm->regions) >= (usize)1 << 32)
|
||||||
|
return 0;
|
||||||
|
vec_push(vm->regions, ((spl_vm_region_t){(unsigned char *)base, size, 1}));
|
||||||
|
return vec_size(vm->regions) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- spl_asp_t 实现 ---- */
|
||||||
|
|
||||||
|
static unsigned char *spl_vm_asp_xlate(void *ctx, uintptr_t off, uintptr_t size) {
|
||||||
|
return (unsigned char *)spl_vm_xlate((spl_vm_t *)ctx, (spl_vm_val_t)off, (usize)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uintptr_t spl_vm_asp_alloc(void *ctx, uintptr_t size) {
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)ctx;
|
||||||
|
void *p;
|
||||||
|
usize h;
|
||||||
|
if (size == 0 || size > 0xFFFFFFFFull)
|
||||||
|
return 0;
|
||||||
|
p = malloc((size_t)size);
|
||||||
|
if (!p)
|
||||||
|
return 0;
|
||||||
|
h = spl_vm_region_new(vm, p, (usize)size);
|
||||||
|
if (h == 0) {
|
||||||
|
free(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (uintptr_t)SPL_ASP_OFF(h, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uintptr_t spl_vm_asp_realloc(void *ctx, uintptr_t off, uintptr_t size) {
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)ctx;
|
||||||
|
spl_vm_region_t *r;
|
||||||
|
void *np;
|
||||||
|
usize h;
|
||||||
|
if (size == 0 || size > 0xFFFFFFFFull)
|
||||||
|
return 0;
|
||||||
|
if (off == 0)
|
||||||
|
return spl_vm_asp_alloc(ctx, size);
|
||||||
|
h = SPL_ASP_HANDLE_OF(off);
|
||||||
|
r = spl_vm_region_at(vm, h);
|
||||||
|
if (!r || !r->used)
|
||||||
|
return 0;
|
||||||
|
np = realloc(r->base, (size_t)size);
|
||||||
|
if (!np)
|
||||||
|
return 0;
|
||||||
|
r->base = (unsigned char *)np;
|
||||||
|
r->size = (usize)size;
|
||||||
|
return (uintptr_t)SPL_ASP_OFF(h, SPL_ASP_BYTE_OF(off) & 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void spl_vm_asp_free(void *ctx, uintptr_t off) {
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)ctx;
|
||||||
|
usize h;
|
||||||
|
spl_vm_region_t *r;
|
||||||
|
if (off == 0 || !vm)
|
||||||
|
return;
|
||||||
|
h = SPL_ASP_HANDLE_OF(off);
|
||||||
|
if (h < 2) /* 0/1 不可释放 */
|
||||||
|
return;
|
||||||
|
r = spl_vm_region_at(vm, h);
|
||||||
|
if (!r || !r->used)
|
||||||
|
return;
|
||||||
|
free(r->base);
|
||||||
|
r->base = NULL;
|
||||||
|
r->size = 0;
|
||||||
|
r->used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uintptr_t spl_vm_asp_to_off(void *ctx, const void *host, uintptr_t size) {
|
||||||
|
(void)size;
|
||||||
|
return (uintptr_t)spl_vm_to_off((spl_vm_t *)ctx, host);
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_asp_t *spl_vm_asp(spl_vm_t *vm) {
|
||||||
|
if (!vm)
|
||||||
|
return NULL;
|
||||||
|
vm->asp.ctx = vm;
|
||||||
|
vm->asp.xlate = spl_vm_asp_xlate;
|
||||||
|
vm->asp.alloc = spl_vm_asp_alloc;
|
||||||
|
vm->asp.realloc = spl_vm_asp_realloc;
|
||||||
|
vm->asp.free = spl_vm_asp_free;
|
||||||
|
vm->asp.to_off = spl_vm_asp_to_off;
|
||||||
|
return &vm->asp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 动态外部回调 + 栈/结果/内存访问
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
void spl_vm_set_foreign(spl_vm_t *vm, spl_vm_foreign_fn fn, void *ctx) {
|
||||||
|
if (!vm)
|
||||||
|
return;
|
||||||
|
vm->foreign = fn;
|
||||||
|
vm->foreign_ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
spl_vm_val_t spl_vm_ret_val(spl_vm_t *vm) { return vm ? vm->ret_val : 0; }
|
||||||
|
|
||||||
|
usize spl_vm_sp(spl_vm_t *vm) { return vm ? (usize)vm->sp : 0; }
|
||||||
|
|
||||||
|
usize spl_vm_fp(spl_vm_t *vm) { return vm ? (usize)vm->fp : 0; }
|
||||||
|
|
||||||
|
spl_vm_val_t spl_vm_stack_get(spl_vm_t *vm, usize index) {
|
||||||
|
if (!vm || index >= (usize)vm->config.max_stack_depth || !vm->stacks.data)
|
||||||
|
return 0;
|
||||||
|
return vm->stacks.data[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
int spl_vm_stack_set(spl_vm_t *vm, usize index, spl_vm_val_t val) {
|
||||||
|
if (!vm || index >= (usize)vm->config.max_stack_depth || !vm->stacks.data)
|
||||||
|
return -1;
|
||||||
|
vm->stacks.data[index] = 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) {
|
||||||
|
void *src = spl_vm_xlate(vm, off, n);
|
||||||
|
if ((!src || !dst) && n)
|
||||||
|
return -1;
|
||||||
|
if (n)
|
||||||
|
memcpy(dst, src, n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int spl_vm_mem_write(spl_vm_t *vm, spl_vm_val_t off, const void *src, usize n) {
|
||||||
|
void *dst = spl_vm_xlate(vm, off, n);
|
||||||
|
if ((!dst || !src) && n)
|
||||||
|
return -1;
|
||||||
|
if (n)
|
||||||
|
memcpy(dst, src, n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
int spl_vm_addr_valid(uintptr_t p, usize size) {
|
int spl_vm_addr_valid(uintptr_t p, usize size) {
|
||||||
if (p == 0)
|
if (p == 0)
|
||||||
@@ -685,6 +902,35 @@ int spl_vm_load_prog(spl_vm_t *vm, spl_prog_t *prog) {
|
|||||||
if (!vm || !prog)
|
if (!vm || !prog)
|
||||||
return -1;
|
return -1;
|
||||||
vm->prog = prog;
|
vm->prog = prog;
|
||||||
|
|
||||||
|
/* 释放上一次加载的堆分配(保留 regions[0]/[1])。 */
|
||||||
|
for (usize i = 2; i < vec_size(vm->regions); i++) {
|
||||||
|
spl_vm_region_t *r = &vec_at(vm->regions, i);
|
||||||
|
if (r->used && r->base)
|
||||||
|
free(r->base);
|
||||||
|
r->base = NULL;
|
||||||
|
r->size = 0;
|
||||||
|
r->used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 把 prog 的常量段搬进本 VM 空间,并记录每个 gdata 的偏移;
|
||||||
|
* gdata 索引 0 为哨兵,偏移 0(null)。 */
|
||||||
|
vm->gdata_off.size = 0;
|
||||||
|
vec_push(vm->gdata_off, (usize)SPL_OFF_NULL);
|
||||||
|
for (usize i = 1; i < vec_size(prog->gdata); i++) {
|
||||||
|
spl_vm_gdata_t *gd = &vec_at(prog->gdata, i);
|
||||||
|
usize sz = gd->size ? gd->size : 1;
|
||||||
|
spl_vm_val_t off = spl_vm_asp_alloc(vm, sz);
|
||||||
|
unsigned char *dst;
|
||||||
|
if (off == SPL_OFF_NULL)
|
||||||
|
return -1;
|
||||||
|
dst = (unsigned char *)spl_vm_xlate(vm, off, sz);
|
||||||
|
if (!dst)
|
||||||
|
return -1;
|
||||||
|
if (gd->data && gd->size)
|
||||||
|
memcpy(dst, gd->data, gd->size);
|
||||||
|
vec_push(vm->gdata_off, (usize)off);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -707,21 +953,53 @@ void spl_vm_set_debug(spl_vm_t *vm, int enabled) {
|
|||||||
|
|
||||||
#define STACK_CANARY(vm) (vm)->stacks.data[(vm)->fp - 1]
|
#define STACK_CANARY(vm) (vm)->stacks.data[(vm)->fp - 1]
|
||||||
|
|
||||||
|
/* 把 C 字符串复制进 VM 空间,返回其偏移。 */
|
||||||
|
static spl_vm_val_t spl_vm_marshal_cstr(spl_vm_t *vm, const char *s) {
|
||||||
|
usize n = s ? strlen(s) + 1 : 1;
|
||||||
|
spl_vm_val_t off = spl_vm_asp_alloc(vm, n);
|
||||||
|
unsigned char *d;
|
||||||
|
if (off == 0)
|
||||||
|
return 0;
|
||||||
|
d = (unsigned char *)spl_vm_xlate(vm, off, n);
|
||||||
|
if (!d)
|
||||||
|
return 0;
|
||||||
|
if (s)
|
||||||
|
memcpy(d, s, n);
|
||||||
|
else
|
||||||
|
d[0] = 0;
|
||||||
|
return off;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 把 argv/envp 指针数组整体编组进 VM 空间,返回数组偏移。 */
|
||||||
|
static spl_vm_val_t spl_vm_marshal_argv(spl_vm_t *vm, int argc, const char **argv) {
|
||||||
|
usize bytes = (usize)(argc + 1) * sizeof(spl_vm_val_t);
|
||||||
|
spl_vm_val_t arr = spl_vm_asp_alloc(vm, bytes);
|
||||||
|
spl_vm_val_t *slots;
|
||||||
|
int i;
|
||||||
|
if (arr == 0)
|
||||||
|
return 0;
|
||||||
|
slots = (spl_vm_val_t *)spl_vm_xlate(vm, arr, bytes);
|
||||||
|
if (!slots)
|
||||||
|
return 0;
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
slots[i] = spl_vm_marshal_cstr(vm, argv ? argv[i] : NULL);
|
||||||
|
slots[argc] = 0;
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ABI:调用者在实参下方预留 2 个空闲 slot(PAD0/PAD1)。
|
||||||
|
* fp 指向 arg0;fp-1 为栈金丝雀槽,fp-2 为空闲槽。
|
||||||
|
* saved_sp = fp-2(调用者压参数前的 sp)。 */
|
||||||
|
#define SPL_CALL_PADS 2
|
||||||
|
|
||||||
static inline int spl_vm_call(spl_vm_t *vm, spl_vm_val_t addr, spl_vm_val_t nargs) {
|
static inline int spl_vm_call(spl_vm_t *vm, spl_vm_val_t addr, spl_vm_val_t nargs) {
|
||||||
|
uintptr_t saved_sp;
|
||||||
if (vm->cp >= vm->config.max_call_depth)
|
if (vm->cp >= vm->config.max_call_depth)
|
||||||
VM_ERROR("call depth exceeded");
|
VM_ERROR("call depth exceeded");
|
||||||
|
if (vm->sp < nargs + SPL_CALL_PADS)
|
||||||
|
VM_ERROR("call: stack underflow (missing pads)");
|
||||||
|
|
||||||
uintptr_t saved_sp;
|
saved_sp = vm->sp - nargs - SPL_CALL_PADS;
|
||||||
if (vm->debug) {
|
|
||||||
/* debug:在 fp-1 留金丝雀位,需要把实参右移一格。 */
|
|
||||||
PUSH(SPL_STACK_CANARY);
|
|
||||||
for (usize i = vm->sp; i >= vm->sp - nargs; --i)
|
|
||||||
vm->stacks.data[i] = vm->stacks.data[i - 1];
|
|
||||||
saved_sp = vm->sp - nargs - 1;
|
|
||||||
} else {
|
|
||||||
/* 非 debug:不做金丝雀搬移;实参在 sp-nargs..sp-1,fp 指向 arg0。 */
|
|
||||||
saved_sp = vm->sp - nargs;
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_callframe_t *fr = &vm->frames.data[vm->cp];
|
spl_callframe_t *fr = &vm->frames.data[vm->cp];
|
||||||
fr->saved_sp = saved_sp;
|
fr->saved_sp = saved_sp;
|
||||||
@@ -736,6 +1014,33 @@ static inline int spl_vm_call(spl_vm_t *vm, spl_vm_val_t addr, spl_vm_val_t narg
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 入口调用:按名字调用顶层函数(同 CALL 的 ABI,实参+2 pads 已由调用方压好)。 */
|
||||||
|
int spl_vm_call_entry(spl_vm_t *vm, const char *name, int nargs) {
|
||||||
|
spl_vm_func_t *func;
|
||||||
|
if (!vm || !vm->prog || !name)
|
||||||
|
return -1;
|
||||||
|
func = spl_prog_get_func(vm->prog, name);
|
||||||
|
if (!func)
|
||||||
|
return -1;
|
||||||
|
if (vm->cp >= vm->config.max_call_depth)
|
||||||
|
return -1;
|
||||||
|
if (vm->sp < (usize)nargs + SPL_CALL_PADS)
|
||||||
|
return -1;
|
||||||
|
vm->ip = (usize)-1;
|
||||||
|
vm->cp = 0;
|
||||||
|
vm->fp = 0;
|
||||||
|
vm->frames.data[0].saved_sp = vm->sp - (usize)nargs - SPL_CALL_PADS;
|
||||||
|
vm->frames.data[0].saved_fp = vm->fp;
|
||||||
|
vm->frames.data[0].saved_ip = vm->ip;
|
||||||
|
vm->frames.data[0].nargs = (spl_vm_val_t)nargs;
|
||||||
|
vm->cp = 1;
|
||||||
|
vm->ip = (usize)func->address;
|
||||||
|
vm->fp = vm->sp - (usize)nargs;
|
||||||
|
if (vm->debug && vm->fp > 0)
|
||||||
|
STACK_CANARY(vm) = SPL_STACK_CANARY;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
||||||
const char **envp) {
|
const char **envp) {
|
||||||
if (!vm || !vm->prog) {
|
if (!vm || !vm->prog) {
|
||||||
@@ -757,15 +1062,22 @@ int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
|||||||
vm->cp = 0;
|
vm->cp = 0;
|
||||||
vm->ip = -1;
|
vm->ip = -1;
|
||||||
vm->exit_code = 0;
|
vm->exit_code = 0;
|
||||||
|
vm->ret_val = 0;
|
||||||
|
|
||||||
|
/* ABI:入口也在实参下方预留 2 个空闲 slot。 */
|
||||||
|
PUSH(0);
|
||||||
|
PUSH(0);
|
||||||
if (fn->nargs >= 1) {
|
if (fn->nargs >= 1) {
|
||||||
PUSH(argc);
|
PUSH(argc);
|
||||||
}
|
}
|
||||||
if (fn->nargs >= 2) {
|
if (fn->nargs >= 2) {
|
||||||
PUSH(argv);
|
PUSH(spl_vm_marshal_argv(vm, argc, argv));
|
||||||
}
|
}
|
||||||
if (fn->nargs >= 3) {
|
if (fn->nargs >= 3) {
|
||||||
PUSH(envp);
|
int envc = 0;
|
||||||
|
while (envp && envp[envc])
|
||||||
|
envc++;
|
||||||
|
PUSH(spl_vm_marshal_argv(vm, envc, envp));
|
||||||
}
|
}
|
||||||
if (fn->nargs >= 4) {
|
if (fn->nargs >= 4) {
|
||||||
printf("the start symbol can't using more than 3 args");
|
printf("the start symbol can't using more than 3 args");
|
||||||
@@ -777,6 +1089,53 @@ int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 外部 -> VM 的调用:保存外层状态,按当前深度建帧跑到边界,读结果后恢复。
|
||||||
|
* 可在 VM 运行中(如外部回调内)安全调用。 */
|
||||||
|
int spl_vm_invoke(spl_vm_t *vm, spl_vm_call_t *call) {
|
||||||
|
spl_vm_func_t *fn;
|
||||||
|
uintptr_t s_ip, s_fp, s_sp, s_cp;
|
||||||
|
int s_exit, i, r;
|
||||||
|
spl_vm_val_t s_ret;
|
||||||
|
if (!vm || !vm->prog || !call || !call->name)
|
||||||
|
return -1;
|
||||||
|
fn = spl_prog_get_func(vm->prog, call->name);
|
||||||
|
if (!fn)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
s_ip = vm->ip;
|
||||||
|
s_fp = vm->fp;
|
||||||
|
s_sp = vm->sp;
|
||||||
|
s_cp = vm->cp;
|
||||||
|
s_exit = vm->exit_code;
|
||||||
|
s_ret = vm->ret_val;
|
||||||
|
|
||||||
|
PUSH(0);
|
||||||
|
PUSH(0);
|
||||||
|
for (i = 0; i < (int)call->nargs; i++)
|
||||||
|
PUSH(call->args ? call->args[i] : 0);
|
||||||
|
/* sentinel:本次调用的 RET 视为边界(与入口调用一致)。 */
|
||||||
|
vm->ip = (spl_vm_val_t)-1;
|
||||||
|
if (spl_vm_call(vm, fn->address, (spl_vm_val_t)call->nargs) != 0) {
|
||||||
|
vm->ip = s_ip;
|
||||||
|
vm->fp = s_fp;
|
||||||
|
vm->sp = s_sp;
|
||||||
|
vm->cp = s_cp;
|
||||||
|
vm->exit_code = s_exit;
|
||||||
|
vm->ret_val = s_ret;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
r = spl_vm_run_until(vm, 0);
|
||||||
|
call->ret = vm->ret_val;
|
||||||
|
|
||||||
|
vm->ip = s_ip;
|
||||||
|
vm->fp = s_fp;
|
||||||
|
vm->sp = s_sp;
|
||||||
|
vm->cp = s_cp;
|
||||||
|
vm->exit_code = s_exit;
|
||||||
|
vm->ret_val = s_ret;
|
||||||
|
return r < 0 ? -1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* spl_vm_run_once - execute one instruction
|
* spl_vm_run_once - execute one instruction
|
||||||
*
|
*
|
||||||
@@ -817,7 +1176,7 @@ static uint64_t prof_now_ns(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void spl_vm_sample(spl_vm_t *vm) {
|
static void spl_vm_sample(spl_vm_t *vm) {
|
||||||
spl_prof_t *p = (spl_prof_t *)vm->profile;
|
spl_prof_t *p = vm->profile;
|
||||||
spl_prog_t *prog = vm->prog;
|
spl_prog_t *prog = vm->prog;
|
||||||
if (!p || !prog)
|
if (!p || !prog)
|
||||||
return;
|
return;
|
||||||
@@ -862,7 +1221,7 @@ static void spl_vm_sample(spl_vm_t *vm) {
|
|||||||
|
|
||||||
usize old = 0;
|
usize old = 0;
|
||||||
if (map_get(p->stacks, buf, &old)) {
|
if (map_get(p->stacks, buf, &old)) {
|
||||||
map_put(p->stacks, buf, old + 1);
|
map_put(p->stacks, strdup(buf), old + 1);
|
||||||
} else {
|
} else {
|
||||||
map_put(p->stacks, strdup(buf), (usize)1);
|
map_put(p->stacks, strdup(buf), (usize)1);
|
||||||
}
|
}
|
||||||
@@ -1007,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,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1250,6 +1610,8 @@ L_SPL_RET:
|
|||||||
intptr_t _saved_ip = vm->frames.data[vm->cp].saved_ip;
|
intptr_t _saved_ip = vm->frames.data[vm->cp].saved_ip;
|
||||||
/* entry return -> halt */
|
/* entry return -> halt */
|
||||||
if (_saved_ip < 0) {
|
if (_saved_ip < 0) {
|
||||||
|
PUSH(_retval);
|
||||||
|
vm->ret_val = _retval;
|
||||||
vm->exit_code = (int)_retval;
|
vm->exit_code = (int)_retval;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -1263,6 +1625,7 @@ L_SPL_RET:
|
|||||||
}
|
}
|
||||||
|
|
||||||
L_SPL_HALT:
|
L_SPL_HALT:
|
||||||
|
vm->ret_val = ins->imm;
|
||||||
if (ins->imm)
|
if (ins->imm)
|
||||||
vm->exit_code = (int)ins->imm;
|
vm->exit_code = (int)ins->imm;
|
||||||
return 1;
|
return 1;
|
||||||
@@ -1281,7 +1644,8 @@ L_SPL_ALLOC:
|
|||||||
}
|
}
|
||||||
|
|
||||||
L_SPL_LADDR:
|
L_SPL_LADDR:
|
||||||
PUSH((spl_vm_val_t)((char *)(vm->stacks.data + vm->fp) + ins->imm));
|
PUSH(SPL_ASP_OFF(SPL_ASP_HANDLE_STACK,
|
||||||
|
(spl_vm_val_t)vm->fp * sizeof(spl_vm_val_t) + ins->imm));
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
|
|
||||||
L_SPL_GADDR:
|
L_SPL_GADDR:
|
||||||
@@ -1289,20 +1653,22 @@ L_SPL_GADDR:
|
|||||||
spl_vm_val_t _idx = ins->imm;
|
spl_vm_val_t _idx = ins->imm;
|
||||||
if (_idx == 0 || _idx >= vec_size(prog->gdata))
|
if (_idx == 0 || _idx >= vec_size(prog->gdata))
|
||||||
VM_ERROR("GADDR: global data index out of range");
|
VM_ERROR("GADDR: global data index out of range");
|
||||||
spl_vm_gdata_t *gd = &vec_at(prog->gdata, _idx);
|
if (_idx >= vec_size(vm->gdata_off))
|
||||||
if (!gd->data)
|
VM_ERROR("GADDR: global data not mapped");
|
||||||
VM_ERROR("GADDR: null gdata entry");
|
PUSH((spl_vm_val_t)vec_at(vm->gdata_off, _idx));
|
||||||
PUSH((spl_vm_val_t)(uintptr_t)gd->data);
|
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== Indirect Memory (load/store with types) ========== */
|
/* ========== Indirect Memory (load/store with types) ========== */
|
||||||
L_SPL_LOAD:
|
L_SPL_LOAD:
|
||||||
{
|
{
|
||||||
void *_addr = (void *)POP();
|
spl_vm_val_t _off = POP();
|
||||||
CHECK_ADDR(_addr, "LOAD");
|
void *_addr;
|
||||||
spl_vm_val_t _v = 0;
|
spl_vm_val_t _v = 0;
|
||||||
usize _sz = spl_type_size(ins->type);
|
usize _sz = spl_type_size(ins->type);
|
||||||
|
_addr = spl_vm_xlate(vm, _off, _sz);
|
||||||
|
if (!_addr)
|
||||||
|
VM_ERROR("LOAD: invalid address");
|
||||||
memcpy(&_v, _addr, _sz);
|
memcpy(&_v, _addr, _sz);
|
||||||
/* Sign-extend signed integer types smaller than 64 bits */
|
/* Sign-extend signed integer types smaller than 64 bits */
|
||||||
if (_sz > 0 && _sz < sizeof(spl_vm_val_t) && spl_is_signed(ins->type)) {
|
if (_sz > 0 && _sz < sizeof(spl_vm_val_t) && spl_is_signed(ins->type)) {
|
||||||
@@ -1315,11 +1681,27 @@ L_SPL_LOAD:
|
|||||||
L_SPL_STORE:
|
L_SPL_STORE:
|
||||||
{
|
{
|
||||||
spl_vm_val_t _v = POP();
|
spl_vm_val_t _v = POP();
|
||||||
void *_addr = (void *)POP();
|
spl_vm_val_t _off = POP();
|
||||||
CHECK_ADDR(_addr, "STORE");
|
void *_addr = spl_vm_xlate(vm, _off, spl_type_size(ins->type));
|
||||||
|
if (!_addr)
|
||||||
|
VM_ERROR("STORE: invalid address");
|
||||||
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:
|
||||||
@@ -1374,13 +1756,23 @@ L_SPL_NCALL:
|
|||||||
if (_nat_idx >= vec_size(prog->natives))
|
if (_nat_idx >= vec_size(prog->natives))
|
||||||
VM_ERROR("NCALL: native index out of range");
|
VM_ERROR("NCALL: native index out of range");
|
||||||
_nat = &vec_at(prog->natives, _nat_idx);
|
_nat = &vec_at(prog->natives, _nat_idx);
|
||||||
if (!_nat->impl_fn) {
|
spl_vm_val_t *_arg_base = vm->stacks.data + vm->sp - _nargs;
|
||||||
|
spl_vm_val_t _result;
|
||||||
|
if (_nat->impl_fn) {
|
||||||
|
_result = _nat->impl_fn(vm, _nargs, _arg_base);
|
||||||
|
} else if (vm->foreign) {
|
||||||
|
/* 线 1 动态:交给按名的外部函数处理器。 */
|
||||||
|
spl_vm_call_t _call;
|
||||||
|
_call.name = _nat->name ? _nat->name : "";
|
||||||
|
_call.args = _arg_base;
|
||||||
|
_call.nargs = (usize)_nargs;
|
||||||
|
_call.ret = 0;
|
||||||
|
_result = vm->foreign(vm, &_call, vm->foreign_ctx);
|
||||||
|
} else {
|
||||||
snprintf(vm->error_msg, sizeof(vm->error_msg),
|
snprintf(vm->error_msg, sizeof(vm->error_msg),
|
||||||
"NCALL: NULL native function pointer expect %s", _nat->name);
|
"NCALL: NULL native function pointer expect %s", _nat->name);
|
||||||
VM_ERROR(vm->error_msg);
|
VM_ERROR(vm->error_msg);
|
||||||
}
|
}
|
||||||
spl_vm_val_t *_arg_base = vm->stacks.data + vm->sp - _nargs;
|
|
||||||
spl_vm_val_t _result = _nat->impl_fn(vm, _nargs, _arg_base);
|
|
||||||
if (vm->exit_code) {
|
if (vm->exit_code) {
|
||||||
snprintf(vm->error_msg, sizeof(vm->error_msg), "NCALL %s failed (ip=%zd)",
|
snprintf(vm->error_msg, sizeof(vm->error_msg), "NCALL %s failed (ip=%zd)",
|
||||||
_nat->name ? _nat->name : "?", vm->ip - 1);
|
_nat->name ? _nat->name : "?", vm->ip - 1);
|
||||||
@@ -1442,7 +1834,11 @@ L_default:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int spl_vm_run_once(spl_vm_t *vm) { return vm_loop(vm, 1); }
|
int spl_vm_run_once(spl_vm_t *vm) {
|
||||||
|
if (!vm)
|
||||||
|
return -1;
|
||||||
|
return vm_loop(vm, 1);
|
||||||
|
}
|
||||||
|
|
||||||
int spl_vm_run_until(spl_vm_t *vm, size_t step) {
|
int spl_vm_run_until(spl_vm_t *vm, size_t step) {
|
||||||
if (!vm)
|
if (!vm)
|
||||||
|
|||||||
@@ -5,11 +5,25 @@
|
|||||||
|
|
||||||
#include "include/core_map.h"
|
#include "include/core_map.h"
|
||||||
#include "include/core_vec.h"
|
#include "include/core_vec.h"
|
||||||
|
#include "spl_asp.h"
|
||||||
#include "spl_mcode.h"
|
#include "spl_mcode.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define SPL_STACK_CANARY ((spl_vm_val_t)0xDEADBEEFCAFEBABEull)
|
#define SPL_STACK_CANARY ((spl_vm_val_t)0xDEADBEEFCAFEBABEull)
|
||||||
|
|
||||||
|
/* 统一偏移地址空间:SPL 侧指针一律是句柄/偏移,0 保留为 null。
|
||||||
|
* offset = (handle<<32) | 区内字节偏移;handle 1 = 栈,handle>=2 = 堆分配。 */
|
||||||
|
#define SPL_OFF_NULL ((spl_vm_val_t)0)
|
||||||
|
#define SPL_ASP_HANDLE_STACK ((spl_vm_val_t)1)
|
||||||
|
#define SPL_ASP_OFF(handle, byte) ((((spl_vm_val_t)(handle)) << 32) | (spl_vm_val_t)(byte))
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned char *base;
|
||||||
|
usize size;
|
||||||
|
int used;
|
||||||
|
} spl_vm_region_t;
|
||||||
|
typedef VEC(spl_vm_region_t) spl_vm_region_vec_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uintptr_t saved_sp;
|
uintptr_t saved_sp;
|
||||||
uintptr_t saved_fp;
|
uintptr_t saved_fp;
|
||||||
@@ -20,6 +34,18 @@ typedef struct {
|
|||||||
typedef VEC(spl_vm_val_t) spl_stack_vec_t;
|
typedef VEC(spl_vm_val_t) spl_stack_vec_t;
|
||||||
typedef VEC(spl_callframe_t) spl_frame_vec_t;
|
typedef VEC(spl_callframe_t) spl_frame_vec_t;
|
||||||
|
|
||||||
|
/* 一次调用描述(VM <-> 外部两侧统一形状)。 */
|
||||||
|
typedef struct {
|
||||||
|
const char *name;
|
||||||
|
const spl_vm_val_t *args;
|
||||||
|
usize nargs;
|
||||||
|
spl_vm_val_t ret;
|
||||||
|
} spl_vm_call_t;
|
||||||
|
|
||||||
|
/* 线 1 动态形态:VM 调“外部(非 VM)函数”的按名处理器。
|
||||||
|
* 当 NCALL 的 native 无 impl_fn 时调用;返回被调函数的返回值。 */
|
||||||
|
typedef spl_vm_val_t (*spl_vm_foreign_fn)(spl_vm_t *vm, const spl_vm_call_t *call, void *ctx);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
MAP(const char *, usize) stacks;
|
MAP(const char *, usize) stacks;
|
||||||
VEC(usize) self_samples;
|
VEC(usize) self_samples;
|
||||||
@@ -49,6 +75,14 @@ typedef struct spl_vm {
|
|||||||
VEC(usize) breakpoints; /* ip 断点(执行到该指令前暂停,run_once 返回 2) */
|
VEC(usize) breakpoints; /* ip 断点(执行到该指令前暂停,run_once 返回 2) */
|
||||||
VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */
|
VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */
|
||||||
spl_prog_t *prog;
|
spl_prog_t *prog;
|
||||||
|
/* 统一偏移地址空间:regions[1]=栈,regions[i>=2]=堆分配。 */
|
||||||
|
spl_vm_region_vec_t regions;
|
||||||
|
VEC(usize) gdata_off; /* prog->gdata[i] 在本 VM 空间内的偏移 */
|
||||||
|
spl_asp_t asp; /* 本 VM 的地址空间描述符(按值持有,避免共享静态) */
|
||||||
|
spl_vm_val_t stdio_off[3]; /* stdin/stdout/stderr 的 VM 句柄(懒建) */
|
||||||
|
spl_vm_val_t ret_val; /* 最近一次完成的调用返回值(64 位) */
|
||||||
|
spl_vm_foreign_fn foreign; /* 线 1 动态处理器(可为 NULL) */
|
||||||
|
void *foreign_ctx;
|
||||||
char error_msg[1024];
|
char error_msg[1024];
|
||||||
struct {
|
struct {
|
||||||
uintptr_t max_stack_depth;
|
uintptr_t max_stack_depth;
|
||||||
@@ -68,6 +102,38 @@ void spl_vm_drop(spl_vm_t *vm);
|
|||||||
|
|
||||||
int spl_vm_addr_valid(uintptr_t p, usize size);
|
int spl_vm_addr_valid(uintptr_t p, usize size);
|
||||||
|
|
||||||
|
/* 偏移地址空间:偏移 -> 宿主地址;越界/ null 返回 NULL。 */
|
||||||
|
void *spl_vm_xlate(spl_vm_t *vm, spl_vm_val_t off, usize size);
|
||||||
|
/* 宿主地址 -> 偏移;不属于本 VM 地址空间返回 0。 */
|
||||||
|
spl_vm_val_t spl_vm_to_off(spl_vm_t *vm, const void *ptr);
|
||||||
|
/* 本 VM 的地址空间描述符(供 runtime 解引用)。 */
|
||||||
|
spl_asp_t *spl_vm_asp(spl_vm_t *vm);
|
||||||
|
|
||||||
|
/* ---- 线 1:VM -> 外部函数 ---- */
|
||||||
|
/* 安装“外部函数”按名处理器(NCALL 的 native 无 impl_fn 时使用)。 */
|
||||||
|
void spl_vm_set_foreign(spl_vm_t *vm, spl_vm_foreign_fn fn, void *ctx);
|
||||||
|
|
||||||
|
/* ---- 线 2:外部 -> VM 函数 ---- */
|
||||||
|
/* 按 call->name 调用 VM 内函数(call->args/nargs 为实参),结果写入 call->ret。
|
||||||
|
* 可在 VM 运行中重入调用。返回 0 成功,-1 失败。 */
|
||||||
|
int spl_vm_invoke(spl_vm_t *vm, spl_vm_call_t *call);
|
||||||
|
|
||||||
|
/* 入口调用:调用方先按 ABI 压 2 个 pad 再压实参。 */
|
||||||
|
int spl_vm_call_entry(spl_vm_t *vm, const char *name, int nargs);
|
||||||
|
|
||||||
|
/* ---- 栈 / 结果 / 内存 访问(不暴露宿主指针) ---- */
|
||||||
|
spl_vm_val_t spl_vm_ret_val(spl_vm_t *vm);
|
||||||
|
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);
|
||||||
|
|
||||||
int spl_vm_load_prog(spl_vm_t *vm, spl_prog_t *prog);
|
int spl_vm_load_prog(spl_vm_t *vm, spl_prog_t *prog);
|
||||||
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv, const char **envp);
|
int spl_vm_prepare(spl_vm_t *vm, const char *entry, int argc, const char **argv, const char **envp);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* spl_vm_embed.h - 原生编译器内嵌 VM(comptime 执行)。
|
/* spl_vm_embed.h - 裸机内嵌 VM(装载/运行,不含任何内置 native/syscall)。
|
||||||
*
|
*
|
||||||
* 单 TU 合并式:定义 SPL_VM_EMBED_IMPLIMENTATION 后 include 本头,会把 stage0 的
|
* 单 TU 合并式:定义 SPL_VM_EMBED_IMPLIMENTATION 后 include 本头,会把 stage0 的
|
||||||
* spl_mcode.c / spl_syscall.c / spl_vm.c 一并纳入当前翻译单元(不单独编译/链接)。
|
* spl_mcode.c / spl_vm.c 一并纳入当前翻译单元(不单独编译/链接)。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SPL_VM_EMBED_H__
|
#ifndef __SPL_VM_EMBED_H__
|
||||||
@@ -13,14 +13,17 @@ int vm_load_from_buffer(void *vm, const void *buf, unsigned long long len);
|
|||||||
void vm_push(void *vm, unsigned long long val);
|
void vm_push(void *vm, unsigned long long val);
|
||||||
int vm_call(void *vm, const char *name, unsigned long long nargs);
|
int vm_call(void *vm, const char *name, unsigned long long nargs);
|
||||||
int vm_run(void *vm);
|
int vm_run(void *vm);
|
||||||
|
unsigned long long vm_ret_val(void *vm);
|
||||||
|
unsigned long long vm_mem_alloc(void *vm, unsigned long long size);
|
||||||
|
int vm_mem_read(void *vm, unsigned long long off, void *dst, unsigned long long n);
|
||||||
|
int vm_mem_write(void *vm, unsigned long long off, const void *src, unsigned long long n);
|
||||||
|
|
||||||
#ifdef SPL_VM_EMBED_IMPLIMENTATION
|
#ifdef SPL_VM_EMBED_IMPLIMENTATION
|
||||||
|
|
||||||
#include "../../stage0/spl_mcode.h"
|
/* 裸机内嵌 VM:只提供装载/运行 VM 的能力,不内置任何 native/syscall。
|
||||||
void spl_syscall_register(spl_prog_t *prog) { (void)prog; /* stub */ }
|
* 外部函数解析与注册属于配置端(调用方自行处理)。 */
|
||||||
|
#include "spl_mcode.c"
|
||||||
#include "../../stage0/spl_mcode.c"
|
#include "spl_vm.c"
|
||||||
#include "../../stage0/spl_vm.c"
|
|
||||||
|
|
||||||
void *vm_new(void) {
|
void *vm_new(void) {
|
||||||
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
||||||
@@ -55,7 +58,6 @@ int vm_load_from_buffer(void *p, const void *buf, unsigned long long len) {
|
|||||||
free(prog);
|
free(prog);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
spl_syscall_register(prog);
|
|
||||||
if (spl_vm_load_prog(vm, prog) != 0) {
|
if (spl_vm_load_prog(vm, prog) != 0) {
|
||||||
spl_prog_drop(prog);
|
spl_prog_drop(prog);
|
||||||
free(prog);
|
free(prog);
|
||||||
@@ -75,26 +77,10 @@ void vm_push(void *p, unsigned long long val) {
|
|||||||
|
|
||||||
int vm_call(void *p, const char *name, unsigned long long nargs) {
|
int vm_call(void *p, const char *name, unsigned long long nargs) {
|
||||||
spl_vm_t *vm = (spl_vm_t *)p;
|
spl_vm_t *vm = (spl_vm_t *)p;
|
||||||
spl_vm_func_t *func;
|
|
||||||
if (!vm || !name)
|
if (!vm || !name)
|
||||||
return -1;
|
return -1;
|
||||||
func = spl_prog_get_func(vm->prog, name);
|
/* 入口调用:调用方需先按 ABI 压 2 个 pad 再压实参。 */
|
||||||
if (!func)
|
return spl_vm_call_entry(vm, name, (int)nargs);
|
||||||
return -1;
|
|
||||||
if (vm->cp >= vm->config.max_call_depth)
|
|
||||||
return -1;
|
|
||||||
/* 入口调用:ip=-1 作 sentinel;实参由 vm_push 压栈。 */
|
|
||||||
vm->ip = (usize)-1;
|
|
||||||
vm->cp = 0;
|
|
||||||
vm->fp = 0;
|
|
||||||
vm->frames.data[0].saved_sp = vm->sp - (usize)nargs;
|
|
||||||
vm->frames.data[0].saved_fp = vm->fp;
|
|
||||||
vm->frames.data[0].saved_ip = vm->ip;
|
|
||||||
vm->frames.data[0].nargs = (spl_vm_val_t)nargs;
|
|
||||||
vm->cp = 1;
|
|
||||||
vm->ip = (usize)func->address;
|
|
||||||
vm->fp = vm->sp - (usize)nargs;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int vm_run(void *p) {
|
int vm_run(void *p) {
|
||||||
@@ -104,6 +90,27 @@ int vm_run(void *p) {
|
|||||||
spl_vm_run_until(vm, 0);
|
spl_vm_run_until(vm, 0);
|
||||||
return (int)vm->exit_code;
|
return (int)vm->exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned long long vm_ret_val(void *p) {
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)p;
|
||||||
|
return vm ? (unsigned long long)vm->ret_val : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long vm_mem_alloc(void *p, unsigned long long size) {
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)p;
|
||||||
|
return (unsigned long long)spl_vm_mem_alloc(vm, (usize)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vm_mem_read(void *p, unsigned long long off, void *dst, unsigned long long n) {
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)p;
|
||||||
|
return spl_vm_mem_read(vm, (spl_vm_val_t)off, dst, (usize)n);
|
||||||
|
}
|
||||||
|
|
||||||
|
int vm_mem_write(void *p, unsigned long long off, const void *src, unsigned long long n) {
|
||||||
|
spl_vm_t *vm = (spl_vm_t *)p;
|
||||||
|
return spl_vm_mem_write(vm, (spl_vm_val_t)off, src, (usize)n);
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* SPL_VM_EMBED_IMPLIMENTATION */
|
#endif /* SPL_VM_EMBED_IMPLIMENTATION */
|
||||||
|
|
||||||
#endif /* __SPL_VM_EMBED_H__ */
|
#endif /* __SPL_VM_EMBED_H__ */
|
||||||
+140
-10
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include "include/acutest.h"
|
#include "include/acutest.h"
|
||||||
#include "spl_mcode.h"
|
#include "spl_mcode.h"
|
||||||
|
#include "spl_syscall.h"
|
||||||
#include "spl_vm.h"
|
#include "spl_vm.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -159,7 +160,8 @@ static int run_with_natives(const spl_vm_ins_t *instns, int ninsns, spl_vm_nativ
|
|||||||
* Native function for NCALL tests
|
* Native function for NCALL tests
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
static spl_vm_val_t native_add_impl(int nargs, spl_vm_val_t *args) {
|
static spl_vm_val_t native_add_impl(spl_vm_t *vm, int nargs, spl_vm_val_t *args) {
|
||||||
|
(void)vm;
|
||||||
return (nargs >= 2) ? args[0] + args[1] : 0;
|
return (nargs >= 2) ? args[0] + args[1] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +481,8 @@ void test_call_add(void) {
|
|||||||
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_PUSH, SPL_VOID, 0),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2),
|
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_CALL, SPL_VOID, 2),
|
||||||
ins(SPL_RET, SPL_I64, 0)};
|
ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
|
||||||
@@ -586,11 +589,12 @@ void test_calli(void) {
|
|||||||
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
spl_vm_ins_t main_insns[] = {ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_PUSH, SPL_VOID, 0),
|
||||||
|
ins(SPL_PUSH, SPL_I32, 10), ins(SPL_PUSH, SPL_I32, 20),
|
||||||
ins(SPL_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0),
|
ins(SPL_PUSH, SPL_VOID, 2), ins(SPL_PUSH, SPL_VOID, 0),
|
||||||
ins(SPL_CALLI, SPL_VOID, 0), ins(SPL_RET, SPL_I64, 0)};
|
ins(SPL_CALLI, SPL_VOID, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
|
||||||
int ninsns_t = 6 + 6;
|
int ninsns_t = 6 + 8;
|
||||||
size_t nlen0 = strlen("add") + 1;
|
size_t nlen0 = strlen("add") + 1;
|
||||||
size_t npad0 = ((nlen0 + 7) / 8) * 8 - nlen0;
|
size_t npad0 = ((nlen0 + 7) / 8) * 8 - nlen0;
|
||||||
size_t nlen1 = strlen("main") + 1;
|
size_t nlen1 = strlen("main") + 1;
|
||||||
@@ -627,7 +631,7 @@ void test_calli(void) {
|
|||||||
p += npad1;
|
p += npad1;
|
||||||
LE64(p, 0);
|
LE64(p, 0);
|
||||||
LE64(p, 0);
|
LE64(p, 0);
|
||||||
LE64(p, 6);
|
LE64(p, 8);
|
||||||
LE64(p, 6);
|
LE64(p, 6);
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
@@ -637,7 +641,7 @@ void test_calli(void) {
|
|||||||
*p++ = (unsigned char)(add_insns[i].type >> 8);
|
*p++ = (unsigned char)(add_insns[i].type >> 8);
|
||||||
LE64(p, add_insns[i].imm);
|
LE64(p, add_insns[i].imm);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
*p++ = (unsigned char)(main_insns[i].opcode);
|
*p++ = (unsigned char)(main_insns[i].opcode);
|
||||||
*p++ = (unsigned char)(main_insns[i].opcode >> 8);
|
*p++ = (unsigned char)(main_insns[i].opcode >> 8);
|
||||||
*p++ = (unsigned char)(main_insns[i].type);
|
*p++ = (unsigned char)(main_insns[i].type);
|
||||||
@@ -669,8 +673,9 @@ void test_calli(void) {
|
|||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
|
|
||||||
void test_gaddr_ld32(void) {
|
void test_gaddr_ld32(void) {
|
||||||
/* Load program, add global data, GADDR + LD32 to read it back */
|
/* Load program, add global data, GADDR + LD32 to read it back.
|
||||||
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_U32, 0),
|
* gdata 索引 0 为哨兵;首次 add_data 得到索引 1。 */
|
||||||
|
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_U32, 0),
|
||||||
ins(SPL_RET, SPL_U32, 0)};
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
size_t len;
|
size_t len;
|
||||||
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
||||||
@@ -697,8 +702,8 @@ void test_gaddr_ld32(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void test_gaddr_multi(void) {
|
void test_gaddr_multi(void) {
|
||||||
/* Two global data entries: read second one via GADDR 1 */
|
/* Two global data entries: read second one via GADDR 2 (0 为哨兵) */
|
||||||
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 1), ins(SPL_LOAD, SPL_U32, 0),
|
spl_vm_ins_t p[] = {ins(SPL_GADDR, SPL_VOID, 2), ins(SPL_LOAD, SPL_U32, 0),
|
||||||
ins(SPL_RET, SPL_U32, 0)};
|
ins(SPL_RET, SPL_U32, 0)};
|
||||||
size_t len;
|
size_t len;
|
||||||
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
unsigned char *bin = build_binary("main", 0, p, 3, &len);
|
||||||
@@ -818,6 +823,127 @@ void test_empty(void) {
|
|||||||
TEST_CHECK(spl_prog_load_from_file("nonexistent_file_xyz.bin", NULL) != 0);
|
TEST_CHECK(spl_prog_load_from_file("nonexistent_file_xyz.bin", NULL) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
* 动态回调 / 重入 / 栈访问 API
|
||||||
|
* ================================================================ */
|
||||||
|
|
||||||
|
static spl_vm_val_t foreign_add_cb(spl_vm_t *vm, const spl_vm_call_t *call, void *ctx) {
|
||||||
|
(void)vm;
|
||||||
|
(void)ctx;
|
||||||
|
if (!call || call->nargs < 2)
|
||||||
|
return 0;
|
||||||
|
return call->args[0] + call->args[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 线 1 动态:NCALL 的 native 无 impl_fn 时走 vm->foreign。 */
|
||||||
|
void test_foreign_fallback(void) {
|
||||||
|
spl_vm_ins_t p[] = {ins(SPL_PUSH, SPL_I32, 30), ins(SPL_PUSH, SPL_I32, 12),
|
||||||
|
ins(SPL_PUSH, SPL_VOID, 0), ins(SPL_NCALL, SPL_I32, 2),
|
||||||
|
ins(SPL_RET, SPL_I32, 0)};
|
||||||
|
size_t len;
|
||||||
|
unsigned char *bin = build_binary("main", 0, p, 5, &len);
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
spl_vm_native_t nat[] = {{"host_add", 0, NULL}};
|
||||||
|
int rc = -1;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
for (int i = 0; i < 1; i++)
|
||||||
|
spl_prog_add_native(&prog, &nat[i]);
|
||||||
|
if (spl_vm_load_prog(&vm, &prog) == 0) {
|
||||||
|
spl_vm_set_foreign(&vm, foreign_add_cb, NULL);
|
||||||
|
if (spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
int ret = spl_vm_run_until(&vm, 0);
|
||||||
|
if (ret == 1)
|
||||||
|
rc = (int)vm.ret_val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
TEST_CHECK(rc == 42);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 线 2:外部 -> VM 调用。 */
|
||||||
|
void test_invoke(void) {
|
||||||
|
spl_vm_ins_t add_insns[] = {ins(SPL_LADDR, SPL_VOID, 0), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
|
ins(SPL_LADDR, SPL_VOID, 8), ins(SPL_LOAD, SPL_I64, 0),
|
||||||
|
ins(SPL_ADD, SPL_I64, 0), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
size_t len;
|
||||||
|
unsigned char *bin = build_binary("add", 2, add_insns, 6, &len);
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
spl_vm_val_t args[2];
|
||||||
|
spl_vm_call_t call;
|
||||||
|
int ok = 0;
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
if (spl_vm_load_prog(&vm, &prog) == 0) {
|
||||||
|
args[0] = 40;
|
||||||
|
args[1] = 2;
|
||||||
|
call.name = "add";
|
||||||
|
call.args = args;
|
||||||
|
call.nargs = 2;
|
||||||
|
call.ret = 0;
|
||||||
|
ok = (spl_vm_invoke(&vm, &call) == 0) && (call.ret == 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
TEST_CHECK(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 栈 / 结果访问 API 不依赖宿主指针。 */
|
||||||
|
void test_stack_api(void) {
|
||||||
|
spl_vm_t vm;
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
TEST_CHECK(spl_vm_stack_set(&vm, 0, 123) == 0);
|
||||||
|
TEST_CHECK(spl_vm_stack_get(&vm, 0) == 123);
|
||||||
|
TEST_CHECK(spl_vm_sp(&vm) == 0);
|
||||||
|
TEST_CHECK(spl_vm_fp(&vm) == 0);
|
||||||
|
TEST_CHECK(spl_vm_stack_get(&vm, (usize)-1) == 0);
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* syscall 注册 + 内存 API:NCALL vm_alloc 得到 VM 偏移,并经 mem_read/write 往返。 */
|
||||||
|
void test_syscall_registration(void) {
|
||||||
|
spl_vm_ins_t p[] = {ins(SPL_PUSH, SPL_I64, 16), ins(SPL_PUSH, SPL_VOID, 0),
|
||||||
|
ins(SPL_NCALL, SPL_I64, 1), ins(SPL_RET, SPL_I64, 0)};
|
||||||
|
size_t len;
|
||||||
|
unsigned char *bin = build_binary("main", 0, p, 4, &len);
|
||||||
|
spl_prog_t prog;
|
||||||
|
spl_vm_t vm;
|
||||||
|
spl_vm_native_t nat[] = {{"vm_alloc", 0, NULL}};
|
||||||
|
spl_vm_val_t off = 0;
|
||||||
|
int ok = 0;
|
||||||
|
char w[4] = {1, 2, 3, 0};
|
||||||
|
char r[4] = {0};
|
||||||
|
|
||||||
|
spl_vm_init(&vm);
|
||||||
|
if (write_temp(bin, len) == 0 && spl_prog_load_from_file(TMPFILE, &prog) == 0) {
|
||||||
|
for (int i = 0; i < 1; i++)
|
||||||
|
spl_prog_add_native(&prog, &nat[i]);
|
||||||
|
spl_syscall_register(&prog);
|
||||||
|
if (spl_vm_load_prog(&vm, &prog) == 0 && spl_vm_prepare(&vm, "main", 0, NULL, NULL) == 0) {
|
||||||
|
if (spl_vm_run_until(&vm, 0) == 1) {
|
||||||
|
off = vm.ret_val;
|
||||||
|
if (off != 0 && spl_vm_mem_write(&vm, off, w, 4) == 0 &&
|
||||||
|
spl_vm_mem_read(&vm, off, r, 4) == 0 && memcmp(w, r, 4) == 0)
|
||||||
|
ok = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spl_vm_drop(&vm);
|
||||||
|
remove(TMPFILE);
|
||||||
|
free(bin);
|
||||||
|
TEST_CHECK(off != 0);
|
||||||
|
TEST_CHECK(ok);
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
* Test list
|
* Test list
|
||||||
* ================================================================ */
|
* ================================================================ */
|
||||||
@@ -872,6 +998,10 @@ TEST_LIST = {
|
|||||||
{"ld_st64", test_ld_st64},
|
{"ld_st64", test_ld_st64},
|
||||||
{"many_ops", test_many_ops},
|
{"many_ops", test_many_ops},
|
||||||
{"halt", test_halt},
|
{"halt", test_halt},
|
||||||
|
{"foreign_fallback", test_foreign_fallback},
|
||||||
|
{"invoke", test_invoke},
|
||||||
|
{"stack_api", test_stack_api},
|
||||||
|
{"syscall_registration", test_syscall_registration},
|
||||||
{"bad_magic", test_bad_magic},
|
{"bad_magic", test_bad_magic},
|
||||||
{"empty", test_empty},
|
{"empty", test_empty},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+115
-13
@@ -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;
|
||||||
|
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);
|
spl_ir_node_ref_t scrut_val = emit_expr(a, matched_ref);
|
||||||
if (!scrut_val)
|
if (!scrut_val)
|
||||||
return 0;
|
return 0;
|
||||||
spl_ir_builder_mem_store(a->ir, matched_ir, scrut_slot, scrut_val);
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -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;
|
||||||
@@ -1084,12 +1081,18 @@ static void cg_control(cgen_t *cg, usize ref, const spl_ir_node_t *n) {
|
|||||||
spl_type_id_t ret_t = n->control_call.tid;
|
spl_type_id_t ret_t = n->control_call.tid;
|
||||||
bool ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
bool ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
||||||
usize sret_off = 0;
|
usize sret_off = 0;
|
||||||
usize j = 0;
|
/* j = 本节点已压入 tracked 块之上的值的个数(用于栈化 PICK 距离)。
|
||||||
|
先压 2 个 ABI 空闲 slot,故从 2 起算。 */
|
||||||
|
usize j = 2;
|
||||||
|
|
||||||
|
/* ABI:实参下方预留 2 个空闲 slot(PAD0/PAD1)。 */
|
||||||
|
CG_PUSH(cg, SPL_U64, 0);
|
||||||
|
CG_PUSH(cg, SPL_U64, 0);
|
||||||
|
|
||||||
if (ret_is_sret) {
|
if (ret_is_sret) {
|
||||||
sret_off = vec_at(cg->frame.slots, ref).data_off;
|
sret_off = vec_at(cg->frame.slots, ref).data_off;
|
||||||
CG_LADDR(cg, sret_off);
|
CG_LADDR(cg, sret_off);
|
||||||
j = 1;
|
j = 3;
|
||||||
}
|
}
|
||||||
for (usize i = 0; i < nargs; ++i) {
|
for (usize i = 0; i < nargs; ++i) {
|
||||||
spl_ir_node_ref_t pref = vec_at(n->control_call.params, i);
|
spl_ir_node_ref_t pref = vec_at(n->control_call.params, i);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
+48
-16
@@ -302,27 +302,23 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
|
|
||||||
spl_lang_type_id_t saved_agg = sema->current_aggregate_lang;
|
spl_lang_type_id_t saved_agg = sema->current_aggregate_lang;
|
||||||
sema->current_aggregate_lang = agg_lang;
|
sema->current_aggregate_lang = agg_lang;
|
||||||
|
/* 聚合体自成命名空间:成员类型可引用同体声明的嵌套类型(裸名)。 */
|
||||||
|
sema_push_scope(sema);
|
||||||
|
|
||||||
spl_type_member_vec_t members;
|
spl_type_member_vec_t members;
|
||||||
vec_init(members);
|
vec_init(members);
|
||||||
spl_lang_method_vec_t methods;
|
spl_lang_method_vec_t methods;
|
||||||
vec_init(methods);
|
vec_init(methods);
|
||||||
|
|
||||||
|
/* 第一遍:登记嵌套类型/函数的 def,挂进 methods 与聚合作用域。成员类型可能引用它们,
|
||||||
|
必须早于成员类型推断,否则限定名(经 methods)与裸名(经作用域)都解析不到。 */
|
||||||
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
|
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
|
||||||
spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i);
|
spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i);
|
||||||
spl_ast_node_t *mn = spl_ast_node(sema->ast, mref);
|
spl_ast_node_t *mn = spl_ast_node(sema->ast, mref);
|
||||||
if (!mn)
|
if (!mn)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mn->kind == SPL_AST_MEMBER_DECL) {
|
if (mn->kind == SPL_AST_FN_DECL) {
|
||||||
spl_lang_type_id_t mtid = 0;
|
|
||||||
if (mn->member_decl.type_expr)
|
|
||||||
mtid = sema_infer(sema, mn->member_decl.type_expr);
|
|
||||||
spl_type_member_t m = {.name = mn->member_decl.name,
|
|
||||||
.type_id = spl_lang_type_get_ir_type(sema->type, mtid),
|
|
||||||
.ast_node_ref = mref};
|
|
||||||
vec_push(members, m);
|
|
||||||
} else if (mn->kind == SPL_AST_FN_DECL) {
|
|
||||||
spl_def_id_t fn_def = mn->resolved_def_id;
|
spl_def_id_t fn_def = mn->resolved_def_id;
|
||||||
if (!fn_def) {
|
if (!fn_def) {
|
||||||
fn_def = spl_def_alloc(sema->type, SPL_DEF_FN, mn->fn_decl.name, mn->dbg, sema->ast,
|
fn_def = spl_def_alloc(sema->type, SPL_DEF_FN, mn->fn_decl.name, mn->dbg, sema->ast,
|
||||||
@@ -334,6 +330,9 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
fn_d->owner_def = owner_def_id;
|
fn_d->owner_def = owner_def_id;
|
||||||
spl_lang_method_t meth = {.name = mn->fn_decl.name, .def_id = fn_def};
|
spl_lang_method_t meth = {.name = mn->fn_decl.name, .def_id = fn_def};
|
||||||
vec_push(methods, meth);
|
vec_push(methods, meth);
|
||||||
|
spl_symbol_t fsym = {.name = mn->fn_decl.name, .kind = SPL_SYMBOL_KIND_FN};
|
||||||
|
fsym.node = fn_def;
|
||||||
|
spl_scope_insert(sema->scope, sema->scope->current_scope, fsym);
|
||||||
} else if (mn->kind == SPL_AST_TYPE_DECL) {
|
} else if (mn->kind == SPL_AST_TYPE_DECL) {
|
||||||
spl_ast_node_t *tpat = spl_ast_node(sema->ast, mn->type_decl.pattern);
|
spl_ast_node_t *tpat = spl_ast_node(sema->ast, mn->type_decl.pattern);
|
||||||
if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT)
|
if (!tpat || tpat->kind != SPL_AST_PATTERN_IDENT)
|
||||||
@@ -352,9 +351,37 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
}
|
}
|
||||||
spl_lang_method_t meth = {.name = tname, .def_id = tdef};
|
spl_lang_method_t meth = {.name = tname, .def_id = tdef};
|
||||||
vec_push(methods, meth);
|
vec_push(methods, meth);
|
||||||
|
spl_symbol_t tsym = {.name = tname, .kind = SPL_SYMBOL_KIND_TYPE};
|
||||||
|
tsym.node = tdef;
|
||||||
|
spl_scope_insert(sema->scope, sema->scope->current_scope, tsym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spl_lang_type_node_t *lslot = spl_lang_type_node(sema->type, agg_lang);
|
||||||
|
if (lslot) {
|
||||||
|
vec_free(lslot->methods);
|
||||||
|
lslot->methods = methods;
|
||||||
|
vec_init(methods);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 第二遍:成员(字段/变体)类型推断;此时嵌套名字已可解析。 */
|
||||||
|
for (usize i = 0; i < vec_size(te->type_aggregate.members); ++i) {
|
||||||
|
spl_ast_node_ref_t mref = vec_at(te->type_aggregate.members, i);
|
||||||
|
spl_ast_node_t *mn = spl_ast_node(sema->ast, mref);
|
||||||
|
if (!mn)
|
||||||
|
continue;
|
||||||
|
if (mn->kind == SPL_AST_MEMBER_DECL) {
|
||||||
|
spl_lang_type_id_t mtid = 0;
|
||||||
|
if (mn->member_decl.type_expr)
|
||||||
|
mtid = sema_infer(sema, mn->member_decl.type_expr);
|
||||||
|
spl_type_member_t m = {.name = mn->member_decl.name,
|
||||||
|
.type_id = spl_lang_type_get_ir_type(sema->type, mtid),
|
||||||
|
.ast_node_ref = mref};
|
||||||
|
vec_push(members, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sema_pop_scope(sema);
|
||||||
sema->current_aggregate_lang = saved_agg;
|
sema->current_aggregate_lang = saved_agg;
|
||||||
|
|
||||||
spl_type_node_t tn = {0};
|
spl_type_node_t tn = {0};
|
||||||
@@ -366,13 +393,6 @@ static spl_lang_type_id_t sema_build_aggregate(spl_sema_t *sema, spl_ast_node_t
|
|||||||
vec_init(members);
|
vec_init(members);
|
||||||
spl_lang_define_ir(sema->type, agg_ir, &tn);
|
spl_lang_define_ir(sema->type, agg_ir, &tn);
|
||||||
|
|
||||||
spl_lang_type_node_t *lslot = spl_lang_type_node(sema->type, agg_lang);
|
|
||||||
if (lslot) {
|
|
||||||
vec_free(lslot->methods);
|
|
||||||
lslot->methods = methods;
|
|
||||||
vec_init(methods);
|
|
||||||
}
|
|
||||||
|
|
||||||
return agg_lang;
|
return agg_lang;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1136,6 +1156,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);
|
||||||
|
|||||||
+21
-10
@@ -1,9 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// AST:用 ADT(enum 带载荷)表达节点,节点引用是 arena 序号 usize。
|
// AST:节点为带载荷 ADT,节点引用是 arena 序号(0 为 NONE 哨兵)。最终 dump/语义须与
|
||||||
// stage1 的 C 结构只是「手写 union 模拟 ADT」;这里直接用 SPL 的 enum 载荷,
|
// splc0 一致。
|
||||||
// 不照抄 union 布局,只要求最终 dump / 语义与 stage1 一致。
|
|
||||||
// 索引 0 为 NONE 哨兵(与 stage1 一致)。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -18,7 +16,6 @@ type .{ .Sources, .Source, .set_at } = @import("utils/source.spl");
|
|||||||
// 算子不另立 enum:AST 直接存词法 Punct。一元/二元由节点 kind 区分
|
// 算子不另立 enum:AST 直接存词法 Punct。一元/二元由节点 kind 区分
|
||||||
// (一元:Sub=负 / Not=! / BitNot=~ / And=& / Mul=.*;二元即对应 Punct)。
|
// (一元:Sub=负 / Not=! / BitNot=~ / And=& / Mul=.*;二元即对应 Punct)。
|
||||||
|
|
||||||
// ---- 节点体(ADT;variant 名 = stage1 的 kind 名;载荷用内联匿名 struct)----
|
|
||||||
|
|
||||||
type Body = enum {
|
type Body = enum {
|
||||||
None,
|
None,
|
||||||
@@ -45,11 +42,12 @@ 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 },
|
||||||
StatementDefer: struct { body: usize },
|
StatementDefer: struct { body: usize },
|
||||||
|
StatementErrdefer: struct { body: usize }, // errdefer:仅错误退出时执行
|
||||||
StatementComptime: struct { body: usize },
|
StatementComptime: struct { body: usize },
|
||||||
StatementDecl: struct { decl: usize },
|
StatementDecl: struct { decl: usize },
|
||||||
StatementExpr: struct { expr: usize },
|
StatementExpr: struct { expr: usize },
|
||||||
@@ -87,6 +85,8 @@ type Body = enum {
|
|||||||
},
|
},
|
||||||
ExprAggregateLit: struct { name: ByteView, fields: Vec },
|
ExprAggregateLit: struct { name: ByteView, fields: Vec },
|
||||||
ExprComptimeCall: struct { name: ByteView, args: Vec },
|
ExprComptimeCall: struct { name: ByteView, args: Vec },
|
||||||
|
ExprTry: struct { value: usize }, // `try expr`
|
||||||
|
ExprCatch: struct { value: usize, pattern: usize, handler: usize }, // `expr catch <pat> { }`
|
||||||
ExprBlock: struct { stmts: Vec, tail_expr: usize },
|
ExprBlock: struct { stmts: Vec, tail_expr: usize },
|
||||||
ExprIf: struct { cond: usize, then_block: usize, else_branch: usize },
|
ExprIf: struct { cond: usize, then_block: usize, else_branch: usize },
|
||||||
ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize },
|
ExprIfvar: struct { var_decl: usize, then_block: usize, else_branch: usize },
|
||||||
@@ -121,6 +121,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 },
|
||||||
}
|
}
|
||||||
@@ -144,6 +145,7 @@ fn kind_name(body: Body): []u8 {
|
|||||||
.StatementBreak(_) => { ret "statement_break"; },
|
.StatementBreak(_) => { ret "statement_break"; },
|
||||||
.StatementContinue(_) => { ret "statement_continue"; },
|
.StatementContinue(_) => { ret "statement_continue"; },
|
||||||
.StatementDefer(_) => { ret "statement_defer"; },
|
.StatementDefer(_) => { ret "statement_defer"; },
|
||||||
|
.StatementErrdefer(_) => { ret "statement_errdefer"; },
|
||||||
.StatementComptime(_) => { ret "statement_comptime"; },
|
.StatementComptime(_) => { ret "statement_comptime"; },
|
||||||
.ExprComptime(_) => { ret "expr_comptime"; },
|
.ExprComptime(_) => { ret "expr_comptime"; },
|
||||||
.StatementDecl(_) => { ret "statement_decl"; },
|
.StatementDecl(_) => { ret "statement_decl"; },
|
||||||
@@ -171,6 +173,8 @@ fn kind_name(body: Body): []u8 {
|
|||||||
.ExprArrayLit(_) => { ret "array_literal"; },
|
.ExprArrayLit(_) => { ret "array_literal"; },
|
||||||
.ExprAggregateLit(_) => { ret "aggregate_literal"; },
|
.ExprAggregateLit(_) => { ret "aggregate_literal"; },
|
||||||
.ExprComptimeCall(_) => { ret "comptime_call"; },
|
.ExprComptimeCall(_) => { ret "comptime_call"; },
|
||||||
|
.ExprTry(_) => { ret "try_expr"; },
|
||||||
|
.ExprCatch(_) => { ret "catch_expr"; },
|
||||||
.ExprComptimeInit(_) => { ret "comptime_init"; },
|
.ExprComptimeInit(_) => { ret "comptime_init"; },
|
||||||
.ExprBlock(_) => { ret "block"; },
|
.ExprBlock(_) => { ret "block"; },
|
||||||
.ExprIf(_) => { ret "if_expr"; },
|
.ExprIf(_) => { ret "if_expr"; },
|
||||||
@@ -195,6 +199,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 "?";
|
||||||
}
|
}
|
||||||
@@ -294,11 +299,11 @@ fn alloc(self: *Ast, body: Body, span: Span): usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 注册一段已解码的字符串字节(Ast 拥有;ByteView 指向其中,生命周期同 Ast)。
|
// 注册一段已解码的字符串字节(Ast 拥有;ByteView 指向其中,生命周期同 Ast)。
|
||||||
// 空字符串也保证非空 data 指针(镜像 stage1:空字面量仍有稳定的非空字节地址)。
|
// 空字符串也保证非空 data 指针(空字面量仍有稳定非空地址)。
|
||||||
fn push_string(self: *Ast, bytes: ByteBuffer): ByteView {
|
fn push_string(self: *Ast, bytes: ByteBuffer): ByteView {
|
||||||
var b = bytes;
|
var b = bytes;
|
||||||
if b.storage.len == 0 {
|
if b.storage.len == 0 {
|
||||||
match ByteBuffer.reserve(&b, 1) { .Ok => {}, .Err(error) => {} }
|
ByteBuffer.reserve(&b, 1);
|
||||||
}
|
}
|
||||||
@assert(b.storage.data != null, "ast string pool must be non-null");
|
@assert(b.storage.data != null, "ast string pool must be non-null");
|
||||||
// 直接构造,避免 from_slice 把空串规范成 null 视图。
|
// 直接构造,避免 from_slice 把空串规范成 null 视图。
|
||||||
@@ -321,7 +326,6 @@ fn ref_vec(): Vec {
|
|||||||
ret Vec.blank(@sizeof(usize), @alignof(usize));
|
ret Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump(镜像 stage1 spl_ast_dump)----
|
|
||||||
//
|
//
|
||||||
// AST:
|
// AST:
|
||||||
// <indent>`-/|-kind #ref [line:col]
|
// <indent>`-/|-kind #ref [line:col]
|
||||||
@@ -466,6 +470,7 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
|||||||
},
|
},
|
||||||
.StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); },
|
.StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); },
|
||||||
.StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
.StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
|
.StatementErrdefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
.StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
.StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
.ExprComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
.ExprComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||||
.StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); },
|
.StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); },
|
||||||
@@ -519,6 +524,12 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
|||||||
},
|
},
|
||||||
.ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); },
|
.ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); },
|
||||||
.ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); },
|
.ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); },
|
||||||
|
.ExprTry(t2) => { ok = dump_opt(ast, buf, stack, t2.value, true); },
|
||||||
|
.ExprCatch(c2) => {
|
||||||
|
ok = dump_opt(ast, buf, stack, c2.value, false) &&
|
||||||
|
dump_opt(ast, buf, stack, c2.pattern, false) &&
|
||||||
|
dump_opt(ast, buf, stack, c2.handler, true);
|
||||||
|
},
|
||||||
.ExprComptimeInit(x) => { ok = dump_list(ast, buf, stack, x.fields); },
|
.ExprComptimeInit(x) => { ok = dump_list(ast, buf, stack, x.fields); },
|
||||||
.ExprBlock(b) => {
|
.ExprBlock(b) => {
|
||||||
ok = dump_list(ast, buf, stack, b.stmts) &&
|
ok = dump_list(ast, buf, stack, b.stmts) &&
|
||||||
@@ -577,6 +588,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;
|
||||||
@@ -591,7 +603,6 @@ fn dump(ast: *Ast, out: *ByteBuffer): bool {
|
|||||||
ret dump_node(ast, out, &stack, ast.root, true);
|
ret dump_node(ast, out, &stack, ast.root, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_kind_names() {
|
fn test_kind_names() {
|
||||||
@assert(ByteView.eq(ByteView.from_slice(kind_name(.None)), ByteView.from_slice("none")),
|
@assert(ByteView.eq(ByteView.from_slice(kind_name(.None)), ByteView.from_slice("none")),
|
||||||
|
|||||||
+364
-194
File diff suppressed because it is too large.
Load diff
+5
-10
@@ -1,8 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 属性注册表与属性节点(移植自 stage1/spl_attr.c)。注册表是数据:内置属性
|
// 属性注册表:内置属性在 register_builtins 登记,按 (ns, name) 查找。新增属性只加
|
||||||
// 在 register_builtins 里登记,查找按 (ns, name)。节点记录命中信息 + 实参。
|
// 注册项,不改 sema 逻辑(SPL.md 的 @/# 扩展点)。
|
||||||
// 新增属性只加注册项,不改 sema 逻辑(对应 SPL.md 的 @/# 扩展点)。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -163,10 +162,7 @@ fn alloc(self: *Attr, info_index: usize, args: Vec): usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn registry_add(self: *Attr, info: AttrInfo) {
|
fn registry_add(self: *Attr, info: AttrInfo) {
|
||||||
match Vec.push(&self.registry, &info as *u8) {
|
Vec.push(&self.registry, &info as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 返回注册表下标(>=1)或 0。
|
// 返回注册表下标(>=1)或 0。
|
||||||
@@ -202,9 +198,9 @@ 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)----
|
|
||||||
|
|
||||||
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
match ByteBuffer.append(buf, bytes) {
|
match ByteBuffer.append(buf, bytes) {
|
||||||
@@ -340,7 +336,6 @@ fn dump(self: *Attr, out: *ByteBuffer): bool {
|
|||||||
ret true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn expect_text(buf: ByteBuffer, expected: []u8) {
|
fn expect_text(buf: ByteBuffer, expected: []u8) {
|
||||||
var got = ByteBuffer.as_slice(buf);
|
var got = ByteBuffer.as_slice(buf);
|
||||||
@@ -360,7 +355,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");
|
||||||
|
|||||||
+4
-12
@@ -1,12 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 声明句柄表(移植自 stage1/spl_lang_type.h 的 spl_def_node_t 部分)。
|
// 声明句柄表。DefId 是声明的稳定身份:作用域符号、节点 resolved_def_id、comptime
|
||||||
// DefId 是「声明」的稳定身份:作用域符号、AST 节点 resolved_def_id、以及将来
|
// 生成的声明都用它;前向/环形引用由 state 状态机表达。owner_ast 为不透明 *any。
|
||||||
// comptime 生成的声明都用它。前向引用/环形引用由 state 状态机表达。
|
|
||||||
//
|
|
||||||
// 与 stage1 的差异:owner_ast 改为不透明 `*any`(借用),使本表不依赖具体 AST
|
|
||||||
// 表示,便于将来 comptime/序列化复用;sema 侧自行 cast 回 *Ast。
|
|
||||||
// 名称是 ByteView(借用源/AST),本表不做拥有。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -84,7 +79,7 @@ fn drop(self: *DefTable) {
|
|||||||
Vec.drop(&self.table);
|
Vec.drop(&self.table);
|
||||||
}
|
}
|
||||||
|
|
||||||
// id 0 与越界均返回 null(对应 stage1 spl_def_node)。
|
// id 0 与越界均返回 null。
|
||||||
fn node(self: *DefTable, id: usize): *DefNode {
|
fn node(self: *DefTable, id: usize): *DefNode {
|
||||||
if id == 0 || id >= self.table.len { ret null; }
|
if id == 0 || id >= self.table.len { ret null; }
|
||||||
ret (self.table.data as *DefNode) + id;
|
ret (self.table.data as *DefNode) + id;
|
||||||
@@ -129,7 +124,6 @@ fn get_lang_id(self: *DefTable, id: usize): usize {
|
|||||||
ret d.lang_id;
|
ret d.lang_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 分类 ----
|
|
||||||
|
|
||||||
fn kind_name(kind: DefKind): []u8 {
|
fn kind_name(kind: DefKind): []u8 {
|
||||||
match kind {
|
match kind {
|
||||||
@@ -156,7 +150,7 @@ fn state_name(state: DeclState): []u8 {
|
|||||||
ret "unknown";
|
ret "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 能作为类型使用的 def(对应 stage1 sema_def_is_type)。
|
// 能作为类型使用的 def。
|
||||||
fn is_type_kind(kind: DefKind): bool {
|
fn is_type_kind(kind: DefKind): bool {
|
||||||
match kind {
|
match kind {
|
||||||
.Scalar => { ret true; },
|
.Scalar => { ret true; },
|
||||||
@@ -169,7 +163,6 @@ fn is_type_kind(kind: DefKind): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump(镜像 stage1 spl_type_def_dump 的文本)----
|
|
||||||
|
|
||||||
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
match ByteBuffer.append(buf, bytes) {
|
match ByteBuffer.append(buf, bytes) {
|
||||||
@@ -203,7 +196,6 @@ fn dump(self: *DefTable, id: usize, out: *ByteBuffer): bool {
|
|||||||
ret push_out(out, ByteView.as_slice(d.name));
|
ret push_out(out, ByteView.as_slice(d.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_alloc_and_state() {
|
fn test_alloc_and_state() {
|
||||||
var dt: DefTable = undefined;
|
var dt: DefTable = undefined;
|
||||||
|
|||||||
+2
-15
@@ -1,14 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 丰富诊断(参考 rustc / Clang / Swift 的设计,不照搬 stage1 C 实现):
|
// 丰富诊断:Severity / 稳定 E 码 / Span / Label / Child,渲染为人类可读或 JSON。
|
||||||
// Severity Note/Help/Warning/Error/Fatal
|
// 位置只存下标,渲染时经 Sources 解析行列;条目拥有其字符串与 labels/children。
|
||||||
// Code 固定语义枚举 + 稳定 E 码;集中管理,利于 diff
|
|
||||||
// Span 字节区间 {file,start,end};file 是 Sources 的下标,NO_FILE 表示无位置
|
|
||||||
// Label 主/次标签(span + 文本),可多个
|
|
||||||
// Child 子诊断(note/help,可带 span),挂在条目上
|
|
||||||
// 渲染:人类可读(源码行 + caret 下划线)与 JSON(字段稳定)双格式。
|
|
||||||
// 位置只存下标,渲染时经 Sources 解析行列/取行,避免复制文件名。
|
|
||||||
// 消息按拥有型 Str 保存;条目拥有其 labels/children;clear/drop 释放全部内部资源。
|
|
||||||
|
|
||||||
type .{ .Error, .Status, .make } = @import("./utils/error.spl");
|
type .{ .Error, .Status, .make } = @import("./utils/error.spl");
|
||||||
type .{ .Vec } = @import("./utils/vec.spl");
|
type .{ .Vec } = @import("./utils/vec.spl");
|
||||||
@@ -181,7 +174,6 @@ fn has_error(self: Diag): bool { ret self.errors > 0; }
|
|||||||
|
|
||||||
fn has_fatal(self: Diag): bool { ret self.fatals > 0; }
|
fn has_fatal(self: Diag): bool { ret self.fatals > 0; }
|
||||||
|
|
||||||
// ---- 采集 ----
|
|
||||||
|
|
||||||
// 追加一条诊断,返回其下标(失败返回 no_file())。span 为主位置。
|
// 追加一条诊断,返回其下标(失败返回 no_file())。span 为主位置。
|
||||||
fn emit(self: *Diag, severity: Severity, stage: Stage, code: Code, span: Span,
|
fn emit(self: *Diag, severity: Severity, stage: Stage, code: Code, span: Span,
|
||||||
@@ -265,7 +257,6 @@ fn attach_child(self: *Diag, severity: Severity, span: Span, msg: []u8): Status
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 名称 ----
|
|
||||||
|
|
||||||
fn severity_name(severity: Severity): []u8 {
|
fn severity_name(severity: Severity): []u8 {
|
||||||
match severity {
|
match severity {
|
||||||
@@ -370,7 +361,6 @@ fn code_name(code: Code): []u8 {
|
|||||||
ret "none";
|
ret "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 渲染:公共小工具 ----
|
|
||||||
|
|
||||||
fn append_num(out: *ByteBuffer, value: usize): Status {
|
fn append_num(out: *ByteBuffer, value: usize): Status {
|
||||||
ret append_usize(out, value, 10, false);
|
ret append_usize(out, value, 10, false);
|
||||||
@@ -519,7 +509,6 @@ fn render_human(self: *Diag, out: *ByteBuffer): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- JSON 渲染(字段稳定,供 LSP / diff 工具)----
|
|
||||||
|
|
||||||
fn append_json_hex4(out: *ByteBuffer, b: u8): Status {
|
fn append_json_hex4(out: *ByteBuffer, b: u8): Status {
|
||||||
var hex: []u8 = "0123456789abcdef";
|
var hex: []u8 = "0123456789abcdef";
|
||||||
@@ -661,7 +650,6 @@ fn render_json(self: *Diag, out: *ByteBuffer): Status {
|
|||||||
ret ByteBuffer.append_byte(out, ']');
|
ret ByteBuffer.append_byte(out, ']');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 生命周期 ----
|
|
||||||
|
|
||||||
fn drop_item(it: *Item) {
|
fn drop_item(it: *Item) {
|
||||||
Str.drop(&it.message);
|
Str.drop(&it.message);
|
||||||
@@ -700,7 +688,6 @@ fn drop(self: *Diag) {
|
|||||||
self.sources = null;
|
self.sources = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
+9
-18
@@ -1,22 +1,16 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// comptime / const 求值的**接口 seam**(本轮只占位,不实现求值)。
|
// comptime/const 求值的接口 seam:sema 只在此调用,未实现时返回 NotConst(放弃折叠、
|
||||||
//
|
// 不报错)。编译期生成的声明经 DeclSink 登记,不改 sema 表。
|
||||||
// 目标:把「编译期求值」从 sema 主体里隔离出来,将来接一个解释器(可直接复用
|
|
||||||
// VM 或 AST 求值器)时不动 sema 结构。sema 只在 comptime 语句 / const 初始化 /
|
|
||||||
// @import 等点调用这里;未实现时统一返回 NotConst(= 放弃折叠,不报错)。
|
|
||||||
//
|
|
||||||
// 分块编译相关:eval 未来的产物是「编译期生成的声明」,统一经 DeclSink 登记,
|
|
||||||
// 由驱动在固定点循环里插入并触发重跑;eval 自己不直接改 sema 表,避免重入。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
type Ast = @import("./ast.spl");
|
type Ast = @import("./ast.spl");
|
||||||
type Diag = @import("./diag.spl");
|
type Diag = @import("./diag.spl");
|
||||||
|
|
||||||
// 编译期值:占位。未来扩展为 BigInt / 聚合 / 类型句柄等。
|
// 编译期值(当前仅标量)。
|
||||||
type EvalValue = enum {
|
type EvalValue = enum {
|
||||||
Int: usize, // TODO: BigInt
|
Int: usize,
|
||||||
Bool: bool,
|
Bool: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,14 +30,13 @@ type DeclSinkResult = enum {
|
|||||||
Err: Error,
|
Err: Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 求值上下文(借用 sink 与 diag;本轮不持有所有权)。
|
// 求值上下文:sink/diag 均借用,不持有所有权。
|
||||||
type Eval = @self;
|
type Eval = @self;
|
||||||
|
|
||||||
sink: *DeclSink, // 借用,可为 null
|
sink: *DeclSink, // 借用,可为 null
|
||||||
diag: *Diag, // 借用,可为 null
|
diag: *Diag, // 借用,可为 null
|
||||||
enabled: bool, // false = 完全不求值(等价旧行为)
|
enabled: bool, // false = 完全不求值
|
||||||
|
|
||||||
// ---- DeclSink ----
|
|
||||||
|
|
||||||
fn sink_new(): DeclSinkResult {
|
fn sink_new(): DeclSinkResult {
|
||||||
var s = DeclSink { .items = Vec.blank(@sizeof(usize), @alignof(usize)) };
|
var s = DeclSink { .items = Vec.blank(@sizeof(usize), @alignof(usize)) };
|
||||||
@@ -66,7 +59,6 @@ fn sink_len(self: *DeclSink): usize {
|
|||||||
ret self.items.len;
|
ret self.items.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Eval ----
|
|
||||||
|
|
||||||
fn new(sink: *DeclSink, diag: *Diag, enabled: bool): Eval {
|
fn new(sink: *DeclSink, diag: *Diag, enabled: bool): Eval {
|
||||||
ret Eval {
|
ret Eval {
|
||||||
@@ -80,25 +72,24 @@ fn is_enabled(self: *Eval): bool {
|
|||||||
ret self.enabled;
|
ret self.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 求值一个表达式。本轮一律 NotConst(可由调用方作为「放弃折叠」处理)。
|
// 求值表达式:当前一律 NotConst(调用方按“放弃折叠”处理)。
|
||||||
fn eval_expr(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
fn eval_expr(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
||||||
if !self.enabled { ret .NotConst; }
|
if !self.enabled { ret .NotConst; }
|
||||||
ret .NotConst;
|
ret .NotConst;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 执行一个 comptime 块(可能产生声明,登记到 sink)。本轮不做。
|
// 执行 comptime 块;当前不实现,直接 NotConst。
|
||||||
fn run_block(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
fn run_block(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
||||||
if !self.enabled { ret .NotConst; }
|
if !self.enabled { ret .NotConst; }
|
||||||
ret .NotConst;
|
ret .NotConst;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 顶层 const 初始化求值。本轮不做。
|
// 顶层 const 初始化求值;当前不实现。
|
||||||
fn eval_const_init(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
fn eval_const_init(self: *Eval, ast: *Ast, ref: usize): EvalOutcome {
|
||||||
if !self.enabled { ret .NotConst; }
|
if !self.enabled { ret .NotConst; }
|
||||||
ret .NotConst;
|
ret .NotConst;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_sink() {
|
fn test_sink() {
|
||||||
var sink: DeclSink = undefined;
|
var sink: DeclSink = undefined;
|
||||||
|
|||||||
+3
-5
@@ -1,8 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 内建(intrinsic)注册表(移植自 stage1/spl_intrin.c)。表是数据:按 kind 查
|
// 内建注册表:按 Intrin 查描述信息,按名字/字段/`as` 规则反查 Intrin。新增内建只改
|
||||||
// 描述信息,按名字/字段/`as` 转换规则反查 kind。新增内建只改 info_at,
|
// info_at,不改 sema 逻辑(SPL.md 的 @/# 扩展点)。
|
||||||
// 不改 sema 逻辑(对应 SPL.md 的 @/# 扩展点)。
|
|
||||||
|
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
type TypeTable = @import("./type_table.spl");
|
type TypeTable = @import("./type_table.spl");
|
||||||
@@ -63,7 +62,7 @@ fn kind_is(a: LangKind, b: LangKind): bool {
|
|||||||
ret @enum2int(a) == @enum2int(b);
|
ret @enum2int(a) == @enum2int(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 与 stage1 表逐项对应(枚举顺序即 kind 数值)。
|
// 枚举顺序即 kind 数值(与 splc0 表一致)。
|
||||||
fn info_at(index: usize): IntrinInfo {
|
fn info_at(index: usize): IntrinInfo {
|
||||||
match index {
|
match index {
|
||||||
1 => {
|
1 => {
|
||||||
@@ -250,7 +249,6 @@ fn lookup_cast(ir: *TypeTable, ft: usize, tt: usize): Intrin {
|
|||||||
ret .None;
|
ret .None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_info_table() {
|
fn test_info_table() {
|
||||||
var d = info(.Sizeof);
|
var d = info(.Sizeof);
|
||||||
|
|||||||
+46
-17
@@ -1,10 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// SIR 中间表示(移植自 stage1/spl_ir.c/.h)。节点用 arena 序号引用(0 = error),
|
// SIR 中间表示:节点用 arena 序号引用(0=error),按函数分桶;公共字段在 IrEntry,
|
||||||
// 按函数分桶。节点公共字段(kind/dbg/attrs)在外层 entry,kind 专属载荷在 ADT
|
// kind 专属载荷在 IrBody ADT(kind 与载荷一一对应)。builder 负责发射与跳转回填。
|
||||||
// IrBody;kind 与载荷变体一一对应(多种 kind 可共享同一载荷形状)。
|
|
||||||
//
|
|
||||||
// builder 维护当前函数/基本块/fixup 表,负责发射节点与回填跳转。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -82,6 +79,14 @@ type ConstLit = enum {
|
|||||||
Float: f64,
|
Float: f64,
|
||||||
Char: u8,
|
Char: u8,
|
||||||
Fn: usize,
|
Fn: usize,
|
||||||
|
Global: usize, // 全局变量下标(Ir.globals);值 = 指向该全局的 *T
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模块级全局变量:编译期分配的静态存储,引用时是一个 *T 指针常量(同 LLVM GlobalVariable)。
|
||||||
|
type Global = struct {
|
||||||
|
tid: usize,
|
||||||
|
zero: bool, // true = 零初始化(BSS,不携带字节)
|
||||||
|
data: Vec, // u8 初始字节(zero=false 时使用)
|
||||||
}
|
}
|
||||||
|
|
||||||
type IrBody = enum {
|
type IrBody = enum {
|
||||||
@@ -128,8 +133,8 @@ type IrFunc = struct {
|
|||||||
type Ir = @self;
|
type Ir = @self;
|
||||||
|
|
||||||
funcs: Vec, // IrFunc
|
funcs: Vec, // IrFunc
|
||||||
|
globals: Vec, // Global
|
||||||
|
|
||||||
// ---- 生命周期 ----
|
|
||||||
|
|
||||||
fn new_entry(): IrEntry {
|
fn new_entry(): IrEntry {
|
||||||
ret IrEntry {
|
ret IrEntry {
|
||||||
@@ -141,7 +146,8 @@ fn new_entry(): IrEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn new(): Ir {
|
fn new(): Ir {
|
||||||
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)) };
|
var ir = Ir { .funcs = Vec.blank(@sizeof(IrFunc), @alignof(IrFunc)),
|
||||||
|
.globals = Vec.blank(@sizeof(Global), @alignof(Global)) };
|
||||||
var sentinel = IrFunc {
|
var sentinel = IrFunc {
|
||||||
.name = ByteView.empty(),
|
.name = ByteView.empty(),
|
||||||
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
|
.attrs = Vec.blank(@sizeof(usize), @alignof(usize)),
|
||||||
@@ -149,8 +155,8 @@ fn new(): Ir {
|
|||||||
.nodes = Vec.blank(@sizeof(IrEntry), @alignof(IrEntry)),
|
.nodes = Vec.blank(@sizeof(IrEntry), @alignof(IrEntry)),
|
||||||
};
|
};
|
||||||
var sn = new_entry();
|
var sn = new_entry();
|
||||||
match Vec.push(&sentinel.nodes, &sn as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&sentinel.nodes, &sn as *u8);
|
||||||
match Vec.push(&ir.funcs, &sentinel as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&ir.funcs, &sentinel as *u8);
|
||||||
ret ir;
|
ret ir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +186,25 @@ fn drop(self: *Ir) {
|
|||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
Vec.drop(&self.funcs);
|
Vec.drop(&self.funcs);
|
||||||
|
var g: usize = 0;
|
||||||
|
while g < self.globals.len {
|
||||||
|
var ge = (self.globals.data as *Global) + g;
|
||||||
|
Vec.drop(&ge.data);
|
||||||
|
g = g + 1;
|
||||||
|
}
|
||||||
|
Vec.drop(&self.globals);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加一个全局变量;返回其下标(gidx)。bytes 的所有权转移进表。
|
||||||
|
fn add_global(self: *Ir, tid: usize, zero: bool, bytes: Vec): usize {
|
||||||
|
var g = Global { .tid = tid, .zero = zero, .data = bytes };
|
||||||
|
match Vec.push(&self.globals, &g as *u8) { .Ok => {}, .Err(error) => { ret 0; } }
|
||||||
|
ret self.globals.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn global_at(self: *Ir, gidx: usize): *Global {
|
||||||
|
if gidx >= self.globals.len { ret null; }
|
||||||
|
ret (self.globals.data as *Global) + gidx;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn alloc_fn(self: *Ir): usize {
|
fn alloc_fn(self: *Ir): usize {
|
||||||
@@ -278,7 +303,6 @@ fn kind_name(kind: IrKind): []u8 {
|
|||||||
ret "?";
|
ret "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump ----
|
|
||||||
|
|
||||||
fn out_str(out: *ByteBuffer, s: []u8): bool {
|
fn out_str(out: *ByteBuffer, s: []u8): bool {
|
||||||
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
@@ -331,11 +355,10 @@ fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
|
|||||||
.Arith(a) => {
|
.Arith(a) => {
|
||||||
if !dump_type(out, ty, a.tid) { ret false; }
|
if !dump_type(out, ty, a.tid) { ret false; }
|
||||||
if !dump_ref(out, a.left) { ret false; }
|
if !dump_ref(out, a.left) { ret false; }
|
||||||
// 一元 arith(neg/abs/not)不打印 right(镜像 stage1)。
|
// 一元 arith(neg/abs/not)不打印 right。
|
||||||
if @enum2int(e.kind) == @enum2int(IrKind.ArithNeg) ||
|
match e.kind {
|
||||||
@enum2int(e.kind) == @enum2int(IrKind.ArithAbs) ||
|
.ArithNeg, .ArithAbs, .ArithNot => { ret true; },
|
||||||
@enum2int(e.kind) == @enum2int(IrKind.ArithNot) {
|
_ => {},
|
||||||
ret true;
|
|
||||||
}
|
}
|
||||||
ret dump_ref(out, a.right);
|
ret dump_ref(out, a.right);
|
||||||
},
|
},
|
||||||
@@ -394,6 +417,13 @@ fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
|
|||||||
},
|
},
|
||||||
.Const(c) => {
|
.Const(c) => {
|
||||||
if !dump_type(out, ty, c.tid) { ret false; }
|
if !dump_type(out, ty, c.tid) { ret false; }
|
||||||
|
match c.lit {
|
||||||
|
.Global(g) => {
|
||||||
|
if !out_str(out, " global#") { ret false; }
|
||||||
|
ret out_u(out, g);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
var te = TypeTable.node(ty, c.tid);
|
var te = TypeTable.node(ty, c.tid);
|
||||||
var is_float = false;
|
var is_float = false;
|
||||||
var is_fn = false;
|
var is_fn = false;
|
||||||
@@ -429,7 +459,7 @@ fn dump_body(out: *ByteBuffer, ty: *TypeTable, e: *IrEntry): bool {
|
|||||||
match ByteBuffer.append_byte(out, cv as u8) { .Ok => {}, .Err(error) => { ret false; } }
|
match ByteBuffer.append_byte(out, cv as u8) { .Ok => {}, .Err(error) => { ret false; } }
|
||||||
ret out_str(out, "'");
|
ret out_str(out, "'");
|
||||||
}
|
}
|
||||||
// stage1 对其他常量统一按 int_lit 打印(union 别名)。
|
// 其他常量统一按 int_lit 打印(union 别名)。
|
||||||
var iv: usize = 0;
|
var iv: usize = 0;
|
||||||
match c.lit {
|
match c.lit {
|
||||||
.Int(v) => { iv = v; },
|
.Int(v) => { iv = v; },
|
||||||
@@ -546,7 +576,6 @@ fn dump(self: *Ir, ty: *TypeTable, attr: *Attr, out: *ByteBuffer): bool {
|
|||||||
ret true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_kinds() {
|
fn test_kinds() {
|
||||||
@assert(ByteView.eq(ByteView.from_slice(kind_name(.ArithAdd)), ByteView.from_slice("arith.add")),
|
@assert(ByteView.eq(ByteView.from_slice(kind_name(.ArithAdd)), ByteView.from_slice("arith.add")),
|
||||||
|
|||||||
+30
-14
@@ -1,12 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// IR -> C99 后端(P1a 子集)。
|
// IR -> C99。每个 IR ref 生成一个 C 局部变量(交给 C 编译器 mem2reg),标签用 goto;
|
||||||
//
|
// 类型由 IR tid 决定,生成期无运行时分发。非 extern 函数名为 `spl_f<fid>`。
|
||||||
// 设计:每个 IR ref 一个 C 局部变量(C 编译器 -O2 会做 mem2reg),标签用 goto;
|
|
||||||
// 类型直接由 IR 的 tid 决定,生成期无运行时分发。
|
|
||||||
//
|
|
||||||
// 每个非 extern 函数的 C 名为 `spl_f<fid>`;extern 函数直接用其 SPL 名
|
|
||||||
// (由 runtime 提供)。暂不支持聚合/切片/全局数据。
|
|
||||||
|
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
@@ -18,7 +13,7 @@ type .{ .Layout } = @import("./layout.spl");
|
|||||||
type AttrM = @import("./attr.spl");
|
type AttrM = @import("./attr.spl");
|
||||||
type .{ .Attr } = @import("./attr.spl");
|
type .{ .Attr } = @import("./attr.spl");
|
||||||
type IrM = @import("./ir.spl");
|
type IrM = @import("./ir.spl");
|
||||||
type .{ .Ir, .IrFunc, .IrEntry } = @import("./ir.spl");
|
type .{ .Ir, .IrFunc, .IrEntry, .Global } = @import("./ir.spl");
|
||||||
|
|
||||||
type Ir2C = @self;
|
type Ir2C = @self;
|
||||||
|
|
||||||
@@ -83,7 +78,6 @@ fn put_unsupported(self: *Ir2C, what: []u8) {
|
|||||||
put(self, " */\n");
|
put(self, " */\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 类型查询 ----
|
|
||||||
|
|
||||||
fn info_of(self: *Ir2C, tid: usize): *LayoutM.LayoutInfo {
|
fn info_of(self: *Ir2C, tid: usize): *LayoutM.LayoutInfo {
|
||||||
ret LayoutM.info_of(self.layout, tid);
|
ret LayoutM.info_of(self.layout, tid);
|
||||||
@@ -168,7 +162,6 @@ fn fn_ret_type(self: *Ir2C, fn_tid: usize): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- C 类型 ----
|
|
||||||
|
|
||||||
fn put_type(self: *Ir2C, tid: usize): bool {
|
fn put_type(self: *Ir2C, tid: usize): bool {
|
||||||
var te = TypeTable.node(self.ty, tid);
|
var te = TypeTable.node(self.ty, tid);
|
||||||
@@ -231,7 +224,6 @@ fn c_fn_name(self: *Ir2C, fid: usize) {
|
|||||||
put_u(self, fid);
|
put_u(self, fid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 常量解析 ----
|
|
||||||
|
|
||||||
fn const_node(self: *Ir2C, f: *IrFunc, ref: usize): usize {
|
fn const_node(self: *Ir2C, f: *IrFunc, ref: usize): usize {
|
||||||
if ref == 0 || ref >= f.nodes.len { ret 0; }
|
if ref == 0 || ref >= f.nodes.len { ret 0; }
|
||||||
@@ -250,7 +242,6 @@ fn const_node(self: *Ir2C, f: *IrFunc, ref: usize): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 聚合字段 ----
|
|
||||||
|
|
||||||
fn field_tid(self: *Ir2C, tid: usize, idx: usize): usize {
|
fn field_tid(self: *Ir2C, tid: usize, idx: usize): usize {
|
||||||
var te = TypeTable.node(self.ty, tid);
|
var te = TypeTable.node(self.ty, tid);
|
||||||
@@ -436,7 +427,6 @@ fn emit_agg_insert(self: *Ir2C, ref: usize, n: *IrEntry) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 节点发射 ----
|
|
||||||
|
|
||||||
fn emit_arith(self: *Ir2C, ref: usize, n: *IrEntry) {
|
fn emit_arith(self: *Ir2C, ref: usize, n: *IrEntry) {
|
||||||
match n.body {
|
match n.body {
|
||||||
@@ -570,6 +560,7 @@ fn emit_const(self: *Ir2C, ref: usize, n: *IrEntry) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Ptr(v) => { put(self, "(void*)"); put_u(self, v); },
|
.Ptr(v) => { put(self, "(void*)"); put_u(self, v); },
|
||||||
|
.Global(g) => { put(self, "(void*)spl_g_"); put_u(self, g); },
|
||||||
.Float(v) => { put(self, "0.0"); },
|
.Float(v) => { put(self, "0.0"); },
|
||||||
.Fn(fid) => { put(self, "(void*)&"); c_fn_name(self, fid); },
|
.Fn(fid) => { put(self, "(void*)&"); c_fn_name(self, fid); },
|
||||||
}
|
}
|
||||||
@@ -1223,7 +1214,6 @@ fn emit_def(self: *Ir2C, fid: usize) {
|
|||||||
put(self, "spl_trap();\n}\n");
|
put(self, "spl_trap();\n}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- driver ----
|
|
||||||
|
|
||||||
fn find_main(self: *Ir2C): usize {
|
fn find_main(self: *Ir2C): usize {
|
||||||
var i: usize = 1;
|
var i: usize = 1;
|
||||||
@@ -1240,6 +1230,32 @@ fn find_main(self: *Ir2C): usize {
|
|||||||
fn run(self: *Ir2C) {
|
fn run(self: *Ir2C) {
|
||||||
put(self, "#include \"spl_rt.h\"\n");
|
put(self, "#include \"spl_rt.h\"\n");
|
||||||
|
|
||||||
|
// 模块级全局变量(Zero → BSS:无初始化式)。
|
||||||
|
var gi: usize = 0;
|
||||||
|
while gi < self.ir.globals.len {
|
||||||
|
var g = (self.ir.globals.data as *Global) + gi;
|
||||||
|
var sz = type_size(self, g.tid);
|
||||||
|
if sz == 0 { sz = 1; }
|
||||||
|
put(self, "static unsigned char spl_g_");
|
||||||
|
put_u(self, gi);
|
||||||
|
put(self, "[");
|
||||||
|
put_u(self, sz);
|
||||||
|
put(self, "]");
|
||||||
|
if !g.zero {
|
||||||
|
put(self, " = {");
|
||||||
|
var k: usize = 0;
|
||||||
|
while k < g.data.len {
|
||||||
|
if k > 0 { put(self, ", "); }
|
||||||
|
put_u(self, ((g.data.data as *u8) + k).* as usize);
|
||||||
|
k = k + 1;
|
||||||
|
}
|
||||||
|
if g.data.len == 0 { put(self, "0"); }
|
||||||
|
put(self, "}");
|
||||||
|
}
|
||||||
|
put(self, ";\n");
|
||||||
|
gi = gi + 1;
|
||||||
|
}
|
||||||
|
|
||||||
var i: usize = 1;
|
var i: usize = 1;
|
||||||
while i < self.ir.funcs.len {
|
while i < self.ir.funcs.len {
|
||||||
var f = IrM.func(self.ir, i);
|
var f = IrM.func(self.ir, i);
|
||||||
|
|||||||
+77
-50
@@ -1,11 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// IR -> VM 机器码(移植自 stage1/spl_ir2vm.c)。
|
// IR -> VM 字节码。extern_native 是按函数 ref 下标的 isize 数组:-1 非 extern,
|
||||||
// 依赖:IR(ir.spl)、IR 类型表(type_table.spl)、布局(layout.spl)、
|
// >=0 为 native 索引。
|
||||||
// 属性(attr.spl)、字节码容器(mcode.spl)。
|
|
||||||
//
|
|
||||||
// extern_native 用「按函数 ref 下标的 isize 数组」代替 stage1 的 map:
|
|
||||||
// 默认 -1 表示非 extern,>=0 表示 native 索引(语义等价)。
|
|
||||||
|
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
@@ -16,10 +12,10 @@ type .{ .Layout } = @import("./layout.spl");
|
|||||||
type AttrM = @import("./attr.spl");
|
type AttrM = @import("./attr.spl");
|
||||||
type .{ .Attr, .AttrArg } = @import("./attr.spl");
|
type .{ .Attr, .AttrArg } = @import("./attr.spl");
|
||||||
type IrM = @import("./ir.spl");
|
type IrM = @import("./ir.spl");
|
||||||
type .{ .Ir, .IrFunc, .IrEntry } = @import("./ir.spl");
|
type .{ .Ir, .IrFunc, .IrEntry, .Global } = @import("./ir.spl");
|
||||||
type Mcode = @import("./mcode.spl");
|
type Mcode = @import("./mcode.spl");
|
||||||
type .{ .Prog, .Op, .VmKind, .VmIns, .VmFunc } = @import("./mcode.spl");
|
type .{ .Prog, .Op, .VmKind, .VmIns, .VmFunc } = @import("./mcode.spl");
|
||||||
type Mem = @import("sys/mem.spl");
|
type Mem = @import("utils/memory.spl");
|
||||||
|
|
||||||
type Ir2Vm = @self;
|
type Ir2Vm = @self;
|
||||||
|
|
||||||
@@ -28,9 +24,9 @@ ty: *TypeTable,
|
|||||||
attr: *Attr,
|
attr: *Attr,
|
||||||
layout: *Layout,
|
layout: *Layout,
|
||||||
extern_native: Vec, // isize
|
extern_native: Vec, // isize
|
||||||
|
global_gaddr: Vec, // usize:每个全局变量在 prog.gdata 的下标
|
||||||
prog: Prog,
|
prog: Prog,
|
||||||
|
|
||||||
// ---- 类型/布局查询 ----
|
|
||||||
|
|
||||||
fn info_of(ctx: *Ir2Vm, tid: usize): *LayoutM.LayoutInfo {
|
fn info_of(ctx: *Ir2Vm, tid: usize): *LayoutM.LayoutInfo {
|
||||||
ret LayoutM.info_of(ctx.layout, tid);
|
ret LayoutM.info_of(ctx.layout, tid);
|
||||||
@@ -186,7 +182,6 @@ fn ir_result_tid(ctx: *Ir2Vm, n: *IrEntry): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 小工具 ----
|
|
||||||
|
|
||||||
// 内部热路径:假定索引有效,直接取址(省去 Vec.get/valid)。
|
// 内部热路径:假定索引有效,直接取址(省去 Vec.get/valid)。
|
||||||
fn usize_at(v: *Vec, i: usize): usize {
|
fn usize_at(v: *Vec, i: usize): usize {
|
||||||
@@ -194,7 +189,7 @@ fn usize_at(v: *Vec, i: usize): usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push_usize(v: *Vec, x: usize) {
|
fn push_usize(v: *Vec, x: usize) {
|
||||||
ignore_bool(Vec.push_raw(v, &x as *u8));
|
Vec.push_raw(v, &x as *u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fnode(f: *IrFunc, i: usize): *IrEntry {
|
fn fnode(f: *IrFunc, i: usize): *IrEntry {
|
||||||
@@ -238,7 +233,6 @@ fn const_fn(n: *IrEntry): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- extern ----
|
|
||||||
|
|
||||||
fn fn_check_extern(ctx: *Ir2Vm, f: *IrFunc): bool {
|
fn fn_check_extern(ctx: *Ir2Vm, f: *IrFunc): bool {
|
||||||
if ctx.attr == null || f == null { ret false; }
|
if ctx.attr == null || f == null { ret false; }
|
||||||
@@ -274,7 +268,6 @@ fn extern_set(ctx: *Ir2Vm, ref: usize, value: isize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 帧布局 ----
|
|
||||||
|
|
||||||
type Slot = struct {
|
type Slot = struct {
|
||||||
result_off: usize,
|
result_off: usize,
|
||||||
@@ -362,7 +355,7 @@ fn frame_build(fl: *FrameLayout, ctx: *Ir2Vm, fid: usize) {
|
|||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while i < n {
|
while i < n {
|
||||||
var blank: Slot = .{ .result_off = 0, .data_off = 0 };
|
var blank: Slot = .{ .result_off = 0, .data_off = 0 };
|
||||||
ignore_bool(Vec.push_raw(&fl.slots, &blank as *u8));
|
Vec.push_raw(&fl.slots, &blank as *u8);
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,7 +389,6 @@ fn frame_drop(fl: *FrameLayout) {
|
|||||||
Vec.drop(&fl.slots);
|
Vec.drop(&fl.slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- cgen ----
|
|
||||||
|
|
||||||
type FixKind = enum {
|
type FixKind = enum {
|
||||||
Jmp,
|
Jmp,
|
||||||
@@ -431,7 +423,7 @@ fn emit(cg: *Cgen, op: Op, ty: VmKind, imm: usize) {
|
|||||||
prog.insns.len = i + 1;
|
prog.insns.len = i + 1;
|
||||||
ret;
|
ret;
|
||||||
}
|
}
|
||||||
ignore_usize(Mcode.add_instr(prog, op, ty, imm));
|
Mcode.add_instr(prog, op, ty, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ip_of(cg: *Cgen): usize {
|
fn ip_of(cg: *Cgen): usize {
|
||||||
@@ -455,20 +447,20 @@ fn cg_swap(cg: *Cgen) { emit(cg, .Swap, .Void, 0); }
|
|||||||
fn cg_gaddr(cg: *Cgen, idx: usize) { emit(cg, .Gaddr, .Void, idx); }
|
fn cg_gaddr(cg: *Cgen, idx: usize) { emit(cg, .Gaddr, .Void, idx); }
|
||||||
|
|
||||||
fn cg_load_slot(cg: *Cgen, ref: usize, k: VmKind) {
|
fn cg_load_slot(cg: *Cgen, ref: usize, k: VmKind) {
|
||||||
cg_laddr(cg, slot_result(&cg.frame, ref));
|
emit(cg, .Laddr, .Void, slot_result(&cg.frame, ref));
|
||||||
cg_load(cg, k);
|
emit(cg, .Load, k, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cg_store_slot(cg: *Cgen, ref: usize, k: VmKind) {
|
fn cg_store_slot(cg: *Cgen, ref: usize, k: VmKind) {
|
||||||
cg_laddr(cg, slot_result(&cg.frame, ref));
|
emit(cg, .Laddr, .Void, slot_result(&cg.frame, ref));
|
||||||
cg_swap(cg);
|
emit(cg, .Swap, .Void, 0);
|
||||||
cg_store(cg, k);
|
emit(cg, .Store, k, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cg_store_at(cg: *Cgen, addr: usize, k: VmKind) {
|
fn cg_store_at(cg: *Cgen, addr: usize, k: VmKind) {
|
||||||
cg_laddr(cg, addr);
|
emit(cg, .Laddr, .Void, addr);
|
||||||
cg_swap(cg);
|
emit(cg, .Swap, .Void, 0);
|
||||||
cg_store(cg, k);
|
emit(cg, .Store, k, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataLoc = struct {
|
type DataLoc = struct {
|
||||||
@@ -533,7 +525,7 @@ fn cg_add_fix(cg: *Cgen, ins_idx: usize, kind: FixKind, node_target: usize, fn_t
|
|||||||
.node_target = node_target,
|
.node_target = node_target,
|
||||||
.fn_target = fn_target,
|
.fn_target = fn_target,
|
||||||
};
|
};
|
||||||
match Vec.push(cg.fixups, &fx as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(cg.fixups, &fx as *u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arith_tid(n: *IrEntry): usize {
|
fn arith_tid(n: *IrEntry): usize {
|
||||||
@@ -589,6 +581,23 @@ fn cg_const(cg: *Cgen, ref: usize, n: *IrEntry) {
|
|||||||
var te = TypeTable.node(ctx.ty, tid);
|
var te = TypeTable.node(ctx.ty, tid);
|
||||||
if te == null { ret; }
|
if te == null { ret; }
|
||||||
|
|
||||||
|
// 全局变量引用:Const{ lit = Global(gidx) } → GADDR(该全局的运行时地址)。
|
||||||
|
match n.body {
|
||||||
|
.Const(c) => {
|
||||||
|
match c.lit {
|
||||||
|
.Global(g) => {
|
||||||
|
var di: usize = 0;
|
||||||
|
if g < ctx.global_gaddr.len { di = (ctx.global_gaddr.data as *usize)[g]; }
|
||||||
|
emit(cg, .Gaddr, .Void, di);
|
||||||
|
cg_store_slot(cg, ref, .Ptr);
|
||||||
|
ret;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
|
||||||
match te.node {
|
match te.node {
|
||||||
.Fn(f) => {
|
.Fn(f) => {
|
||||||
var cref = const_fn(n);
|
var cref = const_fn(n);
|
||||||
@@ -620,6 +629,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 {
|
||||||
@@ -677,7 +687,7 @@ fn cg_cast(cg: *Cgen, ref: usize, n: *IrEntry) {
|
|||||||
} else if f_bool && t_int {
|
} else if f_bool && t_int {
|
||||||
emit(cg, .Zext, .Void, 1);
|
emit(cg, .Zext, .Void, 1);
|
||||||
}
|
}
|
||||||
ignore_bool(t_bool);
|
t_bool;
|
||||||
cg_store_slot(cg, ref, tk);
|
cg_store_slot(cg, ref, tk);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -897,10 +907,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 {
|
||||||
@@ -952,6 +959,9 @@ fn cg_control(cg: *Cgen, ref: usize, n: *IrEntry) {
|
|||||||
cg_store_slot(cg, ref, rk);
|
cg_store_slot(cg, ref, rk);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
/* ABI:实参下方预留 2 个空闲 slot(PAD0/PAD1)。 */
|
||||||
|
cg_push(cg, .U64, 0);
|
||||||
|
cg_push(cg, .U64, 0);
|
||||||
var ret_t = cl.tid;
|
var ret_t = cl.tid;
|
||||||
var ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
var ret_is_sret = ir_is_aggregate(ctx, ret_t);
|
||||||
var sret_off: usize = 0;
|
var sret_off: usize = 0;
|
||||||
@@ -1129,7 +1139,6 @@ fn cmp_tid(n: *IrEntry): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 翻译 ----
|
|
||||||
|
|
||||||
fn translate_function(ctx: *Ir2Vm, fid: usize, all_fixups: *Vec): usize {
|
fn translate_function(ctx: *Ir2Vm, fid: usize, all_fixups: *Vec): usize {
|
||||||
var irf = IrM.func(ctx.ir, fid);
|
var irf = IrM.func(ctx.ir, fid);
|
||||||
@@ -1153,7 +1162,7 @@ fn translate_function(ctx: *Ir2Vm, fid: usize, all_fixups: *Vec): usize {
|
|||||||
|
|
||||||
var func_ip = ip_of(&cg);
|
var func_ip = ip_of(&cg);
|
||||||
|
|
||||||
ignore_usize(Mcode.add_func(&ctx.prog, irf.name, fn_nargs(ctx, irf.fn_tid), func_ip));
|
Mcode.add_func(&ctx.prog, irf.name, fn_nargs(ctx, irf.fn_tid), func_ip);
|
||||||
|
|
||||||
var local_slots = (cg.frame.frame_bytes - cg.frame.args_bytes + 7) / 8;
|
var local_slots = (cg.frame.frame_bytes - cg.frame.args_bytes + 7) / 8;
|
||||||
emit(&cg, .Alloc, .Void, local_slots);
|
emit(&cg, .Alloc, .Void, local_slots);
|
||||||
@@ -1174,10 +1183,9 @@ fn translate_function(ctx: *Ir2Vm, fid: usize, all_fixups: *Vec): usize {
|
|||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while i < local_fixups.len {
|
while i < local_fixups.len {
|
||||||
match Vec.get(&local_fixups, i) {
|
var fx = (local_fixups.data as *Fix) + i;
|
||||||
.Ok(ptr) => {
|
match fx.kind {
|
||||||
var fx = ptr as *Fix;
|
.Jmp => {
|
||||||
if @enum2int(fx.kind) == @enum2int(FixKind.Jmp) {
|
|
||||||
var ins = ins_ptr(&ctx.prog, fx.ins_idx);
|
var ins = ins_ptr(&ctx.prog, fx.ins_idx);
|
||||||
if ins != null {
|
if ins != null {
|
||||||
var target_ip: usize = 0;
|
var target_ip: usize = 0;
|
||||||
@@ -1186,11 +1194,8 @@ fn translate_function(ctx: *Ir2Vm, fid: usize, all_fixups: *Vec): usize {
|
|||||||
}
|
}
|
||||||
ins.imm = target_ip - fx.ins_idx - 1;
|
ins.imm = target_ip - fx.ins_idx - 1;
|
||||||
}
|
}
|
||||||
} else if @enum2int(fx.kind) == @enum2int(FixKind.Fnaddr) {
|
|
||||||
match Vec.push(all_fixups, ptr as *u8) { .Ok => {}, .Err(error) => {} }
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.Err(error) => {},
|
.Fnaddr => { Vec.push(all_fixups, fx as *u8); },
|
||||||
}
|
}
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
@@ -1201,15 +1206,15 @@ fn translate_function(ctx: *Ir2Vm, fid: usize, all_fixups: *Vec): usize {
|
|||||||
ret func_ip;
|
ret func_ip;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 生命周期 / 入口 ----
|
|
||||||
|
|
||||||
// 就地初始化(避免返回含 Prog 的大聚合;对应 stage1 spl_ir2vm_init)。
|
// 就地初始化,避免返回含 Prog 的大聚合。
|
||||||
fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) {
|
fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) {
|
||||||
ctx.ir = ir;
|
ctx.ir = ir;
|
||||||
ctx.ty = ty;
|
ctx.ty = ty;
|
||||||
ctx.attr = attr;
|
ctx.attr = attr;
|
||||||
ctx.layout = layout;
|
ctx.layout = layout;
|
||||||
ctx.extern_native = Vec.blank(@sizeof(isize), @alignof(isize));
|
ctx.extern_native = Vec.blank(@sizeof(isize), @alignof(isize));
|
||||||
|
ctx.global_gaddr = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
|
|
||||||
match Mcode.new() {
|
match Mcode.new() {
|
||||||
.Ok(v) => { ctx.prog = v; },
|
.Ok(v) => { ctx.prog = v; },
|
||||||
@@ -1220,12 +1225,13 @@ fn init(ctx: *Ir2Vm, ir: *Ir, ty: *TypeTable, layout: *Layout, attr: *Attr) {
|
|||||||
fn drop(ctx: *Ir2Vm) {
|
fn drop(ctx: *Ir2Vm) {
|
||||||
Mcode.drop(&ctx.prog);
|
Mcode.drop(&ctx.prog);
|
||||||
Vec.drop(&ctx.extern_native);
|
Vec.drop(&ctx.extern_native);
|
||||||
|
Vec.drop(&ctx.global_gaddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_func(ctx: *Ir2Vm, ref: usize): i32 {
|
fn run_func(ctx: *Ir2Vm, ref: usize): i32 {
|
||||||
var all_fixups = Vec.blank(@sizeof(Fix), @alignof(Fix));
|
var all_fixups = Vec.blank(@sizeof(Fix), @alignof(Fix));
|
||||||
defer { Vec.drop(&all_fixups); }
|
defer { Vec.drop(&all_fixups); }
|
||||||
ignore_usize(translate_function(ctx, ref, &all_fixups));
|
translate_function(ctx, ref, &all_fixups);
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1237,7 +1243,7 @@ fn run(ctx: *Ir2Vm): i32 {
|
|||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while i < n {
|
while i < n {
|
||||||
var v: isize = -1;
|
var v: isize = -1;
|
||||||
match Vec.push(&ctx.extern_native, &v as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&ctx.extern_native, &v as *u8);
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
i = 1;
|
i = 1;
|
||||||
@@ -1250,8 +1256,24 @@ fn run(ctx: *Ir2Vm): i32 {
|
|||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 登记全局变量到 prog.gdata(Zero→零填/BSS,Bytes→拷贝),记下运行时下标。
|
||||||
|
var gi: usize = 0;
|
||||||
|
while gi < ctx.ir.globals.len {
|
||||||
|
var g = (ctx.ir.globals.data as *Global) + gi;
|
||||||
|
var gsize = ir_type_size(ctx, g.tid);
|
||||||
|
var didx: usize = 0;
|
||||||
|
if g.zero {
|
||||||
|
didx = Mcode.add_zero_data(&ctx.prog, gsize);
|
||||||
|
} else {
|
||||||
|
didx = Mcode.add_data(&ctx.prog, g.data.data as usize, gsize);
|
||||||
|
}
|
||||||
|
var dv = didx;
|
||||||
|
Vec.push(&ctx.global_gaddr, &dv as *u8);
|
||||||
|
gi = gi + 1;
|
||||||
|
}
|
||||||
|
|
||||||
// 哨兵:保证真实函数入口地址非零。
|
// 哨兵:保证真实函数入口地址非零。
|
||||||
ignore_usize(Mcode.add_instr(&ctx.prog, .Halt, .Void, 0));
|
Mcode.add_instr(&ctx.prog, .Halt, .Void, 0);
|
||||||
|
|
||||||
// 预分配指令容量(按 IR 节点数粗略估计),减少逐条 append 的扩容/拷贝。
|
// 预分配指令容量(按 IR 节点数粗略估计),减少逐条 append 的扩容/拷贝。
|
||||||
var est: usize = 16;
|
var est: usize = 16;
|
||||||
@@ -1261,7 +1283,7 @@ fn run(ctx: *Ir2Vm): i32 {
|
|||||||
if irf != null { est = est + irf.nodes.len * 8 + 32; }
|
if irf != null { est = est + irf.nodes.len * 8 + 32; }
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
match Vec.reserve(&ctx.prog.insns, est) { .Ok => {}, .Err(error) => {} }
|
Vec.reserve(&ctx.prog.insns, est);
|
||||||
|
|
||||||
// Pass 2:翻译非 extern 函数。
|
// Pass 2:翻译非 extern 函数。
|
||||||
var fid_ip = Vec.blank(@sizeof(usize), @alignof(usize));
|
var fid_ip = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
@@ -1327,6 +1349,15 @@ fn dump(ctx: *Ir2Vm, out: *ByteBuffer): bool {
|
|||||||
ret Mcode.dump(&ctx.prog, out);
|
ret Mcode.dump(&ctx.prog, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 序列化到内存 ByteBuffer(不落盘),供内嵌 VM 直接加载。
|
||||||
|
fn encode(ctx: *Ir2Vm, out: *ByteBuffer): bool {
|
||||||
|
match Mcode.encode(&ctx.prog, out) {
|
||||||
|
.Ok => { ret true; },
|
||||||
|
.Err(error) => { ret false; },
|
||||||
|
}
|
||||||
|
ret false;
|
||||||
|
}
|
||||||
|
|
||||||
// 把翻译结果写成 .sir(二进制)。
|
// 把翻译结果写成 .sir(二进制)。
|
||||||
fn store(ctx: *Ir2Vm, path: ByteView): bool {
|
fn store(ctx: *Ir2Vm, path: ByteView): bool {
|
||||||
match Mcode.store(&ctx.prog, path) {
|
match Mcode.store(&ctx.prog, path) {
|
||||||
@@ -1336,12 +1367,8 @@ fn store(ctx: *Ir2Vm, path: ByteView): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 工具 ----
|
|
||||||
|
|
||||||
fn ignore_usize(v: usize) {}
|
|
||||||
fn ignore_bool(b: bool) {}
|
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
var ir = IrM.new();
|
var ir = IrM.new();
|
||||||
|
|||||||
+21
-43
@@ -1,7 +1,6 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// SIR builder (IrBuilder). Split from ir.spl: stage1 ast2ir
|
// IR 构建器:单独成文件,以免 ast2ir 在同文件 @self 嵌套时无限递归。
|
||||||
// infinitely recurses on same-file @self nesting; cross-file is fine.
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -18,8 +17,6 @@ fn list_at(v: Vec, i: usize): usize {
|
|||||||
ret ((v.data as *usize) + i).*;
|
ret ((v.data as *usize) + i).*;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- builder ----
|
|
||||||
|
|
||||||
type FixKind = enum { BrTrue, BrFalse, Jmp }
|
type FixKind = enum { BrTrue, BrFalse, Jmp }
|
||||||
|
|
||||||
type Fix = struct {
|
type Fix = struct {
|
||||||
@@ -46,13 +43,9 @@ fn builder_alloc(self: *IrBuilder, kind: IrKind): usize {
|
|||||||
e.kind = kind;
|
e.kind = kind;
|
||||||
e.dbg = self.dbg;
|
e.dbg = self.dbg;
|
||||||
}
|
}
|
||||||
if self.current_block >= self.blocks.len { ret ref; }
|
if self.current_block < self.blocks.len {
|
||||||
match Vec.get(&self.blocks, self.current_block) {
|
var slot = (self.blocks.data as *usize) + self.current_block;
|
||||||
.Ok(p) => {
|
|
||||||
var slot = p as *usize;
|
|
||||||
if slot.* == 0 { slot.* = ref; }
|
if slot.* == 0 { slot.* = ref; }
|
||||||
},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
}
|
}
|
||||||
@@ -113,26 +106,19 @@ fn fn_begin_at(self: *IrBuilder, fid: usize) {
|
|||||||
fn fn_end(self: *IrBuilder) {
|
fn fn_end(self: *IrBuilder) {
|
||||||
var k: usize = 0;
|
var k: usize = 0;
|
||||||
while k < self.fixups.len {
|
while k < self.fixups.len {
|
||||||
match Vec.get(&self.fixups, k) {
|
var fx = (self.fixups.data as *Fix) + k;
|
||||||
.Ok(fp) => {
|
|
||||||
var fx = fp as *Fix;
|
|
||||||
var e = Ir.node(&self.ir, self.current_fn, fx.node);
|
var e = Ir.node(&self.ir, self.current_fn, fx.node);
|
||||||
if e != null {
|
if e != null {
|
||||||
var target: usize = 0;
|
var target: usize = 0;
|
||||||
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) {
|
match fx.slot {
|
||||||
match e.body { .Br(b) => { b.true_label = target; e.body = .Br(b); }, _ => {} }
|
.BrTrue => { match e.body { .Br(&b) => { b.true_label = target; }, _ => {} } },
|
||||||
} else if @enum2int(fx.slot) == @enum2int(FixKind.BrFalse) {
|
.BrFalse => { match e.body { .Br(&b) => { b.false_label = target; }, _ => {} } },
|
||||||
match e.body { .Br(b) => { b.false_label = target; e.body = .Br(b); }, _ => {} }
|
.Jmp => { match e.body { .Jmp(&j) => { j.label = target; }, _ => {} } },
|
||||||
} else {
|
|
||||||
match e.body { .Jmp(j) => { j.label = target; e.body = .Jmp(j); }, _ => {} }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
k = k + 1;
|
k = k + 1;
|
||||||
}
|
}
|
||||||
self.current_block = 0;
|
self.current_block = 0;
|
||||||
@@ -143,7 +129,7 @@ fn fn_end(self: *IrBuilder) {
|
|||||||
|
|
||||||
fn block_begin(self: *IrBuilder): usize {
|
fn block_begin(self: *IrBuilder): usize {
|
||||||
var zero: usize = 0;
|
var zero: usize = 0;
|
||||||
match Vec.push(&self.blocks, &zero as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&self.blocks, &zero as *u8);
|
||||||
var block = self.blocks.len - 1;
|
var block = self.blocks.len - 1;
|
||||||
self.current_block = block;
|
self.current_block = block;
|
||||||
self.terminated = false;
|
self.terminated = false;
|
||||||
@@ -151,10 +137,9 @@ fn block_begin(self: *IrBuilder): usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block_end(self: *IrBuilder) {
|
fn block_end(self: *IrBuilder) {
|
||||||
// terminated 与空块校验在 stage1 为 Panic;这里保持同样约束但不终止进程。
|
// 空实现:本实现不做 terminated/空块校验(不终止进程)。
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- arith / cmp / cast ----
|
|
||||||
|
|
||||||
fn set_arith(self: *IrBuilder, kind: IrKind, tid: usize, left: usize, right: usize): usize {
|
fn set_arith(self: *IrBuilder, kind: IrKind, tid: usize, left: usize, right: usize): usize {
|
||||||
var ref = builder_alloc(self, kind);
|
var ref = builder_alloc(self, kind);
|
||||||
@@ -211,7 +196,6 @@ fn cast_bool2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret se
|
|||||||
fn case_int2float(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseInt2float, f, t, v); }
|
fn case_int2float(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseInt2float, f, t, v); }
|
||||||
fn case_float2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseFloat2int, f, t, v); }
|
fn case_float2int(self: *IrBuilder, f: usize, t: usize, v: usize): usize { ret set_cast(self, .CaseFloat2int, f, t, v); }
|
||||||
|
|
||||||
// ---- memory ----
|
|
||||||
|
|
||||||
fn mem_alloca(self: *IrBuilder, tid: usize, count: usize): usize {
|
fn mem_alloca(self: *IrBuilder, tid: usize, count: usize): usize {
|
||||||
var ref = builder_alloc(self, .MemAlloca);
|
var ref = builder_alloc(self, .MemAlloca);
|
||||||
@@ -269,7 +253,6 @@ fn mem_set(self: *IrBuilder, tid: usize, dst: usize, val: usize, size: usize): u
|
|||||||
ret ref;
|
ret ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- type const ----
|
|
||||||
|
|
||||||
fn const_node(self: *IrBuilder, tid: usize, lit: ConstLit): usize {
|
fn const_node(self: *IrBuilder, tid: usize, lit: ConstLit): usize {
|
||||||
var ref = builder_alloc(self, .TypeConst);
|
var ref = builder_alloc(self, .TypeConst);
|
||||||
@@ -285,8 +268,13 @@ fn type_const_float(self: *IrBuilder, tid: usize, val: f64): usize { ret const_n
|
|||||||
fn type_const_char(self: *IrBuilder, tid: usize, val: u8): usize { ret const_node(self, tid, .Char(val)); }
|
fn type_const_char(self: *IrBuilder, tid: usize, val: u8): usize { ret const_node(self, tid, .Char(val)); }
|
||||||
fn type_const_fn(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Fn(val)); }
|
fn type_const_fn(self: *IrBuilder, tid: usize, val: usize): usize { ret const_node(self, tid, .Fn(val)); }
|
||||||
fn type_const_bytes(self: *IrBuilder, tid: usize, data: usize): usize { ret const_node(self, tid, .Ptr(data)); }
|
fn type_const_bytes(self: *IrBuilder, tid: usize, data: usize): usize { ret const_node(self, tid, .Ptr(data)); }
|
||||||
|
fn type_const_global(self: *IrBuilder, tid: usize, gidx: usize): usize { ret const_node(self, tid, .Global(gidx)); }
|
||||||
|
|
||||||
|
// 追加模块级全局变量;返回 gidx。bytes 所有权转移进 Ir。
|
||||||
|
fn add_global(self: *IrBuilder, tid: usize, zero: bool, bytes: Vec): usize {
|
||||||
|
ret Ir.add_global(&self.ir, tid, zero, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
// ---- type ops ----
|
|
||||||
|
|
||||||
fn type_bitsizeof(self: *IrBuilder, tid: usize): usize {
|
fn type_bitsizeof(self: *IrBuilder, tid: usize): usize {
|
||||||
var ref = builder_alloc(self, .TypeBitsizeof);
|
var ref = builder_alloc(self, .TypeBitsizeof);
|
||||||
@@ -323,7 +311,6 @@ fn type_field_count(self: *IrBuilder, tid: usize): usize {
|
|||||||
ret ref;
|
ret ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- agg ----
|
|
||||||
|
|
||||||
fn agg_construct(self: *IrBuilder, tid: usize): usize {
|
fn agg_construct(self: *IrBuilder, tid: usize): usize {
|
||||||
var ref = builder_alloc(self, .AggConstruct);
|
var ref = builder_alloc(self, .AggConstruct);
|
||||||
@@ -337,12 +324,8 @@ fn agg_construct(self: *IrBuilder, tid: usize): usize {
|
|||||||
fn agg_construct_field(self: *IrBuilder, node_ref: usize, field: usize) {
|
fn agg_construct_field(self: *IrBuilder, node_ref: usize, field: usize) {
|
||||||
var e = Ir.node(&self.ir, self.current_fn, node_ref);
|
var e = Ir.node(&self.ir, self.current_fn, node_ref);
|
||||||
if e == null { ret; }
|
if e == null { ret; }
|
||||||
// 载荷按值绑定:改副本后写回。
|
|
||||||
match e.body {
|
match e.body {
|
||||||
.AggConstruct(a) => {
|
.AggConstruct(&a) => { Vec.push(&a.fields, &field as *u8); },
|
||||||
match Vec.push(&a.fields, &field as *u8) { .Ok => {}, .Err(error) => {} }
|
|
||||||
e.body = .AggConstruct(a);
|
|
||||||
},
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,7 +346,6 @@ fn agg_insert(self: *IrBuilder, tid: usize, field_idx: usize, agg: usize, field:
|
|||||||
ret ref;
|
ret ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- control ----
|
|
||||||
|
|
||||||
fn control_select(self: *IrBuilder, tid: usize, cond: usize, tv: usize, fv: usize): usize {
|
fn control_select(self: *IrBuilder, tid: usize, cond: usize, tv: usize, fv: usize): usize {
|
||||||
var ref = builder_alloc(self, .ControlSelect);
|
var ref = builder_alloc(self, .ControlSelect);
|
||||||
@@ -382,9 +364,9 @@ fn control_br(self: *IrBuilder, cond: usize): usize {
|
|||||||
|
|
||||||
fn control_br_fixup(self: *IrBuilder, ref: usize, true_block: usize, false_block: usize) {
|
fn control_br_fixup(self: *IrBuilder, ref: usize, true_block: usize, false_block: usize) {
|
||||||
var f1 = Fix { .node = ref, .slot = .BrTrue, .block = true_block };
|
var f1 = Fix { .node = ref, .slot = .BrTrue, .block = true_block };
|
||||||
match Vec.push(&self.fixups, &f1 as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&self.fixups, &f1 as *u8);
|
||||||
var f2 = Fix { .node = ref, .slot = .BrFalse, .block = false_block };
|
var f2 = Fix { .node = ref, .slot = .BrFalse, .block = false_block };
|
||||||
match Vec.push(&self.fixups, &f2 as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&self.fixups, &f2 as *u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn control_jmp(self: *IrBuilder): usize {
|
fn control_jmp(self: *IrBuilder): usize {
|
||||||
@@ -397,7 +379,7 @@ fn control_jmp(self: *IrBuilder): usize {
|
|||||||
|
|
||||||
fn control_jmp_fixup(self: *IrBuilder, ref: usize, target_block: usize) {
|
fn control_jmp_fixup(self: *IrBuilder, ref: usize, target_block: usize) {
|
||||||
var fx = Fix { .node = ref, .slot = .Jmp, .block = target_block };
|
var fx = Fix { .node = ref, .slot = .Jmp, .block = target_block };
|
||||||
match Vec.push(&self.fixups, &fx as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&self.fixups, &fx as *u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn control_call(self: *IrBuilder, tid: usize, fn_ref: usize): usize {
|
fn control_call(self: *IrBuilder, tid: usize, fn_ref: usize): usize {
|
||||||
@@ -413,12 +395,8 @@ fn control_call(self: *IrBuilder, tid: usize, fn_ref: usize): usize {
|
|||||||
fn control_call_param(self: *IrBuilder, call_node: usize, arg: usize) {
|
fn control_call_param(self: *IrBuilder, call_node: usize, arg: usize) {
|
||||||
var e = Ir.node(&self.ir, self.current_fn, call_node);
|
var e = Ir.node(&self.ir, self.current_fn, call_node);
|
||||||
if e == null { ret; }
|
if e == null { ret; }
|
||||||
// 载荷按值绑定:改副本后写回。
|
|
||||||
match e.body {
|
match e.body {
|
||||||
.Call(c) => {
|
.Call(&c) => { Vec.push(&c.params, &arg as *u8); },
|
||||||
match Vec.push(&c.params, &arg as *u8) { .Ok => {}, .Err(error) => {} }
|
|
||||||
e.body = .Call(c);
|
|
||||||
},
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-37
@@ -1,11 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 语言类型表(移植自 stage1/spl_lang_type.c):在 IR 类型表(type_table.spl)
|
// 语言类型表:在 IR 类型表之上登记「语言层类型」(IR 类型 id + 种类 + 聚合成员),
|
||||||
// 之上,登记「语言层类型」= IR 类型 id + 语言种类 + 聚合方法表,同样 hash-consing。
|
// 按结构 hash-consing。sema 的 resolved_lang_tid / def.lang_id 都指向这里。
|
||||||
// 另持有声明句柄表(def.spl)与语言类型子类型/求交/字面量适配。
|
|
||||||
//
|
|
||||||
// 语言类型是 sema 的主类型:AST 节点 resolved_lang_tid、def.lang_id 都指这里。
|
|
||||||
// 名称/方法名是 ByteView(借用);聚合方法表与 tuple/fn 的载荷 Vec 由本表拥有。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -77,7 +73,6 @@ type LangTypeResult = enum {
|
|||||||
Err: Error,
|
Err: Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 小工具 ----
|
|
||||||
|
|
||||||
fn seed(): usize {
|
fn seed(): usize {
|
||||||
ret 14695981039346656037;
|
ret 14695981039346656037;
|
||||||
@@ -142,7 +137,6 @@ fn elem_of(entry: LangEntry): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 生命周期 ----
|
|
||||||
|
|
||||||
fn new_entry(kind: LangKind, ir_type_id: usize): LangEntry {
|
fn new_entry(kind: LangKind, ir_type_id: usize): LangEntry {
|
||||||
ret LangEntry {
|
ret LangEntry {
|
||||||
@@ -215,7 +209,7 @@ fn drop(self: *LangType) {
|
|||||||
TypeTable.drop(&self.ir);
|
TypeTable.drop(&self.ir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// id 0 与越界返回 null(对应 stage1 spl_lang_type_node)。
|
// id 0 与越界返回 null。
|
||||||
fn lang_node(self: *LangType, id: usize): *LangEntry {
|
fn lang_node(self: *LangType, id: usize): *LangEntry {
|
||||||
if id == 0 || id >= self.lang_table.len { ret null; }
|
if id == 0 || id >= self.lang_table.len { ret null; }
|
||||||
ret (self.lang_table.data as *LangEntry) + id;
|
ret (self.lang_table.data as *LangEntry) + id;
|
||||||
@@ -231,7 +225,6 @@ fn get_ir_type(self: *LangType, lang_id: usize): usize {
|
|||||||
ret ir_type(self, lang_id);
|
ret ir_type(self, lang_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- IR 类型槽位预留(自引用聚合)----
|
|
||||||
|
|
||||||
fn reserve_ir(self: *LangType): usize {
|
fn reserve_ir(self: *LangType): usize {
|
||||||
ret TypeTable.reserve(&self.ir);
|
ret TypeTable.reserve(&self.ir);
|
||||||
@@ -241,7 +234,6 @@ fn define_ir(self: *LangType, id: usize, src: *TypeNode) {
|
|||||||
TypeTable.define(&self.ir, id, src);
|
TypeTable.define(&self.ir, id, src);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- def 便捷封装(sema 侧只用 LangType)----
|
|
||||||
|
|
||||||
fn alloc_def(self: *LangType, kind: DefKind, name: ByteView, span: Span, owner: *any,
|
fn alloc_def(self: *LangType, kind: DefKind, name: ByteView, span: Span, owner: *any,
|
||||||
ast_node_ref: usize): usize {
|
ast_node_ref: usize): usize {
|
||||||
@@ -266,7 +258,6 @@ fn def_is_type(self: *LangType, id: usize): bool {
|
|||||||
ret DefTable.is_type_kind(d.kind);
|
ret DefTable.is_type_kind(d.kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- intern ----
|
|
||||||
|
|
||||||
fn free_extra(e: *LangEntry) {
|
fn free_extra(e: *LangEntry) {
|
||||||
match e.extra {
|
match e.extra {
|
||||||
@@ -423,10 +414,7 @@ fn fold_insert(self: *LangType, hash: usize, id: usize) {
|
|||||||
var e = lang_node(self, id);
|
var e = lang_node(self, id);
|
||||||
if e == null { ret; }
|
if e == null { ret; }
|
||||||
e.hash_next = old;
|
e.hash_next = old;
|
||||||
match Map.put(&self.fold, &hash as *u8, &id as *u8) {
|
Map.put(&self.fold, &hash as *u8, &id as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// intern 一个 probe:命中释放载荷并返回已有 id;否则复制入表。probe 返回后失效。
|
// intern 一个 probe:命中释放载荷并返回已有 id;否则复制入表。probe 返回后失效。
|
||||||
@@ -460,7 +448,6 @@ fn push_simple(self: *LangType, kind: LangKind, ir: usize): usize {
|
|||||||
ret push(self, &e);
|
ret push(self, &e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- builders ----
|
|
||||||
|
|
||||||
fn make_error(self: *LangType): usize {
|
fn make_error(self: *LangType): usize {
|
||||||
ret push_simple(self, .Error, TypeTable.make_error(&self.ir));
|
ret push_simple(self, .Error, TypeTable.make_error(&self.ir));
|
||||||
@@ -560,13 +547,10 @@ fn make_range(self: *LangType, elem_lang: usize): usize {
|
|||||||
ret push(self, &e);
|
ret push(self, &e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为 tuple 字段生成稳定的十进制名字("0"/"1"/...),镜像 stage1。
|
// tuple 字段用稳定的十进制名("0"/"1"/...)。
|
||||||
fn intern_index_name(self: *LangType, value: usize): ByteView {
|
fn intern_index_name(self: *LangType, value: usize): ByteView {
|
||||||
var b = ByteBuffer.new();
|
var b = ByteBuffer.new();
|
||||||
match append_usize(&b, value, 10, false) {
|
append_usize(&b, value, 10, false);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
var view = ByteView.from_slice(ByteBuffer.as_slice(b));
|
var view = ByteView.from_slice(ByteBuffer.as_slice(b));
|
||||||
match Vec.push(&self.owned_names, &b as *u8) {
|
match Vec.push(&self.owned_names, &b as *u8) {
|
||||||
.Ok => {},
|
.Ok => {},
|
||||||
@@ -646,7 +630,7 @@ fn parse_bits(rest: []u8): usize {
|
|||||||
ret value;
|
ret value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 内置类型名:bool/void/isize/usize/any/iN/uN/fN(对应 stage1 spl_type_lookup_builtin)。
|
// 内置类型名:bool/void/isize/usize/any/iN/uN/fN。
|
||||||
fn lookup_builtin(self: *LangType, name: ByteView): usize {
|
fn lookup_builtin(self: *LangType, name: ByteView): usize {
|
||||||
if name.len == 0 { ret 0; }
|
if name.len == 0 { ret 0; }
|
||||||
if ByteView.eq(name, ByteView.from_slice("bool")) { ret make_bool(self); }
|
if ByteView.eq(name, ByteView.from_slice("bool")) { ret make_bool(self); }
|
||||||
@@ -675,7 +659,6 @@ fn lookup_builtin(self: *LangType, name: ByteView): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- IR 位宽读取 ----
|
|
||||||
|
|
||||||
fn int_bits(self: *LangType, ir_id: usize): usize {
|
fn int_bits(self: *LangType, ir_id: usize): usize {
|
||||||
var te = TypeTable.node(&self.ir, ir_id);
|
var te = TypeTable.node(&self.ir, ir_id);
|
||||||
@@ -707,7 +690,6 @@ fn float_bits(self: *LangType, ir_id: usize): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 语言层子类型 / 求交 / 字面量适配 ----
|
|
||||||
|
|
||||||
fn is_subtype(self: *LangType, sub: usize, super: usize): bool {
|
fn is_subtype(self: *LangType, sub: usize, super: usize): bool {
|
||||||
if sub == 0 || super == 0 { ret false; }
|
if sub == 0 || super == 0 { ret false; }
|
||||||
@@ -838,7 +820,7 @@ fn int_fits(self: *LangType, target: usize, value: BigInt): bool {
|
|||||||
.Err(error) => { ret false; },
|
.Err(error) => { ret false; },
|
||||||
}
|
}
|
||||||
|
|
||||||
// 镜像 stage1:目标位宽 >= 64 时不做边界检查(其 mpa 只存低 64 位)。
|
// 目标位宽 >= 64 时不做边界检查(mpa 只存低 64 位)。
|
||||||
if bits >= 64 { ret true; }
|
if bits >= 64 { ret true; }
|
||||||
|
|
||||||
var u: u64 = 0;
|
var u: u64 = 0;
|
||||||
@@ -860,7 +842,6 @@ fn int_fits(self: *LangType, target: usize, value: BigInt): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump(镜像 stage1 spl_lang_type_dump)----
|
|
||||||
|
|
||||||
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
match ByteBuffer.append(buf, bytes) {
|
match ByteBuffer.append(buf, bytes) {
|
||||||
@@ -898,10 +879,7 @@ fn dump_rec(self: *LangType, id: usize, out: *ByteBuffer, stack: *Vec): bool {
|
|||||||
if e == null {
|
if e == null {
|
||||||
if !push_out(out, "none") { ret false; }
|
if !push_out(out, "none") { ret false; }
|
||||||
var dropped: usize = 0;
|
var dropped: usize = 0;
|
||||||
match Vec.pop(stack, &dropped as *u8) {
|
Vec.pop(stack, &dropped as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
ret true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -941,7 +919,7 @@ fn dump_rec(self: *LangType, id: usize, out: *ByteBuffer, stack: *Vec): bool {
|
|||||||
if !push_out(out, "[") { ok = false; }
|
if !push_out(out, "[") { ok = false; }
|
||||||
if ok && !push_usize(out, len) { ok = false; }
|
if ok && !push_usize(out, len) { ok = false; }
|
||||||
if ok && !push_out(out, "]") { ok = false; }
|
if ok && !push_out(out, "]") { ok = false; }
|
||||||
// 镜像 stage1:数组直接 dump IR 类型(会重复一次 [N])。
|
// 数组直接 dump IR 类型(会重复一次 [N])。
|
||||||
if ok { ok = TypeTable.pure_dump(&self.ir, e.ir_type_id, out); }
|
if ok { ok = TypeTable.pure_dump(&self.ir, e.ir_type_id, out); }
|
||||||
},
|
},
|
||||||
.Slice => {
|
.Slice => {
|
||||||
@@ -995,10 +973,7 @@ fn dump_rec(self: *LangType, id: usize, out: *ByteBuffer, stack: *Vec): bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dropped2: usize = 0;
|
var dropped2: usize = 0;
|
||||||
match Vec.pop(stack, &dropped2 as *u8) {
|
Vec.pop(stack, &dropped2 as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
ret ok;
|
ret ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1008,7 +983,6 @@ fn dump(self: *LangType, id: usize, out: *ByteBuffer): bool {
|
|||||||
ret dump_rec(self, id, out, &stack);
|
ret dump_rec(self, id, out, &stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn expect_text(buf: ByteBuffer, expected: []u8) {
|
fn expect_text(buf: ByteBuffer, expected: []u8) {
|
||||||
var got = ByteBuffer.as_slice(buf);
|
var got = ByteBuffer.as_slice(buf);
|
||||||
|
|||||||
+4
-9
@@ -1,9 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 目标布局(移植自 stage1/spl_layout.c)。输入是 IR 类型表 TypeTable;
|
// 目标布局:由 IR 类型表计算 size/align/字段位偏移,供 ir2vm 使用。默认 64 位平台,
|
||||||
// 为每个类型计算 size_bits / align_bits / 字段位偏移,供 ir2vm 使用。
|
// 聚合按最大字段对齐且不重排。
|
||||||
// 默认 64 位平台:标量按 1/2/4/8 字节对齐,指针 = usize,聚合按最大字段对齐
|
|
||||||
// 且不允许重排(与 stage1 的默认策略一致)。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -47,7 +45,7 @@ fn info_at(self: *Layout, id: usize): *LayoutInfo {
|
|||||||
ret (self.infos.data as *LayoutInfo) + id;
|
ret (self.infos.data as *LayoutInfo) + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 未解析或越界返回 null(对应 stage1 spl_layout_info_of)。
|
// 未解析或越界返回 null。
|
||||||
fn info_of(self: *Layout, id: usize): *LayoutInfo {
|
fn info_of(self: *Layout, id: usize): *LayoutInfo {
|
||||||
var info = info_at(self, id);
|
var info = info_at(self, id);
|
||||||
if info == null || !info.resolved { ret null; }
|
if info == null || !info.resolved { ret null; }
|
||||||
@@ -84,10 +82,7 @@ fn ensure(self: *Layout, n: usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn push_usize(v: *Vec, x: usize) {
|
fn push_usize(v: *Vec, x: usize) {
|
||||||
match Vec.push(v, &x as *u8) {
|
Vec.push(v, &x as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usize_at(v: *Vec, i: usize): usize {
|
fn usize_at(v: *Vec, i: usize): usize {
|
||||||
|
|||||||
+2
-7
@@ -1,9 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 词法分析器:字节流 -> Vec<Token>。镜像 stage1/spl_lexer.c 的行为:
|
// 字节流 -> Vec<Token>。行为镜像 splc0(保证 diff):行注释作为 token 产出,块注释
|
||||||
// 行注释作为 token 产出;块注释(可嵌套)整体跳过;换行产出 Endline;EOF 产出 Eof。
|
//(可嵌套)跳过,换行产 Endline,EOF 产 Eof。Token.text 借用输入,不复制。
|
||||||
// 复用 utils/ascii 做字符分类;整型/字符在词法期解码后放进 TokenKind 载荷。
|
|
||||||
// Token.text 借用输入字节,不复制。
|
|
||||||
|
|
||||||
type .{ .Vec, .VecResult } = @import("utils/vec.spl");
|
type .{ .Vec, .VecResult } = @import("utils/vec.spl");
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
@@ -80,7 +78,6 @@ fn decode_char(text: ByteView): u8 {
|
|||||||
ret e;
|
ret e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 读取辅助 ----
|
|
||||||
|
|
||||||
fn has2(self: *Lexer, ch: u8): bool {
|
fn has2(self: *Lexer, ch: u8): bool {
|
||||||
ret self.pos + 1 < self.src.len && self.src.ptr[self.pos + 1] == ch;
|
ret self.pos + 1 < self.src.len && self.src.ptr[self.pos + 1] == ch;
|
||||||
@@ -120,7 +117,6 @@ fn op3(self: *Lexer, out: *Vec, punct: Punct, start: usize, line: usize, col: us
|
|||||||
emit_to(self, out, .Punct(punct), start, start + 3, line, col);
|
emit_to(self, out, .Punct(punct), start, start + 3, line, col);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 复合扫描 ----
|
|
||||||
|
|
||||||
fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) {
|
fn scan_int(self: *Lexer, out: *Vec, start: usize, line: usize, col: usize) {
|
||||||
var p = self.pos;
|
var p = self.pos;
|
||||||
@@ -453,7 +449,6 @@ fn lex(src: ByteView, diag: *Diag, file: usize): VecResult {
|
|||||||
ret .Ok(out);
|
ret .Ok(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
+105
-85
@@ -1,28 +1,15 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// SPL VM 机器码容器(移植自 stage0/spl_mcode.c 的构建/序列化/dump 部分)。
|
// VM 字节码容器:内存中的 Prog 与 .sir 序列化(反序列化在 C 侧 spl_cli)。
|
||||||
// 只负责「内存中的 prog」与「写入 .sir」,不包含反序列化(由 C 侧 spl_cli 读)。
|
// 字符串用 ByteView 借用、不拥有;gdata 条目为 ByteBuffer,由本模块拥有释放。
|
||||||
//
|
|
||||||
// 字符串一律是 ByteView(借用上层 IR/attr 的名称,本模块不复制、不释放);
|
|
||||||
// gdata 条目是 ByteBuffer,由本模块拥有并释放。
|
|
||||||
//
|
|
||||||
// 二进制格式(元数据字段均为 u64 小端):
|
|
||||||
// [HEADER] magic(8) nfuncs(8) ninsns(8) nnatives(8) nstrs(8) ndata(8)
|
|
||||||
// [FUNCS] 每项: name_len(8) name(pad8, 含结尾 NUL) idx_of_strtab(8)
|
|
||||||
// nargs(8) ninsns(8) address(8)
|
|
||||||
// [INSTRS] 每项: opcode(2) type(2) imm(8) = 12 字节
|
|
||||||
// [NATIVES] 每项: name_len(8) name(pad8, 含结尾 NUL) idx_of_strtab(8)
|
|
||||||
// [STRTAB] 每项: slen(8) str(pad8)
|
|
||||||
// [GDATA] 每项: dsize(8) data(pad8)
|
|
||||||
|
|
||||||
type .{ .Error, .Status, .make } = @import("utils/error.spl");
|
type .{ .Error, .Status, .make } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
type .{ .ByteBuffer } = @import("utils/byte_buffer.spl");
|
||||||
type .{ .append_usize, .append_i64 } = @import("utils/fmt.spl");
|
type .{ .append_usize, .append_i64 } = @import("utils/fmt.spl");
|
||||||
type Mem = @import("sys/mem.spl");
|
type Mem = @import("utils/memory.spl");
|
||||||
type File = @import("sys/file.spl");
|
type .{ .open_cstr, .write_all, .read_all, .File } = @import("utils/io.spl");
|
||||||
type .{ .open_cstr, .write_all, .read_all } = @import("utils/io.spl");
|
|
||||||
|
|
||||||
type Op = enum {
|
type Op = enum {
|
||||||
Error,
|
Error,
|
||||||
@@ -72,6 +59,7 @@ type Op = enum {
|
|||||||
Gaddr,
|
Gaddr,
|
||||||
Load,
|
Load,
|
||||||
Store,
|
Store,
|
||||||
|
Memcpy,
|
||||||
Ncall,
|
Ncall,
|
||||||
Nlib,
|
Nlib,
|
||||||
Bk,
|
Bk,
|
||||||
@@ -177,6 +165,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"; },
|
||||||
@@ -206,7 +195,6 @@ fn kind_name(k: VmKind): []u8 {
|
|||||||
ret "???";
|
ret "???";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 生命周期 ----
|
|
||||||
|
|
||||||
fn new(): ProgResult {
|
fn new(): ProgResult {
|
||||||
var prog = Prog {
|
var prog = Prog {
|
||||||
@@ -262,7 +250,6 @@ fn drop(self: *Prog) {
|
|||||||
Vec.drop(&self.insns);
|
Vec.drop(&self.insns);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 构建 ----
|
|
||||||
|
|
||||||
fn add_instr(self: *Prog, op: Op, ty: VmKind, imm: usize): usize {
|
fn add_instr(self: *Prog, op: Op, ty: VmKind, imm: usize): usize {
|
||||||
var i = self.insns.len;
|
var i = self.insns.len;
|
||||||
@@ -304,7 +291,7 @@ fn add_data(self: *Prog, ptr: usize, size: usize): usize {
|
|||||||
}
|
}
|
||||||
if size != 0 {
|
if size != 0 {
|
||||||
if ptr == 0 { ByteBuffer.drop(&entry); ret 0; }
|
if ptr == 0 { ByteBuffer.drop(&entry); ret 0; }
|
||||||
ignore_ptr(Mem.copy(entry.storage.data as *any, ptr as *any, size));
|
Mem.copy(entry.storage.data as *any, ptr as *any, size);
|
||||||
entry.storage.len = size;
|
entry.storage.len = size;
|
||||||
}
|
}
|
||||||
match Vec.push(&self.gdata, &entry as *u8) {
|
match Vec.push(&self.gdata, &entry as *u8) {
|
||||||
@@ -314,6 +301,26 @@ fn add_data(self: *Prog, ptr: usize, size: usize): usize {
|
|||||||
ret self.gdata.len - 1;
|
ret self.gdata.len - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 追加一条 size 字节的零初始化全局数据(BSS 语义);返回其索引。
|
||||||
|
fn add_zero_data(self: *Prog, size: usize): usize {
|
||||||
|
var entry = ByteBuffer.new();
|
||||||
|
match ByteBuffer.reserve(&entry, size) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||||
|
}
|
||||||
|
if size != 0 {
|
||||||
|
entry.storage.len = size;
|
||||||
|
var p = entry.storage.data as *u8;
|
||||||
|
var i: usize = 0;
|
||||||
|
while i < size { p[i] = 0; i = i + 1; }
|
||||||
|
}
|
||||||
|
match Vec.push(&self.gdata, &entry as *u8) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ByteBuffer.drop(&entry); ret 0; },
|
||||||
|
}
|
||||||
|
ret self.gdata.len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
// 返回 name 对应的 native 索引;不存在则登记。name 为空指针时返回 -1。
|
// 返回 name 对应的 native 索引;不存在则登记。name 为空指针时返回 -1。
|
||||||
fn get_or_add_native(self: *Prog, name: ByteView): isize {
|
fn get_or_add_native(self: *Prog, name: ByteView): isize {
|
||||||
if name.ptr == null { ret -1; }
|
if name.ptr == null { ret -1; }
|
||||||
@@ -331,7 +338,6 @@ fn get_or_add_native(self: *Prog, name: ByteView): isize {
|
|||||||
ret (self.natives.len - 1) as isize;
|
ret (self.natives.len - 1) as isize;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump(输出必须与 stage0 spl_prog_dump 逐字节一致)----
|
|
||||||
|
|
||||||
fn out_s(out: *ByteBuffer, s: []u8): bool {
|
fn out_s(out: *ByteBuffer, s: []u8): bool {
|
||||||
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
@@ -448,21 +454,23 @@ fn dump(self: *Prog, out: *ByteBuffer): bool {
|
|||||||
if !out_s(out, " ") { ret false; }
|
if !out_s(out, " ") { ret false; }
|
||||||
if !out_i(out, ins.imm as i64) { ret false; }
|
if !out_i(out, ins.imm as i64) { ret false; }
|
||||||
|
|
||||||
if @enum2int(ins.opcode) == @enum2int(Op.Jmp) ||
|
match ins.opcode {
|
||||||
@enum2int(ins.opcode) == @enum2int(Op.Bz) ||
|
.Jmp, .Bz, .Bnz => {
|
||||||
@enum2int(ins.opcode) == @enum2int(Op.Bnz) {
|
|
||||||
if !out_s(out, " ; -> ") { ret false; }
|
if !out_s(out, " ; -> ") { ret false; }
|
||||||
if !out_u(out, i + 1 + ins.imm) { ret false; }
|
if !out_u(out, i + 1 + ins.imm) { ret false; }
|
||||||
} else if @enum2int(ins.opcode) == @enum2int(Op.Call) {
|
},
|
||||||
|
.Call => {
|
||||||
if !out_s(out, " ; nargs=") { ret false; }
|
if !out_s(out, " ; nargs=") { ret false; }
|
||||||
if !out_u(out, ins.imm) { ret false; }
|
if !out_u(out, ins.imm) { ret false; }
|
||||||
if !out_s(out, ", from stack") { ret false; }
|
if !out_s(out, ", from stack") { ret false; }
|
||||||
} else if @enum2int(ins.opcode) == @enum2int(Op.Calli) {
|
},
|
||||||
if !out_s(out, " ; indirect call") { ret false; }
|
.Calli => { if !out_s(out, " ; indirect call") { ret false; } },
|
||||||
} else if @enum2int(ins.opcode) == @enum2int(Op.Gaddr) {
|
.Gaddr => {
|
||||||
if !out_s(out, " ; gdata[") { ret false; }
|
if !out_s(out, " ; gdata[") { ret false; }
|
||||||
if !out_u(out, ins.imm) { ret false; }
|
if !out_u(out, ins.imm) { ret false; }
|
||||||
if !out_s(out, "]") { ret false; }
|
if !out_s(out, "]") { ret false; }
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
if !out_s(out, "\n") { ret false; }
|
if !out_s(out, "\n") { ret false; }
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
@@ -491,14 +499,13 @@ fn str_at(self: *Prog, i: usize): ByteView {
|
|||||||
ret ByteView.empty();
|
ret ByteView.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 序列化 ----
|
|
||||||
|
|
||||||
fn align8(n: usize): usize {
|
fn align8(n: usize): usize {
|
||||||
ret (n + 7) & (~(7 as usize));
|
ret (n + 7) & (~(7 as usize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 说明:以下 put_* 直接写入已预留容量的 ByteBuffer 存储(store 已按精确总大小 reserve),
|
// put_* 直接写入已预留容量的 ByteBuffer 存储(store 已按精确总大小 reserve),跳过
|
||||||
// 跳过 ByteBuffer.append → Vec.append 的校验/拷贝路径。若容量不足会越界,故 store 先校验容量。
|
// append 的校验/拷贝;容量不足会越界,故 store 先校验容量。
|
||||||
|
|
||||||
fn put_u8(out: *ByteBuffer, b: u8): bool {
|
fn put_u8(out: *ByteBuffer, b: u8): bool {
|
||||||
out.storage.data[out.storage.len] = b;
|
out.storage.data[out.storage.len] = b;
|
||||||
@@ -523,7 +530,7 @@ fn put_u64(out: *ByteBuffer, v: usize): bool {
|
|||||||
|
|
||||||
fn put_zeros(out: *ByteBuffer, n: usize): bool {
|
fn put_zeros(out: *ByteBuffer, n: usize): bool {
|
||||||
if n != 0 {
|
if n != 0 {
|
||||||
ignore_ptr(Mem.set((out.storage.data + out.storage.len) as *any, 0, n));
|
Mem.set((out.storage.data + out.storage.len) as *any, 0, n);
|
||||||
out.storage.len = out.storage.len + n;
|
out.storage.len = out.storage.len + n;
|
||||||
}
|
}
|
||||||
ret true;
|
ret true;
|
||||||
@@ -545,47 +552,46 @@ fn put_name_field(out: *ByteBuffer, name: ByteView): bool {
|
|||||||
ret put_zeros(out, align8(nlen) - nlen);
|
ret put_zeros(out, align8(nlen) - nlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store(self: *Prog, path: ByteView): Status {
|
// 把 prog 序列化为二进制写入 out(不落盘,供内嵌 VM 直接用)。
|
||||||
var out = ByteBuffer.new();
|
fn encode(self: *Prog, out: *ByteBuffer): Status {
|
||||||
defer { ByteBuffer.drop(&out); }
|
|
||||||
|
|
||||||
// 精确预估输出大小并一次性预留,避免大文件逐字节增长导致的重复拷贝。
|
// 精确预估输出大小并一次性预留,避免大文件逐字节增长导致的重复拷贝。
|
||||||
var total: usize = 48;
|
var total: usize = 48;
|
||||||
var fi: usize = 0;
|
var fi: usize = 0;
|
||||||
|
var func_base = self.funcs.data as *VmFunc;
|
||||||
|
var nat_base = self.natives.data as *VmNative;
|
||||||
|
var str_base = self.strtab.data as *ByteView;
|
||||||
|
var gd_base = self.gdata.data as *ByteBuffer;
|
||||||
while fi < self.funcs.len {
|
while fi < self.funcs.len {
|
||||||
var f = func_at(self, fi);
|
var f = func_base + fi;
|
||||||
if f != null {
|
|
||||||
var nlen = f.name.len + 1;
|
var nlen = f.name.len + 1;
|
||||||
total = total + 8 + align8(nlen) + 32;
|
total = total + 8 + align8(nlen) + 32;
|
||||||
}
|
|
||||||
fi = fi + 1;
|
fi = fi + 1;
|
||||||
}
|
}
|
||||||
total = total + self.insns.len * 12;
|
total = total + self.insns.len * 12;
|
||||||
fi = 0;
|
fi = 0;
|
||||||
while fi < self.natives.len {
|
while fi < self.natives.len {
|
||||||
var n = native_at(self, fi);
|
var n = nat_base + fi;
|
||||||
if n != null {
|
|
||||||
var nlen = n.name.len + 1;
|
var nlen = n.name.len + 1;
|
||||||
total = total + 8 + align8(nlen) + 8;
|
total = total + 8 + align8(nlen) + 8;
|
||||||
}
|
|
||||||
fi = fi + 1;
|
fi = fi + 1;
|
||||||
}
|
}
|
||||||
fi = 0;
|
fi = 0;
|
||||||
while fi < self.strtab.len {
|
while fi < self.strtab.len {
|
||||||
total = total + 8 + align8(str_at(self, fi).len);
|
var s = str_base + fi;
|
||||||
|
total = total + 8 + align8(s.len);
|
||||||
fi = fi + 1;
|
fi = fi + 1;
|
||||||
}
|
}
|
||||||
fi = 1;
|
fi = 1;
|
||||||
while fi < self.gdata.len {
|
while fi < self.gdata.len {
|
||||||
var e = gdata_at(self, fi);
|
var e = gd_base + fi;
|
||||||
if e != null { total = total + 8 + align8(e.storage.len); }
|
total = total + 8 + align8(e.storage.len);
|
||||||
fi = fi + 1;
|
fi = fi + 1;
|
||||||
}
|
}
|
||||||
match ByteBuffer.reserve(&out, total) { .Ok => {}, .Err(error) => {} }
|
ByteBuffer.reserve(out, total);
|
||||||
if out.storage.cap < total || out.storage.data == null { ret .Err(make(.Io)); }
|
if out.storage.cap < total || out.storage.data == null { ret .Err(make(.Io)); }
|
||||||
|
|
||||||
if !put_view(&out, ByteView.from_slice("SPLBIN")) { ret .Err(make(.Io)); }
|
if !put_view(out, ByteView.from_slice("SPLBIN")) { ret .Err(make(.Io)); }
|
||||||
if !put_zeros(&out, 2) { ret .Err(make(.Io)); }
|
if !put_zeros(out, 2) { ret .Err(make(.Io)); }
|
||||||
|
|
||||||
var nfuncs = self.funcs.len;
|
var nfuncs = self.funcs.len;
|
||||||
var ninsns = self.insns.len;
|
var ninsns = self.insns.len;
|
||||||
@@ -594,63 +600,75 @@ fn store(self: *Prog, path: ByteView): Status {
|
|||||||
var ndata: usize = 0;
|
var ndata: usize = 0;
|
||||||
if self.gdata.len > 0 { ndata = self.gdata.len - 1; }
|
if self.gdata.len > 0 { ndata = self.gdata.len - 1; }
|
||||||
|
|
||||||
if !put_u64(&out, nfuncs) { ret .Err(make(.Io)); }
|
if !put_u64(out, nfuncs) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, ninsns) { ret .Err(make(.Io)); }
|
if !put_u64(out, ninsns) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, nnatives) { ret .Err(make(.Io)); }
|
if !put_u64(out, nnatives) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, nstrs) { ret .Err(make(.Io)); }
|
if !put_u64(out, nstrs) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, ndata) { ret .Err(make(.Io)); }
|
if !put_u64(out, ndata) { ret .Err(make(.Io)); }
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while i < nfuncs {
|
while i < nfuncs {
|
||||||
var f = func_at(self, i);
|
var f = func_base + i;
|
||||||
if f == null { ret .Err(make(.Io)); }
|
if !put_name_field(out, f.name) { ret .Err(make(.Io)); }
|
||||||
if !put_name_field(&out, f.name) { ret .Err(make(.Io)); }
|
if !put_u64(out, f.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, f.idx_of_strtab) { ret .Err(make(.Io)); }
|
if !put_u64(out, f.nargs) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, f.nargs) { ret .Err(make(.Io)); }
|
if !put_u64(out, f.ninsns) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, f.ninsns) { ret .Err(make(.Io)); }
|
if !put_u64(out, f.address) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, f.address) { ret .Err(make(.Io)); }
|
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
|
var ins_base = self.insns.data as *VmIns;
|
||||||
|
var out_base = out.storage.data;
|
||||||
|
var out_len = out.storage.len;
|
||||||
while i < ninsns {
|
while i < ninsns {
|
||||||
var ins = ins_at(self, i);
|
var p = ins_base + i;
|
||||||
var d = out.storage.data + out.storage.len;
|
var d = out_base + out_len;
|
||||||
(d as *u16).* = @enum2int(ins.opcode) as u16;
|
(d as *u16).* = @enum2int(p.opcode) as u16;
|
||||||
((d + 2) as *u16).* = @enum2int(ins.ty) as u16;
|
((d + 2) as *u16).* = @enum2int(p.ty) as u16;
|
||||||
((d + 4) as *u64).* = ins.imm as u64;
|
((d + 4) as *u64).* = p.imm as u64;
|
||||||
out.storage.len = out.storage.len + 12;
|
out_len = out_len + 12;
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
out.storage.len = out_len;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while i < nnatives {
|
while i < nnatives {
|
||||||
var n = native_at(self, i);
|
var n = nat_base + i;
|
||||||
if n == null { ret .Err(make(.Io)); }
|
if !put_name_field(out, n.name) { ret .Err(make(.Io)); }
|
||||||
if !put_name_field(&out, n.name) { ret .Err(make(.Io)); }
|
if !put_u64(out, n.idx_of_strtab) { ret .Err(make(.Io)); }
|
||||||
if !put_u64(&out, n.idx_of_strtab) { ret .Err(make(.Io)); }
|
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while i < nstrs {
|
while i < nstrs {
|
||||||
var s = str_at(self, i);
|
var s = str_base + i;
|
||||||
if !put_u64(&out, s.len) { ret .Err(make(.Io)); }
|
if !put_u64(out, s.len) { ret .Err(make(.Io)); }
|
||||||
if !put_view(&out, s) { ret .Err(make(.Io)); }
|
if !put_view(out, s.*) { ret .Err(make(.Io)); }
|
||||||
if !put_zeros(&out, align8(s.len) - s.len) { ret .Err(make(.Io)); }
|
if !put_zeros(out, align8(s.len) - s.len) { ret .Err(make(.Io)); }
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 1;
|
i = 1;
|
||||||
while i < self.gdata.len {
|
while i < self.gdata.len {
|
||||||
var e = gdata_at(self, i);
|
var e = gd_base + i;
|
||||||
if e == null { ret .Err(make(.Io)); }
|
|
||||||
var dsize = e.storage.len;
|
var dsize = e.storage.len;
|
||||||
if !put_u64(&out, dsize) { ret .Err(make(.Io)); }
|
if !put_u64(out, dsize) { ret .Err(make(.Io)); }
|
||||||
if !put_view(&out, ByteView.from_slice(ByteBuffer.as_slice(e.*))) { ret .Err(make(.Io)); }
|
if !put_view(out, ByteView.from_slice(ByteBuffer.as_slice(e.*))) { ret .Err(make(.Io)); }
|
||||||
if !put_zeros(&out, align8(dsize) - dsize) { ret .Err(make(.Io)); }
|
if !put_zeros(out, align8(dsize) - dsize) { ret .Err(make(.Io)); }
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
ret .Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store(self: *Prog, path: ByteView): Status {
|
||||||
|
var out = ByteBuffer.new();
|
||||||
|
defer { ByteBuffer.drop(&out); }
|
||||||
|
|
||||||
|
match encode(self, &out) {
|
||||||
|
.Ok => {},
|
||||||
|
.Err(error) => { ret .Err(error); },
|
||||||
|
}
|
||||||
|
|
||||||
var f = open_cstr(path, ByteView.from_slice("wb"));
|
var f = open_cstr(path, ByteView.from_slice("wb"));
|
||||||
if !File.valid(&f) { ret .Err(make(.Io)); }
|
if !File.valid(&f) { ret .Err(make(.Io)); }
|
||||||
@@ -664,10 +682,12 @@ fn store(self: *Prog, path: ByteView): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_ptr(p: *any) {}
|
// 序列化到内存缓冲(供内嵌 VM 直接装载)。
|
||||||
fn ignore_usize(v: usize) {}
|
fn serialize(self: *Prog, out: *ByteBuffer): Status {
|
||||||
|
ret encode(self, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_dump_basic() {
|
fn test_dump_basic() {
|
||||||
var pr: Prog = undefined;
|
var pr: Prog = undefined;
|
||||||
@@ -677,7 +697,7 @@ fn test_dump_basic() {
|
|||||||
}
|
}
|
||||||
defer { drop(&pr); }
|
defer { drop(&pr); }
|
||||||
|
|
||||||
ignore_usize(add_instr(&pr, .Halt, .Void, 0));
|
add_instr(&pr, .Halt, .Void, 0);
|
||||||
|
|
||||||
var out = ByteBuffer.new();
|
var out = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&out); }
|
defer { ByteBuffer.drop(&out); }
|
||||||
@@ -725,8 +745,8 @@ fn test_store_roundtrip() {
|
|||||||
}
|
}
|
||||||
defer { drop(&pr); }
|
defer { drop(&pr); }
|
||||||
|
|
||||||
ignore_usize(add_func(&pr, ByteView.from_slice("main"), 0, 0));
|
add_func(&pr, ByteView.from_slice("main"), 0, 0);
|
||||||
ignore_usize(add_instr(&pr, .Halt, .Void, 0));
|
add_instr(&pr, .Halt, .Void, 0);
|
||||||
|
|
||||||
var path = ByteView.from_slice("build/_mcode_roundtrip.tmp");
|
var path = ByteView.from_slice("build/_mcode_roundtrip.tmp");
|
||||||
match store(&pr, path) {
|
match store(&pr, path) {
|
||||||
|
|||||||
+198
-154
@@ -1,7 +1,6 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// Parser:token 流 -> Ast(ADT 节点)。C 版只作控制流参考,节点分配顺序需与 stage1
|
// token 流 -> Ast。节点分配顺序必须与 splc0 一致,否则 dump 的 #ref 无法逐字节对比。
|
||||||
// 一致,以保证 dump 的 #ref 可对比。pos 在 token 向量上移动,自动跳过 trivia。
|
|
||||||
|
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
@@ -41,7 +40,6 @@ fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- token 访问 ----
|
|
||||||
|
|
||||||
fn token_ptr(self: *Parser, index: usize): *Token {
|
fn token_ptr(self: *Parser, index: usize): *Token {
|
||||||
if index >= self.toks.len { ret null; }
|
if index >= self.toks.len { ret null; }
|
||||||
@@ -138,7 +136,6 @@ fn at_eof(self: *Parser): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 错误 ----
|
|
||||||
|
|
||||||
fn span_of(self: *Parser, tok: *Token): Span {
|
fn span_of(self: *Parser, tok: *Token): Span {
|
||||||
if tok == null { ret span_none(); }
|
if tok == null { ret span_none(); }
|
||||||
@@ -151,7 +148,7 @@ fn parse_error(self: *Parser, tok: *Token, what: []u8) {
|
|||||||
Diag.err(self.diag, span_of(self, tok), .ParseExpected, what);
|
Diag.err(self.diag, span_of(self, tok), .ParseExpected, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转义解码(镜像 stage1 spl_decode_escape):去掉引号,处理 \n \t \r \0 \\ \' \" \xHH。
|
// 转义解码:去掉引号,处理 \n \t \r \0 \\ \' \" \xHH。
|
||||||
fn hex_digit(c: u8): u8 {
|
fn hex_digit(c: u8): u8 {
|
||||||
if c >= '0' && c <= '9' { ret c - '0'; }
|
if c >= '0' && c <= '9' { ret c - '0'; }
|
||||||
if c >= 'a' && c <= 'f' { ret (c - 'a') + 10; }
|
if c >= 'a' && c <= 'f' { ret (c - 'a') + 10; }
|
||||||
@@ -172,9 +169,7 @@ fn decode_str(t: ByteView): ByteBuffer {
|
|||||||
if e == 'x' && i + 3 < end {
|
if e == 'x' && i + 3 < end {
|
||||||
var hi = hex_digit(t.ptr[i + 2]);
|
var hi = hex_digit(t.ptr[i + 2]);
|
||||||
var lo = hex_digit(t.ptr[i + 3]);
|
var lo = hex_digit(t.ptr[i + 3]);
|
||||||
match ByteBuffer.append_byte(&out, (hi * 16 + lo) as u8) {
|
ByteBuffer.append_byte(&out, (hi * 16 + lo) as u8);
|
||||||
.Ok => {}, .Err(error) => {},
|
|
||||||
}
|
|
||||||
i = i + 4;
|
i = i + 4;
|
||||||
} else {
|
} else {
|
||||||
var d: u8 = e;
|
var d: u8 = e;
|
||||||
@@ -182,11 +177,11 @@ fn decode_str(t: ByteView): ByteBuffer {
|
|||||||
else if e == 't' { d = '\t'; }
|
else if e == 't' { d = '\t'; }
|
||||||
else if e == 'r' { d = '\r'; }
|
else if e == 'r' { d = '\r'; }
|
||||||
else if e == '0' { d = 0; }
|
else if e == '0' { d = 0; }
|
||||||
match ByteBuffer.append_byte(&out, d) { .Ok => {}, .Err(error) => {} }
|
ByteBuffer.append_byte(&out, d);
|
||||||
i = i + 2;
|
i = i + 2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match ByteBuffer.append_byte(&out, c) { .Ok => {}, .Err(error) => {} }
|
ByteBuffer.append_byte(&out, c);
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +198,6 @@ fn expect_punct(self: *Parser, punct: Punct, what: []u8): bool {
|
|||||||
ret true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 节点 ----
|
|
||||||
|
|
||||||
fn new_node(self: *Parser, body: Body, tok: *Token): usize {
|
fn new_node(self: *Parser, body: Body, tok: *Token): usize {
|
||||||
ret Ast.alloc(self.ast, body, span_of(self, tok));
|
ret Ast.alloc(self.ast, body, span_of(self, tok));
|
||||||
@@ -262,7 +256,6 @@ fn int_token_u64(self: *Parser, tok: *Token, radix: Radix): u64 {
|
|||||||
ret v;
|
ret v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 判定(stage1 can_start_*)----
|
|
||||||
|
|
||||||
fn can_start_expr(kind: TokenKind): bool {
|
fn can_start_expr(kind: TokenKind): bool {
|
||||||
match kind {
|
match kind {
|
||||||
@@ -282,6 +275,7 @@ fn can_start_expr(kind: TokenKind): bool {
|
|||||||
.For => { ret true; },
|
.For => { ret true; },
|
||||||
.Match => { ret true; },
|
.Match => { ret true; },
|
||||||
.Comptime => { ret true; },
|
.Comptime => { ret true; },
|
||||||
|
.Try => { ret true; },
|
||||||
_ => { ret false; },
|
_ => { ret false; },
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -321,6 +315,7 @@ fn can_start_statement(kind: TokenKind): bool {
|
|||||||
.Break => { ret true; },
|
.Break => { ret true; },
|
||||||
.Continue => { ret true; },
|
.Continue => { ret true; },
|
||||||
.Defer => { ret true; },
|
.Defer => { ret true; },
|
||||||
|
.Errdefer => { ret true; },
|
||||||
.Var => { ret true; },
|
.Var => { ret true; },
|
||||||
.Const => { ret true; },
|
.Const => { ret true; },
|
||||||
.Type => { ret true; },
|
.Type => { ret true; },
|
||||||
@@ -333,11 +328,10 @@ fn can_start_statement(kind: TokenKind): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 模式(M3.2 子集:ident / _ / .variant / tuple / .{ } )----
|
|
||||||
|
|
||||||
fn parse_pattern_aggregate(self: *Parser, start: *Token): usize {
|
fn parse_pattern_aggregate(self: *Parser, start: *Token): usize {
|
||||||
if !expect_punct(self, .LBrace, "'{' after '.'") { ret 0; }
|
if !expect_punct(self, .LBrace, "'{' after '.'") { ret 0; }
|
||||||
// stage1 先分配 PATTERN_AGGREGATE,再解析字段。
|
// 先分配 PATTERN_AGGREGATE,再解析字段。
|
||||||
var ref = new_node(self, .PatternAggregate(.{ .items = ref_vec() }), start);
|
var ref = new_node(self, .PatternAggregate(.{ .items = ref_vec() }), start);
|
||||||
var fields = ref_vec();
|
var fields = ref_vec();
|
||||||
while !self.failed {
|
while !self.failed {
|
||||||
@@ -352,7 +346,7 @@ fn parse_pattern_aggregate(self: *Parser, start: *Token): usize {
|
|||||||
if name == null { break; }
|
if name == null { break; }
|
||||||
match name.kind { .Ident => {}, _ => { break; } }
|
match name.kind { .Ident => {}, _ => { break; } }
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 PATTERN_FIELD,再解析可选子模式。
|
// 先分配 PATTERN_FIELD,再解析可选子模式。
|
||||||
var fre = new_node(self, .PatternField(.{
|
var fre = new_node(self, .PatternField(.{
|
||||||
.name = name.text,
|
.name = name.text,
|
||||||
.pattern = 0,
|
.pattern = 0,
|
||||||
@@ -447,7 +441,7 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
if nx == null { ret 0; }
|
if nx == null { ret 0; }
|
||||||
match nx.kind { .Ident => {}, _ => { ret 0; } }
|
match nx.kind { .Ident => {}, _ => { ret 0; } }
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 PATTERN_DOT,再解析子模式。
|
// 先分配 PATTERN_DOT,再解析子模式。
|
||||||
var ref = new_node(self, .PatternDot(.{
|
var ref = new_node(self, .PatternDot(.{
|
||||||
.name = nx.text,
|
.name = nx.text,
|
||||||
.sub_pattern = 0,
|
.sub_pattern = 0,
|
||||||
@@ -458,9 +452,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);
|
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
@@ -471,7 +464,7 @@ fn parse_pattern_atom(self: *Parser): usize {
|
|||||||
},
|
},
|
||||||
.LParen => {
|
.LParen => {
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 PATTERN_TUPLE,再解析内部模式。
|
// 先分配 PATTERN_TUPLE,再解析内部模式。
|
||||||
var ref = new_node(self, .PatternTuple(.{ .items = ref_vec() }), t);
|
var ref = new_node(self, .PatternTuple(.{ .items = ref_vec() }), t);
|
||||||
if at_punct(self, .RParen) {
|
if at_punct(self, .RParen) {
|
||||||
advance(self);
|
advance(self);
|
||||||
@@ -506,6 +499,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);
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -543,9 +554,8 @@ fn parse_pattern(self: *Parser): usize {
|
|||||||
ret new_node(self, .PatternList(.{ .items = atoms }), peek(self));
|
ret new_node(self, .PatternList(.{ .items = atoms }), peek(self));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 声明 ----
|
|
||||||
|
|
||||||
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; }
|
||||||
@@ -554,30 +564,37 @@ 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; }
|
||||||
|
if bang_only {
|
||||||
|
// 仅消费 `#!`/`@!` 形式的当前层 tag;遇到普通 `#name` 即停。
|
||||||
|
var save = self.pos;
|
||||||
advance(self);
|
advance(self);
|
||||||
if is_external {
|
var nt = peek(self);
|
||||||
t = peek(self);
|
var has_bang = nt != null && kind_is_punct(nt.kind, .Not);
|
||||||
if t == null || !kind_is_punct(t.kind, .Not) {
|
self.pos = save;
|
||||||
parse_error(self, t, "expected '!' in external tag");
|
if !has_bang { ret; }
|
||||||
ret;
|
|
||||||
}
|
}
|
||||||
advance(self);
|
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; }
|
||||||
match t.kind { .Ident => {}, _ => { parse_error(self, t, "expected tag name"); ret; } }
|
match t.kind { .Ident => {}, _ => { parse_error(self, t, "expected tag name"); ret; } }
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 tag 节点,再解析参数。
|
// 先分配 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(),
|
||||||
});
|
});
|
||||||
@@ -606,7 +623,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;
|
||||||
@@ -618,10 +641,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,11 +672,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);
|
||||||
@@ -679,20 +715,16 @@ fn token_starts_type(t: *Token): bool {
|
|||||||
match t.kind {
|
match t.kind {
|
||||||
.Ident => { ret true; },
|
.Ident => { ret true; },
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Type) { ret true; }
|
match k {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Bool) { ret true; }
|
.Type, .Bool, .Any, .Fn, .Struct, .Union, .Enum, .Shape => { ret true; },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Any) { ret true; }
|
_ => {},
|
||||||
if @enum2int(k) == @enum2int(Keyword.Fn) { ret true; }
|
}
|
||||||
if @enum2int(k) == @enum2int(Keyword.Struct) { ret true; }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Union) { ret true; }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Enum) { ret true; }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Shape) { ret true; }
|
|
||||||
},
|
},
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.Mul) { ret true; }
|
match p {
|
||||||
if @enum2int(p) == @enum2int(Punct.LBracket) { ret true; }
|
.Mul, .LBracket, .At, .Sharp => { ret true; },
|
||||||
if @enum2int(p) == @enum2int(Punct.At) { ret true; }
|
_ => {},
|
||||||
if @enum2int(p) == @enum2int(Punct.Sharp) { ret true; }
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
@@ -750,7 +782,7 @@ fn parse_type_decl(self: *Parser, tags: Vec): usize {
|
|||||||
var pattern = parse_pattern(self);
|
var pattern = parse_pattern(self);
|
||||||
if pattern == 0 { ret 0; }
|
if pattern == 0 { ret 0; }
|
||||||
if !expect_punct(self, .Assign, "'=' in type declaration") { ret 0; }
|
if !expect_punct(self, .Assign, "'=' in type declaration") { ret 0; }
|
||||||
// stage1 先分配 TYPE_DECL 再解析 type_expr(影响 #ref 顺序)。
|
// 先分配 TYPE_DECL 再解析 type_expr(影响 #ref 顺序)。
|
||||||
var ref = new_node(self, .TypeDecl(.{
|
var ref = new_node(self, .TypeDecl(.{
|
||||||
.attr_list = tags,
|
.attr_list = tags,
|
||||||
.pattern = pattern,
|
.pattern = pattern,
|
||||||
@@ -823,7 +855,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) {
|
||||||
@@ -862,38 +894,39 @@ 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 {
|
||||||
.Eof => { ret 0; },
|
.Eof => { ret 0; },
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Fn) { ret parse_fn_decl(self, tags, false); }
|
match k {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Type) { ret parse_type_decl(self, tags); }
|
.Fn => { ret parse_fn_decl(self, tags, false); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Var) {
|
.Type => { ret parse_type_decl(self, tags); },
|
||||||
ret parse_var_const_decl(self, tags, false);
|
.Var => { ret parse_var_const_decl(self, tags, false); },
|
||||||
}
|
.Const => { ret parse_var_const_decl(self, tags, true); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Const) {
|
.Comptime => {
|
||||||
ret parse_var_const_decl(self, tags, true);
|
|
||||||
}
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Comptime) {
|
|
||||||
// `comptime fn ...` -> 标记为编译期函数;否则 `comptime { }` 声明。
|
|
||||||
var nxt = peek_next(self);
|
var nxt = peek_next(self);
|
||||||
if nxt != null && kind_is_keyword(nxt.kind, .Fn) {
|
if nxt != null && kind_is_keyword(nxt.kind, .Fn) {
|
||||||
advance(self);
|
advance(self);
|
||||||
ret parse_fn_decl(self, tags, true);
|
ret parse_fn_decl(self, tags, true);
|
||||||
}
|
}
|
||||||
ret parse_comptime_decl(self);
|
ret parse_comptime_decl(self);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Ident => { ret parse_member_decl(self, tags); },
|
.Ident => { ret parse_member_decl(self, tags); },
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.LBrace) {
|
match p {
|
||||||
|
.LBrace => {
|
||||||
var block = parse_block(self);
|
var block = parse_block(self);
|
||||||
ret new_node(self, .TopLevelBlock(.{
|
ret new_node(self, .TopLevelBlock(.{
|
||||||
.tag_list = tags,
|
.tag_list = tags,
|
||||||
.block = block,
|
.block = block,
|
||||||
}), t);
|
}), t);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -903,7 +936,6 @@ fn parse_container_declaration(self: *Parser): usize {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 类型 ----
|
|
||||||
|
|
||||||
fn parse_type_comptime_call(self: *Parser): usize {
|
fn parse_type_comptime_call(self: *Parser): usize {
|
||||||
var start = peek(self);
|
var start = peek(self);
|
||||||
@@ -932,7 +964,7 @@ fn parse_type_base(self: *Parser): usize {
|
|||||||
if kind_is_keyword(bt.kind, .Fn) {
|
if kind_is_keyword(bt.kind, .Fn) {
|
||||||
advance(self);
|
advance(self);
|
||||||
if !expect_punct(self, .LParen, "'(' in function type") { ret 0; }
|
if !expect_punct(self, .LParen, "'(' in function type") { ret 0; }
|
||||||
// stage1 先分配 TYPE_FN,再解析参数与返回类型。
|
// 先分配 TYPE_FN,再解析参数与返回类型。
|
||||||
var ref = new_node(self, .TypeFn(.{
|
var ref = new_node(self, .TypeFn(.{
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.params = ref_vec(),
|
.params = ref_vec(),
|
||||||
@@ -966,10 +998,13 @@ fn parse_type_base(self: *Parser): usize {
|
|||||||
match bt.kind {
|
match bt.kind {
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
var kind_name: []u8 = "";
|
var kind_name: []u8 = "";
|
||||||
if @enum2int(k) == @enum2int(Keyword.Struct) { kind_name = "struct"; }
|
match k {
|
||||||
else if @enum2int(k) == @enum2int(Keyword.Union) { kind_name = "union"; }
|
.Struct => { kind_name = "struct"; },
|
||||||
else if @enum2int(k) == @enum2int(Keyword.Enum) { kind_name = "enum"; }
|
.Union => { kind_name = "union"; },
|
||||||
else if @enum2int(k) == @enum2int(Keyword.Shape) { kind_name = "shape"; }
|
.Enum => { kind_name = "enum"; },
|
||||||
|
.Shape => { kind_name = "shape"; },
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
if kind_name.len != 0 {
|
if kind_name.len != 0 {
|
||||||
advance(self);
|
advance(self);
|
||||||
if !expect_punct(self, .LBrace, "'{' in aggregate type") { ret 0; }
|
if !expect_punct(self, .LBrace, "'{' in aggregate type") { ret 0; }
|
||||||
@@ -1043,20 +1078,21 @@ fn parse_type_base(self: *Parser): usize {
|
|||||||
}), bt);
|
}), bt);
|
||||||
},
|
},
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
// stage1 is_builtin_type_kw:bool / any 作为类型路径;stage3 增加 `type`。
|
match k {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Bool) ||
|
.Bool, .Any, .Type => {
|
||||||
@enum2int(k) == @enum2int(Keyword.Any) ||
|
|
||||||
@enum2int(k) == @enum2int(Keyword.Type) {
|
|
||||||
advance(self);
|
advance(self);
|
||||||
ret new_node(self, .TypePath(.{
|
ret new_node(self, .TypePath(.{
|
||||||
.name = bt.text,
|
.name = bt.text,
|
||||||
.generic_args = ref_vec(),
|
.generic_args = ref_vec(),
|
||||||
}), bt);
|
}), bt);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) {
|
match p {
|
||||||
ret parse_type_comptime_call(self);
|
.At, .Sharp => { ret parse_type_comptime_call(self); },
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -1071,7 +1107,7 @@ fn parse_type_expr(self: *Parser): usize {
|
|||||||
|
|
||||||
if kind_is_punct(t.kind, .Mul) {
|
if kind_is_punct(t.kind, .Mul) {
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 POINTER,再解析 pointee。
|
// 先分配 POINTER,再解析 pointee。
|
||||||
var ref = new_node(self, .TypePointer(.{ .pointee = 0 }), t);
|
var ref = new_node(self, .TypePointer(.{ .pointee = 0 }), t);
|
||||||
var pointee = parse_type_expr(self);
|
var pointee = parse_type_expr(self);
|
||||||
var pn = Ast.node(self.ast, ref);
|
var pn = Ast.node(self.ast, ref);
|
||||||
@@ -1155,7 +1191,6 @@ fn parse_type_expr(self: *Parser): usize {
|
|||||||
ret base;
|
ret base;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 表达式 ----
|
|
||||||
|
|
||||||
type BinInfo = struct { op: Punct, prec: i32, right: bool }
|
type BinInfo = struct { op: Punct, prec: i32, right: bool }
|
||||||
type BinResult = enum { No, Yes: BinInfo }
|
type BinResult = enum { No, Yes: BinInfo }
|
||||||
@@ -1237,7 +1272,19 @@ fn parse_expr_prec(self: *Parser, min_prec: i32): usize {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(self: *Parser): usize {
|
fn parse_expr(self: *Parser): usize {
|
||||||
ret parse_expr_prec(self, 1);
|
var e = parse_expr_prec(self, 1);
|
||||||
|
// `expr catch <pattern> <block>`:catch 用解包语法绑定错误。
|
||||||
|
while !self.failed {
|
||||||
|
var t = peek(self);
|
||||||
|
if t == null { break; }
|
||||||
|
if !kind_is_keyword(t.kind, .Catch) { break; }
|
||||||
|
advance(self);
|
||||||
|
var pat = parse_pattern(self);
|
||||||
|
if pat == 0 { ret e; }
|
||||||
|
var handler = parse_block(self);
|
||||||
|
e = new_node(self, .ExprCatch(.{ .value = e, .pattern = pat, .handler = handler }), t);
|
||||||
|
}
|
||||||
|
ret e;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_cast(self: *Parser): usize {
|
fn parse_cast(self: *Parser): usize {
|
||||||
@@ -1268,9 +1315,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;
|
||||||
@@ -1310,7 +1357,7 @@ fn parse_postfix(self: *Parser): usize {
|
|||||||
|
|
||||||
if kind_is_punct(t.kind, .LParen) {
|
if kind_is_punct(t.kind, .LParen) {
|
||||||
advance(self);
|
advance(self);
|
||||||
// stage1 先分配 CALL,再解析实参(影响 #ref 顺序)。
|
// 先分配 CALL,再解析实参(影响 #ref 顺序)。
|
||||||
var ref = new_node(self, .ExprCall(.{ .callee = expr, .args = ref_vec() }), t);
|
var ref = new_node(self, .ExprCall(.{ .callee = expr, .args = ref_vec() }), t);
|
||||||
var args = parse_expr_list(self);
|
var args = parse_expr_list(self);
|
||||||
var cn = Ast.node(self.ast, ref);
|
var cn = Ast.node(self.ast, ref);
|
||||||
@@ -1423,41 +1470,31 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
ret new_node(self, .ExprStringLit(.{ .data = Ast.push_string(self.ast, decode_str(t.text)) }), t);
|
ret new_node(self, .ExprStringLit(.{ .data = Ast.push_string(self.ast, decode_str(t.text)) }), t);
|
||||||
},
|
},
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
if @enum2int(k) == @enum2int(Keyword.True) {
|
match k {
|
||||||
|
.True => { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = true }), t); },
|
||||||
|
.False => { advance(self); ret new_node(self, .ExprBoolLit(.{ .value = false }), t); },
|
||||||
|
.Null => { advance(self); ret new_node(self, .ExprNullLit, t); },
|
||||||
|
.Undefined => { advance(self); ret new_node(self, .ExprUndefinedLit, t); },
|
||||||
|
.If => { ret parse_if_expr(self); },
|
||||||
|
.While => { ret parse_while_expr(self); },
|
||||||
|
.Loop => { ret parse_loop_expr(self); },
|
||||||
|
.For => { ret parse_for_expr(self); },
|
||||||
|
.Match => { ret parse_match_expr(self); },
|
||||||
|
.Try => {
|
||||||
advance(self);
|
advance(self);
|
||||||
ret new_node(self, .ExprBoolLit(.{ .value = true }), t);
|
var tv = parse_expr(self);
|
||||||
}
|
ret new_node(self, .ExprTry(.{ .value = tv }), t);
|
||||||
if @enum2int(k) == @enum2int(Keyword.False) {
|
},
|
||||||
advance(self);
|
.Comptime => {
|
||||||
ret new_node(self, .ExprBoolLit(.{ .value = false }), t);
|
|
||||||
}
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Null) {
|
|
||||||
advance(self);
|
|
||||||
ret new_node(self, .ExprNullLit, t);
|
|
||||||
}
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Undefined) {
|
|
||||||
advance(self);
|
|
||||||
ret new_node(self, .ExprUndefinedLit, t);
|
|
||||||
}
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.If) { ret parse_if_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.While) { ret parse_while_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Loop) { ret parse_loop_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.For) { ret parse_for_expr(self); }
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Match) { ret parse_match_expr(self); }
|
|
||||||
// `comptime <expr>`:编译期表达式(可调用普通函数、返回类型等)。
|
|
||||||
if @enum2int(k) == @enum2int(Keyword.Comptime) {
|
|
||||||
advance(self);
|
advance(self);
|
||||||
var cbody = parse_expr(self);
|
var cbody = parse_expr(self);
|
||||||
ret new_node(self, .ExprComptime(.{ .body = cbody }), t);
|
ret new_node(self, .ExprComptime(.{ .body = cbody }), t);
|
||||||
}
|
},
|
||||||
// 类型即表达式:仅在 comptime 上下文内允许 `struct { ... }` 等聚合类型。
|
.Struct, .Union, .Enum, .Shape => {
|
||||||
if self.comptime_depth > 0 {
|
// 类型即表达式仅限 comptime 上下文。
|
||||||
if @enum2int(k) == @enum2int(Keyword.Struct) ||
|
if self.comptime_depth > 0 { ret parse_type_base(self); }
|
||||||
@enum2int(k) == @enum2int(Keyword.Union) ||
|
},
|
||||||
@enum2int(k) == @enum2int(Keyword.Enum) ||
|
_ => {},
|
||||||
@enum2int(k) == @enum2int(Keyword.Shape) {
|
|
||||||
ret parse_type_base(self);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
parse_error(self, t, "unrecognized expression");
|
parse_error(self, t, "unrecognized expression");
|
||||||
ret 0;
|
ret 0;
|
||||||
@@ -1465,7 +1502,7 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
.Ident => {
|
.Ident => {
|
||||||
advance(self);
|
advance(self);
|
||||||
if at_punct(self, .LBrace) && self.stop_agg == 0 {
|
if at_punct(self, .LBrace) && self.stop_agg == 0 {
|
||||||
// stage1 先分配 AGGREGATE_LIT,再解析字段。
|
// 先分配 AGGREGATE_LIT,再解析字段。
|
||||||
var ref = new_node(self, .ExprAggregateLit(.{
|
var ref = new_node(self, .ExprAggregateLit(.{
|
||||||
.name = t.text,
|
.name = t.text,
|
||||||
.fields = ref_vec(),
|
.fields = ref_vec(),
|
||||||
@@ -1473,10 +1510,7 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
var fields = parse_aggregate_fields(self);
|
var fields = parse_aggregate_fields(self);
|
||||||
var an = Ast.node(self.ast, ref);
|
var an = Ast.node(self.ast, ref);
|
||||||
match an.body {
|
match an.body {
|
||||||
.ExprAggregateLit(a) => {
|
.ExprAggregateLit(&a) => { a.fields = fields; },
|
||||||
a.fields = fields;
|
|
||||||
an.body = .ExprAggregateLit(a);
|
|
||||||
},
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
@@ -1484,7 +1518,8 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
ret new_node(self, .ExprIdent(.{ .name = t.text }), t);
|
ret new_node(self, .ExprIdent(.{ .name = t.text }), t);
|
||||||
},
|
},
|
||||||
.Punct(p) => {
|
.Punct(p) => {
|
||||||
if @enum2int(p) == @enum2int(Punct.Dot) {
|
match p {
|
||||||
|
.Dot => {
|
||||||
advance(self);
|
advance(self);
|
||||||
var nx = peek(self);
|
var nx = peek(self);
|
||||||
if nx == null { parse_error(self, t, "unexpected '.'"); ret 0; }
|
if nx == null { parse_error(self, t, "unexpected '.'"); ret 0; }
|
||||||
@@ -1496,10 +1531,7 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
var fields = parse_aggregate_fields(self);
|
var fields = parse_aggregate_fields(self);
|
||||||
var an = Ast.node(self.ast, ref);
|
var an = Ast.node(self.ast, ref);
|
||||||
match an.body {
|
match an.body {
|
||||||
.ExprAggregateLit(a) => {
|
.ExprAggregateLit(&a) => { a.fields = fields; },
|
||||||
a.fields = fields;
|
|
||||||
an.body = .ExprAggregateLit(a);
|
|
||||||
},
|
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
ret ref;
|
ret ref;
|
||||||
@@ -1521,13 +1553,11 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
if is_ident { ret parse_variant_construct(self, t); }
|
if is_ident { ret parse_variant_construct(self, t); }
|
||||||
parse_error(self, nx, "expected '{', '[' or identifier after '.'");
|
parse_error(self, nx, "expected '{', '[' or identifier after '.'");
|
||||||
ret 0;
|
ret 0;
|
||||||
}
|
},
|
||||||
if @enum2int(p) == @enum2int(Punct.LBracket) { ret parse_array_literal(self); }
|
.LBracket => { ret parse_array_literal(self); },
|
||||||
if @enum2int(p) == @enum2int(Punct.LBrace) { ret parse_block(self); }
|
.LBrace => { ret parse_block(self); },
|
||||||
if @enum2int(p) == @enum2int(Punct.At) || @enum2int(p) == @enum2int(Punct.Sharp) {
|
.At, .Sharp => { ret parse_comptime_call(self); },
|
||||||
ret parse_comptime_call(self);
|
.LParen => {
|
||||||
}
|
|
||||||
if @enum2int(p) == @enum2int(Punct.LParen) {
|
|
||||||
advance(self);
|
advance(self);
|
||||||
var elems = ref_vec();
|
var elems = ref_vec();
|
||||||
if at_punct(self, .RParen) {
|
if at_punct(self, .RParen) {
|
||||||
@@ -1536,12 +1566,12 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
}
|
}
|
||||||
var first = parse_expr(self);
|
var first = parse_expr(self);
|
||||||
if at_punct(self, .Comma) {
|
if at_punct(self, .Comma) {
|
||||||
match Vec.push(&elems, &first as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
|
Vec.push(&elems, &first as *u8);
|
||||||
while at_punct(self, .Comma) {
|
while at_punct(self, .Comma) {
|
||||||
advance(self);
|
advance(self);
|
||||||
if at_punct(self, .RParen) { break; }
|
if at_punct(self, .RParen) { break; }
|
||||||
var nx = parse_expr(self);
|
var nx = parse_expr(self);
|
||||||
match Vec.push(&elems, &nx as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
|
Vec.push(&elems, &nx as *u8);
|
||||||
}
|
}
|
||||||
expect_punct(self, .RParen, "')' in tuple literal");
|
expect_punct(self, .RParen, "')' in tuple literal");
|
||||||
ret new_node(self, .ExprTuple(.{ .items = elems }), t);
|
ret new_node(self, .ExprTuple(.{ .items = elems }), t);
|
||||||
@@ -1549,6 +1579,8 @@ fn parse_primary(self: *Parser): usize {
|
|||||||
expect_punct(self, .RParen, "')' in paren expression");
|
expect_punct(self, .RParen, "')' in paren expression");
|
||||||
Vec.drop(&elems);
|
Vec.drop(&elems);
|
||||||
ret first;
|
ret first;
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -1698,7 +1730,6 @@ fn parse_expr_list(self: *Parser): Vec {
|
|||||||
ret vec;
|
ret vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- if / while / loop / for / match ----
|
|
||||||
|
|
||||||
fn parse_if_expr(self: *Parser): usize {
|
fn parse_if_expr(self: *Parser): usize {
|
||||||
var kw = peek(self);
|
var kw = peek(self);
|
||||||
@@ -1865,7 +1896,7 @@ fn parse_match_expr(self: *Parser): usize {
|
|||||||
if at_punct(self, .Comma) { advance(self); }
|
if at_punct(self, .Comma) { advance(self); }
|
||||||
}
|
}
|
||||||
if !expect_punct(self, .RBrace, "'}' in match") { ret 0; }
|
if !expect_punct(self, .RBrace, "'}' in match") { ret 0; }
|
||||||
// stage1 先分配 MATCH_EXPR,再逐个分配 MATCH_ARM。
|
// 先分配 MATCH_EXPR,再逐个分配 MATCH_ARM。
|
||||||
var ref = new_node(self, .ExprMatch(.{
|
var ref = new_node(self, .ExprMatch(.{
|
||||||
.matched_expr = matched,
|
.matched_expr = matched,
|
||||||
.arms = ref_vec(),
|
.arms = ref_vec(),
|
||||||
@@ -1894,7 +1925,6 @@ fn parse_match_expr(self: *Parser): usize {
|
|||||||
ret ref;
|
ret ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 语句 ----
|
|
||||||
|
|
||||||
fn parse_ret_stmt(self: *Parser): usize {
|
fn parse_ret_stmt(self: *Parser): usize {
|
||||||
var kw = peek(self);
|
var kw = peek(self);
|
||||||
@@ -1958,6 +1988,22 @@ fn parse_defer_stmt(self: *Parser): usize {
|
|||||||
ret new_node(self, .StatementDefer(.{ .body = body }), kw);
|
ret new_node(self, .StatementDefer(.{ .body = body }), kw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_errdefer_stmt(self: *Parser): usize {
|
||||||
|
var kw = peek(self);
|
||||||
|
if kw == null || !kind_is_keyword(kw.kind, .Errdefer) { ret 0; }
|
||||||
|
advance(self);
|
||||||
|
var body: usize = 0;
|
||||||
|
var t = peek(self);
|
||||||
|
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
|
||||||
|
else if t != null && can_start_statement(t.kind) { body = parse_statement(self); }
|
||||||
|
else {
|
||||||
|
body = parse_expr(self);
|
||||||
|
t = peek(self);
|
||||||
|
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
|
||||||
|
}
|
||||||
|
ret new_node(self, .StatementErrdefer(.{ .body = body }), kw);
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_comptime_stmt(self: *Parser): usize {
|
fn parse_comptime_stmt(self: *Parser): usize {
|
||||||
var kw = peek(self);
|
var kw = peek(self);
|
||||||
if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; }
|
if kw == null || !kind_is_keyword(kw.kind, .Comptime) { ret 0; }
|
||||||
@@ -1975,26 +2021,26 @@ fn parse_statement(self: *Parser): usize {
|
|||||||
|
|
||||||
match t.kind {
|
match t.kind {
|
||||||
.Keyword(k) => {
|
.Keyword(k) => {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Ret) { ret parse_ret_stmt(self); }
|
match k {
|
||||||
if @enum2int(k) == @enum2int(Keyword.Break) { ret parse_break_stmt(self); }
|
.Ret => { ret parse_ret_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Continue) { ret parse_continue_stmt(self); }
|
.Break => { ret parse_break_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Defer) { ret parse_defer_stmt(self); }
|
.Continue => { ret parse_continue_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Comptime) { ret parse_comptime_stmt(self); }
|
.Defer => { ret parse_defer_stmt(self); },
|
||||||
if @enum2int(k) == @enum2int(Keyword.Var) ||
|
.Errdefer => { ret parse_errdefer_stmt(self); },
|
||||||
@enum2int(k) == @enum2int(Keyword.Const) ||
|
.Comptime => { ret parse_comptime_stmt(self); },
|
||||||
@enum2int(k) == @enum2int(Keyword.Type) {
|
.Var, .Const, .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) {
|
match k {
|
||||||
decl = parse_var_const_decl(self, tags, false);
|
.Var => { decl = parse_var_const_decl(self, tags, false); },
|
||||||
} else if @enum2int(k) == @enum2int(Keyword.Const) {
|
.Const => { decl = parse_var_const_decl(self, tags, true); },
|
||||||
decl = parse_var_const_decl(self, tags, true);
|
_ => { decl = parse_type_decl(self, tags); },
|
||||||
} else {
|
|
||||||
decl = parse_type_decl(self, tags);
|
|
||||||
}
|
}
|
||||||
if decl == 0 { ret 0; }
|
if decl == 0 { ret 0; }
|
||||||
ret new_node(self, .StatementDecl(.{ .decl = decl }), t);
|
ret new_node(self, .StatementDecl(.{ .decl = decl }), t);
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -2099,7 +2145,7 @@ fn block_push_stmt(self: *Parser, ref: usize, stmt: usize) {
|
|||||||
var n = Ast.node(self.ast, ref);
|
var n = Ast.node(self.ast, ref);
|
||||||
match n.body {
|
match n.body {
|
||||||
.ExprBlock(b) => {
|
.ExprBlock(b) => {
|
||||||
match Vec.push(&b.stmts, &stmt as *u8) { .Ok => {}, .Err(e) => {} }
|
Vec.push(&b.stmts, &stmt as *u8);
|
||||||
n.body = .ExprBlock(b);
|
n.body = .ExprBlock(b);
|
||||||
},
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
@@ -2120,7 +2166,6 @@ fn block_set_tail(self: *Parser, ref: usize, expr: usize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 入口 ----
|
|
||||||
|
|
||||||
fn skip_to_sync(self: *Parser) {
|
fn skip_to_sync(self: *Parser) {
|
||||||
while !self.failed {
|
while !self.failed {
|
||||||
@@ -2135,7 +2180,7 @@ fn skip_to_sync(self: *Parser) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析整个文件;Root/ContainerMembers 最后分配(与 stage1 一致)。
|
// 解析整个文件;Root/ContainerMembers 最后分配(#ref 顺序须与 splc0 一致)。
|
||||||
fn parse(self: *Parser) {
|
fn parse(self: *Parser) {
|
||||||
var external_tags = ref_vec();
|
var external_tags = ref_vec();
|
||||||
var declarations = ref_vec();
|
var declarations = ref_vec();
|
||||||
@@ -2151,7 +2196,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2178,7 +2223,6 @@ fn parse(self: *Parser) {
|
|||||||
self.ast.root = root;
|
self.ast.root = root;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn lex_tokens(src: []u8): Vec {
|
fn lex_tokens(src: []u8): Vec {
|
||||||
var out = Vec.blank(@sizeof(Token), @alignof(Token));
|
var out = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||||
@@ -2242,7 +2286,7 @@ fn expect_text(buf: *ByteBuffer, expected: []u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 与 stage1 `splc0 --dump ast` 输出逐字节一致(含 #ref 与 [line:col])。
|
// 与 splc0 --dump ast 逐字节一致(含 #ref 与 [line:col])。
|
||||||
fn test_parse_decls() {
|
fn test_parse_decls() {
|
||||||
var src: []u8 = "type Point = struct { x: i32, y: i32 };\nfn f(a: i32): i32;\n";
|
var src: []u8 = "type Point = struct { x: i32, y: i32 };\nfn f(a: i32): i32;\n";
|
||||||
var toks = lex_tokens(src);
|
var toks = lex_tokens(src);
|
||||||
@@ -2274,7 +2318,7 @@ fn test_parse_decls() {
|
|||||||
"AST:\n`-root #13\n `-container_members #12\n |-type_decl #2 [1:1]\n | |-pattern_ident #1 [1:6]\n | `-type_aggregate #3 [1:14]\n | |-member_decl #5 [1:23]\n | | |-type_path #4 [1:26]\n | `-member_decl #7 [1:31]\n | |-type_path #6 [1:34]\n `-fn_decl #11 [2:1]\n `-param_decl #9 [2:6]\n `-type_path #8 [2:9]\n |-type_path #10 [2:15]\n");
|
"AST:\n`-root #13\n `-container_members #12\n |-type_decl #2 [1:1]\n | |-pattern_ident #1 [1:6]\n | `-type_aggregate #3 [1:14]\n | |-member_decl #5 [1:23]\n | | |-type_path #4 [1:26]\n | `-member_decl #7 [1:31]\n | |-type_path #6 [1:34]\n `-fn_decl #11 [2:1]\n `-param_decl #9 [2:6]\n `-type_path #8 [2:9]\n |-type_path #10 [2:15]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 表达式/语句/块:与 001_main.spl 的 stage1 dump 逐字节一致。
|
// 表达式/语句/块:与 splc0 dump 逐字节一致。
|
||||||
fn test_parse_main() {
|
fn test_parse_main() {
|
||||||
var src: []u8 = "// Expect retcode = 0\n\nfn main(): i32 {\n ret 0;\n}\n";
|
var src: []u8 = "// Expect retcode = 0\n\nfn main(): i32 {\n ret 0;\n}\n";
|
||||||
var toks = lex_tokens(src);
|
var toks = lex_tokens(src);
|
||||||
@@ -2306,7 +2350,7 @@ fn test_parse_main() {
|
|||||||
"AST:\n`-root #7\n `-container_members #6\n `-fn_decl #5 [3:1]\n |-type_path #1 [3:12]\n `-block #2 [3:16]\n `-statement_ret #4 [4:5]\n `-int_lit #3 [4:9]\n");
|
"AST:\n`-root #7\n `-container_members #6\n `-fn_decl #5 [3:1]\n |-type_path #1 [3:12]\n `-block #2 [3:16]\n `-statement_ret #4 [4:5]\n `-int_lit #3 [4:9]\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 任意宽度整数:2^64 也能正确解析(stage1 u64 之外);含分隔符与多进制。
|
// 任意宽度整数:2^64 也能正确解析;含分隔符与多进制。
|
||||||
fn check_int(src: []u8, expected: []u8) {
|
fn check_int(src: []u8, expected: []u8) {
|
||||||
var toks = lex_tokens(src);
|
var toks = lex_tokens(src);
|
||||||
defer { Vec.drop(&toks); }
|
defer { Vec.drop(&toks); }
|
||||||
|
|||||||
+2
-5
@@ -1,9 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 作用域树(移植自 stage1/spl_scope.c)。每个作用域是一张名字 -> Symbol 表,
|
// 作用域树:名字 -> Symbol,节点经 parent 上溯查找(支持遮蔽)。名字借用源字节,
|
||||||
// 节点用 parent 指向父作用域;查找从 current_scope 沿父链上溯(支持遮蔽)。
|
// 存活由调用方保证;node 为 def.spl 的 DefId。
|
||||||
// 名字是 ByteView(借用源/AST);Map 复制键值字节,但 ByteView 指向的字节由
|
|
||||||
// 调用方保证存活。node 字段是 def.spl 的 DefId。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -134,7 +132,6 @@ fn find(self: *Scope, name: ByteView, out: *Symbol): bool {
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn sym(name: []u8, kind: SymbolKind, node: usize): Symbol {
|
fn sym(name: []u8, kind: SymbolKind, node: usize): Symbol {
|
||||||
ret Symbol {
|
ret Symbol {
|
||||||
|
|||||||
+247
-245
File diff suppressed because it is too large.
Load diff
+25
-42
@@ -1,8 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// splc1 (stage2 自举编译器入口)。
|
// splc1:自举编译器入口。`splc1 <in> <out>` 编译;`--dump <mode> <file>` 打印各阶段
|
||||||
// 当前实现 `--dump <lex|ast|sema|ir|vm> <file> [out]`:读取源文件,按与 stage1
|
// 产物,格式必须与 splc0 逐字节一致以通过 diff。
|
||||||
// splc0 完全一致的格式打印各阶段产物,供 diff 测试逐字节比对。
|
|
||||||
|
|
||||||
type .{ .ByteView } = @import("./utils/byte_view.spl");
|
type .{ .ByteView } = @import("./utils/byte_view.spl");
|
||||||
type .{ .ByteBuffer } = @import("./utils/byte_buffer.spl");
|
type .{ .ByteBuffer } = @import("./utils/byte_buffer.spl");
|
||||||
@@ -13,8 +12,7 @@ type Ast = @import("./ast.spl");
|
|||||||
type Parser = @import("./parser.spl");
|
type Parser = @import("./parser.spl");
|
||||||
type Sema = @import("./sema.spl");
|
type Sema = @import("./sema.spl");
|
||||||
type .{ .append_usize } = @import("./utils/fmt.spl");
|
type .{ .append_usize } = @import("./utils/fmt.spl");
|
||||||
type .{ .read_all, .print, .eprint, .open_cstr, .write_all } = @import("./utils/io.spl");
|
type .{ .read_all, .print, .eprint, .open_cstr, .write_all, .File } = @import("./utils/io.spl");
|
||||||
type File = @import("./sys/file.spl");
|
|
||||||
type .{ .Sources, .Source, .set_new, .set_add, .set_at, .set_drop } = @import("./utils/source.spl");
|
type .{ .Sources, .Source, .set_new, .set_add, .set_at, .set_drop } = @import("./utils/source.spl");
|
||||||
type .{ .Diag, .Code, .span_none } = @import("./diag.spl");
|
type .{ .Diag, .Code, .span_none } = @import("./diag.spl");
|
||||||
type Logger = @import("./utils/log.spl");
|
type Logger = @import("./utils/log.spl");
|
||||||
@@ -27,7 +25,7 @@ type .{ .Layout } = @import("./layout.spl");
|
|||||||
type Ir2VmM = @import("./ir2vm.spl");
|
type Ir2VmM = @import("./ir2vm.spl");
|
||||||
type .{ .Ir2Vm } = @import("./ir2vm.spl");
|
type .{ .Ir2Vm } = @import("./ir2vm.spl");
|
||||||
type Ir2CM = @import("./ir2c.spl");
|
type Ir2CM = @import("./ir2c.spl");
|
||||||
type .{ .alloc, .free } = @import("./sys/mem.spl");
|
type .{ .alloc, .free } = @import("./utils/memory.spl");
|
||||||
|
|
||||||
fn token_at(tokens: *Vec, index: usize): Token {
|
fn token_at(tokens: *Vec, index: usize): Token {
|
||||||
match Vec.get(tokens, index) {
|
match Vec.get(tokens, index) {
|
||||||
@@ -131,14 +129,11 @@ fn dump_lex(log: *Logger, path: ByteView): i32 {
|
|||||||
}
|
}
|
||||||
defer { Vec.drop(&tokens); }
|
defer { Vec.drop(&tokens); }
|
||||||
|
|
||||||
// 非法输入:走诊断(stage1 在此处是宽容无视,stage2 显式报错)。
|
// 非法输入:走诊断(显式报错,不静默)。
|
||||||
if Diag.has_error(diag) {
|
if Diag.has_error(diag) {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) {
|
Diag.render_human(&diag, &rendered);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -200,10 +195,7 @@ fn dump_ast(log: *Logger, path: ByteView): i32 {
|
|||||||
if Diag.has_error(diag) || p.failed {
|
if Diag.has_error(diag) || p.failed {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) {
|
Diag.render_human(&diag, &rendered);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -221,7 +213,6 @@ fn dump_ast(log: *Logger, path: ByteView): i32 {
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- @import 模块加载器(driver 侧)----
|
|
||||||
|
|
||||||
type ModEntry = struct {
|
type ModEntry = struct {
|
||||||
path: ByteView,
|
path: ByteView,
|
||||||
@@ -236,7 +227,6 @@ type LoaderCtx = struct {
|
|||||||
diag: *Diag,
|
diag: *Diag,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_bool(b: bool) {}
|
|
||||||
|
|
||||||
fn append_bytes(buf: *ByteBuffer, bytes: []u8): bool {
|
fn append_bytes(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
match ByteBuffer.append(buf, bytes) {
|
match ByteBuffer.append(buf, bytes) {
|
||||||
@@ -263,7 +253,7 @@ fn sub(p: ByteView, a: usize, b: usize): []u8 {
|
|||||||
fn normalize_pop(out: *ByteBuffer) {
|
fn normalize_pop(out: *ByteBuffer) {
|
||||||
var cur = ByteBuffer.as_slice(out.*);
|
var cur = ByteBuffer.as_slice(out.*);
|
||||||
if cur.len == 0 {
|
if cur.len == 0 {
|
||||||
ignore_bool(append_bytes(out, ".."));
|
append_bytes(out, "..");
|
||||||
ret;
|
ret;
|
||||||
}
|
}
|
||||||
var end = cur.len;
|
var end = cur.len;
|
||||||
@@ -274,7 +264,7 @@ fn normalize_pop(out: *ByteBuffer) {
|
|||||||
var start = end;
|
var start = end;
|
||||||
while start > 0 && cur.ptr[start - 1] != '/' { start = start - 1; }
|
while start > 0 && cur.ptr[start - 1] != '/' { start = start - 1; }
|
||||||
if end - start == 2 && cur.ptr[start] == '.' && cur.ptr[start + 1] == '.' {
|
if end - start == 2 && cur.ptr[start] == '.' && cur.ptr[start + 1] == '.' {
|
||||||
ignore_bool(append_bytes(out, ".."));
|
append_bytes(out, "..");
|
||||||
ret;
|
ret;
|
||||||
}
|
}
|
||||||
var new_len: usize = 0;
|
var new_len: usize = 0;
|
||||||
@@ -286,7 +276,7 @@ fn normalize_pop(out: *ByteBuffer) {
|
|||||||
// 折叠重复分隔符、解析 "." 与 ".."、'\' 统一成 '/'。
|
// 折叠重复分隔符、解析 "." 与 ".."、'\' 统一成 '/'。
|
||||||
fn normalize_into(out: *ByteBuffer, p: ByteView) {
|
fn normalize_into(out: *ByteBuffer, p: ByteView) {
|
||||||
if p.len > 0 && (p.ptr[0] == '/' || p.ptr[0] == '\\') {
|
if p.len > 0 && (p.ptr[0] == '/' || p.ptr[0] == '\\') {
|
||||||
ignore_bool(append_byte(out, '/'));
|
append_byte(out, '/');
|
||||||
}
|
}
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while i < p.len {
|
while i < p.len {
|
||||||
@@ -302,9 +292,9 @@ fn normalize_into(out: *ByteBuffer, p: ByteView) {
|
|||||||
}
|
}
|
||||||
var cur = ByteBuffer.as_slice(out.*);
|
var cur = ByteBuffer.as_slice(out.*);
|
||||||
if cur.len > 0 && cur.ptr[cur.len - 1] != '/' {
|
if cur.len > 0 && cur.ptr[cur.len - 1] != '/' {
|
||||||
ignore_bool(append_byte(out, '/'));
|
append_byte(out, '/');
|
||||||
}
|
}
|
||||||
ignore_bool(append_bytes(out, seg));
|
append_bytes(out, seg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,9 +315,9 @@ fn resolve_path(from: ByteView, path: ByteView): ByteBuffer {
|
|||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
if last >= 0 {
|
if last >= 0 {
|
||||||
ignore_bool(append_bytes(&out, sub(from, 0, (last as usize) + 1)));
|
append_bytes(&out, sub(from, 0, (last as usize) + 1));
|
||||||
}
|
}
|
||||||
ignore_bool(append_bytes(&out, ByteView.as_slice(path)));
|
append_bytes(&out, ByteView.as_slice(path));
|
||||||
var norm = ByteBuffer.new();
|
var norm = ByteBuffer.new();
|
||||||
normalize_into(&norm, ByteView.from_slice(ByteBuffer.as_slice(out)));
|
normalize_into(&norm, ByteView.from_slice(ByteBuffer.as_slice(out)));
|
||||||
ByteBuffer.drop(&out);
|
ByteBuffer.drop(&out);
|
||||||
@@ -504,10 +494,7 @@ fn dump_sema(log: *Logger, path: ByteView): i32 {
|
|||||||
if Diag.has_error(diag) || p.failed {
|
if Diag.has_error(diag) || p.failed {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) {
|
Diag.render_human(&diag, &rendered);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -538,10 +525,7 @@ fn dump_sema(log: *Logger, path: ByteView): i32 {
|
|||||||
if Diag.has_error(diag) {
|
if Diag.has_error(diag) {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) {
|
Diag.render_human(&diag, &rendered);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -573,7 +557,7 @@ type PipeMode = enum {
|
|||||||
|
|
||||||
fn step(mode: PipeMode, s: []u8) {
|
fn step(mode: PipeMode, s: []u8) {
|
||||||
if @enum2int(mode) == @enum2int(PipeMode.State) {
|
if @enum2int(mode) == @enum2int(PipeMode.State) {
|
||||||
match eprint(s) { .Ok => {}, .Err(error) => {} }
|
eprint(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,7 +615,7 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
|||||||
if Diag.has_error(diag) || p.failed {
|
if Diag.has_error(diag) || p.failed {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) { .Ok => {}, .Err(error) => {} }
|
Diag.render_human(&diag, &rendered);
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -662,7 +646,7 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
|||||||
if Diag.has_error(diag) {
|
if Diag.has_error(diag) {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) { .Ok => {}, .Err(error) => {} }
|
Diag.render_human(&diag, &rendered);
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -677,13 +661,13 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
|||||||
var asts = Vec.blank(@sizeof(usize), @alignof(usize));
|
var asts = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||||
defer { Vec.drop(&asts); }
|
defer { Vec.drop(&asts); }
|
||||||
var ap: *Ast = *
|
var ap: *Ast = *
|
||||||
match Vec.push(&asts, &ap as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&asts, &ap as *u8);
|
||||||
var mi: usize = 0;
|
var mi: usize = 0;
|
||||||
while mi < ctx.mods.len {
|
while mi < ctx.mods.len {
|
||||||
var m = mod_at(&ctx, mi);
|
var m = mod_at(&ctx, mi);
|
||||||
if m != null && m.ok && m.ast != null {
|
if m != null && m.ok && m.ast != null {
|
||||||
var mp: *Ast = m.ast;
|
var mp: *Ast = m.ast;
|
||||||
match Vec.push(&asts, &mp as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&asts, &mp as *u8);
|
||||||
}
|
}
|
||||||
mi = mi + 1;
|
mi = mi + 1;
|
||||||
}
|
}
|
||||||
@@ -692,7 +676,7 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
|||||||
if Diag.has_error(diag) {
|
if Diag.has_error(diag) {
|
||||||
var rendered = ByteBuffer.new();
|
var rendered = ByteBuffer.new();
|
||||||
defer { ByteBuffer.drop(&rendered); }
|
defer { ByteBuffer.drop(&rendered); }
|
||||||
match Diag.render_human(&diag, &rendered) { .Ok => {}, .Err(error) => {} }
|
Diag.render_human(&diag, &rendered);
|
||||||
eprint(ByteBuffer.as_slice(rendered));
|
eprint(ByteBuffer.as_slice(rendered));
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
@@ -736,8 +720,8 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
|||||||
Logger.error(log, "cannot open C output file");
|
Logger.error(log, "cannot open C output file");
|
||||||
ret 1;
|
ret 1;
|
||||||
}
|
}
|
||||||
match write_all(&f, ByteBuffer.as_slice(cout)) { .Ok => {}, .Err(error) => {} }
|
write_all(&f, ByteBuffer.as_slice(cout));
|
||||||
ignore_i32(File.close(&f));
|
File.close(&f);
|
||||||
} else {
|
} else {
|
||||||
match print(ByteBuffer.as_slice(cout)) {
|
match print(ByteBuffer.as_slice(cout)) {
|
||||||
.Ok => {},
|
.Ok => {},
|
||||||
@@ -754,7 +738,7 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
|||||||
var ir2vm: Ir2Vm = undefined;
|
var ir2vm: Ir2Vm = undefined;
|
||||||
Ir2VmM.init(&ir2vm, &builder.ir, &sema.types.ir, &layout, &sema.attr);
|
Ir2VmM.init(&ir2vm, &builder.ir, &sema.types.ir, &layout, &sema.attr);
|
||||||
defer { Ir2VmM.drop(&ir2vm); }
|
defer { Ir2VmM.drop(&ir2vm); }
|
||||||
ignore_i32(Ir2VmM.run(&ir2vm));
|
Ir2VmM.run(&ir2vm);
|
||||||
step(mode, "state: ir2vm ok\n");
|
step(mode, "state: ir2vm ok\n");
|
||||||
|
|
||||||
if @enum2int(mode) == @enum2int(PipeMode.Vm) {
|
if @enum2int(mode) == @enum2int(PipeMode.Vm) {
|
||||||
@@ -782,7 +766,6 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
|||||||
ret 0;
|
ret 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_i32(v: i32) {}
|
|
||||||
|
|
||||||
fn main(argc: i32, argv: **u8): i32 {
|
fn main(argc: i32, argv: **u8): i32 {
|
||||||
// 无参数:自测运行(`nob test stage2`)视为成功。
|
// 无参数:自测运行(`nob test stage2`)视为成功。
|
||||||
|
|||||||
@@ -1,629 +0,0 @@
|
|||||||
/* spl_rt.h - SPL native runtime (header-only, C89-friendly)
|
|
||||||
*
|
|
||||||
* 提供 ir2c 生成代码所需的所有 `#extern(vm)` 符号,以及 SPL 基础类型别名。
|
|
||||||
* 目标:生成代码可用 C89 语法通过(不依赖 stdint/stdbool)。
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __SPL_RT_H__
|
|
||||||
#define __SPL_RT_H__
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
/* 语义清晰的 inline:C99 用 static inline,GNU 用 __inline__,C89 退化为 static */
|
|
||||||
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
|
||||||
#define SPL_INLINE static inline
|
|
||||||
#elif defined(__GNUC__) || defined(__clang__)
|
|
||||||
#define SPL_INLINE static __inline__
|
|
||||||
#else
|
|
||||||
#define SPL_INLINE static
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ---- SPL 基础类型别名 ---- */
|
|
||||||
typedef unsigned char u8;
|
|
||||||
typedef signed char i8;
|
|
||||||
typedef unsigned short u16;
|
|
||||||
typedef signed short i16;
|
|
||||||
typedef unsigned int u32;
|
|
||||||
typedef signed int i32;
|
|
||||||
typedef unsigned long long u64;
|
|
||||||
typedef signed long long i64;
|
|
||||||
typedef u64 usize;
|
|
||||||
typedef i64 isize;
|
|
||||||
/* 注:不定义 bool/true/false,避免与 C23 关键字冲突;生成代码用 u8 表示布尔。 */
|
|
||||||
|
|
||||||
/* ---- panic / trap ---- */
|
|
||||||
SPL_INLINE void spl_panic(const char *msg) {
|
|
||||||
fprintf(stderr, "%s\n", msg ? msg : "panic");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
SPL_INLINE void spl_trap(void) { abort(); }
|
|
||||||
|
|
||||||
/* ---- mem ---- */
|
|
||||||
SPL_INLINE void *vm_alloc(u64 sz) {
|
|
||||||
if (sz == 0)
|
|
||||||
return NULL;
|
|
||||||
return malloc((size_t)sz);
|
|
||||||
}
|
|
||||||
SPL_INLINE void vm_free(void *p) {
|
|
||||||
if (p)
|
|
||||||
free(p);
|
|
||||||
}
|
|
||||||
SPL_INLINE void *vm_realloc(void *p, u64 sz) {
|
|
||||||
if (!p)
|
|
||||||
return vm_alloc(sz);
|
|
||||||
if (sz == 0) {
|
|
||||||
free(p);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return realloc(p, (size_t)sz);
|
|
||||||
}
|
|
||||||
SPL_INLINE void *vm_memcpy(void *dst, const void *src, u64 n) {
|
|
||||||
if (n)
|
|
||||||
memcpy(dst, src, (size_t)n);
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
SPL_INLINE void *vm_memmove(void *dst, const void *src, u64 n) {
|
|
||||||
if (n)
|
|
||||||
memmove(dst, src, (size_t)n);
|
|
||||||
return dst;
|
|
||||||
}
|
|
||||||
SPL_INLINE i64 vm_memcmp(const void *a, const void *b, u64 n) {
|
|
||||||
if (n == 0)
|
|
||||||
return 0;
|
|
||||||
return (i64)memcmp(a, b, (size_t)n);
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_bytes_eq(const void *a, u64 alen, const void *b, u64 blen) {
|
|
||||||
if (alen != blen)
|
|
||||||
return 0;
|
|
||||||
if (alen == 0)
|
|
||||||
return 1;
|
|
||||||
if (!a || !b)
|
|
||||||
return 0;
|
|
||||||
return memcmp(a, b, (size_t)alen) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Vec ---- */
|
|
||||||
typedef struct {
|
|
||||||
usize raw;
|
|
||||||
usize data;
|
|
||||||
usize len;
|
|
||||||
usize cap;
|
|
||||||
usize elem_size;
|
|
||||||
usize elem_align;
|
|
||||||
} spl_vec_t;
|
|
||||||
|
|
||||||
SPL_INLINE int spl_vec_is_valid(const spl_vec_t *v) {
|
|
||||||
usize a;
|
|
||||||
if (v->elem_size == 0)
|
|
||||||
return 0;
|
|
||||||
a = v->elem_align;
|
|
||||||
if (a == 0 || (a & (a - 1)) != 0)
|
|
||||||
return 0;
|
|
||||||
if (v->len > v->cap)
|
|
||||||
return 0;
|
|
||||||
if (v->cap == 0)
|
|
||||||
return v->raw == 0 && v->data == 0 && v->len == 0;
|
|
||||||
if (v->raw == 0 || v->data == 0)
|
|
||||||
return 0;
|
|
||||||
return (v->data & (a - 1)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPL_INLINE int spl_align_ok(usize a) { return a != 0 && (a & (a - 1)) == 0; }
|
|
||||||
|
|
||||||
SPL_INLINE void spl_grow(usize current, usize need, usize *out) {
|
|
||||||
usize cap = current < 8 ? 8 : current;
|
|
||||||
usize limit = ~(usize)0;
|
|
||||||
while (cap < need) {
|
|
||||||
if (cap > limit / 2) {
|
|
||||||
cap = need;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cap = cap * 2;
|
|
||||||
}
|
|
||||||
*out = cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPL_INLINE int spl_vec_allocate(spl_vec_t *out, usize capacity, usize elem_size, usize elem_align) {
|
|
||||||
usize bytes, mask, addr;
|
|
||||||
void *raw;
|
|
||||||
if (elem_size == 0)
|
|
||||||
return 1;
|
|
||||||
if (!spl_align_ok(elem_align))
|
|
||||||
return 4;
|
|
||||||
out->raw = 0;
|
|
||||||
out->data = 0;
|
|
||||||
out->len = 0;
|
|
||||||
out->cap = 0;
|
|
||||||
out->elem_size = elem_size;
|
|
||||||
out->elem_align = elem_align;
|
|
||||||
if (capacity == 0)
|
|
||||||
return 0;
|
|
||||||
if (capacity > ~(usize)0 / elem_size)
|
|
||||||
return 3;
|
|
||||||
bytes = capacity * elem_size;
|
|
||||||
mask = elem_align - 1;
|
|
||||||
if (bytes > ~(usize)0 - mask)
|
|
||||||
return 3;
|
|
||||||
raw = malloc(bytes + mask);
|
|
||||||
if (!raw)
|
|
||||||
return 3;
|
|
||||||
addr = (usize)raw;
|
|
||||||
if (addr > ~(usize)0 - mask) {
|
|
||||||
free(raw);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
out->raw = addr;
|
|
||||||
out->data = (addr + mask) & ~mask;
|
|
||||||
out->cap = capacity;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPL_INLINE int spl_vec_reserve_raw(spl_vec_t *v, usize need) {
|
|
||||||
usize new_cap = 0, used = 0, old;
|
|
||||||
spl_vec_t fresh;
|
|
||||||
int rc;
|
|
||||||
if (need <= v->cap)
|
|
||||||
return 0;
|
|
||||||
spl_grow(v->cap, need, &new_cap);
|
|
||||||
rc = spl_vec_allocate(&fresh, new_cap, v->elem_size, v->elem_align);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
if (v->len != 0) {
|
|
||||||
if (v->len > ~(usize)0 / v->elem_size) {
|
|
||||||
free((void *)fresh.raw);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
used = v->len * v->elem_size;
|
|
||||||
}
|
|
||||||
if (used)
|
|
||||||
memcpy((void *)fresh.data, (void *)v->data, used);
|
|
||||||
fresh.len = v->len;
|
|
||||||
old = v->raw;
|
|
||||||
*v = fresh;
|
|
||||||
if (old)
|
|
||||||
free((void *)old);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPL_INLINE void spl_vec_release(spl_vec_t *v) {
|
|
||||||
usize es = v->elem_size, ea = v->elem_align;
|
|
||||||
if (v->raw)
|
|
||||||
free((void *)v->raw);
|
|
||||||
v->raw = 0;
|
|
||||||
v->data = 0;
|
|
||||||
v->len = 0;
|
|
||||||
v->cap = 0;
|
|
||||||
v->elem_size = es;
|
|
||||||
v->elem_align = ea;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPL_INLINE i32 vm_vec_valid(void *vv) {
|
|
||||||
const spl_vec_t *v = (const spl_vec_t *)vv;
|
|
||||||
return v ? spl_vec_is_valid(v) : 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_push_raw(void *vv, const void *elem) {
|
|
||||||
spl_vec_t *v = (spl_vec_t *)vv;
|
|
||||||
if (!v || !elem)
|
|
||||||
return 0;
|
|
||||||
if (v->len < v->cap) {
|
|
||||||
memcpy((void *)(v->data + v->len * v->elem_size), elem, v->elem_size);
|
|
||||||
v->len++;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_get(void *vv, u64 index, void **out) {
|
|
||||||
const spl_vec_t *v = (const spl_vec_t *)vv;
|
|
||||||
if (!v || !out)
|
|
||||||
return 1;
|
|
||||||
if (!spl_vec_is_valid(v))
|
|
||||||
return 1;
|
|
||||||
if (index >= v->len)
|
|
||||||
return 2;
|
|
||||||
*out = (void *)(v->data + index * v->elem_size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_push(void *vv, const void *elem) {
|
|
||||||
spl_vec_t *v = (spl_vec_t *)vv;
|
|
||||||
int rc;
|
|
||||||
if (!v || !elem || !spl_vec_is_valid(v))
|
|
||||||
return 1;
|
|
||||||
if (v->len < v->cap) {
|
|
||||||
memcpy((void *)(v->data + v->len * v->elem_size), elem, v->elem_size);
|
|
||||||
v->len++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
rc = spl_vec_reserve_raw(v, v->len + 1);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
memmove((void *)(v->data + v->len * v->elem_size), elem, v->elem_size);
|
|
||||||
v->len++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_allocate(void *out, u64 capacity, u64 elem_size, u64 elem_align) {
|
|
||||||
return spl_vec_allocate((spl_vec_t *)out, capacity, elem_size, elem_align);
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_reserve_raw(void *vv, u64 need) {
|
|
||||||
spl_vec_t *v = (spl_vec_t *)vv;
|
|
||||||
if (!v)
|
|
||||||
return 1;
|
|
||||||
return spl_vec_reserve_raw(v, need);
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_append(void *vv, const void *elems, u64 count) {
|
|
||||||
spl_vec_t *v = (spl_vec_t *)vv;
|
|
||||||
usize copy_bytes, new_len, used, source_offset = 0;
|
|
||||||
int source_inside = 0, rc;
|
|
||||||
if (!v || !spl_vec_is_valid(v))
|
|
||||||
return 1;
|
|
||||||
if (count == 0)
|
|
||||||
return 0;
|
|
||||||
if (!elems)
|
|
||||||
return 1;
|
|
||||||
if (count > ~(usize)0 / v->elem_size)
|
|
||||||
return 3;
|
|
||||||
copy_bytes = count * v->elem_size;
|
|
||||||
if (v->len > ~(usize)0 - count)
|
|
||||||
return 3;
|
|
||||||
new_len = v->len + count;
|
|
||||||
used = v->len * v->elem_size;
|
|
||||||
if (used) {
|
|
||||||
usize base = v->data;
|
|
||||||
usize source = (usize)elems;
|
|
||||||
if (source >= base && source - base < used) {
|
|
||||||
source_offset = source - base;
|
|
||||||
if (copy_bytes > used - source_offset)
|
|
||||||
return 2;
|
|
||||||
source_inside = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rc = spl_vec_reserve_raw(v, new_len);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
if (source_inside)
|
|
||||||
elems = (const void *)(v->data + source_offset);
|
|
||||||
memmove((void *)(v->data + used), elems, copy_bytes);
|
|
||||||
v->len = new_len;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_set(void *vv, u64 index, const void *elem) {
|
|
||||||
spl_vec_t *v = (spl_vec_t *)vv;
|
|
||||||
if (!v || !elem || !spl_vec_is_valid(v))
|
|
||||||
return 1;
|
|
||||||
if (index >= v->len)
|
|
||||||
return 2;
|
|
||||||
memmove((void *)(v->data + index * v->elem_size), elem, v->elem_size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_pop(void *vv, void *out) {
|
|
||||||
spl_vec_t *v = (spl_vec_t *)vv;
|
|
||||||
if (!v || !out || !spl_vec_is_valid(v))
|
|
||||||
return 1;
|
|
||||||
if (v->len == 0)
|
|
||||||
return 2;
|
|
||||||
memmove(out, (void *)(v->data + (v->len - 1) * v->elem_size), v->elem_size);
|
|
||||||
v->len--;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_vec_clone(void *outp, const void *srcp) {
|
|
||||||
spl_vec_t *out = (spl_vec_t *)outp;
|
|
||||||
const spl_vec_t *src = (const spl_vec_t *)srcp;
|
|
||||||
spl_vec_t fresh;
|
|
||||||
usize bytes;
|
|
||||||
int rc;
|
|
||||||
if (!out || !src || !spl_vec_is_valid(out) || !spl_vec_is_valid(src))
|
|
||||||
return 1;
|
|
||||||
if (out->elem_size != src->elem_size || out->elem_align != src->elem_align)
|
|
||||||
return 1;
|
|
||||||
fresh.raw = 0;
|
|
||||||
fresh.data = 0;
|
|
||||||
fresh.len = 0;
|
|
||||||
fresh.cap = 0;
|
|
||||||
fresh.elem_size = src->elem_size;
|
|
||||||
fresh.elem_align = src->elem_align;
|
|
||||||
rc = spl_vec_reserve_raw(&fresh, src->len);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
bytes = src->len * src->elem_size;
|
|
||||||
if (bytes)
|
|
||||||
memcpy((void *)fresh.data, (void *)src->data, bytes);
|
|
||||||
fresh.len = src->len;
|
|
||||||
if (out->raw)
|
|
||||||
free((void *)out->raw);
|
|
||||||
*out = fresh;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE void vm_vec_drop(void *vv) {
|
|
||||||
spl_vec_t *v = (spl_vec_t *)vv;
|
|
||||||
if (v)
|
|
||||||
spl_vec_release(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- Map ---- */
|
|
||||||
typedef struct {
|
|
||||||
spl_vec_t keys;
|
|
||||||
spl_vec_t vals;
|
|
||||||
spl_vec_t states;
|
|
||||||
usize size;
|
|
||||||
usize key_size;
|
|
||||||
usize val_size;
|
|
||||||
usize hash_kind;
|
|
||||||
} spl_map_t;
|
|
||||||
|
|
||||||
SPL_INLINE usize spl_hash_usize_value(usize value) {
|
|
||||||
usize prime = 1099511628211ull;
|
|
||||||
usize h = value;
|
|
||||||
h = (h ^ (h >> 30)) * prime;
|
|
||||||
h = h ^ (h >> 27);
|
|
||||||
h = h * prime;
|
|
||||||
h = h ^ (h >> 31);
|
|
||||||
if (h == 0)
|
|
||||||
return prime;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
SPL_INLINE usize spl_hash_bytes(const void *data, usize len) {
|
|
||||||
const unsigned char *p = (const unsigned char *)data;
|
|
||||||
usize h = 14695981039346656037ull;
|
|
||||||
usize prime = 1099511628211ull;
|
|
||||||
usize i;
|
|
||||||
for (i = 0; i < len; i++)
|
|
||||||
h = (h ^ (usize)p[i]) * prime;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
SPL_INLINE usize spl_map_hash(const spl_map_t *m, const void *key) {
|
|
||||||
const usize *v;
|
|
||||||
if (m->hash_kind == 0)
|
|
||||||
return spl_hash_usize_value(*(const usize *)key);
|
|
||||||
if (m->hash_kind == 2)
|
|
||||||
return 3;
|
|
||||||
v = (const usize *)key;
|
|
||||||
return spl_hash_bytes((const void *)v[0], v[1]);
|
|
||||||
}
|
|
||||||
SPL_INLINE int spl_map_eq(const spl_map_t *m, const void *a, const void *b) {
|
|
||||||
const usize *x;
|
|
||||||
const usize *y;
|
|
||||||
if (m->hash_kind == 0 || m->hash_kind == 2)
|
|
||||||
return *(const usize *)a == *(const usize *)b;
|
|
||||||
x = (const usize *)a;
|
|
||||||
y = (const usize *)b;
|
|
||||||
if (x[1] != y[1])
|
|
||||||
return 0;
|
|
||||||
if (x[1] == 0)
|
|
||||||
return 1;
|
|
||||||
return memcmp((const void *)x[0], (const void *)y[0], x[1]) == 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE int spl_map_valid(const spl_map_t *m) {
|
|
||||||
if (m->key_size == 0 || m->val_size == 0)
|
|
||||||
return 0;
|
|
||||||
if (m->keys.len != m->states.len || m->vals.len != m->states.len)
|
|
||||||
return 0;
|
|
||||||
if (m->size > m->states.len)
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
SPL_INLINE usize spl_map_find_slot(const spl_map_t *m, const void *key) {
|
|
||||||
usize mask, i, probes;
|
|
||||||
unsigned char *st;
|
|
||||||
if (m->states.cap == 0)
|
|
||||||
return ~(usize)0;
|
|
||||||
mask = m->states.cap - 1;
|
|
||||||
i = spl_map_hash(m, key) & mask;
|
|
||||||
st = (unsigned char *)m->states.data;
|
|
||||||
probes = 0;
|
|
||||||
while (probes < m->states.cap && st[i] != 0) {
|
|
||||||
if (st[i] == 1 && spl_map_eq(m, (const void *)(m->keys.data + i * m->key_size), key))
|
|
||||||
return i;
|
|
||||||
i = (i + 1) & mask;
|
|
||||||
probes++;
|
|
||||||
}
|
|
||||||
return ~(usize)0;
|
|
||||||
}
|
|
||||||
SPL_INLINE void spl_map_place(spl_map_t *m, const void *key, const void *val) {
|
|
||||||
usize mask, i, first_deleted, probes;
|
|
||||||
unsigned char *st;
|
|
||||||
mask = m->states.cap - 1;
|
|
||||||
i = spl_map_hash(m, key) & mask;
|
|
||||||
st = (unsigned char *)m->states.data;
|
|
||||||
first_deleted = ~(usize)0;
|
|
||||||
probes = 0;
|
|
||||||
while (probes < m->states.cap && st[i] != 0) {
|
|
||||||
if (st[i] == 2 && first_deleted == ~(usize)0)
|
|
||||||
first_deleted = i;
|
|
||||||
i = (i + 1) & mask;
|
|
||||||
probes++;
|
|
||||||
}
|
|
||||||
if (first_deleted != ~(usize)0)
|
|
||||||
i = first_deleted;
|
|
||||||
memcpy((void *)(m->keys.data + i * m->key_size), key, m->key_size);
|
|
||||||
memcpy((void *)(m->vals.data + i * m->val_size), val, m->val_size);
|
|
||||||
st[i] = 1;
|
|
||||||
m->size++;
|
|
||||||
}
|
|
||||||
SPL_INLINE int spl_map_reserve(spl_map_t *m, usize need) {
|
|
||||||
usize new_cap = 0, i;
|
|
||||||
spl_map_t fresh;
|
|
||||||
int rc;
|
|
||||||
if (!spl_map_valid(m))
|
|
||||||
return 1;
|
|
||||||
if (need <= m->states.cap)
|
|
||||||
return 0;
|
|
||||||
spl_grow(m->states.cap, need, &new_cap);
|
|
||||||
fresh = *m;
|
|
||||||
fresh.keys.raw = 0;
|
|
||||||
fresh.keys.data = 0;
|
|
||||||
fresh.keys.len = 0;
|
|
||||||
fresh.keys.cap = 0;
|
|
||||||
fresh.vals.raw = 0;
|
|
||||||
fresh.vals.data = 0;
|
|
||||||
fresh.vals.len = 0;
|
|
||||||
fresh.vals.cap = 0;
|
|
||||||
fresh.states.raw = 0;
|
|
||||||
fresh.states.data = 0;
|
|
||||||
fresh.states.len = 0;
|
|
||||||
fresh.states.cap = 0;
|
|
||||||
fresh.size = 0;
|
|
||||||
rc = spl_vec_reserve_raw(&fresh.keys, new_cap);
|
|
||||||
if (rc)
|
|
||||||
goto fail;
|
|
||||||
rc = spl_vec_reserve_raw(&fresh.vals, new_cap);
|
|
||||||
if (rc)
|
|
||||||
goto fail;
|
|
||||||
rc = spl_vec_reserve_raw(&fresh.states, new_cap);
|
|
||||||
if (rc)
|
|
||||||
goto fail;
|
|
||||||
fresh.keys.len = new_cap;
|
|
||||||
fresh.vals.len = new_cap;
|
|
||||||
fresh.states.len = new_cap;
|
|
||||||
memset((void *)fresh.states.data, 0, new_cap);
|
|
||||||
for (i = 0; i < m->states.len; i++) {
|
|
||||||
if (((unsigned char *)m->states.data)[i] == 1)
|
|
||||||
spl_map_place(&fresh, (const void *)(m->keys.data + i * m->key_size),
|
|
||||||
(const void *)(m->vals.data + i * m->val_size));
|
|
||||||
}
|
|
||||||
spl_vec_release(&m->keys);
|
|
||||||
spl_vec_release(&m->vals);
|
|
||||||
spl_vec_release(&m->states);
|
|
||||||
*m = fresh;
|
|
||||||
return 0;
|
|
||||||
fail:
|
|
||||||
spl_vec_release(&fresh.keys);
|
|
||||||
spl_vec_release(&fresh.vals);
|
|
||||||
spl_vec_release(&fresh.states);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_map_valid(void *mm) {
|
|
||||||
const spl_map_t *m = (const spl_map_t *)mm;
|
|
||||||
return m ? spl_map_valid(m) : 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_map_reserve(void *mm, u64 need) {
|
|
||||||
spl_map_t *m = (spl_map_t *)mm;
|
|
||||||
if (!m)
|
|
||||||
return 1;
|
|
||||||
return spl_map_reserve(m, need);
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_map_put(void *mm, const void *key, const void *val) {
|
|
||||||
spl_map_t *m = (spl_map_t *)mm;
|
|
||||||
usize existing;
|
|
||||||
if (!m || !key || !val || !spl_map_valid(m))
|
|
||||||
return 1;
|
|
||||||
existing = spl_map_find_slot(m, key);
|
|
||||||
if (existing != ~(usize)0) {
|
|
||||||
memcpy((void *)(m->vals.data + existing * m->val_size), val, m->val_size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (m->states.cap == 0) {
|
|
||||||
int rc = spl_map_reserve(m, 8);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
} else {
|
|
||||||
usize next_size, scaled;
|
|
||||||
if (m->size > ~(usize)0 - 1)
|
|
||||||
return 3;
|
|
||||||
next_size = m->size + 1;
|
|
||||||
if (next_size > ~(usize)0 / 128)
|
|
||||||
return 3;
|
|
||||||
scaled = next_size * 128;
|
|
||||||
if (scaled / m->states.cap >= 70) {
|
|
||||||
int rc;
|
|
||||||
if (m->states.cap > ~(usize)0 / 2)
|
|
||||||
return 3;
|
|
||||||
rc = spl_map_reserve(m, m->states.cap * 2);
|
|
||||||
if (rc)
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spl_map_place(m, key, val);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_map_get(void *mm, const void *key, void *out) {
|
|
||||||
spl_map_t *m = (spl_map_t *)mm;
|
|
||||||
usize i;
|
|
||||||
if (!m || !key || !out || !spl_map_valid(m))
|
|
||||||
return 1;
|
|
||||||
i = spl_map_find_slot(m, key);
|
|
||||||
if (i == ~(usize)0)
|
|
||||||
return 1;
|
|
||||||
memcpy(out, (void *)(m->vals.data + i * m->val_size), m->val_size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_map_del(void *mm, const void *key) {
|
|
||||||
spl_map_t *m = (spl_map_t *)mm;
|
|
||||||
usize i;
|
|
||||||
if (!m || !key || !spl_map_valid(m))
|
|
||||||
return 1;
|
|
||||||
i = spl_map_find_slot(m, key);
|
|
||||||
if (i == ~(usize)0)
|
|
||||||
return 1;
|
|
||||||
((unsigned char *)m->states.data)[i] = 2;
|
|
||||||
m->size--;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE void vm_map_clear(void *mm) {
|
|
||||||
spl_map_t *m = (spl_map_t *)mm;
|
|
||||||
if (!m)
|
|
||||||
return;
|
|
||||||
if (m->states.data && m->states.len)
|
|
||||||
memset((void *)m->states.data, 0, m->states.len);
|
|
||||||
m->size = 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE void vm_map_drop(void *mm) {
|
|
||||||
spl_map_t *m = (spl_map_t *)mm;
|
|
||||||
if (!m)
|
|
||||||
return;
|
|
||||||
spl_vec_release(&m->keys);
|
|
||||||
spl_vec_release(&m->vals);
|
|
||||||
spl_vec_release(&m->states);
|
|
||||||
m->size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- file ---- */
|
|
||||||
SPL_INLINE void *vm_fopen(void *path, void *mode) {
|
|
||||||
return (void *)fopen((const char *)path, (const char *)mode);
|
|
||||||
}
|
|
||||||
SPL_INLINE i32 vm_fclose(void *f) { return f ? fclose((FILE *)f) : 0; }
|
|
||||||
SPL_INLINE u64 vm_fread(void *buf, u64 sz, u64 n, void *f) {
|
|
||||||
return f ? (u64)fread(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE u64 vm_fwrite(void *buf, u64 sz, u64 n, void *f) {
|
|
||||||
return f ? (u64)fwrite(buf, (size_t)sz, (size_t)n, (FILE *)f) : 0;
|
|
||||||
}
|
|
||||||
SPL_INLINE u64 vm_fsize(void *f) {
|
|
||||||
long cur, end;
|
|
||||||
if (!f)
|
|
||||||
return 0;
|
|
||||||
cur = ftell((FILE *)f);
|
|
||||||
if (cur < 0)
|
|
||||||
return 0;
|
|
||||||
if (fseek((FILE *)f, 0, SEEK_END) != 0)
|
|
||||||
return 0;
|
|
||||||
end = ftell((FILE *)f);
|
|
||||||
fseek((FILE *)f, cur, SEEK_SET);
|
|
||||||
return end < 0 ? 0 : (u64)end;
|
|
||||||
}
|
|
||||||
SPL_INLINE void *vm_stdin(void) { return (void *)stdin; }
|
|
||||||
SPL_INLINE void *vm_stdout(void) { return (void *)stdout; }
|
|
||||||
SPL_INLINE void *vm_stderr(void) { return (void *)stderr; }
|
|
||||||
SPL_INLINE i32 vm_putchar(i32 c) { return fputc(c, stdout); }
|
|
||||||
SPL_INLINE i32 vm_getchar(void) { return fgetc(stdin); }
|
|
||||||
|
|
||||||
/* ---- misc ---- */
|
|
||||||
SPL_INLINE void vm_exit(i32 code) { exit(code); }
|
|
||||||
SPL_INLINE u64 vm_timestamp(u64 kind) {
|
|
||||||
time_t sec = time(NULL);
|
|
||||||
if (kind == 1)
|
|
||||||
return (u64)sec;
|
|
||||||
if (kind == 2)
|
|
||||||
return 0;
|
|
||||||
if (kind == 4)
|
|
||||||
return (u64)sec * 1000000000ull;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ---- 内嵌 VM(comptime):实现在 spl_vm_embed.c,链接 stage0 VM ---- */
|
|
||||||
#define SPL_VM_EMBED_IMPLIMENTATION
|
|
||||||
#include "spl_vm_embed.h"
|
|
||||||
|
|
||||||
#endif /* __SPL_RT_H__ */
|
|
||||||
+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() {}
|
||||||
+2
-4
@@ -1,7 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 词法单元:用带载荷 ADT 表达 TokenKind;关键词/算符各自独立 enum。
|
// 词法单元:TokenKind 为带载荷 ADT,关键词/算符各自独立 enum;整型/字符在词法期
|
||||||
// 整型与字符在词法期解码并随载荷携带;文本一律借用源切片(不复制)。
|
// 解码,文本一律借用源切片。
|
||||||
|
|
||||||
type .{ .ByteView } = @import("utils/byte_view.spl");
|
type .{ .ByteView } = @import("utils/byte_view.spl");
|
||||||
|
|
||||||
@@ -128,7 +128,6 @@ fn keyword_of(text: ByteView): KeywordResult {
|
|||||||
ret .No;
|
ret .No;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- token 类型名(与 stage1 spl_tok_type_name 逐字一致,供 dump/diff 使用)----
|
|
||||||
|
|
||||||
fn keyword_name(kw: Keyword): []u8 {
|
fn keyword_name(kw: Keyword): []u8 {
|
||||||
match kw {
|
match kw {
|
||||||
@@ -242,7 +241,6 @@ fn kind_name(kind: TokenKind): []u8 {
|
|||||||
ret "?";
|
ret "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
+7
-28
@@ -1,11 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// IR 类型表(移植自 stage1/spl_type.c)。类型按结构 hash-consing(intern),
|
// IR 类型表:按结构 hash-consing。id 0 为保留哨兵(Error)。成员/参数 Vec 由本表
|
||||||
// id 0 为保留哨兵(Error)。节点借用式持有:名称是 ByteView(指向源/AST),
|
// 拥有;hash 仅用于选桶,相等始终由结构比较决定。
|
||||||
// 不复制字符串;聚合成员与函数参数的 Vec 由本表拥有,drop 时统一释放。
|
|
||||||
//
|
|
||||||
// hash 只用于选桶;相等永远由结构比较决定。桶以「完整 hash -> 链头 id」的
|
|
||||||
// Map 存储,节点自带 hash_next 串链(对应 stage1 的 spl_fold)。
|
|
||||||
|
|
||||||
type .{ .Error } = @import("utils/error.spl");
|
type .{ .Error } = @import("utils/error.spl");
|
||||||
type .{ .Vec } = @import("utils/vec.spl");
|
type .{ .Vec } = @import("utils/vec.spl");
|
||||||
@@ -41,7 +37,7 @@ type AggKind = enum {
|
|||||||
Shape,
|
Shape,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 与 stage1 spl_type_node_kind_t 一一对应;Forall/Exists 保留(暂打印 quant)。
|
// 变体顺序即 kind 数值;Forall/Exists 暂按 quant 打印。
|
||||||
type TypeNode = enum {
|
type TypeNode = enum {
|
||||||
Error,
|
Error,
|
||||||
Never,
|
Never,
|
||||||
@@ -78,7 +74,6 @@ type TypeTableResult = enum {
|
|||||||
Err: Error,
|
Err: Error,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- hash(FNV 风格;值不必与 stage1 相同,只要结构一致即可)----
|
|
||||||
|
|
||||||
fn seed(): usize {
|
fn seed(): usize {
|
||||||
ret 14695981039346656037;
|
ret 14695981039346656037;
|
||||||
@@ -290,7 +285,6 @@ fn free_payload(n: *TypeNode) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 底层访问 ----
|
|
||||||
|
|
||||||
fn usize_at(v: Vec, i: usize): usize {
|
fn usize_at(v: Vec, i: usize): usize {
|
||||||
match Vec.get(&v, i) {
|
match Vec.get(&v, i) {
|
||||||
@@ -317,13 +311,12 @@ fn push_member(v: *Vec, name: []u8, type_id: usize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// id 0 与越界均返回 null(对应 stage1 spl_type_node)。
|
// id 0 与越界均返回 null。
|
||||||
fn node(self: *TypeTable, id: usize): *TypeEntry {
|
fn node(self: *TypeTable, id: usize): *TypeEntry {
|
||||||
if id == 0 || id >= self.table.len { ret null; }
|
if id == 0 || id >= self.table.len { ret null; }
|
||||||
ret (self.table.data as *TypeEntry) + id;
|
ret (self.table.data as *TypeEntry) + id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 生命周期 ----
|
|
||||||
|
|
||||||
fn new(): TypeTableResult {
|
fn new(): TypeTableResult {
|
||||||
var tt = TypeTable {
|
var tt = TypeTable {
|
||||||
@@ -376,7 +369,6 @@ fn define(self: *TypeTable, id: usize, src: *TypeNode) {
|
|||||||
src.* = .Error;
|
src.* = .Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- intern ----
|
|
||||||
|
|
||||||
fn fold_find(self: *TypeTable, hash: usize, probe: *TypeNode): usize {
|
fn fold_find(self: *TypeTable, hash: usize, probe: *TypeNode): usize {
|
||||||
var head: usize = 0;
|
var head: usize = 0;
|
||||||
@@ -403,10 +395,7 @@ fn fold_insert(self: *TypeTable, hash: usize, id: usize) {
|
|||||||
var n = node(self, id);
|
var n = node(self, id);
|
||||||
if n == null { ret; }
|
if n == null { ret; }
|
||||||
n.hash_next = old;
|
n.hash_next = old;
|
||||||
match Map.put(&self.fold, &hash as *u8, &id as *u8) {
|
Map.put(&self.fold, &hash as *u8, &id as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// intern 一个 probe:命中则释放 probe 载荷并返回已有 id;否则复制入表。
|
// intern 一个 probe:命中则释放 probe 载荷并返回已有 id;否则复制入表。
|
||||||
@@ -433,7 +422,6 @@ fn push(self: *TypeTable, probe: *TypeNode): usize {
|
|||||||
ret id;
|
ret id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- builders ----
|
|
||||||
|
|
||||||
fn make_error(self: *TypeTable): usize {
|
fn make_error(self: *TypeTable): usize {
|
||||||
var n: TypeNode = .Error;
|
var n: TypeNode = .Error;
|
||||||
@@ -513,7 +501,6 @@ fn make_agg(self: *TypeTable, kind: AggKind, agg: Agg): usize {
|
|||||||
ret push(self, &n);
|
ret push(self, &n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 查询 ----
|
|
||||||
|
|
||||||
fn is_subtype(self: *TypeTable, sub: usize, super: usize): bool {
|
fn is_subtype(self: *TypeTable, sub: usize, super: usize): bool {
|
||||||
if sub == 0 || super == 0 { ret false; }
|
if sub == 0 || super == 0 { ret false; }
|
||||||
@@ -597,7 +584,6 @@ fn agg_member_idx(self: *TypeTable, tid: usize, name: ByteView, out_idx: *usize)
|
|||||||
ret false;
|
ret false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump(镜像 stage1 spl_type_pure_dump)----
|
|
||||||
|
|
||||||
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
fn push_out(buf: *ByteBuffer, bytes: []u8): bool {
|
||||||
match ByteBuffer.append(buf, bytes) {
|
match ByteBuffer.append(buf, bytes) {
|
||||||
@@ -637,10 +623,7 @@ fn dump_rec(self: *TypeTable, id: usize, out: *ByteBuffer, stack: *Vec): bool {
|
|||||||
if n == null {
|
if n == null {
|
||||||
if !push_out(out, "none") { ret false; }
|
if !push_out(out, "none") { ret false; }
|
||||||
var popped: usize = 0;
|
var popped: usize = 0;
|
||||||
match Vec.pop(stack, &popped as *u8) {
|
Vec.pop(stack, &popped as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
ret true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,10 +681,7 @@ fn dump_rec(self: *TypeTable, id: usize, out: *ByteBuffer, stack: *Vec): bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dropped: usize = 0;
|
var dropped: usize = 0;
|
||||||
match Vec.pop(stack, &dropped as *u8) {
|
Vec.pop(stack, &dropped as *u8);
|
||||||
.Ok => {},
|
|
||||||
.Err(error) => {},
|
|
||||||
}
|
|
||||||
ret ok;
|
ret ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -739,7 +719,6 @@ fn pure_dump_text(self: *TypeTable, id: usize): ByteBuffer {
|
|||||||
ret out;
|
ret out;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_intern_simple() {
|
fn test_intern_simple() {
|
||||||
var tt: TypeTable = undefined;
|
var tt: TypeTable = undefined;
|
||||||
|
|||||||
@@ -155,7 +155,6 @@ fn stable_sort(data: *u8, len: usize, elem_size: usize,
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
type Item = struct {
|
type Item = struct {
|
||||||
key: i32,
|
key: i32,
|
||||||
|
|||||||
@@ -127,7 +127,6 @@ fn drop(self: *Arena) {
|
|||||||
self.current = 0;
|
self.current = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn is_aligned(p: *u8, align: usize): bool {
|
fn is_aligned(p: *u8, align: usize): bool {
|
||||||
ret ((p as usize) & (align - 1)) == 0;
|
ret ((p as usize) & (align - 1)) == 0;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ fn new(): ByteBuffer {
|
|||||||
|
|
||||||
fn with_capacity(cap: usize): ByteBuffer {
|
fn with_capacity(cap: usize): ByteBuffer {
|
||||||
var b = new();
|
var b = new();
|
||||||
match reserve(&b, cap) { .Ok => {}, .Err(error) => {} }
|
reserve(&b, cap);
|
||||||
ret b;
|
ret b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ type .{ .ByteView } = @import("byte_view.spl");
|
|||||||
type .{ .Value, .int, .uint, .char, .str, .addr } = @import("value.spl");
|
type .{ .Value, .int, .uint, .char, .str, .addr } = @import("value.spl");
|
||||||
type .{ .BigInt, .Bits } = @import("mp.spl");
|
type .{ .BigInt, .Bits } = @import("mp.spl");
|
||||||
|
|
||||||
// 注意:radix 边界直接用字面量。stage1 刻意不做常量求值/折叠,
|
// radix 边界直接用字面量:顶层 const 不参与运行时常量折叠。
|
||||||
// 顶层 const 不应用于运行时表达式。
|
|
||||||
fn valid_radix(radix: u8): bool {
|
fn valid_radix(radix: u8): bool {
|
||||||
ret radix >= 2 && radix <= 36;
|
ret radix >= 2 && radix <= 36;
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,6 @@ fn append_bool(out: *ByteBuffer, value: bool): Status {
|
|||||||
ret ByteBuffer.append(out, "false");
|
ret ByteBuffer.append(out, "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 显式装箱值格式化 ----
|
|
||||||
|
|
||||||
// Int/Uint 落到整数;signed 仅对 Int 的十进制生效,其余按无符号位模式输出。
|
// Int/Uint 落到整数;signed 仅对 Int 的十进制生效,其余按无符号位模式输出。
|
||||||
fn append_value_int(out: *ByteBuffer, value: Value, radix: u8, uppercase: bool,
|
fn append_value_int(out: *ByteBuffer, value: Value, radix: u8, uppercase: bool,
|
||||||
@@ -223,7 +222,6 @@ fn format(out: *ByteBuffer, fmt: []u8, args: []Value): Status {
|
|||||||
ret ByteBuffer.append(out, ByteBuffer.as_slice(temp));
|
ret ByteBuffer.append(out, ByteBuffer.as_slice(temp));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ fn hash_usize_value(value: usize): usize {
|
|||||||
ret h;
|
ret h;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- map 回调 ----
|
|
||||||
|
|
||||||
// 键按 usize 解释。
|
// 键按 usize 解释。
|
||||||
fn hash_usize(key: *u8): usize {
|
fn hash_usize(key: *u8): usize {
|
||||||
@@ -55,7 +54,6 @@ fn eq_view(a: *u8, b: *u8): bool {
|
|||||||
ret ByteView.eq((a as *ByteView).*, (b as *ByteView).*);
|
ret ByteView.eq((a as *ByteView).*, (b as *ByteView).*);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_hash(view: []u8, expected: usize) {
|
fn expect_hash(view: []u8, expected: usize) {
|
||||||
var got = hash_bytes(view.ptr, view.len);
|
var got = hash_bytes(view.ptr, view.len);
|
||||||
|
|||||||
+2
-7
@@ -1,11 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// I/O 便利层(构建在 sys/file 之上):
|
// sys/file 之上的 I/O 便利层:C 字符串路径 open、精确读取/完整写入、整读、stdout/stderr 打印。
|
||||||
// - C 字符串路径/模式的 open 包装
|
// 短读/短写零进展与关闭失败返回 Err(.Io);写入失败不承诺回滚。
|
||||||
// - 精确读取、完整写入和普通文件整读
|
|
||||||
// - stdout/stderr 打印
|
|
||||||
// 说明:read_all 会按文件大小一次性读入 ByteBuffer;不处理增长中的文件。
|
|
||||||
// 短读、短写后的零进展及关闭失败返回 Err(.Io);文件写入不承诺回滚。
|
|
||||||
|
|
||||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||||
type .{ .ByteView } = @import("byte_view.spl");
|
type .{ .ByteView } = @import("byte_view.spl");
|
||||||
@@ -130,7 +126,6 @@ fn read_all(path: ByteView, out: *ByteBuffer): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
// 轻量 JSON 解析 / 输出器(递归下降 + 节点池),用于验证底层库的拼装,未来服务 LSP。
|
// 轻量 JSON 解析/输出器:输入按源切片借用、不解码(round-trip 保真),数字不做数值化。
|
||||||
// 设计:
|
// 节点/子项存 Vec 保留插入顺序;输出写 ByteBuffer。递归/输出深度上限见 max_depth()。
|
||||||
// - 输入 ByteView,字符串/数字以“源切片”借用,不做解码,round-trip 保真;
|
|
||||||
// - 节点池 Vec[JsonNode];数组/对象子项存 Vec[JsonChild],保留插入顺序;
|
|
||||||
// - 错误带字节位置(error.spl);输出写 ByteBuffer;缩进/数字用 ByteBuffer/fmt。
|
|
||||||
// 限制:不做字符串转义解码、数字不做数值化(原样保存);递归/输出深度上限见 max_depth()。
|
|
||||||
|
|
||||||
type .{ .Error, .Status, .make, .at } = @import("error.spl");
|
type .{ .Error, .Status, .make, .at } = @import("error.spl");
|
||||||
type .{ .ByteView, .ViewResult, .FindResult } = @import("byte_view.spl");
|
type .{ .ByteView, .ViewResult, .FindResult } = @import("byte_view.spl");
|
||||||
@@ -393,7 +389,7 @@ fn root_index(doc: *Json): usize {
|
|||||||
ret doc.root;
|
ret doc.root;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 枚举 tag 取整(stage1 枚举 == 有缺陷,用 @enum2int 比较更稳妥)。
|
// 枚举 tag 取整:枚举 == 不可靠,用 @enum2int 比较。
|
||||||
fn kind_code(kind: JsonKind): usize {
|
fn kind_code(kind: JsonKind): usize {
|
||||||
ret @enum2int(kind) as usize;
|
ret @enum2int(kind) as usize;
|
||||||
}
|
}
|
||||||
@@ -578,7 +574,6 @@ fn emit_pretty(out: *ByteBuffer, doc: *Json, indent: usize): Status {
|
|||||||
ret emit(out, doc, doc.root, indent, 0);
|
ret emit(out, doc, doc.root, indent, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 构造辅助(写 JSON 片段;字符串需由调用方保证已转义)----
|
|
||||||
|
|
||||||
fn write_int(out: *ByteBuffer, value: i64): Status {
|
fn write_int(out: *ByteBuffer, value: i64): Status {
|
||||||
ret append_i64(out, value, 10, false);
|
ret append_i64(out, value, 10, false);
|
||||||
@@ -588,7 +583,6 @@ fn write_u64(out: *ByteBuffer, value: u64): Status {
|
|||||||
ret append_u64(out, value, 10, false);
|
ret append_u64(out, value, 10, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ fn fatal(self: *Logger, message: []u8) {
|
|||||||
Exit.fail();
|
Exit.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 格式化入口(显式装箱,见 value.spl / fmt.format)----
|
|
||||||
|
|
||||||
// 先格式化到临时缓冲;格式错误在写出前返回 Err(.InvalidFormat)。
|
// 先格式化到临时缓冲;格式错误在写出前返回 Err(.InvalidFormat)。
|
||||||
fn logf(self: *Logger, level: Level, fmt: []u8, args: []Value): Status {
|
fn logf(self: *Logger, level: Level, fmt: []u8, args: []Value): Status {
|
||||||
@@ -125,7 +124,6 @@ fn fatalf(self: *Logger, fmt: []u8, args: []Value) {
|
|||||||
Exit.fail();
|
Exit.fail();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn expect_name(level: Level, expected: []u8) {
|
fn expect_name(level: Level, expected: []u8) {
|
||||||
@assert(ByteView.eq(ByteView.from_slice(level_name(level)),
|
@assert(ByteView.eq(ByteView.from_slice(level_name(level)),
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ fn parse_frame(input: ByteView, body_out: *ByteBuffer): FrameResult {
|
|||||||
ret .Ok(body_start + content_len);
|
ret .Ok(body_start + content_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -155,7 +155,6 @@ fn take(source: *Map): Map {
|
|||||||
ret result;
|
ret result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
+13
-3
@@ -1,13 +1,23 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 尺寸计算和字节移动;不分配、不释放内存,不改变任何资源的所有权。
|
// 尺寸计算、字节移动与内存分配统一门面(分配/释放转发自 sys/mem)。
|
||||||
|
// 本模块不改变任何资源的所有权。
|
||||||
|
|
||||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||||
|
type SysMem = @import("../sys/mem.spl");
|
||||||
|
|
||||||
|
// 分配/释放/字节操作转发(core 只经本模块使用内存,不直接依赖 sys)。
|
||||||
|
fn alloc(sz: usize): *any { ret SysMem.alloc(sz); }
|
||||||
|
fn free(p: *any) { SysMem.free(p); }
|
||||||
|
fn realloc(p: *any, sz: usize): *any { ret SysMem.realloc(p, sz); }
|
||||||
|
fn copy(dst: *any, src: *any, n: usize): *any { ret SysMem.copy(dst, src, n); }
|
||||||
|
fn cmp(a: *any, b: *any, n: usize): isize { ret SysMem.cmp(a, b, n); }
|
||||||
|
fn set(dst: *any, byte: u8, n: usize): *any { ret SysMem.set(dst, byte, n); }
|
||||||
|
fn zero(dst: *any, n: usize): *any { ret SysMem.zero(dst, n); }
|
||||||
|
|
||||||
#extern(vm) fn vm_memcpy(dst: *any, src: *any, n: usize): *any;
|
#extern(vm) fn vm_memcpy(dst: *any, src: *any, n: usize): *any;
|
||||||
#extern(vm) fn vm_memmove(dst: *any, src: *any, n: usize): *any;
|
#extern(vm) fn vm_memmove(dst: *any, src: *any, n: usize): *any;
|
||||||
|
|
||||||
fn ignore_ptr(p: *any) {}
|
|
||||||
|
|
||||||
type SizeResult = enum {
|
type SizeResult = enum {
|
||||||
Ok: usize,
|
Ok: usize,
|
||||||
@@ -75,7 +85,7 @@ fn move(dst: []u8, src: []u8): Status {
|
|||||||
if dst_addr == src_addr { ret .Ok; }
|
if dst_addr == src_addr { ret .Ok; }
|
||||||
|
|
||||||
// 重叠安全,统一走原生 memmove。
|
// 重叠安全,统一走原生 memmove。
|
||||||
ignore_ptr(vm_memmove(dst.ptr as *any, src.ptr as *any, count));
|
vm_memmove(dst.ptr as *any, src.ptr as *any, count);
|
||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -297,7 +297,6 @@ fn normalize_into(out: *ByteBuffer, path: ByteView, style: PathStyle): Status {
|
|||||||
ret .Ok;
|
ret .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected path error"); }, }
|
match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected path error"); }, }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
// Expect retcode = 0
|
// Expect retcode = 0
|
||||||
|
|
||||||
// 全量热点/耗时记录器(配合 @file_name()/@line()/@col() 与 sys/time.spl)。
|
// 采样热点记录器:每个调用点聚合 count 与 self/total 的 steps/ns(self = total 减子区间)。
|
||||||
// 每个调用点聚合:count、独占(self)与包含(total)的 steps/ns。
|
// 严格 LIFO,用 defer 配对:
|
||||||
// - total_* 含被调用者(嵌套会重复,跨站点求和会超过墙钟)
|
|
||||||
// - self_* = total_* - 子区间之和,用于定位真正的热点
|
|
||||||
// 用法(严格 LIFO,defer 正好满足):
|
|
||||||
// var tok = Profile.begin(&prof, @file_name(), @line(), @col());
|
// var tok = Profile.begin(&prof, @file_name(), @line(), @col());
|
||||||
// defer { Profile.end(&prof, tok); }
|
// defer { Profile.end(&prof, tok); }
|
||||||
|
|
||||||
@@ -139,7 +136,7 @@ fn begin(self: *Profile, fname: []u8, line: usize, col: usize): usize {
|
|||||||
.Err(error) => { ret 0; },
|
.Err(error) => { ret 0; },
|
||||||
}
|
}
|
||||||
var token = self.spans.len - 1;
|
var token = self.spans.len - 1;
|
||||||
match Vec.push(&self.stack, &token as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&self.stack, &token as *u8);
|
||||||
ret token;
|
ret token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +161,7 @@ fn end(self: *Profile, token: usize) {
|
|||||||
|
|
||||||
// 弹栈并把区间计入父 span(LIFO)。
|
// 弹栈并把区间计入父 span(LIFO)。
|
||||||
var popped: usize = 0;
|
var popped: usize = 0;
|
||||||
match Vec.pop(&self.stack, &popped as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.pop(&self.stack, &popped as *u8);
|
||||||
var n = self.stack.len;
|
var n = self.stack.len;
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
var p = span_at(self, usize_at(&self.stack, n - 1));
|
var p = span_at(self, usize_at(&self.stack, n - 1));
|
||||||
@@ -175,7 +172,6 @@ fn end(self: *Profile, token: usize) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- dump / save ----
|
|
||||||
|
|
||||||
fn out_s(out: *ByteBuffer, s: []u8): bool {
|
fn out_s(out: *ByteBuffer, s: []u8): bool {
|
||||||
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
|
||||||
@@ -202,7 +198,7 @@ fn dump(self: *Profile, out: *ByteBuffer): bool {
|
|||||||
defer { Vec.drop(&idx); }
|
defer { Vec.drop(&idx); }
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while i < self.sites.len {
|
while i < self.sites.len {
|
||||||
match Vec.push(&idx, &i as *u8) { .Ok => {}, .Err(error) => {} }
|
Vec.push(&idx, &i as *u8);
|
||||||
i = i + 1;
|
i = i + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +267,6 @@ fn save(self: *Profile, path: ByteView): bool {
|
|||||||
ret true;
|
ret true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn spin(n: usize): usize {
|
fn spin(n: usize): usize {
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
@@ -293,10 +288,9 @@ fn test_profile() {
|
|||||||
@assert(s != null && s.count >= 1, "count");
|
@assert(s != null && s.count >= 1, "count");
|
||||||
@assert(s.total_steps > 0, "steps");
|
@assert(s.total_steps > 0, "steps");
|
||||||
@assert(s.self_steps > 0, "self steps");
|
@assert(s.self_steps > 0, "self steps");
|
||||||
if v == 0 { ignore_usize(v + 1); }
|
if v == 0 { v + 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_usize(v: usize) {}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
test_profile();
|
test_profile();
|
||||||
|
|||||||
@@ -74,7 +74,6 @@ fn drop(self: *Set) {
|
|||||||
Map.drop(&self.map);
|
Map.drop(&self.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ fn drop(self: *Source) {
|
|||||||
Vec.drop(&self.line_starts);
|
Vec.drop(&self.line_starts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 多文件集合 ----
|
|
||||||
|
|
||||||
type Sources = struct {
|
type Sources = struct {
|
||||||
items: Vec, // Source
|
items: Vec, // Source
|
||||||
@@ -191,7 +190,6 @@ fn set_drop(set: *Sources) {
|
|||||||
Map.drop(&set.by_name);
|
Map.drop(&set.by_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn view(text: []u8): ByteView {
|
fn view(text: []u8): ByteView {
|
||||||
ret ByteView.from_slice(text);
|
ret ByteView.from_slice(text);
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ fn drop(self: *Str) {
|
|||||||
ByteBuffer.drop(&self.storage);
|
ByteBuffer.drop(&self.storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -181,7 +181,6 @@ fn utf16_to_byte(data: *u8, len: usize, utf16_off: usize): usize {
|
|||||||
ret i;
|
ret i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试辅助 ----
|
|
||||||
|
|
||||||
fn expect_ok(result: Status) {
|
fn expect_ok(result: Status) {
|
||||||
match result {
|
match result {
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ fn addr(value: *any): Value {
|
|||||||
ret .Ptr(value);
|
ret .Ptr(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 测试 ----
|
|
||||||
|
|
||||||
fn test_int_and_uint() {
|
fn test_int_and_uint() {
|
||||||
match int(-5) {
|
match int(-5) {
|
||||||
|
|||||||
@@ -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