Files
TransPyC/lib/core/Handles/HandlesExprFormat.py
T
TermiNexus cc2f5d34eb feat(tpc): includes 只认项目配置;ASan 改为显式链接 clang64 运行库
includes 目录不再回退仓库硬编码 TransPyCRoot/includes:Phase2Translator 新增 PROJECT_INCLUDE_DIRS(INCLUDE_DIRS 置空以兼容旧引用),Config.py 从 project.vpj 的 includes 同步;HandlesImports 的模块搜索、TypeAnnotationResolver._builtins_include_dirs 一律只读项目目录。

ASan:mingw64 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,新增 _FindAsanRuntimeDir 定位 clang64 运行库并按 -l: 显式链接(asan_dynamic-x86_64.dll.a + runtime_thunk),定位失败即报错退出。HandlesExprFormat:i32 不再写 'l' 长度修饰符(LP64 下 printf 会按 64 位读实参)。
2026-09-11 22:47:21 +08:00

271 lines
14 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
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.LlvmCodeGenerator import LlvmCodeGenerator
from lib.core.Handles.HandlesBase import BaseHandle
import vast as ast
import re
import llvmlite.ir as ir
class ExprFormatHandle(BaseHandle):
def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value:
Gen: LlvmCodeGenerator = self.Trans.LlvmGen
if not Gen or not Gen.builder:
return ir.Constant(ir.IntType(8).as_pointer(), None)
format_str: str = ""
cast_args: list[Any] = []
for part in Node.values:
if isinstance(part, ast.Constant) and isinstance(part.value, str):
format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0')
elif isinstance(part, ast.FormattedValue):
fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen)
if fmt_class and Gen._has_function(f'{fmt_class}.__str__'):
obj_val: Any = self.HandleExprLlvm(part.value)
if obj_val:
if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8:
if fmt_class in Gen.structs:
obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}")
val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__")
Gen._RegisterTempPtr(val)
format_str += "%s"
cast_args.append(val)
else:
format_str += "%s"
cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None))
continue
val = self.HandleExprLlvm(part.value)
if not val:
format_str += "%d"
cast_args.append(ir.Constant(ir.IntType(32), 0))
continue
# 检查 format_spec(Python f-string 格式说明符)
if part.format_spec is not None:
c_fmt: str | None = self._parse_format_spec(part.format_spec, val)
if c_fmt is not None:
cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value)
format_str += c_fmt
cast_args.append(cast_val)
continue
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, ir.IntType) and pointee.width == 8:
format_str += "%s"
cast_args.append(val)
elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int")
trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc")
format_str += "%d"
cast_args.append(trunc_val)
else:
Loaded: Any = Gen._load(val, name="fstr_deref")
if isinstance(Loaded.type, ir.IntType):
if Loaded.type.width == 8:
format_str += "%c"
ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int")
cast_args.append(ext)
else:
format_str += "%d"
cast_args.append(Loaded)
elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)):
format_str += "%f"
cast_args.append(Loaded)
elif isinstance(Loaded.type, ir.PointerType):
format_str += "%s"
cast_args.append(Loaded)
else:
format_str += "%d"
cast_args.append(Loaded)
elif isinstance(val.type, ir.IntType):
if val.type.width == 8:
format_str += "%c"
ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int")
cast_args.append(ext)
elif val.type.width == 1:
zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int")
format_str += "%d"
cast_args.append(zext)
else:
is_u: bool = Gen._check_node_unsigned(part.value)
format_str += "%u" if is_u else "%d"
cast_args.append(val)
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
format_str += "%f"
cast_args.append(val)
else:
format_str += "%d"
cast_args.append(val)
if not format_str:
return Gen.emit_constant("", 'string')
format_ptr: Any = Gen._create_string_global(format_str)
# 统一使用 snprintf 生成字符串,返回 i8*
# __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放)
in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__'))
snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [
ir.PointerType(ir.IntType(8)),
ir.IntType(64),
ir.PointerType(ir.IntType(8))
], var_arg=True)
snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type)
null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None)
# 第一遍:获取所需长度(NULL 缓冲,size=0)
len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len")
# 加 1 用于 null 终止符
size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size")
size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext")
if in_str_method:
# 堆分配(__str__ 返回值需跨函数生命周期)
malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)])
malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type)
buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf")
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
Gen._RegisterTempPtr(buf_ptr)
return buf_ptr
# 栈分配(局部使用,自动释放)
buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val)
Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf")
return buf_ptr
def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None:
"""将 Python f-string format_spec 转换为 C printf 格式说明符。
支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。
返回 None 表示无法解析,调用方应回退到类型推断。
"""
if isinstance(format_spec_node, ast.JoinedStr):
parts: list[str] = []
for v in format_spec_node.values:
if isinstance(v, ast.Constant) and isinstance(v.value, str):
parts.append(v.value)
else:
return None
spec_str: str = ''.join(parts)
elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str):
spec_str = format_spec_node.value
else:
return None
if not spec_str:
return None
m: Any = re.match(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$', spec_str)
if not m:
return None
fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups()
c_flags: str = ''
if sign == '+':
c_flags += '+'
elif sign == ' ':
c_flags += ' '
if alt:
c_flags += '#'
if fill_align:
align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0]
if align_char in ('<', '^'):
c_flags += '-'
if zero:
c_flags += '0'
c_width: str = width or ''
c_precision: str = precision or ''
length_mod: str = ''
if isinstance(val.type, ir.IntType):
# 32 位**不写**长度修饰符(N14):与 TPV 侧 ExprGenFstr 保持同步。
# 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。
if val.type.width == 64:
length_mod = 'll'
elif val.type.width == 16:
length_mod = 'h'
elif val.type.width == 8:
length_mod = 'hh'
c_specifier: str = ''
if fmt_type in ('d', 'n'):
c_specifier = f'{length_mod}d'
elif fmt_type == 'x':
c_specifier = f'{length_mod}x'
elif fmt_type == 'X':
c_specifier = f'{length_mod}X'
elif fmt_type == 'o':
c_specifier = f'{length_mod}o'
elif fmt_type == 'b':
return None
elif fmt_type == 'c':
c_specifier = 'c'
elif fmt_type in ('f', 'F'):
c_specifier = 'f'
elif fmt_type in ('e', 'E', 'g', 'G'):
c_specifier = fmt_type
elif fmt_type == 's':
c_specifier = 's'
elif fmt_type == '%':
c_specifier = 'f%%'
else:
if isinstance(val.type, ir.IntType):
if val.type.width == 8:
c_specifier = 'c'
elif val.type.width == 1:
c_specifier = 'd'
else:
c_specifier = f'{length_mod}d'
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
c_specifier = 'f'
elif isinstance(val.type, ir.PointerType):
c_specifier = 's'
else:
c_specifier = 'd'
return f'%{c_flags}{c_width}{c_precision}{c_specifier}'
def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value:
"""根据 C printf 格式说明符转换值类型。"""
fmt_type_char: str = ''
for ch in reversed(c_fmt):
if ch.isalpha() and ch not in ('l', 'h'):
fmt_type_char = ch
break
elif ch == '%':
break
if fmt_type_char == 's':
if isinstance(val.type, ir.PointerType):
pointee: ir.Type = val.type.pointee
if isinstance(pointee, ir.IntType) and pointee.width == 8:
return val
if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8:
return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr")
return val
elif fmt_type_char == 'c':
if isinstance(val.type, ir.IntType) and val.type.width < 32:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c")
return val
elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'):
if isinstance(val.type, ir.IntType):
if val.type.width == 8:
is_u: bool = Gen._check_node_unsigned(arg_node)
if is_u:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8")
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8")
elif val.type.width == 16:
is_u = Gen._check_node_unsigned(arg_node)
if is_u:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16")
return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16")
elif val.type.width == 1:
return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool")
return val
elif isinstance(val.type, (ir.FloatType, ir.DoubleType)):
return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i")
return val
elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'):
if isinstance(val.type, ir.FloatType):
return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext")
elif isinstance(val.type, ir.IntType):
return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f")
return val
return val