修复了 TPV 的一些错误,包括闭包等
This commit is contained in:
271
App/lib/core/Handles/HandlesExprFormat.py
Normal file
271
App/lib/core/Handles/HandlesExprFormat.py
Normal file
@@ -0,0 +1,271 @@
|
||||
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_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):
|
||||
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
|
||||
Reference in New Issue
Block a user