Files
TransPyC/lib/core/SymbolUtils.py

215 lines
8.8 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 ast
from lib.includes import t
_T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__})
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[...]
if (isinstance(value, ast.Attribute)
and isinstance(value.value, ast.Name)
and value.value.id == 't'
and value.attr == 'CArray'):
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) -> str | None:
"""递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。
检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。
支持泛型特化类型(如 GSList[Param]),构造特化名后检查是否在 struct_names 中。
"""
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
type_args: list[str] = []
if isinstance(slice_node, ast.Name):
type_args.append(slice_node.id)
elif isinstance(slice_node, ast.Attribute):
type_args.append(slice_node.attr)
elif isinstance(slice_node, ast.Tuple):
for elt in slice_node.elts:
if isinstance(elt, ast.Name):
type_args.append(elt.id)
elif isinstance(elt, ast.Attribute):
type_args.append(elt.attr)
if type_args:
spec_name: str = base_name + ''.join(f'[{arg}]' for arg in 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)
if result:
return result
return FindStructNameInAnnotation(node.right, struct_names)
return None
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')