Files
TransPyV/App/lib/StubGen/Converter.py

422 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import t, c
from stdint import *
import ast
import string
import memhub
# ============================================================
# StubGen.Converter - pyi 存根文件生成器
#
# 从 AST 直接生成 .pyi 存根文件,不依赖 CPython 的 PythonToStubConverter。
# 遍历 Module 节点的 children提取函数签名、类定义、变量声明、
# import 语句,生成 Python 类型存根文件。
#
# 本文件使用 Viper 语法实现,可被 TPC 编译为原生码。
# ============================================================
# ============================================================
# _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
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")
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:
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
# 未知类型,回退到 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"""
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:
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"""
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)
# 基类
if cls.bases is not None:
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:
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)
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
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 生成 .pyi 存根文件
#
# 遍历 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 生成 .pyi 存根文件内容到 buf"""
if tree is None or buf is None or buf_size == 0:
return 0
pos: t.CSizeT = 0
# 文件头
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
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:
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:
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:
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)
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