Files
TransPyC/lib/core/SymbolUtils.py
T
2026-09-18 20:00:59 +08:00

631 lines
31 KiB
Python
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.
"""共享工具函数:符号表处理中使用的 AST 分析工具"""
from __future__ import annotations
import vast as ast
import re
from lib.includes import t
from lib.includes.t import CTypeRegistry
_T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__})
# Viper 软关键字 fn/fun/func/function 是 def 的别名(行首 + 空白 + 标识符才算函数定义,
# 变量名/调用/注解中的 fn 不受影响)。展开为 def 后行号不变,供 ast.parse 使用。
_SOFT_KEYWORD_RE: re.Pattern[str] = re.compile(
r'^(\s*)(function|func|fun|fn)(\s+)(?=[A-Za-z_])', re.MULTILINE
)
def ExpandSoftKeywords(Code: str) -> str:
"""将 Viper 软关键字 fn/fun/func/function 展开为 def(函数定义行)。
仅在"行首缩进 + 关键字 + 空白 + 标识符"的函数定义形态匹配,
变量名(fn = 5)、调用(fn(x))、注解(x: fn)与字符串内容不受影响。
展开为 def 与关键字等宽或更短,行号不变,AST 行号映射保持有效。
"""
if not Code:
return Code
return _SOFT_KEYWORD_RE.sub(lambda m: f'{m.group(1)}def{m.group(3)}', Code)
def IsTModule(module_id: str, symtable: object | None = None) -> bool:
"""检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)"""
if module_id in _T_MODULE_NAMES:
return True
if symtable is not None and hasattr(symtable, 'import_aliases'):
resolved: str = symtable.import_aliases.get(module_id, module_id)
if resolved in _T_MODULE_NAMES:
return True
return False
def IsTModulePath(module_path: str) -> bool:
"""检查模块路径是否指向 t 类型模块"""
if module_path in _T_MODULE_NAMES:
return True
if module_path:
t_name: str
for t_name in _T_MODULE_NAMES:
if module_path == t_name or module_path.startswith(t_name + '.'):
return True
return False
def AnnotationContainsTType(node: ast.AST, TypeName: str) -> bool:
"""检测 AST 节点中是否包含指定名称的 t 模块类型"""
if isinstance(node, ast.Attribute):
if (hasattr(node.value, 'id') and IsTModule(node.value.id) and
node.attr == TypeName):
return True
elif isinstance(node, ast.BinOp):
return AnnotationContainsTType(node.left, TypeName) or AnnotationContainsTType(node.right, TypeName)
elif isinstance(node, ast.Call):
if AnnotationContainsTType(node.func, TypeName):
return True
for arg in node.args:
if AnnotationContainsTType(arg, TypeName):
return True
elif isinstance(node, ast.Subscript):
if AnnotationContainsTType(node.value, TypeName):
return True
if AnnotationContainsTType(node.slice, TypeName):
return True
return False
def IsListAnnotation(annotation: ast.expr) -> bool:
"""检查注解是否为栈上固定数组类型(t.CArray[...] 或 list[type, count])"""
if not isinstance(annotation, ast.Subscript):
return False
value = annotation.value
# t.CArray[...] / c.CArray[...]
if (isinstance(value, ast.Attribute)
and isinstance(value.value, ast.Name)
and value.value.id in ('t', 'c')
and value.attr == 'CArray'):
return True
# 裸名 CArray[...](from t import *)/ 内建 rawarray[...]:与 t.CArray 等价
# rawarray 是 CArray 的内建复刻名(hashlib 等裸名化重构使用,免写 t. 前缀)
if (isinstance(value, ast.Name)
and value.id in ('CArray', 'rawarray')):
return True
# list[type, count] — 仅匹配双参数 Tuple 切片(固定数组),
# 单参数 list[type] 不匹配(走泛型类 list[T] 路径)
if (isinstance(value, ast.Name)
and value.id == 'list'
and isinstance(annotation.slice, ast.Tuple)
and len(annotation.slice.elts) == 2):
return True
return False
def ExtractListFromBinOp(annotation: ast.expr) -> ast.Subscript | None:
"""从 BinOp(BitOr) 注解中提取 list[...] 部分,如 list[i32] | t.CPtr"""
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
for side in (annotation.left, annotation.right):
if IsListAnnotation(side):
return side
return None
class ListAnnotationParseResult:
"""list[...] 注解解析结果。
is_pointer=True 表示单参数 list[type] → 指针模式;
is_pointer=False 表示 Tuple(2) 模式 list[type, count]。
"""
__slots__ = ('elem_type_node', 'count_node', 'is_pointer')
def __init__(self, elem_type_node: ast.expr, count_node: ast.expr | None, is_pointer: bool) -> None:
self.elem_type_node: ast.expr = elem_type_node
self.count_node: ast.expr | None = count_node
self.is_pointer: bool = is_pointer
def ParseListAnnotation(annotation: ast.expr) -> ListAnnotationParseResult | None:
"""解析 list[...] 注解,统一处理 list[type, count] / list[type] / BinOp(BitOr) 形式。
自动处理 BinOp(BitOr) 包裹(如 list[i32, N] | t.CPtr)。
返回 None 表示不是 list 注解。
"""
list_node: ast.expr = annotation
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
extracted: ast.Subscript | None = ExtractListFromBinOp(annotation)
if extracted is None:
return None
list_node = extracted
if not IsListAnnotation(list_node):
return None
slice_node: ast.AST = list_node.slice
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
return ListAnnotationParseResult(slice_node.elts[0], slice_node.elts[1], is_pointer=False)
# 单参数 list[type] → 指针模式
return ListAnnotationParseResult(slice_node, None, is_pointer=True)
def CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool:
"""检测注解节点中是否包含 CInline"""
if annotation_node is None:
return False
if isinstance(annotation_node, ast.Attribute):
if hasattr(annotation_node.value, 'id') and IsTModule(annotation_node.value.id) and annotation_node.attr == 'CInline':
return True
if isinstance(annotation_node, ast.Name):
if annotation_node.id == 'CInline':
return True
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
return CheckAnnotationHasCInline(annotation_node.left) or CheckAnnotationHasCInline(annotation_node.right)
return False
def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None:
"""从 BinOp(BitOr) 注解左侧提取类型名(如 TypeA | TypeB → TypeA)。
返回 Name.id 或 Attribute.attr,非 BinOp 返回 None。
"""
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
left: ast.expr = annotation.left
if isinstance(left, ast.Name):
return left.id
if isinstance(left, ast.Attribute):
return left.attr
return None
def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set,
TCAliasMap: dict[str, tuple[str, str]] | None = None,
StructSha1Map: dict[str, str] | None = None) -> str | None:
"""递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。
检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。
支持泛型特化类型(如 GSList[Param]):特化名按**同一规则源**
(ResolveTypeArgStr:sha1 前缀 + rawptr/CPtr 的 '*' 逐层)构造后在
struct_names 中查找——查表键与 _mangle_generic_class_name 产物逐字一致,
否则同一注解在"注解查找"与"特化注册"两处解析出不同名字(opaque 副本)。
"""
if isinstance(node, ast.Name) and node.id in struct_names:
return node.id
if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value in struct_names:
return node.value
if isinstance(node, ast.Attribute) and node.attr in struct_names:
return node.attr
if isinstance(node, ast.Subscript):
base_name: str | None = None
if isinstance(node.value, ast.Name):
base_name = node.value.id
elif isinstance(node.value, ast.Attribute):
base_name = node.value.attr
if base_name:
slice_node: ast.AST = node.slice
if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index):
slice_node = slice_node.value
elem_nodes: list[ast.AST] = (
list(slice_node.elts) if isinstance(slice_node, ast.Tuple) else [slice_node])
type_args: list[str] = [
ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in elem_nodes]
spec_name: str = base_name + '[' + ']['.join(type_args) + ']'
if spec_name in struct_names:
return spec_name
if base_name in struct_names:
return base_name
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
result: str | None = FindStructNameInAnnotation(
node.left, struct_names, TCAliasMap, StructSha1Map)
if result:
return result
return FindStructNameInAnnotation(
node.right, struct_names, TCAliasMap, StructSha1Map)
return None
def FindStructNameInAnnotationWithContext(Node: ast.AST, Trans: object, Gen: object,
StructNames: dict | set | None = None) -> str | None:
"""按 Gen/Trans 上下文注入规则表后查找结构体名。
规则本体唯一(FindStructNameInAnnotation),此处只做与
HandlesExprCall._resolve_type_arg_str 同口径的上下文注入,避免各调用点
各写一套拼名(查表键必须与 _mangle_generic_class_name 产物逐字一致)。
"""
Names: dict | set = StructNames if StructNames is not None else Gen.structs
return FindStructNameInAnnotation(
Node, Names,
TCAliasMap=getattr(Trans, '_t_c_imported_names', {}),
StructSha1Map=getattr(Gen, '_struct_sha1_map', {}))
def AnnotationContainsName(annotation: ast.AST, name: str) -> bool:
"""检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。
同时检查 Attribute.attr 和 Name.id,比 ast.dump 字符串匹配更精确。
"""
if isinstance(annotation, ast.Attribute):
return annotation.attr == name
if isinstance(annotation, ast.Name):
return annotation.id == name
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return AnnotationContainsName(annotation.left, name) or AnnotationContainsName(annotation.right, name)
return False
def IsCPtrNode(node: ast.AST) -> bool:
"""检查节点是否为 t.CPtr 属性引用。"""
return (isinstance(node, ast.Attribute)
and isinstance(node.value, ast.Name)
and node.value.id == 't'
and node.attr == 'CPtr')
def IsCPtrSubscriptNode(node: ast.AST) -> bool:
"""检查节点是否为 t.CPtr[T] 下标指针类型(非裸标记,裸标记见 IsCPtrNode)。
与 TPV TypeGenResolve._is_cptr_subscript(TypeGenResolve.vp:1485-1505)同口径:
value 必须是 t.CPtr 属性引用(只认 t 前缀,c.CPtr[...] 不认);
切片内的指针层级不限(t.CPtr[t.CPtr]、t.CPtr[t.CPtr[t.CInt]] 均命中)。
链式写法 t.CPtr[T][T](value 是 Subscript)两套编译器均不认作该标记。
"""
return (isinstance(node, ast.Subscript)
and IsCPtrNode(node.value))
# 指针下标的 base 名:rawptr 是编译器内置语法(TPC/TPV 双端保留字,见
# includes/builtins.vp 语义注释与 TypeAnnotationResolver._BUILTIN_ROWPTR_NAME),
# 单独 rawptr 等价 t.CPtr(void*)。
_RAWPTR_SUBSCRIPT_NAME: str = 'rawptr'
# 指针标记类型名(t.CPtr):裸名书写 `CPtr[T]` / 导入别名 `X`(from t import CPtr as X)
# 都归一到本名。
_CPTR_TYPE_NAME: str = 'CPtr'
def ResolveTCImportAliasName(NameId: str, symtable: object | None = None,
TCAliasMap: dict[str, tuple[str, str]] | None = None) -> str:
"""t/c 导入别名 → 原始类型名('X' → 'CPtr'、'myint' → 'CInt');未命中原样返回。
两张同源别名表(注册点唯一:HandlesImports._EmitImportFromDeclarationsLlvm /
_RegisterFromImportAliases,与 SymbolTable 序列化),调用方按手头有的表注入:
- TCAliasMap:{别名: (模块, 原名)}(Trans._t_c_imported_names /
DeclarationGenerator._t_c_imported_names 同结构)
- symtable._t_type_symbols:{别名: t.CType 子类}(from t import CPtr as X → t.CPtr)
非 t/c 模块的 from 导入别名(from ast import AST as A)不参与归一。
"""
if not NameId:
return NameId
if TCAliasMap:
Entry: tuple[str, str] | None = TCAliasMap.get(NameId)
if Entry:
SrcModule, SrcName = Entry
if IsTModule(SrcModule):
return SrcName
if symtable is not None:
Cls: object | None = getattr(symtable, '_t_type_symbols', {}).get(NameId)
if Cls is not None:
return getattr(Cls, '__name__', NameId)
return NameId
def IsRawPtrSubscriptNode(node: ast.AST) -> bool:
"""检查节点是否为内置裸指针下标 `rawptr[T]`(非裸标记 rawptr)。
`rawptr[T]` 语义 = `t.CPtr[T]` = 源码联合写法 `T | t.CPtr`(Ptr(T)),三者
必须走同一解析与拼名路径,否则同一类型的不同书写会分裂出不同特化名
(结构体/方法声明/调用点三处不一致)。
注意:本判据**只**认 `rawptr[T]`;`t.CPtr[T]` / 裸名 `CPtr[T]` 由
IsPtrSubscriptNode 判定(见下),二者共用同一条脱糖规则
(DesugarPtrSubscriptToUnion),此处保持 rawptr 专用以便仍需区分书写时使用。
"""
return (isinstance(node, ast.Subscript)
and isinstance(node.value, ast.Name)
and node.value.id == _RAWPTR_SUBSCRIPT_NAME)
def IsPtrSubscriptNode(node: ast.AST, symtable: object | None = None,
TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool:
"""检查节点是否为"指针下标类型":`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]`
/ 导入别名 `X[T]`(`from t import CPtr as X`)。
三者语义完全相同(= 源码联合写法 `T | t.CPtr` = Ptr(T),见
builtins.vp 与 t.py 的 CPtr 语义注释):内层 T 按**参数注解 `a: T` 的规则**
解析(跨模块类 → '<sha1>.X'),再包一层指针——T 为类引用时该层被结构体
自身吸收('<sha1>.X*'),其余(标量 / 已是指针的 typedef / 泛型容器特化 /
嵌套指针下标)逐层外包。
- 裸名 `CPtr[T]`(`from t import CPtr` / `from t import *`)与声明端
DeclarationGenerator._get_type_str 的 ValueIsCPtr 判据一致(那里同样接受
Attribute 'CPtr' 与 Name 'CPtr' 两种书写)。
- 传入 symtable 时,`import t as tt` 的 `tt.CPtr[T]` 一并命中(与声明端
"只看 attr 名"的 ValueIsCPtr 判据对齐,避免同一书写两端分裂);
不传(名称域 ResolveTypeArgStr 的字面名场景)时只认字面 't'。
- 导入名别名(`from t import CPtr as X` 的 `X[T]`)经别名表解析 X→CPtr
(TCAliasMap 或 symtable._t_type_symbols,见 ResolveTCImportAliasName),
定义端与声明端各自注入手头的同一批表,判据只有这一处。
"""
if IsRawPtrSubscriptNode(node):
return True
if not isinstance(node, ast.Subscript):
return False
value: ast.AST = node.value
if isinstance(value, ast.Name) and value.id == _CPTR_TYPE_NAME:
return True
return IsCPtrMarkerNode(value, symtable, TCAliasMap)
def DesugarPtrSubscriptToUnion(node: ast.AST, symtable: object | None = None,
TCAliasMap: dict[str, tuple[str, str]] | None = None) -> ast.AST:
"""把指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` / 别名 `X[T]`)
折叠成等价的联合写法 `T | t.CPtr`。
内层 T 递归折叠(`rawptr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`、
`t.CPtr[rawptr[X]]` → `(X | t.CPtr) | t.CPtr`),
与 TPV TypeGenResolve.resolve_annotation_type 的 Subscript 分支同语义
(T → Ptr(T),层数逐层外包);折叠后交给 HandlesTypeMerge 既有的
联合解析(_CollectAllOperandTypes 会把嵌套 BitOr 拉平成 [T, t.CPtr, ...],
struct 基类型吸收一层、其余逐层累加),从而:
- 内层 T 走通用类型解析(含跨模块类经 Gen.structs 反查 SHA1 前缀);
- `rawptr[X]` / `t.CPtr[X]` / `X[T]` / `X | t.CPtr` 得到同一 CTypeInfo
(PtrCount/BaseType 一致)与同一泛型实参串(逐层 '*')。
非指针下标节点原样返回(调用方无需先判类型);symtable/TCAliasMap 透传给
判据(识别 `import t as tt` 的 `tt.CPtr[T]` 与 `from t import CPtr as X` 的
`X[T]` 内层嵌套)。折叠产物统一用字面 `t.CPtr` 标记,与源码联合写法同形。
"""
if not IsPtrSubscriptNode(node, symtable, TCAliasMap):
return node
inner: ast.AST = DesugarPtrSubscriptToUnion(node.slice, symtable, TCAliasMap)
cptr_marker: ast.AST = ast.Attribute(
value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load())
union_node: ast.AST = ast.BinOp(left=inner, op=ast.BitOr(), right=cptr_marker)
try:
ast.copy_location(union_node, node)
except Exception:
pass
return union_node
# ---------------------------------------------------------------------------
# 泛型实参串(mangling):定义端与声明端唯一规则源
# ---------------------------------------------------------------------------
# 同一泛型特化(如 list[rawptr[ast.AST]])在 define 端发射的结构体名
# (HandlesClassDef._mangle_generic_class_name → %"<mod>.list[ast.ASTptr]")、
# 各端方法名、以及 declare/stub 端引用的类型名必须逐字一致,否则会出现
# Undefined method / 签名错位。此前 DeclarationGenerator._get_type_str 的泛型
# 分支自带第二套(只认 Name / Name-Tuple 实参的)规则,其余形态(Attribute /
# Subscript / BinOp / 混合 Tuple)退回未特化的 %"<mod>.list"*,与 define 端
# 不一致。故此处收拢唯一规则,两端皆调用本组函数。
# 定义端 _resolve_generic_slice_type 的 ast.Name 规范化表:
# 源码里的 C 定长类型名 → 喂给 _mangle_generic_class_name 的 type_arg。
_GENERIC_ARG_INT_NAMES: frozenset[str] = frozenset({
'CInt', 'CUInt', 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T',
'CInt16T', 'CUInt16T', 'CInt8T', 'CUInt8T', 'CChar', 'CShort', 'CLong',
})
_GENERIC_ARG_FLOAT_NAMES: frozenset[str] = frozenset({
'CDouble', 'CFloat', 'CFloat64T', 'CFloat32T',
})
# 源码名 → 泛型 T 替换用的原始类型名(_resolve_generic_slice_type 的第二返回值)
_GENERIC_ARG_RAW_NAMES: dict[str, str] = {
'int': 'CInt', 'double': 'CDouble', 'float': 'CFloat',
'char': 'CChar', 'bool': 'CBool',
}
# _mangle_generic_class_name 的浮点归一集合(LLVM 浮点串 → 'f' + Size)
_LLVM_FLOAT_STRINGS: frozenset[str] = frozenset({'float', 'double', 'half', 'fp128'})
def NormalizeGenericArgName(NameId: str) -> str:
"""ast.Name 泛型实参 → 定义端 type_arg(HandlesExprCall._resolve_generic_slice_type
的 ast.Name 分支,逐分支同构):
int/double/float 保留源码名('list[int]' 而非 'list[i32]')、
char/bool → 'i8'、C 整数族 → 'int'、C 浮点族 → 'double'、其余原名。
"""
if NameId in ('int', 'double', 'float'):
return NameId
if NameId in ('char', 'bool'):
return 'i8'
if NameId in _GENERIC_ARG_INT_NAMES:
return 'int'
if NameId in _GENERIC_ARG_FLOAT_NAMES:
return 'double'
return NameId
def GenericArgRawName(NameId: str) -> str:
"""ast.Name 泛型实参 → 替换模板 T 用的原始类型名(_resolve_generic_slice_type
的第二返回值):'int' → 'CInt'(内建类型须用源码名替换 T,见
HandlesClassDef._specialize_generic_class 的 _BUILTIN_TYPE_NAMES 判定),
其余原名。仅定义端 T 替换用;mangling 只用 NormalizeGenericArgName。
"""
return _GENERIC_ARG_RAW_NAMES.get(NameId, NameId)
def MangleTypeArgByRegistry(ArgStr: str) -> str:
"""实参串 → 最终 mangled 实参(HandlesClassDef._mangle_generic_class_name
的单项逻辑,逐分支同构):t 类型名经 NameToLLVM 归一(CInt32T → 'i32',
't.CInt'/'c.CInt' 先去模块前缀再归一),浮点按 Size 归一为 f{n}
(CDouble → 'f8'),未注册名原样保留(含指针层数与已解析的内层名)。
"""
LookupStr: str = ArgStr
if ArgStr.startswith('t.') or ArgStr.startswith('c.'):
LookupStr = ArgStr[2:]
LlvmStr: str | None = CTypeRegistry.NameToLLVM(LookupStr)
if not LlvmStr:
return ArgStr
if LlvmStr in _LLVM_FLOAT_STRINGS:
Cls: type | None = CTypeRegistry.GetClassByName(LookupStr)
Size: int = Cls().Size if Cls is not None else 0
return 'f' + str(Size)
return LlvmStr
# 指针层数的首层归一化(与 LLVMTypeMapper._add_pointer_level 同口径)
_POINTER_PLACEHOLDER_BASES: frozenset[str] = frozenset({'', 'void', 'void*'})
def AddPointerLayers(TypeStr: str, Layers: int) -> str:
"""给已解析的类型串追加 Layers 层指针(空/void/void* 的首层归一化为 i8*)。
与 LLVMTypeMapper._add_pointer_level 同口径;名称域与类型域因此对
rawptr[t.CVoid] 一致地给出 i8*(唯一允许的 void 兜底)。
"""
Result: str = TypeStr
for _ in range(Layers):
if Result in _POINTER_PLACEHOLDER_BASES:
Result = 'i8*'
else:
Result = Result + '*'
return Result
def IsCPtrMarkerNode(Node: ast.AST, symtable: object | None = None,
TCAliasMap: dict[str, tuple[str, str]] | None = None) -> bool:
"""`t.CPtr` 裸指针标记(含 `import t as tt` 与 `from t import CPtr as X` 别名):
联合写法里贡献一层 '*'。
与 IsCPtrNode 的区别:仅按 attr 名 + t 模块前缀判定,不看切片
(`t.CPtr[T]` 下标是**基类型**而非层数标记,见 ResolveTypeArgStr 的
Subscript 分支)。
- Attribute:`t.CPtr` / `tt.CPtr`(t 模块别名经 symtable.import_aliases 解析)。
- Name:经别名表归一为 'CPtr' 的书写名(`from t import CPtr as X` 的 `X`);
字面裸名 `CPtr` 是否算标记取决于该模块是否 `from t import CPtr`
(别名表命中一致:两端都由同一批表判定,不另设白名单)。
"""
if isinstance(Node, ast.Attribute):
return (Node.attr == _CPTR_TYPE_NAME
and isinstance(Node.value, ast.Name)
and IsTModule(Node.value.id, symtable))
if isinstance(Node, ast.Name):
return ResolveTCImportAliasName(Node.id, symtable, TCAliasMap) == _CPTR_TYPE_NAME
return False
def FlattenUnionOperands(Node: ast.AST) -> list[ast.AST]:
"""拉平嵌套 BitOr,按书写顺序返回操作数(`A | t.CPtr | t.CPtr` → [A, CPtr, CPtr])。"""
if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr):
return FlattenUnionOperands(Node.left) + FlattenUnionOperands(Node.right)
return [Node]
def ResolveTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None,
StructSha1Map: dict[str, str] | None = None) -> str:
"""将类型参数 AST 节点解析为规范化的类型字符串(泛型 mangling 用)。
规则(**与参数注解 `a: T` 完全同一套解析**,定义端/声明端唯一规则源):
- ast.Name: t/c 导入别名 → '{module}.{name}';已注册类 → '{sha1}.{名}'
(与参数注解的 %"{sha1}.X" 同前缀);其余按 NormalizeGenericArgName
归一('int'/'i32'/'str' 原名、'char'/'bool' → 'i8'、C 整数/浮点族 →
'int'/'double')
- ast.Attribute: t 模块类型按 CTypeRegistry.NameToLLVM 解析(t.CPtr → i8*、
t.CInt → i32);跨模块类引用 module.X → '{sha1}.X';其余 '{mod}.{attr}'
- ast.Subscript: 指针下标(`rawptr[T]` / `t.CPtr[T]` / 裸名 `CPtr[T]` /
导入别名 `X[T]`)已在入口脱糖为上面的联合写法(走同一规则),故此处只剩泛型容器:
'<容器名>[<已解析实参>]'(多实参 '][' 分隔),容器名的
模块前缀由调用方(_mangle_name / '%"{模sha1}.{特化名}"')统一加
- ast.BinOp(BitOr)(含指针下标脱糖而来): 取非 CPtr 侧为基类型,
每个 `t.CPtr` 标记追加一层 '*'(`X | t.CPtr` ≡ `rawptr[X]` ≡
`t.CPtr[X]` → '<sha1>.X*';`rawptr[rawptr[X]]` → '<sha1>.X**';
标量 `int | t.CPtr` → 'int*')
- 其余形态: ast.unparse
注意:名称域**不做**"裸类引用吸收一层 CPtr"(那是注解→LLVM 类型域的规则,
见 DeclarationGenerator._is_bare_class_ref_annotation):名称域描述的是类型
身份('X' = X 本身、'X*' = 指向 X 的指针),逐层累加,否则
`rawptr[X]` 与 `X | t.CPtr` 会分裂成两个名字。
"""
if IsPtrSubscriptNode(Node, TCAliasMap=TCAliasMap):
Node = DesugarPtrSubscriptToUnion(Node, TCAliasMap=TCAliasMap)
if isinstance(Node, ast.Name):
tn: str = Node.id
if TCAliasMap and tn in TCAliasMap:
# t 模块别名(from t import CInt as myint)按内建归一表解析到同一名字
# ('myint' 与 'CInt' 都 → 'int'),避免同义写法分裂特化名
SrcModule, SrcName = TCAliasMap[tn]
if IsTModule(SrcModule):
return NormalizeGenericArgName(SrcName)
return f'{SrcModule}.{SrcName}'
if StructSha1Map and tn in StructSha1Map:
return f'{StructSha1Map[tn]}.{tn}'
return NormalizeGenericArgName(tn)
if isinstance(Node, ast.Attribute):
return _ResolveAttributeArgStr(Node, TCAliasMap, StructSha1Map)
if isinstance(Node, ast.Subscript):
return _ResolveSubscriptArgStr(Node, TCAliasMap, StructSha1Map)
if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr):
return _ResolveUnionArgStr(Node, TCAliasMap, StructSha1Map)
return ast.unparse(Node)
def _ResolveAttributeArgStr(Node: ast.Attribute, TCAliasMap: dict[str, tuple[str, str]] | None,
StructSha1Map: dict[str, str] | None) -> str:
"""Attribute 形态(t.CInt / ast.AST / <sha1>.AST)→ 实参串。"""
if not isinstance(Node.value, ast.Name):
return ast.unparse(Node)
ModName: str = Node.value.id
AttrName: str = Node.attr
if IsTModule(ModName) or (TCAliasMap and ModName in TCAliasMap):
LlvmStr: str | None = CTypeRegistry.NameToLLVM(AttrName)
if LlvmStr is not None:
return LlvmStr
return f'{ModName}.{AttrName}'
# 跨模块类引用(ast.AST / <sha1>.AST):与参数注解同一条 sha1 解析
if StructSha1Map and AttrName in StructSha1Map:
return f'{StructSha1Map[AttrName]}.{AttrName}'
return f'{ModName}.{AttrName}'
def _ResolveSubscriptArgStr(Node: ast.Subscript, TCAliasMap: dict[str, tuple[str, str]] | None,
StructSha1Map: dict[str, str] | None) -> str:
"""下标形态(t.CPtr[T] / 泛型容器 list[X] / dict[K,V])→ 实参串。
容器名('list' / 'dict' ...)**不带**模块前缀:它只是 _mangle_generic_class_name
的基名,特化名的模块前缀由调用方统一加(定义端 _mangle_name / 声明端
'%"{模sha1}.{特化名}"'),并保证 '_make_replacement_node' 能把实参串
重新解析成可用的注解 AST('<sha1>.list[...]' 会因数字开头而解析失败)。
"""
if IsCPtrSubscriptNode(Node):
Inner: str = ResolveTypeArgStr(Node.slice, TCAliasMap, StructSha1Map)
return AddPointerLayers(Inner, 1)
BaseName: str = ''
if isinstance(Node.value, ast.Name):
BaseName = Node.value.id
elif isinstance(Node.value, ast.Attribute):
BaseName = Node.value.attr
if not BaseName:
return ast.unparse(Node)
SliceNode: ast.AST = Node.slice
ElemNodes: list[ast.AST] = list(SliceNode.elts) if isinstance(SliceNode, ast.Tuple) else [SliceNode]
Resolved: list[str] = [ResolveTypeArgStr(E, TCAliasMap, StructSha1Map) for E in ElemNodes]
return f'{BaseName}[' + ']['.join(Resolved) + ']'
def _ResolveUnionArgStr(Node: ast.BinOp, TCAliasMap: dict[str, tuple[str, str]] | None,
StructSha1Map: dict[str, str] | None) -> str:
"""联合形态(X | t.CPtr | ...)实参串:非 CPtr 侧为基类型,CPtr 标记逐层加 '*'。
标记判据是同一处 IsCPtrMarkerNode(含 `from t import CPtr as X` 的裸标记别名),
别名经 TCAliasMap 解析;没带别名表的 `import t as tt` 的 `tt.CPtr` 在此域
不做模块别名解析(两端口径一致的既有缺口)。
"""
BaseType: str = ''
Layers: int = 0
for Operand in FlattenUnionOperands(Node):
if IsCPtrMarkerNode(Operand, TCAliasMap=TCAliasMap):
Layers += 1
elif not BaseType:
BaseType = ResolveTypeArgStr(Operand, TCAliasMap, StructSha1Map)
if not BaseType:
# 只有 CPtr 标记(如 `t.CPtr | t.CPtr`):等价 void* 逐层——首层 i8*
return AddPointerLayers('void', Layers)
return AddPointerLayers(BaseType, Layers)
def ResolveGenericTypeArgStr(Node: ast.AST, TCAliasMap: dict[str, tuple[str, str]] | None = None,
StructSha1Map: dict[str, str] | None = None) -> str:
"""泛型实参的最终 mangled 串(喂给 _mangle_generic_class_name 的单参数)。
与定义端(HandlesExprCall._resolve_generic_slice_type + _mangle_generic_class_name)
对单个实参的实际结果同一套规则:
- ast.Name: NormalizeGenericArgName →(命中注册类则 '{sha1}.{名}')→ MangleTypeArgByRegistry
('int' → 'int'、'i32' → 'i32'、'str' → 'str'、'CInt32T' → 'int'、
'CFloat64T' → 'double'、'CChar' → 'i8')
- 其余形态(Attribute / Subscript(含 rawptr[...])/ BinOp / 混合 Tuple 元素):
ResolveTypeArgStr(rawptr[T] 脱糖 → 逐层 '*'、t.CPtr → 'i8*'、跨模块类 → sha1 前缀)
→ MangleTypeArgByRegistry
"""
return MangleTypeArgByRegistry(ResolveTypeArgStr(Node, TCAliasMap, StructSha1Map))