stage2 提供词法分析器通过与stage1的差异测试 stage1提供数组到切片的隐式转换
This commit is contained in:
1 parent
c30143609a
commit
bb9d0a9f18
13 files changed
+1275
-52
No files matched your search
@@ -35,6 +35,14 @@ fn from_slice(bytes: []u8): ByteView {
|
||||
ret ByteView { .ptr = bytes.ptr, .len = bytes.len };
|
||||
}
|
||||
|
||||
// 以 NUL 结尾的 C 字符串转为视图;不复制、不拥有。null 规范化为空视图。
|
||||
fn from_cstr(text: *u8): ByteView {
|
||||
if text == null { ret empty(); }
|
||||
var len: usize = 0;
|
||||
while text[len] != 0 { len = len + 1; }
|
||||
ret from_slice(.{ .ptr = text, .len = len });
|
||||
}
|
||||
|
||||
fn as_slice(self: ByteView): []u8 {
|
||||
ret .{ .ptr = self.ptr, .len = self.len };
|
||||
}
|
||||
@@ -213,6 +221,14 @@ fn test_construction_and_access() {
|
||||
|
||||
var bytes = as_slice(text);
|
||||
@assert(bytes.ptr == text.ptr && bytes.len == text.len, "slice conversion borrows");
|
||||
|
||||
var cstr_data: [4]u8 = .[104, 105, 0, 88];
|
||||
var cstr = from_cstr(&cstr_data[0]);
|
||||
@assert(cstr.len == 2 && cstr.ptr[0] == 104 && cstr.ptr[1] == 105,
|
||||
"from_cstr stops at NUL");
|
||||
@assert(from_cstr(&cstr_data[2]).len == 0, "from_cstr at NUL is empty");
|
||||
var none = from_cstr(null);
|
||||
@assert(none.ptr == null && none.len == 0, "from_cstr null normalized");
|
||||
}
|
||||
|
||||
fn test_compare_and_slice() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// Expect retcode = 0
|
||||
|
||||
// 显式装箱值:运行期格式化用的最小 ADT。调用方显式构造(如 Value.int(x)),
|
||||
// 库内用 match 分派。不做隐式装箱、不带运行期类型系统。
|
||||
// Str 借用 ByteView,不拥有存储;Ptr 是类型擦除指针。
|
||||
|
||||
type .{ .ByteView } = @import("byte_view.spl");
|
||||
|
||||
type Value = enum {
|
||||
Int: i64,
|
||||
Uint: u64,
|
||||
Bool: bool,
|
||||
Char: u8,
|
||||
Str: ByteView,
|
||||
Ptr: *any,
|
||||
}
|
||||
|
||||
fn int(value: i64): Value {
|
||||
ret .Int(value);
|
||||
}
|
||||
|
||||
fn uint(value: u64): Value {
|
||||
ret .Uint(value);
|
||||
}
|
||||
|
||||
fn boolean(value: bool): Value {
|
||||
ret .Bool(value);
|
||||
}
|
||||
|
||||
fn char(value: u8): Value {
|
||||
ret .Char(value);
|
||||
}
|
||||
|
||||
fn str(bytes: []u8): Value {
|
||||
ret .Str(ByteView.from_slice(bytes));
|
||||
}
|
||||
|
||||
fn str_view(view: ByteView): Value {
|
||||
ret .Str(view);
|
||||
}
|
||||
|
||||
fn addr(value: *any): Value {
|
||||
ret .Ptr(value);
|
||||
}
|
||||
|
||||
// ---- 测试 ----
|
||||
|
||||
fn test_int_and_uint() {
|
||||
match int(-5) {
|
||||
.Int(v) => { @assert(v == -5, "int payload"); },
|
||||
_ => { @assert(false, "expected int"); },
|
||||
}
|
||||
match uint(5) {
|
||||
.Uint(v) => { @assert(v == 5, "uint payload"); },
|
||||
_ => { @assert(false, "expected uint"); },
|
||||
}
|
||||
}
|
||||
|
||||
fn test_scalars() {
|
||||
match boolean(true) {
|
||||
.Bool(v) => { @assert(v, "bool payload"); },
|
||||
_ => { @assert(false, "expected bool"); },
|
||||
}
|
||||
match char('A') {
|
||||
.Char(v) => { @assert(v == 'A', "char payload"); },
|
||||
_ => { @assert(false, "expected char"); },
|
||||
}
|
||||
}
|
||||
|
||||
fn test_borrowed_str() {
|
||||
var text: [3]u8 = .[104, 105, 0];
|
||||
var s = str(text[0..2]);
|
||||
match s {
|
||||
.Str(view) => {
|
||||
@assert(view.len == 2 && view.ptr[0] == 104 && view.ptr[1] == 105,
|
||||
"str payload borrows");
|
||||
},
|
||||
_ => { @assert(false, "expected str"); },
|
||||
}
|
||||
@assert(text[0] == 104, "source untouched");
|
||||
}
|
||||
|
||||
fn test_ptr() {
|
||||
var x: i32 = 7;
|
||||
match addr(&x as *any) {
|
||||
.Ptr(p) => { @assert((p as *i32).* == 7, "ptr payload"); },
|
||||
_ => { @assert(false, "expected ptr"); },
|
||||
}
|
||||
match addr(null) {
|
||||
.Ptr(p) => { @assert(p == null, "null ptr payload"); },
|
||||
_ => { @assert(false, "expected ptr"); },
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_int_and_uint();
|
||||
test_scalars();
|
||||
test_borrowed_str();
|
||||
test_ptr();
|
||||
}
|
||||
Reference in new issue
Block a user