Files
ViperC/App/lib/Stub/StubGen/Converter.vp
T
TermiNexus c2d7480f3d fix(transpyv): 槽视图统一 cast 到声明类型(47 处,含 codegen 自身)
同 includes 的同类修正:把一级指针 i8* 隐式塞进二级注解(i8** / str*)。
TransPyV 侧 coerce(ExprGenBase._ptr_cast_reason)没有 TPC 那样的「值侧是显式
指针 cast ⇒ 按 bitcast 重解释」豁免,故一旦用新构建的 TransPyV 编到这些文件
必然 fail-fast(回归测试首爆点在 includes/llvmlite 侧,本仓同形态站点随后必然
同样触发)。

涉及的两种形态:
  · `slot: str | t.CPtr = (t.CPtr)(addr)`   —— cast 目标是一级 t.CPtr
  · `nm_slot: str* = (t.CPtr)(addr)`        —— 注解 str* 即 i8**
统一改为 cast 到声明类型:`(str | t.CPtr)(addr)` / `str*(addr)`(后者与
includes/sys.vp `argv_arr: str* = str*(...)` 同一写法)。

本仓 47 处 / 6 文件:
  App/lib/core/Gen/VarGenMixin.vp 3
  App/lib/core/Gen/TranslatorGenMixin.vp 8
  App/lib/core/Gen/Func/FuncGenMixin.vp 13
  App/lib/core/Gen/AsyncGenMixin.vp 15
  App/lib/core/Gen/DebugInfoGen.vp 6
  App/lib/Stub/StubGen/Converter.vp 2
Converter.vp:107 的 `_g_pyi_cdef_names: str | t.CPtr = None` 保持原样
(None 即空指针,任何指针类型均可,不属本类问题)。

只提交本 6 个文件;本仓其余未提交改动(CallGenBase/CallGenDispatch/CallGenFd/
CallGenProp/Generator/Translator/VLogger)与本迁移无关,保持不动。
2026-09-29 15:10:33 +08:00

1170 lines
62 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 lib.core.Gen.GenApi as GenApi
import lib.core.VLogger as VLogger
import t, c
from stdint import *
import ast
import string
import memhub
import viperlib
import stdlib
import sys
# ============================================================
# StubGen.Converter - vpi 存根文件生成器
# ============================================================
# ============================================================
# _eval_binop_int / _eval_binop_valid - 递归计算 BinOp 常量值
#
# 用于 .vpi 存根文件生成时计算 BinOp 表达式(如 A | B, A + B)的整数值。
# 支持 BitOr/BitAnd/Add/Sub 以及嵌套的 Name(引用已注册的 CDefine 常量)。
# ============================================================
def _eval_binop_valid(node: ast.AST*) -> int:
"""检查 BinOp 节点是否可计算(所有叶子节点都是常量或已注册的 CDefine)"""
if node is None: return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_INT:
return 1
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp* = ast.UnaryOp*(node)
if uop.operand is not None:
return _eval_binop_valid(uop.operand)
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if bop.left is not None and bop.right is not None:
if _eval_binop_valid(bop.left) != 0:
if _eval_binop_valid(bop.right) != 0:
return 1
return 0
if k == ast.ASTKind.Name:
# Name 引用:检查是否是已注册的 CDefine 常量
nm: ast.Name* = ast.Name*(node)
if nm.id is not None:
# 从本地 CDefine 表查找
val: int = _lookup_cdefine_for_pyi(nm.id)
if _pyi_cdefine_found() != 0:
return 1
return 0
return 0
def _eval_binop_int(node: ast.AST*) -> int64:
"""递归计算 BinOp 节点的整数值(64 位,避免大整数 CDefine 截断)"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_INT:
return cn.int_val
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp* = ast.UnaryOp*(node)
if uop.op == ast.OpKind.USub and uop.operand is not None:
return -_eval_binop_int(uop.operand)
if uop.op == ast.OpKind.UAdd and uop.operand is not None:
return _eval_binop_int(uop.operand)
if uop.op == ast.OpKind.Invert and uop.operand is not None:
return ~_eval_binop_int(uop.operand)
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(node)
if bop.left is None or bop.right is None:
return 0
lv: int64 = _eval_binop_int(bop.left)
rv: int64 = _eval_binop_int(bop.right)
if bop.op == ast.OpKind.BitOr: return lv | rv
if bop.op == ast.OpKind.BitAnd: return lv & rv
if bop.op == ast.OpKind.BitXor: return lv ^ rv
if bop.op == ast.OpKind.Add: return lv + rv
if bop.op == ast.OpKind.Sub: return lv - rv
if bop.op == ast.OpKind.Mult: return lv * rv
if bop.op == ast.OpKind.Div: return lv // rv
if bop.op == ast.OpKind.FloorDiv: return lv // rv
if bop.op == ast.OpKind.Mod: return lv % rv
if bop.op == ast.OpKind.LShift: return lv << rv
if bop.op == ast.OpKind.RShift: return lv >> rv
return 0
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None:
return _lookup_cdefine_for_pyi(nm.id)
return 0
return 0
# CDefine 查找的前向声明(实际实现在 HandlesType)
# 避免循环依赖,使用弱引用
_g_pyi_cdefine_found: int = 0
# 本文件 CDefine 值缓存(vpi 生成专用):vpi 生成早于翻译阶段注册(全局表为空),_GeneratePyiFromAst 开头按声明顺序注册,Name 引用先查本缓存。
# 两个缓存必须是 C 数组(int64* / i8**)而非 list[T]——list[int].get 会误解析为 list[str].get(返回元素地址)、list[str] 联合接收者的 .get 会误分派为 HashTable.get(索引 inttoptr 成 key 指针 → 零页崩溃);索引一律用 t.CPtr(base + i*8) + e[0]。
_g_pyi_cdef_names: str | t.CPtr = None
_g_pyi_cdef_vals: int64 | t.CPtr = None
_g_pyi_cdef_count: int = 0
def _lookup_cdefine_for_pyi(name: str) -> int64:
"""从本文件缓存 / 本地 CDefine 表查找常量值(用于 .vpi 生成时的 BinOp 计算)"""
global _g_pyi_cdefine_found
# 优先查本文件 CDefine 缓存(vpi 生成早于翻译阶段注册,全局表为空)
if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None:
# 循环上限用 _g_pyi_cdef_count,且绝不能用 len()/.get:i8** 指针数组上 len() 回退 strlen(基址当字符串算长度 → 垃圾小值 → 靠后注册项查不到),.get 会被误分派为 HashTable.get(索引 inttoptr 成 key 指针 → 零页崩溃);用 t.CPtr(base + i*8) + e[0] 依次取名。
pn: sizet = sizet(_g_pyi_cdef_count)
for pi in range(pn):
entry_pc: str | t.CPtr = (str | t.CPtr)(uint64(_g_pyi_cdef_names) + pi * 8)
pnm: str = entry_pc[0]
if pnm is not None and string.strcmp(pnm, name) == 0:
_g_pyi_cdefine_found = 1
return _g_pyi_cdef_vals[pi]
val: int64 = GenApi.lookup_cdefine_constant(name)
if GenApi.is_cdefine_found() != 0:
_g_pyi_cdefine_found = 1
return val
_g_pyi_cdefine_found = 0
return 0
def _eval_cdefine_value_cache(node: ast.AST*) -> int64:
"""求值 CDefine 值(Constant/UnaryOp/BinOp/Call/Name),供 vpi 预处理注册缓存"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn_c: ast.Constant* = ast.Constant*(node)
if cn_c.const_kind == ast.CONST_INT:
return cn_c.int_val
if cn_c.const_kind == ast.CONST_BOOL:
return cn_c.int_val
# STR/FLOAT/NONE 不参与整数 BinOp 引用(注册 0 无害)
return 0
if k == ast.ASTKind.UnaryOp:
return _eval_binop_int(node)
if k == ast.ASTKind.BinOp:
return _eval_binop_int(node)
if k == ast.ASTKind.Call:
cl_c: ast.Call* = ast.Call*(node)
if cl_c.args is not None and len(cl_c.args) > 0:
return _eval_cdefine_value_cache(cl_c.args.get(0))
return 0
if k == ast.ASTKind.Name:
nm_c: ast.Name* = ast.Name*(node)
if nm_c.id is not None:
return _lookup_cdefine_for_pyi(nm_c.id)
return 0
return 0
def _pyi_cdefine_found() -> int:
"""返回上次 _lookup_cdefine_for_pyi 的查找结果状态"""
return _g_pyi_cdefine_found
# ============================================================
# _PyiEmit - 追加字符串到缓冲区
#
# 将 text 追加到 buf 的 pos 位置,返回新的 pos。
# 自动处理缓冲区溢出(截断)。
# ============================================================
def _PyiEmit(buf: bytes, buf_size: sizet, pos: sizet,
text: str) -> sizet:
"""追加 text 到 buf 的 pos 位置,返回新的 pos"""
if buf is None or text is None or pos >= buf_size:
return pos
tlen: sizet = string.strlen(text)
if tlen == 0:
return pos
remain: sizet = buf_size - pos
if tlen >= remain:
tlen = remain - 1
if tlen > 0:
string.memcpy(buf + pos, text, tlen)
return pos + tlen
# ============================================================
# _PyiIndent - 追加缩进空格
#
# 追加 level*4 个空格到 buf 的 pos 位置。
# ============================================================
def _PyiIndent(buf: bytes, buf_size: sizet, pos: sizet,
level: int) -> sizet:
"""追加 level*4 个空格到 buf,返回新的 pos"""
i: int = 0
while i < level:
pos = _PyiEmit(buf, buf_size, pos, " ")
i += 1
return pos
# ============================================================
# _PyiBinOpOpText - 输出二元运算符两侧的分隔文本
#
# 与 TPC StubGen/_GetTypeString 的 BinOp 运算符表逐字节一致(" | " / " + " /
# " - " / " * " / " / " / " % " / " & " / " >> ")。支持集必须两侧相同,否则
# 同一份源码写出的 .vpi 注解文本不同——旧实现恒写 " | ",使
# zhuff.vp `t.CArray[zhuff_decode_node, 2 * ZHUFF_MAX_CODES]` 写成
# `2 | ZHUFF_MAX_CODES`(读回端按位或算成 15,正确值 30 → 数组长度错)。
# 表外运算符 fail-fast:静默写错运算符会生成错误的类型/长度文本,
# 让下游按错误结果解析(与 TPC 落到未知节点 fail-fast 同语义)。
# ============================================================
def _PyiBinOpOpText(buf: bytes, buf_size: sizet, pos: sizet,
op: int, node: ast.AST*) -> sizet:
"""按运算符种类输出 " OP " 文本,表外运算符 fail-fast"""
if op == ast.OpKind.BitOr: return _PyiEmit(buf, buf_size, pos, " | ")
if op == ast.OpKind.Add: return _PyiEmit(buf, buf_size, pos, " + ")
if op == ast.OpKind.Sub: return _PyiEmit(buf, buf_size, pos, " - ")
if op == ast.OpKind.Mult: return _PyiEmit(buf, buf_size, pos, " * ")
if op == ast.OpKind.Div: return _PyiEmit(buf, buf_size, pos, " / ")
if op == ast.OpKind.Mod: return _PyiEmit(buf, buf_size, pos, " % ")
if op == ast.OpKind.BitAnd: return _PyiEmit(buf, buf_size, pos, " & ")
if op == ast.OpKind.RShift: return _PyiEmit(buf, buf_size, pos, " >> ")
GenApi.fatal_error(node, "vpi 类型注解节点类型未知", "TypeError")
return pos # 不可达(fatal_error 已退出)
# ============================================================
# _PyiTypeStr - 递归生成类型注解字符串
#
# 将 AST 类型注解节点转换为字符串表示,写入 buf 的 pos 位置。
# 支持集与 TPC StubGen/_GetTypeString 等价:
# Name, Attribute, Subscript, Tuple, BinOp, UnaryOp, Compare, BoolOp,
# Constant, List, Call
# 差异项:TPV 额外支持裸 Tuple(TPC 仅在 Subscript.slice 位置接受 Tuple);
# TPC 额外有 Index 分支(vast 遗留类,解析器不产出该节点)。
# ============================================================
def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet,
annot: ast.AST*,
depth: int = 0) -> sizet:
"""递归生成类型注解字符串,返回新的 pos"""
# 递归深度守卫:AST 损坏成自引用环(Attribute.value/Subscript.slice/
# BinOp.left 等回指祖先)时无限递归直至栈耗尽(Phase1 vpi 生成最先
# 执行,早于翻译阶段)。fail-fast 终止,不静默回退。
if depth > 64:
VLogger.error("vpi 类型注解递归过深(AST 环或嵌套超过 64 层)", "StubGen")
sys.exit(1)
if annot is None:
return _PyiEmit(buf, buf_size, pos, "int")
k: int = annot.kind()
# Name: 简单类型名 (如 int, str, MyClass)
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(annot)
if nm.id is not None:
return _PyiEmit(buf, buf_size, pos, nm.id)
return _PyiEmit(buf, buf_size, pos, "int")
# Attribute: 属性引用 (如 int, memhub.MemBuddy)
if k == ast.ASTKind.Attribute:
at: ast.Attribute* = ast.Attribute*(annot)
pos = _PyiTypeStr(buf, buf_size, pos, at.value, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, ".")
if at.attr is not None:
pos = _PyiEmit(buf, buf_size, pos, at.attr)
return pos
# Subscript: 下标类型 (如 list[int], t.CArray[elem, count])
if k == ast.ASTKind.Subscript:
sub: ast.Subscript* = ast.Subscript*(annot)
pos = _PyiTypeStr(buf, buf_size, pos, sub.value, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, "[")
pos = _PyiTypeStr(buf, buf_size, pos, sub.slice, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, "]")
return pos
# BinOp: 二元运算 (联合类型 int | str;CArray 计数表达式 MAX_ORDER + 1、
# 2 * ZHUFF_MAX_CODES;条件宏实参 MASK_BYTE & 0x0F)。
# 运算符文本按 _PyiBinOpOpText 输出,与 TPC 的运算符表逐字节一致。
if k == ast.ASTKind.BinOp:
bop: ast.BinOp* = ast.BinOp*(annot)
pos = _PyiTypeStr(buf, buf_size, pos, bop.left, depth + 1)
pos = _PyiBinOpOpText(buf, buf_size, pos, bop.op, annot)
pos = _PyiTypeStr(buf, buf_size, pos, bop.right, depth + 1)
return pos
# UnaryOp: 一元运算 (计数表达式 -1 / ~0;条件宏实参 not ct.isptr(x))。
# 输出与 TPC 的 UnaryOp 分支逐字节一致:Not → "not "、Invert → "~"、
# USub → "-";其余一元运算符两侧都不支持 → fail-fast(不静默写错符号)。
if k == ast.ASTKind.UnaryOp:
uop_t: ast.UnaryOp* = ast.UnaryOp*(annot)
if uop_t.op == ast.OpKind.Not:
pos = _PyiEmit(buf, buf_size, pos, "not ")
elif uop_t.op == ast.OpKind.Invert:
pos = _PyiEmit(buf, buf_size, pos, "~")
elif uop_t.op == ast.OpKind.USub:
pos = _PyiEmit(buf, buf_size, pos, "-")
else:
GenApi.fatal_error(annot, "vpi 类型注解节点类型未知", "TypeError")
return pos # 不可达(fatal_error 已退出)
return _PyiTypeStr(buf, buf_size, pos, uop_t.operand, depth + 1)
# Compare: 比较表达式 (条件宏实参 MAX_SIZE > 512 / val == 1)。
# 运算符文本与 TPC 的 Compare 分支逐字节一致:== != < <= > >=;
# 表外运算符(is/in 等)两侧一致输出 " ? "。
if k == ast.ASTKind.Compare:
cmp_t: rawptr[ast.Compare] = rawptr[ast.Compare](annot)
pos = _PyiTypeStr(buf, buf_size, pos, cmp_t.left, depth + 1)
ops_ct: list[int] | t.CPtr = (list[int] | t.CPtr)(cmp_t.ops)
cmps_ct: list[ast.AST*]* = cmp_t.comparators
if ops_ct is not None:
on_ct: sizet = len(ops_ct)
oi_ct: sizet = 0
while oi_ct < on_ct:
oid_ct: int = ops_ct.get(oi_ct)
if oid_ct == ast.OpKind.Eq: pos = _PyiEmit(buf, buf_size, pos, " == ")
elif oid_ct == ast.OpKind.Ne: pos = _PyiEmit(buf, buf_size, pos, " != ")
elif oid_ct == ast.OpKind.Lt: pos = _PyiEmit(buf, buf_size, pos, " < ")
elif oid_ct == ast.OpKind.Le: pos = _PyiEmit(buf, buf_size, pos, " <= ")
elif oid_ct == ast.OpKind.Gt: pos = _PyiEmit(buf, buf_size, pos, " > ")
elif oid_ct == ast.OpKind.Ge: pos = _PyiEmit(buf, buf_size, pos, " >= ")
else: pos = _PyiEmit(buf, buf_size, pos, " ? ")
# TPC 按 ops 的下标取 comparators[i];此处加长度守卫,
# 畸形 AST(ops/comparators 不等长)不越界。
if cmps_ct is not None and oi_ct < len(cmps_ct):
pos = _PyiTypeStr(buf, buf_size, pos, cmps_ct.get(oi_ct), depth + 1)
oi_ct += 1
return pos
# BoolOp: 布尔运算 (条件宏实参 ENABLED and MAX_SIZE > 0 / A or B)。
# 与 TPC 的 BoolOp 分支一致:and → " and "、or → " or "。
if k == ast.ASTKind.BoolOp:
bool_t: rawptr[ast.BoolOp] = rawptr[ast.BoolOp](annot)
vals_bt: list[ast.AST*]* = bool_t.values
if vals_bt is not None:
vn_bt: sizet = len(vals_bt)
vi_bt: sizet = 0
while vi_bt < vn_bt:
if vi_bt > 0:
if bool_t.op == ast.OpKind.Or:
pos = _PyiEmit(buf, buf_size, pos, " or ")
else:
pos = _PyiEmit(buf, buf_size, pos, " and ")
pos = _PyiTypeStr(buf, buf_size, pos, vals_bt.get(vi_bt), depth + 1)
vi_bt += 1
return pos
# Constant: 字符串前向引用 (如 'MyClass')、None 或数字常量(CArray count)
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(annot)
if cn.const_kind == ast.CONST_STR:
pos = _PyiEmit(buf, buf_size, pos, "'")
if cn.str_val is not None:
pos = _PyiEmit(buf, buf_size, pos, cn.str_val)
pos = _PyiEmit(buf, buf_size, pos, "'")
return pos
if cn.const_kind == ast.CONST_NONE:
return _PyiEmit(buf, buf_size, pos, "None")
# 数字常量(t.CArray[uint32, 64] 的 count 等):输出数值本身,
# 而不是回退 int——否则 vpi 生成 "t.CArray[..., int]" 在加载
# stub 时触发 TypeError(count 必须是整数常量、None、CDefine 常量名
# 或常量表达式)。
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_CHAR:
num_buf_ct: str = stdlib.malloc(24)
if num_buf_ct is not None:
viperlib.snprintf(num_buf_ct, 24, "%d", cn.int_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_ct)
stdlib.free(num_buf_ct)
return pos
return _PyiEmit(buf, buf_size, pos, "int")
# List: 函数指针参数表 (如 t.Callable[[t.CPtr, ULONG], BOOL] 的内层
# [t.CPtr, ULONG];t.Callable[[], int] 的空表)。TPV 的 AST 把 Callable
# 的参数表成型为 List(TypeGenResolve 的类型名提取同样按 List 递归),
# 故必须输出 "[a, b]" 形式——缺失该分支时该节点落入未知类型 fail-fast。
if k == ast.ASTKind.List:
lst: ast.List* = ast.List*(annot)
pos = _PyiEmit(buf, buf_size, pos, "[")
if lst.elts is not None:
lst_elts: list[ast.AST*]* = lst.elts
ln: sizet = len(lst_elts)
li: sizet = 0
while li < ln:
if li > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
le: ast.AST* = lst_elts.get(li)
pos = _PyiTypeStr(buf, buf_size, pos, le, depth + 1)
li += 1
pos = _PyiEmit(buf, buf_size, pos, "]")
return pos
# Tuple: 多参数下标 (如 Dict[str, int])
if k == ast.ASTKind.Tuple:
tup: ast.Tuple* = ast.Tuple*(annot)
if tup.elts is not None:
tup_elts: list[ast.AST*]* = tup.elts
tn: sizet = len(tup_elts)
ti: sizet = 0
while ti < tn:
if ti > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
el: ast.AST* = tup_elts.get(ti)
pos = _PyiTypeStr(buf, buf_size, pos, el, depth + 1)
ti += 1
return pos
# Call: 函数调用表达式 (用于装饰器如 @t.NoVTable())
if k == ast.ASTKind.Call:
cl: ast.Call* = ast.Call*(annot)
pos = _PyiTypeStr(buf, buf_size, pos, cl.func, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, "(")
if cl.args is not None:
cl_args: list[ast.AST*]* = cl.args
can: sizet = len(cl_args)
cai: sizet = 0
while cai < can:
if cai > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
ca: ast.AST* = cl_args.get(cai)
pos = _PyiTypeStr(buf, buf_size, pos, ca, depth + 1)
cai += 1
pos = _PyiEmit(buf, buf_size, pos, ")")
return pos
# 未知节点类型 fail-fast:走到此处说明 AST 损坏或出现未支持的注解形态,报错退出(静默写 "int" 会生成错误签名,让下游按错误类型解析)
GenApi.fatal_error(annot, "vpi 类型注解节点类型未知", "TypeError")
return _PyiEmit(buf, buf_size, pos, "int") # 不可达(fatal_error 已退出)
# ============================================================
# _PyiDefaultStr - 生成默认值表达式文本
#
# 用于 vpi 签名 '= <default>' 输出(如 depth: int = 0)。
# vpi stub 必须保留默认值:VpiReader 解析后注册默认参数表,
# 跨模块缺参调用(如 resolve_annotation_type 的 depth=0)才能
# 用默认值填充,否则被调函数读到栈垃圾(假报递归过深/崩溃)。
# 支持: Constant(int/str/bool/None), Name(CDefine 常量), UnaryOp。
# ============================================================
def _PyiDefaultStr(buf: bytes, buf_size: sizet, pos: sizet,
node: ast.AST*,
depth: int = 0) -> sizet:
"""生成默认值表达式文本到 buf,返回新的 pos"""
# 递归深度守卫:AST 损坏成自引用环时 fail-fast 终止(同 _PyiTypeStr)
if depth > 64:
VLogger.error("vpi 默认值表达式递归过深(AST 环或嵌套超过 64 层)", "StubGen")
sys.exit(1)
if node is None:
return pos
k: int = node.kind()
# Constant: 数字/字符串/bool/None 直接输出字面量
if k == ast.ASTKind.Constant:
cn: ast.Constant* = ast.Constant*(node)
if cn.const_kind == ast.CONST_INT or cn.const_kind == ast.CONST_CHAR:
num_buf_dv: str = stdlib.malloc(24)
if num_buf_dv is not None:
viperlib.snprintf(num_buf_dv, 24, "%d", cn.int_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_dv)
stdlib.free(num_buf_dv)
return pos
if cn.const_kind == ast.CONST_STR:
pos = _PyiEmit(buf, buf_size, pos, "\"")
if cn.str_val is not None:
pos = _PyiEmit(buf, buf_size, pos, cn.str_val)
return _PyiEmit(buf, buf_size, pos, "\"")
if cn.const_kind == ast.CONST_BOOL:
if cn.int_val != 0:
return _PyiEmit(buf, buf_size, pos, "True")
return _PyiEmit(buf, buf_size, pos, "False")
if cn.const_kind == ast.CONST_NONE:
return _PyiEmit(buf, buf_size, pos, "None")
return pos
# Name: CDefine 常量名(如 share: ULONG = SHARE_READ)
if k == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(node)
if nm.id is not None:
return _PyiEmit(buf, buf_size, pos, nm.id)
return pos
# UnaryOp: 一元运算(如 -1, ~0)
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp* = ast.UnaryOp*(node)
if uop.op == ast.OpKind.USub:
pos = _PyiEmit(buf, buf_size, pos, "-")
elif uop.op == ast.OpKind.UAdd:
pos = _PyiEmit(buf, buf_size, pos, "+")
elif uop.op == ast.OpKind.Invert:
pos = _PyiEmit(buf, buf_size, pos, "~")
else:
return pos
if uop.operand is not None:
return _PyiDefaultStr(buf, buf_size, pos, uop.operand, depth + 1)
return pos
# 其它(BinOp 等复杂表达式):不输出,保持无默认值(fail-fast 由调用侧兜底)
return pos
# ============================================================
# _PyiRejectCReturnDecorator - 装饰器列表里出现 @c.CReturn 即编译期报错
#
# @c.CReturn 已移除:多返回值统一用返回注解 `-> tuple[ty1, ty2, ...]`
# (既有 tuple → struct 通路保留)。vpi 里仍带该装饰器意味着存根过旧,
# fail-fast 提示重新生成,不静默忽略。返回 0 = 装饰器列表里没有 CReturn。
# ============================================================
def _PyiRejectCReturnDecorator(node: ast.AST*,
decorators: list[ast.AST*]*) -> int:
"""装饰器列表里出现 @c.CReturn 即 fatal_error(返回 0 = 无该装饰器)"""
if decorators is None:
return 0
dn: sizet = len(decorators)
di: sizet = 0
while di < dn:
dnode: ast.AST* = decorators.get(di)
di += 1
if dnode is None:
continue
# 兼容 @c.CReturn(...)(Call(func=Attribute))与裸 @c.CReturn(Attribute)两形态
dattr: ast.Attribute* = None
if dnode.kind() == ast.ASTKind.Call:
dcall: ast.Call* = ast.Call*(dnode)
if dcall.func is not None and dcall.func.kind() == ast.ASTKind.Attribute:
dattr = ast.Attribute*(dcall.func)
elif dnode.kind() == ast.ASTKind.Attribute:
dattr = ast.Attribute*(dnode)
if dattr is None or dattr.attr is None:
continue
if string.strcmp(dattr.attr, "CReturn") == 0:
GenApi.fatal_error(node, "vpi 中仍带 `@c.CReturn`(已移除):请用返回注解 `-> tuple[ty1, ty2, ...]` 重新生成该 .vpi(可加 --clean)", "TypeError")
return 1
return 0
# ============================================================
# _PyiFunction - 生成函数签名
#
# 格式: def name(param: Type, ...) -> ReturnType: pass
# ============================================================
def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet,
fn: ast.FunctionDef*,
indent_level: int, class_name: str) -> sizet:
"""生成函数签名到 buf,返回新的 pos"""
# 装饰器
if fn.decorator_list is not None:
fn_decorator_list: list[ast.AST*]* = fn.decorator_list
dn: sizet = len(fn_decorator_list)
di: sizet = 0
while di < dn:
dec: ast.AST* = fn_decorator_list.get(di)
if dec is not None:
pos = _PyiIndent(buf, buf_size, pos, indent_level)
pos = _PyiEmit(buf, buf_size, pos, "@")
pos = _PyiTypeStr(buf, buf_size, pos, dec)
pos = _PyiEmit(buf, buf_size, pos, "\n")
di += 1
pos = _PyiIndent(buf, buf_size, pos, indent_level)
pos = _PyiEmit(buf, buf_size, pos, "def ")
if fn.name is not None:
pos = _PyiEmit(buf, buf_size, pos, fn.name)
pos = _PyiEmit(buf, buf_size, pos, "(")
# 参数列表
args_node: ast.AST* = fn.args
if args_node is not None:
args: ast.Arguments* = ast.Arguments*(args_node)
first: int = 1
# 位置参数
if args.args is not None:
args_args: list[ast.AST*]* = args.args
an: sizet = len(args_args)
# 默认值右对齐到 args 尾部:defaults[i] 对应 args[an - dn + i]。
# 必须输出 '= <default>',否则 VpiReader 解析出的默认参数表无条目,跨模块缺参调用(如 depth=0)读到栈垃圾。
# arg_defaults 必须内联声明:联合接收者的 .get 会被误分派,预声明 + 重赋值会解析成 list[int].get 返回 i32 截断指针而崩溃。
arg_defaults: list[ast.AST*]* = args.defaults
dn: sizet = 0
if arg_defaults is not None:
dn = len(arg_defaults)
ai: sizet = 0
while ai < an:
if first == 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
first = 0
ag: ast.Arg* = ast.Arg*(args_args.get(ai))
if ag.arg is not None:
pos = _PyiEmit(buf, buf_size, pos, ag.arg)
if ag.annotation is not None:
pos = _PyiEmit(buf, buf_size, pos, ": ")
pos = _PyiTypeStr(buf, buf_size, pos, ag.annotation)
if arg_defaults is not None and an - dn <= ai:
di: sizet = ai - (an - dn)
dnode: ast.AST* = arg_defaults.get(di)
if dnode is not None:
pos = _PyiEmit(buf, buf_size, pos, " = ")
pos = _PyiDefaultStr(buf, buf_size, pos, dnode)
ai += 1
# *args
if args.vararg is not None:
if first == 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
first = 0
pos = _PyiEmit(buf, buf_size, pos, "*")
va: ast.Arg* = ast.Arg*(args.vararg)
if va.arg is not None:
pos = _PyiEmit(buf, buf_size, pos, va.arg)
# **kwargs
if args.kwarg is not None:
if first == 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
first = 0
pos = _PyiEmit(buf, buf_size, pos, "**")
kw: ast.Arg* = ast.Arg*(args.kwarg)
if kw.arg is not None:
pos = _PyiEmit(buf, buf_size, pos, kw.arg)
pos = _PyiEmit(buf, buf_size, pos, ")")
# 返回类型
if fn.returns is not None:
pos = _PyiEmit(buf, buf_size, pos, " -> ")
pos = _PyiTypeStr(buf, buf_size, pos, fn.returns)
else:
# 无 `->` 注解时若带 @c.CReturn(...):该装饰器已移除,fail-fast 提示重生成 vpi
_PyiRejectCReturnDecorator(fn, fn.decorator_list)
pos = _PyiEmit(buf, buf_size, pos, ": pass\n")
return pos
# ============================================================
# _PyiClass - 生成类定义
#
# 格式:
# class Name(Base):
# field: Type
# def method(self: Name, ...) -> RetType: pass
# ============================================================
def _PyiClass(buf: bytes, buf_size: sizet, pos: sizet,
cls: ast.ClassDef*, indent_level: int) -> sizet:
"""生成类定义到 buf,返回新的 pos"""
# 装饰器
if cls.decorator_list is not None:
cls_decorator_list: list[ast.AST*]* = cls.decorator_list
dcn: sizet = len(cls_decorator_list)
dci: sizet = 0
while dci < dcn:
ddec: ast.AST* = cls_decorator_list.get(dci)
if ddec is not None:
pos = _PyiIndent(buf, buf_size, pos, indent_level)
pos = _PyiEmit(buf, buf_size, pos, "@")
pos = _PyiTypeStr(buf, buf_size, pos, ddec)
pos = _PyiEmit(buf, buf_size, pos, "\n")
dci += 1
pos = _PyiIndent(buf, buf_size, pos, indent_level)
pos = _PyiEmit(buf, buf_size, pos, "class ")
if cls.name is not None:
pos = _PyiEmit(buf, buf_size, pos, cls.name)
# PEP 695 类型参数 (class Foo[T]:)
if cls.type_params is not None:
# ClassDef.type_params 的真身是 rawptr[list[str]](**名字串**列表,
# 见 includes/ast/stmts.vp:475-483 与 parser.vp:3619-3663 的
# `list[str](ps.pool, 8)`),不是 `list[rawptr[AST]]`(那是 decorator_list
# 的类型)。原注解从 decorator_list 抄错成 list[ast.AST*]* ⇒ .get() 被解析到
# list[ast.AST*].get,而实参是 list[str]* ⇒ 严格规则下 fail-fast:
# "调用 ...list[AST*].get 第 0 个实参 %"...list[str]"* 不能隐式转为形参
# %"...list[AST*]"*"
# 全仓其余 8 处 type_params 均为 list[str]*(VpiReader/Phase1Scan/Phase2/
# ClassGenDef/ClassGenGeneric 等),此处对齐。
cls_type_params: list[str]* = cls.type_params
tpn: sizet = len(cls_type_params)
if tpn > 0:
pos = _PyiEmit(buf, buf_size, pos, "[")
tpi: sizet = 0
while tpi < tpn:
if tpi > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
tpname: str = cls_type_params.get(tpi)
if tpname is not None:
pos = _PyiEmit(buf, buf_size, pos, tpname)
tpi += 1
pos = _PyiEmit(buf, buf_size, pos, "]")
# 基类
if cls.bases is not None:
cls_bases: list[ast.AST*]* = cls.bases
bn: sizet = len(cls_bases)
if bn > 0:
pos = _PyiEmit(buf, buf_size, pos, "(")
bi: sizet = 0
while bi < bn:
if bi > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
base: ast.AST* = cls_bases.get(bi)
pos = _PyiTypeStr(buf, buf_size, pos, base)
bi += 1
pos = _PyiEmit(buf, buf_size, pos, ")")
pos = _PyiEmit(buf, buf_size, pos, ":\n")
# 类成员
has_member: int = 0
if cls.children is not None:
cls_children: list[ast.AST*]* = cls.children
cn: sizet = len(cls_children)
ci: sizet = 0
while ci < cn:
stmt: ast.AST* = cls_children.get(ci)
if stmt is not None:
sk: int = stmt.kind()
# 方法
if sk == ast.ASTKind.FunctionDef:
mfn: ast.FunctionDef* = ast.FunctionDef*(stmt)
# __init__ 方法:先提取 self.x: Type 属性
if mfn.name is not None and string.strcmp(mfn.name, "__init__") == 0:
if mfn.children is not None:
mfn_children: list[ast.AST*]* = mfn.children
icn: sizet = len(mfn_children)
ici: sizet = 0
while ici < icn:
ibody: ast.AST* = mfn_children.get(ici)
if ibody is not None and ibody.kind() == ast.ASTKind.AnnAssign:
iaa: ast.AnnAssign* = ast.AnnAssign*(ibody)
if iaa.target is not None and iaa.target.kind() == ast.ASTKind.Attribute:
iat: ast.Attribute* = ast.Attribute*(iaa.target)
# 检查 target.value 是否为 Name("self")
if iat.value is not None and iat.value.kind() == ast.ASTKind.Name:
iavn: ast.Name* = ast.Name*(iat.value)
if iavn.id is None or string.strcmp(iavn.id, "self") != 0:
ici += 1
continue
pos = _PyiIndent(buf, buf_size, pos, indent_level + 1)
if iat.attr is not None:
pos = _PyiEmit(buf, buf_size, pos, iat.attr)
pos = _PyiEmit(buf, buf_size, pos, ": ")
pos = _PyiTypeStr(buf, buf_size, pos, iaa.annotation)
pos = _PyiEmit(buf, buf_size, pos, "\n")
has_member = 1
ici += 1
pos = _PyiFunction(buf, buf_size, pos, mfn, indent_level + 1, cls.name)
has_member = 1
# 带注解的属性
elif sk == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
tn: ast.Name* = ast.Name*(aa.target)
pos = _PyiIndent(buf, buf_size, pos, indent_level + 1)
if tn.id is not None:
pos = _PyiEmit(buf, buf_size, pos, tn.id)
pos = _PyiEmit(buf, buf_size, pos, ": ")
pos = _PyiTypeStr(buf, buf_size, pos, aa.annotation)
pos = _PyiEmit(buf, buf_size, pos, "\n")
has_member = 1
# 嵌套类
elif sk == ast.ASTKind.ClassDef:
ncls: ast.ClassDef* = ast.ClassDef*(stmt)
pos = _PyiClass(buf, buf_size, pos, ncls, indent_level + 1)
has_member = 1
ci += 1
# 空类补 pass
if has_member == 0:
pos = _PyiIndent(buf, buf_size, pos, indent_level + 1)
pos = _PyiEmit(buf, buf_size, pos, "pass\n")
return pos
# ============================================================
# _GeneratePyiFromAst - 遍历 AST Module 节点,提取函数签名/类定义/变量声明/import 语句,生成 .vpi 存根文件内容到 buf
#
# Args: mb 内存池(保留以符合调用约定,实际未使用);tree AST Module 根节点;rel_path 源文件路径(仅用于报错归因的"当前文件",应传绝对路径以便 fatal_error 读取源码行);buf 输出缓冲区;buf_size 缓冲区大小
# Returns: 写入的字节数(0 表示失败)
# ============================================================
def _GeneratePyiFromAst(mb: memhub.MemBuddy*, tree: ast.AST*,
rel_path: str, buf: bytes,
buf_size: sizet) -> sizet:
"""从 AST 生成 .vpi 存根文件内容到 buf"""
if tree is None or buf is None or buf_size == 0:
return 0
# 错误归因:把"当前文件"指向正在生成 stub 的这份(rel_path 非 None 时)。
# 本函数内触发的 fatal_error(如 L227/L303/L430 的注解节点类型未知)应指向
# 本源文件,而非上一次 set_current_file 的残留文件。
# 调用侧必须传**绝对路径**:fatal_error 的 _read_source_line 直接用该路径
# 打开文件读取行,传相对路径会打不开 ⇒ 误报"无法读取该行源码"。
# 放在函数入口而非各分支:全函数共用同一套遍历/报错路径,逐分支设置会漏掉
# 中途新增的分支;入口设一次覆盖全部(含后续所有 fatal_error / push_trace)。
# rel_path 为 None 时不改写 CurrentFile(保持上一次设置值):调用侧确实拿不到
# 路径时,宁可沿用"上一次设置的文件"也不要把归因抹成 None。
if rel_path is not None:
GenApi.set_current_file_type(rel_path)
pos: sizet = 0
# 预处理:按声明顺序注册本文件 CDefine 到 _g_pyi 缓存(vpi 生成早于翻译阶段注册,直接查全局表为空会使 BinOp 内 Name 引用退化为 0,跨模块读回错误常量)。
# 必须带 global 声明:TPV 语义下函数内对模块级变量赋值无 global 时会创建局部变量,_lookup_cdefine_for_pyi 读到的仍是模块级 None,缓存永不命中。
global _g_pyi_cdef_names, _g_pyi_cdef_vals, _g_pyi_cdef_count
if tree.children is not None:
pre_children: list[ast.AST*]* = tree.children
pcn: sizet = len(pre_children)
# 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配)
_g_pyi_cdef_count = 0
for pci0 in range(pcn):
stmt_pc0: ast.AST* = pre_children.get(pci0)
if stmt_pc0 is not None and stmt_pc0.kind() == ast.ASTKind.AnnAssign:
aa_pc0: ast.AnnAssign* = ast.AnnAssign*(stmt_pc0)
if aa_pc0.target is not None and aa_pc0.target.kind() == ast.ASTKind.Name \
and aa_pc0.annotation is not None:
tgt_pc0: ast.Name* = ast.Name*(aa_pc0.target)
if tgt_pc0.id is not None and aa_pc0.annotation.kind() == ast.ASTKind.Attribute:
ann_pc0: ast.Attribute* = ast.Attribute*(aa_pc0.annotation)
if ann_pc0.attr is not None and string.strcmp(ann_pc0.attr, "CDefine") == 0:
_g_pyi_cdef_count += 1
if _g_pyi_cdef_count > 0:
_g_pyi_cdef_names = (str | t.CPtr)(mb.alloc(sizet(_g_pyi_cdef_count) * 8))
_g_pyi_cdef_vals = (int64 | t.CPtr)(mb.alloc(sizet(_g_pyi_cdef_count) * 8))
# 第二遍:按声明顺序注册(BinOp 内 Name 引用依赖先前注册项)
pci: sizet = 0
cdi: int = 0
for pci in range(pcn):
stmt_pc: ast.AST* = pre_children.get(pci)
if stmt_pc is not None and stmt_pc.kind() == ast.ASTKind.AnnAssign:
aa_pc: ast.AnnAssign* = ast.AnnAssign*(stmt_pc)
if aa_pc.target is not None and aa_pc.target.kind() == ast.ASTKind.Name \
and aa_pc.annotation is not None:
tgt_pc: ast.Name* = ast.Name*(aa_pc.target)
if tgt_pc.id is not None and aa_pc.annotation.kind() == ast.ASTKind.Attribute:
ann_pc: ast.Attribute* = ast.Attribute*(aa_pc.annotation)
if ann_pc.attr is not None and string.strcmp(ann_pc.attr, "CDefine") == 0:
if _g_pyi_cdef_names is not None and _g_pyi_cdef_vals is not None:
slot_pc: str | t.CPtr = (str | t.CPtr)(uint64(_g_pyi_cdef_names) + cdi * 8)
slot_pc[0] = tgt_pc.id
_g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value)
cdi += 1
# 文件头
pos = _PyiEmit(buf, buf_size, pos, "\"\"\"Auto-generated Python stub file\"\"\"\n\n")
if tree.children is None:
if pos < buf_size:
buf[pos] = 0
return pos
tree_children: list[ast.AST*]* = tree.children
n: sizet = len(tree_children)
# 第一遍扫描:检测 import t/c
has_import_t: int = 0
has_import_c: int = 0
for i in range(n):
stmt: ast.AST* = tree_children.get(i)
if stmt is not None and stmt.kind() == ast.ASTKind.Import:
imp: ast.Import* = ast.Import*(stmt)
if imp.names is not None:
imp_names: list[ast.AST*]* = imp.names
an: sizet = len(imp_names)
for ai in range(an):
al: ast.Alias* = ast.Alias*(imp_names.get(ai))
if al is not None and al.name is not None:
if string.strcmp(al.name, "t") == 0:
has_import_t = 1
elif string.strcmp(al.name, "c") == 0:
has_import_c = 1
# 补充缺失的 import t/c
if has_import_t == 0 and has_import_c == 0:
pos = _PyiEmit(buf, buf_size, pos, "import t, c\n\n")
elif has_import_t == 0:
pos = _PyiEmit(buf, buf_size, pos, "import t\n\n")
elif has_import_c == 0:
pos = _PyiEmit(buf, buf_size, pos, "import c\n\n")
else:
pos = _PyiEmit(buf, buf_size, pos, "\n")
# 第二遍扫描:生成声明
for i in range(n):
stmt = tree_children.get(i)
if stmt is not None:
k: int = stmt.kind()
# Import 语句
if k == ast.ASTKind.Import:
imp2: ast.Import* = ast.Import*(stmt)
pos = _PyiEmit(buf, buf_size, pos, "import ")
# fail-fast:别名列表为空 ⇒ 写出 `import ` 这种残缺行,import 名字
# 全丢,包重导出注册随之失效(与下面逐个别名为空的检查互补)。
if imp2.names is None or len(imp2.names) == 0:
GenApi.fatal_error(stmt,
"vpi 生成:import 语句别名列表为空(应为 ≥1 个别名)——AST 构造/解析异常",
"NameError")
if imp2.names is not None:
imp2_names: list[ast.AST*]* = imp2.names
an2: sizet = len(imp2_names)
ai2: sizet = 0
while ai2 < an2:
if ai2 > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
al2: ast.Alias* = ast.Alias*(imp2_names.get(ai2))
# fail-fast:别名节点或其名字为空 ⇒ 绝不静默写出 "import , "。
# vpi 里 import 行的名字是**包重导出注册的唯一输入**
# (VpiReader._inject_reexport_class_declare 靠它把子模块里的类以
# 包 __init__ 的 sha1 注册别名 entry),名字静默丢失会让 `包.类`
# 形式的模块限定注解解析落空,下游报出与根因无关的 NameError
# (实测:ast/__init__.vpi 的 `from .base import , , ,` 名字全空
# ⇒ ast.AST 报"方法 'Result.__init__' 形参 'elem' 类型注解解析失败")。
# 两种成因分开报,便于一次定位:节点指针为空 = Alias 构造失败;
# 节点在而 name 为空 = _copy_str/__init__ 未生效。
if al2 is None:
GenApi.fatal_error(stmt,
"vpi 生成:import 别名**节点指针为空**(Alias(...) 构造未产出对象:"
"pool.alloc 失败或 __init__ 未执行)——import 名字丢失会破坏包重导出注册",
"NameError")
if al2.name is None or al2.name[0] == '\0':
GenApi.fatal_error(stmt,
"vpi 生成:import 别名节点存在但 **name 为空**(_copy_str 返回 None / "
"__init__ 未赋值 name)——import 名字丢失会破坏包重导出注册",
"NameError")
pos = _PyiEmit(buf, buf_size, pos, al2.name)
if al2.asname is not None and string.strlen(al2.asname) > 0:
pos = _PyiEmit(buf, buf_size, pos, " as ")
pos = _PyiEmit(buf, buf_size, pos, al2.asname)
ai2 += 1
pos = _PyiEmit(buf, buf_size, pos, "\n")
# ImportFrom 语句
elif k == ast.ASTKind.ImportFrom:
ifr: ast.ImportFrom* = ast.ImportFrom*(stmt)
pos = _PyiEmit(buf, buf_size, pos, "from ")
lvl: int = ifr.level
while lvl > 0:
pos = _PyiEmit(buf, buf_size, pos, ".")
lvl -= 1
if ifr.module is not None:
pos = _PyiEmit(buf, buf_size, pos, ifr.module)
pos = _PyiEmit(buf, buf_size, pos, " import ")
# fail-fast:同 Import 分支——`from X import *` 走的是 Alias("*"),
# 列表为空或名字为空都说明 AST 坏;静默写出 `from X import ` 会让
# 包重导出注册失效(star 也用于 re-export 判断)。
if ifr.names is None or len(ifr.names) == 0:
GenApi.fatal_error(stmt,
"vpi 生成:from-import 语句别名列表为空(应为 ≥1 个别名,含 '*')"
"——AST 构造/解析异常",
"NameError")
if ifr.names is not None:
ifr_names: list[ast.AST*]* = ifr.names
an3: sizet = len(ifr_names)
ai3: sizet = 0
while ai3 < an3:
if ai3 > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
al3: ast.Alias* = ast.Alias*(ifr_names.get(ai3))
# fail-fast:同 Import 分支(两种成因分开报,一次定位)。
if al3 is None:
GenApi.fatal_error(stmt,
"vpi 生成:from-import 别名**节点指针为空**(Alias(...) 构造未产出对象:"
"pool.alloc 失败或 __init__ 未执行)——包重导出注册会因此失效",
"NameError")
if al3.name is None or al3.name[0] == '\0':
GenApi.fatal_error(stmt,
"vpi 生成:from-import 别名节点存在但 **name 为空**(_copy_str 返回 None / "
"__init__ 未赋值 name)——包重导出注册会因此失效",
"NameError")
pos = _PyiEmit(buf, buf_size, pos, al3.name)
if al3.asname is not None and string.strlen(al3.asname) > 0:
pos = _PyiEmit(buf, buf_size, pos, " as ")
pos = _PyiEmit(buf, buf_size, pos, al3.asname)
ai3 += 1
pos = _PyiEmit(buf, buf_size, pos, "\n")
# 函数定义(_PyiFunction 已以 "\n" 收尾,不再补空行)
elif k == ast.ASTKind.FunctionDef:
fn: ast.FunctionDef* = ast.FunctionDef*(stmt)
pos = _PyiFunction(buf, buf_size, pos, fn, 0, "")
# 类定义
elif k == ast.ASTKind.ClassDef:
cls: ast.ClassDef* = ast.ClassDef*(stmt)
pos = _PyiClass(buf, buf_size, pos, cls, 0)
# 带注解的变量声明
elif k == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign* = ast.AnnAssign*(stmt)
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
nm: ast.Name* = ast.Name*(aa.target)
if nm.id is not None:
pos = _PyiEmit(buf, buf_size, pos, nm.id)
pos = _PyiEmit(buf, buf_size, pos, ": ")
pos = _PyiTypeStr(buf, buf_size, pos, aa.annotation)
# 如果有值(如 CDefine 常量),输出 = value
# CTypedef 特殊处理:值是类型表达式(如 VOIDPTR / t.CPtr),
# 不是整数,用 _PyiTypeStr 输出类型字符串
is_ctypedef_ann: int = 0
if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Attribute:
ctd_at: ast.Attribute* = ast.Attribute*(aa.annotation)
if ctd_at.attr is not None and string.strcmp(ctd_at.attr, "CTypedef") == 0:
if ctd_at.value is not None and ctd_at.value.kind() == ast.ASTKind.Name:
ctd_mod: ast.Name* = ast.Name*(ctd_at.value)
if ctd_mod.id is not None and string.strcmp(ctd_mod.id, "t") == 0:
is_ctypedef_ann = 1
if is_ctypedef_ann != 0 and aa.value is not None:
pos = _PyiEmit(buf, buf_size, pos, " = ")
pos = _PyiTypeStr(buf, buf_size, pos, aa.value)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Constant:
cv: ast.Constant* = ast.Constant*(aa.value)
num_buf: bytes = mb.alloc(32)
if cv.const_kind == ast.CONST_INT and num_buf is not None:
viperlib.snprintf(num_buf, 32, " = %lld", cv.int_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf)
elif cv.const_kind == ast.CONST_FLOAT:
# 浮点 CDefine(numpy 的 pi/e 等)必须输出值:读回端「vpi 中任一 CDefine 带值 → 不读源文件」的启发式会被同模块 int CDefine 挡住,无值的 pi 解析不到会退化成 @pi 全局指针导致 float<->ptr 报错;%.17g 保证 double 全精度往返(与读回端缓存格式一致)。
fbuf_c: bytes = mb.alloc(64)
if fbuf_c is not None:
viperlib.snprintf(fbuf_c, 64, " = %.17g", cv.float_val)
pos = _PyiEmit(buf, buf_size, pos, fbuf_c)
elif cv.const_kind == ast.CONST_BOOL:
if cv.int_val != 0:
pos = _PyiEmit(buf, buf_size, pos, " = True")
else:
pos = _PyiEmit(buf, buf_size, pos, " = False")
elif cv.const_kind == ast.CONST_STR:
# 字符串 CDefine(pyzlib.ZLIB_VERSION = "1.3.2" 等)必须输出值,让 stub 自包含(读回端按目标名判定会回退源文件)
sbuf_c: bytes = mb.alloc(string.strlen(cv.str_val) + 6)
if sbuf_c is not None:
viperlib.snprintf(sbuf_c, string.strlen(cv.str_val) + 6,
" = \"%s\"", cv.str_val)
pos = _PyiEmit(buf, buf_size, pos, sbuf_c)
elif cv.const_kind == ast.CONST_NONE:
pos = _PyiEmit(buf, buf_size, pos, " = None")
elif aa.value is not None and aa.value.kind() == ast.ASTKind.UnaryOp:
# 处理负值常量(如 INVALID_HANDLE_VALUE = -1)
# UnaryOp(USub, Constant(1)) → -1
uop_cv: ast.UnaryOp* = ast.UnaryOp*(aa.value)
num_buf_u: bytes = mb.alloc(32)
if uop_cv.op == ast.OpKind.USub and uop_cv.operand is not None \
and uop_cv.operand.kind() == ast.ASTKind.Constant:
uop_const: ast.Constant* = ast.Constant*(uop_cv.operand)
if uop_const.const_kind == ast.CONST_INT and num_buf_u is not None:
neg_val: int64 = -uop_const.int_val
viperlib.snprintf(num_buf_u, 32, " = %lld", neg_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_u)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Call:
# 处理类型构造函数常量(如 t.CUnsignedLong(-11))
# 提取第一个参数的整数值并输出
call_cv: ast.Call* = ast.Call*(aa.value)
if call_cv.args is not None:
call_args_cv: list[ast.AST*]* = call_cv.args
if len(call_args_cv) > 0:
first_arg: ast.AST* = call_args_cv.get(0)
if first_arg is not None:
arg_kind: int = first_arg.kind()
if arg_kind == ast.ASTKind.Constant:
ca_cv: ast.Constant* = ast.Constant*(first_arg)
num_buf_c: bytes = mb.alloc(32)
if ca_cv.const_kind == ast.CONST_INT and num_buf_c is not None:
viperlib.snprintf(num_buf_c, 32, " = %lld", ca_cv.int_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_c)
elif ca_cv.const_kind == ast.CONST_FLOAT:
# Call 包装的浮点常量(如 PI: t.CDefine = float(3.14159...)):
# stub 必须输出值,否则读回端 vpi 无值 → 退化 0.0。
# %.17g 保证 double 全精度往返(与上方 CONST_FLOAT 分支一致)。
fbuf_cc: bytes = mb.alloc(64)
if fbuf_cc is not None:
viperlib.snprintf(fbuf_cc, 64, " = %.17g", ca_cv.float_val)
pos = _PyiEmit(buf, buf_size, pos, fbuf_cc)
elif arg_kind == ast.ASTKind.UnaryOp:
uop_ca: ast.UnaryOp* = ast.UnaryOp*(first_arg)
num_buf_cu: bytes = mb.alloc(32)
if uop_ca.op == ast.OpKind.USub and uop_ca.operand is not None \
and uop_ca.operand.kind() == ast.ASTKind.Constant:
uoc: ast.Constant* = ast.Constant*(uop_ca.operand)
if uoc.const_kind == ast.CONST_INT and num_buf_cu is not None:
neg_c_val: int64 = -uoc.int_val
viperlib.snprintf(num_buf_cu, 32, " = %lld", neg_c_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_cu)
elif uoc.const_kind == ast.CONST_FLOAT:
# 负浮点 Call 包装(如 t.CDefine = float(-1.5)):
# 同 CONST_FLOAT,%.17g 全精度往返
neg_cf: double = -uoc.float_val
fbuf_cu: bytes = mb.alloc(64)
if fbuf_cu is not None:
viperlib.snprintf(fbuf_cu, 64, " = %.17g", neg_cf)
pos = _PyiEmit(buf, buf_size, pos, fbuf_cu)
elif arg_kind == ast.ASTKind.BinOp:
# Call 内部的 BinOp(如 t.CUnsignedLong(A | B))
# 递归计算 BinOp 值
binop_val_bo: int64 = _eval_binop_int(first_arg)
num_buf_bo: bytes = mb.alloc(32)
if (binop_val_bo != 0 or _eval_binop_valid(first_arg) != 0) \
and num_buf_bo is not None:
viperlib.snprintf(num_buf_bo, 32, " = %lld", binop_val_bo)
pos = _PyiEmit(buf, buf_size, pos, num_buf_bo)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.BinOp:
# 处理 BinOp 常量(如 FOREGROUND_COMBO = FOREGROUND_RED | FOREGROUND_GREEN)
# 递归计算 BinOp 值并输出
binop_val_av: int64 = _eval_binop_int(aa.value)
if binop_val_av != 0 or _eval_binop_valid(aa.value) != 0:
num_buf_b: bytes = mb.alloc(32)
if num_buf_b is not None:
viperlib.snprintf(num_buf_b, 32, " = %lld", binop_val_av)
pos = _PyiEmit(buf, buf_size, pos, num_buf_b)
elif aa.value is not None and aa.value.kind() == ast.ASTKind.Name:
# 处理 Name 引用常量(如 ALIAS = OTHER_CONST)
# 输出引用的名字,让消费方在本地查找
name_val: ast.Name* = ast.Name*(aa.value)
if name_val.id is not None:
pos = _PyiEmit(buf, buf_size, pos, " = ")
pos = _PyiEmit(buf, buf_size, pos, name_val.id)
pos = _PyiEmit(buf, buf_size, pos, "\n")
# 全局赋值(无注解)— 从 Constant value 推断类型
elif k == ast.ASTKind.Assign:
asg: ast.Assign* = ast.Assign*(stmt)
if asg.targets is not None and asg.value is not None:
asg_targets: list[ast.AST*]* = asg.targets
atn: sizet = len(asg_targets)
if atn == 1:
atg: ast.AST* = asg_targets.get(0)
if atg is not None and atg.kind() == ast.ASTKind.Name:
agn: ast.Name* = ast.Name*(atg)
aval: ast.AST* = asg.value
avk: int = aval.kind()
inferred: int = 0
if avk == ast.ASTKind.Constant:
acv: ast.Constant* = ast.Constant*(aval)
if acv.const_kind == ast.CONST_INT:
inferred = 1
elif acv.const_kind == ast.CONST_FLOAT:
inferred = 2
elif acv.const_kind == ast.CONST_STR:
inferred = 3
elif acv.const_kind == ast.CONST_BOOL:
inferred = 4
elif acv.const_kind == ast.CONST_NONE:
inferred = 5
if inferred > 0 and agn.id is not None:
pos = _PyiEmit(buf, buf_size, pos, agn.id)
pos = _PyiEmit(buf, buf_size, pos, ": ")
if inferred == 1:
pos = _PyiEmit(buf, buf_size, pos, "int")
elif inferred == 2:
pos = _PyiEmit(buf, buf_size, pos, "t.CDouble")
elif inferred == 3:
pos = _PyiEmit(buf, buf_size, pos, "str")
elif inferred == 4:
pos = _PyiEmit(buf, buf_size, pos, "bool")
elif inferred == 5:
pos = _PyiEmit(buf, buf_size, pos, "None")
pos = _PyiEmit(buf, buf_size, pos, "\n")
# NUL 终止
if pos < buf_size:
buf[pos] = 0
else:
buf[buf_size - 1] = 0
return pos