// Expect retcode = 0 // 内建注册表:按 Intrin 查描述信息,按名字/字段/`as` 规则反查 Intrin。新增内建只改 // info_at,不改 sema 逻辑(SPL.md 的 @/# 扩展点)。 type .{ .ByteView } = @import("utils/byte_view.spl"); type TypeTable = @import("./type_table.spl"); type .{ .TypeNode, .TypeEntry } = @import("./type_table.spl"); type .{ .LangKind } = @import("./lang_type.spl"); // 文件命名空间(供跨模块用 IntrinOps.foo 调用)。 type IntrinOps = @self; type Intrin = enum { None, BitSizeof, Sizeof, Alignof, Assert, Breakpoint, Len, IntCast, Truncate, BitCast, FloatCast, Float2Int, Int2Float, Bool2Int, Ptr2Int, Int2Ptr, PtrCast, Enum2Int, OffsetOf, FieldCount, } type IntrinForm = enum { Call, Field, } type IntrinResult = enum { Unit, Bool, Usize, } type IntrinInfo = struct { name: ByteView, kind: Intrin, form: IntrinForm, min_args: i32, max_args: i32, // -1 = variadic result: IntrinResult, } fn form_is(a: IntrinForm, b: IntrinForm): bool { ret @enum2int(a) == @enum2int(b); } fn kind_is(a: LangKind, b: LangKind): bool { ret @enum2int(a) == @enum2int(b); } // 枚举顺序即 kind 数值(与 splc0 表一致)。 fn info_at(index: usize): IntrinInfo { match index { 1 => { ret IntrinInfo { .name = ByteView.from_slice("@bitsizeof"), .kind = .BitSizeof, .form = .Call, .min_args = 1, .max_args = 1, .result = .Usize }; }, 2 => { ret IntrinInfo { .name = ByteView.from_slice("@sizeof"), .kind = .Sizeof, .form = .Call, .min_args = 1, .max_args = 1, .result = .Usize }; }, 3 => { ret IntrinInfo { .name = ByteView.from_slice("@alignof"), .kind = .Alignof, .form = .Call, .min_args = 1, .max_args = 1, .result = .Usize }; }, 4 => { ret IntrinInfo { .name = ByteView.from_slice("@assert"), .kind = .Assert, .form = .Call, .min_args = 1, .max_args = 2, .result = .Unit }; }, 5 => { ret IntrinInfo { .name = ByteView.from_slice("@breakpoint"), .kind = .Breakpoint, .form = .Call, .min_args = 0, .max_args = 0, .result = .Unit }; }, 6 => { ret IntrinInfo { .name = ByteView.from_slice("len"), .kind = .Len, .form = .Field, .min_args = 0, .max_args = 0, .result = .Usize }; }, 7 => { ret IntrinInfo { .name = ByteView.from_slice("@int_cast"), .kind = .IntCast, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 8 => { ret IntrinInfo { .name = ByteView.from_slice("@truncate"), .kind = .Truncate, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 9 => { ret IntrinInfo { .name = ByteView.from_slice("@bit_cast"), .kind = .BitCast, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 10 => { ret IntrinInfo { .name = ByteView.from_slice("@float_cast"), .kind = .FloatCast, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 11 => { ret IntrinInfo { .name = ByteView.from_slice("@float2int"), .kind = .Float2Int, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 12 => { ret IntrinInfo { .name = ByteView.from_slice("@int2float"), .kind = .Int2Float, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 13 => { ret IntrinInfo { .name = ByteView.from_slice("@bool2int"), .kind = .Bool2Int, .form = .Call, .min_args = 1, .max_args = 2, .result = .Unit }; }, 14 => { ret IntrinInfo { .name = ByteView.from_slice("@ptr2int"), .kind = .Ptr2Int, .form = .Call, .min_args = 1, .max_args = 2, .result = .Unit }; }, 15 => { ret IntrinInfo { .name = ByteView.from_slice("@int2ptr"), .kind = .Int2Ptr, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 16 => { ret IntrinInfo { .name = ByteView.from_slice("@ptr_cast"), .kind = .PtrCast, .form = .Call, .min_args = 2, .max_args = 2, .result = .Unit }; }, 17 => { ret IntrinInfo { .name = ByteView.from_slice("@enum2int"), .kind = .Enum2Int, .form = .Call, .min_args = 1, .max_args = 1, .result = .Usize }; }, 18 => { ret IntrinInfo { .name = ByteView.from_slice("@offsetof"), .kind = .OffsetOf, .form = .Call, .min_args = 2, .max_args = 2, .result = .Usize }; }, 19 => { ret IntrinInfo { .name = ByteView.from_slice("@field_count"), .kind = .FieldCount, .form = .Call, .min_args = 1, .max_args = 1, .result = .Usize }; }, _ => { ret IntrinInfo { .name = ByteView.empty(), .kind = .None, .form = .Call, .min_args = 0, .max_args = 0, .result = .Unit }; }, } ret IntrinInfo { .name = ByteView.empty(), .kind = .None, .form = .Call, .min_args = 0, .max_args = 0, .result = .Unit }; } fn info(kind: Intrin): IntrinInfo { ret info_at(@enum2int(kind) as usize); } fn table_count(): usize { ret 20; } fn strip_at(name: ByteView): ByteView { if name.len > 0 && name.ptr[0] == '@' { ret ByteView { .ptr = name.ptr + 1, .len = name.len - 1 }; } ret name; } // 按名查找 CALL 形态的内建;忽略前导 '@'。 fn lookup_call(name: ByteView): Intrin { var want = strip_at(name); var i: usize = 1; while i < table_count() { var d = info_at(i); if form_is(d.form, .Call) { var dn = strip_at(d.name); if ByteView.eq(dn, want) { ret d.kind; } } i = i + 1; } ret .None; } // 按接收者类型 + 字段名查找 FIELD 形态内建。 fn lookup_field(recv: LangKind, field: ByteView): Intrin { if ByteView.eq(field, ByteView.from_slice("len")) { if kind_is(recv, .Array) || kind_is(recv, .Slice) { ret .Len; } } ret .None; } fn is_int(e: *TypeEntry): bool { match e.node { .Int(x) => { ret true; }, _ => { ret false; }, } ret false; } fn is_float(e: *TypeEntry): bool { match e.node { .Float(x) => { ret true; }, _ => { ret false; }, } ret false; } fn is_bool(e: *TypeEntry): bool { match e.node { .Bool => { ret true; }, _ => { ret false; }, } ret false; } fn is_ptr(e: *TypeEntry): bool { match e.node { .Ptr(x) => { ret true; }, _ => { ret false; }, } ret false; } fn int_bits(e: *TypeEntry): usize { match e.node { .Int(x) => { ret x.bits; }, _ => { ret 0; }, } ret 0; } // `as` 的展开规则表(改 as 语义只改这里)。 fn lookup_cast(ir: *TypeTable, ft: usize, tt: usize): Intrin { var f = TypeTable.node(ir, ft); var t = TypeTable.node(ir, tt); if f == null || t == null { ret .None; } if is_int(f) && is_int(t) { var fb = int_bits(f); var tb = int_bits(t); if fb == tb { ret .BitCast; } if fb > tb { ret .Truncate; } ret .IntCast; } if is_float(f) && is_float(t) { ret .FloatCast; } if is_bool(f) && is_int(t) { ret .Bool2Int; } if is_ptr(f) && is_int(t) { ret .Ptr2Int; } if is_int(f) && is_ptr(t) { ret .Int2Ptr; } if is_ptr(f) && is_ptr(t) { ret .PtrCast; } ret .None; } fn test_info_table() { var d = info(.Sizeof); @assert(ByteView.eq(d.name, ByteView.from_slice("@sizeof")), "sizeof name"); @assert(d.min_args == 1 && d.max_args == 1, "sizeof arity"); @assert(form_is(d.form, .Call), "sizeof form"); var l = info(.Len); @assert(ByteView.eq(l.name, ByteView.from_slice("len")), "len name"); @assert(form_is(l.form, .Field), "len form"); } fn test_lookup_call() { @assert(@enum2int(lookup_call(ByteView.from_slice("sizeof"))) == @enum2int(Intrin.Sizeof), "sizeof by bare name"); @assert(@enum2int(lookup_call(ByteView.from_slice("@sizeof"))) == @enum2int(Intrin.Sizeof), "sizeof by @name"); @assert(@enum2int(lookup_call(ByteView.from_slice("assert"))) == @enum2int(Intrin.Assert), "assert"); @assert(@enum2int(lookup_call(ByteView.from_slice("len"))) == @enum2int(Intrin.None), "len is field, not call"); @assert(@enum2int(lookup_call(ByteView.from_slice("nope"))) == @enum2int(Intrin.None), "unknown"); } fn test_lookup_field() { @assert(@enum2int(lookup_field(.Slice, ByteView.from_slice("len"))) == @enum2int(Intrin.Len), "slice len"); @assert(@enum2int(lookup_field(.Array, ByteView.from_slice("len"))) == @enum2int(Intrin.Len), "array len"); @assert(@enum2int(lookup_field(.Struct, ByteView.from_slice("len"))) == @enum2int(Intrin.None), "struct len"); @assert(@enum2int(lookup_field(.Slice, ByteView.from_slice("ptr"))) == @enum2int(Intrin.None), "slice ptr"); } fn test_lookup_cast() { var ir: TypeTable = undefined; match TypeTable.new() { .Ok(v) => { ir = v; }, .Err(error) => { @assert(false, "type table new failed"); }, } defer { TypeTable.drop(&ir); } var i32t = TypeTable.make_int(&ir, 32, true); var i64t = TypeTable.make_int(&ir, 64, true); var f64t = TypeTable.make_float(&ir, 64); var boolt = TypeTable.make_bool(&ir); var pt = TypeTable.make_ptr(&ir, i32t); @assert(@enum2int(lookup_cast(&ir, i64t, i32t)) == @enum2int(Intrin.Truncate), "truncate"); @assert(@enum2int(lookup_cast(&ir, i32t, i64t)) == @enum2int(Intrin.IntCast), "int cast"); @assert(@enum2int(lookup_cast(&ir, i32t, i32t)) == @enum2int(Intrin.BitCast), "bit cast"); @assert(@enum2int(lookup_cast(&ir, f64t, f64t)) == @enum2int(Intrin.FloatCast), "float cast"); @assert(@enum2int(lookup_cast(&ir, boolt, i32t)) == @enum2int(Intrin.Bool2Int), "bool2int"); @assert(@enum2int(lookup_cast(&ir, pt, i32t)) == @enum2int(Intrin.Ptr2Int), "ptr2int"); @assert(@enum2int(lookup_cast(&ir, i32t, pt)) == @enum2int(Intrin.Int2Ptr), "int2ptr"); @assert(@enum2int(lookup_cast(&ir, pt, pt)) == @enum2int(Intrin.PtrCast), "ptr cast"); @assert(@enum2int(lookup_cast(&ir, i32t, f64t)) == @enum2int(Intrin.None), "int->float forbidden"); } fn main() { test_info_table(); test_lookup_call(); test_lookup_field(); test_lookup_cast(); }