Files
ViperC/App/lib/Stub/StubGen/Converter.vp
T

1061 lines
54 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 存根文件生成器
#
# 从 AST 直接生成 .vpi 存根文件,不依赖 CPython 的 PythonToStubConverter。
# 遍历 Module 节点的 children,提取函数签名、类定义、变量声明、
# import 语句,生成 Python 类型存根文件。
#
# 本文件使用 Viper 语法实现,可被 TPC 编译为原生码。
# ============================================================
# ============================================================
# _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 | t.CPtr) -> int:
"""检查 BinOp 节点是否可计算(所有叶子节点都是常量或已注册的 CDefine)"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_INT:
return 1
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node)
if uop.operand is not None:
return _eval_binop_valid(uop.operand)
return 0
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr) -> int64:
"""递归计算 BinOp 节点的整数值(64 位,避免大整数 CDefine 截断)"""
if node is None:
return 0
k: int = node.kind()
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(node)
if cn.const_kind == ast.CONST_INT:
return cn.int_val
return 0
if k == ast.ASTKind.UnaryOp:
uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(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 | t.CPtr = (ast.BinOp | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(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 生成专用):
# Phase1 生成 .vpi(L1614)早于翻译阶段的 CDefine 注册(L1629+),此时
# HandlesType 全局表为空 → BinOp 内 Name 引用查表失败退化为 0(ZDEFLATE
# WINDOW_SIZE = 1<<0 = 1、MASK = 0-1 = -1,跨模块读回错误常量根因)。
# _GeneratePyiFromAst 开头按声明顺序注册到本缓存,Name 引用先查缓存。
# 缓存一律用 C 数组而非 list[T]:
# - 值缓存用 C 数组(int64*)而非 list[int]:TPC 对 list[int].get 会误解析
# 为 list[str].get(返回元素指针 i8*)→ ptrtoint 返回元素地址而非值 → MASK
# 变成 20323785277439(地址-1)。C 数组索引 [i] 直接返回 i64 值,可靠。
# - 名缓存用 C 数组(str|t.CPtr = i8**,字符串指针数组)而非 list[str]:
# list[str] | t.CPtr 联合类型接收者的 .get 会被 TPC/TPV 误分派为
# HashTable.get(self, key, default) —— 索引被 inttoptr 成 key 指针传给
# _fnv1a_hash → 解引用零页 access-violation(1.LOG ASan 崩溃根因)。
# 读写沿用 argparse.vp 遍历 argv 的可靠模式: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(_g_pyi_cdef_names):
# i8** 指针数组上 TPV 的 len() 回退 strlen(把基址指针当字符串算长度
# → 垃圾小值 → 靠后注册项查不到,ZDEFLATE_HASH_MASK = lookup(HASH_SIZE)
# 失败 = 0-1 = -1 根因;WINDOW_MASK 查位置 1 恰好落在 strlen 结果内所以
# 碰巧正确)。且绝不能对 _g_pyi_cdef_names 调 .get —— list 联合接收者
# 会被误分派为 HashTable.get,索引 inttoptr 成 key 指针读零页崩溃
# (1.LOG)。用 t.CPtr(base + i*8) + e[0] 取第 i 个名字指针(argparse 模式)。
pn: sizet = sizet(_g_pyi_cdef_count)
pi: sizet
for pi in range(pn):
entry_pc: str | t.CPtr = 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 | t.CPtr) -> 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 | t.CPtr = (ast.Constant | t.CPtr)(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 | t.CPtr = (ast.Call | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(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
# ============================================================
# _PyiTypeStr - 递归生成类型注解字符串
#
# 将 AST 类型注解节点转换为字符串表示,写入 buf 的 pos 位置。
# 支持: Name, Attribute, Subscript, BinOp(|), Constant(前向引用), Tuple
# ============================================================
def _PyiTypeStr(buf: bytes, buf_size: sizet, pos: sizet,
annot: ast.AST | t.CPtr,
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 | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr = (ast.Attribute | t.CPtr)(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 | t.CPtr = (ast.Subscript | t.CPtr)(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, File | t.CPtr)
if k == ast.ASTKind.BinOp:
bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
pos = _PyiTypeStr(buf, buf_size, pos, bop.left, depth + 1)
pos = _PyiEmit(buf, buf_size, pos, " | ")
pos = _PyiTypeStr(buf, buf_size, pos, bop.right, depth + 1)
return pos
# Constant: 字符串前向引用 (如 'MyClass')、None 或数字常量(CArray count)
if k == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(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")
# Tuple: 多参数下标 (如 Dict[str, int])
if k == ast.ASTKind.Tuple:
tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(annot)
if tup.elts is not None:
tup_elts: list[ast.AST | t.CPtr] | t.CPtr = 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 | t.CPtr = 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 | t.CPtr = (ast.Call | t.CPtr)(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 | t.CPtr] | t.CPtr = 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 | t.CPtr = cl_args.get(cai)
pos = _PyiTypeStr(buf, buf_size, pos, ca, depth + 1)
cai += 1
pos = _PyiEmit(buf, buf_size, pos, ")")
return pos
# 未知类型,回退到 int
return _PyiEmit(buf, buf_size, pos, "int")
# ============================================================
# _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 | t.CPtr,
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 | t.CPtr = (ast.Constant | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr = (ast.UnaryOp | t.CPtr)(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
# ============================================================
# _PyiCreturnArgs - 从装饰器列表提取 @c.CReturn(...) 的实参列表
#
# N11:`@c.CReturn(a, b, c)` 在定义侧被 FuncGenBase._resolve_creturn_ret_ty
# 展开为聚合返回类型 `{a, b, c}`,但 vpi 里该函数只有装饰器行、没有 `->` 注解
# → stub declare 退化成 i32(mm.GetMemoryStats 定义 `{i64,i64,i64}` vs
# `declare i32`)。生成 vpi 时据此合成 `-> tuple[a, b, c]`
# (_resolve_vpi_ret_annot_str 的既有 tuple 分支同样打包成 Struct),
# 使 stub declare 与 define 的返回类型一致。
# 返回 None = 装饰器列表里没有 CReturn。
# ============================================================
def _PyiCreturnArgs(decorators: list[ast.AST | t.CPtr] | t.CPtr) -> list[ast.AST | t.CPtr] | t.CPtr:
"""在装饰器列表里找 @c.CReturn(...),返回其实参列表;未找到返回 None"""
if decorators is None:
return None
dn: sizet = len(decorators)
di: sizet = 0
while di < dn:
dnode: ast.AST | t.CPtr = decorators.get(di)
di += 1
if dnode is None or dnode.kind() != ast.ASTKind.Call:
continue
dcall: ast.Call | t.CPtr = (ast.Call | t.CPtr)(dnode)
if dcall.func is None or dcall.func.kind() != ast.ASTKind.Attribute:
continue
dat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(dcall.func)
if dat.attr is not None and string.strcmp(dat.attr, "CReturn") == 0:
return dcall.args
return None
# ============================================================
# _PyiFunction - 生成函数签名
#
# 格式: def name(param: Type, ...) -> ReturnType: pass
# ============================================================
def _PyiFunction(buf: bytes, buf_size: sizet, pos: sizet,
fn: ast.FunctionDef | t.CPtr,
indent_level: int, class_name: str) -> sizet:
"""生成函数签名到 buf,返回新的 pos"""
# 装饰器
if fn.decorator_list is not None:
fn_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = fn.decorator_list
dn: sizet = len(fn_decorator_list)
di: sizet = 0
while di < dn:
dec: ast.AST | t.CPtr = 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 | t.CPtr = fn.args
if args_node is not None:
args: ast.Arguments | t.CPtr = (ast.Arguments | t.CPtr)(args_node)
first: int = 1
# 位置参数
if args.args is not None:
args_args: list[ast.AST | t.CPtr] | t.CPtr = args.args
an: sizet = len(args_args)
# 默认值右对齐到 args 尾部:defaults[i] 对应 args[an - dn + i]。
# vpi stub 必须输出 '= <default>',否则 VpiReader 解析后默认参数
# 表无条目,跨模块缺参调用(如 depth=0)读到栈垃圾。
# 注意:arg_defaults 必须内联声明(list[ast.AST|CPtr] | t.CPtr 联合
# 接收者的 .get 会被 TPC/TPV 误分派——预声明 + 重赋值会解析成
# list[int].get 返回 i32 截断指针崩溃;内联声明走 list[ast.ASTptr].get)。
arg_defaults: list[ast.AST | t.CPtr] | t.CPtr = 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 | t.CPtr = (ast.Arg | t.CPtr)(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 | t.CPtr = 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 | t.CPtr = (ast.Arg | t.CPtr)(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 | t.CPtr = (ast.Arg | t.CPtr)(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(...):合成 " -> tuple[a, b, c]"(N11)
cre_args: list[ast.AST | t.CPtr] | t.CPtr = _PyiCreturnArgs(fn.decorator_list)
if cre_args is not None:
cre_n: sizet = len(cre_args)
if cre_n > 0:
pos = _PyiEmit(buf, buf_size, pos, " -> tuple[")
cre_i: sizet = 0
while cre_i < cre_n:
if cre_i > 0:
pos = _PyiEmit(buf, buf_size, pos, ", ")
pos = _PyiTypeStr(buf, buf_size, pos, cre_args.get(cre_i))
cre_i += 1
pos = _PyiEmit(buf, buf_size, pos, "]")
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 | t.CPtr, indent_level: int) -> sizet:
"""生成类定义到 buf,返回新的 pos"""
# 装饰器
if cls.decorator_list is not None:
cls_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = cls.decorator_list
dcn: sizet = len(cls_decorator_list)
dci: sizet = 0
while dci < dcn:
ddec: ast.AST | t.CPtr = 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:
cls_type_params: list[ast.AST | t.CPtr] | t.CPtr = 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 | t.CPtr] | t.CPtr = 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 | t.CPtr = 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 | t.CPtr] | t.CPtr = cls.children
cn: sizet = len(cls_children)
ci: sizet = 0
while ci < cn:
stmt: ast.AST | t.CPtr = cls_children.get(ci)
if stmt is not None:
sk: int = stmt.kind()
# 方法
if sk == ast.ASTKind.FunctionDef:
mfn: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(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 | t.CPtr] | t.CPtr = mfn.children
icn: sizet = len(mfn_children)
ici: sizet = 0
while ici < icn:
ibody: ast.AST | t.CPtr = mfn_children.get(ici)
if ibody is not None and ibody.kind() == ast.ASTKind.AnnAssign:
iaa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(ibody)
if iaa.target is not None and iaa.target.kind() == ast.ASTKind.Attribute:
iat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(iaa.target)
# 检查 target.value 是否为 Name("self")
if iat.value is not None and iat.value.kind() == ast.ASTKind.Name:
iavn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt)
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
tn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr = (ast.ClassDef | t.CPtr)(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 生成 .vpi 存根文件
#
# 遍历 AST Module 节点,提取函数签名、类定义、变量声明、import 语句,
# 生成 Python 类型存根文件内容到 buf。
#
# Args:
# mb: 内存池(保留以符合调用约定,实际未使用)
# tree: AST Module 根节点
# rel_path: 相对路径(用于模块名,当前未使用)
# buf: 输出缓冲区
# buf_size: 缓冲区大小
#
# Returns:
# 写入的字节数(0 表示失败)
# ============================================================
def _GeneratePyiFromAst(mb: memhub.MemBuddy | t.CPtr, tree: ast.AST | t.CPtr,
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
pos: sizet = 0
# 预处理:按声明顺序注册本文件 CDefine 到 _g_pyi 缓存(供 BinOp 求值)
# vpi 生成(Phase1 L1614)早于翻译阶段的 CDefine 注册(L1629+),直接查
# HandlesType 全局表为空 → BinOp 内 Name 引用退化为 0(ZDEFLATE_WINDOW_SIZE
# = 1<<0 = 1、MASK = 0-1 = -1 → 跨模块读回错误常量 → 索引越界崩溃根因)。
# 必须 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 | t.CPtr] | t.CPtr = tree.children
pcn: sizet = len(pre_children)
# 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配)
_g_pyi_cdef_count = 0
for pci0 in range(pcn):
stmt_pc0: ast.AST | t.CPtr = pre_children.get(pci0)
if stmt_pc0 is not None and stmt_pc0.kind() == ast.ASTKind.AnnAssign:
aa_pc0: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(aa_pc0.target)
if tgt_pc0.id is not None and aa_pc0.annotation.kind() == ast.ASTKind.Attribute:
ann_pc0: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(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 | t.CPtr = pre_children.get(pci)
if stmt_pc is not None and stmt_pc.kind() == ast.ASTKind.AnnAssign:
aa_pc: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(aa_pc.target)
if tgt_pc.id is not None and aa_pc.annotation.kind() == ast.ASTKind.Attribute:
ann_pc: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(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 = 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 | t.CPtr] | t.CPtr = 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 | t.CPtr = tree_children.get(i)
if stmt is not None and stmt.kind() == ast.ASTKind.Import:
imp: ast.Import | t.CPtr = (ast.Import | t.CPtr)(stmt)
if imp.names is not None:
imp_names: list[ast.AST | t.CPtr] | t.CPtr = imp.names
an: sizet = len(imp_names)
for ai in range(an):
al: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(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 | t.CPtr = (ast.Import | t.CPtr)(stmt)
pos = _PyiEmit(buf, buf_size, pos, "import ")
if imp2.names is not None:
imp2_names: list[ast.AST | t.CPtr] | t.CPtr = 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 | t.CPtr = (ast.Alias | t.CPtr)(imp2_names.get(ai2))
if al2 is not None and al2.name is not None:
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 | t.CPtr = (ast.ImportFrom | t.CPtr)(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 ")
if ifr.names is not None:
ifr_names: list[ast.AST | t.CPtr] | t.CPtr = 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 | t.CPtr = (ast.Alias | t.CPtr)(ifr_names.get(ai3))
if al3 is not None and al3.name is not None:
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 | t.CPtr = (ast.FunctionDef | t.CPtr)(stmt)
pos = _PyiFunction(buf, buf_size, pos, fn, 0, "")
# 类定义
elif k == ast.ASTKind.ClassDef:
cls: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(stmt)
pos = _PyiClass(buf, buf_size, pos, cls, 0)
# 带注解的变量声明
elif k == ast.ASTKind.AnnAssign:
aa: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(stmt)
if aa.target is not None and aa.target.kind() == ast.ASTKind.Name:
nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr = (ast.Attribute | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr = (ast.Constant | t.CPtr)(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 等):stub 必须输出值。
# 否则读回端"vpi 中任一 CDefine 带值 → 不读源文件"
# 的启发式会被同模块 int CDefine(如 MAX_NDIM=4)
# 挡住,无值的 pi 解析不到 → 退化 @pi 全局指针 →
# float<->ptr 报错(NumpyTest 根因)。%.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 必须输出值(同 CONST_FLOAT 追加44 教训——读回端
# 按目标名判定会回退源文件可工作,但 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 | t.CPtr = (ast.UnaryOp | t.CPtr)(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 | t.CPtr = (ast.Constant | t.CPtr)(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 | t.CPtr = (ast.Call | t.CPtr)(aa.value)
if call_cv.args is not None:
call_args_cv: list[ast.AST | t.CPtr] | t.CPtr = call_cv.args
if len(call_args_cv) > 0:
first_arg: ast.AST | t.CPtr = 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 | t.CPtr = (ast.Constant | t.CPtr)(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 | t.CPtr = (ast.UnaryOp | t.CPtr)(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 | t.CPtr = (ast.Constant | t.CPtr)(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 | t.CPtr = (ast.Name | t.CPtr)(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 | t.CPtr = (ast.Assign | t.CPtr)(stmt)
if asg.targets is not None and asg.value is not None:
asg_targets: list[ast.AST | t.CPtr] | t.CPtr = asg.targets
atn: sizet = len(asg_targets)
if atn == 1:
atg: ast.AST | t.CPtr = asg_targets.get(0)
if atg is not None and atg.kind() == ast.ASTKind.Name:
agn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(atg)
aval: ast.AST | t.CPtr = asg.value
avk: int = aval.kind()
inferred: int = 0
if avk == ast.ASTKind.Constant:
acv: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(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