stage2 提供c后端 提交stage1的测试
This commit is contained in:
1 parent
846f441e3a
commit
51d408d524
82 files changed
+6872
-65
No files matched your search
@@ -121,6 +121,8 @@ type Body = enum {
|
||||
PatternList: struct { items: Vec },
|
||||
PatternAggregate: struct { items: Vec },
|
||||
PatternField: struct { name: ByteView, pattern: usize },
|
||||
ExprComptime: struct { body: usize },
|
||||
ExprComptimeInit: struct { name: ByteView, args: Vec, fields: Vec },
|
||||
}
|
||||
|
||||
fn kind_name(body: Body): []u8 {
|
||||
@@ -143,6 +145,7 @@ fn kind_name(body: Body): []u8 {
|
||||
.StatementContinue(_) => { ret "statement_continue"; },
|
||||
.StatementDefer(_) => { ret "statement_defer"; },
|
||||
.StatementComptime(_) => { ret "statement_comptime"; },
|
||||
.ExprComptime(_) => { ret "expr_comptime"; },
|
||||
.StatementDecl(_) => { ret "statement_decl"; },
|
||||
.StatementExpr(_) => { ret "statement_expr"; },
|
||||
.AggregateMember(_) => { ret "aggregate_member"; },
|
||||
@@ -168,6 +171,7 @@ fn kind_name(body: Body): []u8 {
|
||||
.ExprArrayLit(_) => { ret "array_literal"; },
|
||||
.ExprAggregateLit(_) => { ret "aggregate_literal"; },
|
||||
.ExprComptimeCall(_) => { ret "comptime_call"; },
|
||||
.ExprComptimeInit(_) => { ret "comptime_init"; },
|
||||
.ExprBlock(_) => { ret "block"; },
|
||||
.ExprIf(_) => { ret "if_expr"; },
|
||||
.ExprIfvar(_) => { ret "ifvar_expr"; },
|
||||
@@ -463,6 +467,7 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
||||
.StatementContinue(x) => { ok = dump_opt(ast, buf, stack, x.label, true); },
|
||||
.StatementDefer(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||
.StatementComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||
.ExprComptime(x) => { ok = dump_opt(ast, buf, stack, x.body, true); },
|
||||
.StatementDecl(d) => { ok = dump_opt(ast, buf, stack, d.decl, true); },
|
||||
.StatementExpr(e) => { ok = dump_opt(ast, buf, stack, e.expr, true); },
|
||||
.AggregateMember(d) => {
|
||||
@@ -514,6 +519,7 @@ fn dump_node(ast: *Ast, buf: *ByteBuffer, stack: *Vec, ref: usize, last: bool):
|
||||
},
|
||||
.ExprAggregateLit(a) => { ok = dump_list(ast, buf, stack, a.fields); },
|
||||
.ExprComptimeCall(c) => { ok = dump_list(ast, buf, stack, c.args); },
|
||||
.ExprComptimeInit(x) => { ok = dump_list(ast, buf, stack, x.fields); },
|
||||
.ExprBlock(b) => {
|
||||
ok = dump_list(ast, buf, stack, b.stmts) &&
|
||||
dump_opt(ast, buf, stack, b.tail_expr, true);
|
||||
|
||||
+1304
File diff suppressed because it is too large.
Load diff
+179
-7
@@ -25,6 +25,7 @@ failed: bool,
|
||||
stop_agg: i32,
|
||||
error_count: usize,
|
||||
file: usize, // 当前源文件在 Sources 中的下标
|
||||
comptime_depth: usize, // >0 表示处于 comptime 上下文(类型可作为表达式)
|
||||
|
||||
fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
|
||||
ret Parser {
|
||||
@@ -36,6 +37,7 @@ fn new(ast: *Ast, toks: Vec, diag: *Diag): Parser {
|
||||
.stop_agg = 0,
|
||||
.error_count = 0,
|
||||
.file = 0,
|
||||
.comptime_depth = 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,6 +73,21 @@ fn peek(self: *Parser): *Token {
|
||||
ret base + i;
|
||||
}
|
||||
|
||||
// 跳过 trivia,返回当前 token 之后的下一个非 trivia token(不移动 pos)。
|
||||
fn peek_next(self: *Parser): *Token {
|
||||
var n = self.toks.len;
|
||||
var base = self.toks.data as *Token;
|
||||
var i = self.pos + 1;
|
||||
while i < n {
|
||||
if base == null { break; }
|
||||
var t = base + i;
|
||||
if !is_trivia(t.kind) { break; }
|
||||
i = i + 1;
|
||||
}
|
||||
if i >= n || base == null { ret null; }
|
||||
ret base + i;
|
||||
}
|
||||
|
||||
fn advance(self: *Parser): *Token {
|
||||
var t = peek(self);
|
||||
if t == null { ret null; }
|
||||
@@ -264,6 +281,7 @@ fn can_start_expr(kind: TokenKind): bool {
|
||||
.Loop => { ret true; },
|
||||
.For => { ret true; },
|
||||
.Match => { ret true; },
|
||||
.Comptime => { ret true; },
|
||||
_ => { ret false; },
|
||||
}
|
||||
},
|
||||
@@ -655,7 +673,33 @@ fn parse_param_list(self: *Parser): Vec {
|
||||
ret out;
|
||||
}
|
||||
|
||||
fn parse_fn_decl(self: *Parser, tags: Vec): usize {
|
||||
// 该 token 是否可能开始一个类型表达式(用于省略冒号的返回类型)。
|
||||
fn token_starts_type(t: *Token): bool {
|
||||
if t == null { ret false; }
|
||||
match t.kind {
|
||||
.Ident => { ret true; },
|
||||
.Keyword(k) => {
|
||||
if @enum2int(k) == @enum2int(Keyword.Type) { ret true; }
|
||||
if @enum2int(k) == @enum2int(Keyword.Bool) { 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) => {
|
||||
if @enum2int(p) == @enum2int(Punct.Mul) { ret true; }
|
||||
if @enum2int(p) == @enum2int(Punct.LBracket) { ret true; }
|
||||
if @enum2int(p) == @enum2int(Punct.At) { ret true; }
|
||||
if @enum2int(p) == @enum2int(Punct.Sharp) { ret true; }
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
ret false;
|
||||
}
|
||||
|
||||
fn parse_fn_decl(self: *Parser, tags: Vec, is_comptime: bool): usize {
|
||||
var kw = peek(self);
|
||||
if kw == null || !kind_is_keyword(kw.kind, .Fn) { ret 0; }
|
||||
advance(self);
|
||||
@@ -672,19 +716,26 @@ fn parse_fn_decl(self: *Parser, tags: Vec): usize {
|
||||
advance(self);
|
||||
ret_type = parse_type_expr(self);
|
||||
t = peek(self);
|
||||
} else if token_starts_type(t) {
|
||||
// 省略冒号:`fn f() T { ... }`
|
||||
ret_type = parse_type_expr(self);
|
||||
t = peek(self);
|
||||
}
|
||||
var body: usize = 0;
|
||||
if is_comptime { self.comptime_depth = self.comptime_depth + 1; }
|
||||
if t != null && kind_is_punct(t.kind, .LBrace) {
|
||||
body = parse_block(self);
|
||||
} else if t != null && kind_is_punct(t.kind, .Semicolon) {
|
||||
advance(self);
|
||||
} else {
|
||||
if is_comptime { self.comptime_depth = self.comptime_depth - 1; }
|
||||
parse_error(self, t, "function declaration must end with ';' or block");
|
||||
ret 0;
|
||||
}
|
||||
if is_comptime { self.comptime_depth = self.comptime_depth - 1; }
|
||||
ret new_node(self, .FnDecl(.{
|
||||
.tag_list = tags,
|
||||
.is_comptime = false,
|
||||
.is_comptime = is_comptime,
|
||||
.name = name.text,
|
||||
.params = params,
|
||||
.ret_type = ret_type,
|
||||
@@ -802,8 +853,10 @@ fn parse_comptime_decl(self: *Parser): usize {
|
||||
advance(self);
|
||||
var body: usize = 0;
|
||||
var t = peek(self);
|
||||
self.comptime_depth = self.comptime_depth + 1;
|
||||
if t != null && kind_is_punct(t.kind, .LBrace) { body = parse_block(self); }
|
||||
else { body = parse_expr(self); }
|
||||
self.comptime_depth = self.comptime_depth - 1;
|
||||
ret new_node(self, .ComptimeDecl(.{ .body = body }), kw);
|
||||
}
|
||||
|
||||
@@ -815,7 +868,7 @@ fn parse_container_declaration(self: *Parser): usize {
|
||||
match t.kind {
|
||||
.Eof => { ret 0; },
|
||||
.Keyword(k) => {
|
||||
if @enum2int(k) == @enum2int(Keyword.Fn) { ret parse_fn_decl(self, tags); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Fn) { ret parse_fn_decl(self, tags, false); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Type) { ret parse_type_decl(self, tags); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Var) {
|
||||
ret parse_var_const_decl(self, tags, false);
|
||||
@@ -823,7 +876,15 @@ fn parse_container_declaration(self: *Parser): usize {
|
||||
if @enum2int(k) == @enum2int(Keyword.Const) {
|
||||
ret parse_var_const_decl(self, tags, true);
|
||||
}
|
||||
if @enum2int(k) == @enum2int(Keyword.Comptime) { ret parse_comptime_decl(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Comptime) {
|
||||
// `comptime fn ...` -> 标记为编译期函数;否则 `comptime { }` 声明。
|
||||
var nxt = peek_next(self);
|
||||
if nxt != null && kind_is_keyword(nxt.kind, .Fn) {
|
||||
advance(self);
|
||||
ret parse_fn_decl(self, tags, true);
|
||||
}
|
||||
ret parse_comptime_decl(self);
|
||||
}
|
||||
},
|
||||
.Ident => { ret parse_member_decl(self, tags); },
|
||||
.Punct(p) => {
|
||||
@@ -951,15 +1012,41 @@ fn parse_type_base(self: *Parser): usize {
|
||||
match bt.kind {
|
||||
.Ident => {
|
||||
advance(self);
|
||||
// 类型调用 `Name(T, ...)`(comptime 类型构造,如 `vec(i32)`)。
|
||||
if at_punct(self, .LParen) {
|
||||
advance(self);
|
||||
var ref = new_node(self, .TypePath(.{
|
||||
.name = bt.text,
|
||||
.generic_args = ref_vec(),
|
||||
}), bt);
|
||||
var args = ref_vec();
|
||||
while !self.failed && !at_punct(self, .RParen) {
|
||||
var a = parse_type_expr(self);
|
||||
match Vec.push(&args, &a as *u8) { .Ok => {}, .Err(e) => { ret 0; } }
|
||||
if at_punct(self, .Comma) { advance(self); continue; }
|
||||
break;
|
||||
}
|
||||
expect_punct(self, .RParen, "')' in type call");
|
||||
var tn = Ast.node(self.ast, ref);
|
||||
match tn.body {
|
||||
.TypePath(tp) => {
|
||||
tp.generic_args = args;
|
||||
tn.body = .TypePath(tp);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
ret ref;
|
||||
}
|
||||
ret new_node(self, .TypePath(.{
|
||||
.name = bt.text,
|
||||
.generic_args = ref_vec(),
|
||||
}), bt);
|
||||
},
|
||||
.Keyword(k) => {
|
||||
// stage1 is_builtin_type_kw:bool / any 作为类型路径。
|
||||
// stage1 is_builtin_type_kw:bool / any 作为类型路径;stage3 增加 `type`。
|
||||
if @enum2int(k) == @enum2int(Keyword.Bool) ||
|
||||
@enum2int(k) == @enum2int(Keyword.Any) {
|
||||
@enum2int(k) == @enum2int(Keyword.Any) ||
|
||||
@enum2int(k) == @enum2int(Keyword.Type) {
|
||||
advance(self);
|
||||
ret new_node(self, .TypePath(.{
|
||||
.name = bt.text,
|
||||
@@ -1357,6 +1444,21 @@ fn parse_primary(self: *Parser): usize {
|
||||
if @enum2int(k) == @enum2int(Keyword.Loop) { ret parse_loop_expr(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.For) { ret parse_for_expr(self); }
|
||||
if @enum2int(k) == @enum2int(Keyword.Match) { ret parse_match_expr(self); }
|
||||
// `comptime <expr>`:编译期表达式(可调用普通函数、返回类型等)。
|
||||
if @enum2int(k) == @enum2int(Keyword.Comptime) {
|
||||
advance(self);
|
||||
var cbody = parse_expr(self);
|
||||
ret new_node(self, .ExprComptime(.{ .body = cbody }), t);
|
||||
}
|
||||
// 类型即表达式:仅在 comptime 上下文内允许 `struct { ... }` 等聚合类型。
|
||||
if self.comptime_depth > 0 {
|
||||
if @enum2int(k) == @enum2int(Keyword.Struct) ||
|
||||
@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");
|
||||
ret 0;
|
||||
},
|
||||
@@ -1553,6 +1655,15 @@ fn parse_comptime_call(self: *Parser): usize {
|
||||
args = parse_expr_list(self);
|
||||
if !expect_punct(self, .RParen, "')' in comptime call") { ret 0; }
|
||||
}
|
||||
// `@name(args){ ... }`:构造该编译期类型的值(聚合初始化)。
|
||||
if at_punct(self, .LBrace) && self.stop_agg == 0 {
|
||||
var fields = parse_aggregate_fields(self);
|
||||
ret new_node(self, .ExprComptimeInit(.{
|
||||
.name = name.text,
|
||||
.args = args,
|
||||
.fields = fields,
|
||||
}), start);
|
||||
}
|
||||
ret new_node(self, .ExprComptimeCall(.{ .name = name.text, .args = args }), start);
|
||||
}
|
||||
|
||||
@@ -1791,7 +1902,18 @@ fn parse_ret_stmt(self: *Parser): usize {
|
||||
advance(self);
|
||||
var expr: usize = 0;
|
||||
var t = peek(self);
|
||||
if t != null && can_start_expr(t.kind) { expr = parse_expr(self); }
|
||||
var can = false;
|
||||
if t != null {
|
||||
can = can_start_expr(t.kind);
|
||||
// comptime 上下文:允许返回类型表达式(如 `ret struct { ... };`)。
|
||||
if !can && self.comptime_depth > 0 {
|
||||
if kind_is_keyword(t.kind, .Struct) || kind_is_keyword(t.kind, .Union) ||
|
||||
kind_is_keyword(t.kind, .Enum) || kind_is_keyword(t.kind, .Shape) {
|
||||
can = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if can { expr = parse_expr(self); }
|
||||
t = peek(self);
|
||||
if t != null && kind_is_punct(t.kind, .Semicolon) { advance(self); }
|
||||
ret new_node(self, .StatementRet(.{ .expr = expr }), kw);
|
||||
@@ -2223,10 +2345,60 @@ fn test_big_int_literal() {
|
||||
check_int("0o_17", "15");
|
||||
}
|
||||
|
||||
fn expect_contains(buf: *ByteBuffer, needle: []u8) {
|
||||
var actual = ByteBuffer.as_slice(buf.*);
|
||||
if needle.len > actual.len { @assert(false, "contains length"); }
|
||||
var i: usize = 0;
|
||||
while i + needle.len <= actual.len {
|
||||
var j: usize = 0;
|
||||
var ok = true;
|
||||
while j < needle.len {
|
||||
if actual.ptr[i + j] != needle.ptr[j] { ok = false; break; }
|
||||
j = j + 1;
|
||||
}
|
||||
if ok { ret; }
|
||||
i = i + 1;
|
||||
}
|
||||
@assert(false, "expected substring not found");
|
||||
}
|
||||
|
||||
// stage3 语法:`comptime fn ... (t: type) type { ... }` 与省略冒号的返回类型。
|
||||
fn test_parse_comptime_fn() {
|
||||
var src: []u8 = "comptime fn vec(t: type) type { ret t; }\n";
|
||||
var toks = lex_tokens(src);
|
||||
defer { Vec.drop(&toks); }
|
||||
|
||||
var sources = set_new();
|
||||
defer { set_drop(&sources); }
|
||||
match set_add(&sources, ByteView.from_slice("test.spl"), ByteView.from_slice(src)) {
|
||||
.Ok(index) => {},
|
||||
.Err(error) => { @assert(false, "set_add"); },
|
||||
}
|
||||
|
||||
var ast: Ast = undefined;
|
||||
match Ast.new(&sources) {
|
||||
.Ok(v) => { ast = v; },
|
||||
.Err(error) => { @assert(false, "ast new"); },
|
||||
}
|
||||
defer { Ast.drop(&ast); }
|
||||
|
||||
var p = new(&ast, toks, null);
|
||||
parse(&p);
|
||||
@assert(!p.failed, "comptime fn parse ok");
|
||||
@assert(p.error_count == 0, "comptime fn no errors");
|
||||
|
||||
var buf = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&buf); }
|
||||
@assert(Ast.dump(&ast, &buf), "dump");
|
||||
expect_contains(&buf, "fn_decl");
|
||||
expect_contains(&buf, "type_path");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_peek_advance();
|
||||
test_new_node_span();
|
||||
test_parse_decls();
|
||||
test_parse_main();
|
||||
test_big_int_literal();
|
||||
test_parse_comptime_fn();
|
||||
}
|
||||
+35
-2
@@ -13,7 +13,8 @@ type Ast = @import("./ast.spl");
|
||||
type Parser = @import("./parser.spl");
|
||||
type Sema = @import("./sema.spl");
|
||||
type .{ .append_usize } = @import("./utils/fmt.spl");
|
||||
type .{ .read_all, .print, .eprint } = @import("./utils/io.spl");
|
||||
type .{ .read_all, .print, .eprint, .open_cstr, .write_all } = @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 .{ .Diag, .Code, .span_none } = @import("./diag.spl");
|
||||
type Logger = @import("./utils/log.spl");
|
||||
@@ -25,6 +26,7 @@ type LayoutM = @import("./layout.spl");
|
||||
type .{ .Layout } = @import("./layout.spl");
|
||||
type Ir2VmM = @import("./ir2vm.spl");
|
||||
type .{ .Ir2Vm } = @import("./ir2vm.spl");
|
||||
type Ir2CM = @import("./ir2c.spl");
|
||||
type .{ .alloc, .free } = @import("./sys/mem.spl");
|
||||
|
||||
fn token_at(tokens: *Vec, index: usize): Token {
|
||||
@@ -566,6 +568,7 @@ type PipeMode = enum {
|
||||
Vm, // 继续 layout + ir2vm,dump VM
|
||||
Compile, // 继续 layout + ir2vm,写 outpath
|
||||
State, // 只逐步打日志(诊断“卡在哪一步”)
|
||||
C, // 继续 layout + ir2c,dump C
|
||||
}
|
||||
|
||||
fn step(mode: PipeMode, s: []u8) {
|
||||
@@ -723,6 +726,31 @@ fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView)
|
||||
LayoutM.resolve(&layout, &sema.types.ir);
|
||||
step(mode, "state: layout ok\n");
|
||||
|
||||
if @enum2int(mode) == @enum2int(PipeMode.C) {
|
||||
var cout = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&cout); }
|
||||
var ok = Ir2CM.compile(&builder.ir, &sema.types.ir, &layout, &sema.attr, &cout);
|
||||
if outpath.len != 0 {
|
||||
var f = open_cstr(outpath, ByteView.from_slice("wb"));
|
||||
if !File.valid(&f) {
|
||||
Logger.error(log, "cannot open C output file");
|
||||
ret 1;
|
||||
}
|
||||
match write_all(&f, ByteBuffer.as_slice(cout)) { .Ok => {}, .Err(error) => {} }
|
||||
ignore_i32(File.close(&f));
|
||||
} else {
|
||||
match print(ByteBuffer.as_slice(cout)) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret 1; },
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
Logger.error(log, "ir2c failed (see generated C for unsupported markers)");
|
||||
ret 1;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
var ir2vm: Ir2Vm = undefined;
|
||||
Ir2VmM.init(&ir2vm, &builder.ir, &sema.types.ir, &layout, &sema.attr);
|
||||
defer { Ir2VmM.drop(&ir2vm); }
|
||||
@@ -776,11 +804,13 @@ fn main(argc: i32, argv: **u8): i32 {
|
||||
}
|
||||
|
||||
if argc < 4 {
|
||||
Logger.fatal(&log, "usage: splc1 --dump <lex|ast|sema|ir|vm> <file> [out]");
|
||||
Logger.fatal(&log, "usage: splc1 --dump <lex|ast|sema|ir|vm|c> <file> [out]");
|
||||
ret 1;
|
||||
}
|
||||
var mode = ByteView.from_cstr(argv[2]);
|
||||
var path = ByteView.from_cstr(argv[3]);
|
||||
var dout = ByteView.empty();
|
||||
if argc >= 5 { dout = ByteView.from_cstr(argv[4]); }
|
||||
|
||||
if ByteView.eq(mode, ByteView.from_slice("lex")) {
|
||||
ret dump_lex(&log, path);
|
||||
@@ -800,6 +830,9 @@ fn main(argc: i32, argv: **u8): i32 {
|
||||
if ByteView.eq(mode, ByteView.from_slice("state")) {
|
||||
ret run_pipeline(&log, path, .State, ByteView.empty());
|
||||
}
|
||||
if ByteView.eq(mode, ByteView.from_slice("c")) {
|
||||
ret run_pipeline(&log, path, .C, dout);
|
||||
}
|
||||
Logger.fatal(&log, "dump mode not implemented");
|
||||
ret 1;
|
||||
}
|
||||
@@ -0,0 +1,629 @@
|
||||
/* 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__ */
|
||||
@@ -0,0 +1,109 @@
|
||||
/* spl_vm_embed.h - 原生编译器内嵌 VM(comptime 执行)。
|
||||
*
|
||||
* 单 TU 合并式:定义 SPL_VM_EMBED_IMPLIMENTATION 后 include 本头,会把 stage0 的
|
||||
* spl_mcode.c / spl_syscall.c / spl_vm.c 一并纳入当前翻译单元(不单独编译/链接)。
|
||||
*/
|
||||
|
||||
#ifndef __SPL_VM_EMBED_H__
|
||||
#define __SPL_VM_EMBED_H__
|
||||
|
||||
void *vm_new(void);
|
||||
void vm_drop(void *vm);
|
||||
int vm_load_from_buffer(void *vm, const void *buf, unsigned long long len);
|
||||
void vm_push(void *vm, unsigned long long val);
|
||||
int vm_call(void *vm, const char *name, unsigned long long nargs);
|
||||
int vm_run(void *vm);
|
||||
|
||||
#ifdef SPL_VM_EMBED_IMPLIMENTATION
|
||||
|
||||
#include "../../stage0/spl_mcode.h"
|
||||
void spl_syscall_register(spl_prog_t *prog) { (void)prog; /* stub */ }
|
||||
|
||||
#include "../../stage0/spl_mcode.c"
|
||||
#include "../../stage0/spl_vm.c"
|
||||
|
||||
void *vm_new(void) {
|
||||
spl_vm_t *vm = (spl_vm_t *)malloc(sizeof(spl_vm_t));
|
||||
if (!vm)
|
||||
return 0;
|
||||
spl_vm_init(vm);
|
||||
return vm;
|
||||
}
|
||||
|
||||
void vm_drop(void *p) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
if (!vm)
|
||||
return;
|
||||
if (vm->prog) {
|
||||
spl_prog_drop(vm->prog);
|
||||
free(vm->prog);
|
||||
vm->prog = 0;
|
||||
}
|
||||
spl_vm_drop(vm);
|
||||
free(vm);
|
||||
}
|
||||
|
||||
int vm_load_from_buffer(void *p, const void *buf, unsigned long long len) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
spl_prog_t *prog;
|
||||
if (!vm || !buf)
|
||||
return -1;
|
||||
prog = (spl_prog_t *)malloc(sizeof(spl_prog_t));
|
||||
if (!prog)
|
||||
return -1;
|
||||
if (spl_prog_load_from_buffer((const char *)buf, (usize)len, prog) != 0) {
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
spl_syscall_register(prog);
|
||||
if (spl_vm_load_prog(vm, prog) != 0) {
|
||||
spl_prog_drop(prog);
|
||||
free(prog);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vm_push(void *p, unsigned long long val) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
if (!vm)
|
||||
return;
|
||||
if (vm->sp >= vm->config.max_stack_depth)
|
||||
return;
|
||||
vm->stacks.data[vm->sp++] = (spl_vm_val_t)val;
|
||||
}
|
||||
|
||||
int vm_call(void *p, const char *name, unsigned long long nargs) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
spl_vm_func_t *func;
|
||||
if (!vm || !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;
|
||||
/* 入口调用: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) {
|
||||
spl_vm_t *vm = (spl_vm_t *)p;
|
||||
if (!vm)
|
||||
return -1;
|
||||
spl_vm_run_until(vm, 0);
|
||||
return (int)vm->exit_code;
|
||||
}
|
||||
#endif /* SPL_VM_EMBED_IMPLIMENTATION */
|
||||
|
||||
#endif /* __SPL_VM_EMBED_H__ */
|
||||
+1
-1
@@ -5,7 +5,7 @@ type spl_vm_val_t = u64; // usize
|
||||
#extern(vm) fn vm_drop(vm: *any): void;
|
||||
|
||||
#extern(vm) fn vm_load(vm: *any, fpath: *u8): void;
|
||||
#extern(vm) fn vm_load_from_buffer(vm: *any, buffer: []u8): void;
|
||||
#extern(vm) fn vm_load_from_buffer(vm: *any, buf: *any, len: usize): i32;
|
||||
|
||||
#extern(vm) fn vm_push(vm: *any, val: spl_vm_val_t): void;
|
||||
// TODO
|
||||
|
||||
Reference in new issue
Block a user