Files
spl/stage2/splc1.spl
T

822 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Expect retcode = 0
// splc1:自举编译器入口。`splc1 <in> <out>` 编译;`--dump <mode> <file>` 打印各阶段
// 产物,格式必须与 splc0 逐字节一致以通过 diff。
type .{ .ByteView } = @import("./utils/byte_view.spl");
type .{ .ByteBuffer } = @import("./utils/byte_buffer.spl");
type .{ .Vec } = @import("./utils/vec.spl");
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, .open_cstr, .write_all, .File } = @import("./utils/io.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 Ir2CM = @import("./ir2c.spl");
type .{ .alloc, .free } = @import("./utils/memory.spl");
fn token_at(tokens: *Vec, index: usize): Token {
match Vec.get(tokens, index) {
.Ok(ptr) => { ret (ptr as *Token).*; },
.Err(error) => { ret Token.make(.Unknown(0), ByteView.empty(), 0, 0, 0); },
}
ret Token.make(.Unknown(0), ByteView.empty(), 0, 0, 0);
}
// 追加成功返回 true;失败(仅 OOM)返回 false。
fn push(out: *ByteBuffer, bytes: []u8): bool {
match ByteBuffer.append(out, bytes) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
fn push_byte(out: *ByteBuffer, byte: u8): bool {
match ByteBuffer.append_byte(out, byte) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
fn push_usize(out: *ByteBuffer, value: usize): bool {
match append_usize(out, value, 10, false) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
// 输出格式与 splc0.c 的 dump_tokens 一致:
// tokens got (N)\n
// [TOK_TYPE] <lexeme> (line:col)\n
fn dump_tokens(tokens: *Vec): bool {
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
if !push(&out, "tokens got (") { ret false; }
if !push_usize(&out, tokens.len) { ret false; }
if !push(&out, ")\n") { ret false; }
var i: usize = 0;
while i < tokens.len {
var tok = token_at(tokens, i);
if !push(&out, "[") { ret false; }
if !push(&out, kind_name(tok.kind)) { ret false; }
if !push(&out, "] ") { ret false; }
if !push(&out, ByteView.as_slice(tok.text)) { ret false; }
if !push(&out, " (") { ret false; }
if !push_usize(&out, tok.line) { ret false; }
if !push_byte(&out, ':') { ret false; }
if !push_usize(&out, tok.col) { ret false; }
if !push(&out, ")\n") { ret false; }
i = i + 1;
}
match print(ByteBuffer.as_slice(out)) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
fn dump_lex(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); }
// 非法输入:走诊断(显式报错,不静默)。
if Diag.has_error(diag) {
var rendered = ByteBuffer.new();
defer { ByteBuffer.drop(&rendered); }
Diag.render_human(&diag, &rendered);
eprint(ByteBuffer.as_slice(rendered));
ret 1;
}
if !dump_tokens(&tokens) {
Logger.fatal(log, "out of memory while dumping tokens");
ret 1;
}
ret 0;
}
fn dump_ast(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); }
Diag.render_human(&diag, &rendered);
eprint(ByteBuffer.as_slice(rendered));
ret 1;
}
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
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 ModEntry = struct {
path: ByteView,
ast: *Ast,
ok: bool,
}
type LoaderCtx = struct {
sources: *Sources,
mods: Vec, // ModEntry
buffers: Vec, // ByteBuffer(路径/文本稳定存储)
diag: *Diag,
}
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 {
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] == '.' {
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] == '\\') {
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] != '/' {
append_byte(out, '/');
}
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 {
append_bytes(&out, sub(from, 0, (last as usize) + 1));
}
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); }
Diag.render_human(&diag, &rendered);
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); }
Diag.render_human(&diag, &rendered);
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, // 只逐步打日志(诊断“卡在哪一步”)
C, // 继续 layout + ir2c,dump C
}
fn step(mode: PipeMode, s: []u8) {
if @enum2int(mode) == @enum2int(PipeMode.State) {
eprint(s);
}
}
// 完整前端流水线: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); }
Diag.render_human(&diag, &rendered);
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); }
Diag.render_human(&diag, &rendered);
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 = &ast;
Vec.push(&asts, &ap as *u8);
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;
Vec.push(&asts, &mp as *u8);
}
mi = mi + 1;
}
Ast2Ir.run(&a2ir, asts);
if Diag.has_error(diag) {
var rendered = ByteBuffer.new();
defer { ByteBuffer.drop(&rendered); }
Diag.render_human(&diag, &rendered);
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");
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;
}
write_all(&f, ByteBuffer.as_slice(cout));
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); }
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 main(argc: i32, argv: **u8): i32 {
// 无参数:自测运行(`nob test stage2`)视为成功。
if argc <= 1 { ret 0; }
var log = Logger.new(ByteView.from_slice("splc1"));
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);
}
if argc < 4 {
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);
}
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());
}
if ByteView.eq(mode, ByteView.from_slice("c")) {
ret run_pipeline(&log, path, .C, dout);
}
Logger.fatal(&log, "dump mode not implemented");
ret 1;
}