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

+240
View File
@@ -5,6 +5,8 @@
type .{ .Error, .Status, .make } = @import("error.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type .{ .ByteView } = @import("byte_view.spl");
type .{ .Value, .int, .uint, .char, .str, .addr } = @import("value.spl");
type .{ .BigInt, .Bits } = @import("mp.spl");
// 注意:radix 边界直接用字面量。stage1 刻意不做常量求值/折叠,
@@ -69,6 +71,158 @@ fn append_bool(out: *ByteBuffer, value: bool): Status {
ret ByteBuffer.append(out, "false");
}
// ---- 显式装箱值格式化 ----
// Int/Uint 落到整数;signed 仅对 Int 的十进制生效,其余按无符号位模式输出。
fn append_value_int(out: *ByteBuffer, value: Value, radix: u8, uppercase: bool,
signed: bool): Status {
match value {
.Int(v) => {
if signed && radix == 10 { ret append_i64(out, v, radix, uppercase); }
ret append_u64(out, v as u64, radix, uppercase);
},
.Uint(v) => { ret append_u64(out, v, radix, uppercase); },
_ => { ret .Err(make(.InvalidFormat)); },
}
}
fn append_value_char(out: *ByteBuffer, value: Value): Status {
match value {
.Char(c) => { ret ByteBuffer.append_byte(out, c); },
_ => { ret .Err(make(.InvalidFormat)); },
}
}
fn append_value_str(out: *ByteBuffer, value: Value): Status {
match value {
.Str(view) => { ret ByteBuffer.append(out, ByteView.as_slice(view)); },
_ => { ret .Err(make(.InvalidFormat)); },
}
}
fn append_value_ptr(out: *ByteBuffer, value: Value): Status {
match value {
.Ptr(p) => {
match ByteBuffer.append(out, "0x") {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_u64(out, p as usize, 16, false);
},
_ => { ret .Err(make(.InvalidFormat)); },
}
}
// {} 自动格式化:按 Value 变体选择默认表示。
fn append_value_auto(out: *ByteBuffer, value: Value): Status {
match value {
.Int(v) => { ret append_i64(out, v, 10, false); },
.Uint(v) => { ret append_u64(out, v, 10, false); },
.Bool(v) => { ret append_bool(out, v); },
.Char(c) => { ret ByteBuffer.append_byte(out, c); },
.Str(view) => { ret ByteBuffer.append(out, ByteView.as_slice(view)); },
.Ptr(p) => {
match ByteBuffer.append(out, "0x") {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_u64(out, p as usize, 16, false);
},
}
ret .Ok;
}
// {} 风格:{} 自动;{:d} {:i} {:u} {:x} {:X} {:o} {:b} {:c} {:s} {:p} 指定。
// {{ 与 }} 输出字面量括号。参数按序从 args 消费;数量不足、类型不符、未知转换
// 或括号不配返回 Err(.InvalidFormat)。失败不产生部分输出:先在临时缓冲完成。
fn format(out: *ByteBuffer, fmt: []u8, args: []Value): Status {
var temp = ByteBuffer.new();
defer { ByteBuffer.drop(&temp); }
var argi: usize = 0;
var i: usize = 0;
while i < fmt.len {
var c = fmt.ptr[i];
if c == '{' {
if i + 1 < fmt.len && fmt.ptr[i + 1] == '{' {
match ByteBuffer.append_byte(&temp, '{') {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
i = i + 2;
continue;
}
var j = i + 1;
if j < fmt.len && fmt.ptr[j] == ':' { j = j + 1; }
var has_spec = false;
var spec: u8 = 0;
if j < fmt.len && fmt.ptr[j] != '}' {
has_spec = true;
spec = fmt.ptr[j];
j = j + 1;
}
if j >= fmt.len || fmt.ptr[j] != '}' { ret .Err(make(.InvalidFormat)); }
i = j + 1;
if argi >= args.len { ret .Err(make(.InvalidFormat)); }
var value = args.ptr[argi];
argi = argi + 1;
var step: Status = .Ok;
if !has_spec {
step = append_value_auto(&temp, value);
} else if spec == 'd' || spec == 'i' {
step = append_value_int(&temp, value, 10, false, true);
} else if spec == 'u' {
step = append_value_int(&temp, value, 10, false, false);
} else if spec == 'x' {
step = append_value_int(&temp, value, 16, false, false);
} else if spec == 'X' {
step = append_value_int(&temp, value, 16, true, false);
} else if spec == 'o' {
step = append_value_int(&temp, value, 8, false, false);
} else if spec == 'b' {
step = append_value_int(&temp, value, 2, false, false);
} else if spec == 'c' {
step = append_value_char(&temp, value);
} else if spec == 's' {
step = append_value_str(&temp, value);
} else if spec == 'p' {
step = append_value_ptr(&temp, value);
} else {
ret .Err(make(.InvalidFormat));
}
match step {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
continue;
}
if c == '}' {
if i + 1 < fmt.len && fmt.ptr[i + 1] == '}' {
match ByteBuffer.append_byte(&temp, '}') {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
i = i + 2;
continue;
}
ret .Err(make(.InvalidFormat));
}
match ByteBuffer.append_byte(&temp, c) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
i = i + 1;
}
ret ByteBuffer.append(out, ByteBuffer.as_slice(temp));
}
// ---- 测试辅助 ----
fn expect_ok(result: Status) {
@@ -262,10 +416,96 @@ fn test_composition_and_growth() {
@assert(s.ptr[1] == ',', "0 then comma");
}
fn expect_format(fmt: []u8, args: []Value, expected: []u8) {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
expect_ok(format(&b, fmt, args));
expect_text(&b, expected);
}
fn expect_invalid_format(fmt: []u8, args: []Value) {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
match format(&b, fmt, args) {
.Ok => { @assert(false, "expected invalid format"); },
.Err(error) => {
match error.kind {
.InvalidFormat => {},
_ => { @assert(false, "expected InvalidFormat kind"); },
}
},
}
}
fn test_format_specs() {
var one: [1]Value = .[int(-5)];
expect_format("{}", one[0..1], "-5");
expect_format("{d}", one[0..1], "-5");
expect_format("{i}", one[0..1], "-5");
var u: [1]Value = .[uint(7)];
expect_format("{}", u[0..1], "7");
expect_format("{u}", u[0..1], "7");
expect_format("{x}", u[0..1], "7");
expect_format("{o}", u[0..1], "7");
expect_format("{b}", u[0..1], "111");
var hex: [1]Value = .[uint(255)];
expect_format("{x}", hex[0..1], "ff");
expect_format("{X}", hex[0..1], "FF");
var oct: [1]Value = .[uint(8)];
expect_format("{o}", oct[0..1], "10");
var bits: [1]Value = .[uint(5)];
expect_format("{b}", bits[0..1], "101");
var ch: [1]Value = .[char('A')];
expect_format("{}", ch[0..1], "A");
expect_format("{c}", ch[0..1], "A");
var text: [1]Value = .[str("hi")];
expect_format("{}", text[0..1], "hi");
expect_format("{s}", text[0..1], "hi");
var nul: [1]Value = .[addr(null)];
expect_format("{p}", nul[0..1], "0x0");
var none: [1]Value = .[int(0)];
expect_format("100%", none[0..0], "100%");
expect_format("{{}}", none[0..0], "{}");
expect_format("plain", none[0..0], "plain");
}
fn test_format_mixed_and_failure() {
var args: [3]Value = .[int(42), str("x"), char('!')];
expect_format("n={} s={} c={}", args[0..3], "n=42 s=x c=!");
expect_format("n={} s={s} c={c}", args[0..3], "n=42 s=x c=!");
// 失败不改动输出,且不产生部分结果:前两个转换成功、第三个参数不足。
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
expect_ok(ByteBuffer.append(&b, "prefix"));
var two: [2]Value = .[int(1), int(2)];
expect_invalid_format("a={} b={} c={}", two[0..2]);
expect_text(&b, "prefix");
var only_int: [1]Value = .[int(1)];
expect_invalid_format("{} {}", only_int[0..1]);
expect_invalid_format("{}", only_int[0..0]);
expect_invalid_format("trailing {", only_int[0..1]);
expect_invalid_format("dangling }", only_int[0..1]);
expect_invalid_format("{q}", only_int[0..1]);
expect_invalid_format("{s}", only_int[0..1]);
expect_invalid_format("{c}", only_int[0..1]);
}
fn main() {
test_unsigned();
test_signed();
test_usize_bool_and_errors();
test_mpa_integer_formatting();
test_composition_and_growth();
test_format_specs();
test_format_mixed_and_failure();
}