stage2 提供词法分析器通过与stage1的差异测试 stage1提供数组到切片的隐式转换

This commit is contained in:
zzy committed 2026-09-18 19:40:10 +08:00
1 parent c30143609a
commit bb9d0a9f18
13 files changed
+1275 -52

No files matched your search

+205
View File
@@ -0,0 +1,205 @@
// Expect retcode = 0
// 编译器自身日志(区别于 diag:diag 负责带源码位置的诊断)。
// 输出到 stderr,格式稳定:[LEVEL] name: message\n。
// 消息为借用;Logger 不拥有 name。fatal 打印后终止进程(退出码 1)。
// write 返回 Status;OOM 归类为 .Overflow(无独立内存耗尽错误码)。
type .{ .Status, .make } = @import("error.spl");
type .{ .ByteView } = @import("byte_view.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type .{ .format } = @import("fmt.spl");
type .{ .Value, .int, .str } = @import("value.spl");
type .{ .eprint } = @import("io.spl");
type Exit = @import("../sys/exit.spl");
type Level = enum { Debug, Info, Warn, Error, Fatal }
fn level_name(level: Level): []u8 {
match level {
.Debug => { ret "DEBUG"; },
.Info => { ret "INFO "; },
.Warn => { ret "WARN "; },
.Error => { ret "ERROR"; },
.Fatal => { ret "FATAL"; },
}
ret "NOTSET";
}
type Logger = @self;
name: ByteView,
min_level: Level,
fn new(name: ByteView): Logger {
ret Logger { .name = name, .min_level = .Debug };
}
fn set_level(self: *Logger, level: Level) {
self.min_level = level;
}
fn enabled(self: Logger, level: Level): bool {
ret @enum2int(level) >= @enum2int(self.min_level);
}
// 追加成功返回 true;失败(仅 OOM)返回 false。
fn push(line: *ByteBuffer, text: []u8): bool {
match ByteBuffer.append(line, text) {
.Ok => { ret true; },
.Err(error) => { ret false; },
}
ret false;
}
// 低于 min_level 的日志不写出,直接成功。
fn write(self: *Logger, level: Level, message: []u8): Status {
if !enabled(self.*, level) { ret .Ok; }
var line = ByteBuffer.new();
defer { ByteBuffer.drop(&line); }
if !push(&line, "[") { ret .Err(make(.Overflow)); }
if !push(&line, level_name(level)) { ret .Err(make(.Overflow)); }
if !push(&line, "] ") { ret .Err(make(.Overflow)); }
if !push(&line, ByteView.as_slice(self.name)) { ret .Err(make(.Overflow)); }
if !push(&line, ": ") { ret .Err(make(.Overflow)); }
if !push(&line, message) { ret .Err(make(.Overflow)); }
if !push(&line, "\n") { ret .Err(make(.Overflow)); }
ret eprint(ByteBuffer.as_slice(line));
}
fn debug(self: *Logger, message: []u8): Status {
ret write(self, .Debug, message);
}
fn info(self: *Logger, message: []u8): Status {
ret write(self, .Info, message);
}
fn warn(self: *Logger, message: []u8): Status {
ret write(self, .Warn, message);
}
fn error(self: *Logger, message: []u8): Status {
ret write(self, .Error, message);
}
// 尽力写出后终止;写失败无法挽回,直接退出。
fn fatal(self: *Logger, message: []u8) {
write(self, .Fatal, message);
Exit.fail();
}
// ---- 格式化入口(显式装箱,见 value.spl / fmt.format)----
// 先格式化到临时缓冲;格式错误在写出前返回 Err(.InvalidFormat)。
fn logf(self: *Logger, level: Level, fmt: []u8, args: []Value): Status {
if !enabled(self.*, level) { ret .Ok; }
var message = ByteBuffer.new();
defer { ByteBuffer.drop(&message); }
match format(&message, fmt, args) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret write(self, level, ByteBuffer.as_slice(message));
}
fn debugf(self: *Logger, fmt: []u8, args: []Value): Status {
ret logf(self, .Debug, fmt, args);
}
fn infof(self: *Logger, fmt: []u8, args: []Value): Status {
ret logf(self, .Info, fmt, args);
}
fn warnf(self: *Logger, fmt: []u8, args: []Value): Status {
ret logf(self, .Warn, fmt, args);
}
fn errorf(self: *Logger, fmt: []u8, args: []Value): Status {
ret logf(self, .Error, fmt, args);
}
// 格式化后终止;格式化失败也要退出(避免吞掉致命错误)。
fn fatalf(self: *Logger, fmt: []u8, args: []Value) {
logf(self, .Fatal, fmt, args);
Exit.fail();
}
// ---- 测试 ----
fn expect_name(level: Level, expected: []u8) {
@assert(ByteView.eq(ByteView.from_slice(level_name(level)),
ByteView.from_slice(expected)),
"level name");
}
fn expect_ok(result: Status) {
match result {
.Ok => {},
.Err(error) => { @assert(false, "unexpected log error"); },
}
}
fn test_level_names() {
expect_name(.Debug, "DEBUG");
expect_name(.Info, "INFO ");
expect_name(.Warn, "WARN ");
expect_name(.Error, "ERROR");
expect_name(.Fatal, "FATAL");
var debug: Level = .Debug;
var info: Level = .Info;
var warn: Level = .Warn;
var err: Level = .Error;
var fatal: Level = .Fatal;
@assert(@enum2int(fatal) > @enum2int(err), "fatal above error");
@assert(@enum2int(err) > @enum2int(warn), "error above warn");
@assert(@enum2int(warn) > @enum2int(info), "warn above info");
@assert(@enum2int(info) > @enum2int(debug), "info above debug");
}
fn test_filter_and_write() {
var log = new(ByteView.from_slice("splc1"));
@assert(enabled(log, .Debug), "default shows debug");
@assert(enabled(log, .Fatal), "default shows fatal");
set_level(&log, .Warn);
@assert(!enabled(log, .Debug), "filtered debug");
@assert(!enabled(log, .Info), "filtered info");
@assert(enabled(log, .Warn), "warn passes");
@assert(enabled(log, .Error), "error passes");
// 被过滤的写入不产生输出,且返回成功。
expect_ok(debug(&log, "hidden"));
expect_ok(write(&log, .Info, "hidden too"));
expect_ok(error(&log, "visible"));
}
fn test_logf() {
var log = new(ByteView.from_slice("splc1"));
set_level(&log, .Error);
var args: [2]Value = .[int(3), str("x")];
// 被过滤的级别不写出,直接成功。
expect_ok(infof(&log, "n={} s={}", args[0..2]));
// 通过级别正常写出。
expect_ok(errorf(&log, "n={} s={}", args[0..2]));
// 格式化失败向上传播,且不产出消息。
match errorf(&log, "{} {}", args[0..1]) {
.Ok => { @assert(false, "expected invalid format"); },
.Err(error) => {
match error.kind {
.InvalidFormat => {},
_ => { @assert(false, "expected InvalidFormat"); },
}
},
}
}
fn main() {
test_level_names();
test_filter_and_write();
test_logf();
}