263 lines
11 KiB
Markdown
263 lines
11 KiB
Markdown
# 10 - 模块与导入系统
|
||
|
||
Viper 的模块系统基于 Python 的 `import` 语法,但编译时通过 TransPyC 的符号表和类型系统实现跨模块的类型解析。
|
||
|
||
> 注:`extern`/`export`/`define` 关键字属语言语法([includes/ast](../includes/ast) parser 中的软关键字;`define` 为宏函数),而非 `t` 模块的类型注解。
|
||
|
||
## 导入语法
|
||
|
||
### 标准导入
|
||
|
||
```python
|
||
import t, c # Viper 内置类型和操作模块
|
||
import asm # 汇编辅助模块
|
||
import string # 字符串操作模块
|
||
```
|
||
|
||
### 模块导入
|
||
|
||
```python
|
||
import bootinfo
|
||
import intr
|
||
import platform.pch as pch
|
||
import drivers.serial.uart.serial as serial
|
||
```
|
||
|
||
### from 导入
|
||
|
||
```python
|
||
from stdint import * # 导入所有 stdint 类型别名
|
||
from drivers.video.vesafb.vga import _VGAScreenDriver
|
||
```
|
||
|
||
### 相对导入
|
||
|
||
```python
|
||
from . import submodule
|
||
from .. import parent_module
|
||
```
|
||
|
||
## 特殊模块
|
||
|
||
### `t` 模块
|
||
|
||
类型定义模块,提供所有 C 类型对应的 Viper 类型。每个 Viper 文件通常都需要导入:
|
||
|
||
```python
|
||
import t
|
||
```
|
||
|
||
### `c` 模块
|
||
|
||
C 语言操作模块,提供指针操作、内联汇编、预处理指令等:
|
||
|
||
```python
|
||
import c
|
||
```
|
||
|
||
### `asm` 模块
|
||
|
||
汇编辅助模块,提供常用汇编指令的封装:
|
||
|
||
```python
|
||
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](../includes/string.vp))。因使用 x86_64 专有汇编,某些函数在其它架构可能需要手动重新编写。
|
||
|
||
```python
|
||
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` 中的类型别名:
|
||
|
||
```python
|
||
from stdint import *
|
||
|
||
# 提供以下类型别名:
|
||
# UINT8PTR, UINT16PTR, UINT32PTR, UINT64PTR
|
||
# INT8PTR, INT16PTR, INT32PTR, INT64PTR
|
||
# 等等
|
||
```
|
||
|
||
### `viperlib` 模块
|
||
|
||
ViperOS 标准库,提供常用 C 库函数:
|
||
|
||
```python
|
||
import viperlib
|
||
|
||
viperlib.snprintf(&buf, 64, "value=%d", 42) # snprintf(&buf, 64, "value=%d", 42);
|
||
```
|
||
|
||
### `ast` 模块(自研解析器)
|
||
|
||
[includes/ast](../includes/ast) 是**完全自研的 Python 解析器**(词法器 + 语法器 + AST 构造,现行 9 个文件约 8K 行;"约 4176 LOC"为 2026-06 历史口径),覆盖 CPython 几乎全部语法。这是向 `ViperC` 完全自举的关键组件——自举后编译器不再依赖 Python 运行时解析源文件。
|
||
|
||
```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](13-bootstrapping.md) 和 [Test/AstTest](../Test/AstTest)。
|
||
|
||
### `llvmlite` 模块(自研 IR 生成库)
|
||
|
||
[includes/llvmlite](../includes/llvmlite) 是**完全自研的 LLVM IR 文本生成库**(现行约 5.9K 行;"约 1500 LOC"为 2026-06 历史口径),封装类型/值/模块/函数/基本块/构造器的创建与打印。同样是自举的关键组件。
|
||
|
||
```python
|
||
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](../includes/llvmlite/README.md) 的选型论证。
|
||
|
||
### `linkedlist` 模块(novirtual + PEP 695 泛型示范)
|
||
|
||
[includes/linkedlist](../includes/linkedlist.vp) 提供链表实现,是 `novirtual` 非多态继承和 PEP 695 递归泛型继承的典范用例:
|
||
|
||
```python
|
||
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 的泛型类章节](05-classes.md#泛型类pep-695-语法) 和 [06-oop.md 的 novirtual 章节](06-oop.md#novirtual-非多态继承)。
|
||
|
||
### `w32` 模块(Win32 FFI 绑定)
|
||
|
||
[includes/w32](../includes/w32) 提供 Win32 API 绑定,全部用 `extern def` 软关键字声明(解析器内部改写为 `t.State` 标记):
|
||
|
||
```python
|
||
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 章节](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 语句](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 命名空间机制](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` 常量
|
||
|
||
### 跨模块类型引用
|
||
|
||
```python
|
||
# 在 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 前缀的类型名:
|
||
```llvm
|
||
%"<SHA1_of_fat32_types>.fat32_fileinfo" = type { [13 x i8], i8, i32 }
|
||
```
|
||
|
||
## 项目 includes 配置
|
||
|
||
在 `project.json` 中配置模块搜索路径:
|
||
|
||
```json
|
||
{
|
||
"includes": ["../.."],
|
||
"source_dir": "./Kernel"
|
||
}
|
||
```
|
||
|
||
编译器会在 `includes` 指定的目录中搜索导入的模块。
|