405 lines
16 KiB
Python
405 lines
16 KiB
Python
"""统一常量表达式求值器
|
||
|
||
整合了原先分散在 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
|