feat(vast): 新增自研 Viper/Python 语法解析器取代标准库 ast

TPC 侧取缔对 Python 标准库 ast 模块的依赖,改用 lib/vast 包:
- Nodes: 与 CPython 3.12 ast 完全同构的 131 个节点类(含抽象基类继承)
- Tokenizer: 词法分析(缩进/字符串/f-string/数字/运算符/软关键字)
- ExprParser: 表达式递归下降解析(优先级/lambda/推导式/f-string/切片)
- Parser: 语句解析(def/class/if/for/try/with/match/import/注解)
- Utils/Unparser: walk/dump/unparse/copy_location/literal_eval 等辅助
- 软关键字 fn/fun/func/function 并入解析器, 废除 ExpandSoftKeywords 预处理
- lib/_bootstrap 将 lib 目录加入 sys.path, import vast as ast 零修改替换
- 52 个文件 import ast 替换为 import vast as ast; 94 文件差分测试 0 失败
This commit is contained in:
TermiNexus committed 2026-08-17 09:36:38 +08:00
1 parent a7bbd68507
commit 6fc9e19e24
62 files changed
+19293 -15066

No files matched your search

+1
View File
@@ -147,6 +147,7 @@ def main() -> None:
parser.add_argument('--run', action='store_true', help='编译成功后立即执行生成的可执行文件')
parser.add_argument('--rebuild-includes', action='store_true', help='删除 includes.binary 预编译缓存并重新编译所有 includes')
parser.add_argument('--clear-cache', action='store_true', help='清除 .transpyc_cache 全局缓存(.vpi/.stub.ll/.doc.json)')
parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)')
args: argparse.Namespace = parser.parse_args()
+80 -80
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
import ast
import vast as ast
import re
from typing import List
@@ -214,85 +214,85 @@ class DeclarationGenerator:
"""递归检查类型注解中是否包含 CExport 或 t.State"""
return bool(annotation) and (AnnotationContainsName(annotation, 'CExport') or AnnotationContainsName(annotation, 'State'))
def _generate_global_decl(self, node: ast.AnnAssign) -> str | None:
"""生成全局变量声明"""
if not isinstance(node.target, ast.Name):
return None
var_name: str = node.target.id
if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CDefine':
return None
if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef':
return None
if isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef':
return None
if isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef':
return None
if isinstance(node.annotation, ast.List):
for elt in node.annotation.elts:
if isinstance(elt, ast.Attribute) and hasattr(elt, 'attr') and elt.attr in ('CTypedef', 'Callable'):
return None
if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable':
return None
VarType: str = self._get_type_str(node.annotation)
if VarType.startswith('[0 x ') and node.value and isinstance(node.value, ast.List):
elem_type: str = VarType[5:-1]
actual_count: int = len(node.value.elts)
if actual_count > 0:
VarType = f'[{actual_count} x {elem_type}]'
return f'@{var_name} = external global {VarType}'
def _generate_global_assign_decl(self, node: ast.Assign) -> str | None:
"""从无类型标注赋值推断并生成全局变量声明"""
if not node.targets or not isinstance(node.targets[0], ast.Name):
return None
var_name: str = node.targets[0].id
VarType: str | None = self._infer_type(node.value)
if VarType:
return f'@{var_name} = external global {VarType}'
return None
def _resolve_base_kind(self, base_name: str) -> str | None:
"""解析基类名称的类型种类(支持别名)
Returns:
'enum' | 'union' | 'renum' | 'struct' | 'ctype_marker' | None
"""
# 1) 查 t 别名映射(from t import CEnum as en)
if base_name in self._t_alias_map:
t_cls: type = self._t_alias_map[base_name]
if issubclass(t_cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(t_cls, t.REnum) else 'enum'
if issubclass(t_cls, t.CUnion):
return 'union'
if issubclass(t_cls, t.CStruct):
return 'struct'
# CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记,
# 不应被编译为真实结构体,也不应被生成 stub 声明。
if issubclass(t_cls, t.CType):
return 'ctype_marker'
# 2) 直接查 t 模块属性
t_cls: type | None = getattr(t, base_name, None)
if isinstance(t_cls, type) and issubclass(t_cls, t.CType):
if issubclass(t_cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(t_cls, t.REnum) else 'enum'
if issubclass(t_cls, t.CUnion):
return 'union'
if issubclass(t_cls, t.CStruct):
return 'struct'
# CType 及其子类是"类型强转"标记,不编译为结构体。
return 'ctype_marker'
return None
def _is_marker_base(self, base_name: str) -> bool:
"""检查是否为标记基类(Object/CVTable/Exception/CEnum/CUnion/CStruct/REnum 及其别名)"""
if base_name in ('Object', 'CVTable', 'Exception', 'CEnum', 'Enum', 'CStruct', 'CUnion', 'REnum'):
return True
if self._resolve_base_kind(base_name) is not None:
return True
return False
def _generate_global_decl(self, node: ast.AnnAssign) -> str | None:
"""生成全局变量声明"""
if not isinstance(node.target, ast.Name):
return None
var_name: str = node.target.id
if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CDefine':
return None
if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef':
return None
if isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef':
return None
if isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef':
return None
if isinstance(node.annotation, ast.List):
for elt in node.annotation.elts:
if isinstance(elt, ast.Attribute) and hasattr(elt, 'attr') and elt.attr in ('CTypedef', 'Callable'):
return None
if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable':
return None
VarType: str = self._get_type_str(node.annotation)
if VarType.startswith('[0 x ') and node.value and isinstance(node.value, ast.List):
elem_type: str = VarType[5:-1]
actual_count: int = len(node.value.elts)
if actual_count > 0:
VarType = f'[{actual_count} x {elem_type}]'
return f'@{var_name} = external global {VarType}'
def _generate_global_assign_decl(self, node: ast.Assign) -> str | None:
"""从无类型标注赋值推断并生成全局变量声明"""
if not node.targets or not isinstance(node.targets[0], ast.Name):
return None
var_name: str = node.targets[0].id
VarType: str | None = self._infer_type(node.value)
if VarType:
return f'@{var_name} = external global {VarType}'
return None
def _resolve_base_kind(self, base_name: str) -> str | None:
"""解析基类名称的类型种类(支持别名)
Returns:
'enum' | 'union' | 'renum' | 'struct' | 'ctype_marker' | None
"""
# 1) 查 t 别名映射(from t import CEnum as en)
if base_name in self._t_alias_map:
t_cls: type = self._t_alias_map[base_name]
if issubclass(t_cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(t_cls, t.REnum) else 'enum'
if issubclass(t_cls, t.CUnion):
return 'union'
if issubclass(t_cls, t.CStruct):
return 'struct'
# CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记,
# 不应被编译为真实结构体,也不应被生成 stub 声明。
if issubclass(t_cls, t.CType):
return 'ctype_marker'
# 2) 直接查 t 模块属性
t_cls: type | None = getattr(t, base_name, None)
if isinstance(t_cls, type) and issubclass(t_cls, t.CType):
if issubclass(t_cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(t_cls, t.REnum) else 'enum'
if issubclass(t_cls, t.CUnion):
return 'union'
if issubclass(t_cls, t.CStruct):
return 'struct'
# CType 及其子类是"类型强转"标记,不编译为结构体。
return 'ctype_marker'
return None
def _is_marker_base(self, base_name: str) -> bool:
"""检查是否为标记基类(Object/CVTable/Exception/CEnum/CUnion/CStruct/REnum 及其别名)"""
if base_name in ('Object', 'CVTable', 'Exception', 'CEnum', 'Enum', 'CStruct', 'CUnion', 'REnum'):
return True
if self._resolve_base_kind(base_name) is not None:
return True
return False
def _generate_class_decl(self, node: ast.ClassDef) -> List[str]:
"""生成结构体/类声明"""
decls: list[str] = []
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import annotations
import os
import ast
import vast as ast
import json
import shutil
import traceback
+2 -2
View File
@@ -5,7 +5,7 @@ import os
import re
import shutil
import subprocess
import ast
import vast as ast
import traceback
import json
import pickle
@@ -1730,7 +1730,7 @@ class Phase2Translator:
for root, dirs, files in os.walk(includes_dir):
dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__']
for fname in files:
if fname.endswith('.vp') or fname.endswith('.py'):
if fname.endswith('.vp') or fname.endswith('.py'):
py_index.add(os.path.normcase(os.path.join(root, fname)))
return py_index
+1 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import hashlib
import os
import ast
import vast as ast
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _ConfigMode
+1 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
import sys
import os
import ast
import vast as ast
import shlex
import datetime
import tempfile
+1001 -1001
View File
File diff suppressed because it is too large. Load diff
+6 -1
View File
@@ -1 +1,6 @@
"""TransPyC 编译器核心库"""
"""TransPyC 编译器核心库
任何 ``import lib.*`` 都会先触发 _bootstrap(sys.path 设置:
项目根 + lib 目录 + lib/includes),保证 ``import vast`` 直接可用。
"""
import lib._bootstrap # noqa: F401
+3
View File
@@ -30,6 +30,9 @@ def _bootstrap() -> None:
if _ProjectRoot not in sys.path:
sys.path.insert(0, _ProjectRoot)
if _LibDir not in sys.path:
# lib 目录入 path:使 vast 包(lib/vast/)可直接 import vast
sys.path.append(_LibDir)
if _IncludesDir not in sys.path:
sys.path.append(_IncludesDir)
+652 -652
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
import ast
import vast as ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
from lib.core.VLogger import get_logger as _vlog
+59 -59
View File
@@ -1,60 +1,60 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
import ast
import llvmlite.ir as ir
class AssertHandle(BaseHandle):
def _HandleAssertLlvm(self, Node: ast.Assert) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
cond_val: ir.Value | None = self.HandleExprLlvm(Node.test)
if not cond_val:
return
if not isinstance(cond_val.type, ir.IntType) or cond_val.type.width != 1:
zero: ir.Constant = ir.Constant(cond_val.type, 0)
cond_val = Gen.builder.icmp_signed('!=', cond_val, zero, name="assert_cond")
AssertFailBB: ir.Block = Gen.func.append_basic_block(name="assert.fail")
AssertOkBB: ir.Block = Gen.func.append_basic_block(name="assert.ok")
Gen.builder.cbranch(cond_val, AssertOkBB, AssertFailBB)
Gen.builder.position_at_start(AssertFailBB)
exc_code: int = 9
exc_msg: ir.Value | None = None
if Node.msg:
if isinstance(Node.msg, ast.Constant) and isinstance(Node.msg.value, str):
exc_msg = self.HandleExprLlvm(Node.msg)
elif isinstance(Node.msg, ast.Call) and isinstance(Node.msg.func, ast.Name):
exc_code = EXCEPTION_CODE_MAP.get(Node.msg.func.id, 99)
if Node.msg.args:
first_arg: ast.expr = Node.msg.args[0]
if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str):
exc_msg = self.HandleExprLlvm(first_arg)
elif isinstance(Node.msg, ast.Name):
exc_code = EXCEPTION_CODE_MAP.get(Node.msg.id, 99)
else:
exc_msg = self.HandleExprLlvm(Node.msg)
if Gen.eh_except_block_stack:
ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0]
EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1]
exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2]
eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3]
Gen._store(ir.Constant(ir.IntType(32), exc_code), exception_code)
if exc_msg:
Gen._store(exc_msg, eh_message)
else:
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen._store(null_ptr, eh_message)
Gen.builder.branch(ExceptBB)
else:
printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
if exc_msg:
fmt: ir.Value = Gen.emit_constant("AssertionError: %s\n", 'string')
Gen.builder.call(printf_func, [fmt, exc_msg], name="assert_fail_print")
else:
fmt: ir.Value = Gen.emit_constant("AssertionError\n", 'string')
Gen.builder.call(printf_func, [fmt], name="assert_fail_print")
exit_func: ir.Function = Gen.get_or_declare_c_func('exit', ir.FunctionType(ir.VoidType(), [ir.IntType(32)]))
Gen.builder.call(exit_func, [ir.Constant(ir.IntType(32), 1)], name="call_exit")
Gen.builder.unreachable()
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
import vast as ast
import llvmlite.ir as ir
class AssertHandle(BaseHandle):
def _HandleAssertLlvm(self, Node: ast.Assert) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
cond_val: ir.Value | None = self.HandleExprLlvm(Node.test)
if not cond_val:
return
if not isinstance(cond_val.type, ir.IntType) or cond_val.type.width != 1:
zero: ir.Constant = ir.Constant(cond_val.type, 0)
cond_val = Gen.builder.icmp_signed('!=', cond_val, zero, name="assert_cond")
AssertFailBB: ir.Block = Gen.func.append_basic_block(name="assert.fail")
AssertOkBB: ir.Block = Gen.func.append_basic_block(name="assert.ok")
Gen.builder.cbranch(cond_val, AssertOkBB, AssertFailBB)
Gen.builder.position_at_start(AssertFailBB)
exc_code: int = 9
exc_msg: ir.Value | None = None
if Node.msg:
if isinstance(Node.msg, ast.Constant) and isinstance(Node.msg.value, str):
exc_msg = self.HandleExprLlvm(Node.msg)
elif isinstance(Node.msg, ast.Call) and isinstance(Node.msg.func, ast.Name):
exc_code = EXCEPTION_CODE_MAP.get(Node.msg.func.id, 99)
if Node.msg.args:
first_arg: ast.expr = Node.msg.args[0]
if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str):
exc_msg = self.HandleExprLlvm(first_arg)
elif isinstance(Node.msg, ast.Name):
exc_code = EXCEPTION_CODE_MAP.get(Node.msg.id, 99)
else:
exc_msg = self.HandleExprLlvm(Node.msg)
if Gen.eh_except_block_stack:
ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0]
EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1]
exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2]
eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3]
Gen._store(ir.Constant(ir.IntType(32), exc_code), exception_code)
if exc_msg:
Gen._store(exc_msg, eh_message)
else:
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen._store(null_ptr, eh_message)
Gen.builder.branch(ExceptBB)
else:
printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True))
if exc_msg:
fmt: ir.Value = Gen.emit_constant("AssertionError: %s\n", 'string')
Gen.builder.call(printf_func, [fmt, exc_msg], name="assert_fail_print")
else:
fmt: ir.Value = Gen.emit_constant("AssertionError\n", 'string')
Gen.builder.call(printf_func, [fmt], name="assert_fail_print")
exit_func: ir.Function = Gen.get_or_declare_c_func('exit', ir.FunctionType(ir.VoidType(), [ir.IntType(32)]))
Gen.builder.call(exit_func, [ir.Constant(ir.IntType(32), 1)], name="call_exit")
Gen.builder.unreachable()
Gen.builder.position_at_start(AssertOkBB)
File diff suppressed because it is too large. Load diff
+242 -242
View File
@@ -1,243 +1,243 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import 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__',
}
_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__',
}
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 VarName in Gen.var_const_flags:
src_info: str = Gen._get_node_info()
_vlog().error(f"不能对 const 变量 '{VarName}' 增量赋值{src_info}")
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)
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)
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)
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)
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)
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)
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")
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__',
}
_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__',
}
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 VarName in Gen.var_const_flags:
src_info: str = Gen._get_node_info()
_vlog().error(f"不能对 const 变量 '{VarName}' 增量赋值{src_info}")
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)
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)
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)
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)
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)
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)
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)
File diff suppressed because it is too large. Load diff
+76 -76
View File
@@ -1,77 +1,77 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle
import ast
import llvmlite.ir as ir
class BodyHandle(BaseHandle):
def __init__(self, translator: "Translator") -> None:
super().__init__(translator)
def HandleBodyLlvm(self, Body: list[ast.stmt]) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
for Node in Body:
if Gen.builder and Gen.builder.block.is_terminated:
break
Gen._set_node_info(Node)
try:
if isinstance(Node, ast.Expr):
self.HandleExprLlvm(Node.value)
elif isinstance(Node, ast.Return):
self.Trans.ReturnHandler._HandleReturnLlvm(Node)
elif isinstance(Node, ast.Assign):
self.Trans.AssignHandler._HandleAssignLlvm(Node)
elif isinstance(Node, ast.AugAssign):
self.Trans.AugAssignHandler._HandleAugAssignLlvm(Node)
elif isinstance(Node, ast.AnnAssign):
self.Trans.AnnAssignHandler._HandleAnnAssignLlvm(Node)
elif isinstance(Node, ast.If):
self.Trans.IfHandler._HandleIfLlvm(Node)
elif isinstance(Node, ast.For):
self.Trans.ForHandler._HandleForLlvm(Node)
elif isinstance(Node, ast.While):
self.Trans.WhileHandler._HandleWhileLlvm(Node)
elif isinstance(Node, ast.Break):
if Gen.loop_break_targets:
Gen.builder.branch(Gen.loop_break_targets[-1])
elif isinstance(Node, ast.Continue):
if Gen.loop_continue_targets:
Gen.builder.branch(Gen.loop_continue_targets[-1])
elif isinstance(Node, ast.With):
self.Trans.WithHandler._HandleWithLlvm(Node)
elif isinstance(Node, ast.ClassDef):
pass
elif getattr(ast, 'Match', None) and isinstance(Node, ast.Match):
self.Trans.MatchHandler._HandleMatchLlvm(Node)
elif isinstance(Node, ast.Try):
self.Trans.TryHandler._HandleTryLlvm(Node)
elif isinstance(Node, ast.Raise):
self.Trans.RaiseHandler._HandleRaiseLlvm(Node)
elif isinstance(Node, ast.Assert):
self.Trans.AssertHandler._HandleAssertLlvm(Node)
elif isinstance(Node, ast.Global):
self.Trans.ForHandler._HandleGlobalLlvm(Node)
elif isinstance(Node, ast.FunctionDef):
self.Trans.ForHandler._HandleNestedFunctionLlvm(Node)
elif isinstance(Node, ast.Nonlocal):
self.Trans.ForHandler._HandleNonlocalLlvm(Node)
elif isinstance(Node, ast.Delete):
self.Trans.DeleteHandler._HandleDeleteLlvm(Node)
except Exception as e:
lineno: int = getattr(Node, 'lineno', 0)
source_line: str | None = Gen._get_source_line(lineno)
src_file: str = Gen._current_source_file or ''
src_path: str = src_file if src_file else 'unknown'
line_info: str = "源代码 (%s, line %d)" % (src_path, lineno)
if source_line:
line_info += ":\n %d | %s" % (lineno, source_line)
self.Trans._ErrorStack.append((type(e).__name__, str(e), line_info))
raise
if Gen.builder and not Gen.builder.block.is_terminated:
Gen._EmitTempFrees()
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None) -> ir.Value | None:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import llvmlite.ir as ir
class BodyHandle(BaseHandle):
def __init__(self, translator: "Translator") -> None:
super().__init__(translator)
def HandleBodyLlvm(self, Body: list[ast.stmt]) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
for Node in Body:
if Gen.builder and Gen.builder.block.is_terminated:
break
Gen._set_node_info(Node)
try:
if isinstance(Node, ast.Expr):
self.HandleExprLlvm(Node.value)
elif isinstance(Node, ast.Return):
self.Trans.ReturnHandler._HandleReturnLlvm(Node)
elif isinstance(Node, ast.Assign):
self.Trans.AssignHandler._HandleAssignLlvm(Node)
elif isinstance(Node, ast.AugAssign):
self.Trans.AugAssignHandler._HandleAugAssignLlvm(Node)
elif isinstance(Node, ast.AnnAssign):
self.Trans.AnnAssignHandler._HandleAnnAssignLlvm(Node)
elif isinstance(Node, ast.If):
self.Trans.IfHandler._HandleIfLlvm(Node)
elif isinstance(Node, ast.For):
self.Trans.ForHandler._HandleForLlvm(Node)
elif isinstance(Node, ast.While):
self.Trans.WhileHandler._HandleWhileLlvm(Node)
elif isinstance(Node, ast.Break):
if Gen.loop_break_targets:
Gen.builder.branch(Gen.loop_break_targets[-1])
elif isinstance(Node, ast.Continue):
if Gen.loop_continue_targets:
Gen.builder.branch(Gen.loop_continue_targets[-1])
elif isinstance(Node, ast.With):
self.Trans.WithHandler._HandleWithLlvm(Node)
elif isinstance(Node, ast.ClassDef):
pass
elif getattr(ast, 'Match', None) and isinstance(Node, ast.Match):
self.Trans.MatchHandler._HandleMatchLlvm(Node)
elif isinstance(Node, ast.Try):
self.Trans.TryHandler._HandleTryLlvm(Node)
elif isinstance(Node, ast.Raise):
self.Trans.RaiseHandler._HandleRaiseLlvm(Node)
elif isinstance(Node, ast.Assert):
self.Trans.AssertHandler._HandleAssertLlvm(Node)
elif isinstance(Node, ast.Global):
self.Trans.ForHandler._HandleGlobalLlvm(Node)
elif isinstance(Node, ast.FunctionDef):
self.Trans.ForHandler._HandleNestedFunctionLlvm(Node)
elif isinstance(Node, ast.Nonlocal):
self.Trans.ForHandler._HandleNonlocalLlvm(Node)
elif isinstance(Node, ast.Delete):
self.Trans.DeleteHandler._HandleDeleteLlvm(Node)
except Exception as e:
lineno: int = getattr(Node, 'lineno', 0)
source_line: str | None = Gen._get_source_line(lineno)
src_file: str = Gen._current_source_file or ''
src_path: str = src_file if src_file else 'unknown'
line_info: str = "源代码 (%s, line %d)" % (src_path, lineno)
if source_line:
line_info += ":\n %d | %s" % (lineno, source_line)
self.Trans._ErrorStack.append((type(e).__name__, str(e), line_info))
raise
if Gen.builder and not Gen.builder.block.is_terminated:
Gen._EmitTempFrees()
def HandleExprLlvm(self, Node: ast.expr, VarType: ir.Type | None = None) -> ir.Value | None:
return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType)
+196 -196
View File
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
import ast
import vast as ast
import copy
import llvmlite.ir as ir
from lib.core.Exportable import EnumMember as ExportEnumMember
@@ -247,125 +247,125 @@ class ClassHandle(BaseHandle):
Gen._struct_sha1_map[spec_name] = self.Trans._module_sha1
return spec_name
def _resolve_base_kind(self, base_name: str, Gen: LlvmGeneratorMixin) -> str | None:
"""解析基类名称的类型种类
查询顺序:
1. _t_type_symbols(from t import CEnum as en)
2. t 模块属性(t.CEnum / t.CUnion / t.CStruct / t.REnum)
3. 返回 None 表示不是已知标记基类
Returns:
'enum' | 'union' | 'renum' | 'struct' | None
"""
# 1) 查 _t_type_symbols(别名导入)
symtab: object = self.Trans.SymbolTable
cls: type | None = symtab._t_type_symbols.get(base_name)
if cls is not None:
if issubclass(cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(cls, t.REnum) else 'enum'
if issubclass(cls, t.CUnion):
return 'union'
if issubclass(cls, t.CStruct):
return 'struct'
# CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记,
# 不应被编译为真实结构体。
if issubclass(cls, t.CType):
return 'ctype_marker'
# 2) 查 t 模块属性(直接引用 t.CEnum 等)
t_cls: type | None = getattr(t, base_name, None)
if isinstance(t_cls, type) and issubclass(t_cls, t.CType):
if issubclass(t_cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(t_cls, t.REnum) else 'enum'
if issubclass(t_cls, t.CUnion):
return 'union'
if issubclass(t_cls, t.CStruct):
return 'struct'
# CType 及其子类是"类型强转"标记,不编译为结构体。
return 'ctype_marker'
return None
@staticmethod
def _is_classname_ctype_marker(class_name: str) -> bool:
"""检查类名是否是 t.CType 的"类型强转"标记子类。
只有 position 不含 BASE 的纯修饰符/标记类型(CPtr/CVolatile/CConst/
CInline/CStatic/CExtern/CExport/CRegister/CAuto/_CTypedef/CTypeDefault 等)
才应跳过编译。有 BASE position 的基本类型(CInt/CChar/CSizeT/CLong 等)
有具体大小,应被正常编译。CType 基类本身无 bases,base 检测无法触发,
需要直接按类名检查。
注意:CTypeRegistry.GetClassByName 会跳过 CType 本身和 CTypeDefault,
所以这里直接用 getattr(t, name) 检查 t 模块属性。
Returns:
True 如果 class_name 是 CType 标记子类名(应跳过编译)
"""
if not class_name:
return False
# 直接查 t 模块属性(CTypeRegistry._build 会跳过 CType/CTypeDefault/BigEndian/LittleEndian)
t_cls: type | None = getattr(t, class_name, None)
if not isinstance(t_cls, type) or not issubclass(t_cls, t.CType):
return False
if issubclass(t_cls, (t.CStruct, t.CEnum, t.REnum, t.CUnion)):
return False
# 基本类型(position 含 BASE 且 Size > 0)有具体大小,不应跳过
# CVoid 虽含 BASE 但 Size=0,CType Size=None,仍需跳过
if t.CType.BASE in t_cls.position:
try:
inst = t_cls()
size_val = getattr(inst, 'Size', None)
if size_val is not None and size_val > 0:
return False
except Exception:
pass
return True
def _resolve_decorator_kind(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> str | None:
"""从装饰器列表中解析结构体类型种类
支持 @t.CStruct, @t.CUnion, @t.CEnum, @t.REnum
也支持 from t import CStruct 等别名形式
Returns:
'enum' | 'union' | 'renum' | 'struct' | None
"""
if not hasattr(Node, 'decorator_list') or not Node.decorator_list:
return None
symtab: object = self.Trans.SymbolTable
for decorator in Node.decorator_list:
attr_name: str | None = None
# @t.CStruct 形式
if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name):
if IsTModule(decorator.value.id, symtab):
attr_name = decorator.attr
# @CStruct 形式 (from t import CStruct)
elif isinstance(decorator, ast.Name):
cls: type | None = getattr(t, decorator.id, None)
if cls is None and hasattr(symtab, '_t_type_symbols'):
cls = symtab._t_type_symbols.get(decorator.id)
if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType):
attr_name = cls.__name__
if attr_name:
# 查 _t_type_symbols 和 t 模块属性以确定种类
resolved_cls: type | None = None
if hasattr(symtab, '_t_type_symbols'):
resolved_cls = symtab._t_type_symbols.get(attr_name)
if resolved_cls is None:
resolved_cls = getattr(t, attr_name, None)
if resolved_cls is not None and isinstance(resolved_cls, type):
if issubclass(resolved_cls, t.REnum):
return 'renum'
if issubclass(resolved_cls, t.CEnum):
return 'enum'
if issubclass(resolved_cls, t.CUnion):
return 'union'
if issubclass(resolved_cls, t.CStruct):
return 'struct'
return None
def _resolve_base_kind(self, base_name: str, Gen: LlvmGeneratorMixin) -> str | None:
"""解析基类名称的类型种类
查询顺序:
1. _t_type_symbols(from t import CEnum as en)
2. t 模块属性(t.CEnum / t.CUnion / t.CStruct / t.REnum)
3. 返回 None 表示不是已知标记基类
Returns:
'enum' | 'union' | 'renum' | 'struct' | None
"""
# 1) 查 _t_type_symbols(别名导入)
symtab: object = self.Trans.SymbolTable
cls: type | None = symtab._t_type_symbols.get(base_name)
if cls is not None:
if issubclass(cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(cls, t.REnum) else 'enum'
if issubclass(cls, t.CUnion):
return 'union'
if issubclass(cls, t.CStruct):
return 'struct'
# CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记,
# 不应被编译为真实结构体。
if issubclass(cls, t.CType):
return 'ctype_marker'
# 2) 查 t 模块属性(直接引用 t.CEnum 等)
t_cls: type | None = getattr(t, base_name, None)
if isinstance(t_cls, type) and issubclass(t_cls, t.CType):
if issubclass(t_cls, (t.CEnum, t.REnum)):
return 'renum' if issubclass(t_cls, t.REnum) else 'enum'
if issubclass(t_cls, t.CUnion):
return 'union'
if issubclass(t_cls, t.CStruct):
return 'struct'
# CType 及其子类是"类型强转"标记,不编译为结构体。
return 'ctype_marker'
return None
@staticmethod
def _is_classname_ctype_marker(class_name: str) -> bool:
"""检查类名是否是 t.CType 的"类型强转"标记子类。
只有 position 不含 BASE 的纯修饰符/标记类型(CPtr/CVolatile/CConst/
CInline/CStatic/CExtern/CExport/CRegister/CAuto/_CTypedef/CTypeDefault 等)
才应跳过编译。有 BASE position 的基本类型(CInt/CChar/CSizeT/CLong 等)
有具体大小,应被正常编译。CType 基类本身无 bases,base 检测无法触发,
需要直接按类名检查。
注意:CTypeRegistry.GetClassByName 会跳过 CType 本身和 CTypeDefault,
所以这里直接用 getattr(t, name) 检查 t 模块属性。
Returns:
True 如果 class_name 是 CType 标记子类名(应跳过编译)
"""
if not class_name:
return False
# 直接查 t 模块属性(CTypeRegistry._build 会跳过 CType/CTypeDefault/BigEndian/LittleEndian)
t_cls: type | None = getattr(t, class_name, None)
if not isinstance(t_cls, type) or not issubclass(t_cls, t.CType):
return False
if issubclass(t_cls, (t.CStruct, t.CEnum, t.REnum, t.CUnion)):
return False
# 基本类型(position 含 BASE 且 Size > 0)有具体大小,不应跳过
# CVoid 虽含 BASE 但 Size=0,CType Size=None,仍需跳过
if t.CType.BASE in t_cls.position:
try:
inst = t_cls()
size_val = getattr(inst, 'Size', None)
if size_val is not None and size_val > 0:
return False
except Exception:
pass
return True
def _resolve_decorator_kind(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> str | None:
"""从装饰器列表中解析结构体类型种类
支持 @t.CStruct, @t.CUnion, @t.CEnum, @t.REnum
也支持 from t import CStruct 等别名形式
Returns:
'enum' | 'union' | 'renum' | 'struct' | None
"""
if not hasattr(Node, 'decorator_list') or not Node.decorator_list:
return None
symtab: object = self.Trans.SymbolTable
for decorator in Node.decorator_list:
attr_name: str | None = None
# @t.CStruct 形式
if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name):
if IsTModule(decorator.value.id, symtab):
attr_name = decorator.attr
# @CStruct 形式 (from t import CStruct)
elif isinstance(decorator, ast.Name):
cls: type | None = getattr(t, decorator.id, None)
if cls is None and hasattr(symtab, '_t_type_symbols'):
cls = symtab._t_type_symbols.get(decorator.id)
if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType):
attr_name = cls.__name__
if attr_name:
# 查 _t_type_symbols 和 t 模块属性以确定种类
resolved_cls: type | None = None
if hasattr(symtab, '_t_type_symbols'):
resolved_cls = symtab._t_type_symbols.get(attr_name)
if resolved_cls is None:
resolved_cls = getattr(t, attr_name, None)
if resolved_cls is not None and isinstance(resolved_cls, type):
if issubclass(resolved_cls, t.REnum):
return 'renum'
if issubclass(resolved_cls, t.CEnum):
return 'enum'
if issubclass(resolved_cls, t.CUnion):
return 'union'
if issubclass(resolved_cls, t.CStruct):
return 'struct'
return None
def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool]:
"""解析类的 Object/CVTable 标志和父类信息
@@ -467,8 +467,8 @@ class ClassHandle(BaseHandle):
if IsCVTable:
Gen.class_vtable.add(ClassName)
# 沿继承链向上传播,将所有祖先类加入 vtable
visited: set[str] = {ClassName}
ancestor: str | None = ParentClassName
visited: set[str] = {ClassName}
ancestor: str | None = ParentClassName
while ancestor and ancestor not in visited:
visited.add(ancestor)
Gen.class_vtable.add(ancestor)
@@ -550,44 +550,44 @@ class ClassHandle(BaseHandle):
if IsRenum:
self._EmitREnumLlvm(Node, Gen)
return
IsCpythonObject: bool
IsCVTable: bool
ParentClassName: str | None
IsNoVTable: bool
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable = self._resolve_class_flags(Node, Gen)
# 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body
Gen._classes_in_progress.add(ClassName)
# 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class)
# 和 CollectAnnAssignMember 触发的嵌套 _generate_structs 用不完整 class_members 调用 set_body
Gen._collecting_members_set.add(ClassName)
if ClassName not in Gen.class_methods:
Gen.class_methods[ClassName] = []
else:
# 防御性清除:移除导入阶段可能添加的构造函数(__new__/__init__/__before_init__)。
# 构造函数不参与虚分派,放入 vtable 会导致子类与基类 vtable 大小不一致,
# 索引偏移破坏多态分派(如 Module 有 __new__/__init__ 使 vtable=7,而 AST 无它们 vtable=5,
# 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。
Gen.class_methods[ClassName] = [
m for m in Gen.class_methods[ClassName]
if m.split('.')[-1] not in ('__new__', '__init__', '__before_init__')
]
# 当前文件定义的类是权威定义,覆盖之前导入阶段可能添加的外部声明。
# 关键:如果本地类是 @t.NoVTable 但同名导入类有 vtable(名称冲突),
# 必须清除 vtable 状态,否则 _generate_structs 会错误添加 vtable 指针。
if IsNoVTable:
Gen.class_vtable.discard(ClassName)
Gen._cross_module_vtable_classes.discard(ClassName)
Gen._cross_module_novtable.add(ClassName)
# 清除可能从导入 stub 加载的旧 struct body(含错误 vtable 指针),
# 让 _generate_structs 用本地 class_members 重新生成
_old_st = Gen.structs.get(ClassName)
if isinstance(_old_st, ir.IdentifiedStructType) and _old_st.elements is not None:
_old_st._elements = None
_ih = getattr(Gen, '_import_handler_ref', None)
if _ih is not None:
_ck = (id(Gen), ClassName)
if _ck in _ih._struct_Load_cache:
del _ih._struct_Load_cache[_ck]
IsCpythonObject: bool
IsCVTable: bool
ParentClassName: str | None
IsNoVTable: bool
IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable = self._resolve_class_flags(Node, Gen)
# 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body
Gen._classes_in_progress.add(ClassName)
# 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class)
# 和 CollectAnnAssignMember 触发的嵌套 _generate_structs 用不完整 class_members 调用 set_body
Gen._collecting_members_set.add(ClassName)
if ClassName not in Gen.class_methods:
Gen.class_methods[ClassName] = []
else:
# 防御性清除:移除导入阶段可能添加的构造函数(__new__/__init__/__before_init__)。
# 构造函数不参与虚分派,放入 vtable 会导致子类与基类 vtable 大小不一致,
# 索引偏移破坏多态分派(如 Module 有 __new__/__init__ 使 vtable=7,而 AST 无它们 vtable=5,
# 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。
Gen.class_methods[ClassName] = [
m for m in Gen.class_methods[ClassName]
if m.split('.')[-1] not in ('__new__', '__init__', '__before_init__')
]
# 当前文件定义的类是权威定义,覆盖之前导入阶段可能添加的外部声明。
# 关键:如果本地类是 @t.NoVTable 但同名导入类有 vtable(名称冲突),
# 必须清除 vtable 状态,否则 _generate_structs 会错误添加 vtable 指针。
if IsNoVTable:
Gen.class_vtable.discard(ClassName)
Gen._cross_module_vtable_classes.discard(ClassName)
Gen._cross_module_novtable.add(ClassName)
# 清除可能从导入 stub 加载的旧 struct body(含错误 vtable 指针),
# 让 _generate_structs 用本地 class_members 重新生成
_old_st = Gen.structs.get(ClassName)
if isinstance(_old_st, ir.IdentifiedStructType) and _old_st.elements is not None:
_old_st._elements = None
_ih = getattr(Gen, '_import_handler_ref', None)
if _ih is not None:
_ck = (id(Gen), ClassName)
if _ck in _ih._struct_Load_cache:
del _ih._struct_Load_cache[_ck]
Gen.class_members[ClassName] = []
Gen.class_member_defaults[ClassName] = {}
Gen.class_member_signeds[ClassName] = {}
@@ -707,13 +707,13 @@ class ClassHandle(BaseHandle):
if method_name not in covered_names:
new_methods.append(m)
Gen.class_methods[ClassName] = new_methods
# 提取首个裸字符串字面量作为结构体 __doc__ docstring
if (Node.body and isinstance(Node.body[0], ast.Expr)
and isinstance(Node.body[0].value, ast.Constant)
and isinstance(Node.body[0].value.value, str)):
Gen._create_doc_global(ClassName, Node.body[0].value.value)
else:
Gen._create_doc_global(ClassName, None)
# 提取首个裸字符串字面量作为结构体 __doc__ docstring
if (Node.body and isinstance(Node.body[0], ast.Expr)
and isinstance(Node.body[0].value, ast.Constant)
and isinstance(Node.body[0].value.value, str)):
Gen._create_doc_global(ClassName, Node.body[0].value.value)
else:
Gen._create_doc_global(ClassName, None)
# 解析 __provides__/__requires__/__require_must__ 编译期元数据
for item in Node.body:
if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name):
@@ -1081,35 +1081,35 @@ class ClassHandle(BaseHandle):
continue
self._scan_node_for_self_assign(child, ClassName, existing_members, Gen)
def _EmitUnionLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None:
ClassName: str = Node.name
union_member_types: list[ir.Type] = []
for item in Node.body:
if isinstance(item, ast.ClassDef):
nested_class_name: str = item.name
nested_member_types: list[ir.Type] = []
for nested_item in item.body:
parsed = self.CollectAnnAssignMember(nested_item, Gen)
if parsed:
nested_member_types.append(parsed[1])
if nested_member_types:
nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, f"{ClassName}_{nested_class_name}")
nested_struct_type.set_body(*nested_member_types)
Gen.structs[f"{ClassName}_{nested_class_name}"] = nested_struct_type
union_member_types.append(nested_struct_type)
nested_class_full_name: str = f"{ClassName}_{nested_class_name}"
if nested_class_full_name not in Gen.class_members:
Gen.class_members[nested_class_full_name] = []
if nested_class_full_name not in Gen.class_member_signeds:
Gen.class_member_signeds[nested_class_full_name] = {}
for nested_item in item.body:
parsed = self.CollectAnnAssignMember(nested_item, Gen)
if parsed:
VarName, MemberType, TypeInfo = parsed
Gen.class_members[nested_class_full_name].append((VarName, MemberType))
if TypeInfo and hasattr(TypeInfo.BaseType, 'IsSigned'):
Gen.class_member_signeds[nested_class_full_name][VarName] = TypeInfo.BaseType.IsSigned
else:
def _EmitUnionLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None:
ClassName: str = Node.name
union_member_types: list[ir.Type] = []
for item in Node.body:
if isinstance(item, ast.ClassDef):
nested_class_name: str = item.name
nested_member_types: list[ir.Type] = []
for nested_item in item.body:
parsed = self.CollectAnnAssignMember(nested_item, Gen)
if parsed:
nested_member_types.append(parsed[1])
if nested_member_types:
nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, f"{ClassName}_{nested_class_name}")
nested_struct_type.set_body(*nested_member_types)
Gen.structs[f"{ClassName}_{nested_class_name}"] = nested_struct_type
union_member_types.append(nested_struct_type)
nested_class_full_name: str = f"{ClassName}_{nested_class_name}"
if nested_class_full_name not in Gen.class_members:
Gen.class_members[nested_class_full_name] = []
if nested_class_full_name not in Gen.class_member_signeds:
Gen.class_member_signeds[nested_class_full_name] = {}
for nested_item in item.body:
parsed = self.CollectAnnAssignMember(nested_item, Gen)
if parsed:
VarName, MemberType, TypeInfo = parsed
Gen.class_members[nested_class_full_name].append((VarName, MemberType))
if TypeInfo and hasattr(TypeInfo.BaseType, 'IsSigned'):
Gen.class_member_signeds[nested_class_full_name][VarName] = TypeInfo.BaseType.IsSigned
else:
Gen.class_member_signeds[nested_class_full_name][VarName] = None
if union_member_types:
max_size: int = 0
+101 -101
View File
@@ -1,102 +1,102 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta
import ast
import llvmlite.ir as ir
class DeleteHandle(BaseHandle):
def _HandleDeleteLlvm(self, Node: ast.Delete) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
for target in Node.targets:
if isinstance(target, ast.Subscript):
ClassName: str | None = self.Trans.ExprHandler._get_var_class(target.value, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__delitem__'):
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.value)
idx_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.slice)
if obj_val and idx_val:
# 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8
if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8:
obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
Gen.builder.call(Gen._get_function(f'{ClassName}.__delitem__'), [obj_val, idx_val], name=f"call_{ClassName}.__delitem__")
elif isinstance(target, ast.Name):
ClassName: str | None = Gen.var_struct_class.get(target.id)
if ClassName and Gen._has_function(f'{ClassName}.__delete__'):
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target)
if obj_val:
# 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8
if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8:
obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
Gen.builder.call(Gen._get_function(f'{ClassName}.__delete__'), [obj_val], name=f"call_{ClassName}.__delete__")
elif isinstance(target, ast.Attribute):
# 检查 property deleter
AttrName: str = target.attr
ClassName: str | None = None
obj_val_for_prop: ir.Value | None = None
if isinstance(target.value, ast.Name) and target.value.id == 'self':
# del self.attr — 检查当前类的 property deleter
ClassName = self.Trans._CurrentCpythonObjectClass
if ClassName:
PropKey: str = f'{ClassName}.{AttrName}'
PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey)
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_DELETER in PropInfo.MetaList:
SelfVar: ir.Value | None = Gen._get_var_ptr('self')
if SelfVar:
SelfPtr: ir.Value = Gen._load(SelfVar, name="self")
DeleterFunc: ir.Function | None = Gen._get_function(PropKey + '$del')
if DeleterFunc and SelfPtr:
Gen.builder.call(DeleterFunc, [SelfPtr], name=f"prop_del_{PropKey}")
continue
else:
# del obj.attr — 检查 obj 类的 property deleter
ClassName = self.Trans.ExprHandler._get_var_class(target.value, Gen)
if ClassName:
PropKey: str = f'{ClassName}.{AttrName}'
PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey)
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_DELETER in PropInfo.MetaList:
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.value)
if obj_val:
DeleterFunc: ir.Function | None = Gen._get_function(PropKey + '$del')
if DeleterFunc:
Gen.builder.call(DeleterFunc, [obj_val], name=f"prop_del_{PropKey}")
continue
# 原有的 __del__ 处理逻辑
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target)
if obj_val:
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.PointerType):
obj_val = Gen._load(obj_val, name="Load_del_ptr")
ClassName = None
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
for CN, ST in Gen.structs.items():
if obj_val.type.pointee == ST:
ClassName = CN
break
elif isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
if isinstance(target.value, ast.Name) and target.value.id == 'self':
AttrName = target.attr
SelfClassName: str | None = self.Trans._CurrentCpythonObjectClass
if SelfClassName and SelfClassName in Gen.class_members:
for member_name, member_type in Gen.class_members[SelfClassName]:
if member_name == AttrName:
for CN in Gen.structs:
if CN == AttrName or Gen._has_function(f'{CN}.__del__'):
ClassName = CN
break
break
if not ClassName:
for CN, ST in Gen.structs.items():
if Gen._has_function(f'{CN}.__del__'):
ClassName = CN
break
if ClassName and Gen._has_function(f'{ClassName}.__del__'):
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta
import vast as ast
import llvmlite.ir as ir
class DeleteHandle(BaseHandle):
def _HandleDeleteLlvm(self, Node: ast.Delete) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
for target in Node.targets:
if isinstance(target, ast.Subscript):
ClassName: str | None = self.Trans.ExprHandler._get_var_class(target.value, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__delitem__'):
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.value)
idx_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.slice)
if obj_val and idx_val:
# 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8
if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8:
obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
Gen.builder.call(Gen._get_function(f'{ClassName}.__delitem__'), [obj_val, idx_val], name=f"call_{ClassName}.__delitem__")
elif isinstance(target, ast.Name):
ClassName: str | None = Gen.var_struct_class.get(target.id)
if ClassName and Gen._has_function(f'{ClassName}.__delete__'):
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target)
if obj_val:
# 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8
if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8:
obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
Gen.builder.call(Gen._get_function(f'{ClassName}.__delete__'), [obj_val], name=f"call_{ClassName}.__delete__")
elif isinstance(target, ast.Attribute):
# 检查 property deleter
AttrName: str = target.attr
ClassName: str | None = None
obj_val_for_prop: ir.Value | None = None
if isinstance(target.value, ast.Name) and target.value.id == 'self':
# del self.attr — 检查当前类的 property deleter
ClassName = self.Trans._CurrentCpythonObjectClass
if ClassName:
PropKey: str = f'{ClassName}.{AttrName}'
PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey)
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_DELETER in PropInfo.MetaList:
SelfVar: ir.Value | None = Gen._get_var_ptr('self')
if SelfVar:
SelfPtr: ir.Value = Gen._load(SelfVar, name="self")
DeleterFunc: ir.Function | None = Gen._get_function(PropKey + '$del')
if DeleterFunc and SelfPtr:
Gen.builder.call(DeleterFunc, [SelfPtr], name=f"prop_del_{PropKey}")
continue
else:
# del obj.attr — 检查 obj 类的 property deleter
ClassName = self.Trans.ExprHandler._get_var_class(target.value, Gen)
if ClassName:
PropKey: str = f'{ClassName}.{AttrName}'
PropInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(PropKey)
if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_DELETER in PropInfo.MetaList:
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target.value)
if obj_val:
DeleterFunc: ir.Function | None = Gen._get_function(PropKey + '$del')
if DeleterFunc:
Gen.builder.call(DeleterFunc, [obj_val], name=f"prop_del_{PropKey}")
continue
# 原有的 __del__ 处理逻辑
obj_val: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(target)
if obj_val:
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.PointerType):
obj_val = Gen._load(obj_val, name="Load_del_ptr")
ClassName = None
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
for CN, ST in Gen.structs.items():
if obj_val.type.pointee == ST:
ClassName = CN
break
elif isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
if isinstance(target.value, ast.Name) and target.value.id == 'self':
AttrName = target.attr
SelfClassName: str | None = self.Trans._CurrentCpythonObjectClass
if SelfClassName and SelfClassName in Gen.class_members:
for member_name, member_type in Gen.class_members[SelfClassName]:
if member_name == AttrName:
for CN in Gen.structs:
if CN == AttrName or Gen._has_function(f'{CN}.__del__'):
ClassName = CN
break
break
if not ClassName:
for CN, ST in Gen.structs.items():
if Gen._has_function(f'{CN}.__del__'):
ClassName = CN
break
if ClassName and Gen._has_function(f'{ClassName}.__del__'):
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
Gen.builder.call(Gen._get_function(f'{ClassName}.__del__'), [obj_val], name=f"call_{ClassName}.__del__")
+1 -1
View File
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
import ast
import vast as ast
import re
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import ast
import vast as ast
import re
import llvmlite.ir as ir
if TYPE_CHECKING:
+1 -1
View File
@@ -4,7 +4,7 @@ if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta
import ast
import vast as ast
import llvmlite.ir as ir
+1 -1
View File
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
import ast
import vast as ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle
from lib.core.VLogger import get_logger as _vlog
+156 -156
View File
@@ -1,31 +1,31 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
import ast
import os
import re
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.includes import t
from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import IsTModule, ExtractTypeNameFromBinOp
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
"""LLVM IR 中不允许 void* 指针,Ptr(VoidType) 统一替换为 i8*(对应 C 的 void* 语义)"""
if isinstance(ty, ir.PointerType):
cur: ir.Type = ty
while isinstance(cur, ir.PointerType):
if isinstance(cur.pointee, ir.VoidType):
return ir.IntType(8).as_pointer()
cur = cur.pointee
return ty
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
import vast as ast
import os
import re
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.VLogger import get_logger as _vlog
from lib.constants.config import mode as _config_mode
from lib.includes import t
from lib.includes.t import CTypeRegistry
from lib.core.SymbolUtils import IsTModule, ExtractTypeNameFromBinOp
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
"""LLVM IR 中不允许 void* 指针,Ptr(VoidType) 统一替换为 i8*(对应 C 的 void* 语义)"""
if isinstance(ty, ir.PointerType):
cur: ir.Type = ty
while isinstance(cur, ir.PointerType):
if isinstance(cur.pointee, ir.VoidType):
return ir.IntType(8).as_pointer()
cur = cur.pointee
return ty
class ExprCallHandle(BaseHandle):
def _append_eh_msg_out_arg(self, CallArgs: list[ir.Value], func: ir.Function | None, Gen: LlvmGeneratorMixin) -> None:
if not func:
@@ -929,15 +929,15 @@ class ExprCallHandle(BaseHandle):
ArgVal = self.HandleExprLlvm(arg)
if ArgVal:
CallArgs.append(ArgVal)
if (ClassName not in Gen.class_vtable
and ClassName not in Gen._cross_module_vtable_classes
and class_sha1):
self._try_register_vtable_ondemand(ClassName, class_sha1, Gen)
# 跨模块多继承:确保 class_parents 可用(MRO 回退需要)。
# 如 HandlesType 委托调用 Generator 的方法——Generator 定义在
# 另一模块,class_parents 未随 import 填充,需从 vpi 按需加载。
self._EnsureClassParentsLoaded(ClassName, Gen)
if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods and CallArgs and not is_static_call:
if (ClassName not in Gen.class_vtable
and ClassName not in Gen._cross_module_vtable_classes
and class_sha1):
self._try_register_vtable_ondemand(ClassName, class_sha1, Gen)
# 跨模块多继承:确保 class_parents 可用(MRO 回退需要)。
# 如 HandlesType 委托调用 Generator 的方法——Generator 定义在
# 另一模块,class_parents 未随 import 填充,需从 vpi 按需加载。
self._EnsureClassParentsLoaded(ClassName, Gen)
if (ClassName in Gen.class_vtable or ClassName in Gen._cross_module_vtable_classes) and ClassName in Gen.class_methods and CallArgs and not is_static_call:
methods = Gen.class_methods[ClassName]
if class_sha1:
FullMethodName = f'{class_sha1}.{ClassName}.{MethodName}'
@@ -2407,125 +2407,125 @@ class ExprCallHandle(BaseHandle):
# 失败时恢复 declare_only 标记,避免后续调用跳过补发
Gen._generic_class_specializations[spec_key] = (cached_name, True)
def _compute_field_byte_offset(self, Gen: LlvmGeneratorMixin, ClassName: str, field_name: str) -> int | None:
"""计算字段在 struct 中的字节偏移(按 LLVM 布局含对齐)。
遍历 struct elements 带对齐累加(与 _get_struct_size/_get_align 一致)。
有 vtable 的类 struct 首元素为 vtable 指针(i8*),class_members 不含它。
返回 None 表示无法计算(struct 未定义 / 字段不存在)。
"""
st = Gen.structs.get(ClassName)
if not st or not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
return None
members: list = Gen.class_members.get(ClassName, [])
has_vtable: bool = (ClassName in Gen.class_vtable
or ClassName in Gen._cross_module_vtable_classes)
base: int = 1 if has_vtable else 0
idx: int | None = None
for i, (name, _) in enumerate(members):
if name == field_name:
idx = base + i
break
if idx is None or idx >= len(st.elements):
return None
# ============================================================
# 修复(2026-08-13):class_members 与 struct.elements 数量不一致时对齐。
# 根因链(NewAstTest ASTCtx 枚举丢失):
# DeclarationGenerator/收集路径生成的 class_members 可能漏部分 Mixin/自动
# 字段(如 Generator 漏 GenericSpecDepth/GenericClassFiles/ISINSTANCE_CLASS_MAX
# 3 个,class_members=88 vs struct=91),导致 this_adjust 用 class_members
# 索引算错字节偏移(Generator 624 vs 真实 644)。StmtGenMixin 方法(find_enum/
# register_enum/is_enum_class)字段访问错位 20 字节 → EnumTable 落在
# ReexportSrcs 内存 → 与 reexport 数据冲突覆盖 → ASTCtx 等枚举丢失。
# 修正:字段真实 struct 索引 = class_members 索引 + (struct 元素数 - members 数)。
# 前提:遗漏字段位于目标字段之前(本 bug 场景成立:漏字段在 StmtGenMixin 前)。
# ============================================================
diff_elems: int = len(st.elements) - len(members)
if diff_elems > 0:
idx += diff_elems
if idx >= len(st.elements):
return None
offset: int = 0
for i in range(idx):
et = st.elements[i]
align: int = Gen._get_align(et)
size: int = Gen._get_struct_size(et)
if align > 1:
offset = ((offset + align - 1) // align) * align
offset += size
align = Gen._get_align(st.elements[idx])
if align > 1:
offset = ((offset + align - 1) // align) * align
return offset
def _adjust_this_for_inherited_method(self, Gen: LlvmGeneratorMixin, ObjVal: ir.Value,
ClassName: str, method_anc: str | None) -> ir.Value:
"""多继承 this 调整(与 TPV _translate_method_call 一致):
继承方法体按"定义类自身布局"读 self 字段(如 B.get_b 内
GEP(self_B, [0, 1]),字节偏移为 B 布局中 b 的 offset 8)。父类字段区
在子类中的**字节偏移**可能不同(如 AB={i8*,a@8,b@12} 中 b 偏移 12),
self 需按字节差前移,使父类方法内二次 GEP 恰好落到子类字段。
修复(2026-08-10):字段索引法在"父类与子类元素大小不同"时错位
(GEP 索引按元素个数计,非字节等价)。改用字节偏移:
delta = 字段在子类中字节偏移 − 字段在父类自身布局中字节偏移
delta==0(第一父类,字段区前缀对齐)无需调整;delta>0 时按字节
GEP 到父类字段区在子类中的等效基址,再 bitcast 成父类指针。
"""
if method_anc is None or method_anc == ClassName:
return ObjVal
anc_members: list = Gen.class_members.get(method_anc, [])
if not anc_members:
return ObjVal
first_field_name: str | None = None
for _fn, _ft in anc_members:
if _fn == '__vtable__':
continue
first_field_name = _fn
break
if first_field_name is None:
return ObjVal
sub_off: int | None = self._compute_field_byte_offset(Gen, ClassName, first_field_name)
if sub_off is None:
return ObjVal
anc_off: int | None = self._compute_field_byte_offset(Gen, method_anc, first_field_name)
if anc_off is None:
return ObjVal
delta: int = sub_off - anc_off
if delta <= 0:
return ObjVal
if method_anc not in Gen.structs:
return ObjVal
obj_i8: ir.Value = Gen.builder.bitcast(ObjVal, ir.PointerType(ir.IntType(8)),
name=f"this_adjust_i8_{method_anc}")
adj: ir.Value = Gen.builder.gep(obj_i8, [ir.Constant(ir.IntType(64), delta)],
name=f"this_adjust_{method_anc}")
return Gen.builder.bitcast(adj, ir.PointerType(Gen.structs[method_anc]),
name=f"this_adjust_cast_{method_anc}")
def _EnsureClassParentsLoaded(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None:
"""跨模块多继承:class_parents 缺失时从 vpi 按需加载继承信息。
调用方(如 HandlesType 委托)引用的类定义在另一模块,其父类
信息未随 import 填充。_TryLoadClassMembersFromPyi 读取 .vpi 中
的 ClassDef 并经 _EmitExternalClassDeclLlvm 填充 class_parents,
使 _get_mro_chain 能沿 MRO 回退到定义类(如 TypeGenMixin)。
"""
if not ClassName:
return
if Gen.class_parents.get(ClassName):
return
sha1_cls = Gen.ModuleSha1Map.get(ClassName)
if not sha1_cls:
sha1_cls = getattr(Gen, '_struct_sha1_map', {}).get(ClassName)
if not sha1_cls:
return
try:
self.Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, f"{sha1_cls}.stub.ll", Gen)
except Exception:
pass
def _compute_field_byte_offset(self, Gen: LlvmGeneratorMixin, ClassName: str, field_name: str) -> int | None:
"""计算字段在 struct 中的字节偏移(按 LLVM 布局含对齐)。
遍历 struct elements 带对齐累加(与 _get_struct_size/_get_align 一致)。
有 vtable 的类 struct 首元素为 vtable 指针(i8*),class_members 不含它。
返回 None 表示无法计算(struct 未定义 / 字段不存在)。
"""
st = Gen.structs.get(ClassName)
if not st or not isinstance(st, ir.IdentifiedStructType) or st.elements is None:
return None
members: list = Gen.class_members.get(ClassName, [])
has_vtable: bool = (ClassName in Gen.class_vtable
or ClassName in Gen._cross_module_vtable_classes)
base: int = 1 if has_vtable else 0
idx: int | None = None
for i, (name, _) in enumerate(members):
if name == field_name:
idx = base + i
break
if idx is None or idx >= len(st.elements):
return None
# ============================================================
# 修复(2026-08-13):class_members 与 struct.elements 数量不一致时对齐。
# 根因链(NewAstTest ASTCtx 枚举丢失):
# DeclarationGenerator/收集路径生成的 class_members 可能漏部分 Mixin/自动
# 字段(如 Generator 漏 GenericSpecDepth/GenericClassFiles/ISINSTANCE_CLASS_MAX
# 3 个,class_members=88 vs struct=91),导致 this_adjust 用 class_members
# 索引算错字节偏移(Generator 624 vs 真实 644)。StmtGenMixin 方法(find_enum/
# register_enum/is_enum_class)字段访问错位 20 字节 → EnumTable 落在
# ReexportSrcs 内存 → 与 reexport 数据冲突覆盖 → ASTCtx 等枚举丢失。
# 修正:字段真实 struct 索引 = class_members 索引 + (struct 元素数 - members 数)。
# 前提:遗漏字段位于目标字段之前(本 bug 场景成立:漏字段在 StmtGenMixin 前)。
# ============================================================
diff_elems: int = len(st.elements) - len(members)
if diff_elems > 0:
idx += diff_elems
if idx >= len(st.elements):
return None
offset: int = 0
for i in range(idx):
et = st.elements[i]
align: int = Gen._get_align(et)
size: int = Gen._get_struct_size(et)
if align > 1:
offset = ((offset + align - 1) // align) * align
offset += size
align = Gen._get_align(st.elements[idx])
if align > 1:
offset = ((offset + align - 1) // align) * align
return offset
def _adjust_this_for_inherited_method(self, Gen: LlvmGeneratorMixin, ObjVal: ir.Value,
ClassName: str, method_anc: str | None) -> ir.Value:
"""多继承 this 调整(与 TPV _translate_method_call 一致):
继承方法体按"定义类自身布局"读 self 字段(如 B.get_b 内
GEP(self_B, [0, 1]),字节偏移为 B 布局中 b 的 offset 8)。父类字段区
在子类中的**字节偏移**可能不同(如 AB={i8*,a@8,b@12} 中 b 偏移 12),
self 需按字节差前移,使父类方法内二次 GEP 恰好落到子类字段。
修复(2026-08-10):字段索引法在"父类与子类元素大小不同"时错位
(GEP 索引按元素个数计,非字节等价)。改用字节偏移:
delta = 字段在子类中字节偏移 − 字段在父类自身布局中字节偏移
delta==0(第一父类,字段区前缀对齐)无需调整;delta>0 时按字节
GEP 到父类字段区在子类中的等效基址,再 bitcast 成父类指针。
"""
if method_anc is None or method_anc == ClassName:
return ObjVal
anc_members: list = Gen.class_members.get(method_anc, [])
if not anc_members:
return ObjVal
first_field_name: str | None = None
for _fn, _ft in anc_members:
if _fn == '__vtable__':
continue
first_field_name = _fn
break
if first_field_name is None:
return ObjVal
sub_off: int | None = self._compute_field_byte_offset(Gen, ClassName, first_field_name)
if sub_off is None:
return ObjVal
anc_off: int | None = self._compute_field_byte_offset(Gen, method_anc, first_field_name)
if anc_off is None:
return ObjVal
delta: int = sub_off - anc_off
if delta <= 0:
return ObjVal
if method_anc not in Gen.structs:
return ObjVal
obj_i8: ir.Value = Gen.builder.bitcast(ObjVal, ir.PointerType(ir.IntType(8)),
name=f"this_adjust_i8_{method_anc}")
adj: ir.Value = Gen.builder.gep(obj_i8, [ir.Constant(ir.IntType(64), delta)],
name=f"this_adjust_{method_anc}")
return Gen.builder.bitcast(adj, ir.PointerType(Gen.structs[method_anc]),
name=f"this_adjust_cast_{method_anc}")
def _EnsureClassParentsLoaded(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None:
"""跨模块多继承:class_parents 缺失时从 vpi 按需加载继承信息。
调用方(如 HandlesType 委托)引用的类定义在另一模块,其父类
信息未随 import 填充。_TryLoadClassMembersFromPyi 读取 .vpi 中
的 ClassDef 并经 _EmitExternalClassDeclLlvm 填充 class_parents,
使 _get_mro_chain 能沿 MRO 回退到定义类(如 TypeGenMixin)。
"""
if not ClassName:
return
if Gen.class_parents.get(ClassName):
return
sha1_cls = Gen.ModuleSha1Map.get(ClassName)
if not sha1_cls:
sha1_cls = getattr(Gen, '_struct_sha1_map', {}).get(ClassName)
if not sha1_cls:
return
try:
self.Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, f"{sha1_cls}.stub.ll", Gen)
except Exception:
pass
def _HandleMethodCallLlvm(self, Node: ast.Call) -> ir.Value:
Gen = self.Trans.LlvmGen
MethodName = Node.func.attr
+270 -270
View File
@@ -1,271 +1,271 @@
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 ast
import re
import llvmlite.ir as ir
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.match(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$', 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):
if val.type.width == 64:
length_mod = 'll'
elif val.type.width == 32:
length_mod = 'l'
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
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.match(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$', 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):
if val.type.width == 64:
length_mod = 'll'
elif val.type.width == 32:
length_mod = 'l'
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
+248 -248
View File
@@ -1,249 +1,249 @@
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 ast
import llvmlite.ir as ir
class ExprLambdaHandle(BaseHandle):
def _HandleLambdaLlvm(self, Node: ast.Lambda) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if not getattr(Gen, '_lambda_counter', None):
Gen._lambda_counter = 0
lambda_id: int = Gen._lambda_counter
Gen._lambda_counter += 1
fn_name: str = f"lambda_fn_{lambda_id}"
captured_vars: list[tuple[str, Any]] = self._get_lambda_captured_vars(Node)
env_types: list[ir.Type] = []
env_var_names: list[str] = []
for var_name, var_val in captured_vars:
env_types.append(var_val.type)
env_var_names.append(var_name)
env_struct_type: ir.LiteralStructType = ir.LiteralStructType(env_types) if env_types else ir.LiteralStructType([])
ret_type: ir.Type = self.Trans.ExprUtils._infer_expr_llvm_type_full(Node.body)
param_types: list[ir.Type] = [ir.PointerType(env_struct_type)]
if Node.args:
for arg in Node.args.args:
param_types.append(ir.IntType(32))
fn_type: ir.FunctionType = ir.FunctionType(ret_type, param_types)
fn: ir.Function = ir.Function(Gen.module, fn_type, name=fn_name)
Gen.functions[fn_name] = fn
entry_block: ir.Block = fn.append_basic_block(name=f"{fn_name}_entry")
prev_builder: Any = Gen.builder
prev_func: Any = Gen.func
prev_vars: dict[str, Any] = dict(Gen.variables)
Gen.builder = ir.IRBuilder(entry_block)
Gen.func = fn
Gen.variables = {}
for i, arg in enumerate(fn.args):
arg.name = f"arg_{i}" if i > 0 else "env"
if env_types:
for idx, var_name in enumerate(env_var_names):
env_ptr: Any = fn.args[0]
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
member_ptr: Any = Gen.builder.gep(env_ptr, [zero, ir.Constant(ir.IntType(32), idx)], name=f"env_{var_name}")
Loaded: Any = Gen._load(member_ptr, name=f"Load_env_{var_name}")
temp_alloca: Any = Gen._alloca(Loaded.type, name=f"temp_{var_name}")
Gen._store(Loaded, temp_alloca)
Gen.variables[var_name] = temp_alloca
if Node.args:
for i, arg in enumerate(Node.args.args):
Gen.variables[arg.arg] = Gen._alloca(ir.IntType(32), name=arg.arg)
Gen._store(fn.args[i + 1], Gen.variables[arg.arg])
body_val: Any = self.HandleExprLlvm(Node.body)
if body_val:
if isinstance(body_val.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
body_val = Gen._load(body_val, name="Load_lambda_ret")
if body_val.type != ret_type:
if isinstance(body_val.type, ir.IntType) and isinstance(ret_type, ir.IntType):
if body_val.type.width < ret_type.width:
body_val = Gen.builder.zext(body_val, ret_type, name="zext_lambda_ret")
elif body_val.type.width > ret_type.width:
body_val = Gen.builder.trunc(body_val, ret_type, name="trunc_lambda_ret")
elif isinstance(body_val.type, ir.PointerType) and isinstance(ret_type, ir.PointerType):
body_val = Gen.builder.bitcast(body_val, ret_type, name="bitcast_lambda_ret")
Gen.builder.ret(body_val)
else:
if isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 0))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.BaseStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
else:
Gen.builder.ret(ir.Constant(ret_type, 0))
Gen.builder = prev_builder
Gen.func = prev_func
Gen.variables = prev_vars
closure_struct_type: ir.LiteralStructType = ir.LiteralStructType([
ir.PointerType(ir.IntType(8)),
ir.PointerType(fn_type)
])
closure_ptr: Any = Gen._alloca(closure_struct_type, name=f"closure_{lambda_id}")
env_ptr: Any = Gen._alloca(env_struct_type, name=f"env_{lambda_id}")
if env_types:
for idx, (var_name, var_val) in enumerate(captured_vars):
zero = ir.Constant(ir.IntType(32), 0)
member_ptr = Gen.builder.gep(env_ptr, [zero, ir.Constant(ir.IntType(32), idx)], name=f"store_env_{var_name}")
Gen._store(var_val, member_ptr)
zero = ir.Constant(ir.IntType(32), 0)
env_field_ptr: Any = Gen.builder.gep(closure_ptr, [zero, zero], name="closure_env_ptr")
env_i8_ptr: Any = Gen.builder.bitcast(env_ptr, ir.PointerType(ir.IntType(8)), name="env_i8_ptr")
Gen._store(env_i8_ptr, env_field_ptr)
fn_field_ptr: Any = Gen.builder.gep(closure_ptr, [zero, ir.Constant(ir.IntType(32), 1)], name="closure_fn_ptr")
Gen._store(fn, fn_field_ptr)
return closure_ptr
def _get_lambda_captured_vars(self, Node: ast.Lambda) -> list[tuple[str, ir.Value]]:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
captured: list[tuple[str, Any]] = []
free_vars: list[str] = self._get_lambda_free_vars(Node)
for var_name in free_vars:
if var_name in Gen.variables:
var_ptr: Any = Gen.variables[var_name]
if isinstance(var_ptr, ir.AllocaInstr) or isinstance(var_ptr, ir.GlobalVariable):
Loaded: Any = Gen._load(var_ptr, name=f"capture_{var_name}")
captured.append((var_name, Loaded))
return captured
def _get_lambda_free_vars(self, Node: ast.Lambda) -> list[str]:
bound_vars: set[str] = set()
free_vars: set[str] = set()
# 收集 lambda body 中的 bound 和 free 变量
self.Trans.ExprUtils._collect_names(Node.body, bound_vars, free_vars)
# lambda 参数也是 bound 的
if Node.args:
for arg in Node.args.args:
bound_vars.add(arg.arg)
# free 变量 = 在 body 中引用(Load)但不是参数也不是 body 内赋值的变量
result: list[str] = list(free_vars - bound_vars)
return result
def _HandleIfExpLlvm(self, Node: ast.IfExp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
TestVal: Any = self.HandleExprLlvm(Node.test)
if not TestVal:
return None
if not isinstance(TestVal.type, ir.IntType) or TestVal.type.width != 1:
TestVal = Gen.builder.icmp_signed('!=', TestVal, Gen._ZeroConst(TestVal.type), name="ifexp_cond")
ThenBB: ir.Block = Gen.func.append_basic_block(name="ifexp.then")
ElseBB: ir.Block = Gen.func.append_basic_block(name="ifexp.else")
MergeBB: ir.Block = Gen.func.append_basic_block(name="ifexp.end")
Gen.builder.cbranch(TestVal, ThenBB, ElseBB)
# 先在 ThenBB 中生成 BodyVal
Gen.builder.position_at_start(ThenBB)
BodyVal: Any = self.HandleExprLlvm(Node.body)
if not BodyVal:
BodyVal = ir.Constant(ir.IntType(32), 0)
ThenEndBB: ir.Block = Gen.builder.block
# 在 ElseBB 中生成 OrelseVal
Gen.builder.position_at_start(ElseBB)
OrelseVal: Any = self.HandleExprLlvm(Node.orelse)
if not OrelseVal:
OrelseVal = ir.Constant(ir.IntType(32), 0)
ElseEndBB: ir.Block = Gen.builder.block
# 确定 phi 节点的目标类型
result_type: ir.Type = BodyVal.type
if BodyVal.type != OrelseVal.type:
if isinstance(BodyVal.type, ir.IntType) and isinstance(OrelseVal.type, ir.IntType):
result_type = BodyVal.type if BodyVal.type.width >= OrelseVal.type.width else OrelseVal.type
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(OrelseVal.type, ir.PointerType):
# 指针类型,使用 BodyVal 的类型
result_type = BodyVal.type
elif isinstance(BodyVal.type, ir.IntType) and isinstance(OrelseVal.type, ir.PointerType):
# BodyVal 是整数,OrelseVal 是指针(字符串字面量)
# 如果指针指向 i8,将指针转换为 i8(加载字符)
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
result_type = ir.IntType(8)
else:
result_type = OrelseVal.type
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(OrelseVal.type, ir.IntType):
# BodyVal 是指针(字符串字面量),OrelseVal 是整数
# 如果指针指向 i8,将指针转换为 i8(加载字符)
if isinstance(BodyVal.type.pointee, ir.IntType) and BodyVal.type.pointee.width == 8:
result_type = ir.IntType(8)
else:
result_type = BodyVal.type
else:
result_type = OrelseVal.type
# 在 ThenBB 末尾添加类型转换(如果需要)和分支
if not ThenEndBB.is_terminated:
Gen.builder.position_at_end(ThenEndBB)
if BodyVal.type != result_type:
if isinstance(BodyVal.type, ir.IntType) and isinstance(result_type, ir.IntType):
if BodyVal.type.width < result_type.width:
BodyVal = Gen.builder.zext(BodyVal, result_type, name="ifexp_body_zext")
else:
BodyVal = Gen.builder.trunc(BodyVal, result_type, name="ifexp_body_trunc")
elif isinstance(BodyVal.type, ir.IntType) and isinstance(result_type, ir.PointerType):
# 如果 result_type 是 i8*,说明 Else 分支是字符串字面量
# 不应该将整数转换为指针,而应该保持整数类型
# 这里将 result_type 改为 i8
result_type = ir.IntType(8)
# 重新处理 Else 分支的类型转换
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 指针转整数
BodyVal = Gen.builder.ptrtoint(BodyVal, result_type, name="ifexp_body_ptr2int")
Gen.builder.branch(MergeBB)
# 在 ElseBB 末尾添加类型转换(如果需要)和分支
if not ElseEndBB.is_terminated:
Gen.builder.position_at_end(ElseEndBB)
if OrelseVal.type != result_type:
if isinstance(OrelseVal.type, ir.IntType) and isinstance(result_type, ir.IntType):
if OrelseVal.type.width < result_type.width:
OrelseVal = Gen.builder.zext(OrelseVal, result_type, name="ifexp_orelse_zext")
else:
OrelseVal = Gen.builder.trunc(OrelseVal, result_type, name="ifexp_orelse_trunc")
elif isinstance(OrelseVal.type, ir.IntType) and isinstance(result_type, ir.PointerType):
# 如果 result_type 是 i8*,说明 BodyVal 分支是字符串字面量
# 不应该将整数转换为指针,而应该保持整数类型
# 这里将 result_type 改为 i8
result_type = ir.IntType(8)
# 重新处理 BodyVal 分支的类型转换(已经在上面处理过了)
elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 如果指针指向 i8,加载字符值
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char")
else:
OrelseVal = Gen.builder.ptrtoint(OrelseVal, result_type, name="ifexp_orelse_ptr2int")
Gen.builder.branch(MergeBB)
Gen.builder.position_at_start(MergeBB)
result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result")
result_phi.add_incoming(BodyVal, ThenEndBB)
result_phi.add_incoming(OrelseVal, ElseEndBB)
return result_phi
def _HandleNamedExprLlvm(self, Node: ast.NamedExpr) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
ValueVal: Any = self.HandleExprLlvm(Node.value)
if not ValueVal:
return None
TargetName: str = Node.target.id
if TargetName in Gen._reg_values:
del Gen._reg_values[TargetName]
if TargetName in Gen.variables and Gen.variables[TargetName] is not None:
VarPtr: Any = Gen.variables[TargetName]
if VarPtr.type.pointee == ValueVal.type:
Gen._store(ValueVal, VarPtr)
else:
NewVar: Any = Gen._alloca(ValueVal.type, name=TargetName)
Gen._store(ValueVal, NewVar)
Gen.variables[TargetName] = NewVar
else:
NewVar = Gen._alloca(ValueVal.type, name=TargetName)
Gen._store(ValueVal, NewVar)
Gen.variables[TargetName] = NewVar
Gen._reg_values[TargetName] = ValueVal
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 llvmlite.ir as ir
class ExprLambdaHandle(BaseHandle):
def _HandleLambdaLlvm(self, Node: ast.Lambda) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if not getattr(Gen, '_lambda_counter', None):
Gen._lambda_counter = 0
lambda_id: int = Gen._lambda_counter
Gen._lambda_counter += 1
fn_name: str = f"lambda_fn_{lambda_id}"
captured_vars: list[tuple[str, Any]] = self._get_lambda_captured_vars(Node)
env_types: list[ir.Type] = []
env_var_names: list[str] = []
for var_name, var_val in captured_vars:
env_types.append(var_val.type)
env_var_names.append(var_name)
env_struct_type: ir.LiteralStructType = ir.LiteralStructType(env_types) if env_types else ir.LiteralStructType([])
ret_type: ir.Type = self.Trans.ExprUtils._infer_expr_llvm_type_full(Node.body)
param_types: list[ir.Type] = [ir.PointerType(env_struct_type)]
if Node.args:
for arg in Node.args.args:
param_types.append(ir.IntType(32))
fn_type: ir.FunctionType = ir.FunctionType(ret_type, param_types)
fn: ir.Function = ir.Function(Gen.module, fn_type, name=fn_name)
Gen.functions[fn_name] = fn
entry_block: ir.Block = fn.append_basic_block(name=f"{fn_name}_entry")
prev_builder: Any = Gen.builder
prev_func: Any = Gen.func
prev_vars: dict[str, Any] = dict(Gen.variables)
Gen.builder = ir.IRBuilder(entry_block)
Gen.func = fn
Gen.variables = {}
for i, arg in enumerate(fn.args):
arg.name = f"arg_{i}" if i > 0 else "env"
if env_types:
for idx, var_name in enumerate(env_var_names):
env_ptr: Any = fn.args[0]
zero: ir.Constant = ir.Constant(ir.IntType(32), 0)
member_ptr: Any = Gen.builder.gep(env_ptr, [zero, ir.Constant(ir.IntType(32), idx)], name=f"env_{var_name}")
Loaded: Any = Gen._load(member_ptr, name=f"Load_env_{var_name}")
temp_alloca: Any = Gen._alloca(Loaded.type, name=f"temp_{var_name}")
Gen._store(Loaded, temp_alloca)
Gen.variables[var_name] = temp_alloca
if Node.args:
for i, arg in enumerate(Node.args.args):
Gen.variables[arg.arg] = Gen._alloca(ir.IntType(32), name=arg.arg)
Gen._store(fn.args[i + 1], Gen.variables[arg.arg])
body_val: Any = self.HandleExprLlvm(Node.body)
if body_val:
if isinstance(body_val.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType):
body_val = Gen._load(body_val, name="Load_lambda_ret")
if body_val.type != ret_type:
if isinstance(body_val.type, ir.IntType) and isinstance(ret_type, ir.IntType):
if body_val.type.width < ret_type.width:
body_val = Gen.builder.zext(body_val, ret_type, name="zext_lambda_ret")
elif body_val.type.width > ret_type.width:
body_val = Gen.builder.trunc(body_val, ret_type, name="trunc_lambda_ret")
elif isinstance(body_val.type, ir.PointerType) and isinstance(ret_type, ir.PointerType):
body_val = Gen.builder.bitcast(body_val, ret_type, name="bitcast_lambda_ret")
Gen.builder.ret(body_val)
else:
if isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 0))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.BaseStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
else:
Gen.builder.ret(ir.Constant(ret_type, 0))
Gen.builder = prev_builder
Gen.func = prev_func
Gen.variables = prev_vars
closure_struct_type: ir.LiteralStructType = ir.LiteralStructType([
ir.PointerType(ir.IntType(8)),
ir.PointerType(fn_type)
])
closure_ptr: Any = Gen._alloca(closure_struct_type, name=f"closure_{lambda_id}")
env_ptr: Any = Gen._alloca(env_struct_type, name=f"env_{lambda_id}")
if env_types:
for idx, (var_name, var_val) in enumerate(captured_vars):
zero = ir.Constant(ir.IntType(32), 0)
member_ptr = Gen.builder.gep(env_ptr, [zero, ir.Constant(ir.IntType(32), idx)], name=f"store_env_{var_name}")
Gen._store(var_val, member_ptr)
zero = ir.Constant(ir.IntType(32), 0)
env_field_ptr: Any = Gen.builder.gep(closure_ptr, [zero, zero], name="closure_env_ptr")
env_i8_ptr: Any = Gen.builder.bitcast(env_ptr, ir.PointerType(ir.IntType(8)), name="env_i8_ptr")
Gen._store(env_i8_ptr, env_field_ptr)
fn_field_ptr: Any = Gen.builder.gep(closure_ptr, [zero, ir.Constant(ir.IntType(32), 1)], name="closure_fn_ptr")
Gen._store(fn, fn_field_ptr)
return closure_ptr
def _get_lambda_captured_vars(self, Node: ast.Lambda) -> list[tuple[str, ir.Value]]:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
captured: list[tuple[str, Any]] = []
free_vars: list[str] = self._get_lambda_free_vars(Node)
for var_name in free_vars:
if var_name in Gen.variables:
var_ptr: Any = Gen.variables[var_name]
if isinstance(var_ptr, ir.AllocaInstr) or isinstance(var_ptr, ir.GlobalVariable):
Loaded: Any = Gen._load(var_ptr, name=f"capture_{var_name}")
captured.append((var_name, Loaded))
return captured
def _get_lambda_free_vars(self, Node: ast.Lambda) -> list[str]:
bound_vars: set[str] = set()
free_vars: set[str] = set()
# 收集 lambda body 中的 bound 和 free 变量
self.Trans.ExprUtils._collect_names(Node.body, bound_vars, free_vars)
# lambda 参数也是 bound 的
if Node.args:
for arg in Node.args.args:
bound_vars.add(arg.arg)
# free 变量 = 在 body 中引用(Load)但不是参数也不是 body 内赋值的变量
result: list[str] = list(free_vars - bound_vars)
return result
def _HandleIfExpLlvm(self, Node: ast.IfExp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
TestVal: Any = self.HandleExprLlvm(Node.test)
if not TestVal:
return None
if not isinstance(TestVal.type, ir.IntType) or TestVal.type.width != 1:
TestVal = Gen.builder.icmp_signed('!=', TestVal, Gen._ZeroConst(TestVal.type), name="ifexp_cond")
ThenBB: ir.Block = Gen.func.append_basic_block(name="ifexp.then")
ElseBB: ir.Block = Gen.func.append_basic_block(name="ifexp.else")
MergeBB: ir.Block = Gen.func.append_basic_block(name="ifexp.end")
Gen.builder.cbranch(TestVal, ThenBB, ElseBB)
# 先在 ThenBB 中生成 BodyVal
Gen.builder.position_at_start(ThenBB)
BodyVal: Any = self.HandleExprLlvm(Node.body)
if not BodyVal:
BodyVal = ir.Constant(ir.IntType(32), 0)
ThenEndBB: ir.Block = Gen.builder.block
# 在 ElseBB 中生成 OrelseVal
Gen.builder.position_at_start(ElseBB)
OrelseVal: Any = self.HandleExprLlvm(Node.orelse)
if not OrelseVal:
OrelseVal = ir.Constant(ir.IntType(32), 0)
ElseEndBB: ir.Block = Gen.builder.block
# 确定 phi 节点的目标类型
result_type: ir.Type = BodyVal.type
if BodyVal.type != OrelseVal.type:
if isinstance(BodyVal.type, ir.IntType) and isinstance(OrelseVal.type, ir.IntType):
result_type = BodyVal.type if BodyVal.type.width >= OrelseVal.type.width else OrelseVal.type
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(OrelseVal.type, ir.PointerType):
# 指针类型,使用 BodyVal 的类型
result_type = BodyVal.type
elif isinstance(BodyVal.type, ir.IntType) and isinstance(OrelseVal.type, ir.PointerType):
# BodyVal 是整数,OrelseVal 是指针(字符串字面量)
# 如果指针指向 i8,将指针转换为 i8(加载字符)
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
result_type = ir.IntType(8)
else:
result_type = OrelseVal.type
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(OrelseVal.type, ir.IntType):
# BodyVal 是指针(字符串字面量),OrelseVal 是整数
# 如果指针指向 i8,将指针转换为 i8(加载字符)
if isinstance(BodyVal.type.pointee, ir.IntType) and BodyVal.type.pointee.width == 8:
result_type = ir.IntType(8)
else:
result_type = BodyVal.type
else:
result_type = OrelseVal.type
# 在 ThenBB 末尾添加类型转换(如果需要)和分支
if not ThenEndBB.is_terminated:
Gen.builder.position_at_end(ThenEndBB)
if BodyVal.type != result_type:
if isinstance(BodyVal.type, ir.IntType) and isinstance(result_type, ir.IntType):
if BodyVal.type.width < result_type.width:
BodyVal = Gen.builder.zext(BodyVal, result_type, name="ifexp_body_zext")
else:
BodyVal = Gen.builder.trunc(BodyVal, result_type, name="ifexp_body_trunc")
elif isinstance(BodyVal.type, ir.IntType) and isinstance(result_type, ir.PointerType):
# 如果 result_type 是 i8*,说明 Else 分支是字符串字面量
# 不应该将整数转换为指针,而应该保持整数类型
# 这里将 result_type 改为 i8
result_type = ir.IntType(8)
# 重新处理 Else 分支的类型转换
elif isinstance(BodyVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 指针转整数
BodyVal = Gen.builder.ptrtoint(BodyVal, result_type, name="ifexp_body_ptr2int")
Gen.builder.branch(MergeBB)
# 在 ElseBB 末尾添加类型转换(如果需要)和分支
if not ElseEndBB.is_terminated:
Gen.builder.position_at_end(ElseEndBB)
if OrelseVal.type != result_type:
if isinstance(OrelseVal.type, ir.IntType) and isinstance(result_type, ir.IntType):
if OrelseVal.type.width < result_type.width:
OrelseVal = Gen.builder.zext(OrelseVal, result_type, name="ifexp_orelse_zext")
else:
OrelseVal = Gen.builder.trunc(OrelseVal, result_type, name="ifexp_orelse_trunc")
elif isinstance(OrelseVal.type, ir.IntType) and isinstance(result_type, ir.PointerType):
# 如果 result_type 是 i8*,说明 BodyVal 分支是字符串字面量
# 不应该将整数转换为指针,而应该保持整数类型
# 这里将 result_type 改为 i8
result_type = ir.IntType(8)
# 重新处理 BodyVal 分支的类型转换(已经在上面处理过了)
elif isinstance(OrelseVal.type, ir.PointerType) and isinstance(result_type, ir.IntType):
# 如果指针指向 i8,加载字符值
if isinstance(OrelseVal.type.pointee, ir.IntType) and OrelseVal.type.pointee.width == 8:
OrelseVal = Gen._load(OrelseVal, name="ifexp_orelse_Load_char")
else:
OrelseVal = Gen.builder.ptrtoint(OrelseVal, result_type, name="ifexp_orelse_ptr2int")
Gen.builder.branch(MergeBB)
Gen.builder.position_at_start(MergeBB)
result_phi: Any = Gen.builder.phi(result_type, name="ifexp.result")
result_phi.add_incoming(BodyVal, ThenEndBB)
result_phi.add_incoming(OrelseVal, ElseEndBB)
return result_phi
def _HandleNamedExprLlvm(self, Node: ast.NamedExpr) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
ValueVal: Any = self.HandleExprLlvm(Node.value)
if not ValueVal:
return None
TargetName: str = Node.target.id
if TargetName in Gen._reg_values:
del Gen._reg_values[TargetName]
if TargetName in Gen.variables and Gen.variables[TargetName] is not None:
VarPtr: Any = Gen.variables[TargetName]
if VarPtr.type.pointee == ValueVal.type:
Gen._store(ValueVal, VarPtr)
else:
NewVar: Any = Gen._alloca(ValueVal.type, name=TargetName)
Gen._store(ValueVal, NewVar)
Gen.variables[TargetName] = NewVar
else:
NewVar = Gen._alloca(ValueVal.type, name=TargetName)
Gen._store(ValueVal, NewVar)
Gen.variables[TargetName] = NewVar
Gen._reg_values[TargetName] = ValueVal
return ValueVal
+486 -486
View File
@@ -1,487 +1,487 @@
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 ast
import llvmlite.ir as ir
class ExprOpsHandle(BaseHandle):
def _HandleBinOpLlvm(self, Node: ast.BinOp, VarType: ir.Type | str | None = None) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
Gen._set_node_info(Node, "BinOp")
LeftVal: Any = self.HandleExprLlvm(Node.left, VarType)
if not LeftVal:
return None
RightVal: Any = self.HandleExprLlvm(Node.right, VarType)
if not RightVal:
return None
Op: str | None = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if not Op:
return None
OP_OVERLOAD_MAP: dict[str, list[str]] = {
'+': ['__add__'], '-': ['__sub__'], '*': ['__mul__'],
'/': ['__truediv__', '__div__'], '//': ['__floordiv__'], '%': ['__mod__'],
'**': ['__pow__'],
'&': ['__and__'], '|': ['__or__'], '^': ['__xor__'],
'<<': ['__lshift__'], '>>': ['__rshift__'],
}
if Op in OP_OVERLOAD_MAP:
LeftClassName: str | None = None
if isinstance(LeftVal.type, ir.PointerType):
pointee: ir.Type = LeftVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
LeftClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen)
if LeftClassName and LeftClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName is None and isinstance(Node.left, ast.Name):
LeftClassName = Gen.var_struct_class.get(Node.left.id)
if LeftClassName and LeftClassName in Gen.structs:
if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName:
op_names: list[str] = OP_OVERLOAD_MAP[Op]
for op_name in op_names:
result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal)
if result is not None:
return result
if Op in ('+', 'Add') and isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
if isinstance(Node.left, ast.Constant) and isinstance(Node.left.value, str) and len(Node.left.value) == 1:
CharVal: Any = Gen._load(LeftVal, name="Load_char_const")
CharAsInt: Any = Gen.builder.zext(CharVal, RightVal.type, name="char_to_int")
result = Gen.builder.add(CharAsInt, RightVal, name="char_add")
TruncResult: Any = Gen.builder.trunc(result, ir.IntType(8), name="add_to_char")
return TruncResult
is_unsigned: bool = Gen._check_node_unsigned(Node.left) or Gen._check_node_unsigned(Node.right)
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max: int = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.right) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right")
elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if Op not in ('+', '-', 'Add', 'Sub') and isinstance(LeftVal.type.pointee, ir.IntType):
LeftVal = Gen._load(LeftVal, name="deref_ptr_binop")
if LeftVal.type.width < RightVal.type.width:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_deref_left")
elif LeftVal.type.width > RightVal.type.width:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_deref")
elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType):
if Op not in ('+', '-', 'Add', 'Sub') and isinstance(RightVal.type.pointee, ir.IntType):
RightVal = Gen._load(RightVal, name="deref_ptr_binop_r")
if RightVal.type.width < LeftVal.type.width:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_deref_right")
elif RightVal.type.width > LeftVal.type.width:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_deref")
if Op in ('>>', 'RShift') and isinstance(Node.left, ast.Name):
vname: str = Node.left.id
if vname in Gen.var_signedness and Gen.var_signedness[vname]:
is_unsigned = True
result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned)
return result
def _HandleBoolOpLlvm(self, Node: ast.BoolOp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
def _to_bool(val: Any, name: str) -> Any:
if isinstance(val.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fcmp_ordered('!=', val, ir.Constant(val.type, 0.0), name=name)
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
return Gen.builder.icmp_signed('!=', val, Gen._ZeroConst(val.type), name=name)
return val
values: list[ast.expr] = Node.values
if len(values) < 2:
return self.HandleExprLlvm(values[0]) if values else None
if isinstance(Node.op, ast.And):
phi_incomings: list[tuple[ir.Constant, ir.Block]] = []
end_bb: ir.Block = Gen.func.append_basic_block(name="and.end")
for idx in range(len(values) - 1):
val: Any = self.HandleExprLlvm(values[idx])
if not val:
if not end_bb.is_terminated:
saved_block: ir.Block = Gen.builder.block
Gen.builder.position_at_start(end_bb)
Gen.builder.unreachable()
Gen.builder.position_at_end(saved_block)
return None
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
val = _to_bool(val, f"andcond{idx}")
cur_bb: ir.Block = Gen.builder.block
next_bb: ir.Block = Gen.func.append_basic_block(name=f"and.rhs{idx}")
Gen.builder.cbranch(val, next_bb, end_bb)
phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb))
Gen.builder.position_at_start(next_bb)
last_val: Any = self.HandleExprLlvm(values[-1])
if not last_val:
last_val = ir.Constant(ir.IntType(1), 0)
if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1:
last_val = _to_bool(last_val, "andcond_last")
last_bb: ir.Block = Gen.builder.block
Gen.builder.branch(end_bb)
Gen.builder.position_at_start(end_bb)
result_phi: Any = Gen.builder.phi(ir.IntType(1), name="and.result")
for const_val, bb in phi_incomings:
result_phi.add_incoming(const_val, bb)
result_phi.add_incoming(last_val, last_bb)
return result_phi
elif isinstance(Node.op, ast.Or):
phi_incomings = []
end_bb = Gen.func.append_basic_block(name="or.end")
for idx in range(len(values) - 1):
val = self.HandleExprLlvm(values[idx])
if not val:
if not end_bb.is_terminated:
saved_block = Gen.builder.block
Gen.builder.position_at_start(end_bb)
Gen.builder.unreachable()
Gen.builder.position_at_end(saved_block)
return None
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
val = _to_bool(val, f"orcond{idx}")
cur_bb = Gen.builder.block
next_bb = Gen.func.append_basic_block(name=f"or.rhs{idx}")
Gen.builder.cbranch(val, end_bb, next_bb)
phi_incomings.append((ir.Constant(ir.IntType(1), 1), cur_bb))
Gen.builder.position_at_start(next_bb)
last_val = self.HandleExprLlvm(values[-1])
if not last_val:
last_val = ir.Constant(ir.IntType(1), 1)
if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1:
last_val = _to_bool(last_val, "orcond_last")
last_bb = Gen.builder.block
Gen.builder.branch(end_bb)
Gen.builder.position_at_start(end_bb)
result_phi = Gen.builder.phi(ir.IntType(1), name="or.result")
for const_val, bb in phi_incomings:
result_phi.add_incoming(const_val, bb)
result_phi.add_incoming(last_val, last_bb)
return result_phi
return None
def _HandleUnaryOpLlvm(self, Node: ast.UnaryOp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
OperandVal: Any = self.HandleExprLlvm(Node.operand)
if not OperandVal:
return None
OpSymbol: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.op)
if not OpSymbol:
return None
UNARY_OVERLOAD_MAP: dict[str, str] = {
'-': '__neg__',
'+': '__pos__',
'~': '__invert__',
}
if OpSymbol in UNARY_OVERLOAD_MAP:
ClassName: str | None = None
if isinstance(OperandVal.type, ir.PointerType):
pointee: ir.Type = OperandVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
ClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
ClassName = self.Trans.ExprUtils._get_var_class(Node.operand, Gen)
if ClassName and ClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[ClassName])
OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}")
if ClassName is None and isinstance(Node.operand, ast.Name):
ClassName = Gen.var_struct_class.get(Node.operand.id)
if ClassName and ClassName in Gen.structs:
if isinstance(OperandVal.type, ir.PointerType) and isinstance(OperandVal.type.pointee, ir.IntType) and OperandVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[ClassName])
OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}")
if ClassName:
op_name: str = UNARY_OVERLOAD_MAP[OpSymbol]
result: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, op_name, OperandVal, Gen)
if result is not None:
return result
if OpSymbol == 'not':
if isinstance(OperandVal.type, ir.IntType) and OperandVal.type.width == 1:
return Gen.builder.not_(OperandVal, name="not_result")
if isinstance(OperandVal.type, ir.PointerType):
null_val: ir.Constant = ir.Constant(OperandVal.type, None)
return Gen.builder.icmp_signed('==', OperandVal, null_val, name="not_result")
zero: ir.Constant = ir.Constant(OperandVal.type, 0)
return Gen.builder.icmp_signed('==', OperandVal, zero, name="not_result")
elif OpSymbol == '~':
if isinstance(OperandVal.type, ir.IntType):
mask: int = (1 << OperandVal.type.width) - 1
return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, mask), name="bnot_result")
return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, -1), name="bnot_result")
elif OpSymbol == '+':
return OperandVal
elif OpSymbol == '-':
if isinstance(OperandVal.type, ir.IntType):
return Gen.builder.neg(OperandVal, name="neg_result")
elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fneg(OperandVal, name="fneg_result")
return None
def _HandleCompareLlvm(self, Node: ast.Compare) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
LeftVal: Any = self.HandleExprLlvm(Node.left)
if not LeftVal:
return None
is_unsigned: bool = Gen._check_node_unsigned(Node.left) or (Gen._check_node_unsigned(Node.comparators[0]) if Node.comparators else False)
if len(Node.ops) == 1:
Op: ast.cmpop = Node.ops[0]
# in/not in: 调用 __contains__
if isinstance(Op, (ast.In, ast.NotIn)):
RightVal: Any = self.HandleExprLlvm(Node.comparators[0])
if RightVal:
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.comparators[0], Gen)
if ClassName and Gen._has_function(f'{ClassName}.__contains__'):
# 处理 RightVal 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8
if isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8:
RightVal = Gen.builder.inttoptr(RightVal, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8:
RightVal = Gen.builder.bitcast(RightVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
contains_func: ir.Function = Gen._get_function(f'{ClassName}.__contains__')
result: ir.Value = Gen.builder.call(contains_func, [RightVal, LeftVal], name=f"call_{ClassName}.__contains__")
# 归一化为 i1
if isinstance(result.type, ir.IntType) and result.type.width > 1:
zero: ir.Constant = ir.Constant(result.type, 0)
result = Gen.builder.icmp_signed('!=', result, zero, name="contains_bool")
if isinstance(Op, ast.NotIn):
result = Gen.builder.not_(result, name="not_in_result")
return result
return ir.Constant(ir.IntType(1), 0)
ComparatorSymbol: str | None = self.Trans.ExprHandler.GetComparatorSymbol(Op)
if not ComparatorSymbol:
return None
RightVal: Any = self.HandleExprLlvm(Node.comparators[0])
if not RightVal:
return None
# 比较运算符重载检测:当左侧是结构体指针时,尝试调用 __eq__/__ne__/__lt__/__le__/__gt__/__ge__
CMP_OVERLOAD_MAP: dict[str, str] = {
'==': '__eq__', '!=': '__ne__', '<': '__lt__',
'<=': '__le__', '>': '__gt__', '>=': '__ge__',
}
if ComparatorSymbol in CMP_OVERLOAD_MAP:
LeftClassName: str | None = None
if isinstance(LeftVal.type, ir.PointerType):
pointee: ir.Type = LeftVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
LeftClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen)
if LeftClassName and LeftClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName is None and isinstance(Node.left, ast.Name):
LeftClassName = Gen.var_struct_class.get(Node.left.id)
if LeftClassName and LeftClassName in Gen.structs:
if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName:
op_name: str = CMP_OVERLOAD_MAP[ComparatorSymbol]
result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal)
if result is not None:
return result
if isinstance(LeftVal, ir.Constant) and isinstance(RightVal, ir.Constant):
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
lv: int = LeftVal.constant
rv: int = RightVal.constant
lw: int = LeftVal.type.width
rw: int = RightVal.type.width
if lw < rw:
lv = lv if lv >= 0 else lv + (1 << lw)
elif rw < lw:
rv = rv if rv >= 0 else rv + (1 << rw)
if is_unsigned:
mask_l: int = (1 << max(lw, rw)) - 1
lv = lv & mask_l
rv = rv & mask_l
cmp_map: dict[str, bool] = {
'==': lv == rv, '!=': lv != rv,
'<': lv < rv, '>': lv > rv,
'<=': lv <= rv, '>=': lv >= rv,
}
result: bool = cmp_map.get(ComparatorSymbol, False)
return ir.Constant(ir.IntType(1), 1 if result else 0)
# is / is not: 值类型(IntType)与 None(空指针常量)比较
# 值类型(如 i8/i32)永远不可能为 None,因此:
# - 值类型 is None → 恒为 false (i1 0)
# - 值类型 is not None → 恒为 true (i1 1)
# 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃
if ComparatorSymbol in ('is', 'is not'):
LeftIsNullPtr: bool = isinstance(LeftVal, ir.Constant) and LeftVal.constant is None and isinstance(LeftVal.type, ir.PointerType)
RightIsNullPtr: bool = isinstance(RightVal, ir.Constant) and RightVal.constant is None and isinstance(RightVal.type, ir.PointerType)
LeftIsValueType: bool = isinstance(LeftVal.type, ir.IntType)
RightIsValueType: bool = isinstance(RightVal.type, ir.IntType)
if (LeftIsValueType and RightIsNullPtr) or (RightIsValueType and LeftIsNullPtr):
IsNotResult: int = 1 if ComparatorSymbol == 'is not' else 0
return ir.Constant(ir.IntType(1), IsNotResult)
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
# 如果宽类型常量值超出窄类型有符号范围,则必须用zext
# i8 类型默认使用 zext(系统编程中 i8 几乎总是无符号字节)
need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max: int = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max or RightVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_cmp")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left_cmp")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.comparators[0]) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max or LeftVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_cmp")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right_cmp")
elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8 and isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8:
LeftVal = Gen._load(LeftVal, name="Load_char_ptr_cmp")
else:
if isinstance(RightVal, ir.Constant) and RightVal.constant == 0:
RightVal = ir.Constant(LeftVal.type, None)
else:
# 指针 vs 非零整数: 将指针转为整数走正常 ==(地址值比较)
LeftVal = Gen.builder.ptrtoint(LeftVal, RightVal.type, name="ptr2int_cmp")
elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType):
if isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8 and isinstance(LeftVal.type, ir.IntType) and LeftVal.type.width == 8:
RightVal = Gen._load(RightVal, name="Load_char_ptr_cmp")
else:
if isinstance(LeftVal, ir.Constant) and LeftVal.constant == 0:
LeftVal = ir.Constant(RightVal.type, None)
else:
# 非零整数 vs 指针: 将指针转为整数走正常 ==(地址值比较)
RightVal = Gen.builder.ptrtoint(RightVal, LeftVal.type, name="ptr2int_cmp")
# is / is not: 地址比较(指针)或值比较(int)
if ComparatorSymbol in ('is', 'is not'):
if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType):
addr_cmp = Gen.builder.icmp_unsigned('==', LeftVal, RightVal, name="is_cmp")
if ComparatorSymbol == 'is not':
addr_cmp = Gen.builder.not_(addr_cmp, name="is_not_result")
return addr_cmp
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
val_cmp = Gen.builder.icmp_signed('==', LeftVal, RightVal, name="is_cmp")
if ComparatorSymbol == 'is not':
val_cmp = Gen.builder.not_(val_cmp, name="is_not_result")
return val_cmp
# == / !=: 指针间做地址比较(C-Like 语义,不隐式调用 strcmp)
# 如需字符串内容比较,应显式调用 string.strcmp()
if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType):
result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)):
if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType):
RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name="int_to_float_cmp")
elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType):
LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name="int_to_float_cmp")
result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
elif is_unsigned:
result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
else:
result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
return result
EndBB: ir.Block = Gen.func.append_basic_block(name="cmp.end")
phi_incomings: list[tuple[ir.Value, ir.Block]] = []
for i, (Op, Comparator) in enumerate(zip(Node.ops, Node.comparators)):
ComparatorSymbol = self.Trans.ExprHandler.GetComparatorSymbol(Op)
if not ComparatorSymbol:
continue
RightVal = self.HandleExprLlvm(Comparator)
if not RightVal:
continue
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max or RightVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name=f"zext_left_{i}")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name=f"sext_left_{i}")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Comparator) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max or LeftVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name=f"zext_right_{i}")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name=f"sext_right_{i}")
if is_unsigned:
cmp_result: Any = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)):
if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType):
RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name=f"int_to_float_cmp_{i}")
elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType):
LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name=f"int_to_float_cmp_{i}")
cmp_result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
else:
cmp_result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
if i < len(Node.ops) - 1:
NextBB: ir.Block = Gen.func.append_basic_block(name=f"cmp.next.{i}")
cur_bb: ir.Block = Gen.builder.block
Gen.builder.cbranch(cmp_result, NextBB, EndBB)
phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb))
Gen.builder.position_at_start(NextBB)
LeftVal = RightVal
else:
cur_bb = Gen.builder.block
Gen.builder.branch(EndBB)
phi_incomings.append((cmp_result, cur_bb))
Gen.builder.position_at_start(EndBB)
final_result: Any = Gen.builder.phi(ir.IntType(1), name="final_cmp")
for val, bb in phi_incomings:
final_result.add_incoming(val, bb)
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 llvmlite.ir as ir
class ExprOpsHandle(BaseHandle):
def _HandleBinOpLlvm(self, Node: ast.BinOp, VarType: ir.Type | str | None = None) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
Gen._set_node_info(Node, "BinOp")
LeftVal: Any = self.HandleExprLlvm(Node.left, VarType)
if not LeftVal:
return None
RightVal: Any = self.HandleExprLlvm(Node.right, VarType)
if not RightVal:
return None
Op: str | None = self.Trans.ExprHandler.GetOpSymbol(Node.op)
if not Op:
return None
OP_OVERLOAD_MAP: dict[str, list[str]] = {
'+': ['__add__'], '-': ['__sub__'], '*': ['__mul__'],
'/': ['__truediv__', '__div__'], '//': ['__floordiv__'], '%': ['__mod__'],
'**': ['__pow__'],
'&': ['__and__'], '|': ['__or__'], '^': ['__xor__'],
'<<': ['__lshift__'], '>>': ['__rshift__'],
}
if Op in OP_OVERLOAD_MAP:
LeftClassName: str | None = None
if isinstance(LeftVal.type, ir.PointerType):
pointee: ir.Type = LeftVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
LeftClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen)
if LeftClassName and LeftClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName is None and isinstance(Node.left, ast.Name):
LeftClassName = Gen.var_struct_class.get(Node.left.id)
if LeftClassName and LeftClassName in Gen.structs:
if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName:
op_names: list[str] = OP_OVERLOAD_MAP[Op]
for op_name in op_names:
result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal)
if result is not None:
return result
if Op in ('+', 'Add') and isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
if isinstance(Node.left, ast.Constant) and isinstance(Node.left.value, str) and len(Node.left.value) == 1:
CharVal: Any = Gen._load(LeftVal, name="Load_char_const")
CharAsInt: Any = Gen.builder.zext(CharVal, RightVal.type, name="char_to_int")
result = Gen.builder.add(CharAsInt, RightVal, name="char_add")
TruncResult: Any = Gen.builder.trunc(result, ir.IntType(8), name="add_to_char")
return TruncResult
is_unsigned: bool = Gen._check_node_unsigned(Node.left) or Gen._check_node_unsigned(Node.right)
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max: int = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.right) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right")
elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if Op not in ('+', '-', 'Add', 'Sub') and isinstance(LeftVal.type.pointee, ir.IntType):
LeftVal = Gen._load(LeftVal, name="deref_ptr_binop")
if LeftVal.type.width < RightVal.type.width:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_deref_left")
elif LeftVal.type.width > RightVal.type.width:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_deref")
elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType):
if Op not in ('+', '-', 'Add', 'Sub') and isinstance(RightVal.type.pointee, ir.IntType):
RightVal = Gen._load(RightVal, name="deref_ptr_binop_r")
if RightVal.type.width < LeftVal.type.width:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_deref_right")
elif RightVal.type.width > LeftVal.type.width:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_deref")
if Op in ('>>', 'RShift') and isinstance(Node.left, ast.Name):
vname: str = Node.left.id
if vname in Gen.var_signedness and Gen.var_signedness[vname]:
is_unsigned = True
result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned)
return result
def _HandleBoolOpLlvm(self, Node: ast.BoolOp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
def _to_bool(val: Any, name: str) -> Any:
if isinstance(val.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fcmp_ordered('!=', val, ir.Constant(val.type, 0.0), name=name)
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
return Gen.builder.icmp_signed('!=', val, Gen._ZeroConst(val.type), name=name)
return val
values: list[ast.expr] = Node.values
if len(values) < 2:
return self.HandleExprLlvm(values[0]) if values else None
if isinstance(Node.op, ast.And):
phi_incomings: list[tuple[ir.Constant, ir.Block]] = []
end_bb: ir.Block = Gen.func.append_basic_block(name="and.end")
for idx in range(len(values) - 1):
val: Any = self.HandleExprLlvm(values[idx])
if not val:
if not end_bb.is_terminated:
saved_block: ir.Block = Gen.builder.block
Gen.builder.position_at_start(end_bb)
Gen.builder.unreachable()
Gen.builder.position_at_end(saved_block)
return None
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
val = _to_bool(val, f"andcond{idx}")
cur_bb: ir.Block = Gen.builder.block
next_bb: ir.Block = Gen.func.append_basic_block(name=f"and.rhs{idx}")
Gen.builder.cbranch(val, next_bb, end_bb)
phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb))
Gen.builder.position_at_start(next_bb)
last_val: Any = self.HandleExprLlvm(values[-1])
if not last_val:
last_val = ir.Constant(ir.IntType(1), 0)
if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1:
last_val = _to_bool(last_val, "andcond_last")
last_bb: ir.Block = Gen.builder.block
Gen.builder.branch(end_bb)
Gen.builder.position_at_start(end_bb)
result_phi: Any = Gen.builder.phi(ir.IntType(1), name="and.result")
for const_val, bb in phi_incomings:
result_phi.add_incoming(const_val, bb)
result_phi.add_incoming(last_val, last_bb)
return result_phi
elif isinstance(Node.op, ast.Or):
phi_incomings = []
end_bb = Gen.func.append_basic_block(name="or.end")
for idx in range(len(values) - 1):
val = self.HandleExprLlvm(values[idx])
if not val:
if not end_bb.is_terminated:
saved_block = Gen.builder.block
Gen.builder.position_at_start(end_bb)
Gen.builder.unreachable()
Gen.builder.position_at_end(saved_block)
return None
if not isinstance(val.type, ir.IntType) or val.type.width != 1:
val = _to_bool(val, f"orcond{idx}")
cur_bb = Gen.builder.block
next_bb = Gen.func.append_basic_block(name=f"or.rhs{idx}")
Gen.builder.cbranch(val, end_bb, next_bb)
phi_incomings.append((ir.Constant(ir.IntType(1), 1), cur_bb))
Gen.builder.position_at_start(next_bb)
last_val = self.HandleExprLlvm(values[-1])
if not last_val:
last_val = ir.Constant(ir.IntType(1), 1)
if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1:
last_val = _to_bool(last_val, "orcond_last")
last_bb = Gen.builder.block
Gen.builder.branch(end_bb)
Gen.builder.position_at_start(end_bb)
result_phi = Gen.builder.phi(ir.IntType(1), name="or.result")
for const_val, bb in phi_incomings:
result_phi.add_incoming(const_val, bb)
result_phi.add_incoming(last_val, last_bb)
return result_phi
return None
def _HandleUnaryOpLlvm(self, Node: ast.UnaryOp) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
OperandVal: Any = self.HandleExprLlvm(Node.operand)
if not OperandVal:
return None
OpSymbol: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.op)
if not OpSymbol:
return None
UNARY_OVERLOAD_MAP: dict[str, str] = {
'-': '__neg__',
'+': '__pos__',
'~': '__invert__',
}
if OpSymbol in UNARY_OVERLOAD_MAP:
ClassName: str | None = None
if isinstance(OperandVal.type, ir.PointerType):
pointee: ir.Type = OperandVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
ClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
ClassName = self.Trans.ExprUtils._get_var_class(Node.operand, Gen)
if ClassName and ClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[ClassName])
OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}")
if ClassName is None and isinstance(Node.operand, ast.Name):
ClassName = Gen.var_struct_class.get(Node.operand.id)
if ClassName and ClassName in Gen.structs:
if isinstance(OperandVal.type, ir.PointerType) and isinstance(OperandVal.type.pointee, ir.IntType) and OperandVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[ClassName])
OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}")
if ClassName:
op_name: str = UNARY_OVERLOAD_MAP[OpSymbol]
result: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, op_name, OperandVal, Gen)
if result is not None:
return result
if OpSymbol == 'not':
if isinstance(OperandVal.type, ir.IntType) and OperandVal.type.width == 1:
return Gen.builder.not_(OperandVal, name="not_result")
if isinstance(OperandVal.type, ir.PointerType):
null_val: ir.Constant = ir.Constant(OperandVal.type, None)
return Gen.builder.icmp_signed('==', OperandVal, null_val, name="not_result")
zero: ir.Constant = ir.Constant(OperandVal.type, 0)
return Gen.builder.icmp_signed('==', OperandVal, zero, name="not_result")
elif OpSymbol == '~':
if isinstance(OperandVal.type, ir.IntType):
mask: int = (1 << OperandVal.type.width) - 1
return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, mask), name="bnot_result")
return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, -1), name="bnot_result")
elif OpSymbol == '+':
return OperandVal
elif OpSymbol == '-':
if isinstance(OperandVal.type, ir.IntType):
return Gen.builder.neg(OperandVal, name="neg_result")
elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fneg(OperandVal, name="fneg_result")
return None
def _HandleCompareLlvm(self, Node: ast.Compare) -> ir.Value | None:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
LeftVal: Any = self.HandleExprLlvm(Node.left)
if not LeftVal:
return None
is_unsigned: bool = Gen._check_node_unsigned(Node.left) or (Gen._check_node_unsigned(Node.comparators[0]) if Node.comparators else False)
if len(Node.ops) == 1:
Op: ast.cmpop = Node.ops[0]
# in/not in: 调用 __contains__
if isinstance(Op, (ast.In, ast.NotIn)):
RightVal: Any = self.HandleExprLlvm(Node.comparators[0])
if RightVal:
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.comparators[0], Gen)
if ClassName and Gen._has_function(f'{ClassName}.__contains__'):
# 处理 RightVal 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8
if isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8:
RightVal = Gen.builder.inttoptr(RightVal, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}")
if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8:
RightVal = Gen.builder.bitcast(RightVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
contains_func: ir.Function = Gen._get_function(f'{ClassName}.__contains__')
result: ir.Value = Gen.builder.call(contains_func, [RightVal, LeftVal], name=f"call_{ClassName}.__contains__")
# 归一化为 i1
if isinstance(result.type, ir.IntType) and result.type.width > 1:
zero: ir.Constant = ir.Constant(result.type, 0)
result = Gen.builder.icmp_signed('!=', result, zero, name="contains_bool")
if isinstance(Op, ast.NotIn):
result = Gen.builder.not_(result, name="not_in_result")
return result
return ir.Constant(ir.IntType(1), 0)
ComparatorSymbol: str | None = self.Trans.ExprHandler.GetComparatorSymbol(Op)
if not ComparatorSymbol:
return None
RightVal: Any = self.HandleExprLlvm(Node.comparators[0])
if not RightVal:
return None
# 比较运算符重载检测:当左侧是结构体指针时,尝试调用 __eq__/__ne__/__lt__/__le__/__gt__/__ge__
CMP_OVERLOAD_MAP: dict[str, str] = {
'==': '__eq__', '!=': '__ne__', '<': '__lt__',
'<=': '__le__', '>': '__gt__', '>=': '__ge__',
}
if ComparatorSymbol in CMP_OVERLOAD_MAP:
LeftClassName: str | None = None
if isinstance(LeftVal.type, ir.PointerType):
pointee: ir.Type = LeftVal.type.pointee
if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
LeftClassName = CN
break
elif isinstance(pointee, ir.IntType) and pointee.width == 8:
LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen)
if LeftClassName and LeftClassName in Gen.structs:
TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName is None and isinstance(Node.left, ast.Name):
LeftClassName = Gen.var_struct_class.get(Node.left.id)
if LeftClassName and LeftClassName in Gen.structs:
if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8:
TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName])
LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}")
if LeftClassName:
op_name: str = CMP_OVERLOAD_MAP[ComparatorSymbol]
result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal)
if result is not None:
return result
if isinstance(LeftVal, ir.Constant) and isinstance(RightVal, ir.Constant):
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
lv: int = LeftVal.constant
rv: int = RightVal.constant
lw: int = LeftVal.type.width
rw: int = RightVal.type.width
if lw < rw:
lv = lv if lv >= 0 else lv + (1 << lw)
elif rw < lw:
rv = rv if rv >= 0 else rv + (1 << rw)
if is_unsigned:
mask_l: int = (1 << max(lw, rw)) - 1
lv = lv & mask_l
rv = rv & mask_l
cmp_map: dict[str, bool] = {
'==': lv == rv, '!=': lv != rv,
'<': lv < rv, '>': lv > rv,
'<=': lv <= rv, '>=': lv >= rv,
}
result: bool = cmp_map.get(ComparatorSymbol, False)
return ir.Constant(ir.IntType(1), 1 if result else 0)
# is / is not: 值类型(IntType)与 None(空指针常量)比较
# 值类型(如 i8/i32)永远不可能为 None,因此:
# - 值类型 is None → 恒为 false (i1 0)
# - 值类型 is not None → 恒为 true (i1 1)
# 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃
if ComparatorSymbol in ('is', 'is not'):
LeftIsNullPtr: bool = isinstance(LeftVal, ir.Constant) and LeftVal.constant is None and isinstance(LeftVal.type, ir.PointerType)
RightIsNullPtr: bool = isinstance(RightVal, ir.Constant) and RightVal.constant is None and isinstance(RightVal.type, ir.PointerType)
LeftIsValueType: bool = isinstance(LeftVal.type, ir.IntType)
RightIsValueType: bool = isinstance(RightVal.type, ir.IntType)
if (LeftIsValueType and RightIsNullPtr) or (RightIsValueType and LeftIsNullPtr):
IsNotResult: int = 1 if ComparatorSymbol == 'is not' else 0
return ir.Constant(ir.IntType(1), IsNotResult)
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
# 如果宽类型常量值超出窄类型有符号范围,则必须用zext
# i8 类型默认使用 zext(系统编程中 i8 几乎总是无符号字节)
need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max: int = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max or RightVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_cmp")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left_cmp")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.comparators[0]) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max or LeftVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_cmp")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right_cmp")
elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType):
if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8 and isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8:
LeftVal = Gen._load(LeftVal, name="Load_char_ptr_cmp")
else:
if isinstance(RightVal, ir.Constant) and RightVal.constant == 0:
RightVal = ir.Constant(LeftVal.type, None)
else:
# 指针 vs 非零整数: 将指针转为整数走正常 ==(地址值比较)
LeftVal = Gen.builder.ptrtoint(LeftVal, RightVal.type, name="ptr2int_cmp")
elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType):
if isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8 and isinstance(LeftVal.type, ir.IntType) and LeftVal.type.width == 8:
RightVal = Gen._load(RightVal, name="Load_char_ptr_cmp")
else:
if isinstance(LeftVal, ir.Constant) and LeftVal.constant == 0:
LeftVal = ir.Constant(RightVal.type, None)
else:
# 非零整数 vs 指针: 将指针转为整数走正常 ==(地址值比较)
RightVal = Gen.builder.ptrtoint(RightVal, LeftVal.type, name="ptr2int_cmp")
# is / is not: 地址比较(指针)或值比较(int)
if ComparatorSymbol in ('is', 'is not'):
if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType):
addr_cmp = Gen.builder.icmp_unsigned('==', LeftVal, RightVal, name="is_cmp")
if ComparatorSymbol == 'is not':
addr_cmp = Gen.builder.not_(addr_cmp, name="is_not_result")
return addr_cmp
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
val_cmp = Gen.builder.icmp_signed('==', LeftVal, RightVal, name="is_cmp")
if ComparatorSymbol == 'is not':
val_cmp = Gen.builder.not_(val_cmp, name="is_not_result")
return val_cmp
# == / !=: 指针间做地址比较(C-Like 语义,不隐式调用 strcmp)
# 如需字符串内容比较,应显式调用 string.strcmp()
if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType):
result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)):
if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType):
RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name="int_to_float_cmp")
elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType):
LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name="int_to_float_cmp")
result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
elif is_unsigned:
result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
else:
result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name="cmpresult")
return result
EndBB: ir.Block = Gen.func.append_basic_block(name="cmp.end")
phi_incomings: list[tuple[ir.Value, ir.Block]] = []
for i, (Op, Comparator) in enumerate(zip(Node.ops, Node.comparators)):
ComparatorSymbol = self.Trans.ExprHandler.GetComparatorSymbol(Op)
if not ComparatorSymbol:
continue
RightVal = self.HandleExprLlvm(Comparator)
if not RightVal:
continue
if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType):
if LeftVal.type.width < RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1
if not need_zext and isinstance(RightVal, ir.Constant):
signed_max = (1 << (LeftVal.type.width - 1)) - 1
try:
if RightVal.constant > signed_max or RightVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name=f"zext_left_{i}")
else:
LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name=f"sext_left_{i}")
elif LeftVal.type.width > RightVal.type.width:
need_zext = is_unsigned or Gen._check_node_unsigned(Comparator) or RightVal.type.width == 8 or RightVal.type.width == 1
if not need_zext and isinstance(LeftVal, ir.Constant):
signed_max = (1 << (RightVal.type.width - 1)) - 1
try:
if LeftVal.constant > signed_max or LeftVal.constant < 0:
need_zext = True
except (TypeError, ValueError):
pass
if need_zext:
RightVal = Gen.builder.zext(RightVal, LeftVal.type, name=f"zext_right_{i}")
else:
RightVal = Gen.builder.sext(RightVal, LeftVal.type, name=f"sext_right_{i}")
if is_unsigned:
cmp_result: Any = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)):
if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType):
RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name=f"int_to_float_cmp_{i}")
elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType):
LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name=f"int_to_float_cmp_{i}")
cmp_result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
else:
cmp_result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}")
if i < len(Node.ops) - 1:
NextBB: ir.Block = Gen.func.append_basic_block(name=f"cmp.next.{i}")
cur_bb: ir.Block = Gen.builder.block
Gen.builder.cbranch(cmp_result, NextBB, EndBB)
phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb))
Gen.builder.position_at_start(NextBB)
LeftVal = RightVal
else:
cur_bb = Gen.builder.block
Gen.builder.branch(EndBB)
phi_incomings.append((cmp_result, cur_bb))
Gen.builder.position_at_start(EndBB)
final_result: Any = Gen.builder.phi(ir.IntType(1), name="final_cmp")
for val, bb in phi_incomings:
final_result.add_incoming(val, bb)
return final_result
+304 -304
View File
@@ -1,305 +1,305 @@
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 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: '-'}
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 BaseClass and BaseClass in Gen.class_members:
for m_name, m_type in Gen.class_members[BaseClass]:
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]
# 联合类型(如 GSList[Param] | t.CPtr)降级为 i8* 时,
# 从 class_member_element_class 查找泛型特化类名
element_class_map = getattr(Gen, 'class_member_element_class', {})
element_class = element_class_map.get(BaseClass, {}).get(AttrName)
if element_class:
return element_class
break
# Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后,
# 避免字段名和变量名冲突导致错误分派)
cls: str | None = Gen.var_struct_class.get(AttrName)
if cls:
return cls
return None
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
for CN in Gen.structs.keys():
if CN.startswith(f'{ClassName}[') and Gen._has_function(f'{CN}.{op_name}'):
SpecClass = CN
break
if CN.startswith(f'{ClassName}[') and Gen._has_function(f'{CN}.{op_name}__'):
SpecClass = CN
break
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):
return ir.IntType(32)
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):
return self._infer_expr_llvm_type_full(node.operand)
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: '-'}
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 BaseClass and BaseClass in Gen.class_members:
for m_name, m_type in Gen.class_members[BaseClass]:
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]
# 联合类型(如 GSList[Param] | t.CPtr)降级为 i8* 时,
# 从 class_member_element_class 查找泛型特化类名
element_class_map = getattr(Gen, 'class_member_element_class', {})
element_class = element_class_map.get(BaseClass, {}).get(AttrName)
if element_class:
return element_class
break
# Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后,
# 避免字段名和变量名冲突导致错误分派)
cls: str | None = Gen.var_struct_class.get(AttrName)
if cls:
return cls
return None
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
for CN in Gen.structs.keys():
if CN.startswith(f'{ClassName}[') and Gen._has_function(f'{CN}.{op_name}'):
SpecClass = CN
break
if CN.startswith(f'{ClassName}[') and Gen._has_function(f'{CN}.{op_name}__'):
SpecClass = CN
break
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):
return ir.IntType(32)
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):
return self._infer_expr_llvm_type_full(node.operand)
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
+15 -15
View File
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
import ast
import vast as ast
import copy
import traceback
import llvmlite.ir as ir
@@ -1757,8 +1757,8 @@ class FunctionHandle(BaseHandle):
Gen.builder.store(last_param_ptr, last_param_alloc)
Gen.emit_va_start(va_list_ptr)
Gen._variadic_info['va_start_called'] = True
# 用 _direct_values 注册,Name 解析时直接返回 va_list 指针(i8*),
# 不走 variables 的 load 路径(否则会把 i8* 当 char* 加载一个字节)
# 用 _direct_values 注册,Name 解析时直接返回 va_list 指针(i8*),
# 不走 variables 的 load 路径(否则会把 i8* 当 char* 加载一个字节)
Gen._direct_values[vararg_name] = va_list_ptr
# @t.TLS 一次性初始化检查:首次调用执行函数体,后续调用直接返回
if is_tls_func:
@@ -1790,18 +1790,18 @@ class FunctionHandle(BaseHandle):
Gen.builder.ret(ir.Constant(ActualReturnType, None))
Gen.builder.position_at_end(cont_bb)
Gen.builder.store(ir.Constant(ir.IntType(1), 1), flag_global)
_saved_class_name = getattr(Gen, '_current_class_name', None)
Gen._current_class_name = ClassName
# 提取首个裸字符串字面量作为 __doc__ docstring,并跳过该节点避免生成死代码
func_body: list[ast.stmt] = Node.body
doc_symbol: str = f"{ClassName}.{Node.name}" if ClassName else Node.name
if (func_body and isinstance(func_body[0], ast.Expr)
and isinstance(func_body[0].value, ast.Constant)
and isinstance(func_body[0].value.value, str)):
Gen._create_doc_global(doc_symbol, func_body[0].value.value)
func_body = func_body[1:]
else:
Gen._create_doc_global(doc_symbol, None)
_saved_class_name = getattr(Gen, '_current_class_name', None)
Gen._current_class_name = ClassName
# 提取首个裸字符串字面量作为 __doc__ docstring,并跳过该节点避免生成死代码
func_body: list[ast.stmt] = Node.body
doc_symbol: str = f"{ClassName}.{Node.name}" if ClassName else Node.name
if (func_body and isinstance(func_body[0], ast.Expr)
and isinstance(func_body[0].value, ast.Constant)
and isinstance(func_body[0].value.value, str)):
Gen._create_doc_global(doc_symbol, func_body[0].value.value)
func_body = func_body[1:]
else:
Gen._create_doc_global(doc_symbol, None)
try:
self.Trans.BodyHandler.HandleBodyLlvm(func_body)
except Exception as _body_exc:
+213 -213
View File
@@ -1,214 +1,214 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
import ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle
from lib.core.ConstEvaluator import ConstEvaluator, EvalContext
class IfHandle(BaseHandle):
def _get_attr_full_name(self, node: ast.expr) -> str | None:
if isinstance(node, ast.Attribute):
parts: list[str] = []
cur: ast.expr = node
while isinstance(cur, ast.Attribute):
parts.append(cur.attr)
cur = cur.value
if isinstance(cur, ast.Name):
parts.append(cur.id)
parts.reverse()
return '.'.join(parts)
return None
def _is_cif_call(self, node: ast.expr) -> bool:
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute):
if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c':
return node.func.attr in ('CIf', 'CIfdef', 'CIfndef')
if isinstance(node.func, ast.Name):
return node.func.id in ('CIf', 'CIfdef', 'CIfndef')
return False
def _resolve_macro_name(self, arg: ast.expr) -> str | None:
if isinstance(arg, ast.Name):
return arg.id
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
return arg.value
if isinstance(arg, ast.Attribute):
full_name: str | None = self._get_attr_full_name(arg)
if full_name:
return full_name
return None
def _evaluate_cif_condition(self, node: ast.Call) -> int | None:
if not isinstance(node, ast.Call):
return None
args: list[ast.expr] = node.args
if not args:
return 0
kind: str | None = None
if isinstance(node.func, ast.Attribute):
kind = node.func.attr
elif isinstance(node.func, ast.Name):
kind = node.func.id
else:
return None
if kind == 'CIfdef':
name: str | None = self._resolve_macro_name(args[0])
if name is None:
return 0
return 1 if self._is_macro_defined(name) else 0
elif kind == 'CIfndef':
name: str | None = self._resolve_macro_name(args[0])
if name is None:
return 1
return 0 if self._is_macro_defined(name) else 1
elif kind == 'CIf':
return self._eval_const_expr(args[0])
return None
def _is_macro_defined(self, name: str) -> bool:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if hasattr(Gen, '_define_constants') and name in Gen._define_constants:
return True
if self.Trans.SymbolTable.is_define(name):
return True
platform_macros: dict[str, int] = self._get_platform_macros()
return name in platform_macros
def _get_macro_value(self, name: str) -> int | float | None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if hasattr(Gen, '_define_constants') and name in Gen._define_constants:
val: int | float | None = Gen._define_constants[name]
# 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 覆盖
try:
if hasattr(Gen, 'module') and name in Gen.module.globals:
_gv: Any = Gen.module.globals[name]
if getattr(_gv, 'initializer', None) is not None:
_iv: Any = _gv.initializer
if hasattr(_iv, 'constant') and isinstance(_iv.constant, (int, float)):
val = _iv.constant
except Exception:
pass
if isinstance(val, (int, float)):
return val
info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(name)
if info:
if info.IsDefine:
val: int | float | None = info.DefineValue
if isinstance(val, (int, float)):
return val
platform_macros: dict[str, int] = self._get_platform_macros()
if name in platform_macros:
return platform_macros[name]
return None
def _get_platform_macros(self) -> dict[str, int]:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
macros: dict[str, int] = {}
pi: dict[str, object] = Gen._platform_info
ptr_size: int = getattr(Gen, 'ptr_size', 8)
# 根据目标平台设置宏(从三元组推导)
if pi.get('is_windows'):
macros['_WIN32'] = 1
if not pi.get('is_32bit'):
macros['_WIN64'] = 1
macros['WIN64'] = 1
macros['WIN32'] = 1
if pi.get('is_linux'):
macros['__linux__'] = 1
if pi.get('is_macos'):
macros['__APPLE__'] = 1
macros['__MACH__'] = 1
if pi.get('is_x86_64'):
macros['__x86_64__'] = 1
macros['__x86_64'] = 1
macros['_M_X64'] = 1
elif pi.get('is_arm64'):
macros['__aarch64__'] = 1
macros['_M_ARM64'] = 1
if not pi.get('is_32bit') and pi.get('is_linux'):
macros['__LP64__'] = 1
elif pi.get('is_32bit'):
macros['_ILP32'] = 1
macros['SIZEOF_VOID_P'] = ptr_size
macros['__SIZEOF_POINTER__'] = ptr_size
macros['NDEBUG'] = 0
return macros
def _eval_const_expr(self, node: ast.expr) -> int | None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
ctx: EvalContext = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans)
result: int | float | bool | None = ConstEvaluator.eval_full(node, ctx)
if result is None:
# 保留 CIf 嵌套调用处理(HandlesIf 特有逻辑)
if isinstance(node, ast.Call) and self._is_cif_call(node):
return self._evaluate_cif_condition(node)
return None
# HandlesIf 需要 bool/float → int 转换(#if 条件编译语义)
if isinstance(result, bool):
return 1 if result else 0
if isinstance(result, float):
return 1 if result != 0.0 else 0
return result
def _HandleIfLlvm(self, Node: ast.If) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if self._is_cif_call(Node.test):
compile_time_val: int | None = self._evaluate_cif_condition(Node.test)
if compile_time_val is not None:
if compile_time_val:
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
elif Node.orelse:
if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If):
self._HandleIfLlvm(Node.orelse[0])
else:
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
return
Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test)
if not Cond:
return
is_const: bool = isinstance(Cond, ir.Constant) and isinstance(Cond.type, ir.IntType) and Cond.type.width == 1
if is_const:
if Cond.constant:
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
elif Node.orelse:
if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If):
self._HandleIfLlvm(Node.orelse[0])
else:
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
return
if not isinstance(Cond.type, ir.IntType) or Cond.type.width != 1:
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.test, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__bool__'):
obj_val: ir.Value = Cond
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
Cond = Gen.builder.call(Gen._get_function(f'{ClassName}.__bool__'), [obj_val], name=f"call_{ClassName}.__bool__")
if isinstance(Cond.type, ir.IntType) and Cond.type.width != 1:
Cond = Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="bool_result")
else:
if isinstance(Cond.type, (ir.FloatType, ir.DoubleType)):
Cond = Gen.builder.fcmp_ordered('!=', Cond, ir.Constant(Cond.type, 0.0), name="ifcond")
else:
Cond = Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="ifcond")
ThenBB: ir.Block = Gen.func.append_basic_block(name="then")
ElseBB: ir.Block | None = Gen.func.append_basic_block(name="else") if Node.orelse else None
MergeBB: ir.Block = Gen.func.append_basic_block(name="endif")
if not Gen.builder.block.is_terminated:
Gen.builder.cbranch(Cond, ThenBB, ElseBB if ElseBB else MergeBB)
Gen.builder.position_at_start(ThenBB)
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
if not Gen.builder.block.is_terminated:
Gen.builder.branch(MergeBB)
if ElseBB:
Gen.builder.position_at_start(ElseBB)
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
if not Gen.builder.block.is_terminated:
Gen.builder.branch(MergeBB)
if not MergeBB.is_terminated:
Gen.builder.position_at_start(MergeBB)
else:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
import vast as ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle
from lib.core.ConstEvaluator import ConstEvaluator, EvalContext
class IfHandle(BaseHandle):
def _get_attr_full_name(self, node: ast.expr) -> str | None:
if isinstance(node, ast.Attribute):
parts: list[str] = []
cur: ast.expr = node
while isinstance(cur, ast.Attribute):
parts.append(cur.attr)
cur = cur.value
if isinstance(cur, ast.Name):
parts.append(cur.id)
parts.reverse()
return '.'.join(parts)
return None
def _is_cif_call(self, node: ast.expr) -> bool:
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute):
if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c':
return node.func.attr in ('CIf', 'CIfdef', 'CIfndef')
if isinstance(node.func, ast.Name):
return node.func.id in ('CIf', 'CIfdef', 'CIfndef')
return False
def _resolve_macro_name(self, arg: ast.expr) -> str | None:
if isinstance(arg, ast.Name):
return arg.id
if isinstance(arg, ast.Constant) and isinstance(arg.value, str):
return arg.value
if isinstance(arg, ast.Attribute):
full_name: str | None = self._get_attr_full_name(arg)
if full_name:
return full_name
return None
def _evaluate_cif_condition(self, node: ast.Call) -> int | None:
if not isinstance(node, ast.Call):
return None
args: list[ast.expr] = node.args
if not args:
return 0
kind: str | None = None
if isinstance(node.func, ast.Attribute):
kind = node.func.attr
elif isinstance(node.func, ast.Name):
kind = node.func.id
else:
return None
if kind == 'CIfdef':
name: str | None = self._resolve_macro_name(args[0])
if name is None:
return 0
return 1 if self._is_macro_defined(name) else 0
elif kind == 'CIfndef':
name: str | None = self._resolve_macro_name(args[0])
if name is None:
return 1
return 0 if self._is_macro_defined(name) else 1
elif kind == 'CIf':
return self._eval_const_expr(args[0])
return None
def _is_macro_defined(self, name: str) -> bool:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if hasattr(Gen, '_define_constants') and name in Gen._define_constants:
return True
if self.Trans.SymbolTable.is_define(name):
return True
platform_macros: dict[str, int] = self._get_platform_macros()
return name in platform_macros
def _get_macro_value(self, name: str) -> int | float | None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if hasattr(Gen, '_define_constants') and name in Gen._define_constants:
val: int | float | None = Gen._define_constants[name]
# 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 覆盖
try:
if hasattr(Gen, 'module') and name in Gen.module.globals:
_gv: Any = Gen.module.globals[name]
if getattr(_gv, 'initializer', None) is not None:
_iv: Any = _gv.initializer
if hasattr(_iv, 'constant') and isinstance(_iv.constant, (int, float)):
val = _iv.constant
except Exception:
pass
if isinstance(val, (int, float)):
return val
info: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(name)
if info:
if info.IsDefine:
val: int | float | None = info.DefineValue
if isinstance(val, (int, float)):
return val
platform_macros: dict[str, int] = self._get_platform_macros()
if name in platform_macros:
return platform_macros[name]
return None
def _get_platform_macros(self) -> dict[str, int]:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
macros: dict[str, int] = {}
pi: dict[str, object] = Gen._platform_info
ptr_size: int = getattr(Gen, 'ptr_size', 8)
# 根据目标平台设置宏(从三元组推导)
if pi.get('is_windows'):
macros['_WIN32'] = 1
if not pi.get('is_32bit'):
macros['_WIN64'] = 1
macros['WIN64'] = 1
macros['WIN32'] = 1
if pi.get('is_linux'):
macros['__linux__'] = 1
if pi.get('is_macos'):
macros['__APPLE__'] = 1
macros['__MACH__'] = 1
if pi.get('is_x86_64'):
macros['__x86_64__'] = 1
macros['__x86_64'] = 1
macros['_M_X64'] = 1
elif pi.get('is_arm64'):
macros['__aarch64__'] = 1
macros['_M_ARM64'] = 1
if not pi.get('is_32bit') and pi.get('is_linux'):
macros['__LP64__'] = 1
elif pi.get('is_32bit'):
macros['_ILP32'] = 1
macros['SIZEOF_VOID_P'] = ptr_size
macros['__SIZEOF_POINTER__'] = ptr_size
macros['NDEBUG'] = 0
return macros
def _eval_const_expr(self, node: ast.expr) -> int | None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
ctx: EvalContext = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans)
result: int | float | bool | None = ConstEvaluator.eval_full(node, ctx)
if result is None:
# 保留 CIf 嵌套调用处理(HandlesIf 特有逻辑)
if isinstance(node, ast.Call) and self._is_cif_call(node):
return self._evaluate_cif_condition(node)
return None
# HandlesIf 需要 bool/float → int 转换(#if 条件编译语义)
if isinstance(result, bool):
return 1 if result else 0
if isinstance(result, float):
return 1 if result != 0.0 else 0
return result
def _HandleIfLlvm(self, Node: ast.If) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if self._is_cif_call(Node.test):
compile_time_val: int | None = self._evaluate_cif_condition(Node.test)
if compile_time_val is not None:
if compile_time_val:
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
elif Node.orelse:
if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If):
self._HandleIfLlvm(Node.orelse[0])
else:
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
return
Cond: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.test)
if not Cond:
return
is_const: bool = isinstance(Cond, ir.Constant) and isinstance(Cond.type, ir.IntType) and Cond.type.width == 1
if is_const:
if Cond.constant:
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
elif Node.orelse:
if isinstance(Node.orelse, list) and len(Node.orelse) == 1 and isinstance(Node.orelse[0], ast.If):
self._HandleIfLlvm(Node.orelse[0])
else:
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
return
if not isinstance(Cond.type, ir.IntType) or Cond.type.width != 1:
ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.test, Gen)
if ClassName and Gen._has_function(f'{ClassName}.__bool__'):
obj_val: ir.Value = Cond
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}")
Cond = Gen.builder.call(Gen._get_function(f'{ClassName}.__bool__'), [obj_val], name=f"call_{ClassName}.__bool__")
if isinstance(Cond.type, ir.IntType) and Cond.type.width != 1:
Cond = Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="bool_result")
else:
if isinstance(Cond.type, (ir.FloatType, ir.DoubleType)):
Cond = Gen.builder.fcmp_ordered('!=', Cond, ir.Constant(Cond.type, 0.0), name="ifcond")
else:
Cond = Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="ifcond")
ThenBB: ir.Block = Gen.func.append_basic_block(name="then")
ElseBB: ir.Block | None = Gen.func.append_basic_block(name="else") if Node.orelse else None
MergeBB: ir.Block = Gen.func.append_basic_block(name="endif")
if not Gen.builder.block.is_terminated:
Gen.builder.cbranch(Cond, ThenBB, ElseBB if ElseBB else MergeBB)
Gen.builder.position_at_start(ThenBB)
self.Trans.BodyHandler.HandleBodyLlvm(Node.body)
if not Gen.builder.block.is_terminated:
Gen.builder.branch(MergeBB)
if ElseBB:
Gen.builder.position_at_start(ElseBB)
self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse)
if not Gen.builder.block.is_terminated:
Gen.builder.branch(MergeBB)
if not MergeBB.is_terminated:
Gen.builder.position_at_start(MergeBB)
else:
Gen.builder.position_at_end(MergeBB)
+2 -2
View File
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin
import ast
import vast as ast
import inspect
import os
import re
@@ -381,7 +381,7 @@ class ImportHandle(BaseHandle):
# 治本修复:使用 file_module_name(源模块名)而非 module_name(可能是别名/类名)。
# 这样 _EmitExternalClassDeclLlvm 内的 ModuleSha1Map 查找才能命中,
# _EmitExternalFuncDeclLlvm 才能拿到正确的 source_module_name → 正确的 struct sha1。
# 注意:actual_module_name 保持原值(可能为 None),不回退到 module_name(可能是别名)
# 注意:actual_module_name 保持原值(可能为 None),不回退到 module_name(可能是别名)
self._EmitExternalClassDeclLlvm(node, Gen, module_name=file_module_name, actual_module_name=actual_module_name)
elif isinstance(node, ast.ImportFrom):
if node.level > 0:
+419 -419
View File
@@ -1,420 +1,420 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle
import ast
import llvmlite.ir as ir
class MatchHandle(BaseHandle):
def _HandleMatchLlvm(self, Node: ast.Match) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
SubjectVal: ir.Value | None = self.HandleExprLlvm(Node.subject)
if not SubjectVal:
return
IsRenumMatch: bool = False
RenumName: str | None = None
SubjectPtr: ir.Value | None = None
if isinstance(Node.subject, ast.Name):
VarName: str = Node.subject.id
TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
if TypeInfo and TypeInfo.IsRenum:
IsRenumMatch = True
RenumName = TypeInfo.Name
SubjectPtr = Gen._Load_var(VarName)
if not IsRenumMatch:
for case in Node.cases:
if isinstance(case.pattern, ast.MatchClass):
cls_node: ast.expr = case.pattern.cls
VariantName: str | None = None
QualifiedName: str | None = None
if isinstance(cls_node, ast.Name):
VariantName = cls_node.id
elif isinstance(cls_node, ast.Attribute):
VariantName = cls_node.attr
# Extract enum name for qualified lookup to avoid collision
# with same-name factory functions (e.g. def Ptr vs LLVMType.Ptr)
if isinstance(cls_node.value, ast.Attribute):
QualifiedName = f"{cls_node.value.attr}.{VariantName}"
elif isinstance(cls_node.value, ast.Name):
QualifiedName = f"{cls_node.value.id}.{VariantName}"
if VariantName:
# Try qualified name first (e.g., "LLVMType.Ptr") to avoid
# collision with same-name functions (e.g., def Ptr(...))
SymInfo: "SymbolTable.SymbolInfo" = None
if QualifiedName:
SymInfo = self.Trans.SymbolTable.lookup(QualifiedName)
if not (SymInfo and SymInfo.IsEnumMember):
SymInfo = self.Trans.SymbolTable.lookup(VariantName)
if SymInfo and SymInfo.IsEnumMember and SymInfo.EnumName:
EnumName: str = SymInfo.EnumName
EnumInfo: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(EnumName)
if EnumInfo and EnumInfo.IsRenum:
IsRenumMatch = True
RenumName = EnumName
if SubjectPtr is None:
SubjectPtr = self.HandleExprLlvm(Node.subject)
break
if IsRenumMatch and SubjectPtr:
self._HandleRenumMatchLlvm(Node, RenumName, SubjectPtr)
return
if not isinstance(SubjectVal.type, ir.IntType):
try:
SubjectVal = Gen.builder.ptrtoint(SubjectVal, ir.IntType(64), name="match_subj")
SubjectVal = Gen.builder.trunc(SubjectVal, ir.IntType(32), name="match_subj_i32")
except Exception: # 回退:ptrtoint 失败时直接返回
return
SwitchIntType: ir.IntType = SubjectVal.type
DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default")
AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end")
CaseBBs: list[ir.Block] = []
CaseValues: list[ir.Value] = []
HasDefault: bool = False
HasNoBreak: list[bool] = []
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
if isinstance(pattern, ast.MatchValue):
Val: ir.Value | None = self.HandleExprLlvm(pattern.value)
if Val:
CaseVal: ir.Value
if isinstance(Val.type, ir.IntType):
if Val.type != SwitchIntType:
if Val.type.width > SwitchIntType.width:
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
else:
CaseVal = ir.Constant(SwitchIntType, Val.constant)
else:
CaseVal = Val
else:
try:
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}")
except Exception: # 回退:ptrtoint 失败时设默认值 0
CaseVal = ir.Constant(SwitchIntType, 0)
CaseValues.append(CaseVal)
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
elif isinstance(pattern, ast.MatchOr):
for j, SubPattern in enumerate(pattern.patterns):
if isinstance(SubPattern, ast.MatchValue):
Val: ir.Value | None = self.HandleExprLlvm(SubPattern.value)
if Val:
CaseVal: ir.Value
if isinstance(Val.type, ir.IntType):
if Val.type != SwitchIntType:
if Val.type.width > SwitchIntType.width:
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
else:
CaseVal = ir.Constant(SwitchIntType, Val.constant)
else:
CaseVal = Val
else:
try:
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}_{j}")
except Exception: # 回退:ptrtoint 失败时设默认值 0
CaseVal = ir.Constant(SwitchIntType, 0)
CaseValues.append(CaseVal)
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}_{j}"))
elif isinstance(pattern, ast.MatchSingleton):
if pattern.value is None:
CaseValues.append(ir.Constant(SwitchIntType, 0))
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
HasDefault = True
CaseBBs.append(DefaultBB)
elif isinstance(pattern, ast.MatchSequence):
HasDefault = True
CaseBBs.append(DefaultBB)
elif isinstance(pattern, ast.MatchClass):
# 兜底处理:REnum 检测失败但 case 是 MatchClass(REnum 变体模式)
# 尝试从变体名查找 tag 值,按整数值处理
cls_node_mc: ast.expr = pattern.cls
VariantName_mc: str | None = None
QualifiedName_mc: str | None = None
if isinstance(cls_node_mc, ast.Name):
VariantName_mc = cls_node_mc.id
elif isinstance(cls_node_mc, ast.Attribute):
VariantName_mc = cls_node_mc.attr
if isinstance(cls_node_mc.value, ast.Attribute):
QualifiedName_mc = f"{cls_node_mc.value.attr}.{VariantName_mc}"
elif isinstance(cls_node_mc.value, ast.Name):
QualifiedName_mc = f"{cls_node_mc.value.id}.{VariantName_mc}"
TagValue_mc: int | None = None
SymInfo_mc: "SymbolTable.SymbolInfo" = None
if QualifiedName_mc:
SymInfo_mc = self.Trans.SymbolTable.lookup(QualifiedName_mc)
if not (SymInfo_mc and SymInfo_mc.IsEnumMember):
SymInfo_mc = self.Trans.SymbolTable.lookup(VariantName_mc)
if SymInfo_mc and SymInfo_mc.IsEnumMember:
TagValue_mc = SymInfo_mc.value
if TagValue_mc is not None:
CaseValues.append(ir.Constant(SwitchIntType, TagValue_mc))
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{VariantName_mc}"))
else:
# 变体名未找到,跳过此 case(fast-fail 已在外层处理)
pass
def _HasNoBreak(stmts: list[ast.stmt]) -> bool:
for stmt in stmts:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
if isinstance(stmt.value.func, ast.Attribute):
if (isinstance(stmt.value.func.value, ast.Name) and
stmt.value.func.value.id == 'c' and
stmt.value.func.attr == 'NoBreak'):
return True
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
if _HasNoBreak(stmt.body):
return True
if getattr(stmt, 'orelse', None):
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
return True
return False
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
if not HasDefault:
CaseBBs.append(DefaultBB)
SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
switch_instr: ir.SwitchInstr = Gen.builder.switch(SubjectVal, DefaultBB)
for val, bb in SwitchCases:
switch_instr.add_case(val, bb)
CaseIdx: int = 0
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
if isinstance(pattern, ast.MatchOr):
NumSubCases: int = len(pattern.patterns)
for j in range(NumSubCases):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, (ast.MatchValue, ast.MatchSingleton)):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchSequence):
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchClass):
# 兜底处理:MatchClass 在非 REnum 路径的 case body 生成
# case 收集阶段已创建 CaseBB,此处生成 body
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
if not HasDefault:
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
Gen.builder.position_at_start(AfterBB)
def _HandleRenumMatchLlvm(self, Node: ast.Match, RenumName: str, SubjectPtr: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(SubjectPtr.type, ir.PointerType) and isinstance(SubjectPtr.type.pointee, ir.PointerType):
SubjectPtr = Gen._load(SubjectPtr, name="Load_match_subj")
# Bug fix: HandleExprLlvm 可能对 REnum 字段执行了 load,返回值而非指针。
# 情况1: SubjectPtr 不是指针类型(如 IntType)→ alloca REnum 结构体 + store
# 情况2: SubjectPtr 是指针但 pointee 不是结构体(如 i32* 指向 __tag)→ bitcast
NeedAlloca: bool = not isinstance(SubjectPtr.type, ir.PointerType)
NeedBitcast: bool = (isinstance(SubjectPtr.type, ir.PointerType) and
not isinstance(SubjectPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)))
if NeedAlloca or NeedBitcast:
RenumStructType: Any = Gen.structs.get(RenumName)
if RenumStructType is None:
# 跨模块编译时 REnum 结构体可能尚未在 Gen.structs 中注册
# (如 import llvmlite 后 LLVMType 仅在符号表,Gen.structs 无条目)。
# 使用 _get_or_create_struct 按需创建(可能为 opaque)。
RenumStructType = Gen._get_or_create_struct(RenumName)
if NeedAlloca:
AllocaPtr: ir.Value = Gen._allocaEntry(RenumStructType, name="match_subj_alloca")
CastedPtr: ir.Value = Gen.builder.bitcast(AllocaPtr, ir.PointerType(SubjectPtr.type), name="match_subj_cast")
Gen._store(SubjectPtr, CastedPtr)
SubjectPtr = AllocaPtr
else:
SubjectPtr = Gen.builder.bitcast(SubjectPtr, ir.PointerType(RenumStructType), name="match_subj_recast")
# REnum 布局为 { i32 __tag, <payload> },tag 在偏移 0。
# 使用 bitcast 到 i32* 替代 gep [0,0],兼容 opaque 结构体
# (跨模块编译时 REnum 结构体可能未 set_body,gep 会失败)。
tag_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, ir.PointerType(ir.IntType(32)), name="match_tag_ptr")
TagVal: ir.Value = Gen._load(tag_ptr, name="match_tag_val")
DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default")
AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end")
CaseBBs: list[ir.Block] = []
CaseValues: list[ir.Value] = []
CaseBindings: list[list[tuple[str, str, ir.Type, int]]] = []
HasDefault: bool = False
HasNoBreak: list[bool] = []
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
bindings: list[tuple[str, str, ir.Type, int]] = []
if isinstance(pattern, ast.MatchClass):
cls_node: ast.expr = pattern.cls
VariantName: str | None = None
QualifiedName: str | None = None
if isinstance(cls_node, ast.Name):
VariantName = cls_node.id
elif isinstance(cls_node, ast.Attribute):
VariantName = cls_node.attr
if isinstance(cls_node.value, ast.Attribute):
QualifiedName = f"{cls_node.value.attr}.{VariantName}"
elif isinstance(cls_node.value, ast.Name):
QualifiedName = f"{cls_node.value.id}.{VariantName}"
if VariantName:
TagValue: int | None = None
SymInfo: "SymbolTable.SymbolInfo" = None
if QualifiedName:
SymInfo = self.Trans.SymbolTable.lookup(QualifiedName)
if not (SymInfo and SymInfo.IsEnumMember):
SymInfo = self.Trans.SymbolTable.lookup(VariantName)
if SymInfo and SymInfo.IsEnumMember:
TagValue = SymInfo.value
if TagValue is not None:
CaseValues.append(ir.Constant(ir.IntType(32), TagValue))
CaseBB: ir.Block = Gen.func.append_basic_block(name=f"match.case_{VariantName}")
CaseBBs.append(CaseBB)
NestedStructName: str = f"{RenumName}_{VariantName}"
if NestedStructName in Gen.structs:
members: list[tuple[str, ir.Type]] = Gen.class_members.get(NestedStructName, [])
payLoad_members: list[tuple[str, ir.Type]] = [(n, t) for n, t in members if n != '__tag']
for j, sub_pat in enumerate(pattern.patterns):
if isinstance(sub_pat, ast.MatchAs) and sub_pat.name and j < len(payLoad_members):
bindings.append((sub_pat.name, payLoad_members[j][0], payLoad_members[j][1], j))
CaseBindings.append(bindings)
else:
CaseValues.append(ir.Constant(ir.IntType(32), 0))
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
CaseBindings.append([])
elif isinstance(pattern, ast.MatchValue):
Val: ir.Value | None = self.HandleExprLlvm(pattern.value)
if Val:
CaseVal: ir.Value
if isinstance(Val.type, ir.IntType):
CaseVal = Val
else:
try:
CaseVal = Gen.builder.ptrtoint(Val, ir.IntType(32), name=f"case_val_{i}")
except Exception: # 回退:ptrtoint 失败时设默认值 0
CaseVal = ir.Constant(ir.IntType(32), 0)
CaseValues.append(CaseVal)
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
CaseBindings.append([])
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
HasDefault = True
CaseBBs.append(DefaultBB)
CaseBindings.append([])
elif isinstance(pattern, ast.MatchSequence):
HasDefault = True
CaseBBs.append(DefaultBB)
CaseBindings.append([])
def _HasNoBreak(stmts: list[ast.stmt]) -> bool:
for stmt in stmts:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
if isinstance(stmt.value.func, ast.Attribute):
if (isinstance(stmt.value.func.value, ast.Name) and
stmt.value.func.value.id == 'c' and
stmt.value.func.attr == 'NoBreak'):
return True
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
if _HasNoBreak(stmt.body):
return True
if getattr(stmt, 'orelse', None):
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
return True
return False
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
if not HasDefault:
CaseBBs.append(DefaultBB)
SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
switch_instr: ir.SwitchInstr = Gen.builder.switch(TagVal, DefaultBB)
for val, bb in SwitchCases:
switch_instr.add_case(val, bb)
CaseIdx: int = 0
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
if isinstance(pattern, ast.MatchClass):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
bindings: list[tuple[str, str, ir.Type, int]] = CaseBindings[CaseIdx] if CaseIdx < len(CaseBindings) else []
cls_node: ast.expr = pattern.cls
VariantName: str | None = None
if isinstance(cls_node, ast.Name):
VariantName = cls_node.id
elif isinstance(cls_node, ast.Attribute):
VariantName = cls_node.attr
if VariantName:
NestedStructName: str = f"{RenumName}_{VariantName}"
if NestedStructName in Gen.structs:
NestedStructType: ir.Type = Gen.structs[NestedStructName]
NestedStructPtrType: ir.PointerType = ir.PointerType(NestedStructType)
variant_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, NestedStructPtrType, name=f"match_cast_{VariantName}")
for bind_name, member_name, member_type, member_idx in bindings:
elem_ptr: ir.Value = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), member_idx + 1)], name=f"match_{bind_name}")
# REnum 嵌套结构体共享 max_variant_struct 布局,成员类型可能与
# 结构体字段类型不一致。bitcast 到成员类型以确保后续 load 得到正确类型。
if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type:
elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"match_{bind_name}_cast")
Gen.variables[bind_name] = elem_ptr
self.HandleBodyLlvm(case.body)
for bind_name, _, _, _ in bindings:
if bind_name in Gen.variables:
del Gen.variables[bind_name]
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchValue):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchSequence):
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
if not HasDefault:
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import llvmlite.ir as ir
class MatchHandle(BaseHandle):
def _HandleMatchLlvm(self, Node: ast.Match) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
SubjectVal: ir.Value | None = self.HandleExprLlvm(Node.subject)
if not SubjectVal:
return
IsRenumMatch: bool = False
RenumName: str | None = None
SubjectPtr: ir.Value | None = None
if isinstance(Node.subject, ast.Name):
VarName: str = Node.subject.id
TypeInfo: "SymbolTable.SymbolInfo | None" = self.Trans.SymbolTable.lookup(VarName)
if TypeInfo and TypeInfo.IsRenum:
IsRenumMatch = True
RenumName = TypeInfo.Name
SubjectPtr = Gen._Load_var(VarName)
if not IsRenumMatch:
for case in Node.cases:
if isinstance(case.pattern, ast.MatchClass):
cls_node: ast.expr = case.pattern.cls
VariantName: str | None = None
QualifiedName: str | None = None
if isinstance(cls_node, ast.Name):
VariantName = cls_node.id
elif isinstance(cls_node, ast.Attribute):
VariantName = cls_node.attr
# Extract enum name for qualified lookup to avoid collision
# with same-name factory functions (e.g. def Ptr vs LLVMType.Ptr)
if isinstance(cls_node.value, ast.Attribute):
QualifiedName = f"{cls_node.value.attr}.{VariantName}"
elif isinstance(cls_node.value, ast.Name):
QualifiedName = f"{cls_node.value.id}.{VariantName}"
if VariantName:
# Try qualified name first (e.g., "LLVMType.Ptr") to avoid
# collision with same-name functions (e.g., def Ptr(...))
SymInfo: "SymbolTable.SymbolInfo" = None
if QualifiedName:
SymInfo = self.Trans.SymbolTable.lookup(QualifiedName)
if not (SymInfo and SymInfo.IsEnumMember):
SymInfo = self.Trans.SymbolTable.lookup(VariantName)
if SymInfo and SymInfo.IsEnumMember and SymInfo.EnumName:
EnumName: str = SymInfo.EnumName
EnumInfo: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(EnumName)
if EnumInfo and EnumInfo.IsRenum:
IsRenumMatch = True
RenumName = EnumName
if SubjectPtr is None:
SubjectPtr = self.HandleExprLlvm(Node.subject)
break
if IsRenumMatch and SubjectPtr:
self._HandleRenumMatchLlvm(Node, RenumName, SubjectPtr)
return
if not isinstance(SubjectVal.type, ir.IntType):
try:
SubjectVal = Gen.builder.ptrtoint(SubjectVal, ir.IntType(64), name="match_subj")
SubjectVal = Gen.builder.trunc(SubjectVal, ir.IntType(32), name="match_subj_i32")
except Exception: # 回退:ptrtoint 失败时直接返回
return
SwitchIntType: ir.IntType = SubjectVal.type
DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default")
AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end")
CaseBBs: list[ir.Block] = []
CaseValues: list[ir.Value] = []
HasDefault: bool = False
HasNoBreak: list[bool] = []
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
if isinstance(pattern, ast.MatchValue):
Val: ir.Value | None = self.HandleExprLlvm(pattern.value)
if Val:
CaseVal: ir.Value
if isinstance(Val.type, ir.IntType):
if Val.type != SwitchIntType:
if Val.type.width > SwitchIntType.width:
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
else:
CaseVal = ir.Constant(SwitchIntType, Val.constant)
else:
CaseVal = Val
else:
try:
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}")
except Exception: # 回退:ptrtoint 失败时设默认值 0
CaseVal = ir.Constant(SwitchIntType, 0)
CaseValues.append(CaseVal)
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
elif isinstance(pattern, ast.MatchOr):
for j, SubPattern in enumerate(pattern.patterns):
if isinstance(SubPattern, ast.MatchValue):
Val: ir.Value | None = self.HandleExprLlvm(SubPattern.value)
if Val:
CaseVal: ir.Value
if isinstance(Val.type, ir.IntType):
if Val.type != SwitchIntType:
if Val.type.width > SwitchIntType.width:
CaseVal = ir.Constant(SwitchIntType, Val.constant & ((1 << SwitchIntType.width) - 1))
else:
CaseVal = ir.Constant(SwitchIntType, Val.constant)
else:
CaseVal = Val
else:
try:
CaseVal = Gen.builder.ptrtoint(Val, SwitchIntType, name=f"case_val_{i}_{j}")
except Exception: # 回退:ptrtoint 失败时设默认值 0
CaseVal = ir.Constant(SwitchIntType, 0)
CaseValues.append(CaseVal)
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}_{j}"))
elif isinstance(pattern, ast.MatchSingleton):
if pattern.value is None:
CaseValues.append(ir.Constant(SwitchIntType, 0))
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
HasDefault = True
CaseBBs.append(DefaultBB)
elif isinstance(pattern, ast.MatchSequence):
HasDefault = True
CaseBBs.append(DefaultBB)
elif isinstance(pattern, ast.MatchClass):
# 兜底处理:REnum 检测失败但 case 是 MatchClass(REnum 变体模式)
# 尝试从变体名查找 tag 值,按整数值处理
cls_node_mc: ast.expr = pattern.cls
VariantName_mc: str | None = None
QualifiedName_mc: str | None = None
if isinstance(cls_node_mc, ast.Name):
VariantName_mc = cls_node_mc.id
elif isinstance(cls_node_mc, ast.Attribute):
VariantName_mc = cls_node_mc.attr
if isinstance(cls_node_mc.value, ast.Attribute):
QualifiedName_mc = f"{cls_node_mc.value.attr}.{VariantName_mc}"
elif isinstance(cls_node_mc.value, ast.Name):
QualifiedName_mc = f"{cls_node_mc.value.id}.{VariantName_mc}"
TagValue_mc: int | None = None
SymInfo_mc: "SymbolTable.SymbolInfo" = None
if QualifiedName_mc:
SymInfo_mc = self.Trans.SymbolTable.lookup(QualifiedName_mc)
if not (SymInfo_mc and SymInfo_mc.IsEnumMember):
SymInfo_mc = self.Trans.SymbolTable.lookup(VariantName_mc)
if SymInfo_mc and SymInfo_mc.IsEnumMember:
TagValue_mc = SymInfo_mc.value
if TagValue_mc is not None:
CaseValues.append(ir.Constant(SwitchIntType, TagValue_mc))
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{VariantName_mc}"))
else:
# 变体名未找到,跳过此 case(fast-fail 已在外层处理)
pass
def _HasNoBreak(stmts: list[ast.stmt]) -> bool:
for stmt in stmts:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
if isinstance(stmt.value.func, ast.Attribute):
if (isinstance(stmt.value.func.value, ast.Name) and
stmt.value.func.value.id == 'c' and
stmt.value.func.attr == 'NoBreak'):
return True
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
if _HasNoBreak(stmt.body):
return True
if getattr(stmt, 'orelse', None):
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
return True
return False
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
if not HasDefault:
CaseBBs.append(DefaultBB)
SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
switch_instr: ir.SwitchInstr = Gen.builder.switch(SubjectVal, DefaultBB)
for val, bb in SwitchCases:
switch_instr.add_case(val, bb)
CaseIdx: int = 0
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
if isinstance(pattern, ast.MatchOr):
NumSubCases: int = len(pattern.patterns)
for j in range(NumSubCases):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, (ast.MatchValue, ast.MatchSingleton)):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchSequence):
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchClass):
# 兜底处理:MatchClass 在非 REnum 路径的 case body 生成
# case 收集阶段已创建 CaseBB,此处生成 body
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
if not HasDefault:
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
Gen.builder.position_at_start(AfterBB)
def _HandleRenumMatchLlvm(self, Node: ast.Match, RenumName: str, SubjectPtr: ir.Value) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(SubjectPtr.type, ir.PointerType) and isinstance(SubjectPtr.type.pointee, ir.PointerType):
SubjectPtr = Gen._load(SubjectPtr, name="Load_match_subj")
# Bug fix: HandleExprLlvm 可能对 REnum 字段执行了 load,返回值而非指针。
# 情况1: SubjectPtr 不是指针类型(如 IntType)→ alloca REnum 结构体 + store
# 情况2: SubjectPtr 是指针但 pointee 不是结构体(如 i32* 指向 __tag)→ bitcast
NeedAlloca: bool = not isinstance(SubjectPtr.type, ir.PointerType)
NeedBitcast: bool = (isinstance(SubjectPtr.type, ir.PointerType) and
not isinstance(SubjectPtr.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)))
if NeedAlloca or NeedBitcast:
RenumStructType: Any = Gen.structs.get(RenumName)
if RenumStructType is None:
# 跨模块编译时 REnum 结构体可能尚未在 Gen.structs 中注册
# (如 import llvmlite 后 LLVMType 仅在符号表,Gen.structs 无条目)。
# 使用 _get_or_create_struct 按需创建(可能为 opaque)。
RenumStructType = Gen._get_or_create_struct(RenumName)
if NeedAlloca:
AllocaPtr: ir.Value = Gen._allocaEntry(RenumStructType, name="match_subj_alloca")
CastedPtr: ir.Value = Gen.builder.bitcast(AllocaPtr, ir.PointerType(SubjectPtr.type), name="match_subj_cast")
Gen._store(SubjectPtr, CastedPtr)
SubjectPtr = AllocaPtr
else:
SubjectPtr = Gen.builder.bitcast(SubjectPtr, ir.PointerType(RenumStructType), name="match_subj_recast")
# REnum 布局为 { i32 __tag, <payload> },tag 在偏移 0。
# 使用 bitcast 到 i32* 替代 gep [0,0],兼容 opaque 结构体
# (跨模块编译时 REnum 结构体可能未 set_body,gep 会失败)。
tag_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, ir.PointerType(ir.IntType(32)), name="match_tag_ptr")
TagVal: ir.Value = Gen._load(tag_ptr, name="match_tag_val")
DefaultBB: ir.Block = Gen.func.append_basic_block(name="match.default")
AfterBB: ir.Block = Gen.func.append_basic_block(name="match.end")
CaseBBs: list[ir.Block] = []
CaseValues: list[ir.Value] = []
CaseBindings: list[list[tuple[str, str, ir.Type, int]]] = []
HasDefault: bool = False
HasNoBreak: list[bool] = []
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
bindings: list[tuple[str, str, ir.Type, int]] = []
if isinstance(pattern, ast.MatchClass):
cls_node: ast.expr = pattern.cls
VariantName: str | None = None
QualifiedName: str | None = None
if isinstance(cls_node, ast.Name):
VariantName = cls_node.id
elif isinstance(cls_node, ast.Attribute):
VariantName = cls_node.attr
if isinstance(cls_node.value, ast.Attribute):
QualifiedName = f"{cls_node.value.attr}.{VariantName}"
elif isinstance(cls_node.value, ast.Name):
QualifiedName = f"{cls_node.value.id}.{VariantName}"
if VariantName:
TagValue: int | None = None
SymInfo: "SymbolTable.SymbolInfo" = None
if QualifiedName:
SymInfo = self.Trans.SymbolTable.lookup(QualifiedName)
if not (SymInfo and SymInfo.IsEnumMember):
SymInfo = self.Trans.SymbolTable.lookup(VariantName)
if SymInfo and SymInfo.IsEnumMember:
TagValue = SymInfo.value
if TagValue is not None:
CaseValues.append(ir.Constant(ir.IntType(32), TagValue))
CaseBB: ir.Block = Gen.func.append_basic_block(name=f"match.case_{VariantName}")
CaseBBs.append(CaseBB)
NestedStructName: str = f"{RenumName}_{VariantName}"
if NestedStructName in Gen.structs:
members: list[tuple[str, ir.Type]] = Gen.class_members.get(NestedStructName, [])
payLoad_members: list[tuple[str, ir.Type]] = [(n, t) for n, t in members if n != '__tag']
for j, sub_pat in enumerate(pattern.patterns):
if isinstance(sub_pat, ast.MatchAs) and sub_pat.name and j < len(payLoad_members):
bindings.append((sub_pat.name, payLoad_members[j][0], payLoad_members[j][1], j))
CaseBindings.append(bindings)
else:
CaseValues.append(ir.Constant(ir.IntType(32), 0))
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
CaseBindings.append([])
elif isinstance(pattern, ast.MatchValue):
Val: ir.Value | None = self.HandleExprLlvm(pattern.value)
if Val:
CaseVal: ir.Value
if isinstance(Val.type, ir.IntType):
CaseVal = Val
else:
try:
CaseVal = Gen.builder.ptrtoint(Val, ir.IntType(32), name=f"case_val_{i}")
except Exception: # 回退:ptrtoint 失败时设默认值 0
CaseVal = ir.Constant(ir.IntType(32), 0)
CaseValues.append(CaseVal)
CaseBBs.append(Gen.func.append_basic_block(name=f"match.case_{i}"))
CaseBindings.append([])
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
HasDefault = True
CaseBBs.append(DefaultBB)
CaseBindings.append([])
elif isinstance(pattern, ast.MatchSequence):
HasDefault = True
CaseBBs.append(DefaultBB)
CaseBindings.append([])
def _HasNoBreak(stmts: list[ast.stmt]) -> bool:
for stmt in stmts:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
if isinstance(stmt.value.func, ast.Attribute):
if (isinstance(stmt.value.func.value, ast.Name) and
stmt.value.func.value.id == 'c' and
stmt.value.func.attr == 'NoBreak'):
return True
if getattr(stmt, 'body', None) and isinstance(stmt.body, list):
if _HasNoBreak(stmt.body):
return True
if getattr(stmt, 'orelse', None):
if isinstance(stmt.orelse, list) and _HasNoBreak(stmt.orelse):
return True
return False
HasNoBreak.append(_HasNoBreak(case.body) if case.body else False)
if not HasDefault:
CaseBBs.append(DefaultBB)
SwitchCases: list[tuple[ir.Value, ir.Block]] = [(val, bb) for val, bb in zip(CaseValues, CaseBBs) if bb != DefaultBB]
switch_instr: ir.SwitchInstr = Gen.builder.switch(TagVal, DefaultBB)
for val, bb in SwitchCases:
switch_instr.add_case(val, bb)
CaseIdx: int = 0
for i, case in enumerate(Node.cases):
pattern: ast.pattern = case.pattern
if isinstance(pattern, ast.MatchClass):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
bindings: list[tuple[str, str, ir.Type, int]] = CaseBindings[CaseIdx] if CaseIdx < len(CaseBindings) else []
cls_node: ast.expr = pattern.cls
VariantName: str | None = None
if isinstance(cls_node, ast.Name):
VariantName = cls_node.id
elif isinstance(cls_node, ast.Attribute):
VariantName = cls_node.attr
if VariantName:
NestedStructName: str = f"{RenumName}_{VariantName}"
if NestedStructName in Gen.structs:
NestedStructType: ir.Type = Gen.structs[NestedStructName]
NestedStructPtrType: ir.PointerType = ir.PointerType(NestedStructType)
variant_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, NestedStructPtrType, name=f"match_cast_{VariantName}")
for bind_name, member_name, member_type, member_idx in bindings:
elem_ptr: ir.Value = Gen.builder.gep(variant_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), member_idx + 1)], name=f"match_{bind_name}")
# REnum 嵌套结构体共享 max_variant_struct 布局,成员类型可能与
# 结构体字段类型不一致。bitcast 到成员类型以确保后续 load 得到正确类型。
if isinstance(elem_ptr.type, ir.PointerType) and elem_ptr.type.pointee != member_type:
elem_ptr = Gen.builder.bitcast(elem_ptr, ir.PointerType(member_type), name=f"match_{bind_name}_cast")
Gen.variables[bind_name] = elem_ptr
self.HandleBodyLlvm(case.body)
for bind_name, _, _, _ in bindings:
if bind_name in Gen.variables:
del Gen.variables[bind_name]
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchValue):
if CaseIdx < len(CaseBBs) and CaseBBs[CaseIdx] != DefaultBB:
Gen.builder.position_at_start(CaseBBs[CaseIdx])
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchAs):
if pattern.pattern is None:
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
elif isinstance(pattern, ast.MatchSequence):
Gen.builder.position_at_start(DefaultBB)
self.HandleBodyLlvm(case.body)
if not Gen.builder.block.is_terminated:
if not HasNoBreak[i]:
Gen.builder.branch(AfterBB)
CaseIdx += 1
if not HasDefault:
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
elif not any(isinstance(c.pattern, ast.MatchAs) and c.pattern.pattern is None for c in Node.cases) and not any(isinstance(c.pattern, ast.MatchSequence) for c in Node.cases):
Gen.builder.position_at_start(DefaultBB)
Gen.builder.branch(AfterBB)
Gen.builder.position_at_start(AfterBB)
+154 -154
View File
@@ -1,155 +1,155 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
import ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
from lib.constants.config import mode as _config_mode
class RaiseHandle(BaseHandle):
def _get_exception_code(self, ExcName: str) -> int:
if ExcName in EXCEPTION_CODE_MAP:
return EXCEPTION_CODE_MAP[ExcName]
if ExcName in self.Trans.exception_registry:
return self.Trans.exception_registry[ExcName]
return 1
def _HandleRaiseLlvm(self, Node: ast.Raise) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
exc_val: ir.Constant = ir.Constant(ir.IntType(32), 1)
exc_msg: ir.Value | None = None
IsStopIteration: bool = False
if Node.exc:
if isinstance(Node.exc, ast.Constant) and isinstance(Node.exc.value, int):
exc_val = ir.Constant(ir.IntType(32), Node.exc.value)
elif isinstance(Node.exc, ast.Name):
ExcName: str = Node.exc.id
if ExcName == 'StopIteration':
IsStopIteration = True
exc_val = ir.Constant(ir.IntType(32), self._get_exception_code(ExcName))
elif isinstance(Node.exc, ast.Call) and isinstance(Node.exc.func, ast.Name):
ExcName: str = Node.exc.func.id
if ExcName == 'StopIteration':
IsStopIteration = True
exc_val = ir.Constant(ir.IntType(32), self._get_exception_code(ExcName))
if Node.exc.args:
first_arg: ast.expr = Node.exc.args[0]
if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str):
exc_msg = self.HandleExprLlvm(first_arg)
elif isinstance(first_arg, (ast.Name, ast.Call, ast.Attribute)):
arg_val: ir.Value | None = self.HandleExprLlvm(first_arg)
if arg_val:
if isinstance(arg_val.type, ir.PointerType):
if self._is_char_pointer(arg_val):
exc_msg = arg_val
else:
try:
exc_msg = Gen.builder.bitcast(arg_val, ir.PointerType(ir.IntType(8)), name="exc_msg_cast")
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
else:
try:
exc_msg = Gen.builder.inttoptr(arg_val, ir.PointerType(ir.IntType(8)), name="exc_msg_ptr")
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
else:
val: ir.Value | None = self.HandleExprLlvm(Node.exc)
if val:
if isinstance(val.type, ir.IntType):
if val.type.width != 32:
try:
val = Gen.builder.trunc(val, ir.IntType(32), name="exc_trunc")
except Exception: # 回退:trunc 失败时设默认值 1
val = ir.Constant(ir.IntType(32), 1)
exc_val = val
else:
try:
val = Gen.builder.ptrtoint(val, ir.IntType(32), name="exc_ptr2int")
exc_val = val
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
if IsStopIteration:
if Gen._stop_iter_flag_param is not None:
Gen.builder.store(ir.Constant(ir.IntType(1), 1), Gen._stop_iter_flag_param)
if Gen.func and Gen.func.type.pointee.return_type != ir.VoidType():
ret_type: ir.Type = Gen.func.type.pointee.return_type
if isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 0))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.BaseStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
else:
Gen.builder.ret(ir.Constant(ret_type, 0))
else:
Gen.builder.ret_void()
return
if Gen.eh_except_block_stack:
ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0]
EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1]
exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2]
eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3]
Gen._store(exc_val, exception_code)
if exc_msg:
Gen._store(exc_msg, eh_message)
else:
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen._store(null_ptr, eh_message)
if Gen.func and ExceptBB and ExceptBB.function is Gen.func:
Gen.builder.branch(ExceptBB)
else:
eh_msg_arg: ir.Argument | None = None
eh_code_arg: ir.Argument | None = None
if Gen.func:
for arg in Gen.func.args:
if arg.name == '__eh_msg_out__':
eh_msg_arg = arg
elif arg.name == '__eh_code_out__':
eh_code_arg = arg
if eh_msg_arg is not None:
if exc_msg:
Gen.builder.store(exc_msg, eh_msg_arg)
else:
null_ptr = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen.builder.store(null_ptr, eh_msg_arg)
if eh_code_arg is not None:
Gen.builder.store(exc_val, eh_code_arg)
if Gen.func and Gen.func.type.pointee.return_type == ir.IntType(32):
Gen.builder.ret(ir.Constant(ir.IntType(32), 1))
else:
Gen.builder.ret_void()
else:
eh_msg_arg: ir.Argument | None = None
eh_code_arg: ir.Argument | None = None
if Gen.func:
for arg in Gen.func.args:
if arg.name == '__eh_msg_out__':
eh_msg_arg = arg
elif arg.name == '__eh_code_out__':
eh_code_arg = arg
if eh_msg_arg is not None:
if exc_msg:
Gen.builder.store(exc_msg, eh_msg_arg)
else:
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen.builder.store(null_ptr, eh_msg_arg)
if eh_code_arg is not None:
Gen.builder.store(exc_val, eh_code_arg)
if Gen.func and Gen.func.type.pointee.return_type == ir.IntType(32):
Gen.builder.ret(ir.Constant(ir.IntType(32), 1))
else:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
import vast as ast
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
from lib.constants.config import mode as _config_mode
class RaiseHandle(BaseHandle):
def _get_exception_code(self, ExcName: str) -> int:
if ExcName in EXCEPTION_CODE_MAP:
return EXCEPTION_CODE_MAP[ExcName]
if ExcName in self.Trans.exception_registry:
return self.Trans.exception_registry[ExcName]
return 1
def _HandleRaiseLlvm(self, Node: ast.Raise) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
exc_val: ir.Constant = ir.Constant(ir.IntType(32), 1)
exc_msg: ir.Value | None = None
IsStopIteration: bool = False
if Node.exc:
if isinstance(Node.exc, ast.Constant) and isinstance(Node.exc.value, int):
exc_val = ir.Constant(ir.IntType(32), Node.exc.value)
elif isinstance(Node.exc, ast.Name):
ExcName: str = Node.exc.id
if ExcName == 'StopIteration':
IsStopIteration = True
exc_val = ir.Constant(ir.IntType(32), self._get_exception_code(ExcName))
elif isinstance(Node.exc, ast.Call) and isinstance(Node.exc.func, ast.Name):
ExcName: str = Node.exc.func.id
if ExcName == 'StopIteration':
IsStopIteration = True
exc_val = ir.Constant(ir.IntType(32), self._get_exception_code(ExcName))
if Node.exc.args:
first_arg: ast.expr = Node.exc.args[0]
if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str):
exc_msg = self.HandleExprLlvm(first_arg)
elif isinstance(first_arg, (ast.Name, ast.Call, ast.Attribute)):
arg_val: ir.Value | None = self.HandleExprLlvm(first_arg)
if arg_val:
if isinstance(arg_val.type, ir.PointerType):
if self._is_char_pointer(arg_val):
exc_msg = arg_val
else:
try:
exc_msg = Gen.builder.bitcast(arg_val, ir.PointerType(ir.IntType(8)), name="exc_msg_cast")
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
else:
try:
exc_msg = Gen.builder.inttoptr(arg_val, ir.PointerType(ir.IntType(8)), name="exc_msg_ptr")
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
else:
val: ir.Value | None = self.HandleExprLlvm(Node.exc)
if val:
if isinstance(val.type, ir.IntType):
if val.type.width != 32:
try:
val = Gen.builder.trunc(val, ir.IntType(32), name="exc_trunc")
except Exception: # 回退:trunc 失败时设默认值 1
val = ir.Constant(ir.IntType(32), 1)
exc_val = val
else:
try:
val = Gen.builder.ptrtoint(val, ir.IntType(32), name="exc_ptr2int")
exc_val = val
except Exception as _e:
if _config_mode == "strict":
self.Trans.LogWarning(f"异常被忽略: {_e}")
if IsStopIteration:
if Gen._stop_iter_flag_param is not None:
Gen.builder.store(ir.Constant(ir.IntType(1), 1), Gen._stop_iter_flag_param)
if Gen.func and Gen.func.type.pointee.return_type != ir.VoidType():
ret_type: ir.Type = Gen.func.type.pointee.return_type
if isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 0))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.BaseStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
else:
Gen.builder.ret(ir.Constant(ret_type, 0))
else:
Gen.builder.ret_void()
return
if Gen.eh_except_block_stack:
ExceptBB: ir.Block = Gen.eh_except_block_stack[-1][0]
EndBB: ir.Block | None = Gen.eh_except_block_stack[-1][1]
exception_code: ir.AllocaInstr = Gen.eh_except_block_stack[-1][2]
eh_message: ir.AllocaInstr = Gen.eh_except_block_stack[-1][3]
Gen._store(exc_val, exception_code)
if exc_msg:
Gen._store(exc_msg, eh_message)
else:
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen._store(null_ptr, eh_message)
if Gen.func and ExceptBB and ExceptBB.function is Gen.func:
Gen.builder.branch(ExceptBB)
else:
eh_msg_arg: ir.Argument | None = None
eh_code_arg: ir.Argument | None = None
if Gen.func:
for arg in Gen.func.args:
if arg.name == '__eh_msg_out__':
eh_msg_arg = arg
elif arg.name == '__eh_code_out__':
eh_code_arg = arg
if eh_msg_arg is not None:
if exc_msg:
Gen.builder.store(exc_msg, eh_msg_arg)
else:
null_ptr = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen.builder.store(null_ptr, eh_msg_arg)
if eh_code_arg is not None:
Gen.builder.store(exc_val, eh_code_arg)
if Gen.func and Gen.func.type.pointee.return_type == ir.IntType(32):
Gen.builder.ret(ir.Constant(ir.IntType(32), 1))
else:
Gen.builder.ret_void()
else:
eh_msg_arg: ir.Argument | None = None
eh_code_arg: ir.Argument | None = None
if Gen.func:
for arg in Gen.func.args:
if arg.name == '__eh_msg_out__':
eh_msg_arg = arg
elif arg.name == '__eh_code_out__':
eh_code_arg = arg
if eh_msg_arg is not None:
if exc_msg:
Gen.builder.store(exc_msg, eh_msg_arg)
else:
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
Gen.builder.store(null_ptr, eh_msg_arg)
if eh_code_arg is not None:
Gen.builder.store(exc_val, eh_code_arg)
if Gen.func and Gen.func.type.pointee.return_type == ir.IntType(32):
Gen.builder.ret(ir.Constant(ir.IntType(32), 1))
else:
Gen.builder.ret_void()
+227 -227
View File
@@ -1,228 +1,228 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
import ast
import llvmlite.ir as ir
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
"""LLVM IR 中不允许 void* 指针,Ptr(VoidType) 统一替换为 i8*"""
if isinstance(ty, ir.PointerType):
cur: ir.Type = ty
while isinstance(cur, ir.PointerType):
if isinstance(cur.pointee, ir.VoidType):
return ir.IntType(8).as_pointer()
cur = cur.pointee
return ty
class ReturnHandle(BaseHandle):
def _find_active_finally(self, Gen: "Translator.LlvmGen") -> tuple[ir.Block, ir.AllocaInstr, ir.AllocaInstr | None] | None:
for item in reversed(Gen.eh_finally_stack):
if item is not None and item[0] is not None:
return item
return None
def _HandleReturnLlvm(self, Node: ast.Return) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
# 闭包返回检测:当返回值是带 nonlocal 参数的嵌套函数时,创建闭包结构体
# 闭包结构体 {i8* env, i8* fn} 在堆上分配,env 指向 nonlocal 变量的堆存储
if Node.value and isinstance(Node.value, ast.Name):
ret_name: str = Node.value.id
if ret_name in Gen.nonlocal_params and ret_name in Gen.functions:
nested_fn: ir.Function = Gen.functions[ret_name]
nonlocal_params: list[tuple[str, ir.Type]] = Gen.nonlocal_params[ret_name]
fn_return_type: ir.Type = nested_fn.function_type.return_type
nonlocal_types: list[ir.Type] = [_safe_ptr_type(t) for _, t in nonlocal_params]
# 构建 env:收集所有 nonlocal 变量指针
env_ptrs: list[ir.Value] = []
for var_name, _ in nonlocal_params:
vp: ir.Value | None = Gen.variables.get(var_name)
if vp is not None:
env_ptrs.append(vp)
if env_ptrs:
# env 值:单个指针直接用,多个指针打包成结构体
i8_ptr_type: ir.PointerType = ir.IntType(8).as_pointer()
if len(env_ptrs) == 1:
env_val: ir.Value = Gen.builder.bitcast(env_ptrs[0], i8_ptr_type, name="closure_env")
else:
env_struct_type: ir.LiteralStructType = ir.LiteralStructType([p.type for p in env_ptrs])
env_size: int = len(env_ptrs) * 8
malloc_type: ir.FunctionType = ir.FunctionType(i8_ptr_type, [ir.IntType(64)])
malloc_func: ir.Function = Gen._get_or_declare_func('malloc', malloc_type)
env_raw: ir.Value = Gen.builder.call(malloc_func, [ir.Constant(ir.IntType(64), env_size)], name="env_struct_raw")
env_struct_ptr: ir.Value = Gen.builder.bitcast(env_raw, ir.PointerType(env_struct_type), name="env_struct_ptr")
for i, ptr in enumerate(env_ptrs):
gep: ir.Value = Gen.builder.gep(env_struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"env_field_{i}")
Gen._store(ptr, gep)
env_val = Gen.builder.bitcast(env_struct_ptr, i8_ptr_type, name="closure_env")
# fn 指针
fn_ptr: ir.Value = Gen.builder.bitcast(nested_fn, i8_ptr_type, name="closure_fn")
# 创建闭包结构体 {i8* env, i8* fn}
closure_type: ir.LiteralStructType = ir.LiteralStructType([i8_ptr_type, i8_ptr_type])
malloc_type2: ir.FunctionType = ir.FunctionType(i8_ptr_type, [ir.IntType(64)])
malloc_func2: ir.Function = Gen._get_or_declare_func('malloc', malloc_type2)
closure_raw: ir.Value = Gen.builder.call(malloc_func2, [ir.Constant(ir.IntType(64), 16)], name="closure_raw")
closure_ptr: ir.Value = Gen.builder.bitcast(closure_raw, ir.PointerType(closure_type), name="closure_ptr")
# 填充闭包结构体
env_gep: ir.Value = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="closure_env_slot")
Gen._store(env_val, env_gep)
fn_gep: ir.Value = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 1)], name="closure_fn_slot")
Gen._store(fn_ptr, fn_gep)
# 记录闭包返回类型
current_name: str = Gen.func.name if Gen.func else ''
short_name: str = current_name.split('.', 1)[1] if '.' in current_name else current_name
Gen._closure_return_types[short_name] = (fn_return_type, nonlocal_types)
# 返回闭包结构体指针(bitcast 到 i8*)
closure_ret: ir.Value = Gen.builder.bitcast(closure_ptr, i8_ptr_type, name="closure_ret")
Gen.emit_return(closure_ret)
return
active_finally: tuple[ir.Block, ir.AllocaInstr, ir.AllocaInstr | None] | None = self._find_active_finally(Gen)
if getattr(self.Trans, 'CurrentCReturnTypes', None) and self.Trans.CurrentCReturnTypes and Node.value:
return_values: list[ast.expr] = []
if isinstance(Node.value, ast.Tuple):
return_values = Node.value.elts
else:
return_values = [Node.value]
if len(return_values) == len(self.Trans.CurrentCReturnTypes):
llvm_vals: list[ir.Value] = []
expected_types: list[ir.Type] = []
for i, val in enumerate(return_values):
ReturnTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(self.Trans.CurrentCReturnTypes[i], self.Trans.SymbolTable)
IsRetPtr: bool = ReturnTypeInfo.IsPtr if ReturnTypeInfo else False
if (isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.IsStr) or (isinstance(self.Trans.CurrentCReturnTypes[i], ast.Name) and self.Trans.CurrentCReturnTypes[i].id == 'str'):
IsRetPtr = True
ExpectedType: ir.Type = ReturnTypeInfo.ToLLVM(Gen) if ReturnTypeInfo and ReturnTypeInfo.BaseType else Gen._CType2LLVM('int', IsRetPtr)
expected_types.append(ExpectedType)
Val: ir.Value | None = self.HandleExprLlvm(val)
if Val is None:
if isinstance(ExpectedType, ir.PointerType):
Val = ir.Constant(ExpectedType, None)
elif isinstance(ExpectedType, ir.IntType):
Val = ir.Constant(ExpectedType, 0)
else:
Val = ir.Constant(ExpectedType, ir.Undefined)
else:
if Val.type != ExpectedType:
if isinstance(ExpectedType, ir.IntType) and isinstance(Val.type, ir.IntType):
if Val.type.width < ExpectedType.width:
Val = Gen.builder.zext(Val, ExpectedType, name=f"zext_ret_{i}")
elif Val.type.width > ExpectedType.width:
Val = Gen.builder.trunc(Val, ExpectedType, name=f"trunc_ret_{i}")
elif isinstance(ExpectedType, ir.PointerType) and isinstance(Val.type, ir.PointerType):
Val = Gen.builder.bitcast(Val, ExpectedType, name=f"cast_ret_{i}")
llvm_vals.append(Val)
if llvm_vals:
struct_type: ir.LiteralStructType = ir.LiteralStructType(expected_types)
result: ir.Value = ir.Constant(struct_type, ir.Undefined)
for i, val in enumerate(llvm_vals):
result = Gen.builder.insert_value(result, val, i, name=f"ret_field_{i}")
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(result, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(result)
return
if Node.value:
Val: ir.Value | None = self.HandleExprLlvm(Node.value)
if Val:
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IdentifiedStructType) and isinstance(Val, ir.Constant) and isinstance(Val.type, ir.PointerType) and Val.constant is None:
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(zero_val, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(zero_val)
return
if getattr(self.Trans, 'CurrentCReturnTypes', None) and self.Trans.CurrentCReturnTypes:
struct_type: ir.Type = Gen.func.ftype.return_type
if isinstance(struct_type, ir.LiteralStructType):
result: ir.Value = ir.Constant(struct_type, ir.Undefined)
if isinstance(Val.type, ir.PointerType) and len(struct_type.elements) > 0 and isinstance(struct_type.elements[0], ir.PointerType):
result = Gen.builder.insert_value(result, Val, 0, name="ret_field_0")
for j in range(1, len(struct_type.elements)):
elem_type: ir.Type = struct_type.elements[j]
zero_val: ir.Constant = ir.Constant(elem_type, 0 if isinstance(elem_type, ir.IntType) else None)
result = Gen.builder.insert_value(result, zero_val, j, name=f"ret_field_{j}")
else:
for j, elem_type in enumerate(struct_type.elements):
if j == 0:
if Val.type == elem_type:
result = Gen.builder.insert_value(result, Val, j, name=f"ret_field_{j}")
elif isinstance(elem_type, ir.IntType) and isinstance(Val.type, ir.IntType):
if Val.type.width < elem_type.width:
Val = Gen.builder.zext(Val, elem_type, name=f"zext_ret_{j}")
elif Val.type.width > elem_type.width:
Val = Gen.builder.trunc(Val, elem_type, name=f"trunc_ret_{j}")
result = Gen.builder.insert_value(result, Val, j, name=f"ret_field_{j}")
else:
zero_val = ir.Constant(elem_type, 0 if isinstance(elem_type, ir.IntType) else None)
result = Gen.builder.insert_value(result, zero_val, j, name=f"ret_field_{j}")
else:
zero_val = ir.Constant(elem_type, 0 if isinstance(elem_type, ir.IntType) else None)
result = Gen.builder.insert_value(result, zero_val, j, name=f"ret_field_{j}")
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(result, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(result)
return
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(Val, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(Val)
else:
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 0))
elif isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.BaseStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
elif isinstance(ret_type, ir.ArrayType):
Gen.builder.ret(ir.Constant(ret_type, None))
else:
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo
import vast as ast
import llvmlite.ir as ir
def _safe_ptr_type(ty: ir.Type) -> ir.Type:
"""LLVM IR 中不允许 void* 指针,Ptr(VoidType) 统一替换为 i8*"""
if isinstance(ty, ir.PointerType):
cur: ir.Type = ty
while isinstance(cur, ir.PointerType):
if isinstance(cur.pointee, ir.VoidType):
return ir.IntType(8).as_pointer()
cur = cur.pointee
return ty
class ReturnHandle(BaseHandle):
def _find_active_finally(self, Gen: "Translator.LlvmGen") -> tuple[ir.Block, ir.AllocaInstr, ir.AllocaInstr | None] | None:
for item in reversed(Gen.eh_finally_stack):
if item is not None and item[0] is not None:
return item
return None
def _HandleReturnLlvm(self, Node: ast.Return) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
# 闭包返回检测:当返回值是带 nonlocal 参数的嵌套函数时,创建闭包结构体
# 闭包结构体 {i8* env, i8* fn} 在堆上分配,env 指向 nonlocal 变量的堆存储
if Node.value and isinstance(Node.value, ast.Name):
ret_name: str = Node.value.id
if ret_name in Gen.nonlocal_params and ret_name in Gen.functions:
nested_fn: ir.Function = Gen.functions[ret_name]
nonlocal_params: list[tuple[str, ir.Type]] = Gen.nonlocal_params[ret_name]
fn_return_type: ir.Type = nested_fn.function_type.return_type
nonlocal_types: list[ir.Type] = [_safe_ptr_type(t) for _, t in nonlocal_params]
# 构建 env:收集所有 nonlocal 变量指针
env_ptrs: list[ir.Value] = []
for var_name, _ in nonlocal_params:
vp: ir.Value | None = Gen.variables.get(var_name)
if vp is not None:
env_ptrs.append(vp)
if env_ptrs:
# env 值:单个指针直接用,多个指针打包成结构体
i8_ptr_type: ir.PointerType = ir.IntType(8).as_pointer()
if len(env_ptrs) == 1:
env_val: ir.Value = Gen.builder.bitcast(env_ptrs[0], i8_ptr_type, name="closure_env")
else:
env_struct_type: ir.LiteralStructType = ir.LiteralStructType([p.type for p in env_ptrs])
env_size: int = len(env_ptrs) * 8
malloc_type: ir.FunctionType = ir.FunctionType(i8_ptr_type, [ir.IntType(64)])
malloc_func: ir.Function = Gen._get_or_declare_func('malloc', malloc_type)
env_raw: ir.Value = Gen.builder.call(malloc_func, [ir.Constant(ir.IntType(64), env_size)], name="env_struct_raw")
env_struct_ptr: ir.Value = Gen.builder.bitcast(env_raw, ir.PointerType(env_struct_type), name="env_struct_ptr")
for i, ptr in enumerate(env_ptrs):
gep: ir.Value = Gen.builder.gep(env_struct_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), i)], name=f"env_field_{i}")
Gen._store(ptr, gep)
env_val = Gen.builder.bitcast(env_struct_ptr, i8_ptr_type, name="closure_env")
# fn 指针
fn_ptr: ir.Value = Gen.builder.bitcast(nested_fn, i8_ptr_type, name="closure_fn")
# 创建闭包结构体 {i8* env, i8* fn}
closure_type: ir.LiteralStructType = ir.LiteralStructType([i8_ptr_type, i8_ptr_type])
malloc_type2: ir.FunctionType = ir.FunctionType(i8_ptr_type, [ir.IntType(64)])
malloc_func2: ir.Function = Gen._get_or_declare_func('malloc', malloc_type2)
closure_raw: ir.Value = Gen.builder.call(malloc_func2, [ir.Constant(ir.IntType(64), 16)], name="closure_raw")
closure_ptr: ir.Value = Gen.builder.bitcast(closure_raw, ir.PointerType(closure_type), name="closure_ptr")
# 填充闭包结构体
env_gep: ir.Value = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="closure_env_slot")
Gen._store(env_val, env_gep)
fn_gep: ir.Value = Gen.builder.gep(closure_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 1)], name="closure_fn_slot")
Gen._store(fn_ptr, fn_gep)
# 记录闭包返回类型
current_name: str = Gen.func.name if Gen.func else ''
short_name: str = current_name.split('.', 1)[1] if '.' in current_name else current_name
Gen._closure_return_types[short_name] = (fn_return_type, nonlocal_types)
# 返回闭包结构体指针(bitcast 到 i8*)
closure_ret: ir.Value = Gen.builder.bitcast(closure_ptr, i8_ptr_type, name="closure_ret")
Gen.emit_return(closure_ret)
return
active_finally: tuple[ir.Block, ir.AllocaInstr, ir.AllocaInstr | None] | None = self._find_active_finally(Gen)
if getattr(self.Trans, 'CurrentCReturnTypes', None) and self.Trans.CurrentCReturnTypes and Node.value:
return_values: list[ast.expr] = []
if isinstance(Node.value, ast.Tuple):
return_values = Node.value.elts
else:
return_values = [Node.value]
if len(return_values) == len(self.Trans.CurrentCReturnTypes):
llvm_vals: list[ir.Value] = []
expected_types: list[ir.Type] = []
for i, val in enumerate(return_values):
ReturnTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(self.Trans.CurrentCReturnTypes[i], self.Trans.SymbolTable)
IsRetPtr: bool = ReturnTypeInfo.IsPtr if ReturnTypeInfo else False
if (isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.IsStr) or (isinstance(self.Trans.CurrentCReturnTypes[i], ast.Name) and self.Trans.CurrentCReturnTypes[i].id == 'str'):
IsRetPtr = True
ExpectedType: ir.Type = ReturnTypeInfo.ToLLVM(Gen) if ReturnTypeInfo and ReturnTypeInfo.BaseType else Gen._CType2LLVM('int', IsRetPtr)
expected_types.append(ExpectedType)
Val: ir.Value | None = self.HandleExprLlvm(val)
if Val is None:
if isinstance(ExpectedType, ir.PointerType):
Val = ir.Constant(ExpectedType, None)
elif isinstance(ExpectedType, ir.IntType):
Val = ir.Constant(ExpectedType, 0)
else:
Val = ir.Constant(ExpectedType, ir.Undefined)
else:
if Val.type != ExpectedType:
if isinstance(ExpectedType, ir.IntType) and isinstance(Val.type, ir.IntType):
if Val.type.width < ExpectedType.width:
Val = Gen.builder.zext(Val, ExpectedType, name=f"zext_ret_{i}")
elif Val.type.width > ExpectedType.width:
Val = Gen.builder.trunc(Val, ExpectedType, name=f"trunc_ret_{i}")
elif isinstance(ExpectedType, ir.PointerType) and isinstance(Val.type, ir.PointerType):
Val = Gen.builder.bitcast(Val, ExpectedType, name=f"cast_ret_{i}")
llvm_vals.append(Val)
if llvm_vals:
struct_type: ir.LiteralStructType = ir.LiteralStructType(expected_types)
result: ir.Value = ir.Constant(struct_type, ir.Undefined)
for i, val in enumerate(llvm_vals):
result = Gen.builder.insert_value(result, val, i, name=f"ret_field_{i}")
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(result, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(result)
return
if Node.value:
Val: ir.Value | None = self.HandleExprLlvm(Node.value)
if Val:
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IdentifiedStructType) and isinstance(Val, ir.Constant) and isinstance(Val.type, ir.PointerType) and Val.constant is None:
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(zero_val, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(zero_val)
return
if getattr(self.Trans, 'CurrentCReturnTypes', None) and self.Trans.CurrentCReturnTypes:
struct_type: ir.Type = Gen.func.ftype.return_type
if isinstance(struct_type, ir.LiteralStructType):
result: ir.Value = ir.Constant(struct_type, ir.Undefined)
if isinstance(Val.type, ir.PointerType) and len(struct_type.elements) > 0 and isinstance(struct_type.elements[0], ir.PointerType):
result = Gen.builder.insert_value(result, Val, 0, name="ret_field_0")
for j in range(1, len(struct_type.elements)):
elem_type: ir.Type = struct_type.elements[j]
zero_val: ir.Constant = ir.Constant(elem_type, 0 if isinstance(elem_type, ir.IntType) else None)
result = Gen.builder.insert_value(result, zero_val, j, name=f"ret_field_{j}")
else:
for j, elem_type in enumerate(struct_type.elements):
if j == 0:
if Val.type == elem_type:
result = Gen.builder.insert_value(result, Val, j, name=f"ret_field_{j}")
elif isinstance(elem_type, ir.IntType) and isinstance(Val.type, ir.IntType):
if Val.type.width < elem_type.width:
Val = Gen.builder.zext(Val, elem_type, name=f"zext_ret_{j}")
elif Val.type.width > elem_type.width:
Val = Gen.builder.trunc(Val, elem_type, name=f"trunc_ret_{j}")
result = Gen.builder.insert_value(result, Val, j, name=f"ret_field_{j}")
else:
zero_val = ir.Constant(elem_type, 0 if isinstance(elem_type, ir.IntType) else None)
result = Gen.builder.insert_value(result, zero_val, j, name=f"ret_field_{j}")
else:
zero_val = ir.Constant(elem_type, 0 if isinstance(elem_type, ir.IntType) else None)
result = Gen.builder.insert_value(result, zero_val, j, name=f"ret_field_{j}")
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(result, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(result)
return
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
if pending_ret_val:
Gen._store(Val, pending_ret_val)
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
Gen.emit_return(Val)
else:
if active_finally:
FinallyBB: ir.Block = active_finally[0]
pending_ret_flag: ir.AllocaInstr = active_finally[1]
pending_ret_val: ir.AllocaInstr | None = active_finally[2]
Gen._store(ir.Constant(ir.IntType(32), 1), pending_ret_flag)
Gen.builder.branch(FinallyBB)
else:
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 0))
elif isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.BaseStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
elif isinstance(ret_type, ir.ArrayType):
Gen.builder.ret(ir.Constant(ret_type, None))
else:
Gen.emit_return()
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
from typing import TYPE_CHECKING
import ast
import vast as ast
import llvmlite.ir as ir
if TYPE_CHECKING:
from lib.core.translator import Translator
+213 -213
View File
@@ -1,214 +1,214 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
import ast
import llvmlite.ir as ir
class TryHandle(BaseHandle):
def _get_exception_type_code(self, type_node: ast.expr) -> int:
if isinstance(type_node, ast.Name):
name: str = type_node.id
if name in EXCEPTION_CODE_MAP:
return EXCEPTION_CODE_MAP[name]
if name in self.Trans.exception_registry:
return self.Trans.exception_registry[name]
return 1
if isinstance(type_node, ast.Attribute):
name: str = type_node.attr
if name in EXCEPTION_CODE_MAP:
return EXCEPTION_CODE_MAP[name]
if name in self.Trans.exception_registry:
return self.Trans.exception_registry[name]
return 1
if isinstance(type_node, ast.Tuple):
for elt in type_node.elts:
code: int = self._get_exception_type_code(elt)
if code != 1:
return code
return 1
return 1
def _get_exception_name(self, type_node: ast.expr) -> str | None:
if isinstance(type_node, ast.Name):
return type_node.id
if isinstance(type_node, ast.Attribute):
return type_node.attr
return None
def _get_all_match_codes(self, type_node: ast.expr) -> list[int]:
name: str | None = self._get_exception_name(type_node)
if name is None:
return [self._get_exception_type_code(type_node)]
if name not in self.Trans.exception_registry:
return [self._get_exception_type_code(type_node)]
codes: list[int] = [self.Trans.exception_registry[name]]
self._collect_descendant_codes(name, codes)
return codes
def _collect_descendant_codes(self, parent_name: str, codes: list[int]) -> None:
for child_name, parent in self.Trans.exception_parents.items():
if parent == parent_name:
child_code: int | None = self.Trans.exception_registry.get(child_name)
if child_code is not None and child_code not in codes:
codes.append(child_code)
self._collect_descendant_codes(child_name, codes)
def _HandleTryLlvm(self, Node: ast.Try) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if not Gen.func:
return
exception_code: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name="eh_code")
eh_message: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name="eh_message")
Gen._store(ir.Constant(ir.IntType(32), 0), exception_code)
Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), eh_message)
HasFinally: bool = bool(Node.finalbody)
if HasFinally:
Gen.eh_finally_stack.append((None, None, None))
else:
Gen.eh_finally_stack.append(None)
DispatchBB: ir.Block = Gen.func.append_basic_block(name="try.dispatch")
AfterBB: ir.Block = Gen.func.append_basic_block(name="try.after")
except_blocks: list[tuple[ir.Block, list[int], ast.ExceptHandler]] = []
for i, handler in enumerate(Node.handlers):
ExcTypeCodes: list[int] = [1]
if handler.type:
ExcTypeCodes = self._get_all_match_codes(handler.type)
ExceptBB: ir.Block = Gen.func.append_basic_block(name=f"try.except_{i}")
Gen.eh_except_block_stack.append((DispatchBB, None, exception_code, eh_message))
except_blocks.append((ExceptBB, ExcTypeCodes, handler))
TryBB: ir.Block = Gen.func.append_basic_block(name="try.body")
ElseBB: ir.Block | None = None
if Node.orelse:
ElseBB = Gen.func.append_basic_block(name="try.else")
Gen.builder.branch(TryBB)
Gen.builder.position_at_start(TryBB)
self.HandleBodyLlvm(Node.body)
if not Gen.builder.block.is_terminated:
Gen._store(ir.Constant(ir.IntType(32), 0), exception_code)
if ElseBB:
Gen.builder.branch(ElseBB)
else:
Gen.builder.branch(AfterBB)
Gen.builder.position_at_start(DispatchBB)
eh_code_val: ir.Value = Gen._load(exception_code, name="Load_eh_code")
next_check_bb: ir.Block | None = None
handler_var_map: dict[int, tuple[str, ir.AllocaInstr, ir.Value | None]] = {}
UnhandledBB: ir.Block = Gen.func.append_basic_block(name="try.unhandled")
for i, (ExceptBB, ExcTypeCodes, handler) in enumerate(except_blocks):
MatchBB: ir.Block = Gen.func.append_basic_block(name=f"try.match_{i}")
if i > 0 and next_check_bb is not None:
Gen.builder.position_at_start(next_check_bb)
eh_code_val = Gen._load(exception_code, name=f"Load_eh_code_{i}")
if handler.type is None:
Gen.builder.branch(MatchBB)
else:
match_cond: ir.Value | None = None
for code in ExcTypeCodes:
is_code_match: ir.Value = Gen.builder.icmp_signed('==', eh_code_val, ir.Constant(ir.IntType(32), code), name=f"eh_match_{i}_c{code}")
if match_cond is None:
match_cond = is_code_match
else:
match_cond = Gen.builder.or_(match_cond, is_code_match, name=f"eh_match_any_{i}")
if i < len(except_blocks) - 1:
next_check_bb = Gen.func.append_basic_block(name=f"try.check_{i}")
else:
next_check_bb = UnhandledBB
Gen.builder.cbranch(match_cond, MatchBB, next_check_bb)
Gen.builder.position_at_start(MatchBB)
if handler.name:
var_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(8).as_pointer(), name=handler.name)
eh_msg_val: ir.Value = Gen._load(eh_message, name=f"Load_eh_msg_{handler.name}")
Gen._store(eh_msg_val, var_alloca)
handler_var_map[i] = (handler.name, var_alloca, Gen.variables.get(handler.name))
Gen.builder.branch(ExceptBB)
Gen.builder.position_at_start(UnhandledBB)
eh_msg_out_arg: ir.Argument | None = None
eh_code_out_arg: ir.Argument | None = None
if Gen.func and len(Gen.func.args) > 0:
for arg in Gen.func.args:
if arg.name == '__eh_msg_out__':
eh_msg_out_arg = arg
elif arg.name == '__eh_code_out__':
eh_code_out_arg = arg
if eh_msg_out_arg is not None and eh_code_out_arg is not None:
eh_msg_val: ir.Value = Gen._load(eh_message, name="Load_eh_msg_propagate")
eh_code_val_prop: ir.Value = Gen._load(exception_code, name="Load_eh_code_propagate")
Gen._store(eh_msg_val, eh_msg_out_arg)
Gen._store(eh_code_val_prop, eh_code_out_arg)
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IdentifiedStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
elif isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 1))
elif isinstance(ret_type, ir.VoidType):
Gen.builder.ret_void()
else:
Gen.builder.ret(ir.Constant(ir.IntType(32), 1))
else:
Gen._store(ir.Constant(ir.IntType(32), 0), exception_code)
Gen.builder.branch(AfterBB)
for idx, (ExceptBB, ExcTypeCodes, handler) in enumerate(except_blocks):
Gen.builder.position_at_start(ExceptBB)
old_var: ir.Value | None = None
if idx in handler_var_map:
hname: str = handler_var_map[idx][0]
var_alloca: ir.AllocaInstr = handler_var_map[idx][1]
old_var = handler_var_map[idx][2]
Gen.variables[hname] = var_alloca
self.HandleBodyLlvm(handler.body)
if idx in handler_var_map:
hname = handler_var_map[idx][0]
old_var = handler_var_map[idx][2]
if old_var is not None:
Gen.variables[hname] = old_var
elif hname in Gen.variables:
del Gen.variables[hname]
if not Gen.builder.block.is_terminated:
Gen.builder.branch(AfterBB)
Gen.eh_except_block_stack.pop()
if ElseBB:
Gen.builder.position_at_start(ElseBB)
self.HandleBodyLlvm(Node.orelse)
if not Gen.builder.block.is_terminated:
Gen.builder.branch(AfterBB)
Gen.builder.position_at_start(AfterBB)
if HasFinally:
Gen.eh_finally_stack.pop()
if Node.finalbody:
FinallyBB: ir.Block = Gen.func.append_basic_block(name="try.finally")
pending_ret_flag: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name="pending_ret_flag")
pending_ret_val: ir.AllocaInstr | None = Gen._allocaEntry(ir.IntType(32), name="pending_ret_val") if Gen.func.type.pointee.return_type == ir.IntType(32) else None
Gen.eh_finally_stack.append((FinallyBB, pending_ret_flag, pending_ret_val))
Gen.builder.branch(FinallyBB)
Gen.builder.position_at_start(FinallyBB)
Gen._store(ir.Constant(ir.IntType(32), 0), pending_ret_flag)
self.HandleBodyLlvm(Node.finalbody)
if Gen.eh_finally_stack:
_, pending_ret_flag, pending_ret_val = Gen.eh_finally_stack[-1]
Gen.eh_finally_stack.pop()
ret_flag_val: ir.Value = Gen._load(pending_ret_flag, name="Load_ret_flag")
ret_flag_is_set: ir.Value = Gen.builder.icmp_signed('!=', ret_flag_val, ir.Constant(ir.IntType(32), 0), name="check_ret_flag")
RetBB: ir.Block = Gen.func.append_basic_block(name="finally.ret")
ContinueBB: ir.Block = Gen.func.append_basic_block(name="try.continue")
Gen.builder.cbranch(ret_flag_is_set, RetBB, ContinueBB)
Gen.builder.position_at_start(RetBB)
if pending_ret_val:
ret_val: ir.Value = Gen._load(pending_ret_val, name="Load_ret_val")
Gen.builder.ret(ret_val)
else:
Gen.builder.ret_void()
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, EXCEPTION_CODE_MAP
import vast as ast
import llvmlite.ir as ir
class TryHandle(BaseHandle):
def _get_exception_type_code(self, type_node: ast.expr) -> int:
if isinstance(type_node, ast.Name):
name: str = type_node.id
if name in EXCEPTION_CODE_MAP:
return EXCEPTION_CODE_MAP[name]
if name in self.Trans.exception_registry:
return self.Trans.exception_registry[name]
return 1
if isinstance(type_node, ast.Attribute):
name: str = type_node.attr
if name in EXCEPTION_CODE_MAP:
return EXCEPTION_CODE_MAP[name]
if name in self.Trans.exception_registry:
return self.Trans.exception_registry[name]
return 1
if isinstance(type_node, ast.Tuple):
for elt in type_node.elts:
code: int = self._get_exception_type_code(elt)
if code != 1:
return code
return 1
return 1
def _get_exception_name(self, type_node: ast.expr) -> str | None:
if isinstance(type_node, ast.Name):
return type_node.id
if isinstance(type_node, ast.Attribute):
return type_node.attr
return None
def _get_all_match_codes(self, type_node: ast.expr) -> list[int]:
name: str | None = self._get_exception_name(type_node)
if name is None:
return [self._get_exception_type_code(type_node)]
if name not in self.Trans.exception_registry:
return [self._get_exception_type_code(type_node)]
codes: list[int] = [self.Trans.exception_registry[name]]
self._collect_descendant_codes(name, codes)
return codes
def _collect_descendant_codes(self, parent_name: str, codes: list[int]) -> None:
for child_name, parent in self.Trans.exception_parents.items():
if parent == parent_name:
child_code: int | None = self.Trans.exception_registry.get(child_name)
if child_code is not None and child_code not in codes:
codes.append(child_code)
self._collect_descendant_codes(child_name, codes)
def _HandleTryLlvm(self, Node: ast.Try) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if not Gen.func:
return
exception_code: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name="eh_code")
eh_message: ir.AllocaInstr = Gen._allocaEntry(ir.PointerType(ir.IntType(8)), name="eh_message")
Gen._store(ir.Constant(ir.IntType(32), 0), exception_code)
Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), eh_message)
HasFinally: bool = bool(Node.finalbody)
if HasFinally:
Gen.eh_finally_stack.append((None, None, None))
else:
Gen.eh_finally_stack.append(None)
DispatchBB: ir.Block = Gen.func.append_basic_block(name="try.dispatch")
AfterBB: ir.Block = Gen.func.append_basic_block(name="try.after")
except_blocks: list[tuple[ir.Block, list[int], ast.ExceptHandler]] = []
for i, handler in enumerate(Node.handlers):
ExcTypeCodes: list[int] = [1]
if handler.type:
ExcTypeCodes = self._get_all_match_codes(handler.type)
ExceptBB: ir.Block = Gen.func.append_basic_block(name=f"try.except_{i}")
Gen.eh_except_block_stack.append((DispatchBB, None, exception_code, eh_message))
except_blocks.append((ExceptBB, ExcTypeCodes, handler))
TryBB: ir.Block = Gen.func.append_basic_block(name="try.body")
ElseBB: ir.Block | None = None
if Node.orelse:
ElseBB = Gen.func.append_basic_block(name="try.else")
Gen.builder.branch(TryBB)
Gen.builder.position_at_start(TryBB)
self.HandleBodyLlvm(Node.body)
if not Gen.builder.block.is_terminated:
Gen._store(ir.Constant(ir.IntType(32), 0), exception_code)
if ElseBB:
Gen.builder.branch(ElseBB)
else:
Gen.builder.branch(AfterBB)
Gen.builder.position_at_start(DispatchBB)
eh_code_val: ir.Value = Gen._load(exception_code, name="Load_eh_code")
next_check_bb: ir.Block | None = None
handler_var_map: dict[int, tuple[str, ir.AllocaInstr, ir.Value | None]] = {}
UnhandledBB: ir.Block = Gen.func.append_basic_block(name="try.unhandled")
for i, (ExceptBB, ExcTypeCodes, handler) in enumerate(except_blocks):
MatchBB: ir.Block = Gen.func.append_basic_block(name=f"try.match_{i}")
if i > 0 and next_check_bb is not None:
Gen.builder.position_at_start(next_check_bb)
eh_code_val = Gen._load(exception_code, name=f"Load_eh_code_{i}")
if handler.type is None:
Gen.builder.branch(MatchBB)
else:
match_cond: ir.Value | None = None
for code in ExcTypeCodes:
is_code_match: ir.Value = Gen.builder.icmp_signed('==', eh_code_val, ir.Constant(ir.IntType(32), code), name=f"eh_match_{i}_c{code}")
if match_cond is None:
match_cond = is_code_match
else:
match_cond = Gen.builder.or_(match_cond, is_code_match, name=f"eh_match_any_{i}")
if i < len(except_blocks) - 1:
next_check_bb = Gen.func.append_basic_block(name=f"try.check_{i}")
else:
next_check_bb = UnhandledBB
Gen.builder.cbranch(match_cond, MatchBB, next_check_bb)
Gen.builder.position_at_start(MatchBB)
if handler.name:
var_alloca: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(8).as_pointer(), name=handler.name)
eh_msg_val: ir.Value = Gen._load(eh_message, name=f"Load_eh_msg_{handler.name}")
Gen._store(eh_msg_val, var_alloca)
handler_var_map[i] = (handler.name, var_alloca, Gen.variables.get(handler.name))
Gen.builder.branch(ExceptBB)
Gen.builder.position_at_start(UnhandledBB)
eh_msg_out_arg: ir.Argument | None = None
eh_code_out_arg: ir.Argument | None = None
if Gen.func and len(Gen.func.args) > 0:
for arg in Gen.func.args:
if arg.name == '__eh_msg_out__':
eh_msg_out_arg = arg
elif arg.name == '__eh_code_out__':
eh_code_out_arg = arg
if eh_msg_out_arg is not None and eh_code_out_arg is not None:
eh_msg_val: ir.Value = Gen._load(eh_message, name="Load_eh_msg_propagate")
eh_code_val_prop: ir.Value = Gen._load(exception_code, name="Load_eh_code_propagate")
Gen._store(eh_msg_val, eh_msg_out_arg)
Gen._store(eh_code_val_prop, eh_code_out_arg)
ret_type: ir.Type | None = getattr(Gen.func, 'ftype', None) and Gen.func.ftype.return_type if getattr(Gen, 'func', None) else None
if isinstance(ret_type, ir.IdentifiedStructType):
if ret_type.elements:
zero_val: ir.Constant = ir.Constant(ret_type, [
ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType))
else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType))
else ir.Constant(et, ir.Undefined) for et in ret_type.elements])
else:
zero_val = ir.Constant(ret_type, None)
Gen.builder.ret(zero_val)
elif isinstance(ret_type, ir.PointerType):
Gen.builder.ret(ir.Constant(ret_type, None))
elif isinstance(ret_type, (ir.FloatType, ir.DoubleType)):
Gen.builder.ret(ir.Constant(ret_type, 0.0))
elif isinstance(ret_type, ir.IntType):
Gen.builder.ret(ir.Constant(ret_type, 1))
elif isinstance(ret_type, ir.VoidType):
Gen.builder.ret_void()
else:
Gen.builder.ret(ir.Constant(ir.IntType(32), 1))
else:
Gen._store(ir.Constant(ir.IntType(32), 0), exception_code)
Gen.builder.branch(AfterBB)
for idx, (ExceptBB, ExcTypeCodes, handler) in enumerate(except_blocks):
Gen.builder.position_at_start(ExceptBB)
old_var: ir.Value | None = None
if idx in handler_var_map:
hname: str = handler_var_map[idx][0]
var_alloca: ir.AllocaInstr = handler_var_map[idx][1]
old_var = handler_var_map[idx][2]
Gen.variables[hname] = var_alloca
self.HandleBodyLlvm(handler.body)
if idx in handler_var_map:
hname = handler_var_map[idx][0]
old_var = handler_var_map[idx][2]
if old_var is not None:
Gen.variables[hname] = old_var
elif hname in Gen.variables:
del Gen.variables[hname]
if not Gen.builder.block.is_terminated:
Gen.builder.branch(AfterBB)
Gen.eh_except_block_stack.pop()
if ElseBB:
Gen.builder.position_at_start(ElseBB)
self.HandleBodyLlvm(Node.orelse)
if not Gen.builder.block.is_terminated:
Gen.builder.branch(AfterBB)
Gen.builder.position_at_start(AfterBB)
if HasFinally:
Gen.eh_finally_stack.pop()
if Node.finalbody:
FinallyBB: ir.Block = Gen.func.append_basic_block(name="try.finally")
pending_ret_flag: ir.AllocaInstr = Gen._allocaEntry(ir.IntType(32), name="pending_ret_flag")
pending_ret_val: ir.AllocaInstr | None = Gen._allocaEntry(ir.IntType(32), name="pending_ret_val") if Gen.func.type.pointee.return_type == ir.IntType(32) else None
Gen.eh_finally_stack.append((FinallyBB, pending_ret_flag, pending_ret_val))
Gen.builder.branch(FinallyBB)
Gen.builder.position_at_start(FinallyBB)
Gen._store(ir.Constant(ir.IntType(32), 0), pending_ret_flag)
self.HandleBodyLlvm(Node.finalbody)
if Gen.eh_finally_stack:
_, pending_ret_flag, pending_ret_val = Gen.eh_finally_stack[-1]
Gen.eh_finally_stack.pop()
ret_flag_val: ir.Value = Gen._load(pending_ret_flag, name="Load_ret_flag")
ret_flag_is_set: ir.Value = Gen.builder.icmp_signed('!=', ret_flag_val, ir.Constant(ir.IntType(32), 0), name="check_ret_flag")
RetBB: ir.Block = Gen.func.append_basic_block(name="finally.ret")
ContinueBB: ir.Block = Gen.func.append_basic_block(name="try.continue")
Gen.builder.cbranch(ret_flag_is_set, RetBB, ContinueBB)
Gen.builder.position_at_start(RetBB)
if pending_ret_val:
ret_val: ir.Value = Gen._load(pending_ret_val, name="Load_ret_val")
Gen.builder.ret(ret_val)
else:
Gen.builder.ret_void()
Gen.builder.position_at_start(ContinueBB)
+1 -1
View File
@@ -7,7 +7,7 @@ from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo
from lib.core.SymbolUtils import IsTModule, IsListAnnotation
from lib.includes import t
import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes)
import ast
import vast as ast
class HandlesTypeMerge(BaseHandle):
+211 -211
View File
@@ -1,212 +1,212 @@
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle
import ast
import llvmlite.ir as ir
class WithHandle(BaseHandle):
def _ResolveInheritedMethod(self, ClassName: str, MethodName: str, Gen: "Translator.LlvmGen") -> tuple[str | None, "ir.Function | None"]:
"""沿继承链查找方法,返回 (定义方法的类名, 函数对象)
当子类未覆写 __enter__/__exit__ 等方法时,沿 Gen.class_parent 链查找父类实现。
_generate_inherited_method_wrappers 只为 class_methods[ClassName] 中的方法生成包装,
不包括父类独有方法,因此 with 语句需要单独处理继承查找。
"""
# 先检查子类自身:必须有函数体(define)才算真方法。
# 子类继承方法的 stub declare(如 MemPool.__enter__,Phase1 生成的继承
# 包装声明)无函数体且 TPC 不生成对应定义,直接调用会链接 undefined,
# 必须跳过并沿 MRO 找父类实现。
FullName: str = f'{ClassName}.{MethodName}'
func = Gen._find_function(FullName) if hasattr(Gen, '_find_function') else Gen.functions.get(FullName)
if func and len(list(func.blocks)) > 0:
return (ClassName, func)
# 沿 MRO 继承链查找(多继承:先声明父类优先)。
# 祖先的 stub declare(无函数体)是真实定义的声明(定义在其模块 obj 中),
# 可直接调用——与 _HandleMethodCallLlvm 的 MRO 回退语义一致。
for anc in Gen._get_mro_chain(ClassName)[1:]:
parent_full: str = f'{anc}.{MethodName}'
parent_func = Gen._find_function(parent_full) if hasattr(Gen, '_find_function') else Gen.functions.get(parent_full)
if parent_func:
return (anc, parent_func)
return (None, None)
def _HandleWithLlvm(self, Node: ast.With) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
for item in Node.items:
context_expr: ast.expr = item.context_expr
asname: str | None = None
target: ast.expr | None = getattr(item, 'optional_vars', getattr(item, 'target', getattr(item, 'asname', None)))
if target and isinstance(target, ast.Name):
asname = target.id
ClassName: str | None = None
if isinstance(context_expr, ast.Call):
if isinstance(context_expr.func, ast.Name):
ClassName = context_expr.func.id
elif isinstance(context_expr.func, ast.Attribute):
ClassName = context_expr.func.attr
if ClassName and ClassName not in Gen.structs:
SymInfo: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(ClassName)
if SymInfo:
if SymInfo.IsStruct or SymInfo.IsRenum:
pass
else:
ClassName = None
else:
ClassName = None
elif isinstance(context_expr, ast.Name):
VarName: str = context_expr.id
if VarName in Gen.var_struct_class:
ClassName = Gen.var_struct_class[VarName]
ctx_val: ir.Value | None = self.HandleExprLlvm(context_expr)
if not ctx_val:
continue
original_ctx_val: ir.Value = ctx_val
if ClassName and isinstance(ctx_val.type, ir.PointerType):
pointee: ir.Type = ctx_val.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
ClassName = CN
StructPtrType: ir.PointerType | None = ir.PointerType(Gen.structs[ClassName]) if ClassName and ClassName in Gen.structs else None
if StructPtrType and isinstance(ctx_val.type, ir.PointerType) and ctx_val.type.pointee != Gen.structs.get(ClassName):
ctx_val = Gen.builder.bitcast(ctx_val, StructPtrType, name="cast_with_ctx")
ctx_alloca: ir.AllocaInstr = Gen._allocaEntry(ctx_val.type, name="__with_ctx")
Gen._store(ctx_val, ctx_alloca)
Gen._UnregisterTempPtr(ctx_val)
if ClassName:
Gen.var_struct_class['__with_ctx'] = ClassName
Gen._var_to_heap_ptr['__with_ctx'] = ctx_val
original_asname_heap_ptr: ir.Value | None = Gen._var_to_heap_ptr.get(asname) if asname else None
ctx_is_var_ref: bool = isinstance(context_expr, ast.Name)
enter_result: ir.Value | None = None
if ClassName:
# 沿继承链查找 __enter__(子类可能未覆写,使用父类实现)
enter_class, enter_func = self._ResolveInheritedMethod(ClassName, '__enter__', Gen)
if enter_func is not None:
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
# 父类方法需要 self 为父类指针类型,bitcast 子类指针。
# bitcast 目标用 enter_func 签名参数类型(args[0] 本身即
# MemManager* 指针),避免 Gen.structs 查找返回 opaque/异
# sha1 占位对象导致 llvmlite 类型不匹配。
if enter_class != ClassName:
parent_ptr_type: ir.Type = enter_func.function_type.args[0]
if ctx_Loaded.type != parent_ptr_type:
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_enter_self")
enter_call: ir.CallInstr = Gen.builder.call(enter_func, [ctx_Loaded], name="call_enter")
enter_result = enter_call
if isinstance(enter_call.type, ir.PointerType):
pointee: ir.Type = enter_call.type.pointee
if isinstance(pointee, ir.PointerType) and isinstance(pointee.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
enter_result = Gen._load(enter_call, name="enter_deref")
elif self._is_char_pointer(enter_call) and StructPtrType:
enter_result = Gen.builder.bitcast(enter_call, StructPtrType, name="cast_enter")
if asname and enter_result:
if isinstance(enter_result.type, ir.PointerType) and isinstance(enter_result.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(enter_result.type.pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
Gen.var_struct_class[asname] = CN
Gen._var_to_heap_ptr[asname] = enter_result
Gen._register_local_heap_ptr(enter_result, VarName=asname)
if asname in Gen._reg_values:
OldVal: ir.Value = Gen._reg_values[asname]
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=asname)
Gen._store(OldVal, var)
Gen.variables[asname] = var
del Gen._reg_values[asname]
if asname in Gen.variables and Gen.variables[asname] is not None:
VarPtr: ir.Value = Gen.variables[asname]
if VarPtr.type.pointee == enter_result.type:
Gen._store(enter_result, VarPtr)
elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(enter_result.type, ir.PointerType):
CastedVal: ir.Value = Gen.builder.bitcast(enter_result, VarPtr.type.pointee, name=f"cast_{asname}")
Gen._store(CastedVal, VarPtr)
else:
NewVar: ir.AllocaInstr = Gen._alloca(enter_result.type, name=asname)
Gen._store(enter_result, NewVar)
Gen.variables[asname] = NewVar
else:
Gen._reg_values[asname] = enter_result
Gen.variables[asname] = None
# 如果类有 __provides__,自动生成 push 代码
if ClassName and ClassName in Gen.class_provides:
# 编译期检查: with provider 栈深度是否超过 _MAX_PROVIDER_DEPTH (32)
# _with_provides_stack 只反映当前嵌套路径,顺序执行的 with 会在退出时弹栈
current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack)
new_fields: int = len(Gen.class_provides[ClassName])
if current_depth + new_fields > 32:
raise SyntaxError(
f"[TransPyC] with provider stack overflow at line {Node.lineno}: "
f"current depth {current_depth} + new {new_fields} fields > max 32. "
f"Class '{ClassName}' provides too many fields or with nesting too deep."
)
# 编译期压栈:记录当前 with 上下文提供的字段名,供 __require_must__ 静态检查
Gen._with_provides_stack.append(Gen.class_provides[ClassName])
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
ctx_i8: ir.Value = ctx_Loaded
if isinstance(ctx_Loaded.type, ir.PointerType) and ctx_Loaded.type != ir.PointerType(ir.IntType(8)):
ctx_i8 = Gen.builder.bitcast(ctx_Loaded, ir.PointerType(ir.IntType(8)), name="ctx_push_i8")
for field_name in Gen.class_provides[ClassName]:
field_str: ir.Value = Gen._create_string_global(field_name)
push_func = Gen.functions.get('_push_provider')
if push_func:
Gen.builder.call(push_func, [field_str, ctx_i8], name="call_push_provider")
self.Trans.VarScopes.append({})
if asname:
self.Trans.VarScopes[-1][asname] = True
self.HandleBodyLlvm(Node.body)
if self.Trans.VarScopes:
self.Trans.VarScopes.pop()
# 如果类有 __provides__,自动生成 pop 代码
if ClassName and ClassName in Gen.class_provides:
for _ in Gen.class_provides[ClassName]:
pop_func = Gen.functions.get('_pop_provider')
if pop_func:
Gen.builder.call(pop_func, [], name="call_pop_provider")
# 编译期弹栈
if Gen._with_provides_stack:
Gen._with_provides_stack.pop()
if ClassName:
# 沿继承链查找 __exit__(子类可能未覆写,使用父类实现)
exit_class, exit_func = self._ResolveInheritedMethod(ClassName, '__exit__', Gen)
if exit_func is not None:
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
# 父类方法需要 self 为父类指针类型,bitcast 子类指针(同上)
if exit_class != ClassName:
parent_ptr_type: ir.Type = exit_func.function_type.args[0]
if ctx_Loaded.type != parent_ptr_type:
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_exit_self")
Gen.builder.call(exit_func, [ctx_Loaded], name="call_exit")
Gen._unregister_local_heap_ptr(original_ctx_val)
if ctx_val is not original_ctx_val:
Gen._unregister_local_heap_ptr(ctx_val)
if ctx_is_var_ref and original_asname_heap_ptr and original_asname_heap_ptr is not original_ctx_val and original_asname_heap_ptr is not ctx_val:
Gen._unregister_local_heap_ptr(original_asname_heap_ptr)
if '__with_ctx' in Gen._var_to_heap_ptr:
del Gen._var_to_heap_ptr['__with_ctx']
if asname and enter_result is not None and isinstance(ctx_val.type, ir.PointerType) and isinstance(enter_result.type, ir.PointerType):
ctx_int: ir.Value = Gen.builder.ptrtoint(ctx_val, ir.IntType(64), name="ctx_int")
enter_int: ir.Value = Gen.builder.ptrtoint(enter_result, ir.IntType(64), name="enter_int")
same_ptr: ir.Value = Gen.builder.icmp_unsigned('==', ctx_int, enter_int, name="same_ptr_check")
with_free_bb: ir.Block = Gen.func.append_basic_block("with_free_ctx")
with_skip_bb: ir.Block = Gen.func.append_basic_block("with_skip_free")
Gen.builder.cbranch(same_ptr, with_skip_bb, with_free_bb)
Gen.builder.position_at_start(with_free_bb)
raw: ir.Value = Gen.builder.bitcast(ctx_val, ir.PointerType(ir.IntType(8)), name="with_free_cast")
free_func: ir.Function = Gen.get_or_declare_c_func('free', ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]))
Gen.builder.call(free_func, [raw])
Gen.builder.branch(with_skip_bb)
Gen.builder.position_at_start(with_skip_bb)
Gen._unregister_local_heap_ptr(enter_result)
Gen._unregister_local_heap_ptr(original_ctx_val)
elif isinstance(ctx_val.type, ir.PointerType):
raw: ir.Value = Gen.builder.bitcast(ctx_val, ir.PointerType(ir.IntType(8)), name="with_free_cast")
free_func: ir.Function = Gen.get_or_declare_c_func('free', ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]))
Gen.builder.call(free_func, [raw])
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import llvmlite.ir as ir
class WithHandle(BaseHandle):
def _ResolveInheritedMethod(self, ClassName: str, MethodName: str, Gen: "Translator.LlvmGen") -> tuple[str | None, "ir.Function | None"]:
"""沿继承链查找方法,返回 (定义方法的类名, 函数对象)
当子类未覆写 __enter__/__exit__ 等方法时,沿 Gen.class_parent 链查找父类实现。
_generate_inherited_method_wrappers 只为 class_methods[ClassName] 中的方法生成包装,
不包括父类独有方法,因此 with 语句需要单独处理继承查找。
"""
# 先检查子类自身:必须有函数体(define)才算真方法。
# 子类继承方法的 stub declare(如 MemPool.__enter__,Phase1 生成的继承
# 包装声明)无函数体且 TPC 不生成对应定义,直接调用会链接 undefined,
# 必须跳过并沿 MRO 找父类实现。
FullName: str = f'{ClassName}.{MethodName}'
func = Gen._find_function(FullName) if hasattr(Gen, '_find_function') else Gen.functions.get(FullName)
if func and len(list(func.blocks)) > 0:
return (ClassName, func)
# 沿 MRO 继承链查找(多继承:先声明父类优先)。
# 祖先的 stub declare(无函数体)是真实定义的声明(定义在其模块 obj 中),
# 可直接调用——与 _HandleMethodCallLlvm 的 MRO 回退语义一致。
for anc in Gen._get_mro_chain(ClassName)[1:]:
parent_full: str = f'{anc}.{MethodName}'
parent_func = Gen._find_function(parent_full) if hasattr(Gen, '_find_function') else Gen.functions.get(parent_full)
if parent_func:
return (anc, parent_func)
return (None, None)
def _HandleWithLlvm(self, Node: ast.With) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
for item in Node.items:
context_expr: ast.expr = item.context_expr
asname: str | None = None
target: ast.expr | None = getattr(item, 'optional_vars', getattr(item, 'target', getattr(item, 'asname', None)))
if target and isinstance(target, ast.Name):
asname = target.id
ClassName: str | None = None
if isinstance(context_expr, ast.Call):
if isinstance(context_expr.func, ast.Name):
ClassName = context_expr.func.id
elif isinstance(context_expr.func, ast.Attribute):
ClassName = context_expr.func.attr
if ClassName and ClassName not in Gen.structs:
SymInfo: "SymbolTable.SymbolInfo" = self.Trans.SymbolTable.lookup(ClassName)
if SymInfo:
if SymInfo.IsStruct or SymInfo.IsRenum:
pass
else:
ClassName = None
else:
ClassName = None
elif isinstance(context_expr, ast.Name):
VarName: str = context_expr.id
if VarName in Gen.var_struct_class:
ClassName = Gen.var_struct_class[VarName]
ctx_val: ir.Value | None = self.HandleExprLlvm(context_expr)
if not ctx_val:
continue
original_ctx_val: ir.Value = ctx_val
if ClassName and isinstance(ctx_val.type, ir.PointerType):
pointee: ir.Type = ctx_val.type.pointee
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
ClassName = CN
StructPtrType: ir.PointerType | None = ir.PointerType(Gen.structs[ClassName]) if ClassName and ClassName in Gen.structs else None
if StructPtrType and isinstance(ctx_val.type, ir.PointerType) and ctx_val.type.pointee != Gen.structs.get(ClassName):
ctx_val = Gen.builder.bitcast(ctx_val, StructPtrType, name="cast_with_ctx")
ctx_alloca: ir.AllocaInstr = Gen._allocaEntry(ctx_val.type, name="__with_ctx")
Gen._store(ctx_val, ctx_alloca)
Gen._UnregisterTempPtr(ctx_val)
if ClassName:
Gen.var_struct_class['__with_ctx'] = ClassName
Gen._var_to_heap_ptr['__with_ctx'] = ctx_val
original_asname_heap_ptr: ir.Value | None = Gen._var_to_heap_ptr.get(asname) if asname else None
ctx_is_var_ref: bool = isinstance(context_expr, ast.Name)
enter_result: ir.Value | None = None
if ClassName:
# 沿继承链查找 __enter__(子类可能未覆写,使用父类实现)
enter_class, enter_func = self._ResolveInheritedMethod(ClassName, '__enter__', Gen)
if enter_func is not None:
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
# 父类方法需要 self 为父类指针类型,bitcast 子类指针。
# bitcast 目标用 enter_func 签名参数类型(args[0] 本身即
# MemManager* 指针),避免 Gen.structs 查找返回 opaque/异
# sha1 占位对象导致 llvmlite 类型不匹配。
if enter_class != ClassName:
parent_ptr_type: ir.Type = enter_func.function_type.args[0]
if ctx_Loaded.type != parent_ptr_type:
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_enter_self")
enter_call: ir.CallInstr = Gen.builder.call(enter_func, [ctx_Loaded], name="call_enter")
enter_result = enter_call
if isinstance(enter_call.type, ir.PointerType):
pointee: ir.Type = enter_call.type.pointee
if isinstance(pointee, ir.PointerType) and isinstance(pointee.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
enter_result = Gen._load(enter_call, name="enter_deref")
elif self._is_char_pointer(enter_call) and StructPtrType:
enter_result = Gen.builder.bitcast(enter_call, StructPtrType, name="cast_enter")
if asname and enter_result:
if isinstance(enter_result.type, ir.PointerType) and isinstance(enter_result.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
found: tuple[str, ir.Type] | None = Gen.find_struct_by_pointee(enter_result.type.pointee)
if found:
CN: str = found[0]
ST: ir.Type = found[1]
Gen.var_struct_class[asname] = CN
Gen._var_to_heap_ptr[asname] = enter_result
Gen._register_local_heap_ptr(enter_result, VarName=asname)
if asname in Gen._reg_values:
OldVal: ir.Value = Gen._reg_values[asname]
var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=asname)
Gen._store(OldVal, var)
Gen.variables[asname] = var
del Gen._reg_values[asname]
if asname in Gen.variables and Gen.variables[asname] is not None:
VarPtr: ir.Value = Gen.variables[asname]
if VarPtr.type.pointee == enter_result.type:
Gen._store(enter_result, VarPtr)
elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(enter_result.type, ir.PointerType):
CastedVal: ir.Value = Gen.builder.bitcast(enter_result, VarPtr.type.pointee, name=f"cast_{asname}")
Gen._store(CastedVal, VarPtr)
else:
NewVar: ir.AllocaInstr = Gen._alloca(enter_result.type, name=asname)
Gen._store(enter_result, NewVar)
Gen.variables[asname] = NewVar
else:
Gen._reg_values[asname] = enter_result
Gen.variables[asname] = None
# 如果类有 __provides__,自动生成 push 代码
if ClassName and ClassName in Gen.class_provides:
# 编译期检查: with provider 栈深度是否超过 _MAX_PROVIDER_DEPTH (32)
# _with_provides_stack 只反映当前嵌套路径,顺序执行的 with 会在退出时弹栈
current_depth: int = sum(len(pl) for pl in Gen._with_provides_stack)
new_fields: int = len(Gen.class_provides[ClassName])
if current_depth + new_fields > 32:
raise SyntaxError(
f"[TransPyC] with provider stack overflow at line {Node.lineno}: "
f"current depth {current_depth} + new {new_fields} fields > max 32. "
f"Class '{ClassName}' provides too many fields or with nesting too deep."
)
# 编译期压栈:记录当前 with 上下文提供的字段名,供 __require_must__ 静态检查
Gen._with_provides_stack.append(Gen.class_provides[ClassName])
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
ctx_i8: ir.Value = ctx_Loaded
if isinstance(ctx_Loaded.type, ir.PointerType) and ctx_Loaded.type != ir.PointerType(ir.IntType(8)):
ctx_i8 = Gen.builder.bitcast(ctx_Loaded, ir.PointerType(ir.IntType(8)), name="ctx_push_i8")
for field_name in Gen.class_provides[ClassName]:
field_str: ir.Value = Gen._create_string_global(field_name)
push_func = Gen.functions.get('_push_provider')
if push_func:
Gen.builder.call(push_func, [field_str, ctx_i8], name="call_push_provider")
self.Trans.VarScopes.append({})
if asname:
self.Trans.VarScopes[-1][asname] = True
self.HandleBodyLlvm(Node.body)
if self.Trans.VarScopes:
self.Trans.VarScopes.pop()
# 如果类有 __provides__,自动生成 pop 代码
if ClassName and ClassName in Gen.class_provides:
for _ in Gen.class_provides[ClassName]:
pop_func = Gen.functions.get('_pop_provider')
if pop_func:
Gen.builder.call(pop_func, [], name="call_pop_provider")
# 编译期弹栈
if Gen._with_provides_stack:
Gen._with_provides_stack.pop()
if ClassName:
# 沿继承链查找 __exit__(子类可能未覆写,使用父类实现)
exit_class, exit_func = self._ResolveInheritedMethod(ClassName, '__exit__', Gen)
if exit_func is not None:
ctx_Loaded: ir.Value = Gen._load(ctx_alloca, name="__with_ctx")
# 父类方法需要 self 为父类指针类型,bitcast 子类指针(同上)
if exit_class != ClassName:
parent_ptr_type: ir.Type = exit_func.function_type.args[0]
if ctx_Loaded.type != parent_ptr_type:
ctx_Loaded = Gen.builder.bitcast(ctx_Loaded, parent_ptr_type, name="cast_exit_self")
Gen.builder.call(exit_func, [ctx_Loaded], name="call_exit")
Gen._unregister_local_heap_ptr(original_ctx_val)
if ctx_val is not original_ctx_val:
Gen._unregister_local_heap_ptr(ctx_val)
if ctx_is_var_ref and original_asname_heap_ptr and original_asname_heap_ptr is not original_ctx_val and original_asname_heap_ptr is not ctx_val:
Gen._unregister_local_heap_ptr(original_asname_heap_ptr)
if '__with_ctx' in Gen._var_to_heap_ptr:
del Gen._var_to_heap_ptr['__with_ctx']
if asname and enter_result is not None and isinstance(ctx_val.type, ir.PointerType) and isinstance(enter_result.type, ir.PointerType):
ctx_int: ir.Value = Gen.builder.ptrtoint(ctx_val, ir.IntType(64), name="ctx_int")
enter_int: ir.Value = Gen.builder.ptrtoint(enter_result, ir.IntType(64), name="enter_int")
same_ptr: ir.Value = Gen.builder.icmp_unsigned('==', ctx_int, enter_int, name="same_ptr_check")
with_free_bb: ir.Block = Gen.func.append_basic_block("with_free_ctx")
with_skip_bb: ir.Block = Gen.func.append_basic_block("with_skip_free")
Gen.builder.cbranch(same_ptr, with_skip_bb, with_free_bb)
Gen.builder.position_at_start(with_free_bb)
raw: ir.Value = Gen.builder.bitcast(ctx_val, ir.PointerType(ir.IntType(8)), name="with_free_cast")
free_func: ir.Function = Gen.get_or_declare_c_func('free', ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]))
Gen.builder.call(free_func, [raw])
Gen.builder.branch(with_skip_bb)
Gen.builder.position_at_start(with_skip_bb)
Gen._unregister_local_heap_ptr(enter_result)
Gen._unregister_local_heap_ptr(original_ctx_val)
elif isinstance(ctx_val.type, ir.PointerType):
raw: ir.Value = Gen.builder.bitcast(ctx_val, ir.PointerType(ir.IntType(8)), name="with_free_cast")
free_func: ir.Function = Gen.get_or_declare_c_func('free', ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]))
Gen.builder.call(free_func, [raw])
Gen._unregister_local_heap_ptr(original_ctx_val)
+842 -842
View File
File diff suppressed because it is too large. Load diff
+557 -557
View File
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
+301 -301
View File
@@ -1,301 +1,301 @@
"""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 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
from lib.includes import t as _t
from lib.includes.t import CTypeRegistry
class LLVMTypeMapper:
"""AST 注解节点 → LLVM IR 类型字符串 映射器"""
def __init__(self, symtab: SymbolTable):
self._symtab = symtab
# ==================================================================
# 公共 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):
left_str: str = self.get_llvm_type_str(returns_node.left)
right_str: str = self.get_llvm_type_str(returns_node.right)
if right_str and right_str.endswith('*'):
return right_str
if left_str and left_str.endswith('*'):
return left_str
if left_str and left_str not in ('i32', 'void', 'i64'):
return left_str
if right_str and right_str not in ('i32', 'void', 'i64'):
return right_str
return left_str
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 字符串"""
if annotation_node is None:
return 'i8*'
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
left_str: str = self.get_llvm_type_str(annotation_node.left)
right_str: str = self.get_llvm_type_str(annotation_node.right)
if right_str and right_str.endswith('*'):
return right_str
if left_str and left_str.endswith('*'):
return left_str
if right_str and right_str not in ('i32', 'void', 'i64'):
return right_str
if left_str and left_str not in ('i32', 'void', 'i64'):
return left_str
return right_str if right_str else left_str
result: str = self.get_llvm_type_str(annotation_node)
return result if result else 'i8*'
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
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'
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)
if right_str == 'ptr' or (right_str and right_str.endswith('*')):
base: str = left_str if left_str and left_str != 'ptr' else ''
return base + ' *' if base else 'void *'
if left_str == 'ptr' or (left_str and left_str.endswith('*')):
base: str = right_str if right_str and right_str != 'ptr' else ''
return base + ' *' if base else 'void *'
if right_str and right_str not in ('ptr', 'void', 'i32', 'i64'):
return right_str
if left_str and 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:
# 优先用 OriginalType 解析
if entry.OriginalType:
if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr:
return 'i8*'
elif isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType:
# 用 CTypeRegistry 生成归一化 LLVM 字符串(void*→i8*),struct/enum 回退 ToString()
resolved: str = self._ctype_to_llvm_str(entry.OriginalType)
if resolved:
return resolved
return entry.OriginalType.ToString()
elif isinstance(entry.OriginalType, str):
# 字符串形式的 OriginalType(如 'CUnsignedInt *')需转换为 LLVM 类型字符串
llvm_str: str = self._ctype_str_to_llvm(entry.OriginalType)
if llvm_str:
return llvm_str
# OriginalType 为空或字符串无法转换时,回退到 BaseType
if entry.BaseType and (not isinstance(entry.BaseType, (_t._CTypedef,)) or entry.PtrCount > 0):
resolved = self._ctype_to_llvm_str(entry)
if resolved:
return resolved
self._symtab.diagnostics.warn('', 0, f"无法解析 Name 类型 '{node.id}',回退到 i32")
return 'i32'
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 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 + '*'
self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32")
return 'i32'
def _resolve_bitor(self, node: ast.BinOp) -> str:
"""解析 ast.BinOp(BitOr) 节点"""
left: str = self.get_llvm_type_str(node.left)
right: str = self.get_llvm_type_str(node.right)
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 _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'
"""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
from lib.includes import t as _t
from lib.includes.t import CTypeRegistry
class LLVMTypeMapper:
"""AST 注解节点 → LLVM IR 类型字符串 映射器"""
def __init__(self, symtab: SymbolTable):
self._symtab = symtab
# ==================================================================
# 公共 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):
left_str: str = self.get_llvm_type_str(returns_node.left)
right_str: str = self.get_llvm_type_str(returns_node.right)
if right_str and right_str.endswith('*'):
return right_str
if left_str and left_str.endswith('*'):
return left_str
if left_str and left_str not in ('i32', 'void', 'i64'):
return left_str
if right_str and right_str not in ('i32', 'void', 'i64'):
return right_str
return left_str
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 字符串"""
if annotation_node is None:
return 'i8*'
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
left_str: str = self.get_llvm_type_str(annotation_node.left)
right_str: str = self.get_llvm_type_str(annotation_node.right)
if right_str and right_str.endswith('*'):
return right_str
if left_str and left_str.endswith('*'):
return left_str
if right_str and right_str not in ('i32', 'void', 'i64'):
return right_str
if left_str and left_str not in ('i32', 'void', 'i64'):
return left_str
return right_str if right_str else left_str
result: str = self.get_llvm_type_str(annotation_node)
return result if result else 'i8*'
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
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'
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)
if right_str == 'ptr' or (right_str and right_str.endswith('*')):
base: str = left_str if left_str and left_str != 'ptr' else ''
return base + ' *' if base else 'void *'
if left_str == 'ptr' or (left_str and left_str.endswith('*')):
base: str = right_str if right_str and right_str != 'ptr' else ''
return base + ' *' if base else 'void *'
if right_str and right_str not in ('ptr', 'void', 'i32', 'i64'):
return right_str
if left_str and 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:
# 优先用 OriginalType 解析
if entry.OriginalType:
if isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.IsFuncPtr:
return 'i8*'
elif isinstance(entry.OriginalType, CTypeInfo) and entry.OriginalType.BaseType:
# 用 CTypeRegistry 生成归一化 LLVM 字符串(void*→i8*),struct/enum 回退 ToString()
resolved: str = self._ctype_to_llvm_str(entry.OriginalType)
if resolved:
return resolved
return entry.OriginalType.ToString()
elif isinstance(entry.OriginalType, str):
# 字符串形式的 OriginalType(如 'CUnsignedInt *')需转换为 LLVM 类型字符串
llvm_str: str = self._ctype_str_to_llvm(entry.OriginalType)
if llvm_str:
return llvm_str
# OriginalType 为空或字符串无法转换时,回退到 BaseType
if entry.BaseType and (not isinstance(entry.BaseType, (_t._CTypedef,)) or entry.PtrCount > 0):
resolved = self._ctype_to_llvm_str(entry)
if resolved:
return resolved
self._symtab.diagnostics.warn('', 0, f"无法解析 Name 类型 '{node.id}',回退到 i32")
return 'i32'
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 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 + '*'
self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32")
return 'i32'
def _resolve_bitor(self, node: ast.BinOp) -> str:
"""解析 ast.BinOp(BitOr) 节点"""
left: str = self.get_llvm_type_str(node.left)
right: str = self.get_llvm_type_str(node.right)
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 _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'
+85 -85
View File
@@ -1,85 +1,85 @@
"""符号表处理的数据结构:四阶段管线中传递的中间数据"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
import ast
if TYPE_CHECKING:
from lib.core.Handles.HandlesBase import CTypeInfo
@dataclass
class ClassSymbolData:
"""从 AST 提取的类符号数据"""
name: str
type_kind: str # 'struct', 'union', 'enum', 'exception'
lineno: int
members: Dict[str, CTypeInfo]
is_cpython_object: bool = False
is_packed: bool = False
enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno)
@dataclass
class TypedefSymbolData:
"""Typedef 符号数据(含原始注解和解析结果)"""
name: str
lineno: int
# 原始注解数据(由 Extractor 设置,Resolver 使用)
annotation: ast.expr | None = None
value: ast.expr | int | str | float | None = None
has_cdefine: bool = False
has_postdef: bool = False
has_ctypedef: bool = False
# 解析结果(由 Resolver 设置,Inserter 使用)
original_type_kind: Optional[str] = None
original_class: type | None = None
members: Optional[Dict] = None
@dataclass
class FuncSymbolData:
"""函数符号数据(含原始注解和解析结果)"""
name: str
lineno: int
# 原始数据(由 Extractor 设置)
returns_node: ast.expr | None = None
params: List = field(default_factory=list) # [(name, annotation_node), ...]
is_variadic: bool = False
is_inline: bool = False
# 解析结果(由 Resolver 设置)
ret_type: str | None = None
param_types: List = field(default_factory=list) # [str, ...]
# 解析结果(CTypeInfo,保留符号性,由 Resolver 设置)
ret_ctype: Optional[CTypeInfo] = None
@dataclass
class DefineSymbolData:
"""Define 符号数据(含原始值和解析结果)"""
name: str
lineno: int
# 原始数据(由 Extractor 设置)
value_node: ast.expr | None = None
# 解析结果(由 Resolver 设置)
value: int | str | float | None = None
@dataclass
class AnonymousTypeData:
"""匿名类型数据"""
name: str
is_union: bool
members: Dict[str, CTypeInfo]
lineno: int
@dataclass
class ModuleSymbols:
"""从模块提取的完整符号数据,在四阶段管线中传递"""
file_path: str
classes: List[ClassSymbolData] = field(default_factory=list)
typedefs: List[TypedefSymbolData] = field(default_factory=list)
functions: List[FuncSymbolData] = field(default_factory=list)
defines: List[DefineSymbolData] = field(default_factory=list)
anonymous_types: Dict[str, AnonymousTypeData] = field(default_factory=dict)
"""符号表处理的数据结构:四阶段管线中传递的中间数据"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple, TYPE_CHECKING
import vast as ast
if TYPE_CHECKING:
from lib.core.Handles.HandlesBase import CTypeInfo
@dataclass
class ClassSymbolData:
"""从 AST 提取的类符号数据"""
name: str
type_kind: str # 'struct', 'union', 'enum', 'exception'
lineno: int
members: Dict[str, CTypeInfo]
is_cpython_object: bool = False
is_packed: bool = False
enum_members: List[Tuple[str, int, int]] = field(default_factory=list) # (name, value, lineno)
@dataclass
class TypedefSymbolData:
"""Typedef 符号数据(含原始注解和解析结果)"""
name: str
lineno: int
# 原始注解数据(由 Extractor 设置,Resolver 使用)
annotation: ast.expr | None = None
value: ast.expr | int | str | float | None = None
has_cdefine: bool = False
has_postdef: bool = False
has_ctypedef: bool = False
# 解析结果(由 Resolver 设置,Inserter 使用)
original_type_kind: Optional[str] = None
original_class: type | None = None
members: Optional[Dict] = None
@dataclass
class FuncSymbolData:
"""函数符号数据(含原始注解和解析结果)"""
name: str
lineno: int
# 原始数据(由 Extractor 设置)
returns_node: ast.expr | None = None
params: List = field(default_factory=list) # [(name, annotation_node), ...]
is_variadic: bool = False
is_inline: bool = False
# 解析结果(由 Resolver 设置)
ret_type: str | None = None
param_types: List = field(default_factory=list) # [str, ...]
# 解析结果(CTypeInfo,保留符号性,由 Resolver 设置)
ret_ctype: Optional[CTypeInfo] = None
@dataclass
class DefineSymbolData:
"""Define 符号数据(含原始值和解析结果)"""
name: str
lineno: int
# 原始数据(由 Extractor 设置)
value_node: ast.expr | None = None
# 解析结果(由 Resolver 设置)
value: int | str | float | None = None
@dataclass
class AnonymousTypeData:
"""匿名类型数据"""
name: str
is_union: bool
members: Dict[str, CTypeInfo]
lineno: int
@dataclass
class ModuleSymbols:
"""从模块提取的完整符号数据,在四阶段管线中传递"""
file_path: str
classes: List[ClassSymbolData] = field(default_factory=list)
typedefs: List[TypedefSymbolData] = field(default_factory=list)
functions: List[FuncSymbolData] = field(default_factory=list)
defines: List[DefineSymbolData] = field(default_factory=list)
anonymous_types: Dict[str, AnonymousTypeData] = field(default_factory=dict)
+307 -307
View File
@@ -1,307 +1,307 @@
"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息"""
from __future__ import annotations
import 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.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)
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
# 提取枚举成员
enum_members: list[tuple[str, int, int]] = []
if type_kind == 'enum':
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,
)
# ------------------------------------------------------------------
# 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:
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,
)
"""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.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)
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
# 提取枚举成员
enum_members: list[tuple[str, int, int]] = []
if type_kind == 'enum':
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,
)
# ------------------------------------------------------------------
# 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:
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,
)
+531 -531
View File
File diff suppressed because it is too large. Load diff
+272 -272
View File
@@ -1,272 +1,272 @@
"""Pass 2: TypeResolver — 解析 typedef 类型、函数签名、define 值"""
from __future__ import annotations
import ast
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from lib.core.SymbolTable import SymbolTable
from lib.core.SymbolData import (
ModuleSymbols, TypedefSymbolData, FuncSymbolData, DefineSymbolData
)
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper
from lib.core.LLVMTypeMapper import LLVMTypeMapper
from lib.core.SymbolUtils import IsTModule
from lib.includes import t
class TypeResolver:
"""解析从 AST 提取的原始符号中的类型信息"""
def __init__(self, symbol_table: SymbolTable):
self._symtab = symbol_table
self._type_mapper = LLVMTypeMapper(symbol_table)
# 本模块内已解析的 typedef: name → original_class(供同模块 typedef 间引用解析)
self._resolved_typedefs: dict[str, str | None] = {}
def resolve(self, module_symbols: ModuleSymbols) -> ModuleSymbols:
"""解析所有类型信息,就地更新 ModuleSymbols"""
# 构建 class_members 查找表(typedef Postdefinition 需要)
class_members: dict[str, dict[str, CTypeInfo]] = {cls.name: cls.members for cls in module_symbols.classes}
# 解析 typedef(按顺序记录,供后续 typedef 引用同模块已解析的 typedef)
td: TypedefSymbolData
for td in module_symbols.typedefs:
self._resolve_typedef(td, class_members)
self._resolved_typedefs[td.name] = td.original_class
# 解析函数签名
func: FuncSymbolData
for func in module_symbols.functions:
self._resolve_function(func)
# 解析 define 值
define: DefineSymbolData
for define in module_symbols.defines:
self._resolve_define(define)
return module_symbols
# ------------------------------------------------------------------
# Typedef 解析
# ------------------------------------------------------------------
def _resolve_typedef(self, td: TypedefSymbolData, class_members: dict[str, dict[str, CTypeInfo]]):
"""解析单个 typedef 的类型信息"""
is_postdef_with_typedef: bool = td.has_postdef and td.has_ctypedef
if is_postdef_with_typedef:
self._resolve_postdef_typedef(td, class_members)
elif td.has_ctypedef:
self._resolve_ctypedef_typedef(td)
else:
# 无特殊注解的 typedef
td.original_type_kind = None
td.original_class = None
def _resolve_postdef_typedef(self, td: TypedefSymbolData, class_members: dict[str, dict[str, CTypeInfo]]):
"""解析 Postdefinition + CTypedef 组合注解"""
original_class: str | None = self._find_postdef_arg(td.annotation)
if original_class and original_class in class_members:
members_info: dict[str, CTypeInfo] = class_members[original_class]
original_type_kind: str = 'struct'
if original_class in self._symtab:
orig_type_info: CTypeInfo = self._symtab[original_class]
if orig_type_info.type_cls in (t.CStruct, t.CEnum, t.CUnion):
if orig_type_info.type_cls is t.CStruct:
original_type_kind = 'struct'
elif orig_type_info.type_cls is t.CEnum:
original_type_kind = 'enum'
elif orig_type_info.type_cls is t.CUnion:
original_type_kind = 'union'
td.original_type_kind = original_type_kind
td.original_class = original_class
td.members = members_info
else:
td.original_type_kind = None
td.original_class = None
def _resolve_ctypedef_typedef(self, td: TypedefSymbolData):
"""解析 CTypedef 注解"""
base_type_name: str | None = self._find_base_type(td.annotation)
_TYPE_QUALIFIERS: frozenset[str] = frozenset({'CTypedef', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern', 'CDefine', 'CPostdefinition'})
if base_type_name == 'Callable':
original_type: CTypeInfo = CTypeInfo()
original_type.IsFuncPtr = True
original_type.FuncPtrReturn = CTypeInfo.VoidTypeInfo()
original_type.FuncPtrParams = []
td.original_type_kind = 'typedef'
td.original_class = original_type
elif base_type_name and base_type_name not in _TYPE_QUALIFIERS:
cname: str | None = CTypeHelper.GetCName(base_type_name)
if cname:
td.original_class = cname
else:
td.original_class = base_type_name
td.original_type_kind = 'typedef'
else:
# 尝试从值表达式解析
self._resolve_typedef_from_value(td)
def _resolve_typedef_from_value(self, td: TypedefSymbolData):
"""从值表达式解析 typedef 类型
BinOp(如 UINT | t.CPtr)优先于 _find_base_type 处理,
因为 _find_base_type 是静态方法无法解析 Name 引用(如 UINT)。
"""
if not td.value:
td.original_type_kind = None
td.original_class = None
return
# 优先处理 BinOp(_find_base_type 无法解析 Name 引用)
if isinstance(td.value, ast.BinOp) and isinstance(td.value.op, ast.BitOr):
left_type: str = self._resolve_type_expr(td.value.left)
right_type: str = self._resolve_type_expr(td.value.right)
is_ptr_type: bool = right_type == 'ptr' or left_type == 'ptr'
if is_ptr_type:
base_name: str = left_type if left_type != 'ptr' else (right_type if right_type != 'ptr' else '')
original_type: str = (base_name + ' *') if base_name else 'void *'
td.original_class = original_type
td.original_type_kind = 'typedef'
elif left_type:
td.original_class = left_type
td.original_type_kind = 'typedef'
elif right_type:
td.original_class = right_type
td.original_type_kind = 'typedef'
else:
td.original_type_kind = None
td.original_class = None
return
# 非 BinOp:使用 _find_base_type
value_base_type: str | None = self._find_base_type(td.value)
_PTR_MODIFIERS: frozenset[str] = frozenset({'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern'})
if value_base_type and value_base_type not in _PTR_MODIFIERS:
cname: str | None = CTypeHelper.GetCName(value_base_type)
if cname:
td.original_class = cname
else:
td.original_class = value_base_type
td.original_type_kind = 'typedef'
return
if isinstance(td.value, ast.Name):
ref_name: str = td.value.id
# 先查本模块已解析的 typedef
if ref_name in self._resolved_typedefs:
ref_class: str | None = self._resolved_typedefs[ref_name]
if ref_class:
td.original_class = ref_class
td.original_type_kind = 'typedef'
return
# 再查 symtab(跨模块)
if ref_name in self._symtab:
ref_info: CTypeInfo = self._symtab[ref_name]
if ref_info and ref_info.IsTypedef and ref_info.OriginalType:
td.original_class = ref_info.OriginalType
td.original_type_kind = 'typedef'
return
td.original_type_kind = None
td.original_class = None
def _resolve_type_expr(self, node: ast.AST) -> str:
"""解析类型表达式为 C 类型名字符串,优先查本模块已解析的 typedef
解决同模块 typedef 间引用(如 UINTPTR = UINT | t.CPtr 中的 UINT)
在 TypeResolver 阶段尚未插入 symtab 的问题。
"""
if isinstance(node, ast.Name):
if node.id in self._resolved_typedefs:
return self._resolved_typedefs[node.id] or ''
return self._type_mapper.resolve_typedef_value_type(node)
# ------------------------------------------------------------------
# 函数签名解析
# ------------------------------------------------------------------
def _resolve_function(self, func: FuncSymbolData):
"""解析函数的返回类型和参数类型"""
func.ret_type = self._type_mapper.get_func_ret_type_str(func.returns_node)
# 同时解析为 CTypeInfo,保留符号性(LLVM 类型字符串 'i64' 不区分有无符号)
if func.returns_node is not None:
try:
func.ret_ctype = CTypeInfo.FromNode(func.returns_node, self._symtab)
except Exception:
func.ret_ctype = None
func.param_types = []
_arg_name: str
annotation: ast.expr | None
for _arg_name, annotation in func.params:
if annotation:
func.param_types.append(self._type_mapper.get_func_param_type_str(annotation))
else:
func.param_types.append('i8*')
# ------------------------------------------------------------------
# Define 值解析
# ------------------------------------------------------------------
def _resolve_define(self, define: DefineSymbolData):
"""解析 define 常量值"""
if define.value_node:
define.value = self._symtab._eval_const_expr(define.value_node)
# ------------------------------------------------------------------
# 静态工具方法(原 _LoadSymbolsFromFile 中的闭包)
# ------------------------------------------------------------------
@staticmethod
def _find_base_type(node: ast.AST | None) -> str | None:
"""从注解 AST 节点中查找基础类型名称"""
if isinstance(node, ast.Attribute):
if hasattr(node.value, 'id') and IsTModule(node.value.id):
if node.attr == 'CPtr':
return 'ptr'
return node.attr
elif isinstance(node, ast.Subscript):
if isinstance(node.value, ast.Attribute) and isinstance(node.value.value, ast.Name) and IsTModule(node.value.value.id) and node.value.attr == 'Callable':
return 'Callable'
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
left_result: str | None = TypeResolver._find_base_type(node.left)
if left_result == 'ptr':
right_result: str | None = TypeResolver._find_base_type(node.right)
return (right_result if right_result and right_result != 'ptr' else 'void') + ' *'
if left_result == 'Callable':
return 'Callable'
right_result: str | None = TypeResolver._find_base_type(node.right)
if right_result == 'ptr':
return (left_result if left_result and left_result != 'ptr' else 'void') + ' *'
if right_result == 'Callable':
return 'Callable'
# 类型拼接语义:CLong | CLong → CLongLong, CUnsignedLong | CLong → CUnsignedLongLong
long_types: frozenset[str] = frozenset({'CLong', 'CUnsignedLong'})
if left_result in long_types and right_result in long_types:
if left_result == 'CUnsignedLong' or right_result == 'CUnsignedLong':
return 'CUnsignedLongLong'
return 'CLongLong'
if left_result:
return left_result
if right_result:
return right_result
return None
@staticmethod
def _find_postdef_arg(node: ast.AST | None) -> str | None:
"""从注解 AST 节点中查找 Postdefinition 参数"""
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute):
if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id) and node.func.attr == 'Postdefinition':
if node.args and isinstance(node.args[0], ast.Name):
return node.args[0].id
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
left_result: str | None = TypeResolver._find_postdef_arg(node.left)
if left_result:
return left_result
right_result: str | None = TypeResolver._find_postdef_arg(node.right)
if right_result:
return right_result
return None
"""Pass 2: TypeResolver — 解析 typedef 类型、函数签名、define 值"""
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, TypedefSymbolData, FuncSymbolData, DefineSymbolData
)
from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper
from lib.core.LLVMTypeMapper import LLVMTypeMapper
from lib.core.SymbolUtils import IsTModule
from lib.includes import t
class TypeResolver:
"""解析从 AST 提取的原始符号中的类型信息"""
def __init__(self, symbol_table: SymbolTable):
self._symtab = symbol_table
self._type_mapper = LLVMTypeMapper(symbol_table)
# 本模块内已解析的 typedef: name → original_class(供同模块 typedef 间引用解析)
self._resolved_typedefs: dict[str, str | None] = {}
def resolve(self, module_symbols: ModuleSymbols) -> ModuleSymbols:
"""解析所有类型信息,就地更新 ModuleSymbols"""
# 构建 class_members 查找表(typedef Postdefinition 需要)
class_members: dict[str, dict[str, CTypeInfo]] = {cls.name: cls.members for cls in module_symbols.classes}
# 解析 typedef(按顺序记录,供后续 typedef 引用同模块已解析的 typedef)
td: TypedefSymbolData
for td in module_symbols.typedefs:
self._resolve_typedef(td, class_members)
self._resolved_typedefs[td.name] = td.original_class
# 解析函数签名
func: FuncSymbolData
for func in module_symbols.functions:
self._resolve_function(func)
# 解析 define 值
define: DefineSymbolData
for define in module_symbols.defines:
self._resolve_define(define)
return module_symbols
# ------------------------------------------------------------------
# Typedef 解析
# ------------------------------------------------------------------
def _resolve_typedef(self, td: TypedefSymbolData, class_members: dict[str, dict[str, CTypeInfo]]):
"""解析单个 typedef 的类型信息"""
is_postdef_with_typedef: bool = td.has_postdef and td.has_ctypedef
if is_postdef_with_typedef:
self._resolve_postdef_typedef(td, class_members)
elif td.has_ctypedef:
self._resolve_ctypedef_typedef(td)
else:
# 无特殊注解的 typedef
td.original_type_kind = None
td.original_class = None
def _resolve_postdef_typedef(self, td: TypedefSymbolData, class_members: dict[str, dict[str, CTypeInfo]]):
"""解析 Postdefinition + CTypedef 组合注解"""
original_class: str | None = self._find_postdef_arg(td.annotation)
if original_class and original_class in class_members:
members_info: dict[str, CTypeInfo] = class_members[original_class]
original_type_kind: str = 'struct'
if original_class in self._symtab:
orig_type_info: CTypeInfo = self._symtab[original_class]
if orig_type_info.type_cls in (t.CStruct, t.CEnum, t.CUnion):
if orig_type_info.type_cls is t.CStruct:
original_type_kind = 'struct'
elif orig_type_info.type_cls is t.CEnum:
original_type_kind = 'enum'
elif orig_type_info.type_cls is t.CUnion:
original_type_kind = 'union'
td.original_type_kind = original_type_kind
td.original_class = original_class
td.members = members_info
else:
td.original_type_kind = None
td.original_class = None
def _resolve_ctypedef_typedef(self, td: TypedefSymbolData):
"""解析 CTypedef 注解"""
base_type_name: str | None = self._find_base_type(td.annotation)
_TYPE_QUALIFIERS: frozenset[str] = frozenset({'CTypedef', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern', 'CDefine', 'CPostdefinition'})
if base_type_name == 'Callable':
original_type: CTypeInfo = CTypeInfo()
original_type.IsFuncPtr = True
original_type.FuncPtrReturn = CTypeInfo.VoidTypeInfo()
original_type.FuncPtrParams = []
td.original_type_kind = 'typedef'
td.original_class = original_type
elif base_type_name and base_type_name not in _TYPE_QUALIFIERS:
cname: str | None = CTypeHelper.GetCName(base_type_name)
if cname:
td.original_class = cname
else:
td.original_class = base_type_name
td.original_type_kind = 'typedef'
else:
# 尝试从值表达式解析
self._resolve_typedef_from_value(td)
def _resolve_typedef_from_value(self, td: TypedefSymbolData):
"""从值表达式解析 typedef 类型
BinOp(如 UINT | t.CPtr)优先于 _find_base_type 处理,
因为 _find_base_type 是静态方法无法解析 Name 引用(如 UINT)。
"""
if not td.value:
td.original_type_kind = None
td.original_class = None
return
# 优先处理 BinOp(_find_base_type 无法解析 Name 引用)
if isinstance(td.value, ast.BinOp) and isinstance(td.value.op, ast.BitOr):
left_type: str = self._resolve_type_expr(td.value.left)
right_type: str = self._resolve_type_expr(td.value.right)
is_ptr_type: bool = right_type == 'ptr' or left_type == 'ptr'
if is_ptr_type:
base_name: str = left_type if left_type != 'ptr' else (right_type if right_type != 'ptr' else '')
original_type: str = (base_name + ' *') if base_name else 'void *'
td.original_class = original_type
td.original_type_kind = 'typedef'
elif left_type:
td.original_class = left_type
td.original_type_kind = 'typedef'
elif right_type:
td.original_class = right_type
td.original_type_kind = 'typedef'
else:
td.original_type_kind = None
td.original_class = None
return
# 非 BinOp:使用 _find_base_type
value_base_type: str | None = self._find_base_type(td.value)
_PTR_MODIFIERS: frozenset[str] = frozenset({'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern'})
if value_base_type and value_base_type not in _PTR_MODIFIERS:
cname: str | None = CTypeHelper.GetCName(value_base_type)
if cname:
td.original_class = cname
else:
td.original_class = value_base_type
td.original_type_kind = 'typedef'
return
if isinstance(td.value, ast.Name):
ref_name: str = td.value.id
# 先查本模块已解析的 typedef
if ref_name in self._resolved_typedefs:
ref_class: str | None = self._resolved_typedefs[ref_name]
if ref_class:
td.original_class = ref_class
td.original_type_kind = 'typedef'
return
# 再查 symtab(跨模块)
if ref_name in self._symtab:
ref_info: CTypeInfo = self._symtab[ref_name]
if ref_info and ref_info.IsTypedef and ref_info.OriginalType:
td.original_class = ref_info.OriginalType
td.original_type_kind = 'typedef'
return
td.original_type_kind = None
td.original_class = None
def _resolve_type_expr(self, node: ast.AST) -> str:
"""解析类型表达式为 C 类型名字符串,优先查本模块已解析的 typedef
解决同模块 typedef 间引用(如 UINTPTR = UINT | t.CPtr 中的 UINT)
在 TypeResolver 阶段尚未插入 symtab 的问题。
"""
if isinstance(node, ast.Name):
if node.id in self._resolved_typedefs:
return self._resolved_typedefs[node.id] or ''
return self._type_mapper.resolve_typedef_value_type(node)
# ------------------------------------------------------------------
# 函数签名解析
# ------------------------------------------------------------------
def _resolve_function(self, func: FuncSymbolData):
"""解析函数的返回类型和参数类型"""
func.ret_type = self._type_mapper.get_func_ret_type_str(func.returns_node)
# 同时解析为 CTypeInfo,保留符号性(LLVM 类型字符串 'i64' 不区分有无符号)
if func.returns_node is not None:
try:
func.ret_ctype = CTypeInfo.FromNode(func.returns_node, self._symtab)
except Exception:
func.ret_ctype = None
func.param_types = []
_arg_name: str
annotation: ast.expr | None
for _arg_name, annotation in func.params:
if annotation:
func.param_types.append(self._type_mapper.get_func_param_type_str(annotation))
else:
func.param_types.append('i8*')
# ------------------------------------------------------------------
# Define 值解析
# ------------------------------------------------------------------
def _resolve_define(self, define: DefineSymbolData):
"""解析 define 常量值"""
if define.value_node:
define.value = self._symtab._eval_const_expr(define.value_node)
# ------------------------------------------------------------------
# 静态工具方法(原 _LoadSymbolsFromFile 中的闭包)
# ------------------------------------------------------------------
@staticmethod
def _find_base_type(node: ast.AST | None) -> str | None:
"""从注解 AST 节点中查找基础类型名称"""
if isinstance(node, ast.Attribute):
if hasattr(node.value, 'id') and IsTModule(node.value.id):
if node.attr == 'CPtr':
return 'ptr'
return node.attr
elif isinstance(node, ast.Subscript):
if isinstance(node.value, ast.Attribute) and isinstance(node.value.value, ast.Name) and IsTModule(node.value.value.id) and node.value.attr == 'Callable':
return 'Callable'
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
left_result: str | None = TypeResolver._find_base_type(node.left)
if left_result == 'ptr':
right_result: str | None = TypeResolver._find_base_type(node.right)
return (right_result if right_result and right_result != 'ptr' else 'void') + ' *'
if left_result == 'Callable':
return 'Callable'
right_result: str | None = TypeResolver._find_base_type(node.right)
if right_result == 'ptr':
return (left_result if left_result and left_result != 'ptr' else 'void') + ' *'
if right_result == 'Callable':
return 'Callable'
# 类型拼接语义:CLong | CLong → CLongLong, CUnsignedLong | CLong → CUnsignedLongLong
long_types: frozenset[str] = frozenset({'CLong', 'CUnsignedLong'})
if left_result in long_types and right_result in long_types:
if left_result == 'CUnsignedLong' or right_result == 'CUnsignedLong':
return 'CUnsignedLongLong'
return 'CLongLong'
if left_result:
return left_result
if right_result:
return right_result
return None
@staticmethod
def _find_postdef_arg(node: ast.AST | None) -> str | None:
"""从注解 AST 节点中查找 Postdefinition 参数"""
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Attribute):
if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id) and node.func.attr == 'Postdefinition':
if node.args and isinstance(node.args[0], ast.Name):
return node.args[0].id
elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
left_result: str | None = TypeResolver._find_postdef_arg(node.left)
if left_result:
return left_result
right_result: str | None = TypeResolver._find_postdef_arg(node.right)
if right_result:
return right_result
return None
+233 -233
View File
@@ -1,233 +1,233 @@
"""共享工具函数:符号表处理中使用的 AST 分析工具"""
from __future__ import annotations
import ast
import re
from lib.includes import t
_T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__})
# Viper 软关键字 fn/fun/func/function 是 def 的别名(行首 + 空白 + 标识符才算函数定义,
# 变量名/调用/注解中的 fn 不受影响)。展开为 def 后行号不变,供 ast.parse 使用。
_SOFT_KEYWORD_RE: re.Pattern[str] = re.compile(
r'^(\s*)(function|func|fun|fn)(\s+)(?=[A-Za-z_])', re.MULTILINE
)
def ExpandSoftKeywords(Code: str) -> str:
"""将 Viper 软关键字 fn/fun/func/function 展开为 def(函数定义行)。
仅在"行首缩进 + 关键字 + 空白 + 标识符"的函数定义形态匹配,
变量名(fn = 5)、调用(fn(x))、注解(x: fn)与字符串内容不受影响。
展开为 def 与关键字等宽或更短,行号不变,AST 行号映射保持有效。
"""
if not Code:
return Code
return _SOFT_KEYWORD_RE.sub(lambda m: f'{m.group(1)}def{m.group(3)}', Code)
def IsTModule(module_id: str, symtable: object | None = None) -> bool:
"""检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)"""
if module_id in _T_MODULE_NAMES:
return True
if symtable is not None and hasattr(symtable, 'import_aliases'):
resolved: str = symtable.import_aliases.get(module_id, module_id)
if resolved in _T_MODULE_NAMES:
return True
return False
def IsTModulePath(module_path: str) -> bool:
"""检查模块路径是否指向 t 类型模块"""
if module_path in _T_MODULE_NAMES:
return True
if module_path:
t_name: str
for t_name in _T_MODULE_NAMES:
if module_path == t_name or module_path.startswith(t_name + '.'):
return True
return False
def AnnotationContainsTType(node: ast.AST, TypeName: str) -> bool:
"""检测 AST 节点中是否包含指定名称的 t 模块类型"""
if isinstance(node, ast.Attribute):
if (hasattr(node.value, 'id') and IsTModule(node.value.id) and
node.attr == TypeName):
return True
elif isinstance(node, ast.BinOp):
return AnnotationContainsTType(node.left, TypeName) or AnnotationContainsTType(node.right, TypeName)
elif isinstance(node, ast.Call):
if AnnotationContainsTType(node.func, TypeName):
return True
for arg in node.args:
if AnnotationContainsTType(arg, TypeName):
return True
elif isinstance(node, ast.Subscript):
if AnnotationContainsTType(node.value, TypeName):
return True
if AnnotationContainsTType(node.slice, TypeName):
return True
return False
def IsListAnnotation(annotation: ast.expr) -> bool:
"""检查注解是否为栈上固定数组类型(t.CArray[...] 或 list[type, count])"""
if not isinstance(annotation, ast.Subscript):
return False
value = annotation.value
# t.CArray[...]
if (isinstance(value, ast.Attribute)
and isinstance(value.value, ast.Name)
and value.value.id == 't'
and value.attr == 'CArray'):
return True
# list[type, count] — 仅匹配双参数 Tuple 切片(固定数组),
# 单参数 list[type] 不匹配(走泛型类 list[T] 路径)
if (isinstance(value, ast.Name)
and value.id == 'list'
and isinstance(annotation.slice, ast.Tuple)
and len(annotation.slice.elts) == 2):
return True
return False
def ExtractListFromBinOp(annotation: ast.expr) -> ast.Subscript | None:
"""从 BinOp(BitOr) 注解中提取 list[...] 部分,如 list[i32] | t.CPtr"""
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
for side in (annotation.left, annotation.right):
if IsListAnnotation(side):
return side
return None
class ListAnnotationParseResult:
"""list[...] 注解解析结果。
is_pointer=True 表示单参数 list[type] → 指针模式;
is_pointer=False 表示 Tuple(2) 模式 list[type, count]。
"""
__slots__ = ('elem_type_node', 'count_node', 'is_pointer')
def __init__(self, elem_type_node: ast.expr, count_node: ast.expr | None, is_pointer: bool) -> None:
self.elem_type_node: ast.expr = elem_type_node
self.count_node: ast.expr | None = count_node
self.is_pointer: bool = is_pointer
def ParseListAnnotation(annotation: ast.expr) -> ListAnnotationParseResult | None:
"""解析 list[...] 注解,统一处理 list[type, count] / list[type] / BinOp(BitOr) 形式。
自动处理 BinOp(BitOr) 包裹(如 list[i32, N] | t.CPtr)。
返回 None 表示不是 list 注解。
"""
list_node: ast.expr = annotation
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
extracted: ast.Subscript | None = ExtractListFromBinOp(annotation)
if extracted is None:
return None
list_node = extracted
if not IsListAnnotation(list_node):
return None
slice_node: ast.AST = list_node.slice
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
return ListAnnotationParseResult(slice_node.elts[0], slice_node.elts[1], is_pointer=False)
# 单参数 list[type] → 指针模式
return ListAnnotationParseResult(slice_node, None, is_pointer=True)
def CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool:
"""检测注解节点中是否包含 CInline"""
if annotation_node is None:
return False
if isinstance(annotation_node, ast.Attribute):
if hasattr(annotation_node.value, 'id') and IsTModule(annotation_node.value.id) and annotation_node.attr == 'CInline':
return True
if isinstance(annotation_node, ast.Name):
if annotation_node.id == 'CInline':
return True
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
return CheckAnnotationHasCInline(annotation_node.left) or CheckAnnotationHasCInline(annotation_node.right)
return False
def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None:
"""从 BinOp(BitOr) 注解左侧提取类型名(如 TypeA | TypeB → TypeA)。
返回 Name.id 或 Attribute.attr,非 BinOp 返回 None。
"""
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
left: ast.expr = annotation.left
if isinstance(left, ast.Name):
return left.id
if isinstance(left, ast.Attribute):
return left.attr
return None
def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set) -> str | None:
"""递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。
检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。
支持泛型特化类型(如 GSList[Param]),构造特化名后检查是否在 struct_names 中。
"""
if isinstance(node, ast.Name) and node.id in struct_names:
return node.id
if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value in struct_names:
return node.value
if isinstance(node, ast.Attribute) and node.attr in struct_names:
return node.attr
if isinstance(node, ast.Subscript):
base_name: str | None = None
if isinstance(node.value, ast.Name):
base_name = node.value.id
elif isinstance(node.value, ast.Attribute):
base_name = node.value.attr
if base_name:
slice_node: ast.AST = node.slice
if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index):
slice_node = slice_node.value
type_args: list[str] = []
if isinstance(slice_node, ast.Name):
type_args.append(slice_node.id)
elif isinstance(slice_node, ast.Attribute):
type_args.append(slice_node.attr)
elif isinstance(slice_node, ast.Tuple):
for elt in slice_node.elts:
if isinstance(elt, ast.Name):
type_args.append(elt.id)
elif isinstance(elt, ast.Attribute):
type_args.append(elt.attr)
if type_args:
spec_name: str = base_name + ''.join(f'[{arg}]' for arg in type_args)
if spec_name in struct_names:
return spec_name
if base_name in struct_names:
return base_name
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
result: str | None = FindStructNameInAnnotation(node.left, struct_names)
if result:
return result
return FindStructNameInAnnotation(node.right, struct_names)
return None
def AnnotationContainsName(annotation: ast.AST, name: str) -> bool:
"""检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。
同时检查 Attribute.attr 和 Name.id,比 ast.dump 字符串匹配更精确。
"""
if isinstance(annotation, ast.Attribute):
return annotation.attr == name
if isinstance(annotation, ast.Name):
return annotation.id == name
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return AnnotationContainsName(annotation.left, name) or AnnotationContainsName(annotation.right, name)
return False
def IsCPtrNode(node: ast.AST) -> bool:
"""检查节点是否为 t.CPtr 属性引用。"""
return (isinstance(node, ast.Attribute)
and isinstance(node.value, ast.Name)
and node.value.id == 't'
and node.attr == 'CPtr')
"""共享工具函数:符号表处理中使用的 AST 分析工具"""
from __future__ import annotations
import vast as ast
import re
from lib.includes import t
_T_MODULE_NAMES: frozenset[str] = frozenset({'t', t.__name__})
# Viper 软关键字 fn/fun/func/function 是 def 的别名(行首 + 空白 + 标识符才算函数定义,
# 变量名/调用/注解中的 fn 不受影响)。展开为 def 后行号不变,供 ast.parse 使用。
_SOFT_KEYWORD_RE: re.Pattern[str] = re.compile(
r'^(\s*)(function|func|fun|fn)(\s+)(?=[A-Za-z_])', re.MULTILINE
)
def ExpandSoftKeywords(Code: str) -> str:
"""将 Viper 软关键字 fn/fun/func/function 展开为 def(函数定义行)。
仅在"行首缩进 + 关键字 + 空白 + 标识符"的函数定义形态匹配,
变量名(fn = 5)、调用(fn(x))、注解(x: fn)与字符串内容不受影响。
展开为 def 与关键字等宽或更短,行号不变,AST 行号映射保持有效。
"""
if not Code:
return Code
return _SOFT_KEYWORD_RE.sub(lambda m: f'{m.group(1)}def{m.group(3)}', Code)
def IsTModule(module_id: str, symtable: object | None = None) -> bool:
"""检查模块标识符是否指向 t 类型模块(支持别名如 import t as tt)"""
if module_id in _T_MODULE_NAMES:
return True
if symtable is not None and hasattr(symtable, 'import_aliases'):
resolved: str = symtable.import_aliases.get(module_id, module_id)
if resolved in _T_MODULE_NAMES:
return True
return False
def IsTModulePath(module_path: str) -> bool:
"""检查模块路径是否指向 t 类型模块"""
if module_path in _T_MODULE_NAMES:
return True
if module_path:
t_name: str
for t_name in _T_MODULE_NAMES:
if module_path == t_name or module_path.startswith(t_name + '.'):
return True
return False
def AnnotationContainsTType(node: ast.AST, TypeName: str) -> bool:
"""检测 AST 节点中是否包含指定名称的 t 模块类型"""
if isinstance(node, ast.Attribute):
if (hasattr(node.value, 'id') and IsTModule(node.value.id) and
node.attr == TypeName):
return True
elif isinstance(node, ast.BinOp):
return AnnotationContainsTType(node.left, TypeName) or AnnotationContainsTType(node.right, TypeName)
elif isinstance(node, ast.Call):
if AnnotationContainsTType(node.func, TypeName):
return True
for arg in node.args:
if AnnotationContainsTType(arg, TypeName):
return True
elif isinstance(node, ast.Subscript):
if AnnotationContainsTType(node.value, TypeName):
return True
if AnnotationContainsTType(node.slice, TypeName):
return True
return False
def IsListAnnotation(annotation: ast.expr) -> bool:
"""检查注解是否为栈上固定数组类型(t.CArray[...] 或 list[type, count])"""
if not isinstance(annotation, ast.Subscript):
return False
value = annotation.value
# t.CArray[...]
if (isinstance(value, ast.Attribute)
and isinstance(value.value, ast.Name)
and value.value.id == 't'
and value.attr == 'CArray'):
return True
# list[type, count] — 仅匹配双参数 Tuple 切片(固定数组),
# 单参数 list[type] 不匹配(走泛型类 list[T] 路径)
if (isinstance(value, ast.Name)
and value.id == 'list'
and isinstance(annotation.slice, ast.Tuple)
and len(annotation.slice.elts) == 2):
return True
return False
def ExtractListFromBinOp(annotation: ast.expr) -> ast.Subscript | None:
"""从 BinOp(BitOr) 注解中提取 list[...] 部分,如 list[i32] | t.CPtr"""
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
for side in (annotation.left, annotation.right):
if IsListAnnotation(side):
return side
return None
class ListAnnotationParseResult:
"""list[...] 注解解析结果。
is_pointer=True 表示单参数 list[type] → 指针模式;
is_pointer=False 表示 Tuple(2) 模式 list[type, count]。
"""
__slots__ = ('elem_type_node', 'count_node', 'is_pointer')
def __init__(self, elem_type_node: ast.expr, count_node: ast.expr | None, is_pointer: bool) -> None:
self.elem_type_node: ast.expr = elem_type_node
self.count_node: ast.expr | None = count_node
self.is_pointer: bool = is_pointer
def ParseListAnnotation(annotation: ast.expr) -> ListAnnotationParseResult | None:
"""解析 list[...] 注解,统一处理 list[type, count] / list[type] / BinOp(BitOr) 形式。
自动处理 BinOp(BitOr) 包裹(如 list[i32, N] | t.CPtr)。
返回 None 表示不是 list 注解。
"""
list_node: ast.expr = annotation
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
extracted: ast.Subscript | None = ExtractListFromBinOp(annotation)
if extracted is None:
return None
list_node = extracted
if not IsListAnnotation(list_node):
return None
slice_node: ast.AST = list_node.slice
if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2:
return ListAnnotationParseResult(slice_node.elts[0], slice_node.elts[1], is_pointer=False)
# 单参数 list[type] → 指针模式
return ListAnnotationParseResult(slice_node, None, is_pointer=True)
def CheckAnnotationHasCInline(annotation_node: ast.AST | None) -> bool:
"""检测注解节点中是否包含 CInline"""
if annotation_node is None:
return False
if isinstance(annotation_node, ast.Attribute):
if hasattr(annotation_node.value, 'id') and IsTModule(annotation_node.value.id) and annotation_node.attr == 'CInline':
return True
if isinstance(annotation_node, ast.Name):
if annotation_node.id == 'CInline':
return True
if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr):
return CheckAnnotationHasCInline(annotation_node.left) or CheckAnnotationHasCInline(annotation_node.right)
return False
def ExtractTypeNameFromBinOp(annotation: ast.expr) -> str | None:
"""从 BinOp(BitOr) 注解左侧提取类型名(如 TypeA | TypeB → TypeA)。
返回 Name.id 或 Attribute.attr,非 BinOp 返回 None。
"""
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
left: ast.expr = annotation.left
if isinstance(left, ast.Name):
return left.id
if isinstance(left, ast.Attribute):
return left.attr
return None
def FindStructNameInAnnotation(node: ast.AST, struct_names: dict | set) -> str | None:
"""递归在注解中查找结构体名(支持 BinOp(BitOr) 递归)。
检查 Name.id、Constant.str、Attribute.attr 是否在 struct_names 中。
支持泛型特化类型(如 GSList[Param]),构造特化名后检查是否在 struct_names 中。
"""
if isinstance(node, ast.Name) and node.id in struct_names:
return node.id
if isinstance(node, ast.Constant) and isinstance(node.value, str) and node.value in struct_names:
return node.value
if isinstance(node, ast.Attribute) and node.attr in struct_names:
return node.attr
if isinstance(node, ast.Subscript):
base_name: str | None = None
if isinstance(node.value, ast.Name):
base_name = node.value.id
elif isinstance(node.value, ast.Attribute):
base_name = node.value.attr
if base_name:
slice_node: ast.AST = node.slice
if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index):
slice_node = slice_node.value
type_args: list[str] = []
if isinstance(slice_node, ast.Name):
type_args.append(slice_node.id)
elif isinstance(slice_node, ast.Attribute):
type_args.append(slice_node.attr)
elif isinstance(slice_node, ast.Tuple):
for elt in slice_node.elts:
if isinstance(elt, ast.Name):
type_args.append(elt.id)
elif isinstance(elt, ast.Attribute):
type_args.append(elt.attr)
if type_args:
spec_name: str = base_name + ''.join(f'[{arg}]' for arg in type_args)
if spec_name in struct_names:
return spec_name
if base_name in struct_names:
return base_name
if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr):
result: str | None = FindStructNameInAnnotation(node.left, struct_names)
if result:
return result
return FindStructNameInAnnotation(node.right, struct_names)
return None
def AnnotationContainsName(annotation: ast.AST, name: str) -> bool:
"""检查注解中是否包含指定名称(支持 BinOp(BitOr) 递归)。
同时检查 Attribute.attr 和 Name.id,比 ast.dump 字符串匹配更精确。
"""
if isinstance(annotation, ast.Attribute):
return annotation.attr == name
if isinstance(annotation, ast.Name):
return annotation.id == name
if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr):
return AnnotationContainsName(annotation.left, name) or AnnotationContainsName(annotation.right, name)
return False
def IsCPtrNode(node: ast.AST) -> bool:
"""检查节点是否为 t.CPtr 属性引用。"""
return (isinstance(node, ast.Attribute)
and isinstance(node.value, ast.Name)
and node.value.id == 't'
and node.attr == 'CPtr')
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
import ast
import vast as ast
import os
import importlib
import types
+9 -9
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
import ast
import vast as ast
from typing import Any, Callable, Dict, TYPE_CHECKING
from lib.core.Exportable import Exportable
@@ -25,9 +25,9 @@ from lib.core.Handles.HandlesBase import CTypeInfo
from lib.core.SymbolTable import SymbolTable
from lib.core.SymbolNode import SymbolNode
from lib.constants.config import mode as _ConfigMode
from lib.core.VLogger import get_logger as _vlog
from lib.core.VLogger import get_logger as _vlog
def _StrictLog(logger: Callable[[str], None], msg: str) -> None:
"""在 strict 模式下记录异常日志"""
if _ConfigMode == "strict":
@@ -66,14 +66,14 @@ class BaseTranslatorMixin:
self.InClass: bool = False # 是否正在处理 class 定义
self._UserTypeModules: dict = {}
self._source_module_sig_files: dict = {}
self._global_function_default_args: dict = {}
self._global_function_default_args: dict = {}
self._global_function_param_names: dict = {}
self.exception_registry: dict = {}
self.exception_parents: dict = {}
self._next_exception_code: int = 100
# 以下属性原先在外部懒注入,此处显式初始化以避免 hasattr/getattr 动态反射
self.config: Any = None
self._ImportedModules: set[str] | None = None
self._next_exception_code: int = 100
# 以下属性原先在外部懒注入,此处显式初始化以避免 hasattr/getattr 动态反射
self.config: Any = None
self._ImportedModules: set[str] | None = None
self._t_c_imported_names: dict = {}
# 导出表
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
import ast
import vast as ast
from typing import TYPE_CHECKING
import llvmlite.ir as ir
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
import ast
import vast as ast
import re
import llvmlite.ir as ir
+1 -1
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
import ast
import vast as ast
import json
import struct
File diff suppressed because it is too large. Load diff
+722 -722
View File
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+348
View File
@@ -0,0 +1,348 @@
"""vast.nodes - 与 CPython 3.12 ast 完全同构的 AST 节点类
vast 是自研 Viper/Python 语法解析器,产出与标准库 ast 模块结构完全一致的
AST 树,从而使编译器侧 ``import vast as ast`` 后原有代码零修改。
本模块定义全部 131 个 AST 节点类(含遗留兼容类),字段/属性与 CPython
3.12 的 ``ast`` 模块逐一对应:
- ``_fields``:节点结构字段(顺序与位置参数构造一致)
- ``_attributes``:dump(include_attributes=True) 时随节点输出的位置属性
构造语义与 CPython 一致:
- 位置参数按 ``_fields`` 顺序赋值,超出字段数抛 TypeError
- 关键字参数全部 setattr(包括未知字段,与 CPython 行为一致)
- 未传入的字段不设置属性(访问时 AttributeError,与 CPython 一致)
"""
from __future__ import annotations
class AST:
"""所有 AST 节点的基类(与 ast.AST 同构)。"""
_fields: tuple[str, ...] = ()
_attributes: tuple[str, ...] = ()
def __init__(self, *args: object, **kwargs: object) -> None:
fields: tuple[str, ...] = self._fields
if len(args) > len(fields):
raise TypeError(
f'{type(self).__name__} constructor takes at most '
f'{len(fields)} positional arguments'
)
for name, val in zip(fields, args):
setattr(self, name, val)
for k, v in kwargs.items():
setattr(self, k, v)
def __eq__(self, other: object) -> bool:
"""AST 节点按对象身份比较(与 CPython ast 一致)。"""
return self is other
# =============================================================================
# 节点定义表:字段表(含位置属性表),数据驱动生成全部节点类
# =============================================================================
_POS_ATTRS: tuple[str, ...] = ('lineno', 'col_offset', 'end_lineno', 'end_col_offset')
_NODE_DEFS: dict[str, tuple[tuple[str, ...], tuple[str, ...]]] = {
# ---- 模块/交互/表达式顶层 ----
'Module': (('body', 'type_ignores'), ()),
'Interactive': (('body',), ()),
'Expression': (('body',), ()),
'FunctionType': (('argtypes', 'returns'), ()),
'Suite': ((), ()),
# ---- 语句 ----
'FunctionDef': (('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment', 'type_params'), _POS_ATTRS),
'AsyncFunctionDef': (('name', 'args', 'body', 'decorator_list', 'returns', 'type_comment', 'type_params'), _POS_ATTRS),
'ClassDef': (('name', 'bases', 'keywords', 'body', 'decorator_list', 'type_params'), _POS_ATTRS),
'Return': (('value',), _POS_ATTRS),
'Delete': (('targets',), _POS_ATTRS),
'Assign': (('targets', 'value', 'type_comment'), _POS_ATTRS),
'AugAssign': (('target', 'op', 'value'), _POS_ATTRS),
'AnnAssign': (('target', 'annotation', 'value', 'simple'), _POS_ATTRS),
'For': (('target', 'iter', 'body', 'orelse', 'type_comment'), _POS_ATTRS),
'AsyncFor': (('target', 'iter', 'body', 'orelse', 'type_comment'), _POS_ATTRS),
'While': (('test', 'body', 'orelse'), _POS_ATTRS),
'If': (('test', 'body', 'orelse'), _POS_ATTRS),
'With': (('items', 'body', 'type_comment'), _POS_ATTRS),
'AsyncWith': (('items', 'body', 'type_comment'), _POS_ATTRS),
'Match': (('subject', 'cases'), _POS_ATTRS),
'Raise': (('exc', 'cause'), _POS_ATTRS),
'Try': (('body', 'handlers', 'orelse', 'finalbody'), _POS_ATTRS),
'TryStar': (('body', 'handlers', 'orelse', 'finalbody'), _POS_ATTRS),
'Assert': (('test', 'msg'), _POS_ATTRS),
'Import': (('names',), _POS_ATTRS),
'ImportFrom': (('module', 'names', 'level'), _POS_ATTRS),
'Global': (('names',), _POS_ATTRS),
'Nonlocal': (('names',), _POS_ATTRS),
'Expr': (('value',), _POS_ATTRS),
'Pass': ((), _POS_ATTRS),
'Break': ((), _POS_ATTRS),
'Continue': ((), _POS_ATTRS),
# ---- 表达式 ----
'BoolOp': (('op', 'values'), _POS_ATTRS),
'NamedExpr': (('target', 'value'), _POS_ATTRS),
'BinOp': (('left', 'op', 'right'), _POS_ATTRS),
'UnaryOp': (('op', 'operand'), _POS_ATTRS),
'Lambda': (('args', 'body'), _POS_ATTRS),
'IfExp': (('test', 'body', 'orelse'), _POS_ATTRS),
'Dict': (('keys', 'values'), _POS_ATTRS),
'Set': (('elts',), _POS_ATTRS),
'ListComp': (('elt', 'generators'), _POS_ATTRS),
'SetComp': (('elt', 'generators'), _POS_ATTRS),
'DictComp': (('key', 'value', 'generators'), _POS_ATTRS),
'GeneratorExp': (('elt', 'generators'), _POS_ATTRS),
'Await': (('value',), _POS_ATTRS),
'Yield': (('value',), _POS_ATTRS),
'YieldFrom': (('value',), _POS_ATTRS),
'Compare': (('left', 'ops', 'comparators'), _POS_ATTRS),
'Call': (('func', 'args', 'keywords'), _POS_ATTRS),
'FormattedValue': (('value', 'conversion', 'format_spec'), _POS_ATTRS),
'JoinedStr': (('values',), _POS_ATTRS),
'Constant': (('value', 'kind'), _POS_ATTRS),
'Attribute': (('value', 'attr', 'ctx'), _POS_ATTRS),
'Subscript': (('value', 'slice', 'ctx'), _POS_ATTRS),
'Starred': (('value', 'ctx'), _POS_ATTRS),
'Name': (('id', 'ctx'), _POS_ATTRS),
'List': (('elts', 'ctx'), _POS_ATTRS),
'Tuple': (('elts', 'ctx'), _POS_ATTRS),
'Slice': (('lower', 'upper', 'step'), _POS_ATTRS),
# ---- match 模式(PEP 634) ----
'MatchValue': (('value',), _POS_ATTRS),
'MatchSingleton': (('value',), _POS_ATTRS),
'MatchSequence': (('patterns',), _POS_ATTRS),
'MatchMapping': (('keys', 'patterns', 'rest'), _POS_ATTRS),
'MatchClass': (('cls', 'patterns', 'kwd_attrs', 'kwd_patterns'), _POS_ATTRS),
'MatchStar': (('name',), _POS_ATTRS),
'MatchAs': (('pattern', 'name'), _POS_ATTRS),
'MatchOr': (('patterns',), _POS_ATTRS),
# ---- 类型参数(PEP 695,仅声明性支持) ----
'TypeVar': (('name', 'bound'), _POS_ATTRS),
'TypeVarTuple': (('name',), _POS_ATTRS),
'ParamSpec': (('name',), _POS_ATTRS),
'TypeAlias': (('name', 'type_params', 'value'), _POS_ATTRS),
# ---- 复合辅助节点 ----
'comprehension': (('target', 'iter', 'ifs', 'is_async'), ()),
'ExceptHandler': (('type', 'name', 'body'), _POS_ATTRS),
'arguments': (('posonlyargs', 'args', 'vararg', 'kwonlyargs', 'kw_defaults', 'kwarg', 'defaults'), ()),
'arg': (('arg', 'annotation', 'type_comment'), _POS_ATTRS),
'keyword': (('arg', 'value'), _POS_ATTRS),
'alias': (('name', 'asname'), _POS_ATTRS),
'withitem': (('context_expr', 'optional_vars'), ()),
'match_case': (('pattern', 'guard', 'body'), ()),
'TypeIgnore': (('lineno', 'tag'), ()),
# ---- 运算符 / 上下文(无字段单例类) ----
'Add': ((), ()),
'Sub': ((), ()),
'Mult': ((), ()),
'MatMult': ((), ()),
'Div': ((), ()),
'Mod': ((), ()),
'Pow': ((), ()),
'LShift': ((), ()),
'RShift': ((), ()),
'BitOr': ((), ()),
'BitXor': ((), ()),
'BitAnd': ((), ()),
'FloorDiv': ((), ()),
'Invert': ((), ()),
'Not': ((), ()),
'UAdd': ((), ()),
'USub': ((), ()),
'Eq': ((), ()),
'NotEq': ((), ()),
'Lt': ((), ()),
'LtE': ((), ()),
'Gt': ((), ()),
'GtE': ((), ()),
'Is': ((), ()),
'IsNot': ((), ()),
'In': ((), ()),
'NotIn': ((), ()),
'And': ((), ()),
'Or': ((), ()),
'Load': ((), ()),
'Store': ((), ()),
'Del': ((), ()),
'AugLoad': ((), ()),
'AugStore': ((), ()),
'Param': ((), ()),
'Ellipsis': ((), _POS_ATTRS),
# ---- 遗留兼容类(已废弃但保留,供 hasattr 检查) ----
'Index': ((), ()),
'ExtSlice': ((), ()),
# ---- 抽象类型占位(纯类型标注用,不实例化) ----
'mod': ((), ()),
'stmt': ((), ()),
'expr': ((), ()),
'expr_context': ((), ()),
'boolop': ((), ()),
'operator': ((), ()),
'unaryop': ((), ()),
'cmpop': ((), ()),
'slice': ((), ()),
'pattern': ((), ()),
'type_ignore': ((), ()),
'type_param': ((), ()),
'excepthandler': ((), ()),
}
_DOCS: dict[str, str] = {
'FunctionDef': '函数定义节点(对应 def 语句)。',
'AsyncFunctionDef': '异步函数定义节点(对应 async def)。',
'ClassDef': '类定义节点(对应 class 语句)。',
'AnnAssign': '带注解的赋值节点(x: T = v)。',
'Constant': '常量节点(数字/字符串/bool/None/Ellipsis)。',
'Name': '名称节点(标识符引用,含 Load/Store/Del 上下文)。',
'Call': '函数调用节点。',
'BinOp': '二元运算节点。',
'Attribute': '属性访问节点(obj.attr)。',
'Subscript': '下标访问节点(obj[idx])。',
'arguments': '函数参数列表节点(含默认值、*args、**kwargs)。',
'alias': 'import 别名节点。',
'keyword': '关键字实参节点(f(x=1))。',
'withitem': 'with 语句上下文节点。',
}
# 类属性默认值(与 CPython 3.12 ast 节点一致):
# dump 时"实例值为 None 且类默认值为 None"的字段会被跳过;
# 无类默认值的字段(如 Constant.value)即使为 None 也会显示。
_CLASS_DEFAULTS: dict[str, dict[str, object]] = {
'AnnAssign': {'value': None},
'Assert': {'msg': None},
'Assign': {'type_comment': None},
'AsyncFor': {'type_comment': None},
'AsyncFunctionDef': {'returns': None, 'type_comment': None},
'AsyncWith': {'type_comment': None},
'Constant': {'kind': None},
'ExceptHandler': {'type': None, 'name': None},
'For': {'type_comment': None},
'FormattedValue': {'format_spec': None},
'FunctionDef': {'returns': None, 'type_comment': None},
'ImportFrom': {'module': None, 'level': None},
'match_case': {'guard': None},
'MatchAs': {'pattern': None, 'name': None},
'MatchMapping': {'rest': None},
'MatchStar': {'name': None},
'Raise': {'exc': None, 'cause': None},
'Return': {'value': None},
'Slice': {'lower': None, 'upper': None, 'step': None},
'TypeVar': {'bound': None},
'With': {'type_comment': None},
'Yield': {'value': None},
'alias': {'asname': None},
'arg': {'annotation': None, 'type_comment': None},
'arguments': {'vararg': None, 'kwarg': None},
'keyword': {'arg': None},
'withitem': {'optional_vars': None},
}
# 节点抽象基类映射(与 CPython 3.12 一致:expr/stmt/mod/operator/cmpop/...)
# 节点类继承对应的抽象基类,使 isinstance(node, ast.expr) 等判断可用
_BASE_CLASS: dict[str, str] = {
# stmt
'AnnAssign': 'stmt', 'Assert': 'stmt', 'Assign': 'stmt', 'AsyncFor': 'stmt',
'AsyncFunctionDef': 'stmt', 'AsyncWith': 'stmt', 'AugAssign': 'stmt',
'Break': 'stmt', 'ClassDef': 'stmt', 'Continue': 'stmt', 'Delete': 'stmt',
'Expr': 'stmt', 'For': 'stmt', 'FunctionDef': 'stmt', 'Global': 'stmt',
'If': 'stmt', 'Import': 'stmt', 'ImportFrom': 'stmt', 'Match': 'stmt',
'Nonlocal': 'stmt', 'Pass': 'stmt', 'Raise': 'stmt', 'Return': 'stmt',
'Try': 'stmt', 'TryStar': 'stmt', 'TypeAlias': 'stmt', 'While': 'stmt',
'With': 'stmt',
# expr
'Attribute': 'expr', 'Await': 'expr', 'BinOp': 'expr', 'BoolOp': 'expr',
'Call': 'expr', 'Compare': 'expr', 'Constant': 'expr', 'Dict': 'expr',
'DictComp': 'expr', 'FormattedValue': 'expr', 'GeneratorExp': 'expr',
'IfExp': 'expr', 'JoinedStr': 'expr', 'Lambda': 'expr', 'List': 'expr',
'ListComp': 'expr', 'Name': 'expr', 'NamedExpr': 'expr', 'Set': 'expr',
'SetComp': 'expr', 'Slice': 'expr', 'Starred': 'expr', 'Subscript': 'expr',
'Tuple': 'expr', 'UnaryOp': 'expr', 'Yield': 'expr', 'YieldFrom': 'expr',
# mod
'Module': 'mod', 'Interactive': 'mod', 'Expression': 'mod',
'FunctionType': 'mod', 'Suite': 'mod',
# operator
'Add': 'operator', 'Sub': 'operator', 'Mult': 'operator', 'MatMult': 'operator',
'Div': 'operator', 'Mod': 'operator', 'Pow': 'operator', 'LShift': 'operator',
'RShift': 'operator', 'BitOr': 'operator', 'BitXor': 'operator',
'BitAnd': 'operator', 'FloorDiv': 'operator',
# unaryop
'Invert': 'unaryop', 'Not': 'unaryop', 'UAdd': 'unaryop', 'USub': 'unaryop',
# boolop
'And': 'boolop', 'Or': 'boolop',
# cmpop
'Eq': 'cmpop', 'NotEq': 'cmpop', 'Lt': 'cmpop', 'LtE': 'cmpop',
'Gt': 'cmpop', 'GtE': 'cmpop', 'Is': 'cmpop', 'IsNot': 'cmpop',
'In': 'cmpop', 'NotIn': 'cmpop',
# expr_context
'Load': 'expr_context', 'Store': 'expr_context', 'Del': 'expr_context',
'AugLoad': 'expr_context', 'AugStore': 'expr_context', 'Param': 'expr_context',
# slice
'Index': 'slice', 'ExtSlice': 'slice',
# pattern
'MatchValue': 'pattern', 'MatchSingleton': 'pattern', 'MatchSequence': 'pattern',
'MatchMapping': 'pattern', 'MatchClass': 'pattern', 'MatchStar': 'pattern',
'MatchAs': 'pattern', 'MatchOr': 'pattern',
# type_param / type_ignore
'TypeVar': 'type_param', 'TypeVarTuple': 'type_param', 'ParamSpec': 'type_param',
'TypeIgnore': 'type_ignore',
# excepthandler
'ExceptHandler': 'excepthandler',
}
def _MakeNodeClass(name: str, fields: tuple[str, ...], attrs: tuple[str, ...]) -> type:
"""按字段表生成节点类(继承对应抽象基类)。"""
def _Init(self: AST, *args: object, **kwargs: object) -> None:
if len(args) > len(fields):
raise TypeError(
f'{name} constructor takes at most {len(fields)} positional arguments'
)
for fname, val in zip(fields, args):
setattr(self, fname, val)
for k, v in kwargs.items():
setattr(self, k, v)
base_name: str | None = _BASE_CLASS.get(name)
base_cls: type = globals()[base_name] if base_name else AST
ns: dict[str, object] = {
'__init__': _Init,
'_fields': fields,
'_attributes': attrs,
'__module__': __name__,
'__doc__': _DOCS.get(name, f'{name} AST 节点(与 ast.{name} 同构)。'),
}
# 类默认值(None 字段在 dump 时跳过)
for fname, fdefault in _CLASS_DEFAULTS.get(name, {}).items():
ns[fname] = fdefault
cls: type = type(name, (base_cls,), ns)
return cls
# 动态生成全部节点类到本模块命名空间
# 抽象基类(mod/stmt/expr/...)在 _NODE_DEFS 末尾定义,但需先生成
_ABSTRACT_BASES: tuple[str, ...] = (
'mod', 'stmt', 'expr', 'expr_context', 'boolop', 'operator', 'unaryop',
'cmpop', 'slice', 'pattern', 'type_ignore', 'type_param', 'excepthandler',
)
for _name in _ABSTRACT_BASES:
_fields, _attrs = _NODE_DEFS[_name]
globals()[_name] = _MakeNodeClass(_name, _fields, _attrs)
for _name, (_fields, _attrs) in _NODE_DEFS.items():
if _name in _ABSTRACT_BASES:
continue
globals()[_name] = _MakeNodeClass(_name, _fields, _attrs)
# 显式别名:AnnAssign 已在表内;这里保证模块级 __all__ 完整性
__all__: list[str] = list(_NODE_DEFS.keys()) + ['AST']
+1044
View File
File diff suppressed because it is too large. Load diff
+490
View File
@@ -0,0 +1,490 @@
"""vast.tokenizer - Viper/Python 词法分析器
将源码字符串切分为 token 流,与 CPython 3.12 ``tokenize`` 语义对齐:
- 支持缩进 INDENT/DEDENT、逻辑行 NEWLINE、括号内续行
- 支持字符串字面量(单引号/双引号/三引号、r/b/u/f 前缀与组合前缀)
- f-string 作为整体 FSTRING token 输出,内部表达式由 Parser 递归解析
- 数字字面量(十进制/十六进制/八进制/二进制/浮点/复数 j/下划线分隔)
- 运算符最长匹配
位置信息与 CPython 一致:``col_offset`` 为 UTF-8 字节偏移(非字符偏移),
行号为 1-based。行列信息通过行起始索引表 + bisect 统一换算,保证跨行
token(字符串/三引号)的行列准确。
软关键字 fn/fun/func/function 不作为独立 token 类型处理(并入解析器),
tokenizer 产出普通 NAME,由 Parser 在语句首识别为函数定义关键字。
"""
from __future__ import annotations
import bisect
# =============================================================================
# Token 类型常量
# =============================================================================
ENDMARKER = 'ENDMARKER'
NAME = 'NAME'
NUMBER = 'NUMBER'
STRING = 'STRING'
FSTRING = 'FSTRING'
OP = 'OP'
NEWLINE = 'NEWLINE'
INDENT = 'INDENT'
DEDENT = 'DEDENT'
COMMENT = 'COMMENT'
NL = 'NL'
# 多字符运算符(最长优先,含 Python 3.12 全部运算符)
_OPERATORS: tuple[str, ...] = (
'**=', '//=', '<<=', '>>=', '**', '//', '<<', '>>', '<=', '>=', '==', '!=',
':=', '->', '+=', '-=', '*=', '/=', '%=', '@=', '&=', '|=', '^=', '...',
'(', ')', '[', ']', '{', '}', ',', ':', '.', ';', '+', '-', '*', '/', '%',
'@', '&', '|', '^', '~', '<', '>', '=', '!',
)
# 行首空白 + 关键字 + 空白 + 标识符 → 软关键字函数定义(Parser 层处理)
SOFT_KEYWORDS: frozenset[str] = frozenset({'fn', 'fun', 'func', 'function'})
# 字符串前缀字母集合(允许任意顺序/大小写组合,如 fr、Rb、BF)
_STRING_PREFIX_CHARS: frozenset[str] = frozenset('rRbBuUfF')
_F_PREFIX = frozenset('fF')
_R_PREFIX = frozenset('rR')
_B_PREFIX = frozenset('bB')
class Token:
"""词法 token。
Attributes:
Type: token 类型(NAME/NUMBER/STRING/FSTRING/OP/NEWLINE/...)
Value: token 原始文本
Line: 1-based 行号
Col: UTF-8 字节偏移(0-based)
EndLine: 结束行号
EndCol: 结束列(UTF-8 字节偏移)
Start: 源码字符偏移(含 UTF-8 字节偏移换算依赖,供 f-string 内部定位)
End: 源码字符偏移(exclusive)
"""
__slots__ = ('Type', 'Value', 'Line', 'Col', 'EndLine', 'EndCol', 'Start', 'End')
def __init__(self, tok_type: str, value: str, line: int, col: int,
end_line: int, end_col: int) -> None:
self.Type: str = tok_type
self.Value: str = value
self.Line: int = line
self.Col: int = col
self.EndLine: int = end_line
self.EndCol: int = end_col
self.Start: int = -1
self.End: int = -1
def __repr__(self) -> str:
return (f'Token({self.Type}, {self.Value!r}, '
f'{self.Line}:{self.Col}-{self.EndLine}:{self.EndCol})')
def _IsIdentStart(ch: str) -> bool:
"""标识符起始字符:字母或下划线(兼容非 ASCII)。"""
return ch == '_' or ch.isalpha() or ord(ch) > 127
def _IsIdentPart(ch: str) -> bool:
"""标识符组成部分:字母/数字/下划线。"""
return ch == '_' or ch.isalnum() or ord(ch) > 127
class Tokenizer:
"""Viper/Python 源码 → token 流。
Usage::
toks: list[Token] = Tokenizer(source).Tokenize()
"""
def __init__(self, source: str) -> None:
self.Source: str = source
self.Length: int = len(source)
self.Pos: int = 0 # 当前字符索引
self.Tokens: list[Token] = []
# 括号深度:>0 表示在括号内(逻辑行不结束)
self.BracketDepth: int = 0
# 缩进栈:0 为根
self.Indents: list[int] = [0]
# 行首标志(决定是否进行缩进分析)
self.AtLineStart: bool = True
# 反斜杠续行延续标志:续行后的物理行行首空白不产生 INDENT
self.ContinuationLine: bool = False
# 空行/注释行标志:本物理行无实质内容,行尾换行产 NL 而非 NEWLINE
self.BlankLine: bool = False
# 注释 token 收集(Parser 忽略,但保留供诊断)
self.Comments: list[Token] = []
# 行起始字符索引表(索引 0 对应第 1 行),用于行列换算
self._LineStarts: list[int] = [0]
# ------------------------------------------------------------------
# 位置换算
# ------------------------------------------------------------------
def _PosInfo(self, pos: int) -> tuple[int, int]:
"""将字符索引换算为 (1-based 行号, UTF-8 字节列偏移)。"""
idx: int = bisect.bisect_right(self._LineStarts, pos) - 1
line: int = idx + 1
col_bytes: int = len(self.Source[self._LineStarts[idx]:pos].encode('utf-8'))
return line, col_bytes
def _Make(self, tok_type: str, start: int, end: int) -> Token:
"""以 [start, end) 源码区间构造 token。"""
start_line, start_col = self._PosInfo(start)
end_line, end_col = self._PosInfo(end)
tok: Token = Token(tok_type, self.Source[start:end], start_line, start_col,
end_line, end_col)
tok.Start = start
tok.End = end
return tok
def _RegisterNewline(self, pos_after_nl: int) -> None:
"""登记换行:pos_after_nl 是换行序列之后的字符索引(即新行行首)。"""
self._LineStarts.append(pos_after_nl)
# ------------------------------------------------------------------
# 主入口
# ------------------------------------------------------------------
def Tokenize(self) -> list[Token]:
"""执行词法分析,返回完整 token 流(以 ENDMARKER 结尾)。"""
while self.Pos < self.Length:
ch: str = self.Source[self.Pos]
if (self.AtLineStart and self.BracketDepth == 0
and not self.ContinuationLine and ch not in '\r\n'):
self._HandleIndent()
if self.Pos >= self.Length:
break
continue
if ch in ' \t\f':
self.Pos += 1
continue
if ch == '\r':
self.Pos += 1
if self.Pos < self.Length and self.Source[self.Pos] == '\n':
self.Pos += 1
self._HandleNewline(self.Pos)
continue
if ch == '\n':
self.Pos += 1
self._HandleNewline(self.Pos)
continue
if ch == '#':
self._ScanComment()
continue
if ch == '\\':
self._ScanContinuation()
continue
if ch in '([{':
self.BracketDepth += 1
self._EmitOp()
continue
if ch in ')]}':
if self.BracketDepth > 0:
self.BracketDepth -= 1
self._EmitOp()
continue
if ch == "'" or ch == '"' or _IsIdentStart(ch):
if self._TryScanString():
continue
if ch.isdigit():
self._ScanNumber()
continue
if _IsIdentStart(ch):
self._ScanName()
continue
if ch.isdigit() or (ch == '.' and self.Pos + 1 < self.Length
and self.Source[self.Pos + 1].isdigit()):
self._ScanNumber()
continue
self._EmitOp()
self._FinishFile()
return self.Tokens
# ------------------------------------------------------------------
# 换行 / 缩进 / 续行 / 注释
# ------------------------------------------------------------------
def _HandleNewline(self, pos_after_nl: int) -> None:
"""处理物理换行:括号外为 NEWLINE,括号内为 NL。
Args:
pos_after_nl: 换行序列之后的字符索引(新行行首)。
"""
# 找换行序列起点(\n 或 \r\n)
nl_start: int = self.Pos - 1
if nl_start > 0 and self.Source[nl_start] == '\n' and self.Source[nl_start - 1] == '\r':
nl_start -= 1
if self.BracketDepth > 0 or self.BlankLine or (self.AtLineStart and not self.ContinuationLine):
self.Tokens.append(self._Make(NL, nl_start, self.Pos))
else:
self.Tokens.append(self._Make(NEWLINE, nl_start, self.Pos))
self._RegisterNewline(pos_after_nl)
self.AtLineStart = True
self.ContinuationLine = False
self.BlankLine = False
def _HandleIndent(self) -> None:
"""行首缩进分析:产出 INDENT/DEDENT 或忽略。"""
start: int = self.Pos
col: int = 0
while self.Pos < self.Length:
ch: str = self.Source[self.Pos]
if ch == ' ':
col += 1
self.Pos += 1
elif ch == '\t':
col = (col // 8 + 1) * 8
self.Pos += 1
elif ch == '\f':
self.Pos += 1
else:
break
self.AtLineStart = False
# 空行/注释行不产生缩进 token,且行尾换行应产 NL(逻辑行不结束)
if self.Pos >= self.Length or self.Source[self.Pos] in '\r\n#':
self.BlankLine = True
return
cur: int = self.Indents[-1]
if col > cur:
self.Indents.append(col)
self.Tokens.append(self._Make(INDENT, start, self.Pos))
elif col < cur:
while self.Indents and self.Indents[-1] > col:
self.Indents.pop()
self.Tokens.append(self._Make(DEDENT, self.Pos, self.Pos))
def _ScanContinuation(self) -> None:
"""反斜杠续行:跳过 \\ + 换行(逻辑行内,不产生 NEWLINE)。"""
if self.Pos + 1 < self.Length and self.Source[self.Pos + 1] in '\r\n':
self.Pos += 1
if self.Pos < self.Length and self.Source[self.Pos] == '\r':
self.Pos += 1
if self.Pos < self.Length and self.Source[self.Pos] == '\n':
self.Pos += 1
self._RegisterNewline(self.Pos)
self.AtLineStart = True
self.ContinuationLine = True
else:
self.Pos += 1
def _ScanComment(self) -> None:
"""扫描注释到行尾(不产出 COMMENT token,仅保留)。"""
start: int = self.Pos
while self.Pos < self.Length and self.Source[self.Pos] not in '\r\n':
self.Pos += 1
tok: Token = self._Make(COMMENT, start, self.Pos)
self.Comments.append(tok)
# ------------------------------------------------------------------
# 基础 token
# ------------------------------------------------------------------
def _EmitOp(self) -> None:
"""运算符最长匹配(含 ... 与增强赋值运算符)。"""
start: int = self.Pos
for op in _OPERATORS:
if self.Source.startswith(op, self.Pos):
self.Pos += len(op)
self.Tokens.append(self._Make(OP, start, self.Pos))
self.AtLineStart = False
return
# 未知字符:单字符 OP 兜底
self.Pos += 1
self.Tokens.append(self._Make(OP, start, self.Pos))
self.AtLineStart = False
def _ScanName(self) -> None:
"""扫描标识符。"""
start: int = self.Pos
while self.Pos < self.Length and _IsIdentPart(self.Source[self.Pos]):
self.Pos += 1
self.Tokens.append(self._Make(NAME, start, self.Pos))
self.AtLineStart = False
def _ScanNumber(self) -> None:
"""扫描数字字面量:进制前缀/十进制/浮点/指数/复数 j/下划线。"""
start: int = self.Pos
# 进制前缀 0x/0o/0b
if self.Source[self.Pos] == '0' and self.Pos + 1 < self.Length:
nxt: str = self.Source[self.Pos + 1]
if nxt in 'xXoObB':
self.Pos += 2
while self.Pos < self.Length and (self.Source[self.Pos].isalnum()
or self.Source[self.Pos] == '_'):
self.Pos += 1
self.Tokens.append(self._Make(NUMBER, start, self.Pos))
self.AtLineStart = False
return
# 整数部分
while self.Pos < self.Length and (self.Source[self.Pos].isdigit()
or self.Source[self.Pos] == '_'):
self.Pos += 1
# 浮点小数点
if self.Pos < self.Length and self.Source[self.Pos] == '.':
self.Pos += 1
while self.Pos < self.Length and (self.Source[self.Pos].isdigit()
or self.Source[self.Pos] == '_'):
self.Pos += 1
# 指数
if self.Pos < self.Length and self.Source[self.Pos] in 'eE':
save: int = self.Pos
self.Pos += 1
if self.Pos < self.Length and self.Source[self.Pos] in '+-':
self.Pos += 1
if self.Pos < self.Length and (self.Source[self.Pos].isdigit()
or self.Source[self.Pos] == '_'):
while self.Pos < self.Length and (self.Source[self.Pos].isdigit()
or self.Source[self.Pos] == '_'):
self.Pos += 1
else:
self.Pos = save # 不是指数,回退
# 复数 j
if self.Pos < self.Length and self.Source[self.Pos] in 'jJ':
self.Pos += 1
self.Tokens.append(self._Make(NUMBER, start, self.Pos))
self.AtLineStart = False
# ------------------------------------------------------------------
# 字符串
# ------------------------------------------------------------------
def _TryScanString(self) -> bool:
"""尝试从当前位置扫描字符串字面量(含 r/b/u/f 前缀)。
- 前缀字母序列后紧跟引号 → 按字符串扫描,返回 True
- 否则回退位置,返回 False(由调用方走标识符/数字路径)
"""
start: int = self.Pos
# 收集前缀字母
while self.Pos < self.Length and self.Source[self.Pos] in _STRING_PREFIX_CHARS:
self.Pos += 1
# 前缀后必须是引号,否则回退
if self.Pos >= self.Length or self.Source[self.Pos] not in '\'"':
self.Pos = start
return False
prefix: str = self.Source[start:self.Pos]
is_fstring: bool = any(c in _F_PREFIX for c in prefix)
quote: str = self.Source[self.Pos]
triple: bool = self.Source.startswith(quote * 3, self.Pos)
if triple:
self.Pos += 3
else:
self.Pos += 1
self._ScanStringBody(quote, triple, is_fstring)
end: int = self.Pos
tok_type: str = FSTRING if is_fstring else STRING
self.Tokens.append(self._Make(tok_type, start, end))
self.AtLineStart = False
return True
def _ScanStringBody(self, quote: str, triple: bool, is_fstring: bool) -> None:
"""扫描字符串主体直到结束引号(处理转义、f-string 花括号、跨行登记)。"""
while self.Pos < self.Length:
ch: str = self.Source[self.Pos]
if ch == '\\':
# 反斜杠转义:\{{ 中反斜杠只转义自身,后续 {{ 仍需按花括号转义处理
if self.Pos + 1 < self.Length and self.Source[self.Pos + 1] in '{}':
self.Pos += 1
else:
self.Pos += 2
continue
if ch == '\n':
# 三引号字符串跨行:登记行起始,保证后续行列换算准确
self.Pos += 1
self._RegisterNewline(self.Pos)
continue
if is_fstring and ch == '{':
if self.Source.startswith('{{', self.Pos):
self.Pos += 2
continue
self._ScanFStringExpr()
continue
if is_fstring and ch == '}':
if self.Source.startswith('}}', self.Pos):
self.Pos += 2
continue
self.Pos += 1
continue
if ch == quote:
if triple:
if self.Source.startswith(quote * 3, self.Pos):
self.Pos += 3
return
self.Pos += 1
else:
self.Pos += 1
return
self.Pos += 1
# 未闭合字符串:直接结束(Parser 层报语法错误)
def _ScanFStringExpr(self) -> None:
"""跳过 f-string 的 {expr[!conv][:spec]} 表达式段。
表达式内可含字符串字面量(含嵌套 f-string)与括号嵌套,需整体跳过;
跨行时登记行起始(三引号 f-string)。
"""
self.Pos += 1 # 跳过 {
depth: int = 1
while self.Pos < self.Length:
ch: str = self.Source[self.Pos]
if ch == '\\':
self.Pos += 2
continue
if ch == '\n':
self.Pos += 1
self._RegisterNewline(self.Pos)
continue
if ch in '\'"':
self._SkipInnerString()
continue
if ch == '(' or ch == '[' or ch == '{':
depth += 1
self.Pos += 1
continue
if ch == ')' or ch == ']':
depth -= 1
self.Pos += 1
continue
if ch == '}':
if depth == 1:
self.Pos += 1
return
depth -= 1
self.Pos += 1
continue
self.Pos += 1
def _SkipInnerString(self) -> None:
"""跳过 f-string 表达式内部的字符串字面量(含前缀与三引号)。"""
save: int = self.Pos
while self.Pos < self.Length and self.Source[self.Pos] in _STRING_PREFIX_CHARS:
self.Pos += 1
if self.Pos >= self.Length or self.Source[self.Pos] not in '\'"':
self.Pos = save
self.Pos += 1
return
inner_quote: str = self.Source[self.Pos]
triple: bool = self.Source.startswith(inner_quote * 3, self.Pos)
if triple:
self.Pos += 3
self._ScanStringBody(inner_quote, True, False)
else:
self.Pos += 1
self._ScanStringBody(inner_quote, False, False)
# ------------------------------------------------------------------
# 文件尾
# ------------------------------------------------------------------
def _FinishFile(self) -> None:
"""文件末尾:补 NEWLINE、DEDENT、ENDMARKER。"""
if self.Tokens and self.Tokens[-1].Type not in (NEWLINE, DEDENT, INDENT):
self.Tokens.append(self._Make(NEWLINE, self.Pos, self.Pos))
while len(self.Indents) > 1:
self.Indents.pop()
self.Tokens.append(self._Make(DEDENT, self.Pos, self.Pos))
self.Tokens.append(self._Make(ENDMARKER, self.Pos, self.Pos))
def TokenizeSource(source: str) -> list[Token]:
"""便捷入口:源码 → token 流。"""
return Tokenizer(source).Tokenize()
+533
View File
@@ -0,0 +1,533 @@
"""vast.unparser - AST 还原为源码字符串(与 ast.unparse 语义一致)
TPC 编译器多处用 ``ast.unparse`` 提取类型注解字符串(如 ``t.CArray[...]``、
``list[i32, N]``、数组维度、默认值文本等)。本模块实现全部节点类型的
源码还原,保证 ``import vast as ast`` 后这些调用零修改。
"""
from __future__ import annotations
from lib.vast import Nodes as N
class Unparser:
"""递归将 vast 节点还原为源码文本。"""
def __init__(self) -> None:
self._Depth: int = 0
# ------------------------------------------------------------------
# 顶层
# ------------------------------------------------------------------
def Unparse(self, node: N.AST) -> str:
if isinstance(node, N.Module):
return '\n'.join(self._stmt(s) for s in node.body)
if isinstance(node, N.Expression):
return self._expr(node.body)
return self._expr(node) if isinstance(node, N.expr) else self._stmt(node) # type: ignore[arg-type]
# ------------------------------------------------------------------
# 语句
# ------------------------------------------------------------------
def _stmt(self, node: N.AST) -> str:
name: str = type(node).__name__
m: dict[str, object] = {
'FunctionDef': self._FunctionDef,
'AsyncFunctionDef': self._FunctionDef,
'ClassDef': self._ClassDef,
'Return': self._Return,
'Delete': self._Delete,
'Assign': self._Assign,
'AugAssign': self._AugAssign,
'AnnAssign': self._AnnAssign,
'For': self._For,
'AsyncFor': self._For,
'While': self._While,
'If': self._If,
'With': self._With,
'AsyncWith': self._With,
'Raise': self._Raise,
'Try': self._Try,
'TryStar': self._Try,
'Assert': self._Assert,
'Import': self._Import,
'ImportFrom': self._ImportFrom,
'Global': self._Global,
'Nonlocal': self._Global,
'Expr': self._ExprStmt,
'Pass': self._Pass,
'Break': lambda n: 'break',
'Continue': lambda n: 'continue',
'Match': self._Match,
}
fn = m.get(name)
if fn is not None:
return fn(node) # type: ignore[operator]
raise NotImplementedError(f'unparse stmt: {name}')
def _Block(self, body: list[N.stmt]) -> str:
self._Depth += 1
pad: str = ' ' * self._Depth
lines: list[str] = [f'{pad}{self._stmt(s)}' for s in body]
self._Depth -= 1
return '\n'.join(lines)
def _FunctionDef(self, node: N.FunctionDef) -> str:
kw: str = 'async def' if isinstance(node, N.AsyncFunctionDef) else 'def'
decos: str = ''.join(f'@{self._expr(d)}\n' for d in node.decorator_list)
params: str = self._Arguments(node.args)
ret: str = ''
if node.returns is not None:
ret = f' -> {self._expr(node.returns)}'
body: str = self._Block(node.body)
return f'{decos}{kw} {node.name}({params}){ret}:\n{body}'
def _ClassDef(self, node: N.ClassDef) -> str:
decos: str = ''.join(f'@{self._expr(d)}\n' for d in node.decorator_list)
args: list[str] = [self._expr(b) for b in node.bases]
for kw in node.keywords:
args.append(f'{kw.arg}={self._expr(kw.value)}' if kw.arg else f'**{self._expr(kw.value)}')
header: str = f'class {node.name}' + (f'({", ".join(args)})' if args else '') + ':'
return f'{decos}{header}\n{self._Block(node.body)}'
def _Return(self, node: N.Return) -> str:
return 'return' if node.value is None else f'return {self._expr(node.value)}'
def _Delete(self, node: N.Delete) -> str:
return 'del ' + ', '.join(self._expr(t) for t in node.targets)
def _Assign(self, node: N.Assign) -> str:
return ', '.join(self._expr(t) for t in node.targets) + f' = {self._expr(node.value)}'
def _AugAssign(self, node: N.AugAssign) -> str:
ops: dict[type, str] = {
N.Add: '+', N.Sub: '-', N.Mult: '*', N.Div: '/', N.FloorDiv: '//',
N.Mod: '%', N.Pow: '**', N.LShift: '<<', N.RShift: '>>',
N.BitOr: '|', N.BitXor: '^', N.BitAnd: '&', N.MatMult: '@',
}
op: str = ops.get(type(node.op), '?')
return f'{self._expr(node.target)} {op}= {self._expr(node.value)}'
def _AnnAssign(self, node: N.AnnAssign) -> str:
val: str = f' = {self._expr(node.value)}' if node.value is not None else ''
return f'{self._expr(node.target)}: {self._expr(node.annotation)}{val}'
def _For(self, node: N.For) -> str:
kw: str = 'async for' if isinstance(node, N.AsyncFor) else 'for'
orelse: str = f'\n{" " * self._Depth}else:\n{self._Block(node.orelse)}' if node.orelse else ''
return f'{kw} {self._expr(node.target)} in {self._expr(node.iter)}:\n{self._Block(node.body)}{orelse}'
def _While(self, node: N.While) -> str:
orelse: str = f'\n{" " * self._Depth}else:\n{self._Block(node.orelse)}' if node.orelse else ''
return f'while {self._expr(node.test)}:\n{self._Block(node.body)}{orelse}'
def _If(self, node: N.If) -> str:
out: str = f'if {self._expr(node.test)}:\n{self._Block(node.body)}'
if node.orelse:
if len(node.orelse) == 1 and isinstance(node.orelse[0], N.If):
out += f'\n{" " * self._Depth}elif {self._expr(node.orelse[0].test)}:\n{self._Block(node.orelse[0].body)}'
out += self._IfTail(node.orelse[0])
else:
out += f'\n{" " * self._Depth}else:\n{self._Block(node.orelse)}'
return out
def _IfTail(self, node: N.If) -> str:
out: str = ''
if node.orelse:
if len(node.orelse) == 1 and isinstance(node.orelse[0], N.If):
out += f'\n{" " * self._Depth}elif {self._expr(node.orelse[0].test)}:\n{self._Block(node.orelse[0].body)}'
out += self._IfTail(node.orelse[0])
else:
out += f'\n{" " * self._Depth}else:\n{self._Block(node.orelse)}'
return out
def _With(self, node: N.With) -> str:
kw: str = 'async with' if isinstance(node, N.AsyncWith) else 'with'
items: list[str] = []
for item in node.items:
s: str = self._expr(item.context_expr)
if item.optional_vars is not None:
s += f' as {self._expr(item.optional_vars)}'
items.append(s)
return f'{kw} {", ".join(items)}:\n{self._Block(node.body)}'
def _Raise(self, node: N.Raise) -> str:
if node.exc is None:
return 'raise'
out: str = f'raise {self._expr(node.exc)}'
if node.cause is not None:
out += f' from {self._expr(node.cause)}'
return out
def _Try(self, node: N.Try) -> str:
out: str = f'try:\n{self._Block(node.body)}'
for h in node.handlers:
hdr: str = 'except*' if isinstance(node, N.TryStar) else 'except'
if h.type is not None:
hdr += f' {self._expr(h.type)}'
if h.name:
hdr += f' as {h.name}'
out += f'\n{" " * self._Depth}{hdr}:\n{self._Block(h.body)}'
if node.orelse:
out += f'\n{" " * self._Depth}else:\n{self._Block(node.orelse)}'
if node.finalbody:
out += f'\n{" " * self._Depth}finally:\n{self._Block(node.finalbody)}'
return out
def _Assert(self, node: N.Assert) -> str:
out: str = f'assert {self._expr(node.test)}'
if node.msg is not None:
out += f', {self._expr(node.msg)}'
return out
def _Import(self, node: N.Import) -> str:
names: list[str] = []
for a in node.names:
s: str = a.name
if a.asname:
s += f' as {a.asname}'
names.append(s)
return 'import ' + ', '.join(names)
def _ImportFrom(self, node: N.ImportFrom) -> str:
dots: str = '.' * node.level
mod: str = node.module or ''
names: list[str] = []
for a in node.names:
s: str = a.name
if a.asname:
s += f' as {a.asname}'
names.append(s)
return f'from {dots}{mod} import {", ".join(names)}'
def _Global(self, node: N.AST) -> str:
kw: str = 'global' if isinstance(node, N.Global) else 'nonlocal'
return f'{kw} {", ".join(node.names)}'
def _ExprStmt(self, node: N.Expr) -> str:
return self._expr(node.value)
def _Pass(self, node: N.Pass) -> str:
return 'pass'
def _Match(self, node: N.Match) -> str:
out: str = f'match {self._expr(node.subject)}:\n'
self._Depth += 1
pad: str = ' ' * self._Depth
for case in node.cases:
guard: str = f' if {self._expr(case.guard)}' if case.guard else ''
out += f'{pad}case {self._Pattern(case.pattern)}{guard}:\n'
self._Depth += 1
out += self._Block(case.body)
self._Depth -= 1
self._Depth -= 1
return out
def _Pattern(self, node: N.pattern) -> str:
name: str = type(node).__name__
if isinstance(node, N.MatchValue):
return self._expr(node.value)
if isinstance(node, N.MatchSingleton):
return repr(node.value)
if isinstance(node, N.MatchSequence):
return '[' + ', '.join(self._Pattern(p) for p in node.patterns) + ']'
if isinstance(node, N.MatchMapping):
parts: list[str] = []
for k, p in zip(node.keys, node.patterns):
parts.append(f'{self._expr(k)}: {self._Pattern(p)}')
if node.rest:
parts.append(f'**{node.rest}')
return '{' + ', '.join(parts) + '}'
if isinstance(node, N.MatchClass):
pos: list[str] = [self._Pattern(p) for p in node.patterns]
kw: list[str] = [f'{a}={self._Pattern(p)}' for a, p in zip(node.kwd_attrs, node.kwd_patterns)]
return f'{self._expr(node.cls)}({", ".join(pos + kw)})'
if isinstance(node, N.MatchStar):
return f'*{node.name}' if node.name else '*_'
if isinstance(node, N.MatchAs):
if node.pattern is not None:
return f'{self._Pattern(node.pattern)} as {node.name}'
return node.name or '_'
if isinstance(node, N.MatchOr):
return ' | '.join(self._Pattern(p) for p in node.patterns)
raise NotImplementedError(f'unparse pattern: {name}')
# ------------------------------------------------------------------
# 表达式
# ------------------------------------------------------------------
def _expr(self, node: N.AST) -> str:
name: str = type(node).__name__
m: dict[str, object] = {
'Name': self._Name,
'Constant': self._Constant,
'Attribute': self._Attribute,
'Call': self._Call,
'BinOp': self._BinOp,
'UnaryOp': self._UnaryOp,
'BoolOp': self._BoolOp,
'Compare': self._Compare,
'Subscript': self._Subscript,
'Slice': self._Slice,
'Tuple': self._Tuple,
'List': self._List,
'Set': self._Set,
'Dict': self._Dict,
'JoinedStr': self._JoinedStr,
'FormattedValue': self._FormattedValue,
'Lambda': self._Lambda,
'IfExp': self._IfExp,
'Starred': self._Starred,
'ListComp': self._ListComp,
'SetComp': self._ListComp,
'GeneratorExp': self._ListComp,
'DictComp': self._DictComp,
'NamedExpr': self._NamedExpr,
'Await': self._Await,
'Yield': self._Yield,
'YieldFrom': self._YieldFrom,
}
fn = m.get(name)
if fn is not None:
return fn(node) # type: ignore[operator]
raise NotImplementedError(f'unparse expr: {name}')
def _Name(self, node: N.Name) -> str:
return node.id
def _Constant(self, node: N.Constant) -> str:
v = node.value
if v is Ellipsis:
return '...'
if v is None:
return 'None'
if v is True:
return 'True'
if v is False:
return 'False'
if isinstance(v, str):
return repr(v)
if isinstance(v, bytes):
return repr(v)
if isinstance(v, complex):
# 复数文本
return repr(v)
return repr(v)
def _Attribute(self, node: N.Attribute) -> str:
return f'{self._expr(node.value)}.{node.attr}'
def _Call(self, node: N.Call) -> str:
args: list[str] = [self._expr(a) for a in node.args]
for kw in node.keywords:
if kw.arg is None:
args.append(f'**{self._expr(kw.value)}')
else:
args.append(f'{kw.arg}={self._expr(kw.value)}')
return f'{self._expr(node.func)}({", ".join(args)})'
_BINOP_OPS: dict[type, str] = {
N.Add: '+', N.Sub: '-', N.Mult: '*', N.Div: '/', N.FloorDiv: '//',
N.Mod: '%', N.Pow: '**', N.LShift: '<<', N.RShift: '>>',
N.BitOr: '|', N.BitXor: '^', N.BitAnd: '&', N.MatMult: '@',
}
def _BinOp(self, node: N.BinOp) -> str:
op: str = self._BINOP_OPS.get(type(node.op), '?')
return f'{self._expr(node.left)} {op} {self._expr(node.right)}'
def _UnaryOp(self, node: N.UnaryOp) -> str:
ops: dict[type, str] = {N.USub: '-', N.UAdd: '+', N.Not: 'not ', N.Invert: '~'}
op: str = ops.get(type(node.op), '?')
return f'{op}{self._expr(node.operand)}'
def _BoolOp(self, node: N.BoolOp) -> str:
kw: str = 'or' if isinstance(node.op, N.Or) else 'and'
return f' {kw} '.join(self._expr(v) for v in node.values)
_CMPOPS: dict[type, str] = {
N.Eq: '==', N.NotEq: '!=', N.Lt: '<', N.LtE: '<=', N.Gt: '>', N.GtE: '>=',
N.Is: 'is', N.IsNot: 'is not', N.In: 'in', N.NotIn: 'not in',
}
def _Compare(self, node: N.Compare) -> str:
parts: list[str] = [self._expr(node.left)]
for op, comp in zip(node.ops, node.comparators):
parts.append(self._CMPOPS.get(type(op), '?'))
parts.append(self._expr(comp))
return ' '.join(parts)
def _Subscript(self, node: N.Subscript) -> str:
return f'{self._expr(node.value)}[{self._SliceLike(node.slice)}]'
def _SliceLike(self, node: N.AST) -> str:
if isinstance(node, N.Slice):
return self._Slice(node)
if isinstance(node, N.Tuple):
# 切片元组:a[1, 2] / a[i, j:k] 元素按需渲染
return ', '.join(self._SliceLike(e) for e in node.elts)
return self._expr(node)
def _Slice(self, node: N.Slice) -> str:
"""渲染切片:a[1:2] / a[::2] / a[:2] / a[1:2:3]。"""
parts: list[str] = []
if node.lower is not None:
parts.append(self._expr(node.lower))
# 冒号:upper 或 step 存在时才需要第一个冒号
if node.upper is not None or node.step is not None:
if len(parts) < 1:
parts.append('')
parts.append(self._expr(node.upper) if node.upper is not None else '')
if node.step is not None:
if len(parts) < 2:
parts.append('')
parts.append('')
parts.append(self._expr(node.step))
return ':'.join(parts)
def _Tuple(self, node: N.Tuple) -> str:
if len(node.elts) == 1:
return f'({self._expr(node.elts[0])},)'
return '(' + ', '.join(self._expr(e) for e in node.elts) + ')'
def _List(self, node: N.List) -> str:
return '[' + ', '.join(self._expr(e) for e in node.elts) + ']'
def _Set(self, node: N.Set) -> str:
return '{' + ', '.join(self._expr(e) for e in node.elts) + '}'
def _Dict(self, node: N.Dict) -> str:
parts: list[str] = []
for k, v in zip(node.keys, node.values):
if k is None:
parts.append(f'**{self._expr(v)}')
else:
parts.append(f'{self._expr(k)}: {self._expr(v)}')
return '{' + ', '.join(parts) + '}'
def _JoinedStr(self, node: N.JoinedStr) -> str:
out: list[str] = []
for v in node.values:
if isinstance(v, N.Constant):
# 字面量段:转义内部引号与花括号
lit: str = str(v.value)
lit = lit.replace('{', '{{').replace('}', '}}')
lit = lit.replace("'", "\\'")
out.append(lit)
else:
out.append('{' + self._FormattedValueRaw(v) + '}')
inner: str = ''.join(out)
# 优先单引号(与 CPython unparse 一致)
return "f'" + inner + "'"
def _FormattedValue(self, node: N.FormattedValue) -> str:
return "f'{" + self._FormattedValueRaw(node) + "}'"
def _FormattedValueRaw(self, node: N.FormattedValue) -> str:
s: str = self._expr(node.value)
if node.conversion >= 0:
s += f'!{chr(node.conversion)}'
if node.format_spec is not None:
if isinstance(node.format_spec, N.JoinedStr):
spec: str = ''
for v in node.format_spec.values:
if isinstance(v, N.Constant):
spec += str(v.value)
else:
spec += '{' + self._FormattedValueRaw(v) + '}'
else:
spec = self._expr(node.format_spec)
s += f':{spec}'
return s
def _Lambda(self, node: N.Lambda) -> str:
return f'lambda {self._Arguments(node.args)}: {self._expr(node.body)}'
def _IfExp(self, node: N.IfExp) -> str:
return f'{self._expr(node.body)} if {self._expr(node.test)} else {self._expr(node.orelse)}'
def _Starred(self, node: N.Starred) -> str:
return f'*{self._expr(node.value)}'
def _ComprehensionClauses(self, gens: list[N.comprehension]) -> str:
out: list[str] = []
for g in gens:
kw: str = 'async for' if g.is_async else 'for'
s: str = f' {kw} {self._expr(g.target)} in {self._expr(g.iter)}'
for cond in g.ifs:
s += f' if {self._expr(cond)}'
out.append(s)
return ''.join(out)
def _ListComp(self, node: N.AST) -> str:
cls_name: str = type(node).__name__
if isinstance(node, N.SetComp):
fmt: str = '{'
close: str = '}'
elt: str = self._expr(node.elt)
elif isinstance(node, N.GeneratorExp):
fmt, close, elt = '(', ')', self._expr(node.elt)
elif isinstance(node, N.DictComp):
return '{' + f'{self._expr(node.key)}: {self._expr(node.value)}' + self._ComprehensionClauses(node.generators) + '}'
else:
fmt, close, elt = '[', ']', self._expr(node.elt)
return fmt + elt + self._ComprehensionClauses(node.generators) + close
def _DictComp(self, node: N.DictComp) -> str:
return '{' + f'{self._expr(node.key)}: {self._expr(node.value)}' + \
self._ComprehensionClauses(node.generators) + '}'
def _NamedExpr(self, node: N.NamedExpr) -> str:
return f'{self._expr(node.target)} := {self._expr(node.value)}'
def _Await(self, node: N.Await) -> str:
return f'await {self._expr(node.value)}'
def _Yield(self, node: N.Yield) -> str:
return 'yield' if node.value is None else f'yield {self._expr(node.value)}'
def _YieldFrom(self, node: N.YieldFrom) -> str:
return f'yield from {self._expr(node.value)}'
# ------------------------------------------------------------------
# 参数列表
# ------------------------------------------------------------------
def _Arguments(self, args: N.arguments) -> str:
parts: list[str] = []
n_args: int = len(args.posonlyargs) + len(args.args)
n_defaults: int = len(args.defaults)
# 合并 posonly + 普通参数,defaults 尾部对齐
all_args: list[N.arg] = list(args.posonlyargs) + list(args.args)
all_defaults: list[N.expr | None] = [None] * (n_args - n_defaults) + list(args.defaults)
for i, a in enumerate(all_args):
s: str = a.arg
if a.annotation is not None:
s += f': {self._expr(a.annotation)}'
if all_defaults[i] is not None:
s += f'={self._expr(all_defaults[i])}'
parts.append(s)
if args.posonlyargs and i == len(args.posonlyargs) - 1:
parts.append('/')
if args.vararg is not None:
s = f'*{args.vararg.arg}'
if args.vararg.annotation is not None:
s += f': {self._expr(args.vararg.annotation)}'
parts.append(s)
elif args.kwonlyargs:
parts.append('*')
for a, d in zip(args.kwonlyargs, args.kw_defaults):
s = a.arg
if a.annotation is not None:
s += f': {self._expr(a.annotation)}'
if d is not None:
s += f'={self._expr(d)}'
parts.append(s)
if args.kwarg is not None:
s = f'**{args.kwarg.arg}'
if args.kwarg.annotation is not None:
s += f': {self._expr(args.kwarg.annotation)}'
parts.append(s)
return ', '.join(parts)
def Unparse(node: N.AST) -> str:
"""便捷入口。"""
return Unparser().Unparse(node)
+281
View File
@@ -0,0 +1,281 @@
"""vast.utils - AST 遍历与序列化工具
与 CPython ``ast`` 模块辅助函数同构:
- ``iter_fields`` / ``iter_child_nodes`` / ``walk``
- ``dump``(含 annotate_fields / include_attributes / indent)
- ``copy_location`` / ``fix_missing_locations`` / ``get_source_segment``
- ``literal_eval``(常量求值)
"""
from __future__ import annotations
from lib.vast.Nodes import AST
from lib.vast.Tokenizer import Token
def iter_fields(node: AST):
"""按 _fields 顺序产出 (字段名, 值)。"""
for field in node._fields:
try:
yield field, getattr(node, field)
except AttributeError:
pass
def iter_child_nodes(node: AST):
"""产出全部子节点(递归扁平,跳过 None 与空列表)。"""
for _name, field in iter_fields(node):
if isinstance(field, AST):
yield field
elif isinstance(field, list):
for item in field:
if isinstance(item, AST):
yield item
def walk(node: AST):
"""深度优先遍历,产出自身与全部后代节点。"""
from collections import deque
todo: deque[AST] = deque([node])
while todo:
n: AST = todo.popleft()
yield n
todo.extend(iter_child_nodes(n))
def dump(node: AST, annotate_fields: bool = True, include_attributes: bool = False,
indent: int | str | None = None) -> str:
"""将 AST 节点格式化为字符串(精确复刻 CPython 3.12 ast.dump)。
- annotate_fields: 是否显示字段名(默认 True)
- include_attributes: 是否附加 lineno/col_offset 等位置属性
- indent: 缩进(int 或字符串),None 为单行紧凑输出
与 CPython 一致的行为:
- 值为 None 且类默认值为 None 的字段跳过(如 FunctionDef.returns)
- 节点字段全部为"简单值"且 ≤3 个时单行输出
"""
if not isinstance(node, AST):
raise TypeError(f'expected AST, got {type(node).__name__!r}')
if indent is not None and not isinstance(indent, str):
indent = ' ' * indent
def _format(node: object, level: int = 0):
if indent is not None:
level += 1
prefix: str = '\n' + indent * level # type: ignore[operator]
sep: str = ',\n' + indent * level # type: ignore[operator]
else:
prefix = ''
sep = ', '
if isinstance(node, AST):
cls: type = type(node)
args: list[str] = []
allsimple: bool = True
keywords: bool = annotate_fields
for name in node._fields:
try:
value = getattr(node, name)
except AttributeError:
keywords = True
continue
if value is None and getattr(cls, name, ...) is None:
keywords = True
continue
value, simple = _format(value, level)
allsimple = allsimple and simple
if keywords:
args.append(f'{name}={value}')
else:
args.append(value)
if include_attributes and node._attributes:
for name in node._attributes:
try:
value = getattr(node, name)
except AttributeError:
continue
if value is None and getattr(cls, name, ...) is None:
continue
value, simple = _format(value, level)
allsimple = allsimple and simple
args.append(f'{name}={value}')
if allsimple and len(args) <= 3:
return f'{type(node).__name__}({", ".join(args)})', not args
return f'{type(node).__name__}({prefix}{sep.join(args)})', False
elif isinstance(node, list):
if not node:
return '[]', True
return f'[{prefix}{sep.join(_format(x, level)[0] for x in node)}]', False
return repr(node), True
return _format(node)[0]
def copy_location(new_node: AST, old_node: AST) -> AST:
"""复制 old_node 的位置属性到 new_node(与 ast.copy_location 一致)。"""
for attr in ('lineno', 'col_offset', 'end_lineno', 'end_col_offset'):
try:
setattr(new_node, attr, getattr(old_node, attr))
except AttributeError:
pass
return new_node
def fix_missing_locations(node: AST) -> AST:
"""为缺失位置的节点补全(从最近的祖先复制;递归处理)。"""
def _fix(n: AST, parent_loc: dict[str, int] | None) -> None:
has_loc: bool = hasattr(n, 'lineno') and getattr(n, 'lineno', None) is not None
if hasattr(n, 'lineno') and getattr(n, 'lineno', None) is None and parent_loc:
for attr, val in parent_loc.items():
setattr(n, attr, val)
loc: dict[str, int] | None = None
if hasattr(n, 'lineno') and getattr(n, 'lineno', None) is not None:
loc = {a: getattr(n, a) for a in
('lineno', 'col_offset', 'end_lineno', 'end_col_offset')
if getattr(n, a, None) is not None}
for child in iter_child_nodes(n):
_fix(child, loc)
_fix(node, None)
return node
def get_source_segment(source: str, node: AST, padded: bool = False) -> str | None:
"""提取节点对应的源码片段(与 ast.get_source_segment 一致)。"""
try:
lineno: int | None = getattr(node, 'lineno', None)
end_lineno: int | None = getattr(node, 'end_lineno', None)
col_offset: int | None = getattr(node, 'col_offset', None)
end_col_offset: int | None = getattr(node, 'end_col_offset', None)
if lineno is None or end_lineno is None or col_offset is None or end_col_offset is None:
return None
lines: list[str] = source.splitlines(keepends=True)
if lineno < 1 or lineno > len(lines):
return None
first: str = lines[lineno - 1]
# col_offset 是 UTF-8 字节偏移 → 转字符偏移
start_char: int = _ByteToChar(first, col_offset)
if lineno == end_lineno:
end_char: int = _ByteToChar(first, end_col_offset)
segment: str = first[start_char:end_char]
else:
last: str = lines[end_lineno - 1]
end_char = _ByteToChar(last, end_col_offset)
middle: list[str] = lines[lineno:end_lineno - 1]
segment = first[start_char:] + ''.join(middle) + last[:end_char]
if padded:
return segment
return segment
except Exception:
return None
def _ByteToChar(line: str, byte_offset: int) -> int:
"""UTF-8 字节偏移 → 字符偏移。"""
b: bytes = line.encode('utf-8')
return len(b[:byte_offset].decode('utf-8', errors='replace')) if byte_offset > 0 else 0
def literal_eval(node_or_string: AST | str):
"""安全求值常量表达式(与 ast.literal_eval 语义一致)。
支持常量、元组/列表/集合/字典、一元 +/-、BinOp 算术、切片。
"""
if isinstance(node_or_string, str):
from lib.vast.Tokenizer import Tokenizer
toks: list[Token] = Tokenizer(node_or_string).Tokenize()
if toks and toks[0].Type == 'NEWLINE':
raise ValueError('malformed node or string')
from lib.vast.Parser import _ComputeLineStarts
from lib.vast.ExprParser import ExprParserMixin
class _EV(ExprParserMixin):
def __init__(self) -> None:
super().__init__(toks, node_or_string, _ComputeLineStarts(node_or_string))
parser = _EV()
node: AST = parser.ParseExpr()
elif isinstance(node_or_string, AST):
node = node_or_string
else:
raise TypeError('bad argument type')
return _EvalLiteral(node)
def _EvalLiteral(node: AST):
"""递归常量求值核心。"""
if isinstance(node, AST):
pass
name: str = type(node).__name__
# 常量
if name == 'Constant':
value = node.value
if isinstance(value, (int, float, complex, str, bool, bytes, type(None), Ellipsis)): # type: ignore[comparison-overlap]
return value
raise ValueError('malformed node or string: unsupported constant')
if name == 'Tuple':
return tuple(_EvalLiteral(e) for e in node.elts)
if name == 'List':
return [_EvalLiteral(e) for e in node.elts]
if name == 'Set':
return {_EvalLiteral(e) for e in node.elts}
if name == 'Dict':
if len(node.keys) != len(node.values):
raise ValueError('malformed node or string: Dict')
out: dict = {}
for k, v in zip(node.keys, node.values):
if k is None:
raise ValueError('malformed node or string: ** in Dict')
out[_EvalLiteral(k)] = _EvalLiteral(v)
return out
if name == 'UnaryOp':
operand = _EvalLiteral(node.operand)
if isinstance(node.op, (__import__('lib.vast.Nodes', fromlist=['USub']).USub,)):
return -operand
if isinstance(node.op, (__import__('lib.vast.Nodes', fromlist=['UAdd']).UAdd,)):
return +operand
if isinstance(node.op, (__import__('lib.vast.Nodes', fromlist=['Not']).Not,)):
return not operand
raise ValueError('malformed node or string: unary op')
if name == 'BinOp':
from lib.vast.Nodes import Add, Sub, Mult, Div, FloorDiv, Mod, Pow, LShift, RShift, BitOr, BitXor, BitAnd
left = _EvalLiteral(node.left)
right = _EvalLiteral(node.right)
op = node.op
if isinstance(op, Add):
return left + right
if isinstance(op, Sub):
return left - right
if isinstance(op, Mult):
return left * right
if isinstance(op, Div):
return left / right
if isinstance(op, FloorDiv):
return left // right
if isinstance(op, Mod):
return left % right
if isinstance(op, Pow):
return left ** right
if isinstance(op, LShift):
return left << right
if isinstance(op, RShift):
return left >> right
if isinstance(op, BitOr):
return left | right
if isinstance(op, BitXor):
return left ^ right
if isinstance(op, BitAnd):
return left & right
raise ValueError('malformed node or string: bin op')
if name == 'Slice':
lower = _EvalLiteral(node.lower) if node.lower is not None else None
upper = _EvalLiteral(node.upper) if node.upper is not None else None
step = _EvalLiteral(node.step) if node.step is not None else None
return slice(lower, upper, step)
raise ValueError(f'malformed node or string: {name}')
+69
View File
@@ -0,0 +1,69 @@
"""vast - Viper/Python 自研语法解析器(ast 替代品)
vast 提供与 CPython 3.12 标准库 ``ast`` 完全一致的 API:
- 节点类:``AST`` 及全部 131 个节点类(Name/Call/BinOp/FunctionDef/...)
- 解析入口:``parse(source, filename, mode, ...)`` 产出 ``Module``
- 辅助函数:``dump`` / ``walk`` / ``iter_child_nodes`` / ``iter_fields`` /
``unparse`` / ``copy_location`` / ``fix_missing_locations`` /
``get_source_segment`` / ``literal_eval``
- 类型标注占位:``expr`` / ``stmt`` / ``operator`` / ``cmpop`` 等
编译器侧替换方式(零修改其余代码)::
import vast as ast
vast 原生支持 Viper 软关键字 fn/fun/func/function(并入解析器),无需
ExpandSoftKeywords 预处理。
"""
from __future__ import annotations
import sys as _sys
from lib.vast.Nodes import AST
from lib.vast.Nodes import _NODE_DEFS
# 将全部节点类提升到包命名空间
for _name in _NODE_DEFS:
globals()[_name] = getattr(_sys.modules['lib.vast.Nodes'], _name)
from lib.vast.Parser import Parse # noqa: E402
from lib.vast.Utils import ( # noqa: E402
iter_fields, iter_child_nodes, walk, dump, copy_location,
fix_missing_locations, get_source_segment, literal_eval,
)
from lib.vast.Unparser import Unparse as unparse # noqa: E402
from lib.vast.Tokenizer import SOFT_KEYWORDS # noqa: E402
def parse(source: str, filename: str = '<unknown>', mode: str = 'exec', *,
type_comments: bool = False, feature_version: tuple[int, int] | int | None = None) -> AST:
"""解析源码字符串为 AST(与 ast.parse 同签名、同结构)。
Args:
source: 源码文本
filename: 文件名(仅用于错误报告,vast 保留)
mode: 'exec'(模块)/ 'eval'(单表达式)/ 'single'(交互式)
type_comments: 是否解析 # type: 注释(vast 暂返回 None 字段)
feature_version: 目标 Python 版本(vast 忽略,保持兼容)
"""
tree: AST = Parse(source)
if mode == 'exec':
return tree
if mode == 'eval':
if len(tree.body) != 1 or not isinstance(tree.body[0], _sys.modules['lib.vast.Nodes'].Expr):
raise SyntaxError('invalid syntax for eval mode')
expr_node: AST = _sys.modules['lib.vast.Nodes'].Expression(
body=tree.body[0].value)
return expr_node
if mode == 'single':
interactive: AST = _sys.modules['lib.vast.Nodes'].Interactive(body=tree.body)
return interactive
raise ValueError(f"invalid mode {mode!r}")
__all__: list[str] = [
'AST', 'parse', 'dump', 'walk', 'iter_fields', 'iter_child_nodes',
'unparse', 'copy_location', 'fix_missing_locations',
'get_source_segment', 'literal_eval', 'SOFT_KEYWORDS',
] + list(_NODE_DEFS.keys())