Files
ViperC/App/lib/core/Gen/ExprGenFstr.vp
T
TermiNexus 89995dffde refactor(gen): 去除 Handles 委托层——链式化 + GenTypes/GenApi 迁移
- Gen 内部 1742 处 HandlesX.func() 改 self.Gen.Y.method() 链式调用
- 数据类/常量迁移 GenTypes.vp(10 类 + CLASS_META_*/META_*/SEQ_KIND_*/FIELD_MAX 等)
- 管线/保留文件经 GenApi.vp 模块级入口(48 函数带类型注解,从 Gen 方法签名自动生成)
- 删除 26 个纯委托 Handles 文件,仅留 5 个实体文件(Base/Translator/Expr/ExprCall/ExprCallTable)
- 修复字段分裂:TypeGenErr CurrentFile/TraceCount 链式化;CallGenBase TempDir/Cexport 真源化
2026-08-21 06:32:40 +08:00

502 lines
22 KiB
Plaintext
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.
import t, c
from stdint import *
import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import viperlib
import w32.fileio as fileio
import lib.core.CompilerFactory as CompilerFactory
import lib.core.Handles.HandlesBase as HandlesBase
import lib.core.Handles.HandlesTranslator as HT
import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.core.StubMerger as StubMerger
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
import lib.core.Gen.ExprGenBase as ExprGenBase
# ============================================================
# f-string 翻译常量(与 ExprGenMixin 保持一致,本地副本避免跨模块
# CDefine 查找 bug:ExprGenFstr(F) 在 ExprGenMixin(M) 之前按文件序
# 编译时,跨模块 CDefine 全局表未注册 → NameError 未定义)
# ============================================================
FSTR_BUF_SIZE: t.CDefine = 1024
FSTR_SPEC_MAX: t.CDefine = 64
FSTR_FMT_MAX: t.CDefine = 64
# ============================================================
# ExprGenFstr - ExprGenMixin 拆分(阶段 D):f-string/格式化值翻译
#
# 继承 ExprGenBase(1 级,TPV self 调用仅支持 1 级父类)。
# 跨 Mixin 调用统一走 HandlesExpr 委托(经 _g_gen)。
# @t.NoVTable:静态绑定。
# ============================================================
@t.NoVTable
class ExprGenFstr(ExprGenBase.ExprGenBase):
"""f-string/格式化值翻译"""
# ============================================================
# translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm)
#
# f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8*
# 栈缓冲。之前 f-string 未翻译导致 print/赋值拿到 NULL 指针
# ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。
# ============================================================
def translate_joined_str(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""f-string → snprintf 生成 i8* 栈缓冲"""
if node is None or builder is None or trans is None:
return None
js: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(node)
if js is None or js.values is None:
return None
# 编译期格式串缓冲(pool 分配)
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
fmt_buf[0] = '\0'
# 运行时参数链表(Value.Next 链接,与 printf 调用一致)
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_count: int = 0
parts: list[ast.AST | t.CPtr] | t.CPtr = js.values
pn: t.CSizeT = len(parts)
for pi in range(pn):
part: ast.AST | t.CPtr = parts.get(pi)
if part is None:
continue
if part.kind() == ast.ASTKind.Constant:
pcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(part)
if pcn.const_kind == ast.CONST_STR and pcn.str_val is not None:
self._fstr_append_literal(fmt_buf, pcn.str_val)
continue
if part.kind() != ast.ASTKind.FormattedValue:
continue
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(part)
if fv.value is None:
continue
val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
continue
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
# 追加格式说明符
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
# 转换参数类型(i8/i16→i32、bool→i32、f32→f64)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
if arg_head is None:
arg_head = cast_val
else:
arg_tail.Next = cast_val
arg_tail = cast_val
arg_count += 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def translate_formatted_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr = None) -> llvmlite.Value | t.CPtr:
"""翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)"""
if node is None or builder is None or trans is None:
return None
fv: ast.FormattedValue | t.CPtr = (ast.FormattedValue | t.CPtr)(node)
if fv is None or fv.value is None:
return None
fmt_buf: str = pool.alloc(FSTR_BUF_SIZE)
if fmt_buf is None:
return None
fmt_buf[0] = '\0'
arg_head: llvmlite.Value | t.CPtr = None
arg_tail: llvmlite.Value | t.CPtr = None
arg_count: int = 0
val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, fv.value, None, 0, trans)
if val is None:
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(
fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
else:
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
if c_fmt is None:
c_fmt = self._fstr_infer_c_fmt(pool, val, fv.value, trans)
if c_fmt is None:
# fail-fast:f-string 格式无法推断(值类型缺失),立即终止编译
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式无法推断(值类型缺失)", "TypeError")
if string.strlen(fmt_buf) + string.strlen(c_fmt) < FSTR_BUF_SIZE - 1:
string.strcat(fmt_buf, c_fmt)
cast_val: llvmlite.Value | t.CPtr = self._fstr_cast_value(
builder, pool, val, c_fmt, fv.value, trans)
if cast_val is None:
cast_val = val
cast_val.Next = None
arg_head = cast_val
arg_tail = cast_val
arg_count = 1
return self._fstr_finalize(builder, pool, mod, fmt_buf, arg_head, arg_tail, arg_count, trans)
def _fstr_finalize(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
mod: llvmlite.LLVMModule | t.CPtr,
fmt_buf: str,
arg_head: llvmlite.Value | t.CPtr,
arg_tail: llvmlite.Value | t.CPtr,
arg_count: int,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""f-string 尾部:全局格式串 + 固定栈缓冲 + snprintf,返回 i8*"""
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool)
if fmt_buf is None or fmt_buf[0] == '\0':
# 空 f-string(f"")→ 空字符串常量
return self.Gen.ExprGenConst.create_global_string(builder, pool, mod, "", trans)
# 格式串 → 全局字符串常量(i8*)
fmt_ptr: llvmlite.Value | t.CPtr = self.Gen.ExprGenConst.create_global_string(
builder, pool, mod, fmt_buf, trans)
if fmt_ptr is None:
return None
# 固定栈缓冲 [FSTR_BUF_SIZE x i8],GEP(0,0) 取 i8*
buf_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE)
buf_alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, buf_ty)
if buf_alloca is None:
return None
zero_i32: llvmlite.Value | t.CPtr = llvmlite.const_int32(pool, 0)
buf_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_array(
builder, buf_ty, i8_ty, buf_alloca, zero_i32)
if buf_ptr is None:
return None
# snprintf(buf, FSTR_BUF_SIZE, fmt, args...)
size_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, FSTR_BUF_SIZE)
buf_ptr.Next = size_val
size_val.Next = fmt_ptr
fmt_ptr.Next = arg_head
if arg_tail is not None:
arg_tail.Next = None
llvmlite.build_call(builder, "snprintf", buf_ptr, 3 + arg_count, i32_ty, 1)
return buf_ptr
def _fstr_append_literal(self, fmt_buf: str,
lit: str) -> None:
"""追加字面量到格式串(% 转义为 %%)"""
if fmt_buf is None or lit is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
rem: t.CSizeT = FSTR_BUF_SIZE - 1 - cur_len
if rem == 0:
return
src_len: t.CSizeT = string.strlen(lit)
si: t.CSizeT = 0
while si < src_len and rem > 0:
ch: t.CChar = lit[si]
if ch == '%':
if rem < 2:
break
fmt_buf[cur_len] = '%'
fmt_buf[cur_len + 1] = '%'
cur_len += 2
rem -= 2
else:
fmt_buf[cur_len] = ch
cur_len += 1
rem -= 1
si += 1
fmt_buf[cur_len] = '\0'
def _fstr_append_raw(self, fmt_buf: str, s: str) -> None:
"""追加原始字符串(不做 % 转义)"""
if fmt_buf is None or s is None:
return
cur_len: t.CSizeT = string.strlen(fmt_buf)
src_len: t.CSizeT = string.strlen(s)
if cur_len + src_len < FSTR_BUF_SIZE:
string.strcpy(fmt_buf + cur_len, s)
def _fstr_parse_format_spec(self, pool: memhub.MemBuddy | t.CPtr,
spec_node: ast.AST | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr) -> str:
"""Python format_spec → C printf 格式串(pool 分配),无法解析返回 None"""
if spec_node is None or pool is None or val is None:
return None
# 1. 提取 spec 字符串(JoinedStr 仅常量拼接)
spec: str = pool.alloc(FSTR_SPEC_MAX)
if spec is None:
return None
spec[0] = '\0'
sk: int = spec_node.kind()
if sk == ast.ASTKind.JoinedStr:
jss: ast.JoinedStr | t.CPtr = (ast.JoinedStr | t.CPtr)(spec_node)
if jss.values is None:
return None
vn: t.CSizeT = len(jss.values)
for vi in range(vn):
vpart: ast.AST | t.CPtr = jss.values.get(vi)
if vpart is None:
continue
if vpart.kind() != ast.ASTKind.Constant:
return None
vcn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(vpart)
if vcn.const_kind != ast.CONST_STR or vcn.str_val is None:
return None
if string.strlen(spec) + string.strlen(vcn.str_val) < FSTR_SPEC_MAX - 1:
string.strcat(spec, vcn.str_val)
elif sk == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(spec_node)
if cn.const_kind != ast.CONST_STR or cn.str_val is None:
return None
string.strncpy(spec, cn.str_val, FSTR_SPEC_MAX - 1)
else:
return None
slen: t.CSizeT = string.strlen(spec)
if slen == 0:
return None
# 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type
pos: t.CSizeT = 0
align_char: t.CChar = 0
if pos < slen:
ch0: t.CChar = spec[pos]
if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=':
align_char = ch0
pos += 1
elif pos + 1 < slen:
ch1: t.CChar = spec[pos + 1]
if ch1 == '<' or ch1 == '>' or ch1 == '^' or ch1 == '=':
align_char = ch1
pos += 2
c_flags: str = pool.alloc(FSTR_FMT_MAX)
c_width: str = pool.alloc(32)
c_prec: str = pool.alloc(32)
if c_flags is None or c_width is None or c_prec is None:
return None
c_flags[0] = '\0'
c_width[0] = '\0'
c_prec[0] = '\0'
# sign
if pos < slen and (spec[pos] == '+' or spec[pos] == '-' or spec[pos] == ' '):
if spec[pos] == '+':
string.strcat(c_flags, "+")
elif spec[pos] == ' ':
string.strcat(c_flags, " ")
pos += 1
# alt '#'
if pos < slen and spec[pos] == '#':
string.strcat(c_flags, "#")
pos += 1
# zero '0'
if pos < slen and spec[pos] == '0':
string.strcat(c_flags, "0")
pos += 1
# 左对齐 '<'/'^' → '-'(与 0 同时存在时 C 语义 '-' 优先)
if align_char == '<' or align_char == '^':
string.strcat(c_flags, "-")
# width digits
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
wl: t.CSizeT = string.strlen(c_width)
if wl < 30:
c_width[wl] = spec[pos]
c_width[wl + 1] = '\0'
pos += 1
# comma ',' → C printf 不支持千分位,忽略
if pos < slen and spec[pos] == ',':
pos += 1
# .precision
if pos < slen and spec[pos] == '.':
string.strcat(c_prec, ".")
pos += 1
while pos < slen and spec[pos] >= '0' and spec[pos] <= '9':
pl: t.CSizeT = string.strlen(c_prec)
if pl < 30:
c_prec[pl] = spec[pos]
c_prec[pl + 1] = '\0'
pos += 1
if string.strlen(c_prec) == 1:
return None # 只有 '.' 无效
# type char
fmt_type: t.CChar = 0
if pos < slen:
fmt_type = spec[pos]
pos += 1
if pos != slen:
return None # 多余字符无法解析
# 3. 长度修饰(按值类型位宽)
len_mod: str = pool.alloc(4)
if len_mod is None:
return None
len_mod[0] = '\0'
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
if ibits == 64:
string.strcpy(len_mod, "ll")
elif ibits == 32:
string.strcpy(len_mod, "l")
elif ibits == 16:
string.strcpy(len_mod, "h")
elif ibits == 8:
string.strcpy(len_mod, "hh")
# 4. 组装 C 格式串
out: str = pool.alloc(FSTR_FMT_MAX)
if out is None:
return None
out[0] = '\0'
string.strcat(out, "%")
string.strcat(out, c_flags)
string.strcat(out, c_width)
string.strcat(out, c_prec)
if fmt_type == 0:
# 无 type:按值类型推断
fbits2: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
if ibits != 0:
if ibits == 8:
string.strcat(out, "c")
elif ibits == 1:
string.strcat(out, "d")
else:
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fbits2 != 0:
string.strcat(out, "f")
elif self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
string.strcat(out, "s")
else:
string.strcat(out, "d")
elif fmt_type == 'c':
string.strcat(out, "c")
elif fmt_type == 'f' or fmt_type == 'F':
string.strcat(out, "f")
elif fmt_type == 'e' or fmt_type == 'E' or fmt_type == 'g' or fmt_type == 'G':
tmp_ch: str = pool.alloc(2)
if tmp_ch is None:
return None
tmp_ch[0] = fmt_type
tmp_ch[1] = '\0'
string.strcat(out, tmp_ch)
elif fmt_type == 's':
string.strcat(out, "s")
elif fmt_type == '%':
string.strcat(out, "%")
elif fmt_type == 'b':
return None # 二进制不支持
elif fmt_type == 'd' or fmt_type == 'i':
string.strcat(out, len_mod)
string.strcat(out, "d")
elif fmt_type == 'x' or fmt_type == 'X' or fmt_type == 'o' or fmt_type == 'u':
string.strcat(out, len_mod)
tmp_ch2: str = pool.alloc(2)
if tmp_ch2 is None:
return None
tmp_ch2[0] = fmt_type
tmp_ch2[1] = '\0'
string.strcat(out, tmp_ch2)
else:
return None
return out
def _fstr_infer_c_fmt(self, pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> str:
"""无 format_spec 时按值类型推断 C printf 格式(对齐 TPC 无 spec 分支,带 % 前缀)"""
if val is None or pool is None:
return None
out: str = pool.alloc(16)
if out is None:
return None
out[0] = '\0'
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
if ibits == 1:
return "%d" # bool
if ibits == 8:
return "%c" # 字符
if ibits != 0:
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
if ibits == 64:
string.strcpy(out, "%llu")
else:
string.strcpy(out, "%u")
else:
if ibits == 64:
string.strcpy(out, "%lld")
else:
string.strcpy(out, "%d")
return out
if fbits != 0:
return "%f"
if self.Gen.ExprGenBase.is_ptr_type(val.Ty) != 0:
return "%s"
# fail-fast:值类型缺失(非整数/浮点/指针),无法推断 C 格式,立即终止编译
self.Gen.TypeGenErr.fatal_error(arg_node, "f-string 格式无法推断(值类型缺失)", "TypeError")
return None
def _fstr_cast_value(self, builder: llvmlite.IRBuilder | t.CPtr,
pool: memhub.MemBuddy | t.CPtr,
val: llvmlite.Value | t.CPtr,
c_fmt: str,
arg_node: ast.AST | t.CPtr,
trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr:
"""按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)"""
if val is None or c_fmt is None:
return None
# 找格式类型字符(最后一个字母,忽略 l/h 长度修饰)
fmt_char: t.CChar = 0
flen: t.CSizeT = string.strlen(c_fmt)
fi: t.CSizeT = flen
while fi > 0:
fi -= 1
ch: t.CChar = c_fmt[fi]
if ch == '%':
break
if (ch >= 'a' and ch <= 'z') or (ch >= 'A' and ch <= 'Z'):
if ch != 'l' and ch != 'h':
fmt_char = ch
break
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
if fmt_char == 's':
return val
if fmt_char == 'c':
ibits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
if ibits != 0 and ibits < 32:
return llvmlite.build_zext(builder, val, i32_ty)
return val
if fmt_char == 'd' or fmt_char == 'i' or fmt_char == 'x' \
or fmt_char == 'X' or fmt_char == 'o' or fmt_char == 'u':
ibits = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty)
if ibits == 1:
return llvmlite.build_zext(builder, val, i32_ty)
if ibits == 8 or ibits == 16:
if self.Gen.ExprGenOps._node_type_unsigned(pool, arg_node, trans) != 0:
return llvmlite.build_zext(builder, val, i32_ty)
return llvmlite.build_sext(builder, val, i32_ty)
return val
if fmt_char == 'f' or fmt_char == 'e' or fmt_char == 'E' \
or fmt_char == 'g' or fmt_char == 'G':
fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(val.Ty)
if fbits == 32:
return llvmlite.build_fpext(builder, val, llvmlite.Double(pool))
return val
return val