Files
TransPyC/lib/core/ConstEvaluator.py

405 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""统一常量表达式求值器
整合了原先分散在 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, Optional
class ConstEvaluator:
"""统一常量表达式求值器"""
# ==================================================================
# Level 1: 纯 AST 常量 + 算术(无外部依赖)
# ==================================================================
@staticmethod
def eval_basic(node: ast.AST) -> Optional[Any]:
"""求值纯常量表达式,不依赖任何外部符号表。
支持: Constant, BinOp, UnaryOp
"""
if isinstance(node, ast.Constant):
return node.value
return ConstEvaluator._eval_arith(node, ConstEvaluator.eval_basic)
# ==================================================================
# Level 2: + SymbolTable define 查找
# ==================================================================
@staticmethod
def eval_with_symtab(node: ast.AST, symtab) -> 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 = symtab[node.id]
if info.IsDefine and isinstance(info.DefineValue, int):
return info.DefineValue
return None
if isinstance(node, ast.Attribute):
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 = 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 = 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 = ConstEvaluator.eval_full(node.left, ctx)
if left is None:
return None
for op, comparator in zip(node.ops, node.comparators):
right = ConstEvaluator.eval_full(comparator, ctx)
if right is None:
return None
result = 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):
return ctx.lookup_attribute(node)
# UnaryOp
if isinstance(node, ast.UnaryOp):
operand = 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
# BinOp
return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_full(n, ctx))
# ==================================================================
# 内部辅助
# ==================================================================
@staticmethod
def _eval_arith(node: ast.AST, recurse) -> Optional[Any]:
"""通用算术求值BinOp + UnaryOp"""
if isinstance(node, ast.BinOp):
left = recurse(node.left)
right = 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 = 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, right) -> 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, left, right) -> 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_attribute_define(node: ast.Attribute, symtab) -> Optional[Any]:
"""从 SymbolTable 查找 Attribute 形式的 define 值"""
parts = []
current = 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 = parts[-1]
possible_keys = [attr_name, '.'.join(parts)]
for key in possible_keys:
if hasattr(symtab, '__contains__') and key in symtab:
info = symtab[key]
if info.IsDefine and isinstance(info.DefineValue, int):
return info.DefineValue
# 模糊匹配
if len(parts) >= 2:
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):
"""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=None, symtab=None, platform_macros: dict | None = None):
self.Gen = Gen
self.symtab = symtab
self._platform_macros = platform_macros
def lookup_name(self, name: str) -> Optional[Any]:
"""查找 Name 节点的值define_constants → SymbolTable → 平台宏"""
# 1. Gen._define_constants
if self.Gen and hasattr(self.Gen, '_define_constants') and name in self.Gen._define_constants:
val = 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 = self.Gen._DefineConstants[name]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
# 3. SymbolTable
if self.symtab and name in self.symtab:
info = self.symtab[name]
val = 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 = 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 = node.value.id if isinstance(node.value, ast.Name) else None
attr_name = node.attr
if module_name and attr_name:
combined_key = f"{module_name}.{attr_name}"
# 1. Gen._define_constants
if self.Gen and hasattr(self.Gen, '_define_constants') and combined_key in self.Gen._define_constants:
val = self.Gen._define_constants[combined_key]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
# 2. SymbolTable (精确匹配 + 后缀匹配)
if self.symtab:
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 = self._get_all_define_constants()
if combined_key in all_dc:
val = all_dc[combined_key]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
if attr_name in all_dc:
val = all_dc[attr_name]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
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 = 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 hasattr(self.Gen, '_define_constants') and attr_name in self.Gen._define_constants:
val = self.Gen._define_constants[attr_name]
if isinstance(val, (int, float)):
return ConstEvaluator._intify(val)
# 6. 平台宏
macros = self._get_platform_macros()
full_key = self._get_attr_full_name(node)
if full_key:
if full_key in macros:
return macros[full_key]
short_name = 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 = {}
pi = getattr(self.Gen, '_platform_info', {})
ptr_size = getattr(self.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
@staticmethod
def _get_attr_full_name(node: ast.Attribute) -> Optional[str]:
"""从 Attribute 节点提取完整名称 (e.g. 'module.name')"""
parts = []
current = 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