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