Files
Viper_Wiki/10-imports.md
T
2026-10-13 15:36:42 +08:00

11 KiB
Raw Blame History

10 - 模块与导入系统

Viper 的模块系统基于 Python 的 import 语法,但编译时通过 TransPyC 的符号表和类型系统实现跨模块的类型解析。

注:extern/export/define 关键字属语言语法(includes/ast parser 中的软关键字;define 为宏函数),而非 t 模块的类型注解。

导入语法

标准导入

import t, c                              # Viper 内置类型和操作模块
import asm                               # 汇编辅助模块
import string                            # 字符串操作模块

模块导入

import bootinfo
import intr
import platform.pch as pch
import drivers.serial.uart.serial as serial

from 导入

from stdint import *                     # 导入所有 stdint 类型别名
from drivers.video.vesafb.vga import _VGAScreenDriver

相对导入

from . import submodule
from .. import parent_module

特殊模块

t 模块

类型定义模块,提供所有 C 类型对应的 Viper 类型。每个 Viper 文件通常都需要导入:

import t

c 模块

C 语言操作模块,提供指针操作、内联汇编、预处理指令等:

import c

asm 模块

汇编辅助模块,提供常用汇编指令的封装:

import asm

asm.sti()          # __asm__ __volatile__("sti");
asm.hlt()          # __asm__ __volatile__("hlt");
asm.cli()          # __asm__ __volatile__("cli");
asm.nop()          # __asm__ __volatile__("nop");
asm.BSSClean()     # 清零 BSS 段

string 模块

字符串/内存操作模块,对应 C 标准库的 string.h:

确认:string 模块确实使用 Viper __asm__ 内联汇编进行加速(memset/memcpy/memmove 等,见 includes/string.vp)。因使用 x86_64 专有汇编,某些函数在其它架构可能需要手动重新编写。

import string

string.memset(&buf, 0, 64)               # memset(&buf, 0, 64);
string.memcpy(dst, src, size)            # memcpy(dst, src, size);
string.memmove(dst, src, size)           # memmove(dst, src, size);
string.memcmp(a, b, size)                # memcmp(a, b, size);
string.strcmp(a, b)                      # strcmp(a, b);
string.strcpy(dst, src)                  # strcpy(dst, src);

stdint 模块

提供 C stdint.h 中的类型别名:

from stdint import *

# 提供以下类型别名:
# UINT8PTR, UINT16PTR, UINT32PTR, UINT64PTR
# INT8PTR, INT16PTR, INT32PTR, INT64PTR
# 等等

viperlib 模块

ViperOS 标准库,提供常用 C 库函数:

import viperlib

viperlib.snprintf(&buf, 64, "value=%d", 42)    # snprintf(&buf, 64, "value=%d", 42);

ast 模块(自研解析器)

includes/ast 是完全自研的 Python 解析器(词法器 + 语法器 + AST 构造,现行 9 个文件约 8K 行;"约 4176 LOC"为 2026-06 历史口径),覆盖 CPython 几乎全部语法。这是向 ViperC 完全自举的关键组件——自举后编译器不再依赖 Python 运行时解析源文件。

from ast import parse, parse_expression

root: AST* = parse(src, pool)              # 解析整个源文件
expr: AST* = parse_expression(src, pool)   # 解析单个表达式

AST 采用胖节点设计:70+ 变体共用一个 struct,用 vtype(ASTVType 枚举)区分类型,通用字段直接属性访问。详见 13-bootstrapping.md 和 Test/AstTest。

llvmlite 模块(自研 IR 生成库)

includes/llvmlite 是完全自研的 LLVM IR 文本生成库(现行约 5.9K 行;"约 1500 LOC"为 2026-06 历史口径),封装类型/值/模块/函数/基本块/构造器的创建与打印。同样是自举的关键组件。

from llvmlite import LLVMTypeCore, LLVMModuleCore

type_core: LLVMTypeCore = LLVMTypeCore(pool)
i32: LLVMType* = type_core.Int32()
ptr_ty: LLVMType* = type_core.Ptr(i32)

mod_core: LLVMModuleCore = LLVMModuleCore(pool)
mod: Module* = mod_core.NewModule("main")
func: Function* = mod_core.CreateFunction(mod, "main", ret_ty)

类型系统用 t.REnum(LLVMType,少变体适合 match),指令 opcode 用 t.CEnum(IROp),对象分配用普通类 + mpool。详见 includes/llvmlite/README.md 的选型论证。

linkedlist 模块(novirtual + PEP 695 泛型示范)

includes/linkedlist 提供链表实现,是 novirtual 非多态继承和 PEP 695 递归泛型继承的典范用例:

from linkedlist import LinkedNode, GSList

novirtual class Node(LinkedNode):     # 非多态继承,零 vtable 开销
    value: int

lst: GSList[Node]* = &GSList[Node]()
node: Node* = &Node()
node.value = 42
lst.append(node)              # 类型安全:append 只接受 Node

包含 LinkedNode/SListNode(双向/单向节点)和 GSList[T](泛型链表,支持递归泛型继承 class GNode(GSListNode[GNode]))。详见 05-classes.md 的泛型类章节 和 06-oop.md 的 novirtual 章节。

w32 模块(Win32 FFI 绑定)

includes/w32 提供 Win32 API 绑定,全部用 extern def 软关键字声明(解析器内部改写为 t.State 标记):

from w32.win32file import CreateFileA, ReadFile, WriteFile
from w32.win32console import WriteConsole
from w32.win32memory import HeapAlloc, HeapFree
from w32.win32process import CreateProcess
from w32.win32sync import CreateMutex, WaitForSingleObject

handle: HANDLE = CreateFileA("test.txt", GENERIC_READ, FILE_SHARE_READ,
                             None, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, None)

子模块:win32base(基础类型/常量)、win32file(文件 IO)、win32console(控制台)、win32memory(内存)、win32process(进程/线程)、win32sync(同步对象)、fileio(高层文件操作封装)。详见 08-c-operations.md 的 FFI 章节。

coro/stdio/stdlib/memhub/testcheck 等内置模块

特殊模块清单末尾补充:

  • coro:协程运行时,提供 coro_run/coro_sleep/coro_destroy/coro_exception/coro_exception_msg/coro_gather
  • stdio / stdlib:C 标准库风格的 IO 与通用库函数
  • memhub:内存管理中枢
  • testcheck:测试辅助(断言/检查用例);使用 assert 时自动导入(见 07-control-flow.md 的 assert 语句)

模块解析流程

编译时模块加载

Viper 的 import 是编译时操作,不涉及运行时模块加载。完整的模块解析流程与两阶段编译紧密耦合:

  1. 阶段一:从入口文件出发,通过 import 语句递归发现所有可达的源文件
  2. 阶段一:对每个源文件生成 <SHA1>.vpi 签名存根和 <SHA1>.stub.ll LLVM IR 声明
  3. 阶段二:加载所有 .vpi 和 .stub.ll,构建共享符号表
  4. 阶段二:翻译源文件时,通过符号表解析跨模块的类型和函数引用
  5. 阶段二:将被引用模块的 .stub.ll 声明嵌入到生成的 .ll 文件头部

注:阶段一主要产出 .vpi;.stub.ll/.text.ll 由阶段二 Phase A 写盘。.vpi/.stub 按需加载而非全量;共享符号表为内存 StubMergerStore;_sha1_map.txt 仅人类可读(映射本体在内存 Sha1Store)。

类型存根文件(.vpi)

.vpi 文件是模块的声明接口,由 PythonToStubConverter 在阶段一生成。VPC 自举路径使用自研 StubGen/Converter.vp(从 AST 直接生成,不依赖 CPython 的 PythonToStubConverter)。存根文件的内容规则:

元素 存根表示
t.CDefine 常量 保留完整定义(含赋值)
t.CDefine 函数 保留完整函数体
普通函数 仅保留签名:def foo(...) -> T: pass
全局变量 TPC 一代 PythonToStubConverter 会添加 t.CExtern;VPC 自研 StubGen/Converter.vp 不再添加(跨模块 external declare 由 VpiReader 依 vpi 全局自动注入)
类定义 保留成员类型注解和方法签名

SHA1 命名空间与导入

每个模块的函数和结构体在 LLVM IR 中自动加上 SHA1 前缀,消除跨模块符号冲突。SHA1 取文件内容 sha1 摘要的前 8 字节(16 个十六进制字符)。豁免(保留裸名)的符号:t.CExport / t.CExtern / t.State 标记的函数、以及 main。详见 README.MD 中 SHA1 命名空间机制。

跨模块约束

  • static 不得跨模块:static 符号(函数 / 全局变量)仅限定义模块内可见,mod.x 取值、mod.f() 调用、from mod import x 三种形态都会编译期 NameError fail-fast。
  • extern 全局:extern NAME: T 生成裸名 @NAME = external global T,跨模块引用侧由 vpi 注入同名 external 声明。
  • extern 重名的解析顺序(临时规则,待修正):当多个模块声明了同名 extern 符号时,调用点按依赖 / 导入顺序取「最近」的那个实体。顺序不同可能解析到不同实现 ⇒ 当前请避免多模块重复声明同名 extern;该行为是临时约定,日后会修正为可显式区分来源的机制。
  • 循环 import 防护:翻译阶段禁止把模块自己的 vpi 反向注入(循环 import 会在同模块制造同名 declare);本文件自己声明的 extern 裸名优先于依赖 vpi 注入的同名声明。
  • 跨模块调用的 declare 形参以定义侧为准:调用点按 vpi 定义侧形参注解对实参 coerce,保证 call / declare / define 三者类型一致。

符号表

所有模块的类型信息统一存储在 SymbolTable 中,包含:

  • 结构体/联合体/枚举定义
  • 函数签名
  • 全局变量
  • typedef 别名
  • t.CDefine 常量

跨模块类型引用

# 在 fat32_types.vp 中定义
class fat32_fileinfo:
    fname: [13]char
    attr: uint8
    file_size: uint32

# 在其他模块中引用
import fat32_types
info: fat32_types.fat32_fileinfo

跨模块引用的结构体在 LLVM IR 中使用 SHA1 前缀的类型名:

%"<SHA1_of_fat32_types>.fat32_fileinfo" = type { [13 x i8], i8, i32 }

项目 includes 配置

在 project.json 中配置模块搜索路径:

{
    "includes": ["../.."],
    "source_dir": "./Kernel"
}

编译器会在 includes 指定的目录中搜索导入的模块。