164 lines
4.9 KiB
Plaintext
164 lines
4.9 KiB
Plaintext
// Expect retcode = 0
|
|
|
|
// splc1 (stage2 自举编译器入口)。
|
|
// 当前仅实现 `--dump lex <file> [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 .{ .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); },
|
|
}
|
|
ret Token.make(.Unknown(0), ByteView.empty(), 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); }
|
|
|
|
// 非法输入:走诊断(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 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> <file> [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 <lex> <file> [out]");
|
|
ret 1;
|
|
}
|
|
if !ByteView.eq(mode, ByteView.from_slice("lex")) {
|
|
Logger.fatal(&log, "dump mode not implemented");
|
|
ret 1;
|
|
}
|
|
ret dump_lex(&log, path);
|
|
}
|