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