snapshot before regression test
This commit is contained in:
515
lib/core/ConstEvaluator.py
Normal file
515
lib/core/ConstEvaluator.py
Normal 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 返回 64(64 位平台)。
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user