// Expect retcode = 0 // splc1 (stage2 自举编译器入口)。 // 当前仅实现 `--dump lex [out]`:读取源文件,词法分析,按与 stage1 // splc0 完全一致的格式打印 token 列表,供 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 .{ .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 Logger = @import("./utils/log.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] (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); } // 非法输入:走诊断(stage1 在此处是宽容无视,stage2 显式报错)。 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; } 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); } 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 !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; } 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 [out]"); ret 1; } var arg1 = ByteView.from_cstr(argv[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 [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); } Logger.fatal(&log, "dump mode not implemented"); ret 1; }