import t, c from stdint import * import ast import llvmlite import memhub import string import lib.core.Gen.Translator as HT import lib.core.Gen.Expr.ExprGenBase as ExprGenBase # ============================================================ # f-string 翻译常量(与 ExprGenMixin 保持一致,本地副本避免跨模块 # CDefine 查找 bug:ExprGenFstr(F) 在 ExprGenMixin(M) 之前按文件序 # 编译时,跨模块 CDefine 全局表未注册 → NameError 未定义) # ============================================================ define FSTR_BUF_SIZE = 1024 define FSTR_SPEC_MAX = 64 define FSTR_FMT_MAX = 64 # ============================================================ # ExprGenFstr - ExprGenMixin 拆分(阶段 D):f-string/格式化值翻译 # ============================================================ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase): """f-string/格式化值翻译""" # ============================================================ # translate_joined_str - f-string 翻译(对齐 TPC _HandleJoinedStrLlvm) # # f"..."(JoinedStr)→ 拼接字面量与格式说明符 → snprintf 生成 i8* # 栈缓冲(每站点独立,线程安全);结果是**函数内临时值,禁止逃逸** # (return / 存字段 / 存全局 由 GenApi.fail_if_fstring_escape 拦; # 需逃逸用 GenApi.escape_fstring_copy 显式拷贝)。 # 之前 f-string 未翻译导致 print/赋值拿到 NULL 指针 # ("(null)"输出 + printf("%s", 垃圾) 崩溃 0xC0000005)。 # ============================================================ def translate_joined_str(self, builder: llvmlite.IRBuilder*, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> llvmlite.Value*: """f-string → snprintf 生成 i8* 栈缓冲(函数内临时值,禁止逃逸)""" if node is None or builder is None or trans is None: return None js: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](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* = None arg_tail: llvmlite.Value* = None arg_count: int = 0 parts: list[ast.AST*]* = js.values pn: sizet = len(parts) for pi in range(pn): part: ast.AST* = parts.get(pi) if part is None: continue if part.kind() == ast.ASTKind.Constant: pcn: ast.Constant* = ast.Constant*(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: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](part) if fv.value is None: continue val: llvmlite.Value* = 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* = 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*, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, node: ast.AST*, trans: HT.Translator* = None) -> llvmlite.Value*: """翻译裸 FormattedValue(f"{expr:spec}" 无字面量时解析器返回 FormattedValue 而非 JoinedStr)""" if node is None or builder is None or trans is None: return None fv: rawptr[ast.FormattedValue] = rawptr[ast.FormattedValue](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* = None arg_tail: llvmlite.Value* = None arg_count: int = 0 val: llvmlite.Value* = 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* = 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*, pool: memhub.MemBuddy*, mod: llvmlite.LLVMModule*, fmt_buf: str, arg_head: llvmlite.Value*, arg_tail: llvmlite.Value*, arg_count: int, trans: HT.Translator*) -> llvmlite.Value*: """f-string 尾部:全局格式串 + 每站点栈缓冲 + snprintf,返回 i8*(函数内临时值)""" i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool) i8_ty: llvmlite.LLVMType* = 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* = self.Gen.ExprGenConst.create_global_string(builder, pool, mod, fmt_buf, trans) if fmt_ptr is None: return None # 结果缓冲:**每站点独立栈缓冲**(entry 块 alloca;每次翻译本站点各建一个, # 站点之间不共享 ⇒ 同函数多个 f-string 不再互相覆盖)。 # # 语义(用户 2026-09-27 决定):f-string 结果是**当前函数内的临时值**。 # · 栈缓冲 ⇒ 线程安全(每个线程各自一份栈帧,同站点并发也不串味) # + 零运行时依赖(内核没有 C-ABI malloc,也不能假设任何分配器); # · 函数返回即失效 ⇒ **禁止逃逸**:return / 存字段 / 存全局 / 存容器一律 # 由 GenApi.fail_if_fstring_escape 在翻译期 fail-fast 拦下。历史事故: # 旧实现让调用方把该栈指针 return 出去,再作 %s 喂 snprintf ⇒ # ASan stack-buffer-underflow in vsnprintf(READ size 1,栈左红区)。 # · 确需逃逸时**显式分配后拷贝**:GenApi.escape_fstring_copy # (stdlib.malloc + string.strcpy);编译器自身的报文构造即此写法 # (AssignGenMixin.const_assign_msg)。 buf_ty: llvmlite.LLVMType* = llvmlite.Array(pool, i8_ty, FSTR_BUF_SIZE) buf_alloca: llvmlite.Value* = self.Gen.VarGen._alloca_at_entry(builder, buf_ty) if buf_alloca is None: return None zero_i32: llvmlite.Value* = llvmlite.const_int32(pool, 0) buf_ptr: llvmlite.Value* = 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* = 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: sizet = string.strlen(fmt_buf) rem: sizet = FSTR_BUF_SIZE - 1 - cur_len if rem == 0: return src_len: sizet = string.strlen(lit) si: sizet = 0 while si < src_len and rem > 0: ch: char = 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: sizet = string.strlen(fmt_buf) src_len: sizet = 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*, spec_node: ast.AST*, val: llvmlite.Value*, arg_node: ast.AST*) -> 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: rawptr[ast.JoinedStr] = rawptr[ast.JoinedStr](spec_node) if jss.values is None: return None vn: sizet = len(jss.values) for vi in range(vn): vpart: ast.AST* = jss.values.get(vi) if vpart is None: continue if vpart.kind() != ast.ASTKind.Constant: return None vcn: ast.Constant* = ast.Constant*(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* = ast.Constant*(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: sizet = string.strlen(spec) if slen == 0: return None # 2. 逐段解析: [fill]align / sign / # / 0 / width / , / .precision / type pos: sizet = 0 align_char: char = 0 if pos < slen: ch0: char = spec[pos] if ch0 == '<' or ch0 == '>' or ch0 == '^' or ch0 == '=': align_char = ch0 pos += 1 elif pos + 1 < slen: ch1: char = 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: sizet = 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: sizet = 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: char = 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) # 32 位**不写**长度修饰符(N14):`{x:08X}` 的实参是 i32,此前推导出 # `l` → 格式串 `%08lX`(long)与实参宽度不符(LP64 下 printf 按 64 位 # 读参 → 变参错位)。与同文件无 spec 路径 `_fstr_infer_c_fmt`(32 位用 # `%u/%d`)以及 TPC 侧 HandlesExprFormat.py 保持一致。 if ibits == 64: string.strcpy(len_mod, "ll") 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*, val: llvmlite.Value*, arg_node: ast.AST*, trans: HT.Translator*) -> 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*, pool: memhub.MemBuddy*, val: llvmlite.Value*, c_fmt: str, arg_node: ast.AST*, trans: HT.Translator*) -> llvmlite.Value*: """按 C 格式转换值类型(i8/i16→i32、bool→i32、f32→f64)""" if val is None or c_fmt is None: return None # 找格式类型字符(最后一个字母,忽略 l/h 长度修饰) fmt_char: char = 0 flen: sizet = string.strlen(c_fmt) fi: sizet = flen while fi > 0: fi -= 1 ch: char = 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* = 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