chore: 改名同步 TransPyV -> ViperC(源码/配置内的纯替换)

This commit is contained in:
TermiNexus committed 2026-10-12 15:06:48 +08:00
1 parent f6447ad594
commit 15f77da9d3
29 files changed
+32737 -32737

No files matched your search

+3 -3
View File
@@ -20,9 +20,9 @@
SUSPECT 其余 ⇒ **需人工判定**;`--strict` 下 exit 1(门禁失败)
覆盖范围:仓库内全部 `*.py`(含根目录脚本、`MCP/**`、`TransPyC/Tools/**`),
跳过 `.git`/`__pycache__`/`output`/`temp`/`.tpv_build`/`dlls` 与解析器自身
跳过 `.git`/`__pycache__`/`output`/`temp`/`__vpcbuild__`/`dlls` 与解析器自身
(`lib/vast`、`lib/vast_c` 是前端**实现**,不是前端**消费者**)。
TPV 侧源码(`TransPyV/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。
VPC 侧源码(`ViperC/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。
用法(**只读,不写任何文件**)
python TransPyC/Tools/AuditViperParseEntries.py # 打印审计表
@@ -35,7 +35,7 @@ import sys
ScriptDir = os.path.dirname(os.path.abspath(__file__))
RepoRoot = os.path.dirname(os.path.dirname(ScriptDir))
SkipDirs = {'__pycache__', '.git', '.tpv_build', 'dlls', 'output', 'temp', 'node_modules'}
SkipDirs = {'__pycache__', '.git', '__vpcbuild__', 'dlls', 'output', 'temp', 'node_modules'}
# 前端实现(vast 解析器本体;它自己就是"唯一许可入口"的实现层)
SkipRelPrefixes = ('TransPyC/lib/vast/', 'TransPyC/lib/vast_c/')
+3 -3
View File
@@ -18,7 +18,7 @@
5) 类成员表按族隔离: 基类先在**本族**找, 再找**支撑族**(''), 最后全局唯一名;
同一基类名跨族重复时报 AMBIG(宁可 UNRESOLVED 也不猜)。
6) `t.REnum`(tagged union)派生类: 变体字段合并进父类成员集
(TPV 允许在父类指针上访问变体字段, 不合并会产生大量误报)。
(VPC 允许在父类指针上访问变体字段, 不合并会产生大量误报)。
前端 = **TPC 自研 vast**(`lib/vast`,与 StubGen/Converter.py 用的 `import vast as ast`
是同一源文件; 不可用 CPython `ast`, 否则含 `list[X*]` 等 vast 专有写法的文件会整体
@@ -44,7 +44,7 @@ SyntaxError 被跳过)。vast 与 CPython ast 的差异及本脚本的适配:
python TransPyC/Tools/ScanAstFieldMiss.py --selftest [--includes DIR] [--out FILE]
默认值:
--app-root <RepoRoot>/TransPyV/App
--app-root <RepoRoot>/ViperC/App
--includes <RepoRoot>/includes
--out <RepoRoot>/Debugger/ScanAstFieldMiss.txt
@@ -81,7 +81,7 @@ ScriptDir = os.path.dirname(os.path.abspath(__file__))
TpcRoot = os.path.dirname(ScriptDir)
RepoRoot = os.path.dirname(TpcRoot)
DefaultAppRoot = os.path.join(RepoRoot, 'TransPyV', 'App')
DefaultAppRoot = os.path.join(RepoRoot, 'ViperC', 'App')
DefaultIncludesDir = os.path.join(RepoRoot, 'includes')
DefaultOutFile = os.path.join(RepoRoot, 'Debugger', 'ScanAstFieldMiss.txt')
+2 -2
View File
@@ -95,7 +95,7 @@ def resolve_paths(config: dict[str, Any], project_file: str) -> dict[str, Any]:
resolved_sources.append(resolve(src))
result['sources'] = [s for s in resolved_sources if os.path.isfile(s)]
# 直接使用 output_dir / temp_dir(与新版本 TPV 一致,不再支持 build_dir)
# 直接使用 output_dir / temp_dir(与新版本 VPC 一致,不再支持 build_dir)
result['temp_dir'] = resolve(config.get('temp_dir', './temp'))
result['output_dir'] = resolve(config.get('output_dir', './output'))
@@ -150,7 +150,7 @@ def main() -> None:
parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)')
parser.add_argument('--dwarf', action='store_true', help='函数级 DWARF 调试元数据:每模块一份 DICompileUnit/DIFile,每个 define 一份 DISubprogram 并挂 !dbg,函数入口第一条指令挂 DILocation。使 ASan/llvm-symbolizer 能把崩溃地址反查为 "<文件>.vp:函数起始行"(默认关闭;--asan 时自动开启)。开启时强制重译 App 模块(不复用不含 !llvm.dbg.cu 的旧 .ll/.obj),.ll/.obj 体积与编译时间略增')
parser.add_argument('--traceback', action='store_true', help='Ctrl+C 调用栈打印(仅 Windows/PE):链接完成后用 llvm-dwarfdump 把最终 exe 的 .debug_line/.debug_info 导出为 "<exe>.stackmap" 地址→源行旁文件(小端定长格式),供 exe 内运行时在 Ctrl+C/Ctrl+Break 时把 CRT 到当前的返回地址逐帧翻译为 "<文件>.vp:行/函数"。会自动开启 DWARF(同 --asan,否则行表为空);找不到 llvm-dwarfdump / 调用失败 / 解析不到任何行记录 / 目标不是 Windows PE 时直接报错终止编译')
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 viperc.exe)')
parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 <path>(默认为 hotspot_sites.txt);同时为每个函数定义加计时器,按自身耗时(self)降序写入同目录 hotspot_time.txt,行为 `<函数名> <self> <inc> <进入次数>`(单位 tick,x86_64 上为序列化 lfence; rdtsc);self 已扣除被插桩子调用的 inc,但不含未插桩被调方(间接调用、strcmp/memcpy 等无定义外部函数),它们计入调用者的 self')
parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat')
parser.add_argument('--hotspot-sites-all', action='store_true', help='--hotspot-sites 对全部直接 call 点插桩(忽略白名单;体积/开销大,用于深度排查)')
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1132 -1132
View File
File diff suppressed because it is too large. Load diff
+684 -684
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+299 -299
View File
@@ -1,300 +1,300 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import vast as ast
import sys
import llvmlite.ir as ir
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.VLogger import get_logger as _vlog
from lib.core.Handles.HandlesBase import BaseHandle
class AugAssignHandle(BaseHandle):
_AUGOP_MAP: dict[type, str] = {
ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__',
ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__',
ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__',
ast.LShift: '__ilshift__', ast.RShift: '__irshift__',
}
_AUGOP_FALLBACK: dict[type, str] = {
ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__',
ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__',
ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__',
ast.LShift: '__lshift__', ast.RShift: '__rshift__',
}
def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type:
"""解析 global 变量的 LLVM 类型:优先用模块级 AnnAssign 的注解类型。
问题场景:函数内 `_g_alloc_count += 1`(global 声明),模块级注解为
`_g_alloc_count: t.CSizeT`(i64)。若不查模块级注解类型,此处会用 RHS 字面量
类型(i32)自动建 i32 全局,与模块内其他读取/写入(i64)类型不匹配,
产生 "Operands must be the same type, got (i64, i32)" 类错误。
模块级 AnnAssign 已通过 _EmitGlobalAnnAssignLlvm 创建 LLVM 全局,
但注解无初始值(如 `_g_alloc_max: t.CSizeT = 0`)时 initializer 为空、
Gen.module.globals 中无该符号,故需在此兜底解析注解类型。
"""
module_ann: dict = getattr(Gen, '_module_ann_types', None)
if module_ann and VarName in module_ann:
ann_type: ir.Type = module_ann[VarName]
if ann_type is not None:
return ann_type
if isinstance(fallback, ir.IntType):
return fallback
return ir.IntType(64)
def _is_aug_unsigned(self, Node: ast.AugAssign, OldVal: ir.Value | None, Value: ir.Value) -> bool:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Node.target, ast.Name):
if Gen._check_node_unsigned(Node.target):
return True
elif isinstance(Node.target, ast.Attribute):
if Gen._check_node_unsigned(Node.target):
return True
elif isinstance(Node.target, ast.Subscript):
if Gen._check_node_unsigned(Node.target):
return True
if Node.value is not None and Gen._check_node_unsigned(Node.value):
return True
return False
def _HandleAugAssignLlvm(self, Node: ast.AugAssign) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
if (VarName not in Gen.variables
and VarName not in Gen.global_vars
and VarName not in Gen._reg_values
and VarName not in Gen._direct_values):
src_info: str = Gen._get_node_info()
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
sys.exit(1)
if Gen._IsVarConst(VarName):
_vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
sys.exit(1)
ClassName: str | None = Gen.var_struct_class.get(VarName)
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
if ClassName and iop_name:
iFuncName: str = f'{ClassName}.{iop_name}'
FuncName: str = f'{ClassName}.{self._AUGOP_FALLBACK.get(type(Node.op), "")}'
if iFuncName in Gen.functions or FuncName in Gen.functions:
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load()))
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
if OldVal and Value:
result: ir.Value | None
if iFuncName in Gen.functions:
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, iop_name, OldVal, Gen, Value)
else:
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, self._AUGOP_FALLBACK[type(Node.op)], OldVal, Gen, Value)
if result is not None:
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarPtr: ir.Value = Gen.variables[VarName]
if VarPtr.type.pointee == result.type:
Gen._store(result, VarPtr)
elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(result.type, ir.PointerType):
CastedVal: ir.Value = Gen.builder.bitcast(result, VarPtr.type.pointee, name=f"cast_{VarName}")
Gen._store(CastedVal, VarPtr)
else:
NewVar: ir.AllocaInstr = Gen._allocaEntry(result.type, name=VarName)
Gen._store(result, NewVar)
Gen.variables[VarName] = NewVar
else:
Gen._reg_values[VarName] = result
Gen.variables[VarName] = None
return
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
if not Value:
return
is_unsigned: bool = self._is_aug_unsigned(Node, None, Value)
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if VarName in Gen.global_vars:
# 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局
# (注解无初始值的全局变量),此处自动创建零初始化全局变量
GVar: ir.GlobalVariable
if VarName not in Gen.module.globals:
_gv_type: ir.Type = self._resolve_global_var_type(Gen, VarName, Value.type)
GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName)
GVar.initializer = ir.Constant(_gv_type, 0)
else:
GVar = Gen.module.globals[VarName]
Gen.variables[VarName] = GVar
OldVal: ir.Value = Gen._load(GVar, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, GVar)
return
if VarName in Gen._reg_values:
OldVal: ir.Value = Gen._reg_values[VarName]
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
del Gen._reg_values[VarName]
if VarName in Gen._direct_values:
OldVal: ir.Value = Gen._direct_values.pop(VarName)
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
if Gen.builder.block.is_terminated:
return
if VarName in Gen.variables and Gen.variables[VarName] is not None:
OldVal: ir.Value = Gen._load(Gen.variables[VarName], name=VarName)
store_ptr: ir.Value = Gen.variables[VarName]
if isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Value.type.width != 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset")
NewVal: ir.Value
if Op == '-' or Op == '+':
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add")
else:
ptr_val: ir.Value = OldVal
OldVal = Gen._load(OldVal, name="deref_ptr_for_op")
orig_type: ir.Type = OldVal.type
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
if NewVal.type.width > orig_type.width:
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
store_ptr = ptr_val
else:
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Op in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_aug")
NewVal: ir.Value
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_aug")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_aug")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
Gen._store(NewVal, store_ptr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(store_ptr.type, ir.PointerType):
TargetType: ir.Type = store_ptr.type.pointee
if NewVal.type != TargetType:
Coerced: ir.Value | None = Gen._coerce_value(NewVal, TargetType)
if Coerced is not None:
NewVal = Coerced
Gen._store(NewVal, store_ptr)
elif VarName == 'pos':
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen.variables[VarName] = var
OldVal: ir.Value = Gen._load(var, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, var)
elif isinstance(Node.target, ast.Attribute):
AttrName: str = Node.target.attr
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.target)
if OldVal:
def _deref_if_ptr(val: ir.Value) -> ir.Value:
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, (ir.IntType, ir.FloatType, ir.DoubleType)):
return Gen._load(val, name="deref_aug")
return val
OldVal = _deref_if_ptr(OldVal)
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
elif isinstance(Node.target, ast.Subscript):
SubTarget: ast.Subscript = Node.target
ElemPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(SubTarget)
if ElemPtr:
if isinstance(ElemPtr.type, ir.PointerType):
OldVal: ir.Value = Gen._load(ElemPtr, name="old_subscript_val")
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_sub")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_sub")
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Op in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_sub")
NewVal: ir.Value
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_sub")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_sub")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
Gen._store(NewVal, ElemPtr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
if NewVal.type.width > ElemPtr.type.pointee.width:
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
Gen._store(NewVal, ElemPtr)
elif isinstance(Node.target, ast.UnaryOp):
# 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref)
# (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。
# 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
if not isinstance(Node.target.op, ast.Deref):
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
f"{Gen._get_node_info()}"
)
DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand)
if DerefPtr is None:
raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}")
if not isinstance(DerefPtr.type, ir.PointerType):
raise Exception(
f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})"
f"{Gen._get_node_info()}"
)
DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val")
if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if DerefOld.type.width < Value.type.width:
DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref")
elif DerefOld.type.width > Value.type.width:
Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref")
elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if DerefOp in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref")
OffVal: ir.Value
if DerefOp == '-':
OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref")
else:
OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref")
Gen._store(OffVal, DerefPtr)
return
DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType):
if DerefNew.type.width > DerefPtr.type.pointee.width:
DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result")
Gen._store(DerefNew, DerefPtr)
else:
# fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 TransPyV
# translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、
# 变量静默不变"。
TargetOp: str | None = None
if isinstance(Node.target, ast.UnaryOp):
TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用"
f"(当前 {type(Node.target).__name__}"
+ (f" op='{TargetOp}'" if TargetOp else "")
+ f"){Gen._get_node_info()}"
from __future__ import annotations
from typing import TYPE_CHECKING
import vast as ast
import sys
import llvmlite.ir as ir
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.VLogger import get_logger as _vlog
from lib.core.Handles.HandlesBase import BaseHandle
class AugAssignHandle(BaseHandle):
_AUGOP_MAP: dict[type, str] = {
ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__',
ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__',
ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__',
ast.LShift: '__ilshift__', ast.RShift: '__irshift__',
}
_AUGOP_FALLBACK: dict[type, str] = {
ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__',
ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__',
ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__',
ast.LShift: '__lshift__', ast.RShift: '__rshift__',
}
def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type:
"""解析 global 变量的 LLVM 类型:优先用模块级 AnnAssign 的注解类型。
问题场景:函数内 `_g_alloc_count += 1`(global 声明),模块级注解为
`_g_alloc_count: t.CSizeT`(i64)。若不查模块级注解类型,此处会用 RHS 字面量
类型(i32)自动建 i32 全局,与模块内其他读取/写入(i64)类型不匹配,
产生 "Operands must be the same type, got (i64, i32)" 类错误。
模块级 AnnAssign 已通过 _EmitGlobalAnnAssignLlvm 创建 LLVM 全局,
但注解无初始值(如 `_g_alloc_max: t.CSizeT = 0`)时 initializer 为空、
Gen.module.globals 中无该符号,故需在此兜底解析注解类型。
"""
module_ann: dict = getattr(Gen, '_module_ann_types', None)
if module_ann and VarName in module_ann:
ann_type: ir.Type = module_ann[VarName]
if ann_type is not None:
return ann_type
if isinstance(fallback, ir.IntType):
return fallback
return ir.IntType(64)
def _is_aug_unsigned(self, Node: ast.AugAssign, OldVal: ir.Value | None, Value: ir.Value) -> bool:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Node.target, ast.Name):
if Gen._check_node_unsigned(Node.target):
return True
elif isinstance(Node.target, ast.Attribute):
if Gen._check_node_unsigned(Node.target):
return True
elif isinstance(Node.target, ast.Subscript):
if Gen._check_node_unsigned(Node.target):
return True
if Node.value is not None and Gen._check_node_unsigned(Node.value):
return True
return False
def _HandleAugAssignLlvm(self, Node: ast.AugAssign) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
if (VarName not in Gen.variables
and VarName not in Gen.global_vars
and VarName not in Gen._reg_values
and VarName not in Gen._direct_values):
src_info: str = Gen._get_node_info()
_vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}")
sys.exit(1)
if Gen._IsVarConst(VarName):
_vlog().error(Gen._ConstAssignMessage("增量赋值", VarName))
sys.exit(1)
ClassName: str | None = Gen.var_struct_class.get(VarName)
iop_name: str | None = self._AUGOP_MAP.get(type(Node.op))
if ClassName and iop_name:
iFuncName: str = f'{ClassName}.{iop_name}'
FuncName: str = f'{ClassName}.{self._AUGOP_FALLBACK.get(type(Node.op), "")}'
if iFuncName in Gen.functions or FuncName in Gen.functions:
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load()))
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
if OldVal and Value:
result: ir.Value | None
if iFuncName in Gen.functions:
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, iop_name, OldVal, Gen, Value)
else:
result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, self._AUGOP_FALLBACK[type(Node.op)], OldVal, Gen, Value)
if result is not None:
if VarName in Gen.variables and Gen.variables[VarName] is not None:
VarPtr: ir.Value = Gen.variables[VarName]
if VarPtr.type.pointee == result.type:
Gen._store(result, VarPtr)
elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(result.type, ir.PointerType):
CastedVal: ir.Value = Gen.builder.bitcast(result, VarPtr.type.pointee, name=f"cast_{VarName}")
Gen._store(CastedVal, VarPtr)
else:
NewVar: ir.AllocaInstr = Gen._allocaEntry(result.type, name=VarName)
Gen._store(result, NewVar)
Gen.variables[VarName] = NewVar
else:
Gen._reg_values[VarName] = result
Gen.variables[VarName] = None
return
Value: ir.Value | None = self.HandleExprLlvm(Node.value)
if not Value:
return
is_unsigned: bool = self._is_aug_unsigned(Node, None, Value)
if isinstance(Node.target, ast.Name):
VarName: str = Node.target.id
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if VarName in Gen.global_vars:
# 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局
# (注解无初始值的全局变量),此处自动创建零初始化全局变量
GVar: ir.GlobalVariable
if VarName not in Gen.module.globals:
_gv_type: ir.Type = self._resolve_global_var_type(Gen, VarName, Value.type)
GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName)
GVar.initializer = ir.Constant(_gv_type, 0)
else:
GVar = Gen.module.globals[VarName]
Gen.variables[VarName] = GVar
OldVal: ir.Value = Gen._load(GVar, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, GVar)
return
if VarName in Gen._reg_values:
OldVal: ir.Value = Gen._reg_values[VarName]
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
del Gen._reg_values[VarName]
if VarName in Gen._direct_values:
OldVal: ir.Value = Gen._direct_values.pop(VarName)
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName)
Gen._store(OldVal, var)
Gen.variables[VarName] = var
if Gen.builder.block.is_terminated:
return
if VarName in Gen.variables and Gen.variables[VarName] is not None:
OldVal: ir.Value = Gen._load(Gen.variables[VarName], name=VarName)
store_ptr: ir.Value = Gen.variables[VarName]
if isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Value.type.width != 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset")
NewVal: ir.Value
if Op == '-' or Op == '+':
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add")
else:
ptr_val: ir.Value = OldVal
OldVal = Gen._load(OldVal, name="deref_ptr_for_op")
orig_type: ir.Type = OldVal.type
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType):
if NewVal.type.width > orig_type.width:
NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result")
store_ptr = ptr_val
else:
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right")
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Op in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_aug")
NewVal: ir.Value
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_aug")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_aug")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug")
Gen._store(NewVal, store_ptr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(store_ptr.type, ir.PointerType):
TargetType: ir.Type = store_ptr.type.pointee
if NewVal.type != TargetType:
Coerced: ir.Value | None = Gen._coerce_value(NewVal, TargetType)
if Coerced is not None:
NewVal = Coerced
Gen._store(NewVal, store_ptr)
elif VarName == 'pos':
var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName)
Gen.variables[VarName] = var
OldVal: ir.Value = Gen._load(var, name=VarName)
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
Gen._store(NewVal, var)
elif isinstance(Node.target, ast.Attribute):
AttrName: str = Node.target.attr
OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.target)
if OldVal:
def _deref_if_ptr(val: ir.Value) -> ir.Value:
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, (ir.IntType, ir.FloatType, ir.DoubleType)):
return Gen._load(val, name="deref_aug")
return val
OldVal = _deref_if_ptr(OldVal)
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr")
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal)
elif isinstance(Node.target, ast.Subscript):
SubTarget: ast.Subscript = Node.target
ElemPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(SubTarget)
if ElemPtr:
if isinstance(ElemPtr.type, ir.PointerType):
OldVal: ir.Value = Gen._load(ElemPtr, name="old_subscript_val")
Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if OldVal.type.width < Value.type.width:
OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_sub")
elif OldVal.type.width > Value.type.width:
Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_sub")
elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if Op in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_sub")
NewVal: ir.Value
if Op == '-':
NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_sub")
NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_sub")
else:
NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub")
Gen._store(NewVal, ElemPtr)
return
NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
if NewVal.type.width > ElemPtr.type.pointee.width:
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
Gen._store(NewVal, ElemPtr)
elif isinstance(Node.target, ast.UnaryOp):
# 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref)
# (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。
# 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
if not isinstance(Node.target.op, ast.Deref):
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
f"{Gen._get_node_info()}"
)
DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand)
if DerefPtr is None:
raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}")
if not isinstance(DerefPtr.type, ir.PointerType):
raise Exception(
f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})"
f"{Gen._get_node_info()}"
)
DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val")
if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if DerefOld.type.width < Value.type.width:
DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref")
elif DerefOld.type.width > Value.type.width:
Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref")
elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if DerefOp in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref")
OffVal: ir.Value
if DerefOp == '-':
OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref")
else:
OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref")
Gen._store(OffVal, DerefPtr)
return
DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType):
if DerefNew.type.width > DerefPtr.type.pointee.width:
DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result")
Gen._store(DerefNew, DerefPtr)
else:
# fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 ViperC
# translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、
# 变量静默不变"。
TargetOp: str | None = None
if isinstance(Node.target, ast.UnaryOp):
TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用"
f"(当前 {type(Node.target).__name__}"
+ (f" op='{TargetOp}'" if TargetOp else "")
+ f"){Gen._get_node_info()}"
)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+279 -279
View File
@@ -1,280 +1,280 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import re
import llvmlite.ir as ir
# ---------------------------------------------------------------
# `_parse_format_spec` 的格式说明符解析正则**预编译**(性能:避免每次
# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。
# 语义与逐字写法完全一致,模式串一字未改)。
# ---------------------------------------------------------------
# Python 格式说明符 `[[fill]align][sign][#][0][width][,][.precision][type]`
_RE_FORMAT_SPEC: re.Pattern[str] = re.compile(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$')
class ExprFormatHandle(BaseHandle):
def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if not Gen or not Gen.builder:
return ir.Constant(ir.IntType(8).as_pointer(), None)
format_str: str = ""
cast_args: list[Any] = []
for part in Node.values:
if isinstance(part, ast.Constant) and isinstance(part.value, str):
format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0')
elif isinstance(part, ast.FormattedValue):
fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen)
if fmt_class and Gen._has_function(f'{fmt_class}.__str__'):
obj_val: Any = self.HandleExprLlvm(part.value)
if obj_val:
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
if fmt_class in Gen.structs:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}")
val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__")
Gen._RegisterTempPtr(val)
format_str += "%s"
cast_args.append(val)
else:
format_str += "%s"
cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None))
continue
val = self.HandleExprLlvm(part.value)
if not val:
format_str += "%d"
cast_args.append(ir.Constant(ir.IntType(32), 0))
continue
# 检查 format_spec(Python f-string 格式说明符)
if part.format_spec is not None:
c_fmt: str | None = self._parse_format_spec(part.format_spec, val)
if c_fmt is not None:
cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value)
format_str += c_fmt
cast_args.append(cast_val)
continue
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, ir.IntType) and pointee.width == 8:
format_str += "%s"
cast_args.append(val)
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int")
trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc")
format_str += "%d"
cast_args.append(trunc_val)
else:
Loaded: Any = Gen._load(val, name="fstr_deref")
if isinstance(Loaded.type, ir.IntType):
if Loaded.type.width == 8:
format_str += "%c"
ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int")
cast_args.append(ext)
else:
format_str += "%d"
cast_args.append(Loaded)
elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)):
format_str += "%f"
cast_args.append(Loaded)
elif isinstance(Loaded.type, ir.PointerType):
format_str += "%s"
cast_args.append(Loaded)
else:
format_str += "%d"
cast_args.append(Loaded)
elif isinstance(val.type, ir.IntType):
if val.type.width == 8:
format_str += "%c"
ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int")
cast_args.append(ext)
elif val.type.width == 1:
zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int")
format_str += "%d"
cast_args.append(zext)
else:
is_u: bool = Gen._check_node_unsigned(part.value)
format_str += "%u" if is_u else "%d"
cast_args.append(val)
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
format_str += "%f"
cast_args.append(val)
else:
format_str += "%d"
cast_args.append(val)
if not format_str:
return Gen.emit_constant("", 'string')
format_ptr: Any = Gen._create_string_global(format_str)
# 统一使用 snprintf 生成字符串,返回 i8*
# __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放)
in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__'))
snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [
ir.PointerType(ir.IntType(8)),
ir.IntType(64),
ir.PointerType(ir.IntType(8))
], var_arg=True)
snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type)
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
# 第一遍:获取所需长度(NULL 缓冲,size=0)
len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len")
# 加 1 用于 null 终止符
size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size")
size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext")
if in_str_method:
# 堆分配(__str__ 返回值需跨函数生命周期)
malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)])
malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type)
buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf")
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
Gen._RegisterTempPtr(buf_ptr)
return buf_ptr
# 栈分配(局部使用,自动释放)
buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val)
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
return buf_ptr
def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None:
"""将 Python f-string format_spec 转换为 C printf 格式说明符。
支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。
返回 None 表示无法解析,调用方应回退到类型推断。
"""
if isinstance(format_spec_node, ast.JoinedStr):
parts: list[str] = []
for v in format_spec_node.values:
if isinstance(v, ast.Constant) and isinstance(v.value, str):
parts.append(v.value)
else:
return None
spec_str: str = ''.join(parts)
elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str):
spec_str = format_spec_node.value
else:
return None
if not spec_str:
return None
m: Any = _RE_FORMAT_SPEC.match(spec_str)
if not m:
return None
fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups()
c_flags: str = ''
if sign == '+':
c_flags += '+'
elif sign == ' ':
c_flags += ' '
if alt:
c_flags += '#'
if fill_align:
align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0]
if align_char in ('<', '^'):
c_flags += '-'
if zero:
c_flags += '0'
c_width: str = width or ''
c_precision: str = precision or ''
length_mod: str = ''
if isinstance(val.type, ir.IntType):
# 32 位**不写**长度修饰符(N14):与 TPV 侧 ExprGenFstr 保持同步。
# 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。
if val.type.width == 64:
length_mod = 'll'
elif val.type.width == 16:
length_mod = 'h'
elif val.type.width == 8:
length_mod = 'hh'
c_specifier: str = ''
if fmt_type in ('d', 'n'):
c_specifier = f'{length_mod}d'
elif fmt_type == 'x':
c_specifier = f'{length_mod}x'
elif fmt_type == 'X':
c_specifier = f'{length_mod}X'
elif fmt_type == 'o':
c_specifier = f'{length_mod}o'
elif fmt_type == 'b':
return None
elif fmt_type == 'c':
c_specifier = 'c'
elif fmt_type in ('f', 'F'):
c_specifier = 'f'
elif fmt_type in ('e', 'E', 'g', 'G'):
c_specifier = fmt_type
elif fmt_type == 's':
c_specifier = 's'
elif fmt_type == '%':
c_specifier = 'f%%'
else:
if isinstance(val.type, ir.IntType):
if val.type.width == 8:
c_specifier = 'c'
elif val.type.width == 1:
c_specifier = 'd'
else:
c_specifier = f'{length_mod}d'
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
c_specifier = 'f'
elif isinstance(val.type, ir.PointerType):
c_specifier = 's'
else:
c_specifier = 'd'
return f'%{c_flags}{c_width}{c_precision}{c_specifier}'
def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value:
"""根据 C printf 格式说明符转换值类型。"""
fmt_type_char: str = ''
for ch in reversed(c_fmt):
if ch.isalpha() and ch not in ('l', 'h'):
fmt_type_char = ch
break
elif ch == '%':
break
if fmt_type_char == 's':
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, ir.IntType) and pointee.width == 8:
return val
if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8:
return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr")
return val
elif fmt_type_char == 'c':
if isinstance(val.type, ir.IntType) and val.type.width < 32:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c")
return val
elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'):
if isinstance(val.type, ir.IntType):
if val.type.width == 8:
is_u: bool = Gen._check_node_unsigned(arg_node)
if is_u:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8")
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8")
elif val.type.width == 16:
is_u = Gen._check_node_unsigned(arg_node)
if is_u:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16")
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16")
elif val.type.width == 1:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool")
return val
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i")
return val
elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'):
if isinstance(val.type, ir.FloatType):
return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext")
elif isinstance(val.type, ir.IntType):
return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f")
return val
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import re
import llvmlite.ir as ir
# ---------------------------------------------------------------
# `_parse_format_spec` 的格式说明符解析正则**预编译**(性能:避免每次
# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。
# 语义与逐字写法完全一致,模式串一字未改)。
# ---------------------------------------------------------------
# Python 格式说明符 `[[fill]align][sign][#][0][width][,][.precision][type]`
_RE_FORMAT_SPEC: re.Pattern[str] = re.compile(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$')
class ExprFormatHandle(BaseHandle):
def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if not Gen or not Gen.builder:
return ir.Constant(ir.IntType(8).as_pointer(), None)
format_str: str = ""
cast_args: list[Any] = []
for part in Node.values:
if isinstance(part, ast.Constant) and isinstance(part.value, str):
format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0')
elif isinstance(part, ast.FormattedValue):
fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen)
if fmt_class and Gen._has_function(f'{fmt_class}.__str__'):
obj_val: Any = self.HandleExprLlvm(part.value)
if obj_val:
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
if fmt_class in Gen.structs:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}")
val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__")
Gen._RegisterTempPtr(val)
format_str += "%s"
cast_args.append(val)
else:
format_str += "%s"
cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None))
continue
val = self.HandleExprLlvm(part.value)
if not val:
format_str += "%d"
cast_args.append(ir.Constant(ir.IntType(32), 0))
continue
# 检查 format_spec(Python f-string 格式说明符)
if part.format_spec is not None:
c_fmt: str | None = self._parse_format_spec(part.format_spec, val)
if c_fmt is not None:
cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value)
format_str += c_fmt
cast_args.append(cast_val)
continue
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, ir.IntType) and pointee.width == 8:
format_str += "%s"
cast_args.append(val)
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int")
trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc")
format_str += "%d"
cast_args.append(trunc_val)
else:
Loaded: Any = Gen._load(val, name="fstr_deref")
if isinstance(Loaded.type, ir.IntType):
if Loaded.type.width == 8:
format_str += "%c"
ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int")
cast_args.append(ext)
else:
format_str += "%d"
cast_args.append(Loaded)
elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)):
format_str += "%f"
cast_args.append(Loaded)
elif isinstance(Loaded.type, ir.PointerType):
format_str += "%s"
cast_args.append(Loaded)
else:
format_str += "%d"
cast_args.append(Loaded)
elif isinstance(val.type, ir.IntType):
if val.type.width == 8:
format_str += "%c"
ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int")
cast_args.append(ext)
elif val.type.width == 1:
zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int")
format_str += "%d"
cast_args.append(zext)
else:
is_u: bool = Gen._check_node_unsigned(part.value)
format_str += "%u" if is_u else "%d"
cast_args.append(val)
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
format_str += "%f"
cast_args.append(val)
else:
format_str += "%d"
cast_args.append(val)
if not format_str:
return Gen.emit_constant("", 'string')
format_ptr: Any = Gen._create_string_global(format_str)
# 统一使用 snprintf 生成字符串,返回 i8*
# __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放)
in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__'))
snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [
ir.PointerType(ir.IntType(8)),
ir.IntType(64),
ir.PointerType(ir.IntType(8))
], var_arg=True)
snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type)
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
# 第一遍:获取所需长度(NULL 缓冲,size=0)
len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len")
# 加 1 用于 null 终止符
size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size")
size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext")
if in_str_method:
# 堆分配(__str__ 返回值需跨函数生命周期)
malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)])
malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type)
buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf")
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
Gen._RegisterTempPtr(buf_ptr)
return buf_ptr
# 栈分配(局部使用,自动释放)
buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val)
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
return buf_ptr
def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None:
"""将 Python f-string format_spec 转换为 C printf 格式说明符。
支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。
返回 None 表示无法解析,调用方应回退到类型推断。
"""
if isinstance(format_spec_node, ast.JoinedStr):
parts: list[str] = []
for v in format_spec_node.values:
if isinstance(v, ast.Constant) and isinstance(v.value, str):
parts.append(v.value)
else:
return None
spec_str: str = ''.join(parts)
elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str):
spec_str = format_spec_node.value
else:
return None
if not spec_str:
return None
m: Any = _RE_FORMAT_SPEC.match(spec_str)
if not m:
return None
fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups()
c_flags: str = ''
if sign == '+':
c_flags += '+'
elif sign == ' ':
c_flags += ' '
if alt:
c_flags += '#'
if fill_align:
align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0]
if align_char in ('<', '^'):
c_flags += '-'
if zero:
c_flags += '0'
c_width: str = width or ''
c_precision: str = precision or ''
length_mod: str = ''
if isinstance(val.type, ir.IntType):
# 32 位**不写**长度修饰符(N14):与 VPC 侧 ExprGenFstr 保持同步。
# 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。
if val.type.width == 64:
length_mod = 'll'
elif val.type.width == 16:
length_mod = 'h'
elif val.type.width == 8:
length_mod = 'hh'
c_specifier: str = ''
if fmt_type in ('d', 'n'):
c_specifier = f'{length_mod}d'
elif fmt_type == 'x':
c_specifier = f'{length_mod}x'
elif fmt_type == 'X':
c_specifier = f'{length_mod}X'
elif fmt_type == 'o':
c_specifier = f'{length_mod}o'
elif fmt_type == 'b':
return None
elif fmt_type == 'c':
c_specifier = 'c'
elif fmt_type in ('f', 'F'):
c_specifier = 'f'
elif fmt_type in ('e', 'E', 'g', 'G'):
c_specifier = fmt_type
elif fmt_type == 's':
c_specifier = 's'
elif fmt_type == '%':
c_specifier = 'f%%'
else:
if isinstance(val.type, ir.IntType):
if val.type.width == 8:
c_specifier = 'c'
elif val.type.width == 1:
c_specifier = 'd'
else:
c_specifier = f'{length_mod}d'
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
c_specifier = 'f'
elif isinstance(val.type, ir.PointerType):
c_specifier = 's'
else:
c_specifier = 'd'
return f'%{c_flags}{c_width}{c_precision}{c_specifier}'
def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value:
"""根据 C printf 格式说明符转换值类型。"""
fmt_type_char: str = ''
for ch in reversed(c_fmt):
if ch.isalpha() and ch not in ('l', 'h'):
fmt_type_char = ch
break
elif ch == '%':
break
if fmt_type_char == 's':
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, ir.IntType) and pointee.width == 8:
return val
if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8:
return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr")
return val
elif fmt_type_char == 'c':
if isinstance(val.type, ir.IntType) and val.type.width < 32:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c")
return val
elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'):
if isinstance(val.type, ir.IntType):
if val.type.width == 8:
is_u: bool = Gen._check_node_unsigned(arg_node)
if is_u:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8")
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8")
elif val.type.width == 16:
is_u = Gen._check_node_unsigned(arg_node)
if is_u:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16")
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16")
elif val.type.width == 1:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool")
return val
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i")
return val
elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'):
if isinstance(val.type, ir.FloatType):
return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext")
elif isinstance(val.type, ir.IntType):
return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f")
return val
return val
File diff suppressed because it is too large. Load diff
+442 -442
View File
@@ -1,443 +1,443 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
import vast as ast
import llvmlite.ir as ir
from lib.core.SymbolUtils import IsTModule
class ExprUtils:
def __init__(self, translator: "Translator") -> None:
self.Trans: Translator = translator
def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None:
OpMap: dict[type, str] = {
ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/',
ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&',
ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>',
ast.MatMult: '@',
}
return OpMap.get(type(Op), None)
def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None:
OpMap: dict[type, str] = {
ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-',
ast.Deref: '*', ast.AddrOf: '&',
}
return OpMap.get(type(Op), None)
def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None:
OpMap: dict[type, str] = {
ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=',
ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not',
ast.In: 'in', ast.NotIn: 'not in',
}
return OpMap.get(type(Op), None)
def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None:
if isinstance(node, ast.Name):
VarName: str = node.id
return Gen.var_struct_class.get(VarName)
if isinstance(node, ast.Attribute):
AttrName: str = node.attr
# 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable)
# 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派
# (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST')
BaseClass: str | None = self._get_var_class(node.value, Gen)
if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self':
BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None)
if not BaseClass:
# 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory):
# var_struct_class 只登记**当前模块**变量,跨模块量不在其中 → 递归到此
# 返回 None → 整条链 `Mod.Var.Field` 解析为空 → 赋值/调用 fail-fast
# ("属性访问未能解析出目标类型/成员",StructGenMixin.vp:139 根因)。
BaseClass = self._resolve_module_var_class(Gen, node.value, AttrName)
if BaseClass:
# 对齐 _HandleLenLlvm L3445-3454:class_member_element_class /
# class_members 的键是裸类名,而 var_struct_class 可能带 sha1 前缀
# (如 'd81b22c853de40a2.Call')。直查会 miss → 容器字段
# (list[...] / rawptr[list[...]],如 ast.Call.args)方法分派失败
# → "Undefined method 'get' in class 'rawptr'"。剥前缀 + MRO 链查
# element_class(list 特化名,如 list[...ASTptr])优先。
_bare: str = BaseClass
if len(_bare) > 17 and _bare[16] == '.':
_bare = _bare[17:]
element_class_map = getattr(Gen, 'class_member_element_class', {})
try:
_mro_chain: list = Gen._get_mro_chain(_bare)
except Exception:
_mro_chain = [_bare]
for _mro_cls in _mro_chain:
_mk: str = _mro_cls
if len(_mk) > 17 and _mk[16] == '.':
_mk = _mk[17:]
_sub = element_class_map.get(_mk)
if _sub and AttrName in _sub:
return _sub[AttrName]
if _bare in Gen.class_members:
for m_name, m_type in Gen.class_members[_bare]:
if m_name == AttrName:
if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
found = Gen.find_struct_by_pointee(m_type.pointee)
if found:
return found[0]
break
# Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后,
# 避免字段名和变量名冲突导致错误分派)
cls: str | None = Gen.var_struct_class.get(AttrName)
if cls:
return cls
return None
return None
def _resolve_module_var_class(self, Gen: LlvmCodeGenerator, ModNode: ast.AST,
AttrName: str) -> str | None:
"""`模块别名.模块级变量` 的类名解析(如 CompilerFactory.factory)
背景:`CompilerFactory.factory.PermPool` 这类链的第一跳既不在
`var_struct_class`(只含当前模块变量)也不在 `global_struct_class`
(只含按变量名登记的项),于是 `_get_var_class` 递归到底返回 None →
整条链解析为空 → 赋值/调用 fail-fast("属性访问未能解析出目标类型/成员")。
这里按模块别名把 `Mod.Var` 落到其 LLVM 全局量,再从全局量类型反推结构体
类名;候选键形态对齐 `_lookup_module_global`(裸名 / {mod}.{var} /
{sha1}.{var})。仅当 ModNode 确为 import 模块(含别名)时生效。
"""
if not isinstance(ModNode, ast.Name):
return None
Mod: str = ModNode.id
imported: Any = getattr(self.Trans, '_ImportedModules', None) or []
sha1_map: Any = getattr(Gen, 'ModuleSha1Map', {}) or {}
try:
resolved_mod: str = self.Trans.SymbolTable.resolve_alias(Mod)
except Exception:
resolved_mod = Mod
aliases: Any = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {}
_is_mod: bool = (Mod in imported or resolved_mod in imported
or Mod in sha1_map or resolved_mod in sha1_map
or Mod in aliases or resolved_mod in aliases)
if not _is_mod:
# 兜底:ModuleSha1Map 的键多为全路径(lib.core.CompilerFactory),
# 用后缀扫描判定「Mod 是否确实是某个已导入模块(含别名)」。
for _k in sha1_map:
if _k == Mod or _k.endswith(f'.{Mod}'):
_is_mod = True
break
if not _is_mod:
return None
# 1) 变量名侧已有登记(stub 解析时跨模块量常以**裸变量名**登记)
gsc: Any = getattr(Gen, 'global_struct_class', {}) or {}
vsc: Any = getattr(Gen, 'var_struct_class', {}) or {}
for key in (f"{Mod}.{AttrName}", f"{resolved_mod}.{AttrName}", AttrName):
if key in gsc:
return gsc[key]
if key in vsc:
return vsc[key]
# 2) 借属性侧既有解析器(覆盖 module.globals/variables 与前缀形态)
try:
_via_attr: str | None = self.Trans.ExprAttrHandle._resolve_var_classname(Gen, AttrName)
except Exception:
_via_attr = None
if _via_attr:
return _via_attr
# 3) 从 LLVM 全局量类型反推结构体类名
cands: list[str] = [f"{Mod}.{AttrName}", AttrName]
for m in imported:
if m == Mod or m.endswith(f".{Mod}"):
k: str = f"{m}.{AttrName}"
if k not in cands:
cands.append(k)
for cand in (Mod, resolved_mod):
sha1: str | None = sha1_map.get(cand)
if sha1:
k2: str = f"{sha1}.{AttrName}"
if k2 not in cands:
cands.append(k2)
globals_map: Any = getattr(getattr(Gen, 'module', None), 'globals', {}) or {}
# 3b) 后缀扫描:全局量键常为 "{sha1}.{var}",sha1 不一定能从 Mod 反查到
for _gk in globals_map:
if _gk.endswith(f".{AttrName}") and _gk not in cands:
cands.append(_gk)
for key in cands:
gv: Any = globals_map.get(key)
if gv is None:
continue
ty: Any = getattr(gv, 'type', None)
if not isinstance(ty, ir.PointerType):
continue
pointee: Any = ty.pointee
# 全局量本身是指针变量(值类型 X*,LLVM 全局类型 X**)→ 再剥一层
if isinstance(pointee, ir.PointerType):
pointee = pointee.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
found: Any = Gen.find_struct_by_pointee(pointee)
if found:
return found[0]
return None
def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None:
FullMethodName: str = f'{ClassName}.{op_name}'
if not Gen._has_function(FullMethodName):
FullMethodName = f'{ClassName}.{op_name}__'
if not Gen._has_function(FullMethodName):
# 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 TPV 一致)
for anc in Gen._get_mro_chain(ClassName)[1:]:
anc_method: str = f'{anc}.{op_name}'
if Gen._has_function(anc_method):
FullMethodName = anc_method
break
anc_method_ss = f'{anc}.{op_name}__'
if Gen._has_function(anc_method_ss):
FullMethodName = anc_method_ss
break
if not Gen._has_function(FullMethodName):
# 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且
# 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本
# (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时,
# var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。
# 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。
if '[' not in ClassName:
SpecClass: str | None = None
# 先收集所有候选特化(有方法 declare 或 define 的)
best_candidates: list[str] = []
for CN in Gen.structs.keys():
if CN.startswith(f'{ClassName}[') and (Gen._has_function(f'{CN}.{op_name}') or Gen._has_function(f'{CN}.{op_name}__')):
best_candidates.append(CN)
# 优先选已编译方法体(define)的特化:dict 插入序选第一个会命中
# 只有 declare 无 define 的错误特化(如 list[AST]),生成 undefined
# symbol(ld.lld undefined symbol: list[AST].__len__ 根因)。
for CN in best_candidates:
_fn: str = f'{CN}.{op_name}' if Gen._has_function(f'{CN}.{op_name}') else f'{CN}.{op_name}__'
_f: Any = Gen._get_function(_fn)
if _f is not None and getattr(_f, 'blocks', []):
SpecClass = CN
break
if SpecClass is None and best_candidates:
# 全部只有 declare:按需补发方法体(幂等,_specialize_generic_class
# 缓存机制会从 declare_only 升级为完整特化),补发后重新检查
try:
self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen)
except Exception:
pass
_fn2: str = f'{best_candidates[0]}.{op_name}' if Gen._has_function(f'{best_candidates[0]}.{op_name}') else f'{best_candidates[0]}.{op_name}__'
_f2: Any = Gen._get_function(_fn2)
if _f2 is not None and getattr(_f2, 'blocks', []):
SpecClass = best_candidates[0]
if SpecClass:
FullMethodName = f'{SpecClass}.{op_name}'
if not Gen._has_function(FullMethodName):
FullMethodName = f'{SpecClass}.{op_name}__'
if Gen._has_function(FullMethodName):
func: Any = Gen._get_function(FullMethodName)
# 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时,
# 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。
method_anc: str | None = None
_fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName
if '.' in FullMethodName:
_fm_cls: str = FullMethodName.split('.')[0]
if _fm_cls != ClassName:
method_anc = _fm_cls
_call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc)
call_args: list[Any] = [_call_self]
if other_val is not None:
call_args.append(other_val)
if len(func.ftype.args) == len(call_args):
for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)):
if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType):
if expected_type != actual_val.type:
if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}")
result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}")
return result
return None
def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]:
info: dict[str, Any] = {
'name': "unknown",
'size': 0,
'signed': False,
'ptr': False
}
if isinstance(llvm_type, ir.IntType):
info['size'] = llvm_type.width // 8
info['signed'] = True
if llvm_type.width == 8:
info['name'] = "char"
elif llvm_type.width == 16:
info['name'] = "short"
elif llvm_type.width == 32:
info['name'] = "int"
elif llvm_type.width == 64:
info['name'] = "long long"
else:
info['name'] = f"int{llvm_type.width}"
elif isinstance(llvm_type, ir.DoubleType):
info['size'] = 8
info['signed'] = True
info['name'] = "double"
elif isinstance(llvm_type, ir.FloatType):
info['size'] = 4
info['signed'] = True
info['name'] = "float"
elif isinstance(llvm_type, ir.PointerType):
info['size'] = 8
info['signed'] = False
info['ptr'] = True
pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee)
info['name'] = f"{pointee_info['name']}*"
elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)):
size: int = 0
for elem in llvm_type.elements:
elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem)
size += elem_info['size']
info['size'] = size
info['signed'] = False
if isinstance(llvm_type, ir.IdentifiedStructType):
info['name'] = llvm_type.name
else:
info['name'] = "struct"
elif isinstance(llvm_type, ir.ArrayType):
elem_info = self._llvm_type_to_detailed_string(llvm_type.element)
info['size'] = elem_info['size'] * llvm_type.count
info['signed'] = elem_info['signed']
info['name'] = f"{elem_info['name']}[]"
elif isinstance(llvm_type, ir.VoidType):
info['size'] = 0
info['signed'] = False
info['name'] = "void"
else:
info['size'] = 0
info['signed'] = False
info['name'] = str(llvm_type)
return info
def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if isinstance(node, ast.Constant):
if isinstance(node.value, bool):
return ir.IntType(32)
elif isinstance(node.value, int):
return ir.IntType(32)
elif isinstance(node.value, float):
return ir.DoubleType()
elif isinstance(node.value, str):
if getattr(node, 'kind', None) == 'u':
return ir.PointerType(ir.IntType(16))
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
elif isinstance(node, ast.Name):
if node.id in Gen.variables:
var_ptr: Any = Gen.variables[node.id]
if isinstance(var_ptr.type, ir.PointerType):
return var_ptr.type.pointee
return var_ptr.type
if node.id in Gen._reg_values:
return Gen._reg_values[node.id].type
return ir.IntType(32)
elif isinstance(node, ast.Attribute):
attr_name: str = node.attr
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
# 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型)
return self.Trans.LlvmGen._enum_int_type(
getattr(AttrInfo, 'EnumName', None), AttrInfo)
return ir.IntType(32)
elif isinstance(node, ast.BinOp):
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
right_type: ir.Type = self._infer_expr_llvm_type_full(node.right)
if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)):
return ir.DoubleType()
if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType):
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
elif isinstance(node, ast.UnaryOp):
op_inner: ir.Type = self._infer_expr_llvm_type_full(node.operand)
if isinstance(node.op, ast.Deref):
# *p → pointee 类型
if isinstance(op_inner, ir.PointerType):
return op_inner.pointee
return op_inner
if isinstance(node.op, ast.AddrOf):
# &x → T*(取地址即指针)
return ir.PointerType(op_inner)
return op_inner
elif isinstance(node, ast.Subscript):
val_type: ir.Type = self._infer_expr_llvm_type_full(node.value)
if isinstance(val_type, ir.PointerType):
if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8:
return ir.IntType(8)
if isinstance(val_type.pointee, ir.ArrayType):
return ir.PointerType(val_type.pointee.element)
return val_type.pointee
if isinstance(val_type, ir.ArrayType):
return val_type.element
if isinstance(val_type, ir.IntType):
return ir.IntType(8)
return ir.IntType(32)
elif isinstance(node, ast.Call):
if isinstance(node.func, ast.Name):
func_name: str = node.func.id
if Gen._has_function(func_name):
fn: Any = Gen._get_function(func_name)
return fn.function_type.return_type
if func_name == 'len':
return ir.IntType(32)
if func_name == 'sizeof':
return ir.IntType(32)
if func_name == 'ord':
return ir.IntType(32)
if func_name == 'chr':
return ir.IntType(8)
if func_name == 'int':
return ir.IntType(32)
if func_name == 'float' or func_name == 'double':
return ir.DoubleType()
elif isinstance(node.func, ast.Attribute):
if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c':
if node.func.attr == 'Deref':
return ir.IntType(64)
if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable):
if node.func.attr == 'CChar':
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
elif isinstance(node, ast.Compare):
return ir.IntType(1)
elif isinstance(node, ast.BoolOp):
return ir.IntType(1)
elif isinstance(node, ast.IfExp):
return self._infer_expr_llvm_type_full(node.body)
elif isinstance(node, ast.Attribute):
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None:
"""收集 AST 节点中的绑定变量和自由变量
Args:
node: AST 节点
bound: 集合,收集绑定变量(ast.Store 上下文)
free: 集合,收集自由变量(ast.Load 上下文),可为 None
"""
if free is None:
free = set()
if isinstance(node, ast.Name):
if isinstance(node.ctx, ast.Store):
bound.add(node.id)
elif isinstance(node.ctx, ast.Load):
free.add(node.id)
elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)):
# 这些节点有自己的作用域,不递归进入
# 但需要收集参数名作为 bound
if hasattr(node, 'args') and node.args:
for arg in node.args.args:
bound.add(arg.arg)
elif getattr(node, '_fields', None):
for field in node._fields:
value: Any = getattr(node, field, None)
if isinstance(value, list):
for item in value:
if isinstance(item, (ast.stmt, ast.expr)):
self._collect_names(item, bound, free)
elif isinstance(value, (ast.stmt, ast.expr)):
from __future__ import annotations
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
import vast as ast
import llvmlite.ir as ir
from lib.core.SymbolUtils import IsTModule
class ExprUtils:
def __init__(self, translator: "Translator") -> None:
self.Trans: Translator = translator
def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None:
OpMap: dict[type, str] = {
ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/',
ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&',
ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>',
ast.MatMult: '@',
}
return OpMap.get(type(Op), None)
def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None:
OpMap: dict[type, str] = {
ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-',
ast.Deref: '*', ast.AddrOf: '&',
}
return OpMap.get(type(Op), None)
def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None:
OpMap: dict[type, str] = {
ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=',
ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not',
ast.In: 'in', ast.NotIn: 'not in',
}
return OpMap.get(type(Op), None)
def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None:
if isinstance(node, ast.Name):
VarName: str = node.id
return Gen.var_struct_class.get(VarName)
if isinstance(node, ast.Attribute):
AttrName: str = node.attr
# 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable)
# 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派
# (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST')
BaseClass: str | None = self._get_var_class(node.value, Gen)
if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self':
BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None)
if not BaseClass:
# 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory):
# var_struct_class 只登记**当前模块**变量,跨模块量不在其中 → 递归到此
# 返回 None → 整条链 `Mod.Var.Field` 解析为空 → 赋值/调用 fail-fast
# ("属性访问未能解析出目标类型/成员",StructGenMixin.vp:139 根因)。
BaseClass = self._resolve_module_var_class(Gen, node.value, AttrName)
if BaseClass:
# 对齐 _HandleLenLlvm L3445-3454:class_member_element_class /
# class_members 的键是裸类名,而 var_struct_class 可能带 sha1 前缀
# (如 'd81b22c853de40a2.Call')。直查会 miss → 容器字段
# (list[...] / rawptr[list[...]],如 ast.Call.args)方法分派失败
# → "Undefined method 'get' in class 'rawptr'"。剥前缀 + MRO 链查
# element_class(list 特化名,如 list[...ASTptr])优先。
_bare: str = BaseClass
if len(_bare) > 17 and _bare[16] == '.':
_bare = _bare[17:]
element_class_map = getattr(Gen, 'class_member_element_class', {})
try:
_mro_chain: list = Gen._get_mro_chain(_bare)
except Exception:
_mro_chain = [_bare]
for _mro_cls in _mro_chain:
_mk: str = _mro_cls
if len(_mk) > 17 and _mk[16] == '.':
_mk = _mk[17:]
_sub = element_class_map.get(_mk)
if _sub and AttrName in _sub:
return _sub[AttrName]
if _bare in Gen.class_members:
for m_name, m_type in Gen.class_members[_bare]:
if m_name == AttrName:
if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
found = Gen.find_struct_by_pointee(m_type.pointee)
if found:
return found[0]
break
# Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后,
# 避免字段名和变量名冲突导致错误分派)
cls: str | None = Gen.var_struct_class.get(AttrName)
if cls:
return cls
return None
return None
def _resolve_module_var_class(self, Gen: LlvmCodeGenerator, ModNode: ast.AST,
AttrName: str) -> str | None:
"""`模块别名.模块级变量` 的类名解析(如 CompilerFactory.factory)
背景:`CompilerFactory.factory.PermPool` 这类链的第一跳既不在
`var_struct_class`(只含当前模块变量)也不在 `global_struct_class`
(只含按变量名登记的项),于是 `_get_var_class` 递归到底返回 None →
整条链解析为空 → 赋值/调用 fail-fast("属性访问未能解析出目标类型/成员")。
这里按模块别名把 `Mod.Var` 落到其 LLVM 全局量,再从全局量类型反推结构体
类名;候选键形态对齐 `_lookup_module_global`(裸名 / {mod}.{var} /
{sha1}.{var})。仅当 ModNode 确为 import 模块(含别名)时生效。
"""
if not isinstance(ModNode, ast.Name):
return None
Mod: str = ModNode.id
imported: Any = getattr(self.Trans, '_ImportedModules', None) or []
sha1_map: Any = getattr(Gen, 'ModuleSha1Map', {}) or {}
try:
resolved_mod: str = self.Trans.SymbolTable.resolve_alias(Mod)
except Exception:
resolved_mod = Mod
aliases: Any = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {}
_is_mod: bool = (Mod in imported or resolved_mod in imported
or Mod in sha1_map or resolved_mod in sha1_map
or Mod in aliases or resolved_mod in aliases)
if not _is_mod:
# 兜底:ModuleSha1Map 的键多为全路径(lib.core.CompilerFactory),
# 用后缀扫描判定「Mod 是否确实是某个已导入模块(含别名)」。
for _k in sha1_map:
if _k == Mod or _k.endswith(f'.{Mod}'):
_is_mod = True
break
if not _is_mod:
return None
# 1) 变量名侧已有登记(stub 解析时跨模块量常以**裸变量名**登记)
gsc: Any = getattr(Gen, 'global_struct_class', {}) or {}
vsc: Any = getattr(Gen, 'var_struct_class', {}) or {}
for key in (f"{Mod}.{AttrName}", f"{resolved_mod}.{AttrName}", AttrName):
if key in gsc:
return gsc[key]
if key in vsc:
return vsc[key]
# 2) 借属性侧既有解析器(覆盖 module.globals/variables 与前缀形态)
try:
_via_attr: str | None = self.Trans.ExprAttrHandle._resolve_var_classname(Gen, AttrName)
except Exception:
_via_attr = None
if _via_attr:
return _via_attr
# 3) 从 LLVM 全局量类型反推结构体类名
cands: list[str] = [f"{Mod}.{AttrName}", AttrName]
for m in imported:
if m == Mod or m.endswith(f".{Mod}"):
k: str = f"{m}.{AttrName}"
if k not in cands:
cands.append(k)
for cand in (Mod, resolved_mod):
sha1: str | None = sha1_map.get(cand)
if sha1:
k2: str = f"{sha1}.{AttrName}"
if k2 not in cands:
cands.append(k2)
globals_map: Any = getattr(getattr(Gen, 'module', None), 'globals', {}) or {}
# 3b) 后缀扫描:全局量键常为 "{sha1}.{var}",sha1 不一定能从 Mod 反查到
for _gk in globals_map:
if _gk.endswith(f".{AttrName}") and _gk not in cands:
cands.append(_gk)
for key in cands:
gv: Any = globals_map.get(key)
if gv is None:
continue
ty: Any = getattr(gv, 'type', None)
if not isinstance(ty, ir.PointerType):
continue
pointee: Any = ty.pointee
# 全局量本身是指针变量(值类型 X*,LLVM 全局类型 X**)→ 再剥一层
if isinstance(pointee, ir.PointerType):
pointee = pointee.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
found: Any = Gen.find_struct_by_pointee(pointee)
if found:
return found[0]
return None
def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None:
FullMethodName: str = f'{ClassName}.{op_name}'
if not Gen._has_function(FullMethodName):
FullMethodName = f'{ClassName}.{op_name}__'
if not Gen._has_function(FullMethodName):
# 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 VPC 一致)
for anc in Gen._get_mro_chain(ClassName)[1:]:
anc_method: str = f'{anc}.{op_name}'
if Gen._has_function(anc_method):
FullMethodName = anc_method
break
anc_method_ss = f'{anc}.{op_name}__'
if Gen._has_function(anc_method_ss):
FullMethodName = anc_method_ss
break
if not Gen._has_function(FullMethodName):
# 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且
# 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本
# (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时,
# var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。
# 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。
if '[' not in ClassName:
SpecClass: str | None = None
# 先收集所有候选特化(有方法 declare 或 define 的)
best_candidates: list[str] = []
for CN in Gen.structs.keys():
if CN.startswith(f'{ClassName}[') and (Gen._has_function(f'{CN}.{op_name}') or Gen._has_function(f'{CN}.{op_name}__')):
best_candidates.append(CN)
# 优先选已编译方法体(define)的特化:dict 插入序选第一个会命中
# 只有 declare 无 define 的错误特化(如 list[AST]),生成 undefined
# symbol(ld.lld undefined symbol: list[AST].__len__ 根因)。
for CN in best_candidates:
_fn: str = f'{CN}.{op_name}' if Gen._has_function(f'{CN}.{op_name}') else f'{CN}.{op_name}__'
_f: Any = Gen._get_function(_fn)
if _f is not None and getattr(_f, 'blocks', []):
SpecClass = CN
break
if SpecClass is None and best_candidates:
# 全部只有 declare:按需补发方法体(幂等,_specialize_generic_class
# 缓存机制会从 declare_only 升级为完整特化),补发后重新检查
try:
self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen)
except Exception:
pass
_fn2: str = f'{best_candidates[0]}.{op_name}' if Gen._has_function(f'{best_candidates[0]}.{op_name}') else f'{best_candidates[0]}.{op_name}__'
_f2: Any = Gen._get_function(_fn2)
if _f2 is not None and getattr(_f2, 'blocks', []):
SpecClass = best_candidates[0]
if SpecClass:
FullMethodName = f'{SpecClass}.{op_name}'
if not Gen._has_function(FullMethodName):
FullMethodName = f'{SpecClass}.{op_name}__'
if Gen._has_function(FullMethodName):
func: Any = Gen._get_function(FullMethodName)
# 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时,
# 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。
method_anc: str | None = None
_fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName
if '.' in FullMethodName:
_fm_cls: str = FullMethodName.split('.')[0]
if _fm_cls != ClassName:
method_anc = _fm_cls
_call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc)
call_args: list[Any] = [_call_self]
if other_val is not None:
call_args.append(other_val)
if len(func.ftype.args) == len(call_args):
for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)):
if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType):
if expected_type != actual_val.type:
if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}")
result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}")
return result
return None
def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]:
info: dict[str, Any] = {
'name': "unknown",
'size': 0,
'signed': False,
'ptr': False
}
if isinstance(llvm_type, ir.IntType):
info['size'] = llvm_type.width // 8
info['signed'] = True
if llvm_type.width == 8:
info['name'] = "char"
elif llvm_type.width == 16:
info['name'] = "short"
elif llvm_type.width == 32:
info['name'] = "int"
elif llvm_type.width == 64:
info['name'] = "long long"
else:
info['name'] = f"int{llvm_type.width}"
elif isinstance(llvm_type, ir.DoubleType):
info['size'] = 8
info['signed'] = True
info['name'] = "double"
elif isinstance(llvm_type, ir.FloatType):
info['size'] = 4
info['signed'] = True
info['name'] = "float"
elif isinstance(llvm_type, ir.PointerType):
info['size'] = 8
info['signed'] = False
info['ptr'] = True
pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee)
info['name'] = f"{pointee_info['name']}*"
elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)):
size: int = 0
for elem in llvm_type.elements:
elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem)
size += elem_info['size']
info['size'] = size
info['signed'] = False
if isinstance(llvm_type, ir.IdentifiedStructType):
info['name'] = llvm_type.name
else:
info['name'] = "struct"
elif isinstance(llvm_type, ir.ArrayType):
elem_info = self._llvm_type_to_detailed_string(llvm_type.element)
info['size'] = elem_info['size'] * llvm_type.count
info['signed'] = elem_info['signed']
info['name'] = f"{elem_info['name']}[]"
elif isinstance(llvm_type, ir.VoidType):
info['size'] = 0
info['signed'] = False
info['name'] = "void"
else:
info['size'] = 0
info['signed'] = False
info['name'] = str(llvm_type)
return info
def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if isinstance(node, ast.Constant):
if isinstance(node.value, bool):
return ir.IntType(32)
elif isinstance(node.value, int):
return ir.IntType(32)
elif isinstance(node.value, float):
return ir.DoubleType()
elif isinstance(node.value, str):
if getattr(node, 'kind', None) == 'u':
return ir.PointerType(ir.IntType(16))
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
elif isinstance(node, ast.Name):
if node.id in Gen.variables:
var_ptr: Any = Gen.variables[node.id]
if isinstance(var_ptr.type, ir.PointerType):
return var_ptr.type.pointee
return var_ptr.type
if node.id in Gen._reg_values:
return Gen._reg_values[node.id].type
return ir.IntType(32)
elif isinstance(node, ast.Attribute):
attr_name: str = node.attr
AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name)
if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None):
# 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型)
return self.Trans.LlvmGen._enum_int_type(
getattr(AttrInfo, 'EnumName', None), AttrInfo)
return ir.IntType(32)
elif isinstance(node, ast.BinOp):
left_type: ir.Type = self._infer_expr_llvm_type_full(node.left)
right_type: ir.Type = self._infer_expr_llvm_type_full(node.right)
if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)):
return ir.DoubleType()
if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType):
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
elif isinstance(node, ast.UnaryOp):
op_inner: ir.Type = self._infer_expr_llvm_type_full(node.operand)
if isinstance(node.op, ast.Deref):
# *p → pointee 类型
if isinstance(op_inner, ir.PointerType):
return op_inner.pointee
return op_inner
if isinstance(node.op, ast.AddrOf):
# &x → T*(取地址即指针)
return ir.PointerType(op_inner)
return op_inner
elif isinstance(node, ast.Subscript):
val_type: ir.Type = self._infer_expr_llvm_type_full(node.value)
if isinstance(val_type, ir.PointerType):
if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8:
return ir.IntType(8)
if isinstance(val_type.pointee, ir.ArrayType):
return ir.PointerType(val_type.pointee.element)
return val_type.pointee
if isinstance(val_type, ir.ArrayType):
return val_type.element
if isinstance(val_type, ir.IntType):
return ir.IntType(8)
return ir.IntType(32)
elif isinstance(node, ast.Call):
if isinstance(node.func, ast.Name):
func_name: str = node.func.id
if Gen._has_function(func_name):
fn: Any = Gen._get_function(func_name)
return fn.function_type.return_type
if func_name == 'len':
return ir.IntType(32)
if func_name == 'sizeof':
return ir.IntType(32)
if func_name == 'ord':
return ir.IntType(32)
if func_name == 'chr':
return ir.IntType(8)
if func_name == 'int':
return ir.IntType(32)
if func_name == 'float' or func_name == 'double':
return ir.DoubleType()
elif isinstance(node.func, ast.Attribute):
if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c':
if node.func.attr == 'Deref':
return ir.IntType(64)
if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable):
if node.func.attr == 'CChar':
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
elif isinstance(node, ast.Compare):
return ir.IntType(1)
elif isinstance(node, ast.BoolOp):
return ir.IntType(1)
elif isinstance(node, ast.IfExp):
return self._infer_expr_llvm_type_full(node.body)
elif isinstance(node, ast.Attribute):
return ir.PointerType(ir.IntType(8))
return ir.IntType(32)
def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None:
"""收集 AST 节点中的绑定变量和自由变量
Args:
node: AST 节点
bound: 集合,收集绑定变量(ast.Store 上下文)
free: 集合,收集自由变量(ast.Load 上下文),可为 None
"""
if free is None:
free = set()
if isinstance(node, ast.Name):
if isinstance(node.ctx, ast.Store):
bound.add(node.id)
elif isinstance(node.ctx, ast.Load):
free.add(node.id)
elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)):
# 这些节点有自己的作用域,不递归进入
# 但需要收集参数名作为 bound
if hasattr(node, 'args') and node.args:
for arg in node.args.args:
bound.add(arg.arg)
elif getattr(node, '_fields', None):
for field in node._fields:
value: Any = getattr(node, field, None)
if isinstance(value, list):
for item in value:
if isinstance(item, (ast.stmt, ast.expr)):
self._collect_names(item, bound, free)
elif isinstance(value, (ast.stmt, ast.expr)):
self._collect_names(value, bound, free)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+2493 -2493
View File
File diff suppressed because it is too large. Load diff
+2 -2
View File
@@ -76,7 +76,7 @@ class ExprGenMixin:
# (GEP 只接受「一指针一整数」,已在上方两个 ptr/int 分支处理并返回)。
# 落到下方 ops/shift_ops/bit_ops 会直出非法 IR(`add i8*, i8*` ⇒ opt 报
# invalid operand type for instruction),故在 IR 生成前 fail-fast。
# 判据与报文逐句对齐 TPV ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。
# 判据与报文逐句对齐 VPC ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。
# ============================================================
if op in ('+', 'Add', '-', 'Sub', '*', 'Mult', '/', 'Div', '//', 'FloorDiv',
'%', 'Mod', '**', 'Pow', '&', 'BitAnd', '|', 'BitOr', '^', 'BitXor',
@@ -346,7 +346,7 @@ class ExprGenMixin:
value = self.builder.bitcast(value, ret_type, name="ret_cast")
elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
if isinstance(ret_type, ir.IntType):
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
# 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。
# 原先"同宽 load 所指物 / 其余 ptrtoint"都是静默转换。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
+1258 -1258
View File
File diff suppressed because it is too large. Load diff
+7 -7
View File
@@ -16,7 +16,7 @@ class _VolatileLoadInstr(ir.LoadInstr):
`descr` 也不输出该关键字),故按本仓库既有做法(BaseGen.VaArgInstruction
自定义 `descr`)派生子类,只把 `load` 关键字扩成 `load volatile`。
输出格式与 llvmlite 原始实现逐字段一致(类型、指针、align、元数据),
仅多一个 `volatile` 关键字;语义与 TransPyV 侧 `build_load(..., vol)` 对齐。
仅多一个 `volatile` 关键字;语义与 ViperC 侧 `build_load(..., vol)` 对齐。
"""
def descr(self, buf: list[str]) -> None:
@@ -147,7 +147,7 @@ class MemoryOpsMixin:
# ============================================================
# 隐式转换公共判据:None(空值常量)/ 指针层级 / 基本类型 / 无损
#
# 语义唯一来源与 TransPyV 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。
# 语义唯一来源与 ViperC 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。
# 收敛于此,供赋值 / 返回 / 实参 / 数组元素等所有 coerce 入口复用,避免
# 各处各自演化出相反结论。
# ============================================================
@@ -197,7 +197,7 @@ class MemoryOpsMixin:
@staticmethod
def _ScalarConvertLossless(Src: Any, Dst: Any) -> bool:
"""基本标量「无损」判定(与 TransPyV `_scalar_convert_reason` 同口径,逐条照搬):
"""基本标量「无损」判定(与 ViperC `_scalar_convert_reason` 同口径,逐条照搬):
- 整数 ↔ 整数:允许 ⇔ **目标位宽 ≥ 源位宽**(低→高无损;高→低是**有损窄化**,禁);
- 浮点 ↔ 浮点:**所有方向都允许**;
- 整数 → 浮点:允许 ⇔ **目标浮点位宽 ≥ 源整数位宽**(否则有损 → 禁);
@@ -306,7 +306,7 @@ class MemoryOpsMixin:
Node: Any = None) -> tuple[bool, str]:
"""隐式转换判据:返回 (Allowed, Reason)。
规则(用户决定,与 TransPyV 同口径):
规则(用户决定,与 ViperC 同口径):
① 通用指针(t.CPtr/i8*)为通配:与任何 T* **双向**互转放行(须同级)。
② T* → U*(皆非通用):允许 ⇔ 被指类型 T → U 无损;非基本类型
(结构体/类/联合)一律不允许 ⇒ Foo*→Bar* 禁止;**例外**有二:
@@ -368,7 +368,7 @@ class MemoryOpsMixin:
@staticmethod
def _ValueNodeOf(Node: Any = None) -> Any:
"""把「外层语句节点」下沉到其「值子表达式节点」(与 TransPyV _value_node_of 同口径)。
"""把「外层语句节点」下沉到其「值子表达式节点」(与 ViperC _value_node_of 同口径)。
Assign/AnnAssign/AugAssign/Expr/Return 取 .value;非语句节点原样返回。
规则③的「构造物化」豁免须看到**真正被转换的那个右值表达式**(如 `p: Point = Point()`
@@ -392,7 +392,7 @@ class MemoryOpsMixin:
def _IsFrameStackAddr(Node: Any, Gen: Any, TargetType: Any = None) -> bool:
"""AST 判据:Node 是否交出「当前帧栈上存储的地址」。
两种形态(与 TransPyV fail_if_stack_addr_escape 同口径):
两种形态(与 ViperC fail_if_stack_addr_escape 同口径):
(a) `&local`:UnaryOp(AddrOf, Name),且该 Name 是本帧局部 alloca(含形参);
(b) 数组退让:裸本帧**数组**名出现在**指针**目标位置(如 `return arr` with `-> char*`),
交出的是首元素地址,同属栈地址。
@@ -695,7 +695,7 @@ class MemoryOpsMixin:
val = self.builder.zext(val, ir.IntType(64), name="zext_ptr_store")
return self.builder.inttoptr(val, target_type, name="int_to_ptr_store")
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.PointerType):
# 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。
# 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。
# 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load
# 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。
# None(空指针常量)已在上方短路为目标零值,不会落到这里。
+472 -472
View File
@@ -1,23 +1,23 @@
"""LLVMTypeMapper — AST 注解节点到 LLVM IR 类型字符串的映射器
从 SymbolTable 中提取的类型解析逻辑,职责单一:
将 Python AST 注解节点翻译为 LLVM IR 类型字符串(如 'i32', 'i8*', '{ i32, i8* }')。
原 SymbolTable 上的 4 个方法:
- _GetLLVMTypeStr → LLVMTypeMapper.get_llvm_type_str
- _GetFuncRetTypeStr → LLVMTypeMapper.get_func_ret_type_str
- _GetFuncParamTypeStr → LLVMTypeMapper.get_func_param_type_str
- _ResolveTypedefValueType → LLVMTypeMapper.resolve_typedef_value_type
"""
from __future__ import annotations
import vast as ast
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.SymbolTable import SymbolTable
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap
"""LLVMTypeMapper — AST 注解节点到 LLVM IR 类型字符串的映射器
从 SymbolTable 中提取的类型解析逻辑,职责单一:
将 Python AST 注解节点翻译为 LLVM IR 类型字符串(如 'i32', 'i8*', '{ i32, i8* }')。
原 SymbolTable 上的 4 个方法:
- _GetLLVMTypeStr → LLVMTypeMapper.get_llvm_type_str
- _GetFuncRetTypeStr → LLVMTypeMapper.get_func_ret_type_str
- _GetFuncParamTypeStr → LLVMTypeMapper.get_func_param_type_str
- _ResolveTypedefValueType → LLVMTypeMapper.resolve_typedef_value_type
"""
from __future__ import annotations
import vast as ast
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.SymbolTable import SymbolTable
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap
from lib.core.SymbolUtils import (
IsTModule, IsListAnnotation, IsCPtrNode, IsCPtrSubscriptNode,
IsRawPtrSubscriptNode, EnumIntWidth,
@@ -110,347 +110,347 @@ class LLVMTypeMapper:
resolved_b: str = self._ctype_to_llvm_str(entry)
if resolved_b:
return resolved_b
return ''
# ==================================================================
# 公共 API
# ==================================================================
def get_llvm_type_str(self, node: ast.AST | None) -> str:
"""将 AST 注解节点转换为 LLVM IR 类型字符串"""
if node is None:
self._symtab.diagnostics.warn('', 0, "类型注解为 None,回退到 i32")
return 'i32'
if isinstance(node, ast.Name):
return self._resolve_name(node)
elif isinstance(node, ast.Attribute):
return self._resolve_attribute(node)
elif isinstance(node, ast.Subscript):
return self._resolve_subscript(node)
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
return self._resolve_bitor(node)
elif isinstance(node, ast.Constant):
if isinstance(node.value, bool):
return 'i8'
return 'i32'
elif isinstance(node, ast.Call):
return self._resolve_call(node)
self._symtab.diagnostics.warn('', 0, f"无法解析类型节点 {type(node).__name__},回退到 i32")
return 'i32'
def get_func_ret_type_str(self, returns_node: ast.AST | None) -> str:
"""提取函数返回类型的 LLVM 字符串"""
if returns_node is None:
return 'i32'
if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr):
# 统一走 CPtr 感知的联合解析(int | t.CPtr → i32*、str | t.CPtr → i8**)
return self._resolve_bitor(returns_node)
result: str = self.get_llvm_type_str(returns_node)
return result if result else 'i32'
def get_func_param_type_str(self, annotation_node: ast.AST | None) -> str:
"""提取函数参数类型的 LLVM 字符串。
末尾的「不许静默变 i32」守卫:`Subscript` / 联合(`|`)形态的形参注解若解析结果
是 `'i32'`,只可能是**解析失败后的兜底** —— 合法的下标注解是容器 / 泛型 / 定长数组
(解析出指针、数组或结构体指针),合法的联合写法是 `T | t.CPtr` 这类指针叠加,都
不会是 i32。该串会被 `_InsertFuncSymbol` 原样存进符号表(`FuncPtrParams`),再由
`BuildLLVMFuncTypeFromSig` 物化成形参类型 —— 一旦是 i32,调用点就在 `_adjust_args`
报「实参 %X* 不能隐式转为 i32」,把真因(**注解没解析出来**)掩盖成"调用点类型
不匹配"(现场:CallGenMixin.vp:149 调用 `_specialize_generic_class` 的
`type_args: list[str]*`)。故此处响亮失败,并带上注解原文。
"""
if annotation_node is None:
return 'i8*'
result: str
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
# 统一走 CPtr 感知的联合解析
result = self._resolve_bitor(annotation_node)
else:
result = self.get_llvm_type_str(annotation_node)
if not result:
result = 'i8*'
if result == 'i32' and isinstance(annotation_node, (ast.Subscript, ast.BinOp)):
_text: str
try:
_text = ast.unparse(annotation_node)
except Exception:
_text = f'<{type(annotation_node).__name__}>'
raise TypeNameUnresolvedError(
f"形参注解 '{_text}' 解析失败并落到 i32(下标 / 联合形态的合法结果应为"
f"指针、数组或结构体指针,不会是 i32)—— 拒绝静默降级为 i32")
return result
def resolve_typedef_value_type(self, node: ast.AST | None) -> str:
"""解析 typedef 值表达式的 C 类型字符串"""
if node is None:
return ''
if isinstance(node, ast.Name):
if node.id in self._symtab:
Entry: CTypeInfo = self._symtab[node.id]
if Entry.IsTypedef and Entry.OriginalType:
return Entry.OriginalType
# 短别名 / 内建速记(char/int/int64/uchar/str…):与注解位置
# `_resolve_name` 同源。缺此分支时 typedef 值里的裸名解析为空,
# 整条 typedef 随之解析失败(现场:w32/win32base.vp 的
# `LPCSTR: t.CTypedef = t.CConst | char | t.CPtr` ⇒ LPCSTR 不登记,
# 用到处报「裸名 LPCSTR 无法解析为类型」)。
_binfo: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
if _binfo is None:
_e: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
if _e:
_binfo = CTypeInfo()
_binfo.BaseType = _e[0]
_binfo.PtrCount = _e[1]
if _binfo is not None and _binfo.BaseType:
_bt: object = _binfo.BaseType
_nm: str = (_bt if isinstance(_bt, str)
else CTypeHelper.GetCName(_bt) if isinstance(_bt, type)
else type(_bt).__name__)
if _nm and _nm != 'CType':
return _nm + (' *' * (_binfo.PtrCount or 0))
return ''
if isinstance(node, ast.Attribute):
if isinstance(node.value, ast.Name) and IsTModule(node.value.id):
# 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖)
if node.attr == 'CPtr':
return 'ptr'
if node.attr in _TYPEDEF_VALUE_QUALIFIERS:
# 类型限定符标记(const/volatile)本身不是类型:它只修饰 `|` 另一侧。
# 旧写法 `t.CConst | char | t.CPtr` 靠 char 一侧掩盖了这一点;一旦写成
# `t.CConst | str`(`const str`),把 'CConst' 当基类型会得到 'CConst *',
# 于是别名登记失败、所有使用处报「裸名 … 无法解析为类型」。
return ''
CName: str | None = CTypeHelper.GetCName(node.attr)
if CName == 'CPtr':
return 'ptr'
if CName:
return CName
return ''
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
left_str: str = self.resolve_typedef_value_type(node.left)
right_str: str = self.resolve_typedef_value_type(node.right)
# 限定符侧解析为空串 ⇒ 另一侧就是完整类型(见 _TYPEDEF_VALUE_QUALIFIERS)。
# 只在两侧都有类型信息时才按"基类型 + 指针标记"合并。
if not left_str:
return 'void *' if right_str == 'ptr' else right_str
if not right_str:
return 'void *' if left_str == 'ptr' else left_str
if right_str == 'ptr' or right_str.endswith('*'):
if left_str != 'ptr':
return left_str + ' *'
return right_str if right_str.endswith('*') else 'void *'
if left_str == 'ptr' or left_str.endswith('*'):
if right_str != 'ptr':
return right_str + ' *'
return left_str if left_str.endswith('*') else 'void *'
if right_str not in ('ptr', 'void', 'i32', 'i64'):
return right_str
if left_str not in ('ptr', 'void', 'i32', 'i64'):
return left_str
return left_str if left_str else right_str
return ''
# ==================================================================
# 内部解析方法
# ==================================================================
def _ctype_to_llvm_str(self, info: CTypeInfo) -> str:
"""从 CTypeInfo 的 BaseType+PtrCount 生成归一化的 LLVM 类型字符串。
void* 在 LLVM IR 中归一化为 i8*;struct/enum/union 等非基础类型返回空字符串。
"""
if not info or not info.BaseType:
return ''
base_type = info.BaseType
base_llvm: str = ''
if isinstance(base_type, type) and issubclass(base_type, _t.CType):
base_llvm = CTypeRegistry.CTypeToLLVM(base_type)
elif isinstance(base_type, _t.CType):
base_llvm = CTypeRegistry.CTypeToLLVM(type(base_type))
if not base_llvm:
return ''
if info.PtrCount > 0:
if base_llvm == 'void':
return 'i8*'
return base_llvm + '*' * info.PtrCount
return base_llvm
def _ctype_str_to_llvm(self, type_str: str) -> str:
"""将 C 类型名字符串(如 'CUnsignedInt *')转换为 LLVM 类型字符串(如 'i32*')
支持:
- C 类名:'CUnsignedInt' → 'i32','CChar' → 'i8'
- 指针:'CUnsignedInt *' → 'i32*','void *' → 'i8*'
- 已是 LLVM 类型:'i32' → 'i32','i8*' → 'i8*'
无法识别返回空字符串。
"""
s: str = type_str.strip()
ptr_count: int = 0
while s.endswith('*'):
ptr_count += 1
s = s[:-1].strip()
base_llvm: str = ''
if s in ('void', 'CVoid'):
base_llvm = 'void'
else:
base_llvm = CTypeRegistry.NameToLLVM(s)
if not base_llvm and s in ('i8', 'i16', 'i32', 'i64', 'float', 'double', 'half', 'fp128', 'void'):
base_llvm = s
if not base_llvm:
return ''
if ptr_count > 0:
if base_llvm == 'void':
return 'i8*'
return base_llvm + '*' * ptr_count
return base_llvm
def _resolve_name(self, node: ast.Name) -> str:
"""解析 ast.Name 节点"""
id_map: dict[str, str] = {
'str': 'i8*', 'int': 'i32', 'bool': 'i8',
'float': 'double', 'None': 'void',
'UINT8PTR': 'i8*', 'INT8PTR': 'i8*', 'BYTEPTR': 'i8*',
'UINT16PTR': 'i16*', 'INT16PTR': 'i16*',
'UINT32PTR': 'i32*', 'INT32PTR': 'i32*',
'UINT64PTR': 'i64*', 'INT64PTR': 'i64*',
}
if node.id in id_map:
return id_map[node.id]
# 通过 BuiltinTypeMap 解析 VOIDPTR、INTPTR 等指针别名
builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
if builtin_entry:
TypeClass: type
base_ptr: int
TypeClass, base_ptr = builtin_entry
base_llvm: str = CTypeRegistry.CTypeToLLVM(TypeClass)
if base_ptr > 0:
if base_llvm == 'void':
return 'i8*'
return base_llvm + '*' * base_ptr
return base_llvm if base_llvm else 'i32'
# 查找 SymbolTable 中的 typedef
if node.id in self._symtab:
entry: CTypeInfo = self._symtab[node.id]
if entry and entry.IsTypedef:
resolved_td: str = self._typedef_entry_to_llvm(entry)
if resolved_td:
return resolved_td
# --- 本模块声明的类型名 -------------------------------------------
# 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径
# (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。
if node.id in self._ClassNames:
return f'%struct.{node.id}*'
# 枚举名:按登记的存储位宽物化(登记缺失时 32,与改前恒 i32 同值)
if node.id in self._EnumWidths:
return f'i{self._EnumWidths[node.id]}'
# --- 已加载模块(import / from-import)声明的类型名 ----------------
if node.id in self._symtab:
entry_loaded: CTypeInfo = self._symtab[node.id]
if entry_loaded:
# IsStruct 覆盖 struct / renum / exception 三种类符号(见 _InsertClassSymbol)
if entry_loaded.IsStruct:
return f'%struct.{node.id}*'
if entry_loaded.IsEnum and not entry_loaded.IsRenum:
return f'i{EnumIntWidth(entry_loaded)}'
# --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ----
# 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类**
# 声明同名符号(谁后插入谁占住裸名),两个真实例子:
# · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33
# `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT`
# · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95
# 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]`
# 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间
# (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。
_cand_sym: CTypeInfo | None
for _ns_syms in getattr(self._symtab, '_namespaces', {}).values():
_cand_sym = _ns_syms.get(node.id)
if _cand_sym is None:
continue
if getattr(_cand_sym, 'IsTypedef', False):
_resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym)
if _resolved_ns:
return _resolved_ns
elif getattr(_cand_sym, 'IsStruct', False):
return f'%struct.{node.id}*'
elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False):
return f'i{EnumIntWidth(_cand_sym)}'
# --- builtins.vp 隐式 CTypedef ----------------------------------
# int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss,
# 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径),
# 否则会误伤成下一条 fail-fast。
builtin_info: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
if builtin_info is not None:
builtin_llvm: str = self._ctype_to_llvm_str(builtin_info)
if builtin_llvm:
return builtin_llvm
# 存储类 / 类型限定符标记(CExport/CExtern/CStatic/CInline/CConst/
# CVolatile/State 等,均以 `from t import ...` 裸名出现):自身不是类型,
# 只作标记 ⇒ 贡献空串,由 _resolve_bitor 取另一侧真实类型
# (`CInt | CExport` → i32)。与 _resolve_attribute 的 marker 分支、
# TypeAnnotationResolver._ResolveNameNode 的 STORAGE_CLASS/TYPE_QUALIFIER
# 判定同口径。缺此分支时裸名标记会落到下方 fail-fast —— 而带前缀写作
# `t.CExport` 时却能通过(走 Attribute 路径),同一语义两种写法结果不同。
# 现场:Test/AstTest `def main() -> CInt | CExport`(第 108 行)。
_marker_cls: object = getattr(_t, node.id, None)
if (isinstance(_marker_cls, type) and _marker_cls is not _t.CType
and issubclass(_marker_cls, _t.CType)):
_pos_tags: object = getattr(_marker_cls, 'position', None) or ()
if _t.CType.BASE not in _pos_tags and (
_t.CType.STORAGE_CLASS in _pos_tags or _t.CType.TYPE_QUALIFIER in _pos_tags):
return ''
# fail-fast:类型信息缺失不再静默退化成 i32
_node_line: object = getattr(node, 'lineno', None)
_node_col: object = getattr(node, 'col_offset', None)
_pos: str = ""
if _node_line:
_pos = f"(注解位置 第 {_node_line} 行"
_pos += f", 列 {_node_col})" if _node_col is not None else ")"
_loaded_cnt: int = len(getattr(self._symtab, '_symbols', None) or {})
raise TypeNameUnresolvedError(
f"裸名 '{node.id}' 无法解析为类型{_pos}\n"
f" · 已尝试的解析来源(全部 miss):内置类型速记 / BuiltinTypeMap / "
f"builtins.vp 的 CTypedef / 符号表 typedef / "
f"本模块类名({len(self._ClassNames)} 个)/ 本模块枚举名({len(self._EnumWidths)} 个)/ "
f"符号表已加载条目({_loaded_cnt} 个)中的类与枚举\n"
f" · 若该名字确实是 typedef / 类:先确认声明它的模块已在本模块之前加载"
f"(未加载 ⇒ 符号表里没有它,与注解拼写无关);否则按「注解拼写错误或"
f"该类型不存在」处理")
def _resolve_attribute(self, node: ast.Attribute) -> str:
"""解析 ast.Attribute 节点"""
attr_name: str = node.attr if hasattr(node, 'attr') else ''
llvm_str: str | None = CTypeRegistry.NameToLLVM(attr_name)
if llvm_str is not None:
return llvm_str
if attr_name in ('CState', 'CDefine', 'CTypedef', 'CExtern', 'CStatic', 'CConst', 'State'):
return ''
if attr_name in ('CCharPtr', 'CIntPtr', 'CVoidPtr', 'CArrayPtr'):
return 'i8*'
if attr_name == 'CVoidPtr':
return 'i8*'
if attr_name and attr_name[0].isupper() and attr_name not in CTypeRegistry._name_to_class:
return f'%struct.{attr_name}*'
if attr_name and attr_name not in CTypeRegistry._name_to_class:
return f'%struct.{attr_name}*'
self._symtab.diagnostics.warn('', 0, f"无法解析 Attribute 类型 '{attr_name}',回退到 i32")
return 'i32'
def _resolve_subscript(self, node: ast.Subscript) -> str:
"""解析 ast.Subscript 节点"""
if isinstance(node.value, ast.Name) and node.value.id == 'tuple':
slice_node: ast.AST = node.slice
elem_types: list[str] = []
if isinstance(slice_node, ast.Tuple):
elt: ast.AST
for elt in slice_node.elts:
elem_types.append(self.get_llvm_type_str(elt))
else:
elem_types.append(self.get_llvm_type_str(slice_node))
if elem_types:
return '{ ' + ', '.join(elem_types) + ' }'
return ''
# ==================================================================
# 公共 API
# ==================================================================
def get_llvm_type_str(self, node: ast.AST | None) -> str:
"""将 AST 注解节点转换为 LLVM IR 类型字符串"""
if node is None:
self._symtab.diagnostics.warn('', 0, "类型注解为 None,回退到 i32")
return 'i32'
if isinstance(node, ast.Name):
return self._resolve_name(node)
elif isinstance(node, ast.Attribute):
return self._resolve_attribute(node)
elif isinstance(node, ast.Subscript):
return self._resolve_subscript(node)
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
return self._resolve_bitor(node)
elif isinstance(node, ast.Constant):
if isinstance(node.value, bool):
return 'i8'
return 'i32'
elif isinstance(node, ast.Call):
return self._resolve_call(node)
self._symtab.diagnostics.warn('', 0, f"无法解析类型节点 {type(node).__name__},回退到 i32")
return 'i32'
def get_func_ret_type_str(self, returns_node: ast.AST | None) -> str:
"""提取函数返回类型的 LLVM 字符串"""
if returns_node is None:
return 'i32'
if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr):
# 统一走 CPtr 感知的联合解析(int | t.CPtr → i32*、str | t.CPtr → i8**)
return self._resolve_bitor(returns_node)
result: str = self.get_llvm_type_str(returns_node)
return result if result else 'i32'
def get_func_param_type_str(self, annotation_node: ast.AST | None) -> str:
"""提取函数参数类型的 LLVM 字符串。
末尾的「不许静默变 i32」守卫:`Subscript` / 联合(`|`)形态的形参注解若解析结果
是 `'i32'`,只可能是**解析失败后的兜底** —— 合法的下标注解是容器 / 泛型 / 定长数组
(解析出指针、数组或结构体指针),合法的联合写法是 `T | t.CPtr` 这类指针叠加,都
不会是 i32。该串会被 `_InsertFuncSymbol` 原样存进符号表(`FuncPtrParams`),再由
`BuildLLVMFuncTypeFromSig` 物化成形参类型 —— 一旦是 i32,调用点就在 `_adjust_args`
报「实参 %X* 不能隐式转为 i32」,把真因(**注解没解析出来**)掩盖成"调用点类型
不匹配"(现场:CallGenMixin.vp:149 调用 `_specialize_generic_class` 的
`type_args: list[str]*`)。故此处响亮失败,并带上注解原文。
"""
if annotation_node is None:
return 'i8*'
result: str
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
# 统一走 CPtr 感知的联合解析
result = self._resolve_bitor(annotation_node)
else:
result = self.get_llvm_type_str(annotation_node)
if not result:
result = 'i8*'
if result == 'i32' and isinstance(annotation_node, (ast.Subscript, ast.BinOp)):
_text: str
try:
_text = ast.unparse(annotation_node)
except Exception:
_text = f'<{type(annotation_node).__name__}>'
raise TypeNameUnresolvedError(
f"形参注解 '{_text}' 解析失败并落到 i32(下标 / 联合形态的合法结果应为"
f"指针、数组或结构体指针,不会是 i32)—— 拒绝静默降级为 i32")
return result
def resolve_typedef_value_type(self, node: ast.AST | None) -> str:
"""解析 typedef 值表达式的 C 类型字符串"""
if node is None:
return ''
if isinstance(node, ast.Name):
if node.id in self._symtab:
Entry: CTypeInfo = self._symtab[node.id]
if Entry.IsTypedef and Entry.OriginalType:
return Entry.OriginalType
# 短别名 / 内建速记(char/int/int64/uchar/str…):与注解位置
# `_resolve_name` 同源。缺此分支时 typedef 值里的裸名解析为空,
# 整条 typedef 随之解析失败(现场:w32/win32base.vp 的
# `LPCSTR: t.CTypedef = t.CConst | char | t.CPtr` ⇒ LPCSTR 不登记,
# 用到处报「裸名 LPCSTR 无法解析为类型」)。
_binfo: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
if _binfo is None:
_e: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
if _e:
_binfo = CTypeInfo()
_binfo.BaseType = _e[0]
_binfo.PtrCount = _e[1]
if _binfo is not None and _binfo.BaseType:
_bt: object = _binfo.BaseType
_nm: str = (_bt if isinstance(_bt, str)
else CTypeHelper.GetCName(_bt) if isinstance(_bt, type)
else type(_bt).__name__)
if _nm and _nm != 'CType':
return _nm + (' *' * (_binfo.PtrCount or 0))
return ''
if isinstance(node, ast.Attribute):
if isinstance(node.value, ast.Name) and IsTModule(node.value.id):
# 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖)
if node.attr == 'CPtr':
return 'ptr'
if node.attr in _TYPEDEF_VALUE_QUALIFIERS:
# 类型限定符标记(const/volatile)本身不是类型:它只修饰 `|` 另一侧。
# 旧写法 `t.CConst | char | t.CPtr` 靠 char 一侧掩盖了这一点;一旦写成
# `t.CConst | str`(`const str`),把 'CConst' 当基类型会得到 'CConst *',
# 于是别名登记失败、所有使用处报「裸名 … 无法解析为类型」。
return ''
CName: str | None = CTypeHelper.GetCName(node.attr)
if CName == 'CPtr':
return 'ptr'
if CName:
return CName
return ''
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
left_str: str = self.resolve_typedef_value_type(node.left)
right_str: str = self.resolve_typedef_value_type(node.right)
# 限定符侧解析为空串 ⇒ 另一侧就是完整类型(见 _TYPEDEF_VALUE_QUALIFIERS)。
# 只在两侧都有类型信息时才按"基类型 + 指针标记"合并。
if not left_str:
return 'void *' if right_str == 'ptr' else right_str
if not right_str:
return 'void *' if left_str == 'ptr' else left_str
if right_str == 'ptr' or right_str.endswith('*'):
if left_str != 'ptr':
return left_str + ' *'
return right_str if right_str.endswith('*') else 'void *'
if left_str == 'ptr' or left_str.endswith('*'):
if right_str != 'ptr':
return right_str + ' *'
return left_str if left_str.endswith('*') else 'void *'
if right_str not in ('ptr', 'void', 'i32', 'i64'):
return right_str
if left_str not in ('ptr', 'void', 'i32', 'i64'):
return left_str
return left_str if left_str else right_str
return ''
# ==================================================================
# 内部解析方法
# ==================================================================
def _ctype_to_llvm_str(self, info: CTypeInfo) -> str:
"""从 CTypeInfo 的 BaseType+PtrCount 生成归一化的 LLVM 类型字符串。
void* 在 LLVM IR 中归一化为 i8*;struct/enum/union 等非基础类型返回空字符串。
"""
if not info or not info.BaseType:
return ''
base_type = info.BaseType
base_llvm: str = ''
if isinstance(base_type, type) and issubclass(base_type, _t.CType):
base_llvm = CTypeRegistry.CTypeToLLVM(base_type)
elif isinstance(base_type, _t.CType):
base_llvm = CTypeRegistry.CTypeToLLVM(type(base_type))
if not base_llvm:
return ''
if info.PtrCount > 0:
if base_llvm == 'void':
return 'i8*'
return base_llvm + '*' * info.PtrCount
return base_llvm
def _ctype_str_to_llvm(self, type_str: str) -> str:
"""将 C 类型名字符串(如 'CUnsignedInt *')转换为 LLVM 类型字符串(如 'i32*')
支持:
- C 类名:'CUnsignedInt' → 'i32','CChar' → 'i8'
- 指针:'CUnsignedInt *' → 'i32*','void *' → 'i8*'
- 已是 LLVM 类型:'i32' → 'i32','i8*' → 'i8*'
无法识别返回空字符串。
"""
s: str = type_str.strip()
ptr_count: int = 0
while s.endswith('*'):
ptr_count += 1
s = s[:-1].strip()
base_llvm: str = ''
if s in ('void', 'CVoid'):
base_llvm = 'void'
else:
base_llvm = CTypeRegistry.NameToLLVM(s)
if not base_llvm and s in ('i8', 'i16', 'i32', 'i64', 'float', 'double', 'half', 'fp128', 'void'):
base_llvm = s
if not base_llvm:
return ''
if ptr_count > 0:
if base_llvm == 'void':
return 'i8*'
return base_llvm + '*' * ptr_count
return base_llvm
def _resolve_name(self, node: ast.Name) -> str:
"""解析 ast.Name 节点"""
id_map: dict[str, str] = {
'str': 'i8*', 'int': 'i32', 'bool': 'i8',
'float': 'double', 'None': 'void',
'UINT8PTR': 'i8*', 'INT8PTR': 'i8*', 'BYTEPTR': 'i8*',
'UINT16PTR': 'i16*', 'INT16PTR': 'i16*',
'UINT32PTR': 'i32*', 'INT32PTR': 'i32*',
'UINT64PTR': 'i64*', 'INT64PTR': 'i64*',
}
if node.id in id_map:
return id_map[node.id]
# 通过 BuiltinTypeMap 解析 VOIDPTR、INTPTR 等指针别名
builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(node.id)
if builtin_entry:
TypeClass: type
base_ptr: int
TypeClass, base_ptr = builtin_entry
base_llvm: str = CTypeRegistry.CTypeToLLVM(TypeClass)
if base_ptr > 0:
if base_llvm == 'void':
return 'i8*'
return base_llvm + '*' * base_ptr
return base_llvm if base_llvm else 'i32'
# 查找 SymbolTable 中的 typedef
if node.id in self._symtab:
entry: CTypeInfo = self._symtab[node.id]
if entry and entry.IsTypedef:
resolved_td: str = self._typedef_entry_to_llvm(entry)
if resolved_td:
return resolved_td
# --- 本模块声明的类型名 -------------------------------------------
# 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径
# (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。
if node.id in self._ClassNames:
return f'%struct.{node.id}*'
# 枚举名:按登记的存储位宽物化(登记缺失时 32,与改前恒 i32 同值)
if node.id in self._EnumWidths:
return f'i{self._EnumWidths[node.id]}'
# --- 已加载模块(import / from-import)声明的类型名 ----------------
if node.id in self._symtab:
entry_loaded: CTypeInfo = self._symtab[node.id]
if entry_loaded:
# IsStruct 覆盖 struct / renum / exception 三种类符号(见 _InsertClassSymbol)
if entry_loaded.IsStruct:
return f'%struct.{node.id}*'
if entry_loaded.IsEnum and not entry_loaded.IsRenum:
return f'i{EnumIntWidth(entry_loaded)}'
# --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ----
# 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类**
# 声明同名符号(谁后插入谁占住裸名),两个真实例子:
# · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33
# `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT`
# · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95
# 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]`
# 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间
# (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。
_cand_sym: CTypeInfo | None
for _ns_syms in getattr(self._symtab, '_namespaces', {}).values():
_cand_sym = _ns_syms.get(node.id)
if _cand_sym is None:
continue
if getattr(_cand_sym, 'IsTypedef', False):
_resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym)
if _resolved_ns:
return _resolved_ns
elif getattr(_cand_sym, 'IsStruct', False):
return f'%struct.{node.id}*'
elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False):
return f'i{EnumIntWidth(_cand_sym)}'
# --- builtins.vp 隐式 CTypedef ----------------------------------
# int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss,
# 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径),
# 否则会误伤成下一条 fail-fast。
builtin_info: CTypeInfo | None = self._lookup_builtins_typedef(node.id)
if builtin_info is not None:
builtin_llvm: str = self._ctype_to_llvm_str(builtin_info)
if builtin_llvm:
return builtin_llvm
# 存储类 / 类型限定符标记(CExport/CExtern/CStatic/CInline/CConst/
# CVolatile/State 等,均以 `from t import ...` 裸名出现):自身不是类型,
# 只作标记 ⇒ 贡献空串,由 _resolve_bitor 取另一侧真实类型
# (`CInt | CExport` → i32)。与 _resolve_attribute 的 marker 分支、
# TypeAnnotationResolver._ResolveNameNode 的 STORAGE_CLASS/TYPE_QUALIFIER
# 判定同口径。缺此分支时裸名标记会落到下方 fail-fast —— 而带前缀写作
# `t.CExport` 时却能通过(走 Attribute 路径),同一语义两种写法结果不同。
# 现场:Test/AstTest `def main() -> CInt | CExport`(第 108 行)。
_marker_cls: object = getattr(_t, node.id, None)
if (isinstance(_marker_cls, type) and _marker_cls is not _t.CType
and issubclass(_marker_cls, _t.CType)):
_pos_tags: object = getattr(_marker_cls, 'position', None) or ()
if _t.CType.BASE not in _pos_tags and (
_t.CType.STORAGE_CLASS in _pos_tags or _t.CType.TYPE_QUALIFIER in _pos_tags):
return ''
# fail-fast:类型信息缺失不再静默退化成 i32
_node_line: object = getattr(node, 'lineno', None)
_node_col: object = getattr(node, 'col_offset', None)
_pos: str = ""
if _node_line:
_pos = f"(注解位置 第 {_node_line} 行"
_pos += f", 列 {_node_col})" if _node_col is not None else ")"
_loaded_cnt: int = len(getattr(self._symtab, '_symbols', None) or {})
raise TypeNameUnresolvedError(
f"裸名 '{node.id}' 无法解析为类型{_pos}\n"
f" · 已尝试的解析来源(全部 miss):内置类型速记 / BuiltinTypeMap / "
f"builtins.vp 的 CTypedef / 符号表 typedef / "
f"本模块类名({len(self._ClassNames)} 个)/ 本模块枚举名({len(self._EnumWidths)} 个)/ "
f"符号表已加载条目({_loaded_cnt} 个)中的类与枚举\n"
f" · 若该名字确实是 typedef / 类:先确认声明它的模块已在本模块之前加载"
f"(未加载 ⇒ 符号表里没有它,与注解拼写无关);否则按「注解拼写错误或"
f"该类型不存在」处理")
def _resolve_attribute(self, node: ast.Attribute) -> str:
"""解析 ast.Attribute 节点"""
attr_name: str = node.attr if hasattr(node, 'attr') else ''
llvm_str: str | None = CTypeRegistry.NameToLLVM(attr_name)
if llvm_str is not None:
return llvm_str
if attr_name in ('CState', 'CDefine', 'CTypedef', 'CExtern', 'CStatic', 'CConst', 'State'):
return ''
if attr_name in ('CCharPtr', 'CIntPtr', 'CVoidPtr', 'CArrayPtr'):
return 'i8*'
if attr_name == 'CVoidPtr':
return 'i8*'
if attr_name and attr_name[0].isupper() and attr_name not in CTypeRegistry._name_to_class:
return f'%struct.{attr_name}*'
if attr_name and attr_name not in CTypeRegistry._name_to_class:
return f'%struct.{attr_name}*'
self._symtab.diagnostics.warn('', 0, f"无法解析 Attribute 类型 '{attr_name}',回退到 i32")
return 'i32'
def _resolve_subscript(self, node: ast.Subscript) -> str:
"""解析 ast.Subscript 节点"""
if isinstance(node.value, ast.Name) and node.value.id == 'tuple':
slice_node: ast.AST = node.slice
elem_types: list[str] = []
if isinstance(slice_node, ast.Tuple):
elt: ast.AST
for elt in slice_node.elts:
elem_types.append(self.get_llvm_type_str(elt))
else:
elem_types.append(self.get_llvm_type_str(slice_node))
if elem_types:
return '{ ' + ', '.join(elem_types) + ' }'
if IsCPtrSubscriptNode(node):
# t.CPtr[T] → Ptr(resolve(T))(对齐 TPV Subscript 分支
# t.CPtr[T] → Ptr(resolve(T))(对齐 VPC Subscript 分支
# TypeGenResolve.vp:2769-2790;void 首层归一化为 i8*)
return self._add_pointer_level(self.get_llvm_type_str(node.slice))
if IsRawPtrSubscriptNode(node):
@@ -458,115 +458,115 @@ class LLVMTypeMapper:
# 合并语义同口径(裸类引用自身即指针,吸收这一层;其余逐层外包)。
# 此前只认 IsCPtrSubscriptNode → rawptr[T] 落到下方 warn 并退化成 i32。
return self._rawptr_subscript_type_str(node)
if IsListAnnotation(node):
slice_node: ast.AST = node.slice
elem_type: str
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1:
elem_type = self.get_llvm_type_str(slice_node.elts[0])
else:
elem_type = self.get_llvm_type_str(slice_node)
return elem_type + '*'
# 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。
# 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见
# SymbolUtils.ResolveGenericClassSpecName;输出与 _resolve_name /
# _resolve_attribute 的类引用**同一表示**(%struct.<名>*,短名交给
# TypeConvert._type_str_to_llvm_base 按 _struct_sha1_map 物化到
# %"{模板sha1}.<特化名>"*)。此前单参泛型下标落到下方 warn + 'i32' ⇒
# 形参类型串成 i32(现场:ClassGenGeneric._specialize_generic_class 的
# 形参 `type_args: list[str]*`)。
_Spec: str | None = ResolveGenericClassSpecName(
node, self._symtab,
GetStructSha1MapFromSymtab(self._symtab),
GetTCAliasMapFromSymtab(self._symtab))
if _Spec:
return f'%struct.{_Spec}*'
self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32")
return 'i32'
def _resolve_bitor(self, node: ast.BinOp) -> str:
"""解析 ast.BinOp(BitOr) 节点(CPtr 感知的指针层级叠加)。
T | t.CPtr 一侧为 t.CPtr 标记时,另一侧追加一层 '*':
int | t.CPtr → i32* (原逻辑误降级为 i8*)
str | t.CPtr → i8** (原逻辑误降级为 i8*)
(int | t.CPtr) | t.CPtr → i32**(嵌套递归叠加)
T | t.CPtr[...] 一侧为下标指针类型时,另一侧叠加该下标的指针层级 N
(N = 1 + 切片自身层数,不含 struct 吸收,对齐 TPV BinOp 下标分支
TypeGenResolve.vp:2596-2639):
t.CUInt8T | t.CPtr[t.CPtr] → i8**(原逻辑误降级为首段 i8)
"""
left: str = self.get_llvm_type_str(node.left)
right: str = self.get_llvm_type_str(node.right)
left_is_cptr: bool = IsCPtrNode(node.left)
right_is_cptr: bool = IsCPtrNode(node.right)
if right_is_cptr:
return self._add_pointer_level(left)
if left_is_cptr:
return self._add_pointer_level(right)
# t.CPtr[...] 下标指针标记:X | t.CPtr[...] → Ptr^N(X)
if IsCPtrSubscriptNode(node.right):
return self._add_pointer_levels(left, self._cptr_subscript_ptr_level(node.right))
if IsCPtrSubscriptNode(node.left):
return self._add_pointer_levels(right, self._cptr_subscript_ptr_level(node.left))
if right and right.endswith('*'):
return right
if left and left.endswith('*'):
return left
if left and left not in ('i32', 'void', 'i64'):
return left
if right and right not in ('i32', 'void', 'i64'):
return right
return left if left else right
def _add_pointer_level(self, ty: str) -> str:
"""给类型字符串追加一层指针 '*'(void/void* → i8*)。"""
if not ty:
return 'i8*'
if ty == 'void' or ty == 'void*':
return 'i8*'
if ty.endswith('*'):
return ty + '*'
return ty + '*'
def _rawptr_subscript_type_str(self, node: ast.Subscript) -> str:
"""rawptr[T](内置裸指针下标)→ LLVM 类型串。
与定义端同口径(`T | t.CPtr` 的合并):裸类引用('%struct.X*' /
'%"sha1.X"*' / 泛型特化等以 '%' 开头的结构指针)自身即指针,吸收这一层;
其余(标量、已是指针的 typedef、联合写法、嵌套 rawptr / t.CPtr 下标)
逐层外包(void → i8*)。
"""
inner: ast.AST = node.slice
inner_type: str = self.get_llvm_type_str(inner)
if (not isinstance(inner, ast.BinOp)
and not IsRawPtrSubscriptNode(inner)
and not IsCPtrSubscriptNode(inner)
and inner_type.startswith('%') and inner_type.endswith('*')):
return inner_type
return self._add_pointer_level(inner_type)
def _add_pointer_levels(self, ty: str, count: int) -> str:
"""给类型字符串叠加 count 层指针(void/void* 首层归一化为 i8*)。"""
result: str = ty
for _ in range(count):
result = self._add_pointer_level(result)
return result
def _cptr_subscript_ptr_level(self, node: ast.AST) -> int:
"""t.CPtr[T] 下标指针标记贡献的指针层级 N(= 1 + 切片自身层数)。
复用定义侧解析器 TypeAnnotationResolver.from_node(其 _from_node_subscript
已实现 t.CPtr[T] → Ptr(T) 的层级叠加,见 TypeAnnotationResolver.py:756-783),
与 HandlesTypeMerge._HandleSubscript(HandlesTypeMerge.py:512-531)同口径。
调用前须经 IsCPtrSubscriptNode 判据过滤,故 PtrCount 至少为 1。
"""
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR
return _TAR.from_node(node, self._symtab).PtrCount
def _resolve_call(self, node: ast.Call) -> str:
"""解析 ast.Call 节点"""
if isinstance(node.func, ast.Attribute):
if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id):
return self.get_llvm_type_str(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr))
self._symtab.diagnostics.warn('', 0, "无法解析 Call 类型,回退到 i32")
return 'i32'
if IsListAnnotation(node):
slice_node: ast.AST = node.slice
elem_type: str
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1:
elem_type = self.get_llvm_type_str(slice_node.elts[0])
else:
elem_type = self.get_llvm_type_str(slice_node)
return elem_type + '*'
# 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。
# 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见
# SymbolUtils.ResolveGenericClassSpecName;输出与 _resolve_name /
# _resolve_attribute 的类引用**同一表示**(%struct.<名>*,短名交给
# TypeConvert._type_str_to_llvm_base 按 _struct_sha1_map 物化到
# %"{模板sha1}.<特化名>"*)。此前单参泛型下标落到下方 warn + 'i32' ⇒
# 形参类型串成 i32(现场:ClassGenGeneric._specialize_generic_class 的
# 形参 `type_args: list[str]*`)。
_Spec: str | None = ResolveGenericClassSpecName(
node, self._symtab,
GetStructSha1MapFromSymtab(self._symtab),
GetTCAliasMapFromSymtab(self._symtab))
if _Spec:
return f'%struct.{_Spec}*'
self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32")
return 'i32'
def _resolve_bitor(self, node: ast.BinOp) -> str:
"""解析 ast.BinOp(BitOr) 节点(CPtr 感知的指针层级叠加)。
T | t.CPtr 一侧为 t.CPtr 标记时,另一侧追加一层 '*':
int | t.CPtr → i32* (原逻辑误降级为 i8*)
str | t.CPtr → i8** (原逻辑误降级为 i8*)
(int | t.CPtr) | t.CPtr → i32**(嵌套递归叠加)
T | t.CPtr[...] 一侧为下标指针类型时,另一侧叠加该下标的指针层级 N
(N = 1 + 切片自身层数,不含 struct 吸收,对齐 VPC BinOp 下标分支
TypeGenResolve.vp:2596-2639):
t.CUInt8T | t.CPtr[t.CPtr] → i8**(原逻辑误降级为首段 i8)
"""
left: str = self.get_llvm_type_str(node.left)
right: str = self.get_llvm_type_str(node.right)
left_is_cptr: bool = IsCPtrNode(node.left)
right_is_cptr: bool = IsCPtrNode(node.right)
if right_is_cptr:
return self._add_pointer_level(left)
if left_is_cptr:
return self._add_pointer_level(right)
# t.CPtr[...] 下标指针标记:X | t.CPtr[...] → Ptr^N(X)
if IsCPtrSubscriptNode(node.right):
return self._add_pointer_levels(left, self._cptr_subscript_ptr_level(node.right))
if IsCPtrSubscriptNode(node.left):
return self._add_pointer_levels(right, self._cptr_subscript_ptr_level(node.left))
if right and right.endswith('*'):
return right
if left and left.endswith('*'):
return left
if left and left not in ('i32', 'void', 'i64'):
return left
if right and right not in ('i32', 'void', 'i64'):
return right
return left if left else right
def _add_pointer_level(self, ty: str) -> str:
"""给类型字符串追加一层指针 '*'(void/void* → i8*)。"""
if not ty:
return 'i8*'
if ty == 'void' or ty == 'void*':
return 'i8*'
if ty.endswith('*'):
return ty + '*'
return ty + '*'
def _rawptr_subscript_type_str(self, node: ast.Subscript) -> str:
"""rawptr[T](内置裸指针下标)→ LLVM 类型串。
与定义端同口径(`T | t.CPtr` 的合并):裸类引用('%struct.X*' /
'%"sha1.X"*' / 泛型特化等以 '%' 开头的结构指针)自身即指针,吸收这一层;
其余(标量、已是指针的 typedef、联合写法、嵌套 rawptr / t.CPtr 下标)
逐层外包(void → i8*)。
"""
inner: ast.AST = node.slice
inner_type: str = self.get_llvm_type_str(inner)
if (not isinstance(inner, ast.BinOp)
and not IsRawPtrSubscriptNode(inner)
and not IsCPtrSubscriptNode(inner)
and inner_type.startswith('%') and inner_type.endswith('*')):
return inner_type
return self._add_pointer_level(inner_type)
def _add_pointer_levels(self, ty: str, count: int) -> str:
"""给类型字符串叠加 count 层指针(void/void* 首层归一化为 i8*)。"""
result: str = ty
for _ in range(count):
result = self._add_pointer_level(result)
return result
def _cptr_subscript_ptr_level(self, node: ast.AST) -> int:
"""t.CPtr[T] 下标指针标记贡献的指针层级 N(= 1 + 切片自身层数)。
复用定义侧解析器 TypeAnnotationResolver.from_node(其 _from_node_subscript
已实现 t.CPtr[T] → Ptr(T) 的层级叠加,见 TypeAnnotationResolver.py:756-783),
与 HandlesTypeMerge._HandleSubscript(HandlesTypeMerge.py:512-531)同口径。
调用前须经 IsCPtrSubscriptNode 判据过滤,故 PtrCount 至少为 1。
"""
from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR
return _TAR.from_node(node, self._symtab).PtrCount
def _resolve_call(self, node: ast.Call) -> str:
"""解析 ast.Call 节点"""
if isinstance(node.func, ast.Attribute):
if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id):
return self.get_llvm_type_str(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr))
self._symtab.diagnostics.warn('', 0, "无法解析 Call 类型,回退到 i32")
return 'i32'
+319 -319
View File
@@ -1,320 +1,320 @@
"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息"""
from __future__ import annotations
import vast as ast
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.SymbolTable import SymbolTable
from lib.core.SymbolData import (
ModuleSymbols, ClassSymbolData, TypedefSymbolData,
FuncSymbolData, DefineSymbolData, AnonymousTypeData
)
from lib.core.Handles.HandlesBase import CTypeInfo
"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息"""
from __future__ import annotations
import vast as ast
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.SymbolTable import SymbolTable
from lib.core.SymbolData import (
ModuleSymbols, ClassSymbolData, TypedefSymbolData,
FuncSymbolData, DefineSymbolData, AnonymousTypeData
)
from lib.core.Handles.HandlesBase import CTypeInfo
from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName
from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH
from lib.includes import t
class ASTSymbolExtractor:
"""从 Python 源文件的 AST 中提取所有符号信息"""
def __init__(self, symbol_table: SymbolTable) -> None:
self._symtab: SymbolTable = symbol_table
def extract(self, file_path: str) -> ModuleSymbols:
"""提取文件中的所有符号信息"""
with open(file_path, 'r', encoding='utf-8') as f:
code: str = f.read()
tree: ast.Module = ast.parse(code, file_path)
result: ModuleSymbols = ModuleSymbols(file_path=file_path)
# 第一步:收集所有类的成员和匿名类型
class_members: dict[str, dict[str, CTypeInfo]] = {}
for node in tree.body:
if isinstance(node, ast.ClassDef):
members: dict[str, CTypeInfo] = {}
self._collect_members(node, members, result.anonymous_types)
class_members[node.name] = members
# 第二步:从每个顶层节点提取符号信息
for node in tree.body:
if isinstance(node, ast.ClassDef):
members_info: dict[str, CTypeInfo] = class_members.get(node.name, {})
result.classes.append(self._extract_class(node, members_info))
elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
self._extract_ann_assign(node, result)
elif isinstance(node, ast.FunctionDef):
result.functions.append(self._extract_function(node))
return result
# ------------------------------------------------------------------
# 成员收集(递归处理嵌套匿名类)
# ------------------------------------------------------------------
def _collect_members(self, class_node: ast.ClassDef, members: dict[str, CTypeInfo], anonymous_types: dict[str, AnonymousTypeData], prefix: str = '') -> None:
"""递归收集类成员,包括嵌套匿名类型"""
for item in class_node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
member_name: str = item.target.id
full_name: str = f'{prefix}{member_name}' if prefix else member_name
member_type: CTypeInfo | None = CTypeInfo.FromNode(item.annotation, self._symtab)
if member_type is None:
member_type = CTypeInfo()
member_type.BaseType = t.CInt()
is_ptr: bool = member_type.IsPtr
if isinstance(item.annotation, ast.BinOp):
if AnnotationContainsName(item.annotation, 'CPtr'):
is_ptr = True
members[full_name] = member_type.Copy()
elif isinstance(item, ast.ClassDef):
is_anonymous: bool = any(
any(AnnotationContainsTType(base, 'Anonymous') for base in item.bases)
) if item.bases else False
if is_anonymous:
is_union: bool = any(
any(AnnotationContainsTType(base, 'CUnion') for base in item.bases)
)
# 也检查装饰器形式 @t.CUnion
if not is_union and hasattr(item, 'decorator_list') and item.decorator_list:
for deco in item.decorator_list:
if isinstance(deco, ast.Attribute) and hasattr(deco.value, 'id') and IsTModule(deco.value.id, self._symtab) and deco.attr == 'CUnion':
is_union = True
break
elif isinstance(deco, ast.Name):
cls2: type | None = getattr(t, deco.id, None)
if cls2 is None and hasattr(self._symtab, '_t_type_symbols'):
cls2 = self._symtab._t_type_symbols.get(deco.id)
if cls2 is not None and isinstance(cls2, type) and issubclass(cls2, t.CUnion):
is_union = True
break
nested_name: str = f'{prefix}{item.name}' if prefix else item.name
members[nested_name] = CTypeInfo()
members[nested_name].BaseType = f'union {item.name}' if is_union else f'struct {item.name}'
members[nested_name].PtrCount = 0
members[nested_name].ArrayDims = []
new_prefix: str = f'{prefix}{item.name}.' if prefix else f'{item.name}.'
self._collect_members(item, members, anonymous_types, new_prefix)
anonymous_members: dict[str, CTypeInfo] = {}
for full_name, member_info in members.items():
if full_name.startswith(f"{nested_name}."):
member_name: str = full_name[len(nested_name) + 1:]
anonymous_members[member_name] = member_info
if item.name not in anonymous_types:
anonymous_types[item.name] = AnonymousTypeData(
name=item.name,
is_union=is_union,
members=anonymous_members,
lineno=item.lineno
)
# ------------------------------------------------------------------
# 类符号提取
# ------------------------------------------------------------------
def _extract_class(self, node: ast.ClassDef, members_info: dict[str, CTypeInfo]) -> ClassSymbolData:
"""从 ClassDef 节点提取类符号数据"""
type_kind: str = 'struct'
is_cpython_object: bool = False
is_packed: bool = False
# 检查装饰器
if hasattr(node, 'decorator_list') and node.decorator_list:
for decorator in node.decorator_list:
if isinstance(decorator, ast.Attribute):
if (hasattr(decorator.value, 'id') and
IsTModule(decorator.value.id, self._symtab)):
if decorator.attr == 'Object':
is_cpython_object = True
elif decorator.attr == 'CUnion':
type_kind = 'union'
elif decorator.attr == 'CEnum':
type_kind = 'enum'
elif decorator.attr == 'REnum':
type_kind = 'renum'
elif decorator.attr == 'CStruct':
type_kind = 'struct'
elif isinstance(decorator, ast.Name):
# from t import CStruct / CUnion / CEnum / REnum
cls: type | None = getattr(t, decorator.id, None)
if cls is None and hasattr(self._symtab, '_t_type_symbols'):
cls = self._symtab._t_type_symbols.get(decorator.id)
if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType):
if issubclass(cls, t.CUnion):
type_kind = 'union'
elif issubclass(cls, t.REnum):
type_kind = 'renum'
elif issubclass(cls, t.CEnum):
type_kind = 'enum'
elif issubclass(cls, t.CStruct):
type_kind = 'struct'
elif decorator.id == 'Object':
is_cpython_object = True
elif isinstance(decorator, ast.Call):
if isinstance(decorator.func, ast.Attribute):
if hasattr(decorator.func.value, 'id') and decorator.func.value.id == 'c' and decorator.func.attr == 'Attribute':
for arg in decorator.args:
if isinstance(arg, ast.Attribute):
if isinstance(arg.value, ast.Attribute):
if hasattr(arg.value.value, 'id') and IsTModule(arg.value.value.id, self._symtab) and arg.value.attr == 'attr' and arg.attr == 'packed':
is_packed = True
# 检查基类
for base in node.bases:
if AnnotationContainsTType(base, 'CUnion'):
type_kind = 'union'
break
elif AnnotationContainsTType(base, 'CEnum'):
type_kind = 'enum'
break
elif AnnotationContainsTType(base, 'REnum'):
type_kind = 'renum'
break
elif AnnotationContainsTType(base, 'CStruct'):
type_kind = 'struct'
break
elif AnnotationContainsTType(base, 'Object'):
is_cpython_object = True
type_kind = 'struct'
break
elif isinstance(base, ast.Name) and base.id == 'Exception':
type_kind = 'exception'
break
elif isinstance(base, ast.Name):
base_entry: CTypeInfo | None = self._symtab.get(base.id)
if base_entry and base_entry.IsExceptionClass:
type_kind = 'exception'
break
# 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端
# HandlesClassDef._RegisterEnumMembers / TPV ClassGenEnum 同一规则源)。
# 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源
# (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle),
# 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读
# 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。
enum_members: list[tuple[str, int, int]] = []
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
if type_kind == 'enum':
enum_width = EnumClassIntWidth(node.body, self._symtab)
next_enum_value: int = 0
for item in node.body:
if isinstance(item, ast.Assign):
if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name):
member_name: str = item.targets[0].id
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
next_enum_value = item.value.value + 1
else:
next_enum_value += 1
enum_members.append((member_name, next_enum_value - 1, item.lineno))
elif isinstance(item, ast.AnnAssign):
if isinstance(item.target, ast.Name):
member_name: str = item.target.id
if item.value:
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
next_enum_value = item.value.value + 1
else:
next_enum_value += 1
else:
next_enum_value += 1
enum_members.append((member_name, next_enum_value - 1, item.lineno))
# REnum 变体提取(嵌套 ClassDef + Assign 显式值)
# 与 HandlesClassDef._EmitREnumLlvm 的 tag 分配逻辑对齐:
# - 先收集 Assign 显式值
# - ClassDef 变体从 max(explicit)+1 开始顺序编号
# - Assign 无显式值时用独立计数器 variant_index
if type_kind == 'renum':
explicit_values: dict[str, int] = {}
for item in node.body:
if isinstance(item, ast.Assign):
for target in item.targets:
if isinstance(target, ast.Name):
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
explicit_values[target.id] = item.value.value
next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0
variant_index: int = 0
for item in node.body:
if isinstance(item, ast.ClassDef):
variant_name_r: str = item.name
tag: int = next_class_tag
next_class_tag += 1
enum_members.append((variant_name_r, tag, item.lineno))
elif isinstance(item, ast.Assign):
for target in item.targets:
if isinstance(target, ast.Name):
var_name: str = target.id
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
value: int = item.value.value
else:
value = variant_index
variant_index += 1
enum_members.append((var_name, value, item.lineno))
return ClassSymbolData(
name=node.name,
type_kind=type_kind,
lineno=node.lineno,
members=members_info,
is_cpython_object=is_cpython_object,
is_packed=is_packed,
enum_members=enum_members,
enum_width=enum_width,
)
# ------------------------------------------------------------------
# AnnAssign 提取(typedef / define)
# ------------------------------------------------------------------
def _extract_ann_assign(self, node: ast.AnnAssign, result: ModuleSymbols) -> None:
"""从 AnnAssign 节点提取 typedef 或 define 数据"""
var_name: str = node.target.id
has_cdefine: bool = AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False
has_postdef: bool = AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False
has_ctypedef: bool = AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False
if has_cdefine:
result.defines.append(DefineSymbolData(
name=var_name,
lineno=node.lineno,
value_node=node.value,
))
else:
# rawptr 是编译器内置语法(TPC/TPV 双端),不可重定义:
# 忽略任何对 rawptr 的 typedef 声明,防止覆盖内置裸指针语义
if var_name == 'rawptr':
return
result.typedefs.append(TypedefSymbolData(
name=var_name,
lineno=node.lineno,
annotation=node.annotation,
value=node.value,
has_cdefine=has_cdefine,
has_postdef=has_postdef,
has_ctypedef=has_ctypedef,
))
# ------------------------------------------------------------------
# 函数提取
# ------------------------------------------------------------------
def _extract_function(self, node: ast.FunctionDef) -> FuncSymbolData:
"""从 FunctionDef 节点提取函数符号数据"""
params: list[tuple[str, ast.AST | None]] = [(arg.arg, arg.annotation) for arg in node.args.args]
is_variadic: bool = node.args.vararg is not None
is_inline: bool = CheckAnnotationHasCInline(node.returns)
return FuncSymbolData(
name=node.name,
lineno=node.lineno,
returns_node=node.returns,
params=params,
is_variadic=is_variadic,
is_inline=is_inline,
)
from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH
from lib.includes import t
class ASTSymbolExtractor:
"""从 Python 源文件的 AST 中提取所有符号信息"""
def __init__(self, symbol_table: SymbolTable) -> None:
self._symtab: SymbolTable = symbol_table
def extract(self, file_path: str) -> ModuleSymbols:
"""提取文件中的所有符号信息"""
with open(file_path, 'r', encoding='utf-8') as f:
code: str = f.read()
tree: ast.Module = ast.parse(code, file_path)
result: ModuleSymbols = ModuleSymbols(file_path=file_path)
# 第一步:收集所有类的成员和匿名类型
class_members: dict[str, dict[str, CTypeInfo]] = {}
for node in tree.body:
if isinstance(node, ast.ClassDef):
members: dict[str, CTypeInfo] = {}
self._collect_members(node, members, result.anonymous_types)
class_members[node.name] = members
# 第二步:从每个顶层节点提取符号信息
for node in tree.body:
if isinstance(node, ast.ClassDef):
members_info: dict[str, CTypeInfo] = class_members.get(node.name, {})
result.classes.append(self._extract_class(node, members_info))
elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
self._extract_ann_assign(node, result)
elif isinstance(node, ast.FunctionDef):
result.functions.append(self._extract_function(node))
return result
# ------------------------------------------------------------------
# 成员收集(递归处理嵌套匿名类)
# ------------------------------------------------------------------
def _collect_members(self, class_node: ast.ClassDef, members: dict[str, CTypeInfo], anonymous_types: dict[str, AnonymousTypeData], prefix: str = '') -> None:
"""递归收集类成员,包括嵌套匿名类型"""
for item in class_node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
member_name: str = item.target.id
full_name: str = f'{prefix}{member_name}' if prefix else member_name
member_type: CTypeInfo | None = CTypeInfo.FromNode(item.annotation, self._symtab)
if member_type is None:
member_type = CTypeInfo()
member_type.BaseType = t.CInt()
is_ptr: bool = member_type.IsPtr
if isinstance(item.annotation, ast.BinOp):
if AnnotationContainsName(item.annotation, 'CPtr'):
is_ptr = True
members[full_name] = member_type.Copy()
elif isinstance(item, ast.ClassDef):
is_anonymous: bool = any(
any(AnnotationContainsTType(base, 'Anonymous') for base in item.bases)
) if item.bases else False
if is_anonymous:
is_union: bool = any(
any(AnnotationContainsTType(base, 'CUnion') for base in item.bases)
)
# 也检查装饰器形式 @t.CUnion
if not is_union and hasattr(item, 'decorator_list') and item.decorator_list:
for deco in item.decorator_list:
if isinstance(deco, ast.Attribute) and hasattr(deco.value, 'id') and IsTModule(deco.value.id, self._symtab) and deco.attr == 'CUnion':
is_union = True
break
elif isinstance(deco, ast.Name):
cls2: type | None = getattr(t, deco.id, None)
if cls2 is None and hasattr(self._symtab, '_t_type_symbols'):
cls2 = self._symtab._t_type_symbols.get(deco.id)
if cls2 is not None and isinstance(cls2, type) and issubclass(cls2, t.CUnion):
is_union = True
break
nested_name: str = f'{prefix}{item.name}' if prefix else item.name
members[nested_name] = CTypeInfo()
members[nested_name].BaseType = f'union {item.name}' if is_union else f'struct {item.name}'
members[nested_name].PtrCount = 0
members[nested_name].ArrayDims = []
new_prefix: str = f'{prefix}{item.name}.' if prefix else f'{item.name}.'
self._collect_members(item, members, anonymous_types, new_prefix)
anonymous_members: dict[str, CTypeInfo] = {}
for full_name, member_info in members.items():
if full_name.startswith(f"{nested_name}."):
member_name: str = full_name[len(nested_name) + 1:]
anonymous_members[member_name] = member_info
if item.name not in anonymous_types:
anonymous_types[item.name] = AnonymousTypeData(
name=item.name,
is_union=is_union,
members=anonymous_members,
lineno=item.lineno
)
# ------------------------------------------------------------------
# 类符号提取
# ------------------------------------------------------------------
def _extract_class(self, node: ast.ClassDef, members_info: dict[str, CTypeInfo]) -> ClassSymbolData:
"""从 ClassDef 节点提取类符号数据"""
type_kind: str = 'struct'
is_cpython_object: bool = False
is_packed: bool = False
# 检查装饰器
if hasattr(node, 'decorator_list') and node.decorator_list:
for decorator in node.decorator_list:
if isinstance(decorator, ast.Attribute):
if (hasattr(decorator.value, 'id') and
IsTModule(decorator.value.id, self._symtab)):
if decorator.attr == 'Object':
is_cpython_object = True
elif decorator.attr == 'CUnion':
type_kind = 'union'
elif decorator.attr == 'CEnum':
type_kind = 'enum'
elif decorator.attr == 'REnum':
type_kind = 'renum'
elif decorator.attr == 'CStruct':
type_kind = 'struct'
elif isinstance(decorator, ast.Name):
# from t import CStruct / CUnion / CEnum / REnum
cls: type | None = getattr(t, decorator.id, None)
if cls is None and hasattr(self._symtab, '_t_type_symbols'):
cls = self._symtab._t_type_symbols.get(decorator.id)
if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType):
if issubclass(cls, t.CUnion):
type_kind = 'union'
elif issubclass(cls, t.REnum):
type_kind = 'renum'
elif issubclass(cls, t.CEnum):
type_kind = 'enum'
elif issubclass(cls, t.CStruct):
type_kind = 'struct'
elif decorator.id == 'Object':
is_cpython_object = True
elif isinstance(decorator, ast.Call):
if isinstance(decorator.func, ast.Attribute):
if hasattr(decorator.func.value, 'id') and decorator.func.value.id == 'c' and decorator.func.attr == 'Attribute':
for arg in decorator.args:
if isinstance(arg, ast.Attribute):
if isinstance(arg.value, ast.Attribute):
if hasattr(arg.value.value, 'id') and IsTModule(arg.value.value.id, self._symtab) and arg.value.attr == 'attr' and arg.attr == 'packed':
is_packed = True
# 检查基类
for base in node.bases:
if AnnotationContainsTType(base, 'CUnion'):
type_kind = 'union'
break
elif AnnotationContainsTType(base, 'CEnum'):
type_kind = 'enum'
break
elif AnnotationContainsTType(base, 'REnum'):
type_kind = 'renum'
break
elif AnnotationContainsTType(base, 'CStruct'):
type_kind = 'struct'
break
elif AnnotationContainsTType(base, 'Object'):
is_cpython_object = True
type_kind = 'struct'
break
elif isinstance(base, ast.Name) and base.id == 'Exception':
type_kind = 'exception'
break
elif isinstance(base, ast.Name):
base_entry: CTypeInfo | None = self._symtab.get(base.id)
if base_entry and base_entry.IsExceptionClass:
type_kind = 'exception'
break
# 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端
# HandlesClassDef._RegisterEnumMembers / VPC ClassGenEnum 同一规则源)。
# 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源
# (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle),
# 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读
# 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。
enum_members: list[tuple[str, int, int]] = []
enum_width: int = _ENUM_DEFAULT_INT_WIDTH
if type_kind == 'enum':
enum_width = EnumClassIntWidth(node.body, self._symtab)
next_enum_value: int = 0
for item in node.body:
if isinstance(item, ast.Assign):
if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name):
member_name: str = item.targets[0].id
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
next_enum_value = item.value.value + 1
else:
next_enum_value += 1
enum_members.append((member_name, next_enum_value - 1, item.lineno))
elif isinstance(item, ast.AnnAssign):
if isinstance(item.target, ast.Name):
member_name: str = item.target.id
if item.value:
if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
next_enum_value = item.value.value + 1
else:
next_enum_value += 1
else:
next_enum_value += 1
enum_members.append((member_name, next_enum_value - 1, item.lineno))
# REnum 变体提取(嵌套 ClassDef + Assign 显式值)
# 与 HandlesClassDef._EmitREnumLlvm 的 tag 分配逻辑对齐:
# - 先收集 Assign 显式值
# - ClassDef 变体从 max(explicit)+1 开始顺序编号
# - Assign 无显式值时用独立计数器 variant_index
if type_kind == 'renum':
explicit_values: dict[str, int] = {}
for item in node.body:
if isinstance(item, ast.Assign):
for target in item.targets:
if isinstance(target, ast.Name):
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
explicit_values[target.id] = item.value.value
next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0
variant_index: int = 0
for item in node.body:
if isinstance(item, ast.ClassDef):
variant_name_r: str = item.name
tag: int = next_class_tag
next_class_tag += 1
enum_members.append((variant_name_r, tag, item.lineno))
elif isinstance(item, ast.Assign):
for target in item.targets:
if isinstance(target, ast.Name):
var_name: str = target.id
if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int):
value: int = item.value.value
else:
value = variant_index
variant_index += 1
enum_members.append((var_name, value, item.lineno))
return ClassSymbolData(
name=node.name,
type_kind=type_kind,
lineno=node.lineno,
members=members_info,
is_cpython_object=is_cpython_object,
is_packed=is_packed,
enum_members=enum_members,
enum_width=enum_width,
)
# ------------------------------------------------------------------
# AnnAssign 提取(typedef / define)
# ------------------------------------------------------------------
def _extract_ann_assign(self, node: ast.AnnAssign, result: ModuleSymbols) -> None:
"""从 AnnAssign 节点提取 typedef 或 define 数据"""
var_name: str = node.target.id
has_cdefine: bool = AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False
has_postdef: bool = AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False
has_ctypedef: bool = AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False
if has_cdefine:
result.defines.append(DefineSymbolData(
name=var_name,
lineno=node.lineno,
value_node=node.value,
))
else:
# rawptr 是编译器内置语法(TPC/VPC 双端),不可重定义:
# 忽略任何对 rawptr 的 typedef 声明,防止覆盖内置裸指针语义
if var_name == 'rawptr':
return
result.typedefs.append(TypedefSymbolData(
name=var_name,
lineno=node.lineno,
annotation=node.annotation,
value=node.value,
has_cdefine=has_cdefine,
has_postdef=has_postdef,
has_ctypedef=has_ctypedef,
))
# ------------------------------------------------------------------
# 函数提取
# ------------------------------------------------------------------
def _extract_function(self, node: ast.FunctionDef) -> FuncSymbolData:
"""从 FunctionDef 节点提取函数符号数据"""
params: list[tuple[str, ast.AST | None]] = [(arg.arg, arg.annotation) for arg in node.args.args]
is_variadic: bool = node.args.vararg is not None
is_inline: bool = CheckAnnotationHasCInline(node.returns)
return FuncSymbolData(
name=node.name,
lineno=node.lineno,
returns_node=node.returns,
params=params,
is_variadic=is_variadic,
is_inline=is_inline,
)
+318 -318
View File
@@ -1,318 +1,318 @@
from __future__ import annotations
import vast as ast
from typing import TYPE_CHECKING
import llvmlite.ir as ir
from lib.core.ConstEvaluator import ConstEvaluator
if TYPE_CHECKING:
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
class ConstEvalMixin:
"""常量表达式求值 Mixin
提供数组初始化常量构建与常量表达式求值能力。
"""
@staticmethod
def _bswap_constant_value(val: int, width: int) -> int:
"""编译期字节序交换"""
if width == 16:
return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)
elif width == 32:
return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF)
elif width == 64:
return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF)
return val
def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None:
_zv: ir.Constant | None = Gen._zero_value_for_type(ElemType)
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
if not isinstance(value_node, (ast.List, ast.Set)):
return None
# 显式空元素判据(与 TPV AssignGenMixin._init_global_array_from_list 的裸空指针
# 判据两侧一致):Python 侧的空 AST 元素就是 None(TPV 侧是裸 0)。列表元素计数
# 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值;
# 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。
for _gi, _elt in enumerate(value_node.elts):
if _elt is None:
raise TypeError(
f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。'
f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);'
f'拒绝静默补零生成残缺初始化,已终止编译。'
f'位置 line {getattr(value_node, "lineno", 0)}'
f':{getattr(value_node, "col_offset", 0)}'
)
if isinstance(ElemType, ir.ArrayType):
constants: list[ir.Constant] = []
for elt in value_node.elts:
if isinstance(elt, (ast.List, ast.Set)):
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen)
if inner_consts:
try:
constants.append(ir.Constant(ElemType, inner_consts))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < ArrayCount:
constants.append(_fallback)
return constants[:ArrayCount]
StructName: str | None = None
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
StructName = elem_type_node.id
constants = []
for elt in value_node.elts:
if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName:
const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Constant):
# 字符串常量:根据引号类型决定 i8 或 i8*
if isinstance(elt.value, str):
const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
else:
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.UnaryOp):
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr':
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int):
try:
constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord':
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1:
try:
constants.append(ir.Constant(ElemType, ord(elt.args[0].value)))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
else:
const_val: int | float | str | None = self._try_eval_const_call(elt, Gen)
if const_val is not None:
try:
constants.append(ir.Constant(ElemType, const_val))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < ArrayCount:
constants.append(_fallback)
if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64):
swapped: list[ir.Constant] = []
for c in constants:
if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int):
swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width)
swapped.append(ir.Constant(ElemType, swapped_val))
else:
swapped.append(c)
constants = swapped
return constants[:ArrayCount]
def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
"""根据引号类型决定字符串常量的类型:
- 单引号单字符 → i8 (char 值)
- 双引号/单引号多字符/三重引号 → i8* (C 字符串指针)
- 空字符串 → 0 (None),传入 ptr 目标自动隐式转换
"""
if not isinstance(node.value, str):
return None
s: str = node.value
# 空字符串 → 0 (None)
if len(s) == 0:
try:
return ir.Constant(target_type, 0)
except Exception:
return None
# 获取源代码以判断引号类型
quote_char: str | None = None
is_triple: bool = False
col_offset: int = getattr(node, 'col_offset', 0)
node_lineno: int = getattr(node, 'lineno', 0)
# AST 的 lineno 可能不准确(常量节点可能指向下一行),
# 因此在 lineno 和 lineno-1 两行中查找引号
for try_lineno in (node_lineno, node_lineno - 1):
source_line: str | None = Gen._get_source_line(try_lineno)
if source_line and col_offset < len(source_line):
ch: str = source_line[col_offset]
if ch in ("'", '"'):
quote_char = ch
if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'):
is_triple = True
break
# 判断是否为 char (i8):单引号、非三重、单字符
is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1)
if is_char:
# 单引号单字符 → i8
if isinstance(target_type, ir.IntType):
try:
return ir.Constant(target_type, ord(s))
except Exception:
return None
# 目标类型不是整数 → 类型不匹配
return None
else:
# 双引号/多字符/三重 → i8* (C 字符串)
if isinstance(target_type, ir.PointerType):
str_val: str = s + '\x00'
str_bytes: bytes = str_val.encode('utf-8')
arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes))
gv_name: str = f"str_const_{id(node)}"
gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
gv.linkage = 'internal'
return ir.Constant(target_type, gv.get_reference())
# 目标类型不是指针 → 类型不匹配
return None
def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
"""统一的标量常量构建方法。
合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性:
- 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False)
- 字符串处理委托给 _BuildStringLiteralConstant
- 显式拒绝 BaseStructType
- 全部 try/except 保护
"""
# 显式拒绝结构体类型
if isinstance(target_type, ir.BaseStructType):
return None
# 处理常量节点
if isinstance(node, ast.Constant):
# bool 必须在 int 之前判断(bool 是 int 的子类)
if isinstance(node.value, bool):
try:
return ir.Constant(target_type, 1 if node.value else 0)
except Exception:
return None
elif isinstance(node.value, int):
try:
return ir.Constant(target_type, node.value)
except Exception:
return None
elif isinstance(node.value, float):
try:
return ir.Constant(target_type, node.value)
except Exception:
return None
elif isinstance(node.value, str):
# 字符串处理委托给 _BuildStringLiteralConstant
const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen)
if const is not None:
return const
# 回退:直接创建字符串全局变量(i8*)
try:
return Gen._create_string_global(node.value)
except Exception:
return None
# 处理一元负号
elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int):
try:
return ir.Constant(target_type, -node.operand.value)
except Exception:
return None
# 处理 True/False 名称节点
elif isinstance(node, ast.Name):
if node.id == 'True':
try:
return ir.Constant(target_type, 1)
except Exception:
return None
elif node.id == 'False':
try:
if isinstance(target_type, ir.PointerType):
return ir.Constant(target_type, None)
return ir.Constant(target_type, 0)
except Exception:
return None
return None
def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None:
if isinstance(node, ast.Call):
func_name: str | None = None
if isinstance(node.func, ast.Name):
func_name = node.func.id
elif isinstance(node.func, ast.Attribute):
func_name = node.func.attr
if func_name:
arg_vals: list[int | float | str | None] = []
for arg in node.args:
v: int | float | str | None = self._try_eval_const_expr(arg, Gen)
if v is None:
return None
arg_vals.append(v)
if func_name == 'COLOR_RGB' and len(arg_vals) == 3:
r: int | float | str | None
g: int | float | str | None
b: int | float | str | None
r, g, b = arg_vals
return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r)
if func_name in Gen._DefineConstants:
return Gen._DefineConstants[func_name]
for key in Gen.functions:
if key.endswith(f'__{func_name}'):
return None
return None
return self._try_eval_const_expr(node, Gen)
def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None:
return ConstEvaluator.eval_with_symtab(node, self.SymbolTable)
def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None:
inner_type: ir.Type = ArrayType.element
count: int = ArrayType.count
elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else []
_zv: ir.Constant | None = Gen._zero_value_for_type(inner_type)
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined)
constants: list[ir.Constant] = []
for elt in elts:
if isinstance(inner_type, ir.ArrayType):
if isinstance(elt, (ast.List, ast.Set)):
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen)
if inner_consts:
constants.append(ir.Constant(inner_type, inner_consts))
else:
constants.append(_fallback)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Constant):
const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.UnaryOp):
const = self._BuildScalarConstantUnified(elt, inner_type, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < count:
constants.append(_fallback)
return constants[:count]
from __future__ import annotations
import vast as ast
from typing import TYPE_CHECKING
import llvmlite.ir as ir
from lib.core.ConstEvaluator import ConstEvaluator
if TYPE_CHECKING:
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
class ConstEvalMixin:
"""常量表达式求值 Mixin
提供数组初始化常量构建与常量表达式求值能力。
"""
@staticmethod
def _bswap_constant_value(val: int, width: int) -> int:
"""编译期字节序交换"""
if width == 16:
return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)
elif width == 32:
return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF)
elif width == 64:
return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF)
return val
def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None:
_zv: ir.Constant | None = Gen._zero_value_for_type(ElemType)
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
if not isinstance(value_node, (ast.List, ast.Set)):
return None
# 显式空元素判据(与 VPC AssignGenMixin._init_global_array_from_list 的裸空指针
# 判据两侧一致):Python 侧的空 AST 元素就是 None(VPC 侧是裸 0)。列表元素计数
# 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值;
# 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。
for _gi, _elt in enumerate(value_node.elts):
if _elt is None:
raise TypeError(
f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。'
f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);'
f'拒绝静默补零生成残缺初始化,已终止编译。'
f'位置 line {getattr(value_node, "lineno", 0)}'
f':{getattr(value_node, "col_offset", 0)}'
)
if isinstance(ElemType, ir.ArrayType):
constants: list[ir.Constant] = []
for elt in value_node.elts:
if isinstance(elt, (ast.List, ast.Set)):
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen)
if inner_consts:
try:
constants.append(ir.Constant(ElemType, inner_consts))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < ArrayCount:
constants.append(_fallback)
return constants[:ArrayCount]
StructName: str | None = None
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
StructName = elem_type_node.id
constants = []
for elt in value_node.elts:
if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName:
const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Constant):
# 字符串常量:根据引号类型决定 i8 或 i8*
if isinstance(elt.value, str):
const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
else:
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.UnaryOp):
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr':
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int):
try:
constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord':
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1:
try:
constants.append(ir.Constant(ElemType, ord(elt.args[0].value)))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
else:
const_val: int | float | str | None = self._try_eval_const_call(elt, Gen)
if const_val is not None:
try:
constants.append(ir.Constant(ElemType, const_val))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < ArrayCount:
constants.append(_fallback)
if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64):
swapped: list[ir.Constant] = []
for c in constants:
if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int):
swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width)
swapped.append(ir.Constant(ElemType, swapped_val))
else:
swapped.append(c)
constants = swapped
return constants[:ArrayCount]
def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
"""根据引号类型决定字符串常量的类型:
- 单引号单字符 → i8 (char 值)
- 双引号/单引号多字符/三重引号 → i8* (C 字符串指针)
- 空字符串 → 0 (None),传入 ptr 目标自动隐式转换
"""
if not isinstance(node.value, str):
return None
s: str = node.value
# 空字符串 → 0 (None)
if len(s) == 0:
try:
return ir.Constant(target_type, 0)
except Exception:
return None
# 获取源代码以判断引号类型
quote_char: str | None = None
is_triple: bool = False
col_offset: int = getattr(node, 'col_offset', 0)
node_lineno: int = getattr(node, 'lineno', 0)
# AST 的 lineno 可能不准确(常量节点可能指向下一行),
# 因此在 lineno 和 lineno-1 两行中查找引号
for try_lineno in (node_lineno, node_lineno - 1):
source_line: str | None = Gen._get_source_line(try_lineno)
if source_line and col_offset < len(source_line):
ch: str = source_line[col_offset]
if ch in ("'", '"'):
quote_char = ch
if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'):
is_triple = True
break
# 判断是否为 char (i8):单引号、非三重、单字符
is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1)
if is_char:
# 单引号单字符 → i8
if isinstance(target_type, ir.IntType):
try:
return ir.Constant(target_type, ord(s))
except Exception:
return None
# 目标类型不是整数 → 类型不匹配
return None
else:
# 双引号/多字符/三重 → i8* (C 字符串)
if isinstance(target_type, ir.PointerType):
str_val: str = s + '\x00'
str_bytes: bytes = str_val.encode('utf-8')
arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes))
gv_name: str = f"str_const_{id(node)}"
gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
gv.linkage = 'internal'
return ir.Constant(target_type, gv.get_reference())
# 目标类型不是指针 → 类型不匹配
return None
def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
"""统一的标量常量构建方法。
合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性:
- 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False)
- 字符串处理委托给 _BuildStringLiteralConstant
- 显式拒绝 BaseStructType
- 全部 try/except 保护
"""
# 显式拒绝结构体类型
if isinstance(target_type, ir.BaseStructType):
return None
# 处理常量节点
if isinstance(node, ast.Constant):
# bool 必须在 int 之前判断(bool 是 int 的子类)
if isinstance(node.value, bool):
try:
return ir.Constant(target_type, 1 if node.value else 0)
except Exception:
return None
elif isinstance(node.value, int):
try:
return ir.Constant(target_type, node.value)
except Exception:
return None
elif isinstance(node.value, float):
try:
return ir.Constant(target_type, node.value)
except Exception:
return None
elif isinstance(node.value, str):
# 字符串处理委托给 _BuildStringLiteralConstant
const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen)
if const is not None:
return const
# 回退:直接创建字符串全局变量(i8*)
try:
return Gen._create_string_global(node.value)
except Exception:
return None
# 处理一元负号
elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int):
try:
return ir.Constant(target_type, -node.operand.value)
except Exception:
return None
# 处理 True/False 名称节点
elif isinstance(node, ast.Name):
if node.id == 'True':
try:
return ir.Constant(target_type, 1)
except Exception:
return None
elif node.id == 'False':
try:
if isinstance(target_type, ir.PointerType):
return ir.Constant(target_type, None)
return ir.Constant(target_type, 0)
except Exception:
return None
return None
def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None:
if isinstance(node, ast.Call):
func_name: str | None = None
if isinstance(node.func, ast.Name):
func_name = node.func.id
elif isinstance(node.func, ast.Attribute):
func_name = node.func.attr
if func_name:
arg_vals: list[int | float | str | None] = []
for arg in node.args:
v: int | float | str | None = self._try_eval_const_expr(arg, Gen)
if v is None:
return None
arg_vals.append(v)
if func_name == 'COLOR_RGB' and len(arg_vals) == 3:
r: int | float | str | None
g: int | float | str | None
b: int | float | str | None
r, g, b = arg_vals
return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r)
if func_name in Gen._DefineConstants:
return Gen._DefineConstants[func_name]
for key in Gen.functions:
if key.endswith(f'__{func_name}'):
return None
return None
return self._try_eval_const_expr(node, Gen)
def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None:
return ConstEvaluator.eval_with_symtab(node, self.SymbolTable)
def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None:
inner_type: ir.Type = ArrayType.element
count: int = ArrayType.count
elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else []
_zv: ir.Constant | None = Gen._zero_value_for_type(inner_type)
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined)
constants: list[ir.Constant] = []
for elt in elts:
if isinstance(inner_type, ir.ArrayType):
if isinstance(elt, (ast.List, ast.Set)):
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen)
if inner_consts:
constants.append(ir.Constant(inner_type, inner_consts))
else:
constants.append(_fallback)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Constant):
const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.UnaryOp):
const = self._BuildScalarConstantUnified(elt, inner_type, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < count:
constants.append(_fallback)
return constants[:count]
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+7 -7
View File
@@ -32,9 +32,9 @@ from lib.vast.Tokenizer import (
# novirtual → `@t.NoVTable`。本层只"合成装饰器节点",不新增第二套表示:
# - 类级:CVTable 强制有 vtable / NoVTable 强制无 vtable
# (TPC:HandlesClassDef._resolve_class_flags 读 ClassDef.decorator_list
# 的 CVTable/NoVTable;TPV:ClassGenBase._detect_vtable_status)
# 的 CVTable/NoVTable;VPC:ClassGenBase._detect_vtable_status)
# - 方法级:CVTable 进虚表槽 / NoVTable 不进虚表槽
# (TPV:ClassGenBase._should_method_be_virtual;TPC:同判据见
# (VPC:ClassGenBase._should_method_be_virtual;TPC:同判据见
# SymbolUtils.ClassVtableSlotMethods / 方法收集处)
#
# 行号守恒:合成节点的位置取"修饰符 token"的位置,ClassDef/FunctionDef 自身的
@@ -419,7 +419,7 @@ class Parser(ExprParserMixin):
用于把 typedef 的右值按**类型位置**解析(`X: t.CTypedef = const uint16*`
里的 `*` 后缀由 _ParseTypeAnnotation 消费,与注解位同一规则;描述符
`const`/`volatile` 已由 SymbolUtils._ExpandTypeDescriptors 在文本层展开)。
仅按标记名判定,不依赖 import 别名表(与 TPV `_IsCTypedefAnnotation` 同口径)。
仅按标记名判定,不依赖 import 别名表(与 VPC `_IsCTypedefAnnotation` 同口径)。
"""
if annotation is None:
return False
@@ -761,7 +761,7 @@ class Parser(ExprParserMixin):
nxt: Token = self._Peek(1)
marker: str = _VIRTUAL_MARKERS[mod.Value]
# 后随必须是 class / def;def 的别名 fn/fun/func/function 同样接受
# (与 TPV 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的
# (与 VPC 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的
# fn→def 展开只命中**行首**的 fn,`virtual fn m()` 不会被先展开,
# 故此处必须显式接受别名(否则两端对同一源码一端报错一端通过)。
if nxt.Type != NAME or (nxt.Value != 'class'
@@ -820,7 +820,7 @@ class Parser(ExprParserMixin):
def _ParseStructDef(self, decorators: list[N.expr] | None = None) -> N.ClassDef:
"""`struct X:` / `struct X(Base):` → 与 `@t.NoVTable class X(t.CStruct):`
逐字段同形的 ClassDef(与 TPV 的 _parse_structdef 同口径)。
逐字段同形的 ClassDef(与 VPC 的 _parse_structdef 同口径)。
实现:复用 _ParseClassDef 的完整解析路径(类型参数 / 基类 / 关键字实参 /
类体套件),只在**解析前后**补三件事:
@@ -974,7 +974,7 @@ class Parser(ExprParserMixin):
return node
def _ParseStaticDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef:
"""解析 static 函数定义(等价 ``@t.CStatic``,与 TransPyV 的
"""解析 static 函数定义(等价 ``@t.CStatic``,与 ViperC 的
`_ParseStaticModified` 同源)。
允许语法(与 export/extern 对称,配不配 def 都一样):
@@ -1038,7 +1038,7 @@ class Parser(ExprParserMixin):
define A(x: int): ...
产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.CDefine``
(无显式返回注解时单独 ``t.CDefine``),使 TPC/TPV 复用既有的
(无显式返回注解时单独 ``t.CDefine``),使 TPC/VPC 复用既有的
CDefine 宏函数识别路径(StubGen 原样保留生成 C #define、
alwaysinline 内联);并设自定义属性 ``Define = True`` 供
Unparser 还原 define 语法。