215 lines
8.8 KiB
Python
215 lines
8.8 KiB
Python
"""共享工具函数:符号表处理中使用的 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')
|