feat(tpv): 新增 TypeSpec 统一类型描述符(S1,未接线)

类型信息目前散落在 AnnotClassName/IsPtrElement/TypeStr 等字符串与布尔里,
导致「无符号/指针层级/数组维度/函数签名」到处重复解析且结论不一致
(Debugger/_Type.md 二次盘点 A/C 类)。

新增 App/lib/core/Gen/Type/TypeSpec.vp 作为统一描述符:
- TypeSpec:Kind/Bits/IsSigned/PtrLevel/Elem/ArrayCount/BaseName/Ret/Params/
  ParamCount/IsVarArg/Storage,以及元素语义三元组 ElemBits/IsElemPtr/
  DerefBase(替代单一 IsPtrElement 布尔——同一 Ptr(i8) 下存在两种相反语义)
- TypeSpecArena 驻留表(对象持有,非全局表)+ InternSpec 去重(键=规范文本)
- SpecToText/TextToSpec 规范文本编解码(驻留键/日志/vpi 通道)
- 构造器 NewTypeSpec/NewTypeStruct/NewTypePtr/NewTypeArray/NewTypeFunc
- 访问器 SpecIsPtr/SpecPointedTo/SpecIsUnsigned/SpecBitWidth
- SpecSelfCheck 自检(不参与编译流程)
- 实例与数组一律 PermPool 分配(跨 mark 持久,跨模块可直接传指针)

GenTypes 仅加 import 使该模块参与编译(否则新文件不会被编译、无法验证);
未改动任何既有调用点,产物 IR 应与上一版一致。
This commit is contained in:
TermiNexus committed 2026-09-11 20:02:52 +08:00
1 parent b8470c1cae
commit b1090abe70
2 files changed
+773

No files matched your search

+4
View File
@@ -3,6 +3,10 @@ from stdint import *
import ast
import llvmlite
import memhub
# TypeSpec:统一类型描述符(Debugger/_Type.md 第 6 节设计)。
# S1 阶段仅 import 使该模块参与编译(尚未在任何条目上使用其字段);
# S2 起在 VarEntry/FieldEntry/FuncEntry 上加 Spec 字段并双写。
import lib.core.Gen.Type.TypeSpec as TypeSpec
# ============================================================
+769
View File
@@ -0,0 +1,769 @@
import t, c
from stdint import *
import llvmlite
import memhub
import string
import viperlib
import lib.core.CompilerFactory as CompilerFactory
# ============================================================
# TypeSpec - 统一类型描述符(对齐 TPC lib/core/TypeSpec.py,去 Python 化)
#
# 背景(见 Debugger/_Type.md 第 6 节设计):
# TPV 现有类型信息散落在「字符串 + 零散布尔」里 —— VarEntry.AnnotClassName /
# FieldEntry.AnnotClassName(注解类名串当影子类型系统)、VarEntry.IsPtrElement
# (单一布尔,只覆盖一种 8 字节步长情形)、FuncEntry.TypeStr(函数类型存文本),
# 而 llvmlite.LLVMType 只保留解析结果、已抹平指针层级/数组维度/原始注解。
# 结果是「是否无符号/指针几层/数组几维/函数签名」到处重复解析字符串
# (同一值被末段类名/完整限定名/精确名/mangled 前缀/截断串五种语义解释)。
#
# 本文件是 S1 阶段产物:**只提供结构与构造/序列化,不改动任何既有调用点**。
# S2 起才在各解析处「双写」本结构;S3 逐块切读;S4 删旧字符串路径。
#
# 生命周期(设计 D1):实例与内部数组一律 `CompilerFactory.factory.PermPool`
# 分配(契约见 CompilerFactory.vp:跨 mark 持久、从不 reset),故跨文件/跨模块
# 可直接传指针,无需序列化。代价是 PermPool 只增不减 ⇒ 由 InternSpec 驻留去重。
#
# 不用全局表:驻留表挂在 TypeSpecArena 实例上,由调用方(S2 起为 Generator)
# 持有并传入,避免模块级可变态。
# ============================================================
# ============================================================
# Kind - 基础类型种类
# ============================================================
TS_UNKNOWN: t.CDefine = 0
TS_INT: t.CDefine = 1
TS_UINT: t.CDefine = 2
TS_FLOAT: t.CDefine = 3
TS_BOOL: t.CDefine = 4
TS_PTR: t.CDefine = 5
TS_ARRAY: t.CDefine = 6
TS_STRUCT: t.CDefine = 7
TS_VOID: t.CDefine = 8
TS_FUNC: t.CDefine = 9
TS_UNION: t.CDefine = 10
TS_ENUM: t.CDefine = 11
# ============================================================
# IsSigned - 符号性(单一真源,替代 _annot_str_unsigned 的子串扫描)
# ============================================================
TS_SIGN_UNKNOWN: t.CDefine = -1
TS_SIGN_UNSIGNED: t.CDefine = 0
TS_SIGN_SIGNED: t.CDefine = 1
# 驻留表默认容量与单条类型文本上限(超限 fail-fast,绝不静默丢弃/截断)
# 容量 × 文本上限即驻留表键缓冲的 PermPool 占用(8192 × 256 = 2MB)
SPEC_INTERN_MAX: t.CDefine = 8192
SPEC_TEXT_MAX: t.CDefine = 256
# ============================================================
# TypeSpec - 类型描述符
#
# 元素语义三字段说明(ElemBits/IsElemPtr/DerefBase):
# 它们替代旧的 VarEntry.IsPtrElement 单一布尔。原因(_Type.md D7-4):
# `str | t.CPtr`、`bytes | t.CPtr`、泛型 `T | t.CPtr`、`64 位标量 | t.CPtr`
# 退化后的 LLVM 类型**都是单层 Ptr(i8)**,即 PtrLevel=1 同时对应两种相反语义,
# 仅靠指针层级无法区分;区分靠的是注解内容 ⇒ 需要「元素宽度 + 元素是否指针 +
# 基址是否需二次解引用」三元组。
# ============================================================
@t.NoVTable
class TypeSpec:
"""统一类型描述符(字段语义见上)"""
Kind: int # TS_* 之一
Bits: int # 位宽;非整型/浮点为 0
IsSigned: int # TS_SIGN_* (-1 未知 / 0 无符号 / 1 有符号)
PtrLevel: int # 指针层级(0=非指针)
Elem: TypeSpec | t.CPtr # Ptr/Array 的指向或元素(None=无)
ArrayCount: int # Array 的元素个数(0=未知/动态)
BaseName: str # 结构体/枚举/类名(带模块 sha1;PermPool 深拷贝)
Ret: TypeSpec | t.CPtr # Func:返回类型
Params: sizet | t.CPtr # Func:参数数组(连续 TypeSpec* 指针,ParamCount 个)
ParamCount: int # Func:参数个数
IsVarArg: int # Func:1=变参(...)
Storage: int # 限定宏位标志(0=无)
ElemBits: int # 元素槽位宽(元素语义三元组 1/3)
IsElemPtr: int # 元素是否为指针(1=是)(2/3)
DerefBase: int # 基址是否需二次解引用(1=是)(3/3)
Ty: llvmlite.LLVMType | t.CPtr # 解析结果缓存(可空)
# ============================================================
# TypeSpecArena - 驻留表(对象持有,非全局表)
#
# Keys :cap * SPEC_TEXT_MAX 的连续文本缓冲,第 i 条键位于 Keys + i*SPEC_TEXT_MAX
# Specs :cap 个 TypeSpec 指针
# Count :已驻留条数;Cap:容量
# ============================================================
@t.NoVTable
class TypeSpecArena:
"""TypeSpec 驻留表(按规范文本键去重,同型只存一份)"""
Keys: bytes
Specs: sizet | t.CPtr
Count: int
Cap: int
# ============================================================
# _spec_key_at - 取第 i 个键的 str 指针(键缓冲内偏移 i * SPEC_TEXT_MAX)
# ============================================================
def _spec_key_at(keys: bytes, index: int) -> str:
"""返回第 index 条键的 str 指针(指向 keys 缓冲内部,不可 free)"""
if keys is None or index < 0:
return None
addr: uint64 = uint64(keys) + uint64(index * SPEC_TEXT_MAX)
return (str | t.CPtr)(t.CVoid(addr, t.CPtr))
# ============================================================
# SpecArenaInit - 初始化驻留表(幂等;cap<=0 时用 SPEC_INTERN_MAX)
# 返回 0=成功 / 1=失败(分配失败 fail-fast 由调用方处理,此处返回错误码)
# ============================================================
def SpecArenaInit(arena: TypeSpecArena | t.CPtr, cap: int) -> int:
"""初始化驻留表;已初始化则直接返回 0"""
if arena is None:
return 1
if arena.Keys is not None:
return 0
use_cap: int = cap
if use_cap <= 0:
use_cap = SPEC_INTERN_MAX
pp: memhub.MemBuddy | t.CPtr = CompilerFactory.factory.PermPool
keys: bytes = pp.alloc(use_cap * SPEC_TEXT_MAX)
if keys is None:
return 1
specs: sizet | t.CPtr = pp.alloc(use_cap * 8)
if specs is None:
return 1
string.memset(keys, 0, use_cap * SPEC_TEXT_MAX)
string.memset(specs, 0, use_cap * 8)
arena.Keys = keys
arena.Specs = specs
arena.Count = 0
arena.Cap = use_cap
return 0
# ============================================================
# _spec_alloc - 从 PermPool 分配一个清零的 TypeSpec(不驻留,内部用)
# ============================================================
def _spec_alloc() -> TypeSpec | t.CPtr:
"""分配并清零一个 TypeSpec(PermPool)"""
pp: memhub.MemBuddy | t.CPtr = CompilerFactory.factory.PermPool
size: sizet = TypeSpec.__sizeof__()
sp: TypeSpec | t.CPtr = (TypeSpec | t.CPtr)(t.CVoid(pp.alloc(size), t.CPtr))
if sp is None:
return None
string.memset(sp, 0, size)
sp.Kind = TS_UNKNOWN
sp.Bits = 0
sp.IsSigned = TS_SIGN_UNKNOWN
sp.PtrLevel = 0
sp.IsVarArg = 0
sp.Storage = 0
return sp
# ============================================================
# _spec_make - 构造一个基础 TypeSpec(不驻留)
# ============================================================
def _spec_make(kind: int, bits: int, is_signed: int) -> TypeSpec | t.CPtr:
"""构造基础 TypeSpec(PermPool,不驻留)"""
sp: TypeSpec | t.CPtr = _spec_alloc()
if sp is None:
return None
sp.Kind = kind
sp.Bits = bits
sp.IsSigned = is_signed
return sp
# ============================================================
# _spec_set_struct_name - 深拷贝类名到 PermPool(跨文件存活)
# ============================================================
def _spec_set_struct_name(sp: TypeSpec | t.CPtr, name: str) -> int:
"""把类名深拷贝到 PermPool 并写入 BaseName,返回 0=成功 / 1=失败"""
if sp is None or name is None:
return 1
n: sizet = string.strlen(name)
if n <= 0:
return 1
buf: str = CompilerFactory.factory.PermPool.alloc(n + 1)
if buf is None:
return 1
string.strcpy(buf, name)
sp.BaseName = buf
return 0
# ============================================================
# NewTypeSpec - 构造基础类型(驻留)
# ============================================================
def NewTypeSpec(arena: TypeSpecArena | t.CPtr, kind: int,
bits: int, is_signed: int) -> TypeSpec | t.CPtr:
"""构造基础 TypeSpec 并驻留,失败返回 None"""
sp: TypeSpec | t.CPtr = _spec_make(kind, bits, is_signed)
if sp is None:
return None
return InternSpec(arena, sp)
# ============================================================
# NewTypeStruct - 构造结构体/枚举/类类型(驻留)
# ============================================================
def NewTypeStruct(arena: TypeSpecArena | t.CPtr, kind: int,
base_name: str) -> TypeSpec | t.CPtr:
"""构造 struct/union/enum 类型(BaseName 深拷贝 + 驻留),失败返回 None"""
sp: TypeSpec | t.CPtr = _spec_make(kind, 0, TS_SIGN_UNKNOWN)
if sp is None:
return None
if _spec_set_struct_name(sp, base_name) != 0:
return None
return InternSpec(arena, sp)
# ============================================================
# NewTypePtr - 构造指针类型(PtrLevel = 元素 PtrLevel + 1,驻留)
# ============================================================
def NewTypePtr(arena: TypeSpecArena | t.CPtr,
elem: TypeSpec | t.CPtr) -> TypeSpec | t.CPtr:
"""构造指针类型:指向 elem(PtrLevel 逐层递增),失败返回 None"""
if elem is None:
return None
sp: TypeSpec | t.CPtr = _spec_make(TS_PTR, 0, TS_SIGN_UNKNOWN)
if sp is None:
return None
sp.Elem = elem
sp.PtrLevel = elem.PtrLevel + 1
sp.ElemBits = elem.Bits
sp.IsElemPtr = 0
if elem.Kind == TS_PTR:
sp.IsElemPtr = 1
return InternSpec(arena, sp)
# ============================================================
# NewTypeArray - 构造数组类型(ArrayCount 个 elem,驻留)
# ============================================================
def NewTypeArray(arena: TypeSpecArena | t.CPtr, elem: TypeSpec | t.CPtr,
count: int) -> TypeSpec | t.CPtr:
"""构造数组类型:elem[count](count<=0 表示长度未知/动态),失败返回 None"""
if elem is None or count < 0:
return None
sp: TypeSpec | t.CPtr = _spec_make(TS_ARRAY, 0, TS_SIGN_UNKNOWN)
if sp is None:
return None
sp.Elem = elem
sp.ArrayCount = count
sp.ElemBits = elem.Bits
sp.IsElemPtr = 0
if elem.Kind == TS_PTR:
sp.IsElemPtr = 1
return InternSpec(arena, sp)
# ============================================================
# NewTypeFunc - 构造函数签名类型(ret + 参数数组 + 变参,驻留)
# params:TypeSpec* 连续数组(由调用方预先构造并保证存活);count 个
# ============================================================
def NewTypeFunc(arena: TypeSpecArena | t.CPtr, ret: TypeSpec | t.CPtr,
params: sizet | t.CPtr, count: int,
is_vararg: int) -> TypeSpec | t.CPtr:
"""构造函数签名类型,失败返回 None"""
if ret is None or count < 0:
return None
if count > 0 and params is None:
return None
sp: TypeSpec | t.CPtr = _spec_make(TS_FUNC, 0, TS_SIGN_UNKNOWN)
if sp is None:
return None
sp.Ret = ret
sp.Params = params
sp.ParamCount = count
sp.IsVarArg = is_vararg
return InternSpec(arena, sp)
# ============================================================
# SpecBaseText - 基础类型(非 Ptr/Array/Func)的规范文本
# 返回 0=已写入 / 1=不适用/失败
# ============================================================
def SpecBaseText(sp: TypeSpec | t.CPtr, buf: str, buf_size: sizet) -> int:
"""把基础类型写成规范文本;Ptr/Array/Func 不适用时返回 1"""
if sp is None or buf is None or buf_size == 0:
return 1
if sp.Kind == TS_INT:
viperlib.snprintf(buf, buf_size, "i%d", sp.Bits)
return 0
if sp.Kind == TS_UINT:
viperlib.snprintf(buf, buf_size, "u%d", sp.Bits)
return 0
if sp.Kind == TS_FLOAT:
viperlib.snprintf(buf, buf_size, "f%d", sp.Bits)
return 0
if sp.Kind == TS_BOOL:
viperlib.snprintf(buf, buf_size, "bool")
return 0
if sp.Kind == TS_STRUCT:
viperlib.snprintf(buf, buf_size, "st:%s", sp.BaseName)
return 0
if sp.Kind == TS_UNION:
viperlib.snprintf(buf, buf_size, "un:%s", sp.BaseName)
return 0
if sp.Kind == TS_ENUM:
viperlib.snprintf(buf, buf_size, "en:%s", sp.BaseName)
return 0
if sp.Kind == TS_VOID:
viperlib.snprintf(buf, buf_size, "void")
return 0
return 1
# ============================================================
# SpecToText - 规范文本编码(驻留键 / 日志 / vpi·stub 通道)
#
# 格式草案:
# i32 / u64 / f64 / bool / void
# st:{sha1}.Class 结构体(BaseName 原样);un: / en: 同构,分别表示联合/枚举
# p:INNER 指针(每层一个 "p:" 前缀,层数即 PtrLevel)
# arr[N]:INNER 数组(N=0 表示长度未知)
# fn(INNER,INNER,...)->RET 函数签名(变参追加 "...")
# 递归深度上限 32(防环);超限或缓冲不足返回 1(fail-fast 由调用方决定)
# ============================================================
def SpecToText(sp: TypeSpec | t.CPtr, buf: str, buf_size: sizet) -> int:
"""把 TypeSpec 编码为规范文本,返回 0=成功 / 1=失败"""
return _spec_to_text(sp, buf, buf_size, 0)
def _spec_to_text(sp: TypeSpec | t.CPtr, buf: str, buf_size: sizet,
depth: int) -> int:
"""SpecToText 的递归实现(depth 上限 32)"""
if sp is None or buf is None or buf_size == 0 or depth > 32:
return 1
buf[0] = '\0'
if sp.Kind == TS_PTR:
# p: + 内层
inner_buf: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if inner_buf is None:
return 1
if _spec_to_text(sp.Elem, inner_buf, SPEC_TEXT_MAX, depth + 1) != 0:
return 1
viperlib.snprintf(buf, buf_size, "p:%s", inner_buf)
return 0
if sp.Kind == TS_ARRAY:
# arr[N]: + 内层
inner_buf_a: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if inner_buf_a is None:
return 1
if _spec_to_text(sp.Elem, inner_buf_a, SPEC_TEXT_MAX, depth + 1) != 0:
return 1
viperlib.snprintf(buf, buf_size, "arr[%d]:%s", sp.ArrayCount, inner_buf_a)
return 0
if sp.Kind == TS_FUNC:
# fn(p1,p2,...)->ret
pos: sizet = 0
pos = _spec_text_append(buf, buf_size, pos, "fn(")
i: int = 0
while i < sp.ParamCount:
if i > 0:
pos = _spec_text_append(buf, buf_size, pos, ",")
param: TypeSpec | t.CPtr = _spec_param_at(sp, i)
if param is None:
return 1
param_buf: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if param_buf is None:
return 1
if _spec_to_text(param, param_buf, SPEC_TEXT_MAX, depth + 1) != 0:
return 1
pos = _spec_text_append(buf, buf_size, pos, param_buf)
i += 1
if sp.IsVarArg == 1:
if sp.ParamCount > 0:
pos = _spec_text_append(buf, buf_size, pos, ",...")
else:
pos = _spec_text_append(buf, buf_size, pos, "...")
pos = _spec_text_append(buf, buf_size, pos, ")->")
ret_buf: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if ret_buf is None:
return 1
if _spec_to_text(sp.Ret, ret_buf, SPEC_TEXT_MAX, depth + 1) != 0:
return 1
pos = _spec_text_append(buf, buf_size, pos, ret_buf)
return 0
# 基础类型
return SpecBaseText(sp, buf, buf_size)
# ============================================================
# _spec_text_append - 有界尾部追加(返回新的写入位置;溢出时不再写入)
# ============================================================
def _spec_text_append(buf: str, buf_size: sizet, pos: sizet, text: str) -> sizet:
"""把 text 追加到 buf 的 pos 处(有界),返回新位置"""
if buf is None or text is None:
return pos
tlen: sizet = string.strlen(text)
i: sizet = 0
while i < tlen:
if pos + 1 >= buf_size:
return pos
buf[pos] = text[i]
pos += 1
i += 1
buf[pos] = '\0'
return pos
# ============================================================
# _spec_param_at - 取函数签名第 i 个参数 TypeSpec 指针
# ============================================================
def _spec_param_at(sp: TypeSpec | t.CPtr, index: int) -> TypeSpec | t.CPtr:
"""返回 Func 类型第 index 个参数的 TypeSpec 指针,越界返回 None"""
if sp is None or sp.Params is None or index < 0 or index >= sp.ParamCount:
return None
addr: uint64 = uint64(sp.Params) + uint64(index * 8)
slot: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(addr, t.CPtr))
if slot is None:
return None
return (TypeSpec | t.CPtr)(t.CVoid(uint64(slot[0]), t.CPtr))
# ============================================================
# TextToSpec - 规范文本解码(SpecToText 的逆;不驻留,由调用方决定是否驻留)
# 解析失败返回 None(fail-fast 由调用方决定)
# ============================================================
def TextToSpec(text: str) -> TypeSpec | t.CPtr:
"""把规范文本解码为 TypeSpec(PermPool,不驻留),失败返回 None"""
if text is None:
return None
return _text_to_spec(text, 0)
def _text_to_spec(text: str, depth: int) -> TypeSpec | t.CPtr:
"""TextToSpec 的递归实现(depth 上限 32)"""
if text is None or depth > 32:
return None
# 指针:p:INNER
if string.strncmp(text, "p:", 2) == 0:
inner: TypeSpec | t.CPtr = _text_to_spec(text + 2, depth + 1)
if inner is None:
return None
sp_p: TypeSpec | t.CPtr = _spec_make(TS_PTR, 0, TS_SIGN_UNKNOWN)
if sp_p is None:
return None
sp_p.Elem = inner
sp_p.PtrLevel = inner.PtrLevel + 1
sp_p.ElemBits = inner.Bits
if inner.Kind == TS_PTR:
sp_p.IsElemPtr = 1
return sp_p
# 数组:arr[N]:INNER
if string.strncmp(text, "arr[", 4) == 0:
rb: str = string.strchr(text, ']')
if rb is None:
return None
count: int = string.atoi(text + 4)
if count < 0:
return None
colon: str = string.strchr(rb, ':')
if colon is None:
return None
inner_a: TypeSpec | t.CPtr = _text_to_spec(colon + 1, depth + 1)
if inner_a is None:
return None
sp_a: TypeSpec | t.CPtr = _spec_make(TS_ARRAY, 0, TS_SIGN_UNKNOWN)
if sp_a is None:
return None
sp_a.Elem = inner_a
sp_a.ArrayCount = count
sp_a.ElemBits = inner_a.Bits
if inner_a.Kind == TS_PTR:
sp_a.IsElemPtr = 1
return sp_a
# 函数:fn(...)->RET
if string.strncmp(text, "fn(", 3) == 0:
return _text_to_func(text, depth)
# 结构体/联合/枚举:st:NAME / un:NAME / en:NAME
if string.strncmp(text, "st:", 3) == 0:
return _spec_make_struct(TS_STRUCT, text + 3)
if string.strncmp(text, "un:", 3) == 0:
return _spec_make_struct(TS_UNION, text + 3)
if string.strncmp(text, "en:", 3) == 0:
return _spec_make_struct(TS_ENUM, text + 3)
if string.strcmp(text, "bool") == 0:
return _spec_make(TS_BOOL, 1, TS_SIGN_UNSIGNED)
if string.strcmp(text, "void") == 0:
return _spec_make(TS_VOID, 0, TS_SIGN_UNKNOWN)
# i32 / u64 / f64
if text[0] == 'u' or text[0] == 'i' or text[0] == 'f':
bits: int = stdlib.atoi(text + 1)
if bits <= 0:
return None
if text[0] == 'u':
return _spec_make(TS_UINT, bits, TS_SIGN_UNSIGNED)
if text[0] == 'i':
return _spec_make(TS_INT, bits, TS_SIGN_SIGNED)
return _spec_make(TS_FLOAT, bits, TS_SIGN_SIGNED)
return None
# ============================================================
# _spec_make_struct - 构造 struct(深拷贝名字)
# ============================================================
def _spec_make_struct(kind: int, name: str) -> TypeSpec | t.CPtr:
"""构造 struct/union/enum 类型(不驻留),失败返回 None"""
if name is None or name[0] == '\0':
return None
sp: TypeSpec | t.CPtr = _spec_make(kind, 0, TS_SIGN_UNKNOWN)
if sp is None:
return None
if _spec_set_struct_name(sp, name) != 0:
return None
return sp
# ============================================================
# _text_to_func - 解析 "fn(p1,p2,...)->RET"
# ============================================================
def _text_to_func(text: str, depth: int) -> TypeSpec | t.CPtr:
"""解析函数签名文本,失败返回 None"""
lp: str = string.strchr(text, '(')
if lp is None:
return None
arrow: str = string.strstr(text, ")->")
if arrow is None:
return None
# 参数段范围(lp+1 .. arrow-1)
params_buf: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if params_buf is None:
return None
params_buf[0] = '\0'
nlen: sizet = t.CSizeT(t.CUInt64T(arrow) - t.CUInt64T(lp)) - 1
if nlen > 0:
string.strncpy(params_buf, lp + 1, nlen)
params_buf[nlen] = '\0'
is_vararg: int = 0
if string.strstr(params_buf, "...") is not None:
is_vararg = 1
# 逐段解析参数(按顶层 ',' 分割;变参段 "..." 跳过)
param_specs: sizet | t.CPtr = CompilerFactory.factory.PermPool.alloc(8 * 64)
if param_specs is None:
return None
pcount: int = 0
if nlen > 0:
cur: str = params_buf
while cur is not None:
comma: str = string.strchr(cur, ',')
seg: str = cur
if comma is not None:
seg_make: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if seg_make is None:
return None
seg_len: sizet = t.CSizeT(t.CUInt64T(comma) - t.CUInt64T(cur))
string.strncpy(seg_make, cur, seg_len)
seg_make[seg_len] = '\0'
seg = seg_make
cur = comma + 1
else:
cur = None
if string.strcmp(seg, "...") == 0:
continue
if pcount >= 64:
return None
psp: TypeSpec | t.CPtr = _text_to_spec(seg, depth + 1)
if psp is None:
return None
slot_p: sizet | t.CPtr = (sizet | t.CPtr)(
t.CVoid(uint64(param_specs) + uint64(pcount * 8), t.CPtr))
if slot_p is None:
return None
slot_p[0] = sizet(psp)
pcount += 1
ret: TypeSpec | t.CPtr = _text_to_spec(arrow + 3, depth + 1)
if ret is None:
return None
sp_f: TypeSpec | t.CPtr = _spec_make(TS_FUNC, 0, TS_SIGN_UNKNOWN)
if sp_f is None:
return None
sp_f.Ret = ret
sp_f.Params = param_specs
sp_f.ParamCount = pcount
sp_f.IsVarArg = is_vararg
return sp_f
# ============================================================
# InternSpec - 规范化驻留:同键只保留一份,返回驻留后的实例
# 键 = SpecToText;超容量 fail-fast(绝不静默丢弃/复用错误实例)
# ============================================================
def InternSpec(arena: TypeSpecArena | t.CPtr,
sp: TypeSpec | t.CPtr) -> TypeSpec | t.CPtr:
"""按规范文本键驻留 TypeSpec;失败返回 None"""
if arena is None or sp is None:
return sp
if SpecArenaInit(arena, 0) != 0:
return None
key_buf: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if key_buf is None:
return None
if SpecToText(sp, key_buf, SPEC_TEXT_MAX) != 0:
return None
# 线性查找已有键(规模与类型种数同阶,量级不大;命中即复用)
i: int = 0
while i < arena.Count:
exist_key: str = _spec_key_at(arena.Keys, i)
if exist_key is not None and string.strcmp(exist_key, key_buf) == 0:
slot_e: sizet | t.CPtr = (sizet | t.CPtr)(
t.CVoid(uint64(arena.Specs) + uint64(i * 8), t.CPtr))
if slot_e is None:
return None
return (TypeSpec | t.CPtr)(t.CVoid(uint64(slot_e[0]), t.CPtr))
i += 1
# 未命中:追加(容量检查 → fail-fast)
if arena.Count >= arena.Cap:
return None
dst_key: str = _spec_key_at(arena.Keys, arena.Count)
if dst_key is None:
return None
string.strcpy(dst_key, key_buf)
slot_n: sizet | t.CPtr = (sizet | t.CPtr)(
t.CVoid(uint64(arena.Specs) + uint64(arena.Count * 8), t.CPtr))
if slot_n is None:
return None
slot_n[0] = sizet(sp)
arena.Count += 1
return sp
# ============================================================
# 只读访问器(供 S3 起替换旧的字符串判定)
# ============================================================
def SpecIsPtr(sp: TypeSpec | t.CPtr) -> int:
"""是否指针类型(PtrLevel>0)"""
if sp is None:
return 0
if sp.PtrLevel > 0:
return 1
return 0
def SpecPointedTo(sp: TypeSpec | t.CPtr) -> TypeSpec | t.CPtr:
"""指针的指向类型(非指针返回 None)"""
if sp is None or sp.Kind != TS_PTR:
return None
return sp.Elem
def SpecIsUnsigned(sp: TypeSpec | t.CPtr) -> int:
"""是否无符号整数(单一真源;未知返回 0)"""
if sp is None:
return 0
if sp.IsSigned == TS_SIGN_UNSIGNED:
return 1
return 0
def SpecBitWidth(sp: TypeSpec | t.CPtr) -> int:
"""位宽(非整形/浮点返回 0)"""
if sp is None:
return 0
return sp.Bits
# ============================================================
# SpecSelfCheck - S1 自检(不参与编译流程,供手动调用/后续单测)
#
# 构造若干类型 → 编码 → 解码 → 再编码,比较两次文本是否一致(往返一致)。
# 返回 0=全部通过;否则返回失败用例编号(从 1 起)
# ============================================================
def SpecSelfCheck() -> int:
"""TypeSpec 编解码自检,返回 0=通过 / 失败用例号"""
arena: TypeSpecArena | t.CPtr = (TypeSpecArena | t.CPtr)(
t.CVoid(CompilerFactory.factory.PermPool.alloc(TypeSpecArena.__sizeof__()), t.CPtr))
if arena is None:
return 1
string.memset(arena, 0, TypeSpecArena.__sizeof__())
if SpecArenaInit(arena, 64) != 0:
return 1
# 1) u64
s_u64: TypeSpec | t.CPtr = NewTypeSpec(arena, TS_UINT, 64, TS_SIGN_UNSIGNED)
if s_u64 is None:
return 1
if _check_roundtrip(s_u64, "u64") != 0:
return 1
# 2) i32
s_i32: TypeSpec | t.CPtr = NewTypeSpec(arena, TS_INT, 32, TS_SIGN_SIGNED)
if s_i32 is None:
return 2
if _check_roundtrip(s_i32, "i32") != 0:
return 2
# 3) u64*
s_p: TypeSpec | t.CPtr = NewTypePtr(arena, s_u64)
if s_p is None:
return 3
if _check_roundtrip(s_p, "p:u64") != 0:
return 3
# 4) i8[4]
s_arr: TypeSpec | t.CPtr = NewTypeArray(arena, s_i32, 4)
if s_arr is None:
return 4
if _check_roundtrip(s_arr, "arr[4]:i32") != 0:
return 4
# 5) fn(i32,u64)->i32(变参)
params: sizet | t.CPtr = CompilerFactory.factory.PermPool.alloc(8 * 2)
if params is None:
return 5
slot0: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(uint64(params), t.CPtr))
if slot0 is None:
return 5
slot0[0] = sizet(s_i32)
slot1: sizet | t.CPtr = (sizet | t.CPtr)(t.CVoid(uint64(params) + 8, t.CPtr))
if slot1 is None:
return 5
slot1[0] = sizet(s_u64)
s_fn: TypeSpec | t.CPtr = NewTypeFunc(arena, s_i32, params, 2, 1)
if s_fn is None:
return 5
if _check_roundtrip(s_fn, "fn(i32,u64,...)->i32") != 0:
return 5
# 6) 驻留去重:同一文本第二次应返回同一实例
s_u64_again: TypeSpec | t.CPtr = NewTypeSpec(arena, TS_UINT, 64, TS_SIGN_UNSIGNED)
if s_u64_again is None:
return 6
if t.CUInt64T(s_u64_again) != t.CUInt64T(s_u64):
return 6
return 0
# ============================================================
# _check_roundtrip - 自检辅助:编码应等于 expect,且解码再编码应一致
# ============================================================
def _check_roundtrip(sp: TypeSpec | t.CPtr, expect: str) -> int:
"""编码比对 + 解码往返一致;返回 0=通过 / 1=失败"""
if sp is None or expect is None:
return 1
buf1: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if buf1 is None:
return 1
if SpecToText(sp, buf1, SPEC_TEXT_MAX) != 0:
return 1
if string.strcmp(buf1, expect) != 0:
return 1
back: TypeSpec | t.CPtr = TextToSpec(buf1)
if back is None:
return 1
buf2: str = CompilerFactory.factory.PermPool.alloc(SPEC_TEXT_MAX)
if buf2 is None:
return 1
if SpecToText(back, buf2, SPEC_TEXT_MAX) != 0:
return 1
if string.strcmp(buf2, expect) != 0:
return 1
return 0