stage2 提供近乎完整的功能但出现严重性能问题 添加stage0的原生性能记录 stage1添加更多内置函数 nob重构代码使其更加健壮
This commit is contained in:
1 parent
b5a7a811b3
commit
e02b0c633a
35 files changed
+14018
-216
No files matched your search
+578
-12
@@ -1,8 +1,8 @@
|
||||
// Expect retcode = 0
|
||||
|
||||
// splc1 (stage2 自举编译器入口)。
|
||||
// 当前仅实现 `--dump lex <file> [out]`:读取源文件,词法分析,按与 stage1
|
||||
// splc0 完全一致的格式打印 token 列表,供 diff 测试逐字节比对。
|
||||
// 当前实现 `--dump <lex|ast|sema|ir|vm> <file> [out]`:读取源文件,按与 stage1
|
||||
// splc0 完全一致的格式打印各阶段产物,供 diff 测试逐字节比对。
|
||||
|
||||
type .{ .ByteView } = @import("./utils/byte_view.spl");
|
||||
type .{ .ByteBuffer } = @import("./utils/byte_buffer.spl");
|
||||
@@ -11,11 +11,21 @@ type .{ .Token, .kind_name } = @import("./token.spl");
|
||||
type .{ .Lexer } = @import("./lexer.spl");
|
||||
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 .{ .set_new, .set_add, .set_drop } = @import("./utils/source.spl");
|
||||
type Diag = @import("./diag.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");
|
||||
type .{ .Loader, .LoadResult } = @import("./sema.spl");
|
||||
type IrBuilder = @import("./ir_builder.spl");
|
||||
type Ir = @import("./ir.spl");
|
||||
type Ast2Ir = @import("./ast2ir.spl");
|
||||
type LayoutM = @import("./layout.spl");
|
||||
type .{ .Layout } = @import("./layout.spl");
|
||||
type Ir2VmM = @import("./ir2vm.spl");
|
||||
type .{ .Ir2Vm } = @import("./ir2vm.spl");
|
||||
type .{ .alloc, .free } = @import("./sys/mem.spl");
|
||||
|
||||
fn token_at(tokens: *Vec, index: usize): Token {
|
||||
match Vec.get(tokens, index) {
|
||||
@@ -209,31 +219,587 @@ fn dump_ast(log: *Logger, path: ByteView): i32 {
|
||||
ret 0;
|
||||
}
|
||||
|
||||
// ---- @import 模块加载器(driver 侧)----
|
||||
|
||||
type ModEntry = struct {
|
||||
path: ByteView,
|
||||
ast: *Ast,
|
||||
ok: bool,
|
||||
}
|
||||
|
||||
type LoaderCtx = struct {
|
||||
sources: *Sources,
|
||||
mods: Vec, // ModEntry
|
||||
buffers: Vec, // ByteBuffer(路径/文本稳定存储)
|
||||
diag: *Diag,
|
||||
}
|
||||
|
||||
fn ignore_bool(b: bool) {}
|
||||
|
||||
fn append_bytes(buf: *ByteBuffer, bytes: []u8): bool {
|
||||
match ByteBuffer.append(buf, bytes) {
|
||||
.Ok => { ret true; },
|
||||
.Err(error) => { ret false; },
|
||||
}
|
||||
ret false;
|
||||
}
|
||||
|
||||
fn append_byte(buf: *ByteBuffer, b: u8): bool {
|
||||
match ByteBuffer.append_byte(buf, b) {
|
||||
.Ok => { ret true; },
|
||||
.Err(error) => { ret false; },
|
||||
}
|
||||
ret false;
|
||||
}
|
||||
|
||||
fn sub(p: ByteView, a: usize, b: usize): []u8 {
|
||||
ret ByteView.as_slice(p)[a..b];
|
||||
}
|
||||
|
||||
// 处理 "..":弹出 out 的最后一个路径分量;无分量可弹(空 / 根 / 末段已是 "..")时,
|
||||
// 退化为追加 ".."(相对路径的语义),绝对根则忽略。这样 "../x" 与规范路径能归一到同一模块。
|
||||
fn normalize_pop(out: *ByteBuffer) {
|
||||
var cur = ByteBuffer.as_slice(out.*);
|
||||
if cur.len == 0 {
|
||||
ignore_bool(append_bytes(out, ".."));
|
||||
ret;
|
||||
}
|
||||
var end = cur.len;
|
||||
if cur.ptr[end - 1] == '/' {
|
||||
if end == 1 { ret; } // 绝对根 "/":忽略
|
||||
end = end - 1;
|
||||
}
|
||||
var start = end;
|
||||
while start > 0 && cur.ptr[start - 1] != '/' { start = start - 1; }
|
||||
if end - start == 2 && cur.ptr[start] == '.' && cur.ptr[start + 1] == '.' {
|
||||
ignore_bool(append_bytes(out, ".."));
|
||||
ret;
|
||||
}
|
||||
var new_len: usize = 0;
|
||||
if start > 0 { new_len = start - 1; }
|
||||
if new_len == 0 && cur.ptr[0] == '/' { new_len = 1; }
|
||||
out.*.storage.len = new_len;
|
||||
}
|
||||
|
||||
// 折叠重复分隔符、解析 "." 与 ".."、'\' 统一成 '/'。
|
||||
fn normalize_into(out: *ByteBuffer, p: ByteView) {
|
||||
if p.len > 0 && (p.ptr[0] == '/' || p.ptr[0] == '\\') {
|
||||
ignore_bool(append_byte(out, '/'));
|
||||
}
|
||||
var i: usize = 0;
|
||||
while i < p.len {
|
||||
while i < p.len && (p.ptr[i] == '/' || p.ptr[i] == '\\') { i = i + 1; }
|
||||
if i >= p.len { break; }
|
||||
var start = i;
|
||||
while i < p.len && p.ptr[i] != '/' && p.ptr[i] != '\\' { i = i + 1; }
|
||||
var seg = sub(p, start, i);
|
||||
if seg.len == 1 && seg.ptr[0] == '.' { continue; }
|
||||
if seg.len == 2 && seg.ptr[0] == '.' && seg.ptr[1] == '.' {
|
||||
normalize_pop(out);
|
||||
continue;
|
||||
}
|
||||
var cur = ByteBuffer.as_slice(out.*);
|
||||
if cur.len > 0 && cur.ptr[cur.len - 1] != '/' {
|
||||
ignore_bool(append_byte(out, '/'));
|
||||
}
|
||||
ignore_bool(append_bytes(out, seg));
|
||||
}
|
||||
}
|
||||
|
||||
// 把 path 拼到 from 所在目录,并规范化;绝对路径直通。
|
||||
fn resolve_path(from: ByteView, path: ByteView): ByteBuffer {
|
||||
var out = ByteBuffer.new();
|
||||
var absolute = false;
|
||||
if path.len > 0 && (path.ptr[0] == '/' || path.ptr[0] == '\\') { absolute = true; }
|
||||
if path.len >= 2 && path.ptr[1] == ':' { absolute = true; }
|
||||
if absolute {
|
||||
normalize_into(&out, path);
|
||||
ret out;
|
||||
}
|
||||
var last: isize = -1;
|
||||
var i: usize = 0;
|
||||
while i < from.len {
|
||||
if from.ptr[i] == '/' || from.ptr[i] == '\\' { last = i as isize; }
|
||||
i = i + 1;
|
||||
}
|
||||
if last >= 0 {
|
||||
ignore_bool(append_bytes(&out, sub(from, 0, (last as usize) + 1)));
|
||||
}
|
||||
ignore_bool(append_bytes(&out, ByteView.as_slice(path)));
|
||||
var norm = ByteBuffer.new();
|
||||
normalize_into(&norm, ByteView.from_slice(ByteBuffer.as_slice(out)));
|
||||
ByteBuffer.drop(&out);
|
||||
ret norm;
|
||||
}
|
||||
|
||||
fn mod_at(ctx: *LoaderCtx, i: usize): *ModEntry {
|
||||
match Vec.get(&ctx.mods, i) {
|
||||
.Ok(ptr) => { ret ptr as *ModEntry; },
|
||||
.Err(error) => { ret null; },
|
||||
}
|
||||
ret null;
|
||||
}
|
||||
|
||||
fn driver_load(user: *any, from: ByteView, path: ByteView, diag: *Diag): LoadResult {
|
||||
var ctx = user as *LoaderCtx;
|
||||
var absbuf = resolve_path(from, path);
|
||||
var abs = ByteView.from_slice(ByteBuffer.as_slice(absbuf));
|
||||
|
||||
var i: usize = 0;
|
||||
while i < ctx.mods.len {
|
||||
var m = mod_at(ctx, i);
|
||||
if m != null && ByteView.eq(m.path, abs) {
|
||||
ByteBuffer.drop(&absbuf);
|
||||
if !m.ok {
|
||||
Diag.err(diag, span_none(), .CircularDep, "@import: circular import");
|
||||
ret .Missing;
|
||||
}
|
||||
ret .Ok(m.ast);
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
var midx = ctx.mods.len;
|
||||
var placeholder = ModEntry { .path = abs, .ast = null, .ok = false };
|
||||
match Vec.push(&ctx.mods, &placeholder as *u8) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ByteBuffer.drop(&absbuf); ret .Missing; },
|
||||
}
|
||||
match Vec.push(&ctx.buffers, &absbuf as *u8) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ByteBuffer.drop(&absbuf); ret .Missing; },
|
||||
}
|
||||
|
||||
var content = ByteBuffer.new();
|
||||
match read_all(abs, &content) {
|
||||
.Ok => {},
|
||||
.Err(error) => {
|
||||
Diag.err(diag, span_none(), .LexUnexpected, "@import: cannot open module");
|
||||
ByteBuffer.drop(&content);
|
||||
ret .Missing;
|
||||
},
|
||||
}
|
||||
var text = ByteView.from_slice(ByteBuffer.as_slice(content));
|
||||
var sidx: usize = 0;
|
||||
match set_add(ctx.sources, abs, text) {
|
||||
.Ok(index) => { sidx = index; },
|
||||
.Err(error) => { ByteBuffer.drop(&content); ret .Missing; },
|
||||
}
|
||||
match Vec.push(&ctx.buffers, &content as *u8) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ByteBuffer.drop(&content); ret .Missing; },
|
||||
}
|
||||
|
||||
var tokens = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||
match Lexer.lex(text, diag, 0) {
|
||||
.Ok(v) => { tokens = v; },
|
||||
.Err(error) => { Vec.drop(&tokens); ret .Missing; },
|
||||
}
|
||||
|
||||
var ast_val: Ast = undefined;
|
||||
match Ast.new(ctx.sources) {
|
||||
.Ok(v) => { ast_val = v; },
|
||||
.Err(error) => { Vec.drop(&tokens); ret .Missing; },
|
||||
}
|
||||
|
||||
var p = Parser.new(&ast_val, tokens, diag);
|
||||
p.file = sidx;
|
||||
Parser.parse(&p);
|
||||
Vec.drop(&tokens);
|
||||
if Diag.has_error(diag.*) || p.failed {
|
||||
Ast.drop(&ast_val);
|
||||
ret .Missing;
|
||||
}
|
||||
|
||||
var raw = alloc(@sizeof(Ast));
|
||||
if raw == null {
|
||||
Ast.drop(&ast_val);
|
||||
ret .Missing;
|
||||
}
|
||||
var ap = raw as *Ast;
|
||||
ap.* = ast_val;
|
||||
|
||||
var slot = mod_at(ctx, midx);
|
||||
if slot != null {
|
||||
slot.ast = ap;
|
||||
slot.ok = true;
|
||||
}
|
||||
ret .Ok(ap);
|
||||
}
|
||||
|
||||
fn loader_ctx_drop(ctx: *LoaderCtx) {
|
||||
var i: usize = 0;
|
||||
while i < ctx.mods.len {
|
||||
var m = mod_at(ctx, i);
|
||||
if m != null && m.ok && m.ast != null {
|
||||
Ast.drop(m.ast);
|
||||
free(m.ast as *any);
|
||||
m.ast = null;
|
||||
m.ok = false;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
Vec.drop(&ctx.mods);
|
||||
|
||||
var j: usize = 0;
|
||||
while j < ctx.buffers.len {
|
||||
match Vec.get(&ctx.buffers, j) {
|
||||
.Ok(ptr) => { ByteBuffer.drop(ptr as *ByteBuffer); },
|
||||
.Err(error) => {},
|
||||
}
|
||||
j = j + 1;
|
||||
}
|
||||
Vec.drop(&ctx.buffers);
|
||||
}
|
||||
|
||||
fn dump_sema(log: *Logger, path: ByteView): i32 {
|
||||
var content = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&content); }
|
||||
match read_all(path, &content) {
|
||||
.Ok => {},
|
||||
.Err(error) => {
|
||||
Logger.error(log, "cannot read input file");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
var text = ByteView.from_slice(ByteBuffer.as_slice(content));
|
||||
|
||||
var sources = set_new();
|
||||
defer { set_drop(&sources); }
|
||||
match set_add(&sources, path, text) {
|
||||
.Ok(index) => {},
|
||||
.Err(error) => {
|
||||
Logger.error(log, "cannot register source");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
var diag = Diag.new(&sources);
|
||||
defer { Diag.drop(&diag); }
|
||||
|
||||
var tokens = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||
match Lexer.lex(text, &diag, 0) {
|
||||
.Ok(v) => { tokens = v; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "lex failed");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
defer { Vec.drop(&tokens); }
|
||||
|
||||
var ast: Ast = undefined;
|
||||
match Ast.new(&sources) {
|
||||
.Ok(v) => { ast = v; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "ast init failed");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
defer { Ast.drop(&ast); }
|
||||
|
||||
var p = Parser.new(&ast, tokens, &diag);
|
||||
Parser.parse(&p);
|
||||
|
||||
if Diag.has_error(diag) || p.failed {
|
||||
var rendered = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&rendered); }
|
||||
match Diag.render_human(&diag, &rendered) {
|
||||
.Ok => {},
|
||||
.Err(error) => {},
|
||||
}
|
||||
eprint(ByteBuffer.as_slice(rendered));
|
||||
ret 1;
|
||||
}
|
||||
|
||||
var ctx = LoaderCtx {
|
||||
.sources = &sources,
|
||||
.mods = Vec.blank(@sizeof(ModEntry), @alignof(ModEntry)),
|
||||
.buffers = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
|
||||
.diag = &diag,
|
||||
};
|
||||
defer { loader_ctx_drop(&ctx); }
|
||||
var loader = Loader { .load = driver_load, .user = &ctx as *any };
|
||||
|
||||
var sema: Sema = undefined;
|
||||
match Sema.new(&ast, &diag) {
|
||||
.Ok(v) => { sema = v; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "sema init failed");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
defer { Sema.drop(&sema); }
|
||||
sema.sources = &sources;
|
||||
sema.loader = &loader;
|
||||
|
||||
Sema.run(&sema);
|
||||
|
||||
if Diag.has_error(diag) {
|
||||
var rendered = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&rendered); }
|
||||
match Diag.render_human(&diag, &rendered) {
|
||||
.Ok => {},
|
||||
.Err(error) => {},
|
||||
}
|
||||
eprint(ByteBuffer.as_slice(rendered));
|
||||
ret 1;
|
||||
}
|
||||
|
||||
var out = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&out); }
|
||||
if !Sema.dump(&sema, &out) {
|
||||
Logger.fatal(log, "out of memory while dumping sema");
|
||||
ret 1;
|
||||
}
|
||||
if !Ast.dump(&ast, &out) {
|
||||
Logger.fatal(log, "out of memory while dumping ast");
|
||||
ret 1;
|
||||
}
|
||||
match print(ByteBuffer.as_slice(out)) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret 1; },
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
type PipeMode = enum {
|
||||
Ir, // 只 dump IR
|
||||
Vm, // 继续 layout + ir2vm,dump VM
|
||||
Compile, // 继续 layout + ir2vm,写 outpath
|
||||
State, // 只逐步打日志(诊断“卡在哪一步”)
|
||||
}
|
||||
|
||||
fn step(mode: PipeMode, s: []u8) {
|
||||
if @enum2int(mode) == @enum2int(PipeMode.State) {
|
||||
match eprint(s) { .Ok => {}, .Err(error) => {} }
|
||||
}
|
||||
}
|
||||
|
||||
// 完整前端流水线:lex -> parse -> sema -> ast2ir ->(可选)layout + ir2vm。
|
||||
fn run_pipeline(log: *Logger, path: ByteView, mode: PipeMode, outpath: ByteView): i32 {
|
||||
var content = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&content); }
|
||||
match read_all(path, &content) {
|
||||
.Ok => {},
|
||||
.Err(error) => {
|
||||
Logger.error(log, "cannot read input file");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
var text = ByteView.from_slice(ByteBuffer.as_slice(content));
|
||||
step(mode, "state: read ok\n");
|
||||
|
||||
var sources = set_new();
|
||||
defer { set_drop(&sources); }
|
||||
var main_idx: usize = 0;
|
||||
match set_add(&sources, path, text) {
|
||||
.Ok(index) => { main_idx = index; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "cannot register source");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
var diag = Diag.new(&sources);
|
||||
defer { Diag.drop(&diag); }
|
||||
|
||||
var tokens = Vec.blank(@sizeof(Token), @alignof(Token));
|
||||
match Lexer.lex(text, &diag, 0) {
|
||||
.Ok(v) => { tokens = v; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "lex failed");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
defer { Vec.drop(&tokens); }
|
||||
|
||||
var ast: Ast = undefined;
|
||||
match Ast.new(&sources) {
|
||||
.Ok(v) => { ast = v; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "ast init failed");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
defer { Ast.drop(&ast); }
|
||||
|
||||
var p = Parser.new(&ast, tokens, &diag);
|
||||
p.file = main_idx;
|
||||
Parser.parse(&p);
|
||||
|
||||
if Diag.has_error(diag) || p.failed {
|
||||
var rendered = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&rendered); }
|
||||
match Diag.render_human(&diag, &rendered) { .Ok => {}, .Err(error) => {} }
|
||||
eprint(ByteBuffer.as_slice(rendered));
|
||||
ret 1;
|
||||
}
|
||||
step(mode, "state: parse ok\n");
|
||||
|
||||
var ctx = LoaderCtx {
|
||||
.sources = &sources,
|
||||
.mods = Vec.blank(@sizeof(ModEntry), @alignof(ModEntry)),
|
||||
.buffers = Vec.blank(@sizeof(ByteBuffer), @alignof(ByteBuffer)),
|
||||
.diag = &diag,
|
||||
};
|
||||
defer { loader_ctx_drop(&ctx); }
|
||||
var loader = Loader { .load = driver_load, .user = &ctx as *any };
|
||||
|
||||
var sema: Sema = undefined;
|
||||
match Sema.new(&ast, &diag) {
|
||||
.Ok(v) => { sema = v; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "sema init failed");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
defer { Sema.drop(&sema); }
|
||||
sema.sources = &sources;
|
||||
sema.loader = &loader;
|
||||
|
||||
Sema.run(&sema);
|
||||
if Diag.has_error(diag) {
|
||||
var rendered = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&rendered); }
|
||||
match Diag.render_human(&diag, &rendered) { .Ok => {}, .Err(error) => {} }
|
||||
eprint(ByteBuffer.as_slice(rendered));
|
||||
ret 1;
|
||||
}
|
||||
|
||||
step(mode, "state: sema ok\n");
|
||||
|
||||
var builder = IrBuilder.builder_new();
|
||||
defer { IrBuilder.builder_drop(&builder); }
|
||||
var a2ir = Ast2Ir.new(&builder, &sema, &diag);
|
||||
defer { Ast2Ir.drop(&a2ir); }
|
||||
|
||||
var asts = Vec.blank(@sizeof(usize), @alignof(usize));
|
||||
defer { Vec.drop(&asts); }
|
||||
var ap: *Ast = *
|
||||
match Vec.push(&asts, &ap as *u8) { .Ok => {}, .Err(error) => {} }
|
||||
var mi: usize = 0;
|
||||
while mi < ctx.mods.len {
|
||||
var m = mod_at(&ctx, mi);
|
||||
if m != null && m.ok && m.ast != null {
|
||||
var mp: *Ast = m.ast;
|
||||
match Vec.push(&asts, &mp as *u8) { .Ok => {}, .Err(error) => {} }
|
||||
}
|
||||
mi = mi + 1;
|
||||
}
|
||||
|
||||
Ast2Ir.run(&a2ir, asts);
|
||||
if Diag.has_error(diag) {
|
||||
var rendered = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&rendered); }
|
||||
match Diag.render_human(&diag, &rendered) { .Ok => {}, .Err(error) => {} }
|
||||
eprint(ByteBuffer.as_slice(rendered));
|
||||
ret 1;
|
||||
}
|
||||
|
||||
step(mode, "state: ast2ir ok\n");
|
||||
|
||||
if @enum2int(mode) == @enum2int(PipeMode.Ir) {
|
||||
var out = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&out); }
|
||||
if !Ir.dump(&builder.ir, &sema.types.ir, &sema.attr, &out) {
|
||||
Logger.fatal(log, "out of memory while dumping ir");
|
||||
ret 1;
|
||||
}
|
||||
match print(ByteBuffer.as_slice(out)) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret 1; },
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
// 类型就绪后解析布局,再做 IR -> VM。
|
||||
var layout: Layout = undefined;
|
||||
match LayoutM.new() {
|
||||
.Ok(v) => { layout = v; },
|
||||
.Err(error) => {
|
||||
Logger.error(log, "layout init failed");
|
||||
ret 1;
|
||||
},
|
||||
}
|
||||
defer { LayoutM.drop(&layout); }
|
||||
LayoutM.resolve(&layout, &sema.types.ir);
|
||||
step(mode, "state: layout ok\n");
|
||||
|
||||
var ir2vm: Ir2Vm = undefined;
|
||||
Ir2VmM.init(&ir2vm, &builder.ir, &sema.types.ir, &layout, &sema.attr);
|
||||
defer { Ir2VmM.drop(&ir2vm); }
|
||||
ignore_i32(Ir2VmM.run(&ir2vm));
|
||||
step(mode, "state: ir2vm ok\n");
|
||||
|
||||
if @enum2int(mode) == @enum2int(PipeMode.Vm) {
|
||||
var vout = ByteBuffer.new();
|
||||
defer { ByteBuffer.drop(&vout); }
|
||||
if !Ir2VmM.dump(&ir2vm, &vout) {
|
||||
Logger.fatal(log, "out of memory while dumping vm");
|
||||
ret 1;
|
||||
}
|
||||
match print(ByteBuffer.as_slice(vout)) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret 1; },
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
if @enum2int(mode) == @enum2int(PipeMode.State) {
|
||||
ret 0;
|
||||
}
|
||||
|
||||
if !Ir2VmM.store(&ir2vm, outpath) {
|
||||
Logger.error(log, "cannot write output file");
|
||||
ret 1;
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn ignore_i32(v: i32) {}
|
||||
|
||||
fn main(argc: i32, argv: **u8): i32 {
|
||||
// 无参数:自测运行(`nob test stage2`)视为成功。
|
||||
if argc <= 1 { ret 0; }
|
||||
|
||||
var log = Logger.new(ByteView.from_slice("splc1"));
|
||||
|
||||
if argc < 4 {
|
||||
Logger.fatal(&log, "usage: splc1 --dump <lex|ast> <file> [out]");
|
||||
ret 1;
|
||||
var arg1 = ByteView.from_cstr(argv[1]);
|
||||
|
||||
if !ByteView.eq(arg1, ByteView.from_slice("--dump")) {
|
||||
// 编译模式:splc1 <in> <out>
|
||||
if argc < 3 {
|
||||
Logger.fatal(&log, "usage: splc1 <in> <out> | splc1 --dump <lex|ast|sema|ir|vm> <file>");
|
||||
ret 1;
|
||||
}
|
||||
var in = ByteView.from_cstr(argv[1]);
|
||||
var out = ByteView.from_cstr(argv[2]);
|
||||
ret run_pipeline(&log, in, .Compile, out);
|
||||
}
|
||||
|
||||
var arg1 = ByteView.from_cstr(argv[1]);
|
||||
if argc < 4 {
|
||||
Logger.fatal(&log, "usage: splc1 --dump <lex|ast|sema|ir|vm> <file> [out]");
|
||||
ret 1;
|
||||
}
|
||||
var mode = ByteView.from_cstr(argv[2]);
|
||||
var path = ByteView.from_cstr(argv[3]);
|
||||
|
||||
if !ByteView.eq(arg1, ByteView.from_slice("--dump")) {
|
||||
Logger.fatal(&log, "usage: splc1 --dump <lex|ast> <file> [out]");
|
||||
ret 1;
|
||||
}
|
||||
if ByteView.eq(mode, ByteView.from_slice("lex")) {
|
||||
ret dump_lex(&log, path);
|
||||
}
|
||||
if ByteView.eq(mode, ByteView.from_slice("ast")) {
|
||||
ret dump_ast(&log, path);
|
||||
}
|
||||
if ByteView.eq(mode, ByteView.from_slice("sema")) {
|
||||
ret dump_sema(&log, path);
|
||||
}
|
||||
if ByteView.eq(mode, ByteView.from_slice("ir")) {
|
||||
ret run_pipeline(&log, path, .Ir, ByteView.empty());
|
||||
}
|
||||
if ByteView.eq(mode, ByteView.from_slice("vm")) {
|
||||
ret run_pipeline(&log, path, .Vm, ByteView.empty());
|
||||
}
|
||||
if ByteView.eq(mode, ByteView.from_slice("state")) {
|
||||
ret run_pipeline(&log, path, .State, ByteView.empty());
|
||||
}
|
||||
Logger.fatal(&log, "dump mode not implemented");
|
||||
ret 1;
|
||||
}
|
||||
Reference in new issue
Block a user