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

910 lines
45 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 t, c
from stdint import *
import ast
import string
import memhub
import viperlib
import stdlib
# ============================================================
# 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) -> t.CInt64T:
"""递归计算 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: t.CInt64T = _eval_binop_int(bop.left)
rv: t.CInt64T = _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 数组(t.CInt64T*)而非 list[int]:TPC 对 list[int].get 会误解析为
# list[str].get(返回元素指针 i8*)→ ptrtoint 返回元素地址而非值 → MASK 变成
# 20323785277439(地址-1)。C 数组索引 [i] 直接返回 i64 值,可靠。
_g_pyi_cdef_names: list[str] | t.CPtr = None
_g_pyi_cdef_vals: t.CInt64T | t.CPtr = None
_g_pyi_cdef_count: t.CInt = 0
def _lookup_cdefine_for_pyi(name: str) -> t.CInt64T:
"""从本文件缓存 / 本地 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):
# list[str] | t.CPtr 联合类型下 TPV 的 len() 回退 strlen(把 list 对象
# 指针当字符串算长度 → 垃圾小值 → 靠后注册项查不到,ZDEFLATE_HASH_MASK
# = lookup(HASH_SIZE) 失败 = 0-1 = -1 根因;WINDOW_MASK 查位置 1 恰好
# 在 strlen 结果内所以碰巧正确)。
pn: t.CSizeT = t.CSizeT(_g_pyi_cdef_count)
pi: t.CSizeT
for pi in range(pn):
pnm: str = _g_pyi_cdef_names.get(pi)
if pnm is not None and string.strcmp(pnm, name) == 0:
_g_pyi_cdefine_found = 1
return _g_pyi_cdef_vals[pi]
val: t.CInt64T = 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) -> t.CInt64T:
"""求值 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 cl_c.args.__len__() > 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: t.CSizeT, pos: t.CSizeT,
text: str) -> t.CSizeT:
"""追加 text 到 buf 的 pos 位置,返回新的 pos"""
if buf is None or text is None or pos >= buf_size:
return pos
tlen: t.CSizeT = string.strlen(text)
if tlen == 0:
return pos
remain: t.CSizeT = 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: t.CSizeT, pos: t.CSizeT,
level: int) -> t.CSizeT:
"""追加 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: t.CSizeT, pos: t.CSizeT,
annot: ast.AST | t.CPtr) -> t.CSizeT:
"""递归生成类型注解字符串,返回新的 pos"""
if annot is None:
return _PyiEmit(buf, buf_size, pos, "t.CInt")
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, "t.CInt")
# Attribute: 属性引用 (如 t.CInt, 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)
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)
pos = _PyiEmit(buf, buf_size, pos, "[")
pos = _PyiTypeStr(buf, buf_size, pos, sub.slice)
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)
pos = _PyiEmit(buf, buf_size, pos, " | ")
pos = _PyiTypeStr(buf, buf_size, pos, bop.right)
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[t.CUInt32T, 64] 的 count 等):输出数值本身,
# 而不是回退 t.CInt——否则 vpi 生成 "t.CArray[..., t.CInt]" 在加载
# 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, "t.CInt")
# 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: t.CSizeT = tup_elts.__len__()
ti: t.CSizeT = 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)
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)
pos = _PyiEmit(buf, buf_size, pos, "(")
if cl.args is not None:
cl_args: list[ast.AST | t.CPtr] | t.CPtr = cl.args
can: t.CSizeT = cl_args.__len__()
cai: t.CSizeT = 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)
cai += 1
pos = _PyiEmit(buf, buf_size, pos, ")")
return pos
# 未知类型,回退到 t.CInt
return _PyiEmit(buf, buf_size, pos, "t.CInt")
# ============================================================
# _PyiFunction - 生成函数签名
#
# 格式: def name(param: Type, ...) -> ReturnType: pass
# ============================================================
def _PyiFunction(buf: bytes, buf_size: t.CSizeT, pos: t.CSizeT,
fn: ast.FunctionDef | t.CPtr,
indent_level: int, class_name: str) -> t.CSizeT:
"""生成函数签名到 buf,返回新的 pos"""
# 装饰器
if fn.decorator_list is not None:
fn_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = fn.decorator_list
dn: t.CSizeT = fn_decorator_list.__len__()
di: t.CSizeT = 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: t.CSizeT = args_args.__len__()
ai: t.CSizeT = 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)
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)
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: t.CSizeT, pos: t.CSizeT,
cls: ast.ClassDef | t.CPtr, indent_level: int) -> t.CSizeT:
"""生成类定义到 buf,返回新的 pos"""
# 装饰器
if cls.decorator_list is not None:
cls_decorator_list: list[ast.AST | t.CPtr] | t.CPtr = cls.decorator_list
dcn: t.CSizeT = cls_decorator_list.__len__()
dci: t.CSizeT = 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: t.CSizeT = cls_type_params.__len__()
if tpn > 0:
pos = _PyiEmit(buf, buf_size, pos, "[")
tpi: t.CSizeT = 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: t.CSizeT = cls_bases.__len__()
if bn > 0:
pos = _PyiEmit(buf, buf_size, pos, "(")
bi: t.CSizeT = 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: t.CSizeT = cls_children.__len__()
ci: t.CSizeT = 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: t.CSizeT = mfn_children.__len__()
ici: t.CSizeT = 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")
pos = _PyiEmit(buf, buf_size, pos, "\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: t.CSizeT) -> t.CSizeT:
"""从 AST 生成 .vpi 存根文件内容到 buf"""
if tree is None or buf is None or buf_size == 0:
return 0
pos: t.CSizeT = 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 → 缓存永不命中,
# 追加58 首版失效根因)。
global _g_pyi_cdef_names
global _g_pyi_cdef_vals
global _g_pyi_cdef_count
if tree.children is not None:
pre_children: list[ast.AST | t.CPtr] | t.CPtr = tree.children
pcn: t.CSizeT = pre_children.__len__()
# 第一遍:数本文件 t.CDefine 声明数(C 数组需先定长分配)
_g_pyi_cdef_count = 0
pci0: t.CSizeT = 0
while pci0 < 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
pci0 += 1
if _g_pyi_cdef_count > 0:
_g_pyi_cdef_names = list[str](mb, t.CSizeT(_g_pyi_cdef_count))
_g_pyi_cdef_vals = (t.CInt64T | t.CPtr)(mb.alloc(t.CSizeT(_g_pyi_cdef_count) * 8))
# 第二遍:按声明顺序注册(BinOp 内 Name 引用依赖先前注册项)
pci: t.CSizeT = 0
cdi: t.CInt = 0
while pci < 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:
_g_pyi_cdef_names.append(tgt_pc.id)
_g_pyi_cdef_vals[cdi] = _eval_cdefine_value_cache(aa_pc.value)
cdi += 1
pci += 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: t.CSizeT = tree_children.__len__()
# 第一遍扫描:检测 import t/c
has_import_t: int = 0
has_import_c: int = 0
i: t.CSizeT = 0
while i < 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: t.CSizeT = imp_names.__len__()
ai: t.CSizeT = 0
while ai < 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
ai += 1
i += 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")
# 第二遍扫描:生成声明
i = 0
while i < 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: t.CSizeT = imp2_names.__len__()
ai2: t.CSizeT = 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: t.CSizeT = ifr_names.__len__()
ai3: t.CSizeT = 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")
# 函数定义
elif k == ast.ASTKind.FunctionDef:
fn: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(stmt)
pos = _PyiFunction(buf, buf_size, pos, fn, 0, "")
pos = _PyiEmit(buf, buf_size, pos, "\n")
# 类定义
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.CVoid | 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: t.CInt64T = -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 call_args_cv.__len__() > 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 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: t.CInt64T = -uoc.int_val
viperlib.snprintf(num_buf_cu, 32, " = %lld", neg_c_val)
pos = _PyiEmit(buf, buf_size, pos, num_buf_cu)
elif arg_kind == ast.ASTKind.BinOp:
# Call 内部的 BinOp(如 t.CUnsignedLong(A | B))
# 递归计算 BinOp 值
binop_val_bo: t.CInt64T = _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: t.CInt64T = _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: t.CSizeT = asg_targets.__len__()
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, "t.CInt")
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")
i += 1
# NUL 终止
if pos < buf_size:
buf[pos] = 0
else:
buf[buf_size - 1] = 0
return pos