snapshot before regression test

This commit is contained in:
t
2026-07-18 19:25:40 +08:00
commit 796222a300
2295 changed files with 206453 additions and 0 deletions

515
lib/core/ConstEvaluator.py Normal file
View File

@@ -0,0 +1,515 @@
"""统一常量表达式求值器
整合了原先分散在 5 处的常量求值逻辑:
- SymbolTable._eval_const_expr
- CTypeInfo.TryEvalConstExpr
- ConstEvalMixin._try_eval_const_expr
- HandlesAssign._eval_const_expr
- HandlesIf._eval_const_expr
ConstEvaluator 提供三个级别的求值:
1. eval_basic(node) — 纯 AST 常量 + 算术(无外部依赖)
2. eval_with_symtab(node) — + SymbolTable 中的 define 查找
3. eval_full(node, ctx) — + Gen._define_constants / _all_define_constants / 平台宏
"""
from __future__ import annotations
import ast
from typing import Any, Callable, Optional, TYPE_CHECKING
import llvmlite.ir as ir
from lib.core.Handles.HandlesBase import CTypeInfo
if TYPE_CHECKING:
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
class ConstEvaluator:
"""统一常量表达式求值器"""
# ==================================================================
# Level 2: + SymbolTable define 查找
# ==================================================================
@staticmethod
def eval_with_symtab(node: ast.AST, symtab: Any) -> Optional[Any]:
"""求值常量表达式,支持从 SymbolTable 查找 define 值。
支持: Constant, BinOp, UnaryOp, Name(define), Attribute(define)
"""
if isinstance(node, ast.Constant):
return node.value
if isinstance(node, ast.Name):
if hasattr(symtab, '__contains__') and node.id in symtab:
info: CTypeInfo = symtab[node.id]
if info.IsDefine and isinstance(info.DefineValue, int):
return info.DefineValue
return None
if isinstance(node, ast.Attribute):
# 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性)
size_val = ConstEvaluator._eval_ctype_size_attr(node)
if size_val is not None:
return size_val
return ConstEvaluator._eval_attribute_define(node, symtab)
return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_with_symtab(n, symtab))
# ==================================================================
# Level 3: + Gen._define_constants / _all_define_constants / 平台宏
# ==================================================================
@staticmethod
def eval_full(node: ast.AST, ctx: 'EvalContext') -> Optional[Any]:
"""完整求值,支持 define_constants、平台宏、BoolOp、Compare。
ctx: EvalContext 实例,封装了 Gen、SymbolTable 等上下文。
"""
# BoolOp (HandlesIf 逻辑)
if isinstance(node, ast.BoolOp):
if isinstance(node.op, ast.And):
for val in node.values:
result: Any = ConstEvaluator.eval_full(val, ctx)
if result is None:
return None
if not result:
return 0
return 1
elif isinstance(node.op, ast.Or):
for val in node.values:
result: Any = ConstEvaluator.eval_full(val, ctx)
if result is None:
return None
if result:
return 1
return 0
return None
# Compare (HandlesIf 逻辑)
if isinstance(node, ast.Compare):
left: Any = ConstEvaluator.eval_full(node.left, ctx)
if left is None:
return None
op: ast.cmpop
comparator: ast.AST
for op, comparator in zip(node.ops, node.comparators):
right: Any = ConstEvaluator.eval_full(comparator, ctx)
if right is None:
return None
result: bool | None = ConstEvaluator._eval_compare_op(op, left, right)
if result is None:
return None
if not result:
return 0
left = right
return 1
# Constant — 直接返回原始值bool→0/1 转换由 HandlesIf 自行处理
if isinstance(node, ast.Constant):
return node.value
# Name — 查 define_constants / SymbolTable / 平台宏
if isinstance(node, ast.Name):
return ctx.lookup_name(node.id)
# Attribute — 查 define_constants / SymbolTable / 平台宏
if isinstance(node, ast.Attribute):
# 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性)
size_val = ConstEvaluator._eval_ctype_size_attr(node)
if size_val is not None:
return size_val
return ctx.lookup_attribute(node)
# UnaryOp
if isinstance(node, ast.UnaryOp):
operand: Any = ConstEvaluator.eval_full(node.operand, ctx)
if operand is None:
return None
if isinstance(node.op, ast.Not):
return 1 if not operand else 0
if isinstance(node.op, ast.USub):
return -operand
if isinstance(node.op, ast.UAdd):
return +operand
if isinstance(node.op, ast.Invert):
return ~operand
return None
# Call — 编译时函数调用(如 ctraits.isptr(x)
if isinstance(node, ast.Call):
return ConstEvaluator._eval_compile_time_call(node, ctx)
# BinOp
return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_full(n, ctx))
# ==================================================================
# 内部辅助
# ==================================================================
@staticmethod
def _eval_compile_time_call(node: ast.Call, ctx: 'EvalContext') -> Optional[Any]:
"""处理编译时函数调用,如 ctraits.isptr(x)"""
func: ast.expr = node.func
func_name: str | None = None
module_name: str | None = None
if isinstance(func, ast.Attribute):
if isinstance(func.value, ast.Name):
module_name = func.value.id
func_name = func.attr
elif isinstance(func, ast.Name):
func_name = func.id
# ctraits.isptr(x) — 带模块前缀
# isptr(x) — 通过 from ctraits import isptr 导入
is_isptr: bool = False
if module_name == 'ctraits' and func_name == 'isptr':
is_isptr = True
elif func_name == 'isptr' and not module_name:
# 检查是否通过 from ctraits import isptr 导入
t_c_imported = getattr(ctx.translator, '_t_c_imported_names', {}) if ctx.translator else {}
if func_name in t_c_imported:
src_module, _ = t_c_imported[func_name]
if src_module == 'ctraits':
is_isptr = True
# 也检查符号表中是否有 ctraits.isptr
if not is_isptr and ctx.symtab:
sym = ctx.symtab.lookup('ctraits.isptr') if hasattr(ctx.symtab, 'lookup') else None
if sym:
is_isptr = True
if is_isptr and node.args and ctx.translator:
try:
arg_node: ast.expr = node.args[0]
# 局部变量:优先通过 LLVM 类型判断GetCTypeInfo 无法解析局部变量名)
if ctx.Gen and isinstance(arg_node, ast.Name):
var_name: str = arg_node.id
variables: dict = getattr(ctx.Gen, 'variables', {})
if var_name in variables:
var_val: Any = variables[var_name]
if hasattr(var_val, 'type') and isinstance(var_val.type, ir.PointerType):
pointee: ir.Type = var_val.type.pointee
if isinstance(pointee, ir.PointerType):
return 1
return 0
# 类型名 / typedef通过 TypeMergeHandler 获取 CTypeInfo
type_info = ctx.translator.TypeMergeHandler.GetCTypeInfo(arg_node)
if type_info and (type_info.IsPtr or type_info.PtrCount > 0):
return 1
if type_info and not (type_info.IsPtr or type_info.PtrCount > 0):
return 0
return None
except Exception:
return None
return None
@staticmethod
def _eval_arith(node: ast.AST, recurse: Callable[[ast.AST], Any]) -> Optional[Any]:
"""通用算术求值BinOp + UnaryOp"""
if isinstance(node, ast.BinOp):
left: Any = recurse(node.left)
right: Any = recurse(node.right)
if left is None or right is None:
return None
return ConstEvaluator._apply_binop(node.op, left, right)
if isinstance(node, ast.UnaryOp):
operand: Any = recurse(node.operand)
if operand is None:
return None
if isinstance(node.op, ast.USub):
return -operand
if isinstance(node.op, ast.UAdd):
return +operand
if isinstance(node.op, ast.Invert):
return ~operand
return None
return None
@staticmethod
def _apply_binop(op: ast.operator, left: Any, right: Any) -> Optional[Any]:
"""应用二元运算符"""
try:
if isinstance(op, ast.Add):
return left + right
elif isinstance(op, ast.Sub):
return left - right
elif isinstance(op, ast.Mult):
return left * right
elif isinstance(op, ast.Div):
return left // right if isinstance(left, int) and isinstance(right, int) else left / right
elif isinstance(op, ast.FloorDiv):
if isinstance(right, (int, float)) and right == 0:
return None
return left // right
elif isinstance(op, ast.Mod):
if isinstance(right, (int, float)) and right == 0:
return None
return left % right
elif isinstance(op, ast.Pow):
return left ** right
elif isinstance(op, ast.LShift):
return left << right
elif isinstance(op, ast.RShift):
return left >> right
elif isinstance(op, ast.BitOr):
return left | right
elif isinstance(op, ast.BitXor):
return left ^ right
elif isinstance(op, ast.BitAnd):
return left & right
except Exception:
return None
return None
@staticmethod
def _eval_compare_op(op: ast.cmpop, left: Any, right: Any) -> Optional[bool]:
"""应用比较运算符"""
if isinstance(op, ast.Eq):
return left == right
elif isinstance(op, ast.NotEq):
return left != right
elif isinstance(op, ast.Lt):
return left < right
elif isinstance(op, ast.LtE):
return left <= right
elif isinstance(op, ast.Gt):
return left > right
elif isinstance(op, ast.GtE):
return left >= right
return None
@staticmethod
def _eval_ctype_size_attr(node: ast.Attribute) -> Optional[int]:
"""求值 t.CSizeT().Size 等类型构造调用 + .Size 属性模式。
匹配 AST 形态: Attribute(attr='Size', value=Call(func=Attribute(value=Name(id='t'), attr='CSizeT')))
或直接 Call(func=Name(id='CSizeT'))from t import CSizeT 后的使用方式)。
返回类型大小(位数),如 CSizeT().Size 返回 6464 位平台)。
"""
if node.attr != 'Size':
return None
if not isinstance(node.value, ast.Call):
return None
call_node: ast.Call = node.value
if call_node.args or call_node.keywords:
return None
func: ast.expr = call_node.func
type_name: str | None = None
if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name) and func.value.id == 't':
type_name = func.attr
elif isinstance(func, ast.Name):
type_name = func.id
if not type_name:
return None
# 延迟导入避免循环依赖
try:
from lib.includes.t import CTypeRegistry
except Exception:
return None
cls = CTypeRegistry.GetClassByName(type_name)
if cls is None:
return None
try:
instance = cls()
size_val = getattr(instance, 'Size', None)
if isinstance(size_val, int):
return size_val
except Exception:
pass
return None
@staticmethod
def _eval_attribute_define(node: ast.Attribute, symtab: Any) -> Optional[Any]:
"""从 SymbolTable 查找 Attribute 形式的 define 值"""
parts: list[str] = []
current: ast.AST = node
while isinstance(current, ast.Attribute):
parts.append(current.attr)
current = current.value
if isinstance(current, ast.Name):
parts.append(current.id)
parts.reverse()
attr_name: str = parts[-1]
possible_keys: list[str] = [attr_name, '.'.join(parts)]
for key in possible_keys:
if hasattr(symtab, '__contains__') and key in symtab:
info: CTypeInfo = symtab[key]
if info.IsDefine and isinstance(info.DefineValue, int):
return info.DefineValue
# 模糊匹配
if len(parts) >= 2:
mod_key: str
mod_info: CTypeInfo
for mod_key, mod_info in (symtab.items() if hasattr(symtab, 'items') else []):
if mod_info.IsDefine and isinstance(mod_info.DefineValue, int):
if mod_key.endswith('.' + attr_name) or mod_key == attr_name:
return mod_info.DefineValue
return None
@staticmethod
def _intify(val: Any) -> Any:
"""float → int 转换(仅当 float 值恰好是整数时)"""
if isinstance(val, float) and val == int(val):
return int(val)
return val
class EvalContext:
"""常量求值上下文,封装 Gen / SymbolTable / 平台宏查找逻辑。
由调用方构造并传入 ConstEvaluator.eval_full()。
"""
def __init__(self, Gen: LlvmCodeGenerator | None = None, symtab: Any = None, platform_macros: dict | None = None, translator: Any = None):
self.Gen: LlvmCodeGenerator | None = Gen
self.symtab: Any = symtab
self._platform_macros: dict | None = platform_macros
self.translator: Any = translator
def lookup_name(self, name: str) -> Optional[Any]:
"""查找 Name 节点的值define_constants → SymbolTable → 平台宏"""
# 1. Gen._define_constants
if self.Gen and name in self.Gen._define_constants:
val: Any = self.Gen._define_constants[name]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
# 2. Gen._DefineConstants (大写版本)
if self.Gen and hasattr(self.Gen, '_DefineConstants') and name in self.Gen._DefineConstants:
val: Any = self.Gen._DefineConstants[name]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
# 3. SymbolTable
if self.symtab and name in self.symtab:
info: CTypeInfo = self.symtab[name]
val: Any = info.value
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
if info.IsDefine and isinstance(info.DefineValue, (int, float)):
return ConstEvaluator._intify(info.DefineValue)
# 4. 平台宏
macros: dict = self._get_platform_macros()
if name in macros:
return macros[name]
return None
def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]:
"""查找 Attribute 节点的值"""
module_name: str | None = node.value.id if isinstance(node.value, ast.Name) else None
attr_name: str = node.attr
if module_name and attr_name:
combined_key: str = f"{module_name}.{attr_name}"
# 1. Gen._define_constants
if self.Gen and combined_key in self.Gen._define_constants:
val: Any = self.Gen._define_constants[combined_key]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
# 2. SymbolTable (精确匹配 + 后缀匹配)
if self.symtab:
key: str
info: CTypeInfo
for key, info in self.symtab.items():
if key.endswith(f".{combined_key}") or key == combined_key:
if info.IsDefine and isinstance(info.DefineValue, (int, float)):
return ConstEvaluator._intify(info.DefineValue)
# 3. _all_define_constants
all_dc: dict = self._get_all_define_constants()
if combined_key in all_dc:
val: Any = all_dc[combined_key]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
if attr_name in all_dc:
val: Any = all_dc[attr_name]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
dc_key: str
dc_val: Any
for dc_key, dc_val in all_dc.items():
if dc_key == attr_name or dc_key.endswith(f".{attr_name}"):
if isinstance(dc_val, (int, float)):
return ConstEvaluator._intify(dc_val)
# 4. SymbolTable 短名查找
if self.symtab and attr_name in self.symtab:
info: CTypeInfo = self.symtab[attr_name]
if info.IsDefine and isinstance(info.DefineValue, (int, float)):
return ConstEvaluator._intify(info.DefineValue)
# 5. Gen._define_constants 短名
if self.Gen and attr_name in self.Gen._define_constants:
val: Any = self.Gen._define_constants[attr_name]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
# 6. 平台宏
macros: dict = self._get_platform_macros()
full_key: str | None = self._get_attr_full_name(node)
if full_key:
if full_key in macros:
return macros[full_key]
short_name: str = full_key.split('.')[-1] if '.' in full_key else full_key
if short_name in macros:
return macros[short_name]
return None
def _get_all_define_constants(self) -> dict:
"""获取 _all_define_constants"""
if self.Gen and hasattr(self.Gen, '_all_define_constants'):
return self.Gen._all_define_constants
return {}
def _get_platform_macros(self) -> dict:
"""获取平台宏"""
if self._platform_macros is not None:
return self._platform_macros
if not self.Gen:
return {}
macros: dict[str, int] = {}
pi: dict = self.Gen._platform_info
ptr_size: int = self.Gen.ptr_size
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
@staticmethod
def _get_attr_full_name(node: ast.Attribute) -> Optional[str]:
"""从 Attribute 节点提取完整名称 (e.g. 'module.name')"""
parts: list[str] = []
current: ast.AST = node
while isinstance(current, ast.Attribute):
parts.append(current.attr)
current = current.value
if isinstance(current, ast.Name):
parts.append(current.id)
parts.reverse()
return '.'.join(parts) if parts else None