Files
spl/stage2/utils/fmt.spl
T

512 lines
16 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
// 数值/布尔格式化,写入 ByteBuffer。radix 取值 2..36;uppercase 只影响 10..35 的字母形式。
// 整数转换统一复用 MPA。先在临时缓冲区完成转换,再一次性追加,失败不产生部分输出。
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 刻意不做常量求值/折叠,
// 顶层 const 不应用于运行时表达式。
fn valid_radix(radix: u8): bool {
ret radix >= 2 && radix <= 36;
}
fn append_big_int(out: *ByteBuffer, value: BigInt,
radix: u8, uppercase: bool): Status {
if !valid_radix(radix) { ret .Err(make(.InvalidArgument)); }
var text = ByteBuffer.new();
defer { ByteBuffer.drop(&text); }
match BigInt.format(&text, value, radix, uppercase) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret ByteBuffer.append(out, ByteBuffer.as_slice(text));
}
fn append_bits(out: *ByteBuffer, value: Bits, signed: bool,
radix: u8, uppercase: bool): Status {
var integer = BigInt.zero();
defer { BigInt.drop(&integer); }
match Bits.to_big(&integer, value, signed) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_big_int(out, integer, radix, uppercase);
}
fn append_u64(out: *ByteBuffer, value: u64, radix: u8, uppercase: bool): Status {
var integer = BigInt.zero();
defer { BigInt.drop(&integer); }
match BigInt.from_u64(&integer, value) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_big_int(out, integer, radix, uppercase);
}
fn append_i64(out: *ByteBuffer, value: i64, radix: u8, uppercase: bool): Status {
var integer = BigInt.zero();
defer { BigInt.drop(&integer); }
match BigInt.from_i64(&integer, value) {
.Ok => {},
.Err(error) => { ret .Err(error); },
}
ret append_big_int(out, integer, radix, uppercase);
}
fn append_usize(out: *ByteBuffer, value: usize, radix: u8, uppercase: bool): Status {
ret append_u64(out, value as u64, radix, uppercase);
}
fn append_isize(out: *ByteBuffer, value: isize, radix: u8, uppercase: bool): Status {
ret append_i64(out, value as i64, radix, uppercase);
}
fn append_bool(out: *ByteBuffer, value: bool): Status {
if value { ret ByteBuffer.append(out, "true"); }
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) {
match result {
.Ok => {},
.Err(error) => { @assert(false, "unexpected fmt error"); },
}
}
fn expect_invalid(result: Status) {
match result {
.Ok => { @assert(false, "expected fmt error"); },
.Err(error) => {
match error.kind {
.InvalidArgument => {},
_ => { @assert(false, "unexpected fmt error kind"); },
}
},
}
}
fn expect_text(buffer: *ByteBuffer, expected: []u8) {
var actual = ByteBuffer.as_slice(buffer.*);
@assert(actual.len == expected.len, "formatted length");
var i: usize = 0;
while i < expected.len {
@assert(actual.ptr[i] == expected.ptr[i], "formatted byte");
i = i + 1;
}
}
fn test_unsigned() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
expect_ok(append_u64(&b, 0, 10, false));
expect_text(&b, "0");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 42, 10, false));
expect_text(&b, "42");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 1234567890, 10, false));
expect_text(&b, "1234567890");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 18446744073709551615, 10, false));
expect_text(&b, "18446744073709551615");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 255, 16, false));
expect_text(&b, "ff");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 255, 16, true));
expect_text(&b, "FF");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 4096, 16, false));
expect_text(&b, "1000");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 170, 2, false));
expect_text(&b, "10101010");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 35, 36, false));
expect_text(&b, "z");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 18446744073709551615, 2, false));
expect_text(&b, "1111111111111111111111111111111111111111111111111111111111111111");
ByteBuffer.clear(&b);
}
fn test_signed() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
expect_ok(append_i64(&b, 0, 10, false));
expect_text(&b, "0");
ByteBuffer.clear(&b);
expect_ok(append_i64(&b, -123, 10, false));
expect_text(&b, "-123");
ByteBuffer.clear(&b);
expect_ok(append_i64(&b, 9223372036854775807, 10, false));
expect_text(&b, "9223372036854775807");
ByteBuffer.clear(&b);
expect_ok(append_i64(&b, -9223372036854775808, 10, false));
expect_text(&b, "-9223372036854775808");
ByteBuffer.clear(&b);
expect_ok(append_isize(&b, -16, 16, false));
expect_text(&b, "-10");
ByteBuffer.clear(&b);
expect_ok(append_u64(&b, 48879, 16, true));
expect_text(&b, "BEEF");
ByteBuffer.clear(&b);
}
fn test_usize_bool_and_errors() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
expect_ok(append_usize(&b, 1000 as usize, 10, false));
expect_text(&b, "1000");
ByteBuffer.clear(&b);
expect_ok(append_bool(&b, true));
expect_text(&b, "true");
ByteBuffer.clear(&b);
expect_ok(append_bool(&b, false));
expect_text(&b, "false");
ByteBuffer.clear(&b);
expect_invalid(append_u64(&b, 1, 0, false));
expect_invalid(append_u64(&b, 1, 1, false));
expect_invalid(append_u64(&b, 1, 37, false));
expect_invalid(append_i64(&b, -1, 0, false));
@assert(ByteBuffer.size(b) == 0, "invalid radix writes nothing");
expect_ok(ByteBuffer.append(&b, "prefix"));
expect_invalid(append_i64(&b, -123, 1, false));
expect_text(&b, "prefix");
}
fn test_mpa_integer_formatting() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
var huge = BigInt.zero();
defer { BigInt.drop(&huge); }
expect_ok(BigInt.parse(&huge, "1234567890123456789012345678901234567890", 10));
expect_ok(append_big_int(&b, huge, 10, false));
expect_text(&b, "1234567890123456789012345678901234567890");
ByteBuffer.clear(&b);
expect_ok(BigInt.parse(&huge, "-ffffffffffffffffffffffffffffffff", 16));
expect_ok(append_big_int(&b, huge, 16, true));
expect_text(&b, "-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
ByteBuffer.clear(&b);
var raw = BigInt.zero();
defer { BigInt.drop(&raw); }
expect_ok(BigInt.from_u64(&raw, 255));
var bits: Bits = undefined;
match Bits.new(8) {
.Ok(value) => { bits = value; },
.Err(error) => { @assert(false, "Bits construction"); },
}
defer { Bits.drop(&bits); }
expect_ok(Bits.from_big_checked(&bits, raw, 8, false));
expect_ok(append_bits(&b, bits, false, 10, false));
expect_text(&b, "255");
ByteBuffer.clear(&b);
expect_ok(append_bits(&b, bits, true, 10, false));
expect_text(&b, "-1");
}
fn test_composition_and_growth() {
var b = ByteBuffer.new();
defer { ByteBuffer.drop(&b); }
expect_ok(ByteBuffer.append(&b, "x="));
expect_ok(append_i64(&b, -42, 10, false));
expect_ok(ByteBuffer.append(&b, " hex="));
expect_ok(append_u64(&b, 255, 16, false));
expect_ok(ByteBuffer.append(&b, " ok="));
expect_ok(append_bool(&b, true));
expect_text(&b, "x=-42 hex=ff ok=true");
ByteBuffer.clear(&b);
// 反复写入触发 ByteBuffer 扩容,校验累积内容。
var i: i32 = 0;
while i < 200 {
expect_ok(append_i64(&b, i as i64, 10, false));
expect_ok(ByteBuffer.append_byte(&b, ',')); // ','
i = i + 1;
}
expect_ok(append_i64(&b, 200, 10, false));
@assert(ByteBuffer.size(b) > 0, "growth content");
var s = ByteBuffer.as_slice(b);
@assert(s.ptr[0] == '0', "starts with 0");
@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();
}