Files
TransPyC/lib/core/Translator/ConstEval.py
2026-07-18 19:25:40 +08:00

306 lines
15 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.
from __future__ import annotations
import ast
from typing import TYPE_CHECKING
import llvmlite.ir as ir
from lib.core.ConstEvaluator import ConstEvaluator
if TYPE_CHECKING:
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
class ConstEvalMixin:
"""常量表达式求值 Mixin
提供数组初始化常量构建与常量表达式求值能力。
"""
@staticmethod
def _bswap_constant_value(val: int, width: int) -> int:
"""编译期字节序交换"""
if width == 16:
return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)
elif width == 32:
return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF)
elif width == 64:
return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF)
return val
def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None:
_zv: ir.Constant | None = Gen._zero_value_for_type(ElemType)
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined)
if not isinstance(value_node, (ast.List, ast.Set)):
return None
if isinstance(ElemType, ir.ArrayType):
constants: list[ir.Constant] = []
for elt in value_node.elts:
if isinstance(elt, (ast.List, ast.Set)):
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen)
if inner_consts:
try:
constants.append(ir.Constant(ElemType, inner_consts))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < ArrayCount:
constants.append(_fallback)
return constants[:ArrayCount]
StructName: str | None = None
if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs:
StructName = elem_type_node.id
constants = []
for elt in value_node.elts:
if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName:
const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Constant):
# 字符串常量:根据引号类型决定 i8 或 i8*
if isinstance(elt.value, str):
const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
else:
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.UnaryOp):
const = self._BuildScalarConstantUnified(elt, ElemType, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr':
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int):
try:
constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord':
if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1:
try:
constants.append(ir.Constant(ElemType, ord(elt.args[0].value)))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
else:
const_val: int | float | str | None = self._try_eval_const_call(elt, Gen)
if const_val is not None:
try:
constants.append(ir.Constant(ElemType, const_val))
except Exception: # fallback to _fallback on constant construction failure
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < ArrayCount:
constants.append(_fallback)
if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64):
swapped: list[ir.Constant] = []
for c in constants:
if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int):
swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width)
swapped.append(ir.Constant(ElemType, swapped_val))
else:
swapped.append(c)
constants = swapped
return constants[:ArrayCount]
def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
"""根据引号类型决定字符串常量的类型:
- 单引号单字符 → i8 (char 值)
- 双引号/单引号多字符/三重引号 → i8* (C 字符串指针)
- 空字符串 → 0 (None),传入 ptr 目标自动隐式转换
"""
if not isinstance(node.value, str):
return None
s: str = node.value
# 空字符串 → 0 (None)
if len(s) == 0:
try:
return ir.Constant(target_type, 0)
except Exception:
return None
# 获取源代码以判断引号类型
quote_char: str | None = None
is_triple: bool = False
col_offset: int = getattr(node, 'col_offset', 0)
node_lineno: int = getattr(node, 'lineno', 0)
# AST 的 lineno 可能不准确(常量节点可能指向下一行),
# 因此在 lineno 和 lineno-1 两行中查找引号
for try_lineno in (node_lineno, node_lineno - 1):
source_line: str | None = Gen._get_source_line(try_lineno)
if source_line and col_offset < len(source_line):
ch: str = source_line[col_offset]
if ch in ("'", '"'):
quote_char = ch
if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'):
is_triple = True
break
# 判断是否为 char (i8):单引号、非三重、单字符
is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1)
if is_char:
# 单引号单字符 → i8
if isinstance(target_type, ir.IntType):
try:
return ir.Constant(target_type, ord(s))
except Exception:
return None
# 目标类型不是整数 → 类型不匹配
return None
else:
# 双引号/多字符/三重 → i8* (C 字符串)
if isinstance(target_type, ir.PointerType):
str_val: str = s + '\x00'
str_bytes: bytes = str_val.encode('utf-8')
arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes))
gv_name: str = f"str_const_{id(node)}"
gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name)
gv.initializer = ir.Constant(arr_type, bytearray(str_bytes))
gv.linkage = 'internal'
return ir.Constant(target_type, gv.get_reference())
# 目标类型不是指针 → 类型不匹配
return None
def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None:
"""统一的标量常量构建方法。
合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性:
- 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False)
- 字符串处理委托给 _BuildStringLiteralConstant
- 显式拒绝 BaseStructType
- 全部 try/except 保护
"""
# 显式拒绝结构体类型
if isinstance(target_type, ir.BaseStructType):
return None
# 处理常量节点
if isinstance(node, ast.Constant):
# bool 必须在 int 之前判断bool 是 int 的子类)
if isinstance(node.value, bool):
try:
return ir.Constant(target_type, 1 if node.value else 0)
except Exception:
return None
elif isinstance(node.value, int):
try:
return ir.Constant(target_type, node.value)
except Exception:
return None
elif isinstance(node.value, float):
try:
return ir.Constant(target_type, node.value)
except Exception:
return None
elif isinstance(node.value, str):
# 字符串处理委托给 _BuildStringLiteralConstant
const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen)
if const is not None:
return const
# 回退直接创建字符串全局变量i8*
try:
return Gen._create_string_global(node.value)
except Exception:
return None
# 处理一元负号
elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int):
try:
return ir.Constant(target_type, -node.operand.value)
except Exception:
return None
# 处理 True/False 名称节点
elif isinstance(node, ast.Name):
if node.id == 'True':
try:
return ir.Constant(target_type, 1)
except Exception:
return None
elif node.id == 'False':
try:
if isinstance(target_type, ir.PointerType):
return ir.Constant(target_type, None)
return ir.Constant(target_type, 0)
except Exception:
return None
return None
def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None:
if isinstance(node, ast.Call):
func_name: str | None = None
if isinstance(node.func, ast.Name):
func_name = node.func.id
elif isinstance(node.func, ast.Attribute):
func_name = node.func.attr
if func_name:
arg_vals: list[int | float | str | None] = []
for arg in node.args:
v: int | float | str | None = self._try_eval_const_expr(arg, Gen)
if v is None:
return None
arg_vals.append(v)
if func_name == 'COLOR_RGB' and len(arg_vals) == 3:
r: int | float | str | None
g: int | float | str | None
b: int | float | str | None
r, g, b = arg_vals
return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r)
if func_name in Gen._DefineConstants:
return Gen._DefineConstants[func_name]
for key in Gen.functions:
if key.endswith(f'__{func_name}'):
return None
return None
return self._try_eval_const_expr(node, Gen)
def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None:
return ConstEvaluator.eval_with_symtab(node, self.SymbolTable)
def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None:
inner_type: ir.Type = ArrayType.element
count: int = ArrayType.count
elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else []
_zv: ir.Constant | None = Gen._zero_value_for_type(inner_type)
_fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined)
constants: list[ir.Constant] = []
for elt in elts:
if isinstance(inner_type, ir.ArrayType):
if isinstance(elt, (ast.List, ast.Set)):
inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen)
if inner_consts:
constants.append(ir.Constant(inner_type, inner_consts))
else:
constants.append(_fallback)
else:
constants.append(_fallback)
elif isinstance(elt, ast.Constant):
const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
elif isinstance(elt, ast.UnaryOp):
const = self._BuildScalarConstantUnified(elt, inner_type, Gen)
if const:
constants.append(const)
else:
constants.append(_fallback)
else:
constants.append(_fallback)
while len(constants) < count:
constants.append(_fallback)
return constants[:count]