Files
TransPyV/App/lib/core/Handles/HandlesExprFormat.py

271 lines
13 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 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_specPython 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):
if val.type.width == 64:
length_mod = 'll'
elif val.type.width == 32:
length_mod = 'l'
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