snapshot before regression test
This commit is contained in:
357
wiki/01-overview.md
Normal file
357
wiki/01-overview.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# 01 - 语言概述与编译流程
|
||||
|
||||
## 语言定位
|
||||
|
||||
`Viper` 是一种 **系统级编程语言**,其语法基于 `Python`,但编译目标为 `LLVM IR`,最终生成原生机器码。`Viper` 不是 `Python` 的超集或子集,也不是"`C` 的语法糖"——它是一门拥有独立类型系统、独立编译模型、独立模块体系的语言。`Viper` 选择 `Python` 语法作为表达形式,是因为 `Python` 是人类公认的可读性最高的语言。
|
||||
|
||||
当前编译器 `TransPyC`(用 Python 实现)使用 Python 标准库 `ast` 解析源文件,将精力集中在语义翻译而非词法/语法分析上。但项目已在 [includes/ast](../includes/ast) 中实现**完全自研的 Python 解析器**(词法器 + 语法器 + AST 构造,约 4176 LOC,`AstTest` 综合测试通过),覆盖 CPython 几乎全部语法。这是向 `TransPyV`(用 Viper 重写编译器自身)完全自举的关键准备——自举后编译器将脱离 Python 运行时,解析、类型检查、IR 生成都由原生机器码完成。详见 [13-bootstrapping.md](13-bootstrapping.md)。
|
||||
|
||||
而实际上,下文中所有提到的 `C` 语言都是为了便于使用 `C` 的底层开发者理解。由于历史原因,在旧版本中我们使用 `C` 来作为中间语言,如同旧版本的 `C++` 一样,但现在 `target="c"` 的路径已经完全删除,`C` 路径永久不再受维护。
|
||||
|
||||
### 核心设计决策
|
||||
|
||||
1. **Python 语法,LLVM 语义**:源文件是完全合法的 `Python` 语法,但通过 `t` 模块的类型注解系统赋予完全不同的语义 —— `t.CInt` 不是 `Python` 的 `int`,而是 `LLVM` 的 `i32`
|
||||
2. **类型注解即编译指令**:Viper 的类型注解不是可选的提示,而是编译器生成 `LLVM IR` 的决定性依据。`x: t.CInt` 生成 `i32`,`x: t.CInt | t.CPtr` 生成 `i32*`,但无注解时会自动推导,详见下文。
|
||||
3. **两阶段编译**:先从源文件提取声明接口(`.pyi` + `.stub.ll`),再使用声明接口翻译源文件为含代码的 `.ll`。这是 `Viper` 区别于简单"`Python` 转 `C`"工具的关键架构
|
||||
4. **`SHA1` 命名空间**:每个源文件按内容 `SHA1` 哈希命名,非导出函数和结构体自动加上 `SHA1` 前缀,从根本上消除跨模块的符号冲突
|
||||
5. **万物皆数据**:为便于底层开发,实际上所有的变量和类型一般并不适用于鸭子类型,但在语义层面我们会尽可能贴近鸭子类型。
|
||||
|
||||
### 与 `Python` 的关系
|
||||
|
||||
| 特性 | `Python` | `Viper` |
|
||||
|------|--------|-------|
|
||||
| 类型系统 | 动态类型 | 静态类型(通过 `t` 模块注解,编译时确定) |
|
||||
| 内存管理 | `GC` 自动回收 | 手动管理,无 `GC`,无运行时 |
|
||||
| 运行时 | 解释执行 + 字节码 | 编译为原生机器码(通过 `LLVM`) |
|
||||
| 对象模型 | 万物皆对象 | `@t.Object` 启用 OOP,`@t.CVTable` 为多态(vtable),`@t.NoVTable` 为非多态继承(C++ 风格,零运行时开销),普通 `class` 仅为结构体布局 |
|
||||
| 标准库 | `Python` 标准库 | `Viper` 标准库(`includes/`)+ `ViperOS SDK` |
|
||||
| 空值 | `None` | `None`(编译为 `NULL`/零值) |
|
||||
| 模块系统 | 运行时 `import` | 编译时解析,插入 `.stub.ll`,`SHA1` 命名空间隔离 |
|
||||
|
||||
### 与 C 的关系
|
||||
|
||||
Viper 编译后的代码在底层等价于 `C` 编译后的机器码(都经过 LLVM),但 Viper 在语言层面提供了 C 所没有的能力:
|
||||
|
||||
- **SHA1 命名空间**:自动消除符号冲突,无需手动管理 `static`/命名前缀
|
||||
- **类型组合语法**:`t.CConst | t.CInt | t.CPtr` 比 `const int*` 更具组合性
|
||||
- **声明式内联汇编**:`c.Asm(f"mov {c.AsmOut(x, t.ASM_DESCR.OUTPUT_REG)}, rdi")` 比裸 `__asm__` 更安全,也更具可读性。
|
||||
- **结构化预处理**:`c.CIfdef`/`c.CEndif()` 替代 `#ifdef`/`#endif`。
|
||||
- **面向对象**:`@t.Object` + `@t.CVTable` 提供 `vtable` 支持的多态 `OOP`;`@t.NoVTable` 提供 C++ 风格的非多态继承(零 vtable 开销);PEP 695 泛型 + 递归泛型继承实现强类型自引用结构
|
||||
- **存根驱动的模块系统**:`.pyi` 存根文件实现跨模块类型解析,无需头文件
|
||||
- **更多内容**:额外更多的不依赖操作系统的函数和语法
|
||||
|
||||
## 两阶段编译流程
|
||||
|
||||
Viper 的编译由 `Projectrans.py` 驱动,分为两个阶段。这是 Viper 编译模型的核心,理解两阶段编译是理解 `t.CDefine`、`t.CExport`、`t.CInline` 等关键概念的前提。
|
||||
|
||||
```
|
||||
源文件 (.py) ──────────────────────────────────────────────────────
|
||||
│ │
|
||||
│ ┌─────────────── 阶段一:声明提取 ───────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ 1. 计算源文件 SHA1 │ │
|
||||
│ │ 2. 生成 <SHA1>.pyi(签名存根) │ │
|
||||
│ │ 3. 构建结构体注册表 │ │
|
||||
│ │ 4. 生成 <SHA1>.stub.ll(LLVM IR 声明) │ │
|
||||
│ │ │ │
|
||||
│ └────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────── 阶段二:代码翻译 ───────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ 1. 加载所有 .pyi 和 .stub.ll │ │
|
||||
│ │ 2. 构建共享符号表 │ │
|
||||
│ │ 3. 收集内联函数符号 │ │
|
||||
│ │ 4. 翻译源文件 → <SHA1>.ll(含代码) │ │
|
||||
│ │ 5. llc 编译 → <SHA1>.o(目标文件) │ │
|
||||
│ │ 6. ld.lld 链接 → 可执行文件 │ │
|
||||
│ │ │ │
|
||||
│ └────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 阶段一:声明提取(Phase1Generator)
|
||||
|
||||
阶段一的目标是从每个源文件提取**声明接口**,使其他模块在阶段二翻译时能够正确解析跨模块引用。
|
||||
|
||||
#### 步骤 1:可达文件发现与拓扑排序
|
||||
|
||||
从入口文件(`main.py` 或 `project.json` 指定)出发,通过 `import` 语句递归遍历,找出所有可达的 `.py` 源文件。然后对文件进行拓扑排序,确保被依赖的模块先被处理。
|
||||
|
||||
```
|
||||
main.py → import serial → import drivers.serial.uart.serial → ...
|
||||
```
|
||||
|
||||
#### 步骤 2:生成 `.pyi` 签名存根
|
||||
|
||||
对每个源文件,计算其内容的 SHA1 哈希(16位),然后调用 `PythonToStubConverter` 生成签名存根文件 `<SHA1>.pyi`。
|
||||
|
||||
**SHA1 计算方式**:
|
||||
```python
|
||||
sha1 = hashlib.sha1(content.encode('utf-8')).hexdigest()[:16]
|
||||
```
|
||||
|
||||
**存根文件的内容规则**:
|
||||
|
||||
| 源文件元素 | 存根文件中的表示 |
|
||||
|-----------|----------------|
|
||||
| `t.CDefine` 常量 | 保留原样(含赋值):`MAX_SIZE: t.CDefine = 1024` |
|
||||
| `t.CDefine` 函数(返回 `t.CDefine` 的函数) | 保留完整函数体(宏展开需要) |
|
||||
| 普通函数 | 仅保留签名,添加 `| c.State`:`def foo(x: t.CInt) -> t.CVoid | c.State: pass` |
|
||||
| 类定义 | 保留成员类型注解和方法签名 |
|
||||
| 全局变量 | 添加 `t.CExtern`:`x: t.CExtern | t.CInt` |
|
||||
| `c.CIfdef`/`c.CEndif()` 等预处理指令 | 保留原样 |
|
||||
| `import` 语句 | 保留原样 |
|
||||
|
||||
**关键设计**:`t.CDefine` 常量和 `t.CDefine` 函数在存根中**保留完整定义**(包括赋值和函数体),因为它们是编译时常量/宏,其他模块引用时需要完整展开。普通函数只保留签名并标记 `c.State`(仅声明),因为函数体在阶段二生成。
|
||||
|
||||
|
||||
#### 步骤 3:构建结构体注册表
|
||||
|
||||
扫描所有已生成的 `.pyi` 文件,提取:
|
||||
- **结构体名称集合**:所有非枚举、非异常的 `class` 定义
|
||||
- **枚举名称集合**:继承 `t.CEnum` 的类
|
||||
- **异常名称集合**:继承 `Exception` 的类
|
||||
- **结构体→SHA1 映射**:记录每个结构体定义在哪个模块中
|
||||
|
||||
这个注册表用于阶段一的声明生成和阶段二的类型解析,确保跨模块的结构体引用使用正确的 SHA1 命名空间。
|
||||
|
||||
#### 步骤 4:生成 `.stub.ll` LLVM IR 声明
|
||||
|
||||
对每个 `.pyi` 文件,由 `DeclarationGenerator` 生成对应的 LLVM IR 声明文件 `<SHA1>.stub.ll`。
|
||||
|
||||
**声明生成规则**:
|
||||
|
||||
| 元素 | 生成的 LLVM IR |
|
||||
|------|---------------|
|
||||
| 普通函数 | `declare void @"<SHA1>.foo"(i32)` — 非 CExport 函数加 SHA1 前缀 |
|
||||
| CExport 函数 | `declare i32 @main()` — CExport 函数不加前缀,全局可见 |
|
||||
| 结构体 | `%"<SHA1>.ClassName" = type { i32, i32* }` — 类型名加 SHA1 前缀 |
|
||||
| 全局变量 | `@varname = external global i32` |
|
||||
| `t.CDefine` 常量 | **不生成声明**(编译时常量,直接内联展开) |
|
||||
| `t.CTypedef` 别名 | **不生成声明**(类型别名,在类型解析时展开) |
|
||||
| 枚举 | 为每个枚举成员生成 `@__config_EnumName_member = external global i32` |
|
||||
|
||||
**增量编译**:如果 `<SHA1>.pyi` 或 `<SHA1>.stub.ll` 已存在,则跳过生成(缓存命中)。只有源文件内容变化导致 SHA1 变化时才重新生成。
|
||||
|
||||
### 阶段二:代码翻译(Phase2Translator)
|
||||
|
||||
阶段二使用阶段一生成的声明接口,将源文件翻译为包含完整代码的 LLVM IR。
|
||||
|
||||
#### 步骤 1:加载声明接口
|
||||
|
||||
加载 `temp/` 目录中所有的 `.pyi` 和 `.stub.ll` 文件,构建:
|
||||
- `sig_files`:SHA1 → `.pyi` 文件路径映射
|
||||
- `stub_files`:SHA1 → `.stub.ll` 文件路径映射
|
||||
- `sha1_map`:SHA1 → 源文件相对路径映射
|
||||
|
||||
#### 步骤 2:构建共享符号表
|
||||
|
||||
一次性构建所有文件共享的符号表数据,避免每个文件重复加载。将所有 `.pyi` 存根和 `.stub.ll` 声明中的类型信息注册到统一的 `SymbolTable` 中。
|
||||
|
||||
#### 步骤 3:收集内联函数符号
|
||||
|
||||
扫描 `includes/` 目录中被引用的模块,收集标记为 `t.CInline` 的函数。内联函数的完整 AST body 被保存,在翻译调用点时直接展开。值得注意的是,和 C 语言不同,`t.CInline` 不是优化建议,而是强制性的。
|
||||
|
||||
#### 步骤 4:翻译源文件
|
||||
|
||||
对每个源文件,使用 `TransPyC` 翻译器将 Python AST 翻译为 LLVM IR:
|
||||
1. 将对应模块的 `.stub.ll` 声明嵌入到生成的 `.ll` 文件头部
|
||||
2. 所有被引用模块的 `.stub.ll` 声明也被嵌入
|
||||
3. 翻译 AST 节点为 LLVM IR 指令
|
||||
4. 输出 `<SHA1>.ll` 文件
|
||||
|
||||
#### 步骤 5:编译与链接
|
||||
|
||||
- `llc`:将每个 `.ll` 编译为 `.o` 目标文件
|
||||
- `ld.lld`:将所有 `.o` 链接为最终可执行文件
|
||||
|
||||
## SHA1 命名空间机制
|
||||
|
||||
SHA1 命名空间是 Viper 解决跨模块符号冲突的核心机制。
|
||||
|
||||
### 问题
|
||||
|
||||
在 C 语言中,多个模块可能定义同名函数或结构体,导致链接时符号冲突。C 的解决方案是 `static`(限制可见性)和命名前缀(手动避免冲突)。
|
||||
|
||||
### Viper 的解决方案
|
||||
|
||||
Viper 使用源文件内容的 SHA1 哈希作为命名空间前缀:
|
||||
|
||||
```
|
||||
源文件 A.py(SHA1 = a1b2c3d4e5f6g7h8)中定义:
|
||||
class Point: x: t.CInt; y: t.CInt
|
||||
def draw(p: Point) -> t.CVoid: ...
|
||||
|
||||
源文件 B.py(SHA1 = i9j0k1l2m3n4o5p6)中也定义:
|
||||
class Point: x: t.CFloat; y: t.CFloat # 不同的结构体!
|
||||
def draw(p: Point) -> t.CVoid: ...
|
||||
|
||||
编译后:
|
||||
A.py 的结构体 → %"a1b2c3d4e5f6g7h8.Point" = type { i32, i32 }
|
||||
A.py 的函数 → declare void @"a1b2c3d4e5f6g7h8.draw"(%"a1b2c3d4e5f6g7h8.Point"*)
|
||||
|
||||
B.py 的结构体 → %"i9j0k1l2m3n4o5p6.Point" = type { float, float }
|
||||
B.py 的函数 → declare void @"i9j0k1l2m3n4o5p6.draw"(%"i9j0k1l2m3n4o5p6.Point"*)
|
||||
```
|
||||
|
||||
**没有符号冲突**——即使两个模块定义了同名类型和函数,它们的 LLVM IR 符号也是不同的。即便有两个内容完全相同的文件也不会冲突,他们会被识别为同一个文件,然后进行单次编译。
|
||||
|
||||
### SHA1 前缀的豁免:`t.CExport`
|
||||
|
||||
标记为 `t.CExport` 的函数**不加 SHA1 前缀**,保持原始函数名。这是模块向外部暴露 API/ABI 的机制:
|
||||
|
||||
```python
|
||||
# main.py — 入口函数,必须全局可见
|
||||
def main() -> t.CInt | t.CExport:
|
||||
return 0
|
||||
|
||||
# 编译后:define i32 @main() — 无 SHA1 前缀
|
||||
```
|
||||
|
||||
```python
|
||||
# serial.py — 驱动接口,对外暴露
|
||||
def init() -> t.CVoid | t.CExport:
|
||||
serial_puts("init\n")
|
||||
|
||||
# 编译后:define void @init() — 无 SHA1 前缀,其他模块可直接调用
|
||||
```
|
||||
|
||||
### SHA1 与增量编译
|
||||
|
||||
SHA1 同时服务于增量编译:
|
||||
- 源文件内容不变 → SHA1 不变 → `.pyi` 和 `.stub.ll` 缓存命中,跳过阶段一
|
||||
- 源文件内容变化 → SHA1 变化 → 重新生成声明接口
|
||||
- `temp/_sha1_map.txt` 记录 SHA1 → 源文件路径的映射,供阶段二加载
|
||||
|
||||
理论上,如果一个未经变动的模块的依赖路径上存在变动的模块,暂时的方法是将其简单做 SHA1 替换,暨将旧 SHA1 替换为新 SHA1,但不适用于签名改变的情况,不过签名改变这个未经变动的模块必然也需要改变,但是由于宏展开的存在,不能完全保证不会发生错误情况。且依赖路径的 SHA1 替换成功性未经完整测试,可能出现未定义行为。
|
||||
|
||||
## `t.CDefine` 深度解析
|
||||
|
||||
`t.CDefine` 是 Viper 中最特殊的类型——它不是数据类型,而是**编译时元指令**,控制编译器的代码生成行为。
|
||||
|
||||
### `t.CDefine` 常量
|
||||
|
||||
```python
|
||||
MAX_SIZE: t.CDefine = 1024
|
||||
PAGE_SIZE: t.CDefine = 4096
|
||||
FA_READ: t.CDefine = 0x01
|
||||
CPU_FEATURE_FPU: t.CDefine = (1 << 0)
|
||||
```
|
||||
|
||||
**编译行为**:
|
||||
1. **阶段一**:存根生成器保留 `t.CDefine` 常量的完整定义(包括赋值值),因为其他模块可能引用此常量
|
||||
2. **阶段一**:`.stub.ll` 声明生成器**跳过** `t.CDefine` 常量,不生成 `external global` 声明
|
||||
3. **阶段二**:翻译器遇到 `t.CDefine` 常量的引用时,直接内联其值,不生成任何加载指令
|
||||
|
||||
这意味着 `t.CDefine` 常量在 LLVM IR 层面**不存在**——它们在编译时被完全展开,等价于 C 的 `#define` 宏。
|
||||
|
||||
### `t.CDefine` 函数
|
||||
|
||||
当函数的返回类型注解包含 `t.CDefine` 时,该函数被编译器视为**宏函数**:
|
||||
|
||||
```python
|
||||
def MAKE_FLAG(bit) -> t.CDefine:
|
||||
return (1 << bit)
|
||||
|
||||
def GET_PAGE_ORDER(size) -> t.CDefine:
|
||||
return size // PAGE_SIZE
|
||||
```
|
||||
|
||||
**编译行为**:
|
||||
1. **阶段一**:存根生成器保留 `t.CDefine` 函数的**完整函数体**(不像普通函数那样只保留签名)
|
||||
2. **阶段二**:翻译器遇到 `t.CDefine` 函数的调用时,将调用内联展开为函数体的计算结果
|
||||
|
||||
`t.CDefine` 函数在 LLVM IR 中**不生成函数定义**——它们是纯粹的编译时宏。
|
||||
|
||||
### `t.CDefine` 与类型组合
|
||||
|
||||
`t.CDefine` 可以与具体类型组合使用,为常量指定底层类型:
|
||||
|
||||
```python
|
||||
CODE_SEG: t.CDefine | t.CUInt16T = 0x08
|
||||
DATA_SEG: t.CDefine | t.CUInt16T = 0x10
|
||||
```
|
||||
|
||||
这表示常量在类型检查时被视为 `t.CUInt16T`(`uint16_t`),但在代码生成时仍然内联展开。
|
||||
|
||||
## `t.CExport` 深度解析
|
||||
|
||||
`t.CExport` 控制函数的符号可见性,是 SHA1 命名空间的豁免机制。
|
||||
|
||||
### 语义
|
||||
|
||||
| 修饰 | LLVM IR 函数名 | 链接可见性 | 用途 |
|
||||
|------|---------------|-----------|------|
|
||||
| 无修饰 | `@<SHA1>.funcname` | 模块内部 | 模块私有函数 |
|
||||
| `t.CExport` | `@funcname` | 全局可见 | 对外暴露的 API |
|
||||
| `t.CExtern` | `@funcname`(仅声明) | 外部定义 | 引用外部函数 |
|
||||
|
||||
### 使用场景
|
||||
|
||||
```python
|
||||
# 入口函数 — 必须全局可见,链接器需要找到它
|
||||
def _start() -> t.CInt | t.CExport:
|
||||
return kernel_main()
|
||||
|
||||
# 驱动接口 — 其他模块/应用需要调用
|
||||
def init() -> t.CVoid | t.CExport:
|
||||
uart_init()
|
||||
|
||||
# 内部辅助函数 — 不需要全局可见,自动加 SHA1 前缀
|
||||
def _helper() -> t.CInt:
|
||||
return 42
|
||||
```
|
||||
|
||||
### `t.CExport` 在存根中的表现
|
||||
|
||||
在 `.pyi` 存根文件中,`t.CExport` 函数与普通函数一样只保留签名(添加 `c.State`,表示单纯声明),但 `t.CExport` 标记被保留在返回类型注解中。阶段一的 `DeclarationGenerator` 检查 `t.CExport` 标记来决定是否添加 SHA1 前缀。
|
||||
|
||||
## `t.CInline` 深度解析
|
||||
|
||||
`t.CInline` 标记函数为内联函数,编译器在调用点直接展开函数体。
|
||||
|
||||
### 语义
|
||||
|
||||
```python
|
||||
def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline:
|
||||
return a + b
|
||||
```
|
||||
|
||||
**编译行为**:
|
||||
1. **阶段二预收集**:`_collect_inline_symbols` 扫描 `includes/` 目录中被引用的模块,收集所有 `t.CInline` 函数的 AST body
|
||||
2. **翻译时展开**:遇到内联函数调用时,将函数体的 AST 直接嵌入调用点,替换参数为实际值
|
||||
3. **不生成独立函数**:内联函数不生成 LLVM IR 函数定义(除非也被非内联调用)
|
||||
|
||||
### `t.CInline` 与 `t.CDefine` 函数的区别
|
||||
|
||||
| 特性 | `t.CInline` | `t.CDefine` 函数 |
|
||||
|------|------------|-----------------|
|
||||
| 展开时机 | 阶段二翻译时 | 阶段二翻译时 |
|
||||
| 类型检查 | 完整的参数和返回类型检查 | 返回类型为宏,类型检查较弱 |
|
||||
| 存根表示 | 签名 + `c.State` | 完整函数体 |
|
||||
| LLVM IR | 可能生成函数定义(如果有非内联调用) | 不生成函数定义 |
|
||||
| 适用场景 | 性能关键的短函数 | 编译时常量和宏计算 |
|
||||
|
||||
## 目标平台
|
||||
|
||||
默认目标三元组:`x86_64-none-elf`
|
||||
|
||||
数据布局:`e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128`
|
||||
|
||||
## Hello World 示例
|
||||
|
||||
```python
|
||||
import t, c
|
||||
|
||||
def main() -> t.CInt | t.CExport:
|
||||
print("Hello, ViperOS!\n")
|
||||
return 0
|
||||
```
|
||||
|
||||
编译流程:
|
||||
|
||||
1. **阶段一**:计算 `main.py` 的 SHA1,生成 `.pyi` 存根(`def main() -> t.CInt | t.CExport | c.State: pass`),生成 `.stub.ll` 声明(`declare i32 @main()`,因为是 CExport 不加前缀)
|
||||
2. **阶段二**:翻译 `main.py` 为 `<SHA1>.ll`,嵌入 `print` 对应的 `puts`/`printf` 声明,生成 `define i32 @main()` 函数体
|
||||
3. **编译**:`llc` 将 `.ll` 编译为 `.o`
|
||||
4. **链接**:`ld.lld` 链接为可执行文件
|
||||
665
wiki/02-type-system.md
Normal file
665
wiki/02-type-system.md
Normal file
@@ -0,0 +1,665 @@
|
||||
# 02 - 类型系统
|
||||
|
||||
Viper 的类型系统通过 `t` 模块提供,所有类型注解继承 `t.CType` 。大都以 `t.C{TypeName:PascalCase}`为类型,类型注解是 Viper 的核心机制,决定了变量的内存布局、大小和对齐方式。
|
||||
|
||||
## 基本类型
|
||||
|
||||
### 整数类型
|
||||
|
||||
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|
||||
|------------|--------|-----------|--------|
|
||||
| `t.CChar` | `char` | 8 | 是 |
|
||||
| `t.CShort` | `short` | 16 | 是 |
|
||||
| `t.CInt` | `int` | 32 | 是 |
|
||||
| `t.CLong` | `long` | 64 | 是 |
|
||||
| `t.CUnsignedChar` | `unsigned char` | 8 | 否 |
|
||||
| `t.CUnsignedShort` | `unsigned short` | 16 | 否 |
|
||||
| `t.CUnsigned` / `t.CUnsignedInt` | `unsigned int` | 32 | 否 |
|
||||
| `t.CUnsignedLong` | `unsigned long` | 64 | 否 |
|
||||
| `t.CSignedChar` | `signed char` | 8 | 是 |
|
||||
|
||||
值得注意的是,t.CUnsigned 一般不单独使用,而是配合其它有符号类型,替换表示无符号,比如 `t.CUnsigned | t.CInt`,由于为了便于使用一些常用类型,则将其组合为常用的 `t.CUnsignedInt` 等。
|
||||
|
||||
### 固定宽度整数类型
|
||||
|
||||
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|
||||
|------------|--------|-----------|--------|
|
||||
| `t.CInt8T` | `int8_t` | 8 | 是 |
|
||||
| `t.CInt16T` | `int16_t` | 16 | 是 |
|
||||
| `t.CInt32T` | `int32_t` | 32 | 是 |
|
||||
| `t.CInt64T` | `int64_t` | 64 | 是 |
|
||||
| `t.CUInt8T` | `uint8_t` | 8 | 否 |
|
||||
| `t.CUInt16T` | `uint16_t` | 16 | 否 |
|
||||
| `t.CUInt32T` | `uint32_t` | 32 | 否 |
|
||||
| `t.CUInt64T` | `uint64_t` | 64 | 否 |
|
||||
|
||||
### 最小宽度整数类型
|
||||
|
||||
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|
||||
|------------|--------|-----------|--------|
|
||||
| `t.CIntLeast8T` | `int_least8_t` | ≥8 | 是 |
|
||||
| `t.CIntLeast16T` | `int_least16_t` | ≥16 | 是 |
|
||||
| `t.CIntLeast32T` | `int_least32_t` | ≥32 | 是 |
|
||||
| `t.CIntLeast64T` | `int_least64_t` | ≥64 | 是 |
|
||||
| `t.CUIntLeast8T` | `uint_least8_t` | ≥8 | 否 |
|
||||
| `t.CUIntLeast16T` | `uint_least16_t` | ≥16 | 否 |
|
||||
| `t.CUIntLeast32T` | `uint_least32_t` | ≥32 | 否 |
|
||||
| `t.CUIntLeast64T` | `uint_least64_t` | ≥64 | 否 |
|
||||
|
||||
### 最快最小宽度整数类型
|
||||
|
||||
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|
||||
|------------|--------|-----------|--------|
|
||||
| `t.CIntFast8T` | `int_fast8_t` | ≥8 | 是 |
|
||||
| `t.CIntFast16T` | `int_fast16_t` | ≥16 | 是 |
|
||||
| `t.CIntFast32T` | `int_fast32_t` | ≥32 | 是 |
|
||||
| `t.CIntFast64T` | `int_fast64_t` | ≥64 | 是 |
|
||||
| `t.CUIntFast8T` | `uint_fast8_t` | ≥8 | 否 |
|
||||
| `t.CUIntFast16T` | `uint_fast16_t` | ≥16 | 否 |
|
||||
| `t.CUIntFast32T` | `uint_fast32_t` | ≥32 | 否 |
|
||||
| `t.CUIntFast64T` | `uint_fast64_t` | ≥64 | 否 |
|
||||
|
||||
### 最大宽度整数类型
|
||||
|
||||
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|
||||
|------------|--------|-----------|--------|
|
||||
| `t.CIntMaxT` | `intmax_t` | ≥64 | 是 |
|
||||
| `t.CUIntMaxT` | `uintmax_t` | ≥64 | 否 |
|
||||
|
||||
### stdint 短名别名
|
||||
|
||||
通过 `import stdint` 引入,这些短名是上述类型的 `t.CTypedef` 别名,便于在系统编程场景中快速使用。允许直接使用 `from stdint import *` 来直接使用。
|
||||
|
||||
#### 基本类型短名
|
||||
|
||||
| 短名 | 等价展开 | 说明 |
|
||||
|------|---------|------|
|
||||
| `stdint.INT` | `t.CInt` | - |
|
||||
| `stdint.UINT` | `t.CUnsignedInt` | - |
|
||||
| `stdint.BOOL` | `t.CInt` | 1 为真,0 为假 |
|
||||
| `stdint.SHORT` | `t.CShort` | - |
|
||||
| `stdint.USHORT` | `t.CUnsignedShort` | - |
|
||||
| `stdint.LONG` | `t.CLong` | - |
|
||||
| `stdint.ULONG` | `t.CUnsignedLong` | - |
|
||||
| `stdint.LONGLONG` | `t.CLong \| t.CLong` | - |
|
||||
| `stdint.ULONGLONG` | `t.CUnsignedLong \| t.CLong` | - |
|
||||
| `stdint.FLOAT` | `t.CFloat` | - |
|
||||
| `stdint.DOUBLE` | `t.CDouble` | - |
|
||||
|
||||
#### 固定宽度短名
|
||||
|
||||
| 短名 | 等价展开 |
|
||||
|------|---------|
|
||||
| `stdint.INT8` | `t.CInt8T` |
|
||||
| `stdint.INT16` | `t.CInt16T` |
|
||||
| `stdint.INT32` | `t.CInt32T` |
|
||||
| `stdint.INT64` | `t.CInt64T` |
|
||||
| `stdint.UINT8` | `t.CUInt8T` |
|
||||
| `stdint.UINT16` | `t.CUInt16T` |
|
||||
| `stdint.UINT32` | `t.CUInt32T` |
|
||||
| `stdint.UINT64` | `t.CUInt64T` |
|
||||
|
||||
#### 字节与字短名
|
||||
|
||||
| 短名 | 等价展开 | 说明 |
|
||||
|------|---------|------|
|
||||
| `stdint.BYTE` | `t.CUnsignedChar` | 8 位无符号 |
|
||||
| `stdint.WORD` | `t.CUInt16T` | 16 位无符号 |
|
||||
| `stdint.DWORD` | `t.CUInt32T` | 32 位无符号 |
|
||||
| `stdint.QWORD` | `t.CUInt64T` | 64 位无符号 |
|
||||
|
||||
#### 字符类型短名
|
||||
|
||||
| 短名 | 等价展开 | 说明 |
|
||||
|------|---------|------|
|
||||
| `stdint.TCHAR` | `t.CChar` | - |
|
||||
| `stdint.CHARLIST` | `str \| t.CPtr` / `list[str, None]` | - |
|
||||
| `stdint.WCHAR` | `stdint.WORD` | UTF-16 代码单元 |
|
||||
| `stdint.CHAR8` | `t.CChar8T` | - |
|
||||
| `stdint.CHAR16` | `t.CChar16T` | - |
|
||||
| `stdint.CHAR32` | `t.CChar32T` | - |
|
||||
|
||||
#### 指针短名
|
||||
|
||||
| 短名 | 等价展开 |
|
||||
|------|---------|
|
||||
| `stdint.INTPTR` | `t.CInt \| t.CPtr` |
|
||||
| `stdint.UINTPTR` | `t.CUnsignedInt \| t.CPtr` |
|
||||
| `stdint.BYTEPTR` | `stdint.BYTE \| t.CPtr` |
|
||||
| `stdint.SHORTPTR` | `t.CShort \| t.CPtr` |
|
||||
| `stdint.USHORTPTR` | `t.CUnsignedShort \| t.CPtr` |
|
||||
| `stdint.WCHARPTR` | `stdint.WORD \| t.CPtr` |
|
||||
| `stdint.VOIDPTR` | `t.CVoid \| t.CPtr` |
|
||||
| `stdint.INT8PTR` | `t.CInt8T \| t.CPtr` |
|
||||
| `stdint.INT16PTR` | `t.CInt16T \| t.CPtr` |
|
||||
| `stdint.INT32PTR` | `t.CInt32T \| t.CPtr` |
|
||||
| `stdint.INT64PTR` | `t.CInt64T \| t.CPtr` |
|
||||
| `stdint.UINT8PTR` | `t.CUInt8T \| t.CPtr` |
|
||||
| `stdint.UINT16PTR` | `t.CUInt16T \| t.CPtr` |
|
||||
| `stdint.UINT32PTR` | `t.CUInt32T \| t.CPtr` |
|
||||
| `stdint.UINT64PTR` | `t.CUInt64T \| t.CPtr` |
|
||||
| `stdint.CHAR8PTR` | `t.CChar8T \| t.CPtr` |
|
||||
| `stdint.CHAR16PTR` | `t.CChar16T \| t.CPtr` |
|
||||
| `stdint.CHAR32PTR` | `t.CChar32T \| t.CPtr` |
|
||||
|
||||
#### 平台特定短名
|
||||
|
||||
| 短名 | 等价展开 | 说明 |
|
||||
|------|---------|------|
|
||||
| `stdint.FSIZE_t` | `stdint.DWORD` | 文件大小 |
|
||||
| `stdint.LBA_t` | `stdint.DWORD` | 逻辑块地址 |
|
||||
|
||||
### 平台相关类型
|
||||
|
||||
| Viper 类型 | C 等价 | 大小(位) | 有符号 |
|
||||
|------------|--------|-----------|--------|
|
||||
| `t.CSizeT` | `size_t` | 64 | 否 |
|
||||
| `t.CIntPtrT` | `intptr_t` | 64 | 是 |
|
||||
| `t.CUIntPtrT` | `uintptr_t` | 64 | 否 |
|
||||
| `t.CPtrDiffT` | `ptrdiff_t` | 64 | 是 |
|
||||
|
||||
### stdint 短名别名
|
||||
|
||||
Viper 在 `stdint` 模块中提供了更简短的类型别名,通过 `t.CTypedef` 映射到对应的完整类型。使用时需 `import stdint`。
|
||||
|
||||
#### 固定宽度整数短名
|
||||
|
||||
| 短名 | 等价 Viper 类型 | C 等价 |
|
||||
|------|----------------|--------|
|
||||
| `INT8` | `t.CInt8T` | `int8_t` |
|
||||
| `INT16` | `t.CInt16T` | `int16_t` |
|
||||
| `INT32` | `t.CInt32T` | `int32_t` |
|
||||
| `INT64` | `t.CInt64T` | `int64_t` |
|
||||
| `UINT8` | `t.CUInt8T` | `uint8_t` |
|
||||
| `UINT16` | `t.CUInt16T` | `uint16_t` |
|
||||
| `UINT32` | `t.CUInt32T` | `uint32_t` |
|
||||
| `UINT64` | `t.CUInt64T` | `uint64_t` |
|
||||
|
||||
#### 固定宽度指针短名
|
||||
|
||||
| 短名 | 等价 Viper 类型 | C 等价 |
|
||||
|------|----------------|--------|
|
||||
| `INT8PTR` | `t.CInt8T \| t.CPtr` | `int8_t*` |
|
||||
| `INT16PTR` | `t.CInt16T \| t.CPtr` | `int16_t*` |
|
||||
| `INT32PTR` | `t.CInt32T \| t.CPtr` | `int32_t*` |
|
||||
| `INT64PTR` | `t.CInt64T \| t.CPtr` | `int64_t*` |
|
||||
| `UINT8PTR` | `t.CUInt8T \| t.CPtr` | `uint8_t*` |
|
||||
| `UINT16PTR` | `t.CUInt16T \| t.CPtr` | `uint16_t*` |
|
||||
| `UINT32PTR` | `t.CUInt32T \| t.CPtr` | `uint32_t*` |
|
||||
| `UINT64PTR` | `t.CUInt64T \| t.CPtr` | `uint64_t*` |
|
||||
|
||||
#### 字符类型短名
|
||||
|
||||
| 短名 | 等价 Viper 类型 | C 等价 |
|
||||
|------|----------------|--------|
|
||||
| `CHAR8` | `t.CChar8T` | `char8_t` |
|
||||
| `CHAR16` | `t.CChar16T` | `char16_t` |
|
||||
| `CHAR32` | `t.CChar32T` | `char32_t` |
|
||||
| `CHAR8PTR` | `t.CChar8T \| t.CPtr` | `char8_t*` |
|
||||
| `CHAR16PTR` | `t.CChar16T \| t.CPtr` | `char16_t*` |
|
||||
| `CHAR32PTR` | `t.CChar32T \| t.CPtr` | `char32_t*` |
|
||||
|
||||
#### 基本类型短名
|
||||
|
||||
| 短名 | 等价 Viper 类型 | C 等价 |
|
||||
|------|----------------|--------|
|
||||
| `INT` | `t.CInt` | `int` |
|
||||
| `UINT` | `t.CUnsignedInt` | `unsigned int` |
|
||||
| `BOOL` | `t.CInt` | `int`(0/1) |
|
||||
| `SHORT` | `t.CShort` | `short` |
|
||||
| `USHORT` | `t.CUnsignedShort` | `unsigned short` |
|
||||
| `LONG` | `t.CLong` | `long` |
|
||||
| `ULONG` | `t.CUnsignedLong` | `unsigned long` |
|
||||
| `LONGLONG` | `t.CLong \| t.CLong` | `long long` |
|
||||
| `ULONGLONG` | `t.CUnsignedLong \| t.CLong` | `unsigned long long` |
|
||||
| `FLOAT` | `t.CFloat` | `float` |
|
||||
| `DOUBLE` | `t.CDouble` | `double` |
|
||||
|
||||
#### 平台 / Windows 风格短名
|
||||
|
||||
| 短名 | 等价 Viper 类型 | C 等价 |
|
||||
|------|----------------|--------|
|
||||
| `BYTE` | `t.CUnsignedChar` | `unsigned char`(8位) |
|
||||
| `BYTEPTR` | `BYTE \| t.CPtr` | `unsigned char*` |
|
||||
| `WORD` | `t.CUInt16T` | `uint16_t`(16位) |
|
||||
| `DWORD` | `t.CUInt32T` | `uint32_t`(32位) |
|
||||
| `QWORD` | `t.CUInt64T` | `uint64_t`(64位) |
|
||||
| `INTPTR` | `t.CInt \| t.CPtr` | `int*` |
|
||||
| `UINTPTR` | `t.CUnsignedInt \| t.CPtr` | `unsigned int*` |
|
||||
| `SHORTPTR` | `t.CShort \| t.CPtr` | `short*` |
|
||||
| `USHORTPTR` | `t.CUnsignedShort \| t.CPtr` | `unsigned short*` |
|
||||
| `VOIDPTR` | `t.CVoid \| t.CPtr` | `void*` |
|
||||
| `TCHAR` | `t.CChar` | `char` |
|
||||
| `WCHAR` | `WORD` | `uint16_t`(UTF-16) |
|
||||
| `WCHARPTR` | `WORD \| t.CPtr` | `uint16_t*`(UTF-16) |
|
||||
| `CHARLIST` | `str \| t.CPtr` | `char*` |
|
||||
| `FSIZE_t` | `DWORD` | `uint32_t` |
|
||||
| `LBA_t` | `DWORD` | `uint32_t` |
|
||||
|
||||
### 浮点类型
|
||||
|
||||
| Viper 类型 | C 等价 | 大小(位) |
|
||||
|------------|--------|-----------|
|
||||
| `t.CFloat` | `float` | 32 |
|
||||
| `t.CDouble` | `double` | 64 |
|
||||
|
||||
### 其他基本类型
|
||||
|
||||
| Viper 类型 | C 等价 | 说明 |
|
||||
|------------|--------|------|
|
||||
| `t.CVoid` | `void` | 空类型 |
|
||||
| `t.CBool` | `bool` | 布尔类型(8位) |
|
||||
| `t.CWCharT` | `wchar_t` | 宽字符(32位) |
|
||||
| `t.CChar8T` | `char8_t` | UTF-8 字符(8位,无符号) |
|
||||
| `t.CChar16T` | `char16_t` | UTF-16 字符(16位,无符号) |
|
||||
| `t.CChar32T` | `char32_t` | UTF-32 字符(32位,无符号) |
|
||||
|
||||
### Python 便捷映射类型
|
||||
| Python 类型| Viper 类型 | Viper 值 |
|
||||
|--------|--------|------|
|
||||
|`int`|`t.CInt`|-|
|
||||
|`float`|`t.CFloat`|-|
|
||||
|`str`|`t.CChar \| t.CPtr`|-|
|
||||
|`bool`|`t.CBool`|-|
|
||||
|`True`|`t.CBool`|`1`|
|
||||
|`False`|`t.CBool`|`0`|
|
||||
|`None`|`NULL`|`t.CIntPtr(0)`|
|
||||
|
||||
对于 t.py 和 c.py 的引用,必须使用 import,不能使用 from ... import ...,也不能使用 as,因为这两者都是直接以字符串识别的。
|
||||
|
||||
## 类型组合
|
||||
|
||||
Viper 使用 Python 的 `|` 来组合类型修饰符(TypeUnion),其仅在左值注解中有效,其它位置均为位或语义(至少重载前是这样的),这是 Viper 最重要的语法特性之一。
|
||||
|
||||
### 指针类型
|
||||
|
||||
使用 `t.CPtr` 表示指针,通过 `|` 与基本类型组合:
|
||||
|
||||
```python
|
||||
x: t.CInt | t.CPtr # int* — 指向 int 的指针
|
||||
p: t.CVoid | t.CPtr # void* — 通用指针
|
||||
s: t.CChar | t.CPtr # char* — 字符串指针
|
||||
pp: t.CInt | t.CPtr | t.CPtr # int** — 指向指针的指针
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
int* x;
|
||||
void* p;
|
||||
char* s;
|
||||
int** pp;
|
||||
```
|
||||
|
||||
同时,指针也可以用 `Ptr[T]` 的方法表示,但在 Viper 中不太推荐,虽然说这样做一直是类似工程的常见用法,但是如果此处 `T` 是一个多态,同时我们想要复用 Python 的高亮插件,插件无法识别 `Ptr[T]` 的属性取值,但是对于 `T | Ptr` 可以。
|
||||
|
||||
```python
|
||||
x: t.CPtr[t.CInt]
|
||||
p: t.CPtr[t.CVoid]
|
||||
s: t.CPtr[t.CChar]
|
||||
pp: t.CPtr[t.CPtr[t.CInt]]
|
||||
pp2: t.CInt | t.CPtr[t.CPtr] # 同时也可以这么表示
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
|
||||
```c
|
||||
int* x;
|
||||
void* p;
|
||||
char* s:
|
||||
int** pp;
|
||||
int** pp2;
|
||||
```
|
||||
|
||||
### 类型限定符组合
|
||||
|
||||
```python
|
||||
x: t.CConst | t.CInt # const int
|
||||
p: t.CConst | t.CChar | t.CPtr # const char*
|
||||
```
|
||||
|
||||
### 存储类组合
|
||||
|
||||
```python
|
||||
x: t.CStatic | t.CInt # static int
|
||||
f: t.CExtern | t.CInt # extern int(声明其存在于外部)
|
||||
```
|
||||
|
||||
### 函数返回类型组合
|
||||
|
||||
```python
|
||||
def foo() -> t.CInt | t.CExport: # 导出函数,返回 int
|
||||
return 0
|
||||
|
||||
def bar() -> t.CVoid | t.CExtern: # 外部函数声明
|
||||
pass
|
||||
```
|
||||
|
||||
### `t.State` 声明性标记
|
||||
|
||||
`t.State` 是**声明性标记类型**,表示"仅声明不定义"(语义等价于 `t.CExport | t.CExtern`)。主要用于 FFI 外部函数声明:函数体为 `pass`,编译器只生成函数原型(declare),不生成定义(define),由链接器在外部库中解析符号。
|
||||
|
||||
```python
|
||||
def CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: ULONG,
|
||||
dwShareMode: ULONG,
|
||||
lpSecurityAttributes: SECURITY_ATTRIBUTES | t.CPtr,
|
||||
dwCreationDisposition: ULONG,
|
||||
dwFlagsAndAttributes: ULONG,
|
||||
hTemplateFile: HANDLE) -> HANDLE | t.State:
|
||||
pass
|
||||
```
|
||||
|
||||
等价 C 声明:
|
||||
|
||||
```c
|
||||
HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*, ULONG, ULONG, HANDLE);
|
||||
```
|
||||
|
||||
`t.State` 是 Viper FFI 机制的核心,无需任何特殊语法,仅靠返回类型注解即可声明外部函数。详见 [08-c-operations.md 的 FFI 章节](08-c-operations.md#ffi-外部函数声明) 和 [includes/w32](../includes/w32) 的 Win32 绑定。
|
||||
|
||||
## 数组类型
|
||||
|
||||
使用 Python 的 `list[ElementType, Size]` 语法表示固定大小数组(通常的,这用于栈上和 BSS 段上):
|
||||
|
||||
```python
|
||||
buf: list[t.CChar, 64] # char buf[64]
|
||||
ids: list[t.CInt, 10] # int ids[10]
|
||||
matrix: list[t.CFloat, 16] # float matrix[16]
|
||||
entries: list[idt_entry, 256] # struct idt_entry entries[256]
|
||||
s: list[t.CChar, None] # 自推长度 char s[];
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
char buf[64];
|
||||
int ids[10];
|
||||
float matrix[16];
|
||||
struct idt_entry entries[256];
|
||||
```
|
||||
|
||||
### 数组指针和函数指针
|
||||
|
||||
```python
|
||||
lp: list[t.CInt, 12] | t.CPtr
|
||||
vp: t.Callable[[t.CInt, t.CInt], t.CInt] = x # 其中 t.Callable = typing.Callable,可以直接使用。
|
||||
```
|
||||
|
||||
## 结构体类型
|
||||
|
||||
使用 `class` 定义结构体,成员通过类型注解声明:
|
||||
|
||||
```python
|
||||
class point:
|
||||
x: t.CInt
|
||||
y: t.CInt
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
```
|
||||
|
||||
### 带指针成员的结构体
|
||||
|
||||
```python
|
||||
class node:
|
||||
value: t.CInt
|
||||
next: node | t.CPtr # struct node* next
|
||||
```
|
||||
|
||||
### 匿名结构体成员(❌)
|
||||
|
||||
> WARNING: **警告:** 由于 Viper 在声明结构体,枚举等处不需要关键字,因此 `t.Anonymouse` 是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。
|
||||
|
||||
继承 `t.Anonymous` 的类作为匿名成员嵌入:
|
||||
|
||||
```python
|
||||
class header(t.Anonymous):
|
||||
magic: t.CUInt32T
|
||||
version: t.CUInt32T
|
||||
|
||||
class packet:
|
||||
header # 匿名嵌入,可直接访问 packet.magic
|
||||
length: t.CUInt32T
|
||||
```
|
||||
|
||||
### 字节序标记
|
||||
|
||||
> WARNING: **警告:** 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。
|
||||
|
||||
使用 `t.BigEndian` 或 `t.LittleEndian` 标记成员的字节序:
|
||||
|
||||
```python
|
||||
class network_packet:
|
||||
length: t.CUInt16T | t.BigEndian # 大端序存储
|
||||
type: t.CUInt8T # 默认小端序
|
||||
```
|
||||
|
||||
## 联合体类型
|
||||
|
||||
继承 `t.CUnion` 定义联合体:
|
||||
|
||||
```python
|
||||
class value(t.CUnion):
|
||||
ival: t.CInt
|
||||
fval: t.CFloat
|
||||
pval: t.CVoid | t.CPtr
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
union value {
|
||||
int ival;
|
||||
float fval;
|
||||
void* pval;
|
||||
};
|
||||
```
|
||||
|
||||
## 枚举类型
|
||||
|
||||
继承 `t.CEnum` 定义枚举:
|
||||
|
||||
```python
|
||||
class color(t.CEnum):
|
||||
RED: t.CEnum = 0
|
||||
GREEN: t.CEnum = 1
|
||||
BLUE: t.CEnum = 2
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
enum color {
|
||||
RED = 0,
|
||||
GREEN = 1,
|
||||
BLUE = 2
|
||||
};
|
||||
```
|
||||
|
||||
枚举成员可直接使用名称:
|
||||
|
||||
```python
|
||||
c: t.CInt = color.RED
|
||||
```
|
||||
|
||||
### REnum(标签联合体)
|
||||
|
||||
`t.CEnum` 是普通枚举(整数常量集合),`t.REnum` 是**标签联合体**(tagged union / Rust enum 风格)。每个变体可携带不同的 payload,通过 `match` 模式匹配分派。适用于少变体、无通用字段、match 分派的场景(如类型系统、IR 表示)。
|
||||
|
||||
```python
|
||||
class LLVMType(t.REnum):
|
||||
class Int:
|
||||
Bits: t.CInt
|
||||
class Void:
|
||||
pass
|
||||
|
||||
match ty:
|
||||
case LLVMType.Int:
|
||||
snprintf(buf, size, "i%d", ty.Bits)
|
||||
case LLVMType.Void:
|
||||
string.strcpy(buf, "void")
|
||||
```
|
||||
|
||||
REnum 的详细用法、与 CEnum/胖节点 AST 的对比见 [05-classes.md 的 REnum 章节](05-classes.md#renum标签联合体)。
|
||||
|
||||
## Typedef
|
||||
|
||||
使用 `t.CTypedef` 创建类型别名:
|
||||
|
||||
```python
|
||||
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] # typedef int (*irq_handler_t)();
|
||||
```
|
||||
|
||||
或通过注解赋值:
|
||||
|
||||
```python
|
||||
MyStruct: t.CTypedef = original_struct # typedef struct original_struct MyStruct;
|
||||
```
|
||||
|
||||
更推荐通过后者的形式,前者编译器不一定能够识别到注解是对 t.CTypedef 的,还是后面的注解的,而且容易触发未定义行为。
|
||||
|
||||
## 泛型类型(PEP 695 语法)
|
||||
|
||||
Viper 支持 PEP 695 泛型语法 `class Name[T]:`,定义类型参数化的类。类型参数 `T` 在类体内可作为占位类型使用,实例化时用 `Name[ConcreteType]` 特化。
|
||||
|
||||
```python
|
||||
@t.NoVTable
|
||||
class GSList[T]:
|
||||
Head: T | t.CPtr
|
||||
Tail: T | t.CPtr
|
||||
Count: t.CSizeT
|
||||
|
||||
lst: GSList[GNode] | t.CPtr = GSList[GNode]()
|
||||
```
|
||||
|
||||
泛型类还支持**递归泛型继承**(如 `class GNode(GSListNode[GNode])`),实现强类型自引用结构,无需 `void*` + 转型。这是 C/C++/Rust 均无法如此简洁表达的能力。
|
||||
|
||||
泛型类的完整用法、递归继承、实例化示例见 [05-classes.md 的泛型类章节](05-classes.md#泛型类pep-695-语法)。
|
||||
|
||||
## 强制类型转换
|
||||
在 Viper 中的强制类型转换有些不同,强制类型转换有两种方案,都是基于 `t.CType` 的用法,符合 Python 本意。
|
||||
对于原先就继承自 `t.CType` 的类型来说,可以直接使用 call 的方法进行类型转换,比如
|
||||
```python
|
||||
x: t.CChar | t.CPtr = "Hello"
|
||||
t.CVoid(x, t.CPtr)
|
||||
```
|
||||
其中用法为 `t.CType(x, ...)`,此处...可以是其它的 `t.CType` 或将要提到的 `Typedef`,将其视为组合类型对 `x` 进行强制转换。
|
||||
|
||||
额外补充一句,对于比较,四则运算,可以不使用强制类型转换,而是依靠隐式类型转换,但如果是严谨数学计算,推荐还是强制类型转换避免判断出错。
|
||||
|
||||
此外,若将一个不同类型的右值赋值到左值,则会隐式将右值转换为左值的类型。
|
||||
|
||||
如果你不喜欢 `t.CVoid(x, t.CPtr)` 的方法,认为这将 `void` 和 `ptr` 分开,不便于理解,那么还有一个方法,即将二者打包为一个 Typedef,即:
|
||||
```python
|
||||
VOIDPTR: t.CTypedef = t.CVoid | t.CPtr
|
||||
```
|
||||
则如果你想,你可以直接通过 call 的方式进行强转,比如:
|
||||
```python
|
||||
y: VOIDPTR = VOIDPTR(x)
|
||||
```
|
||||
|
||||
## 宏定义常量
|
||||
|
||||
使用 `t.CDefine` 定义编译时常量。`t.CDefine` 不是数据类型,而是编译时元指令——标记为 `t.CDefine` 的常量在 LLVM IR 中不生成任何全局变量,而是在引用处直接内联展开。详见 [01-overview.md 中 t.CDefine 深度解析](01-overview.md#tcdefine-深度解析)。
|
||||
|
||||
```python
|
||||
MAX_SIZE: t.CDefine = 1024
|
||||
PAGE_SIZE: t.CDefine = 4096
|
||||
FA_READ: t.CDefine = 0x01
|
||||
```
|
||||
|
||||
语义上等价于 C 的 `#define`:
|
||||
```c
|
||||
#define MAX_SIZE 1024
|
||||
#define PAGE_SIZE 4096
|
||||
#define FA_READ 0x01
|
||||
```
|
||||
|
||||
宏定义支持表达式:
|
||||
|
||||
```python
|
||||
CPU_FEATURE_FPU: t.CDefine = (1 << 0)
|
||||
CPU_FEATURE_APIC: t.CDefine = (1 << 6)
|
||||
```
|
||||
|
||||
宏定义也可与类型组合使用:
|
||||
|
||||
```python
|
||||
CODE_SEG: t.CDefine | t.CUInt16T = 0x08
|
||||
```
|
||||
|
||||
## 位域
|
||||
|
||||
使用 `t.Bit(n)` 定义位域成员:
|
||||
|
||||
```python
|
||||
class flags:
|
||||
a: t.CUInt32T | t.Bit[1] # 此处,如果存入 2,二进制是 10,会被截断为 0
|
||||
b: t.CUInt32T | t.Bit[3]
|
||||
c: t.CUInt32T | t.Bit[4]
|
||||
```
|
||||
|
||||
## LLVM IR 类型简写
|
||||
|
||||
Viper 提供了 LLVM IR 级别的类型简写:
|
||||
|
||||
```python
|
||||
i1 # 1位整数
|
||||
i8 # 8位有符号整数
|
||||
i16 # 16位有符号整数
|
||||
i32 # 32位有符号整数
|
||||
i64 # 64位有符号整数
|
||||
u1 # 1位无符号整数
|
||||
u8 # 8位无符号整数
|
||||
u16 # 16位无符号整数
|
||||
u32 # 32位无符号整数
|
||||
u64 # 64位无符号整数
|
||||
```
|
||||
|
||||
仍然通过 `t.x` 的方式调用,比如 `t.i1` 等。
|
||||
|
||||
## `__attribute__` 属性
|
||||
|
||||
通过 `t.attr` 命名空间访问 GCC `__attribute__` 属性:
|
||||
|
||||
```python
|
||||
t.attr.packed() # __attribute__((packed))
|
||||
t.attr.aligned(16) # __attribute__((aligned(16)))
|
||||
t.attr.section(".text.startup") # __attribute__((section(".text.startup")))
|
||||
t.attr.always_inline() # __attribute__((always_inline))
|
||||
t.attr.noinline() # __attribute__((noinline))
|
||||
t.attr.noreturn() # __attribute__((noreturn))
|
||||
t.attr.weak() # __attribute__((weak))
|
||||
t.attr.unused() # __attribute__((unused))
|
||||
t.attr.constructor() # __attribute__((constructor))
|
||||
t.attr.destructor() # __attribute__((destructor))
|
||||
t.attr.deprecated("use X") # __attribute__((deprecated("use X")))
|
||||
```
|
||||
|
||||
### LLVM 函数属性
|
||||
|
||||
通过 `t.attr.llvm` 命名空间指定 LLVM 函数属性:
|
||||
|
||||
```python
|
||||
t.attr.llvm.nobuiltin
|
||||
t.attr.llvm.nounwind
|
||||
t.attr.llvm.noredzone
|
||||
t.attr.llvm.willreturn
|
||||
t.attr.llvm.mustprogress
|
||||
t.attr.llvm.optnone
|
||||
t.attr.llvm.noinline
|
||||
t.attr.llvm.alwaysinline
|
||||
t.attr.llvm.readnone
|
||||
t.attr.llvm.readonly
|
||||
t.attr.llvm.writeonly
|
||||
t.attr.llvm.inaccessiblememonly
|
||||
t.attr.llvm.inaccessiblemem_or_argmemonly
|
||||
```
|
||||
|
||||
在函数返回类型注解中使用:
|
||||
|
||||
```python
|
||||
def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind:
|
||||
return 0
|
||||
```
|
||||
177
wiki/03-variables.md
Normal file
177
wiki/03-variables.md
Normal file
@@ -0,0 +1,177 @@
|
||||
# 03 - 变量声明与赋值
|
||||
|
||||
Viper 的变量声明使用 Python 的类型注解语法,通过 `t` 模块指定 C 级别的类型信息。
|
||||
|
||||
## 带注解的变量声明
|
||||
|
||||
使用 `变量名: 类型` 语法声明变量:
|
||||
|
||||
```python
|
||||
x: t.CInt = 0 # int x = 0;
|
||||
y: t.CUInt32T = 42 # uint32_t y = 42;
|
||||
name: list[t.CChar, 64] # char name[64];
|
||||
ptr: t.CVoid | t.CPtr # void* ptr;
|
||||
msg: t.CConst | str = "Hello" # const char* msg = "Hello";
|
||||
```
|
||||
|
||||
### 无初始值的声明
|
||||
|
||||
未赋值的变量声明会生成零初始化的变量,此处是未定义行为,即在不同环境位置可能会覆盖0,也可能不会,最好预先复制或用 memset 清除:
|
||||
|
||||
```python
|
||||
count: t.CInt # int count = 0;
|
||||
buffer: list[t.CChar, 256] # char buffer[256] = {0};
|
||||
```
|
||||
|
||||
### 延迟赋值
|
||||
|
||||
变量可以先声明后赋值:
|
||||
|
||||
```python
|
||||
x: t.CInt
|
||||
x = 10
|
||||
```
|
||||
|
||||
### 指针变量
|
||||
|
||||
```python
|
||||
p: t.CInt | t.CPtr # int* p;
|
||||
fb: t.CVoid | t.CPtr # void* fb;
|
||||
str_ptr: t.CConst | t.CChar | t.CPtr # const char* str_ptr;
|
||||
```
|
||||
|
||||
### 指针变量的特殊初始化
|
||||
|
||||
使用 `t.CVoid(0, t.CPtr)` 初始化指针为 NULL:
|
||||
|
||||
```python
|
||||
p: t.CVoid | t.CPtr = t.CVoid(0, t.CPtr) # void* p = NULL;
|
||||
```
|
||||
|
||||
或使用 `None`:
|
||||
|
||||
```python
|
||||
p: t.CVoid | t.CPtr = None # void* p = NULL;
|
||||
```
|
||||
|
||||
## 普通赋值
|
||||
|
||||
```python
|
||||
x: t.CInt = 0
|
||||
x = 42 # x = 42;
|
||||
```
|
||||
|
||||
## 增强赋值
|
||||
|
||||
支持所有 Python 增强赋值运算符:
|
||||
|
||||
```python
|
||||
x: t.CInt = 0
|
||||
x += 1 # x += 1;
|
||||
x -= 1 # x -= 1;
|
||||
x *= 2 # x *= 2;
|
||||
x //= 3 # x /= 3; (整数除法)
|
||||
x %= 5 # x %= 5;
|
||||
x <<= 1 # x <<= 1;
|
||||
x >>= 1 # x >>= 1;
|
||||
x |= 0xFF # x |= 0xFF;
|
||||
x &= 0x0F # x &= 0x0F;
|
||||
x ^= 0xAA # x ^= 0xAA;
|
||||
```
|
||||
|
||||
## 全局变量
|
||||
|
||||
模块顶层声明的变量即为全局变量:
|
||||
|
||||
```python
|
||||
kbd_tid: t.CInt = -1 # 全局 int kbd_tid = -1;
|
||||
BootInfo: bootinfo | t.CPtr # 全局 struct bootinfo* BootInfo;
|
||||
```
|
||||
|
||||
### 静态全局变量
|
||||
|
||||
```python
|
||||
x: t.CStatic | t.CInt = 0 # static int x = 0;
|
||||
```
|
||||
|
||||
### 外部声明
|
||||
|
||||
```python
|
||||
x: t.CExtern | t.CInt # extern int x;
|
||||
```
|
||||
|
||||
## 常量定义
|
||||
|
||||
使用 `t.CDefine` 定义编译时常量。`t.CDefine` 不是数据类型,而是编译时元指令——常量在 LLVM IR 中不生成全局变量,而是在引用处直接内联展开。详见 [01-overview.md 中 t.CDefine 深度解析](01-overview.md#tcdefine-深度解析)。
|
||||
|
||||
```python
|
||||
MAX_SIZE: t.CDefine = 1024
|
||||
PAGE_SIZE: t.CDefine = 4096
|
||||
FA_READ: t.CDefine = 0x01
|
||||
FA_WRITE: t.CDefine = 0x02
|
||||
```
|
||||
|
||||
## 字符串常量
|
||||
|
||||
Viper 中的字符串字面量编译为 C 的字符串常量(`const char*`):
|
||||
|
||||
```python
|
||||
msg: t.CConst | str = "Hello, World!"
|
||||
serial.puts(msg)
|
||||
```
|
||||
|
||||
也可以直接传递字符串字面量:
|
||||
|
||||
```python
|
||||
serial.puts("Hello, World!")
|
||||
```
|
||||
|
||||
## 类型转换
|
||||
|
||||
使用类型构造函数进行显式类型转换:
|
||||
|
||||
```python
|
||||
x: t.CInt = 42
|
||||
y: t.CUInt32T = t.CUInt32T(x) # (uint32_t)x
|
||||
f: t.CFloat = float(x) # (float)x
|
||||
i: t.CInt = int(f) # (int)f
|
||||
```
|
||||
|
||||
### 指针类型转换
|
||||
|
||||
```python
|
||||
ptr: t.CVoid | t.CPtr = some_ptr
|
||||
int_val: t.CUInt64T = t.CUInt64T(ptr) # (uint64_t)ptr — 指针转整数
|
||||
```
|
||||
|
||||
## 结构体实例化
|
||||
|
||||
> 此处不理解参见 ([05-classes.md 中 结构体实例化与初始化](05-classes.md#结构体实例化与初始化))
|
||||
|
||||
直接使用类名作为构造函数:
|
||||
|
||||
```python
|
||||
info: fat32_types.fat32_fileinfo
|
||||
dp: fat32_types.fat32_dirobj
|
||||
```
|
||||
|
||||
带初始化的实例化:
|
||||
|
||||
```python
|
||||
obj: MyClass = MyClass(arg1, arg2)
|
||||
```
|
||||
|
||||
使用 `c.Addr` 获取实例指针:
|
||||
|
||||
```python
|
||||
obj_ptr: MyClass | t.CPtr = c.Addr(MyClass(arg1, arg2))
|
||||
```
|
||||
|
||||
## delete 语句
|
||||
|
||||
`del` 语句可用于调用对象的 `__del__` 或 `__delete__` 方法:
|
||||
|
||||
```python
|
||||
del obj # 调用 obj.__del__() 或 obj.__delete__()
|
||||
del arr[idx] # 调用 arr.__delitem__(idx)
|
||||
```
|
||||
254
wiki/04-functions.md
Normal file
254
wiki/04-functions.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# 04 - 函数定义与调用
|
||||
|
||||
Viper 的函数定义使用 Python 的 `def` 语法,通过类型注解指定参数类型和返回类型。
|
||||
|
||||
## 基本函数定义
|
||||
|
||||
```python
|
||||
def add(a: t.CInt, b: t.CInt) -> t.CInt:
|
||||
return a + b
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
int add(int a, int b) {
|
||||
return a + b;
|
||||
}
|
||||
```
|
||||
|
||||
## 无返回值函数
|
||||
|
||||
```python
|
||||
def print_msg(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
|
||||
serial.puts(msg)
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
void print_msg(const char* msg) {
|
||||
serial_puts(msg);
|
||||
}
|
||||
```
|
||||
|
||||
## 函数修饰符
|
||||
|
||||
通过返回类型的 `|` 组合添加函数修饰符:
|
||||
|
||||
### 导出函数
|
||||
|
||||
` t.CExport` 控制函数的符号可见性——标记为 `t.CExport` 的函数不加 SHA1 前缀,全局可见。详见 [01-overview.md 中 t.CExport 深度解析](01-overview.md#tcexport-深度解析)。
|
||||
|
||||
```python
|
||||
def _start() -> t.CInt | t.CExport:
|
||||
return 0
|
||||
```
|
||||
|
||||
### 外部函数声明
|
||||
|
||||
使用 `c.State` 表示仅声明不定义:
|
||||
|
||||
```python
|
||||
def isr0() -> t.CExtern | t.CVoid | c.State: pass
|
||||
def isr1() -> t.CExtern | t.CVoid | c.State: pass
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
extern void isr0();
|
||||
extern void isr1();
|
||||
```
|
||||
|
||||
### 内联函数
|
||||
|
||||
`t.CInline` 标记函数为内联函数,编译器在调用点直接展开函数体。详见 [01-overview.md 中 t.CInline 深度解析](01-overview.md#tcinline-深度解析)。
|
||||
|
||||
```python
|
||||
def fast_add(a: t.CInt, b: t.CInt) -> t.CInt | t.CInline:
|
||||
return a + b
|
||||
```
|
||||
|
||||
### 宏函数
|
||||
|
||||
当函数返回类型注解包含 `t.CDefine` 时,该函数被视为宏函数,不生成 LLVM IR 函数定义,在调用处内联展开。详见 [01-overview.md 中 t.CDefine 函数](01-overview.md#tcdefine-函数)。
|
||||
|
||||
```python
|
||||
def MAKE_FLAG(bit) -> t.CDefine:
|
||||
return (1 << bit)
|
||||
```
|
||||
|
||||
### 静态函数
|
||||
|
||||
```python
|
||||
def helper() -> t.CVoid | t.CStatic:
|
||||
pass
|
||||
```
|
||||
|
||||
## 函数属性装饰器
|
||||
|
||||
使用 `@c.Attribute` 添加 GCC `__attribute__` 属性:
|
||||
|
||||
```python
|
||||
@c.Attribute(t.attr.section(".text.startup"), t.attr.aligned(16))
|
||||
def _start() -> t.CInt:
|
||||
return 0
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
__attribute__((section(".text.startup"), aligned(16)))
|
||||
int _start() {
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 常用属性组合
|
||||
|
||||
```python
|
||||
@c.Attribute(t.attr.always_inline())
|
||||
def hot_path() -> t.CInt:
|
||||
return 0
|
||||
|
||||
@c.Attribute(t.attr.noreturn())
|
||||
def panic(msg: t.CConst | t.CChar | t.CPtr) -> t.CVoid:
|
||||
serial.puts(msg)
|
||||
while True: pass
|
||||
|
||||
@c.Attribute(t.attr.packed)
|
||||
```
|
||||
|
||||
## LLVM 函数属性
|
||||
|
||||
在返回类型注解中通过 `t.attr.llvm` 指定 LLVM 属性:
|
||||
|
||||
```python
|
||||
def foo() -> t.CInt | t.attr.llvm.nobuiltin | t.attr.llvm.nounwind:
|
||||
return 0
|
||||
```
|
||||
|
||||
## 多返回值(CReturn)
|
||||
|
||||
使用 `@c.CReturn` 装饰器实现多返回值,通过指针参数实现:
|
||||
|
||||
```python
|
||||
@c.CReturn(t.CInt, t.CInt)
|
||||
def divmod(a: t.CInt, b: t.CInt) -> t.CVoid:
|
||||
q: t.CInt = a // b
|
||||
r: t.CInt = a % b
|
||||
return q, r
|
||||
```
|
||||
|
||||
规则:
|
||||
1. `CReturn` 中的类型数量决定返回值个数
|
||||
2. 自动为每个返回类型添加指针参数(`t.CInt` → `t.CInt | t.CPtr`)
|
||||
3. 参数名自动生成为 `__ReturnValue0__`、`__ReturnValue1__` 等
|
||||
4. 调用处自动传参 `&xxx`
|
||||
5. 必须使用左右值赋值,返回值不能直接传入函数。
|
||||
|
||||
## 指针参数
|
||||
|
||||
```python
|
||||
def read_data(buf: t.CChar | t.CPtr, size: t.CSizeT) -> t.CInt:
|
||||
pass
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
int read_data(char* buf, size_t size);
|
||||
```
|
||||
|
||||
### 输出参数模式
|
||||
|
||||
使用 `c.Addr` 传递变量地址作为输出参数:
|
||||
|
||||
```python
|
||||
res: t.CInt = fat32.opendir("/", c.Addr(dp))
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
int res = fat32_opendir("/", &dp);
|
||||
```
|
||||
|
||||
后续会使用隐式左值获取来减少 `c.Addr` 的直接使用。
|
||||
|
||||
## 函数指针类型
|
||||
|
||||
使用 `t.Callable` 定义函数指针类型:
|
||||
|
||||
```python
|
||||
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt]
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
typedef int (*irq_handler_t)();
|
||||
```
|
||||
|
||||
带参数的函数指针:
|
||||
|
||||
```python
|
||||
callback_t: t.Callable[[t.CInt, t.CInt], t.CVoid]
|
||||
```
|
||||
|
||||
## 默认参数
|
||||
|
||||
Viper 支持函数默认参数值:
|
||||
|
||||
```python
|
||||
def create_window(x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt, title: t.CConst | str = "Window") -> t.CInt:
|
||||
pass
|
||||
```
|
||||
|
||||
## return 语句
|
||||
|
||||
```python
|
||||
return 0 # return 0;
|
||||
return # return; (void 函数)
|
||||
return a, b # 多返回值(需配合 @c.CReturn)
|
||||
```
|
||||
|
||||
## 全局变量声明
|
||||
|
||||
函数外的变量声明为全局变量,函数内通过 `global` 关键字访问:
|
||||
|
||||
```python
|
||||
BootInfo: bootinfo | t.CPtr
|
||||
|
||||
def init() -> t.CVoid:
|
||||
global BootInfo
|
||||
BootInfo = saved_rdi
|
||||
```
|
||||
|
||||
支持 func-in-func 嵌套函数。
|
||||
|
||||
## Call Func 函数调用
|
||||
|
||||
函数调用最好通过顺序调用来实现
|
||||
|
||||
```python
|
||||
def add(x: int, y: int, z: int):
|
||||
return (x + y) * z
|
||||
|
||||
def main() -> int | t.CExport:
|
||||
print(add(1, 2, 3)) # (1 + 2) * 3 = 9
|
||||
```
|
||||
|
||||
这也是 C 中的标准实现方法,但实际上,你也可以乱序传参。
|
||||
|
||||
```python
|
||||
# 仍然用刚才的 add 举例
|
||||
def main() -> int | t.CExport:
|
||||
print(add(z=3, x=1, y=2)) # (1 + 2) * 3 = 9
|
||||
```
|
||||
|
||||
乱序传参或带参传参对于结构体的初始化同样有效。同时,你也能够给函数指定默认值。
|
||||
|
||||
```python
|
||||
def add(x: int, y: int, z: int = 1):
|
||||
return (x + y) * z
|
||||
|
||||
def main() -> int | t.CExport:
|
||||
print(add(1, 2)) # (1 + 2) * 1 = 3
|
||||
```
|
||||
|
||||
在结构体中也可以指定默认值。
|
||||
274
wiki/05-classes.md
Normal file
274
wiki/05-classes.md
Normal file
@@ -0,0 +1,274 @@
|
||||
# 05 - 类与数据布局
|
||||
|
||||
Viper 中的 `class` 有两种用途:定义**数据布局**(结构体/联合体/枚举,本章内容),以及定义**面向对象的对象类型**(见 [06-oop.md](06-oop.md))。
|
||||
|
||||
## 结构体定义
|
||||
|
||||
普通 `class` 定义编译为 LLVM 结构体类型:
|
||||
|
||||
```python
|
||||
class point:
|
||||
x: t.CInt
|
||||
y: t.CInt
|
||||
```
|
||||
|
||||
在 C 中:
|
||||
```c
|
||||
struct point {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
```
|
||||
|
||||
在 LLVM IR 中:
|
||||
```llvm
|
||||
%"<SHA1>.point" = type { i32, i32 }
|
||||
```
|
||||
|
||||
### 带属性的结构体
|
||||
|
||||
```python
|
||||
@c.Attribute(t.attr.packed)
|
||||
class idt_entry:
|
||||
base_low: t.CUInt16T
|
||||
selector: t.CUInt16T
|
||||
ist_attr: t.CUInt8T
|
||||
type_attr: t.CUInt8T
|
||||
base_middle: t.CUInt16T
|
||||
base_high: t.CUInt32T
|
||||
reserved1: t.CUInt32T
|
||||
```
|
||||
|
||||
### 结构体数组
|
||||
|
||||
```python
|
||||
idt: list[idt_entry, 256] = []
|
||||
```
|
||||
|
||||
### 结构体指针成员
|
||||
|
||||
```python
|
||||
class node:
|
||||
value: t.CInt
|
||||
next: node | t.CPtr # 指向自身的指针(有向图链表)
|
||||
```
|
||||
|
||||
### 结构体实例化与初始化
|
||||
|
||||
```python
|
||||
entry: idt_entry = idt_entry()
|
||||
```
|
||||
|
||||
或带构造函数:
|
||||
|
||||
```python
|
||||
mousepoint = Point(x=1, y=2)
|
||||
```
|
||||
|
||||
乱序传参或带参传参对于结构体的初始化同样有效:
|
||||
|
||||
```python
|
||||
entry: idt_entry = idt_entry(selector=0x08, base_low=0x0000)
|
||||
```
|
||||
|
||||
也可以给结构体成员指定默认值:
|
||||
|
||||
```python
|
||||
class config:
|
||||
width: t.CInt = 640
|
||||
height: t.CInt = 480
|
||||
depth: t.CInt = 32
|
||||
```
|
||||
|
||||
使用 `c.Addr` 获取实例指针:
|
||||
|
||||
```python
|
||||
obj_ptr: MyClass | t.CPtr = c.Addr(MyClass(arg1, arg2))
|
||||
```
|
||||
|
||||
### 匿名结构体成员(❌)
|
||||
|
||||
> WARNING: **警告:** 由于 Viper 在声明结构体,枚举等处不需要关键字,因此 `t.Anonymouse` 是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。
|
||||
|
||||
继承 `t.Anonymous` 的类作为匿名成员嵌入:
|
||||
|
||||
```python
|
||||
class header(t.Anonymous):
|
||||
magic: t.CUInt32T
|
||||
version: t.CUInt32T
|
||||
|
||||
class packet:
|
||||
header # 匿名嵌入,可直接访问 packet.magic
|
||||
length: t.CUInt32T
|
||||
```
|
||||
|
||||
### 字节序标记
|
||||
|
||||
> WARNING: **警告:** 此关键字是实验性的。未经充分测试,不当使用可能导致未定义行为。
|
||||
|
||||
使用 `t.BigEndian` 或 `t.LittleEndian` 标记成员的字节序:
|
||||
|
||||
```python
|
||||
class network_packet:
|
||||
length: t.CUInt16T | t.BigEndian # 大端序存储
|
||||
type: t.CUInt8T # 默认小端序
|
||||
```
|
||||
|
||||
## 联合体
|
||||
|
||||
继承 `t.CUnion` 定义联合体:
|
||||
|
||||
```python
|
||||
class value(t.CUnion):
|
||||
ival: t.CInt
|
||||
fval: t.CFloat
|
||||
pval: t.CVoid | t.CPtr
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
union value {
|
||||
int ival;
|
||||
float fval;
|
||||
void* pval;
|
||||
};
|
||||
```
|
||||
|
||||
## 枚举
|
||||
|
||||
继承 `t.CEnum` 定义枚举:
|
||||
|
||||
```python
|
||||
class color(t.CEnum):
|
||||
RED: t.CEnum = 0
|
||||
GREEN: t.CEnum = 1
|
||||
BLUE: t.CEnum = 2
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
enum color {
|
||||
RED = 0,
|
||||
GREEN = 1,
|
||||
BLUE = 2
|
||||
};
|
||||
```
|
||||
|
||||
枚举成员可直接使用名称:
|
||||
|
||||
```python
|
||||
c: t.CInt = color.RED
|
||||
```
|
||||
|
||||
## REnum(标签联合体)
|
||||
|
||||
`t.CEnum` 是普通枚举(整数常量集合),`t.REnum` 是**标签联合体**(tagged union / Rust enum 风格)。每个变体可携带不同的 payload,通过 `match` 模式匹配分派。
|
||||
|
||||
### 适用场景
|
||||
|
||||
| 类型 | 变体数 | 通用字段 | 访问模式 | 适用 |
|
||||
|------|--------|---------|---------|------|
|
||||
| `t.CEnum` | 任意 | 无 | 整数比较 | 状态码、opcode、token 类型 |
|
||||
| `t.REnum` | 少(~10) | 无 | match 分派 | 类型系统、AST 少变体场景 |
|
||||
| 胖节点(如 AST) | 多(70+) | 有 | vtype + 直接属性 | AST 节点(见 [includes/ast](../includes/ast)) |
|
||||
|
||||
### 定义
|
||||
|
||||
```python
|
||||
class LLVMType(t.REnum):
|
||||
class Int: # 变体 Int,携带 payload
|
||||
Bits: t.CInt
|
||||
|
||||
class Ptr: # 变体 Ptr,携带 payload
|
||||
Pointee: LLVMType | t.CPtr # 自引用用指针
|
||||
|
||||
class Void: # 变体 Void,无 payload
|
||||
pass
|
||||
```
|
||||
|
||||
布局为 `{ i32 __tag, <max_variant_payload> }`,所有变体共享最大变体的 payload 空间。
|
||||
|
||||
### match 分派
|
||||
|
||||
```python
|
||||
match ty:
|
||||
case LLVMType.Int: # 位置绑定 payload:ty.Bits 可用
|
||||
snprintf(buf, size, "i%d", ty.IntBits)
|
||||
case LLVMType.Ptr: # ty.PtrPointee 可用
|
||||
TypePrint(buf, size, ty.PtrPointee)
|
||||
case LLVMType.Void:
|
||||
string.strcpy(buf, "void")
|
||||
```
|
||||
|
||||
详见 [includes/llvmlite/__types.py](../includes/llvmlite/__types.py) 的 `LLVMType` 实现。
|
||||
|
||||
## Typedef
|
||||
|
||||
使用 `t.CTypedef` 创建类型别名:
|
||||
|
||||
```python
|
||||
irq_handler_t: t.CTypedef | t.Callable[[], t.CInt] # typedef int (*irq_handler_t)();
|
||||
```
|
||||
|
||||
或通过注解赋值:
|
||||
|
||||
```python
|
||||
MyStruct: t.CTypedef = original_struct # typedef struct original_struct MyStruct;
|
||||
```
|
||||
|
||||
更推荐通过后者的形式,前者编译器不一定能够识别到注解是对 `t.CTypedef` 的,还是后面的注解的,而且容易触发未定义行为。
|
||||
|
||||
## 泛型类(PEP 695 语法)
|
||||
|
||||
TransPyC 支持 PEP 695 泛型语法 `class Name[T]:`,定义类型参数化的类。
|
||||
|
||||
### 基本泛型类
|
||||
|
||||
```python
|
||||
@t.NoVTable
|
||||
class GSListNode[T]:
|
||||
Next: T | t.CPtr
|
||||
|
||||
@t.NoVTable
|
||||
class GSList[T]:
|
||||
Head: T | t.CPtr
|
||||
Tail: T | t.CPtr
|
||||
Count: t.CSizeT
|
||||
def append(self, node: T): ...
|
||||
```
|
||||
|
||||
### 递归泛型继承
|
||||
|
||||
泛型类可递归引用自身作为类型参数,实现强类型自引用结构(无需转型):
|
||||
|
||||
```python
|
||||
@t.NoVTable
|
||||
class GNode(GSListNode[GNode]): # 递归泛型继承
|
||||
value: t.CInt
|
||||
# 此时 GNode.Next: GNode|CPtr —— 强类型,零转型
|
||||
```
|
||||
|
||||
这是 C(`void*` + cast)、C++(模板继承但无 `@t.NoVTable` 精细控制)、Rust(无继承,需 trait bound)均无法如此简洁表达的能力。
|
||||
|
||||
### 实例化
|
||||
|
||||
```python
|
||||
lst: GSList[GNode] | t.CPtr = GSList[GNode]()
|
||||
node: GNode | t.CPtr = GNode()
|
||||
lst.append(node) # 类型安全:append 只接受 GNode
|
||||
```
|
||||
|
||||
详见 [includes/linkedlist.py](../includes/linkedlist.py) 和 [includes/vector.py](../includes/vector.py)。
|
||||
|
||||
## 位域
|
||||
|
||||
使用 `t.Bit(n)` 定义位域成员:
|
||||
|
||||
```python
|
||||
class flags:
|
||||
a: t.CUInt32T | t.Bit[1]
|
||||
b: t.CUInt32T | t.Bit[3]
|
||||
c: t.CUInt32T | t.Bit[4]
|
||||
```
|
||||
|
||||
注意:如果存入超出位域宽度的值,会被截断。例如 `a` 为 1 位,存入 2(二进制 `10`)会被截断为 0。
|
||||
692
wiki/06-oop.md
Normal file
692
wiki/06-oop.md
Normal file
@@ -0,0 +1,692 @@
|
||||
# 06 - 面向对象与运算符重载
|
||||
|
||||
Viper 中的 `class` 有两种语义:**数据布局**(结构体/联合体/枚举,见 [05-classes.md](05-classes.md))和**面向对象**。面向对象特性通过 `@t.Object` 装饰器启用,多态通过 `@t.CVTable` 装饰器启用,非多态继承(C++ 风格,无 vtable 开销)通过 `@t.NoVTable` 装饰器启用。
|
||||
|
||||
## `@t.Object` 面向对象
|
||||
|
||||
使用 `@t.Object` 装饰器启用面向对象特性,类将拥有成员方法、运算符重载、属性装饰器等 OOP 支持。编译器也会自动识别:如果结构体内有函数定义,会自动使用 `@t.Object` 行为,但在实际工程中更推荐明确标记,避免未定义行为。
|
||||
|
||||
### 基本定义
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class ViperKernel:
|
||||
BootInfo: bootinfo | t.CPtr
|
||||
VGA: vga._VGAScreenDriver | t.CPtr
|
||||
VGA_drv: vga._VGAScreenDriver
|
||||
|
||||
def __init__(self):
|
||||
self.BootInfo = BootInfo
|
||||
serial.init()
|
||||
serial.puts("ViperOS VKernel Test\n")
|
||||
```
|
||||
|
||||
避免在 `__init__` 中使用 `return`,虽然不会报错,但是你永远无法获得 `return` 的结果,如果需要提前结束代码流,`return` 也不失为一种办法。
|
||||
|
||||
### 成员方法
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Sheet:
|
||||
id: t.CInt
|
||||
x: t.CInt
|
||||
y: t.CInt
|
||||
w: t.CInt
|
||||
h: t.CInt
|
||||
|
||||
def __init__(self, x: t.CInt, y: t.CInt, w: t.CInt, h: t.CInt):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.w = w
|
||||
self.h = h
|
||||
|
||||
def MoveTo(self, nx: t.CInt, ny: t.CInt):
|
||||
self.x = nx
|
||||
self.y = ny
|
||||
|
||||
def Resize(self, nw: t.CInt, nh: t.CInt):
|
||||
self.w = nw
|
||||
self.h = nh
|
||||
```
|
||||
|
||||
### 方法调用
|
||||
|
||||
```python
|
||||
sheet: Sheet = Sheet(0, 0, 640, 480)
|
||||
sheet.MoveTo(100, 100)
|
||||
sheet.Resize(800, 600)
|
||||
```
|
||||
|
||||
### 属性装饰器
|
||||
|
||||
支持 Python 的 `@property` 和 `@xxx.setter` 装饰器:
|
||||
|
||||
> `@xxx.setter` 和自定义装饰器正在试验阶段,尽可能不要使用
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class MyObj:
|
||||
_value: t.CInt
|
||||
|
||||
@property
|
||||
def value(self) -> t.CInt:
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, v: t.CInt):
|
||||
self._value = v
|
||||
```
|
||||
|
||||
### 静态方法
|
||||
|
||||
```python
|
||||
@staticmethod
|
||||
def _yield():
|
||||
pass
|
||||
```
|
||||
|
||||
### 对象指针与解引用
|
||||
|
||||
`@t.Object` 的实例通常通过指针操作:
|
||||
|
||||
```python
|
||||
obj_ptr: MyClass | t.CPtr = c.Addr(MyClass())
|
||||
obj: MyClass = c.Deref(obj_ptr)
|
||||
```
|
||||
|
||||
### 成员访问
|
||||
|
||||
通过 `self` 访问成员变量和方法:
|
||||
|
||||
```python
|
||||
def method(self) -> t.CVoid:
|
||||
self.x = 10
|
||||
y: t.CInt = self.y
|
||||
self.DoSomething()
|
||||
```
|
||||
|
||||
### 内嵌对象
|
||||
|
||||
`@t.Object` 可以包含其他对象实例(非指针):
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Sheet:
|
||||
_surf_obj: gfx.Surface
|
||||
_renderer_obj: gfx.Renderer
|
||||
surf: gfx.Surface | t.CPtr
|
||||
renderer: gfx.Renderer | t.CPtr
|
||||
|
||||
def __init__(self, fb: UINT32PTR, zb: float | t.CPtr, w: t.CInt, h: t.CInt):
|
||||
self._surf_obj = gfx.Surface(fb, zb, w, h)
|
||||
self.surf = c.Addr(self._surf_obj)
|
||||
self._renderer_obj = gfx.Renderer(self.surf)
|
||||
self.renderer = c.Addr(self._renderer_obj)
|
||||
```
|
||||
|
||||
### `__before_init__` 方法
|
||||
|
||||
所有类(不限 `@t.Object`/`@t.CVTable`)自动生成 `__before_init__` 方法,在 `__init__` 之前**自动填充成员默认值**,并对 `@t.CVTable` 类初始化 vtable:
|
||||
|
||||
```llvm
|
||||
define void @"<SHA1>.ClassName.__before_init__"(%"<SHA1>.ClassName"* %self) {
|
||||
entry:
|
||||
; 对 @t.CVTable 类:存储 vtable 指针到 slot 0
|
||||
; 遍历所有成员:若有默认值则 store 默认值,否则 store 零值
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
**职责**:
|
||||
- 填充类定义中声明的成员默认值(如 `x: t.CInt = 0` 中的 `0`)
|
||||
- 对无默认值的成员填充类型零值
|
||||
- 对 `@t.CVTable` 类额外初始化 vtable 指针
|
||||
|
||||
**调用时机**(由编译器在构造时自动插入):
|
||||
1. `alloca` 栈分配后调用一次
|
||||
2. 若存在 `__new__` 方法且返回堆指针,对堆指针再调用一次(alloca 的默认值已随栈帧丢弃,必须重新填充)
|
||||
3. `__init__` 调用前
|
||||
|
||||
**就地构造**:用户也可手动调用 `__before_init__` 实现就地构造(placement new 语义),典型用法见 `mpool.alloc_buf`:
|
||||
|
||||
```python
|
||||
buf: Buf | t.CPtr = pool.alloc(Buf.__sizeof__())
|
||||
buf.__before_init__() # 填充默认值
|
||||
buf.__init__(args) # 用户初始化逻辑
|
||||
```
|
||||
|
||||
跨模块导入类时,导入侧也会声明 `__before_init__`,保证跨模块构造行为一致。
|
||||
|
||||
## `@t.NoVTable` 非多态继承
|
||||
|
||||
`@t.NoVTable` 启用 C++ 风格的非多态继承(对应 `struct A : B {}` 无 virtual)。与 `@t.CVTable` 的区别:
|
||||
|
||||
| 特性 | `@t.NoVTable` | `@t.CVTable` |
|
||||
|------|---------------|--------------|
|
||||
| vtable 指针 | ❌ 无 | ✅ 自动插入 slot 0 |
|
||||
| 虚派发 | ❌ 无 | ✅ 支持 |
|
||||
| 方法继承 | ✅ 生成包装函数转发 | ✅ 通过 vtable |
|
||||
| 字段展平 | ✅ 父类字段前置嵌入 | ✅ 父类字段前置嵌入(vtable 之后) |
|
||||
| 运行时开销 | 零 | 每次虚调用一次间接跳转 |
|
||||
|
||||
### 基本定义
|
||||
|
||||
```python
|
||||
@t.NoVTable
|
||||
class LinkedNode:
|
||||
next: "LinkedNode" | t.CPtr
|
||||
prev: "LinkedNode" | t.CPtr
|
||||
def append(self, node: "LinkedNode" | t.CPtr): ...
|
||||
|
||||
@t.NoVTable
|
||||
class GNode(LinkedNode): # 非多态继承
|
||||
value: t.CInt
|
||||
```
|
||||
|
||||
子类 `GNode` 的结构体布局为 `{next, prev, value}`(父类字段前置,无 vtable 指针)。
|
||||
|
||||
### 方法继承机制
|
||||
|
||||
子类调用父类方法时,编译器通过 `_generate_inherited_method_wrappers` 自动生成包装函数:将 `self` bitcast 为父类指针后尾调用父类方法。例如 `gnode.append(child)` 会生成:
|
||||
|
||||
```llvm
|
||||
define void @"<SHA1>.GNode.append"(%"<SHA1>.GNode"* %self, ...) {
|
||||
entry:
|
||||
%base = bitcast %"<SHA1>.GNode"* %self to %"<SHA1>.LinkedNode"*
|
||||
call void @"<SHA1>.LinkedNode.append"(%"<SHA1>.LinkedNode"* %base, ...)
|
||||
ret void
|
||||
}
|
||||
```
|
||||
|
||||
### 跨模块继承
|
||||
|
||||
跨模块父类(如 `linkedlist.LinkedNode`)的 `@t.NoVTable` 标记会在导入时被检测并记录,子类继承时同样跳过 vtable 插入。
|
||||
|
||||
### 向下转型
|
||||
|
||||
父类指针→子类指针需显式转型(生成 bitcast IR):
|
||||
|
||||
```python
|
||||
root: Node = Node() # Node 继承 LinkedNode
|
||||
child: LinkedNode | t.CPtr = root.child
|
||||
# child 是 LinkedNode*,需要转回 Node*:
|
||||
node: Node | t.CPtr = (Node | t.CPtr)(child)
|
||||
```
|
||||
|
||||
隐式转型仅向上(子→父)。
|
||||
|
||||
## `@t.CVTable` 虚函数表与多态
|
||||
|
||||
> 尽可能避免使用 `self[0]` 来获取结构体成员,其不稳定,也不要手动操作虚表。
|
||||
|
||||
使用 `@t.CVTable` 装饰器可以启用虚函数表支持。启用后,结构体第一个成员自动插入 `i8*` 类型的 vtable 指针。`@t.CVTable` 的操作必须配合 `@t.Object`,但有时编译器会自动识别是否存在结构体内函数,自动使用 `@t.Object`。
|
||||
|
||||
### 基本定义
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
@t.CVTable
|
||||
class Base:
|
||||
def __init__(self):
|
||||
pass
|
||||
def method(self) -> t.CInt:
|
||||
return 0
|
||||
```
|
||||
|
||||
在 LLVM IR 中,`@t.CVTable` 类的结构体类型自动插入 vtable 指针:
|
||||
|
||||
```llvm
|
||||
%"<SHA1>.Base" = type { i8*, i32 }
|
||||
```
|
||||
|
||||
同时,编译器会生成全局 vtable 变量:
|
||||
|
||||
```llvm
|
||||
@"<SHA1>.Base_Vtable" = internal global [1 x i8*] zeroinitializer
|
||||
```
|
||||
|
||||
### 继承与多态
|
||||
|
||||
虚表可以帮你使用继承语法实现多态:
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
@t.CVTable
|
||||
class Base:
|
||||
def __init__(self):
|
||||
pass
|
||||
def method(self) -> t.CInt:
|
||||
return 0
|
||||
|
||||
@t.Object
|
||||
@t.CVTable
|
||||
class M1(Base):
|
||||
def method(self) -> t.CInt:
|
||||
return 1
|
||||
```
|
||||
|
||||
当 M1 被初始化时,VTable 会自动修改,这点和 C++ 相同,尽可能避免直接操作 VTable。
|
||||
|
||||
继承时,子类会自动继承父类的成员变量和方法。子类重写父类方法时,vtable 中对应位置的函数指针被子类方法替换,实现多态。
|
||||
|
||||
### VTable 运行时修改
|
||||
|
||||
如果需要临时构造一个额外的 `Base` 结构体,还可以使用:
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
@t.CVTable
|
||||
class Base:
|
||||
def __init__(self):
|
||||
pass
|
||||
def method(self) -> t.CInt:
|
||||
return 0
|
||||
|
||||
def __method1(self) -> t.CInt:
|
||||
return 1
|
||||
|
||||
def main() -> t.CInt | t.CExport:
|
||||
b: Base = Base()
|
||||
b.method = __method1
|
||||
```
|
||||
|
||||
启用 `VTable` 的表中,允许直接修改内部函数为函数指针,在底层,此操作将修改 `VTable` 来达成目的。编译器会检测当前 vtable 是否与类 vtable 相同,如果相同则创建 vtable 的副本(memcpy),避免修改影响所有实例。
|
||||
|
||||
### 虚方法调度机制
|
||||
|
||||
虚方法调用的核心流程:
|
||||
|
||||
1. 从对象指针 GEP 获取第 0 个 slot(vtable 指针)
|
||||
2. 加载 vtable 指针
|
||||
3. bitcast 为 `[N x i8*]*` 类型
|
||||
4. GEP 获取方法索引处的函数指针
|
||||
5. 加载函数指针
|
||||
6. bitcast 为正确的函数指针类型
|
||||
7. 间接调用
|
||||
|
||||
## 继承
|
||||
|
||||
### 结构体成员继承
|
||||
|
||||
当子类继承父类(父类在 `Gen.class_members` 中存在)时:
|
||||
|
||||
- 父类的成员变量被继承到子类(排在子类成员之前)
|
||||
- 父类的默认值也被继承
|
||||
- 父类的方法被继承(重命名为 `子类名.方法名`)
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Animal:
|
||||
name: list[t.CChar, 32]
|
||||
age: t.CInt
|
||||
|
||||
def __init__(self, age: t.CInt):
|
||||
self.age = age
|
||||
|
||||
def Speak(self) -> t.CVoid:
|
||||
serial.puts("...\n")
|
||||
|
||||
@t.Object
|
||||
class Dog(Animal):
|
||||
breed: t.CInt
|
||||
|
||||
def Speak(self) -> t.CVoid:
|
||||
serial.puts("Woof!\n")
|
||||
```
|
||||
|
||||
### 异常类继承
|
||||
|
||||
Viper 支持异常类的继承,用于 `try/except` 的异常匹配:
|
||||
|
||||
```python
|
||||
class MyError(Exception):
|
||||
pass
|
||||
|
||||
class SpecificError(MyError):
|
||||
pass
|
||||
```
|
||||
|
||||
详见 [09-exceptions.md](09-exceptions.md)。
|
||||
|
||||
## 运算符重载
|
||||
|
||||
`@t.Object` 类支持运算符重载。编译器在遇到运算符时,首先检查左操作数是否为结构体类型,如果是则尝试调用对应的 dunder 方法。
|
||||
|
||||
### 算术运算符重载
|
||||
|
||||
| 运算符 | Dunder 方法 | 示例 |
|
||||
|--------|------------|------|
|
||||
| `+` | `__add__` | `a + b` → `a.__add__(b)` |
|
||||
| `-` | `__sub__` | `a - b` → `a.__sub__(b)` |
|
||||
| `*` | `__mul__` | `a * b` → `a.__mul__(b)` |
|
||||
| `/` | `__truediv__` | `a / b` → `a.__truediv__(b)` |
|
||||
| `//` | `__floordiv__` | `a // b` → `a.__floordiv__(b)` |
|
||||
| `%` | `__mod__` | `a % b` → `a.__mod__(b)` |
|
||||
| `**` | `__pow__` | `a ** b` → `a.__pow__(b)` |
|
||||
|
||||
示例:
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Vec2:
|
||||
x: t.CFloat
|
||||
y: t.CFloat
|
||||
|
||||
def __init__(self, x: t.CFloat, y: t.CFloat):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def __add__(self, other: Vec2) -> Vec2:
|
||||
result: Vec2 = Vec2(0.0, 0.0)
|
||||
result.x = self.x + other.x
|
||||
result.y = self.y + other.y
|
||||
return result
|
||||
|
||||
def __mul__(self, scalar: t.CFloat) -> Vec2:
|
||||
result: Vec2 = Vec2(0.0, 0.0)
|
||||
result.x = self.x * scalar
|
||||
result.y = self.y * scalar
|
||||
return result
|
||||
```
|
||||
|
||||
### 增量赋值运算符重载
|
||||
|
||||
| 增量运算符 | 优先 Dunder | 回退 Dunder |
|
||||
|-----------|------------|------------|
|
||||
| `+=` | `__iadd__` | `__add__` |
|
||||
| `-=` | `__isub__` | `__sub__` |
|
||||
| `*=` | `__imul__` | `__mul__` |
|
||||
| `/=` | `__itruediv__` | `__truediv__` |
|
||||
| `//=` | `__ifloordiv__` | `__floordiv__` |
|
||||
| `%=` | `__imod__` | `__mod__` |
|
||||
| `**=` | `__ipow__` | `__pow__` |
|
||||
|
||||
如果未定义增量版本(如 `__iadd__`),编译器自动回退到普通版本(如 `__add__`)。
|
||||
|
||||
### 比较运算符重载
|
||||
|
||||
> **实验性**:以下比较运算符 dunder 方法在编译器中被识别,但尚未实现自动调度。定义它们不会报错,但比较运算不会自动调用。
|
||||
|
||||
| 运算符 | Dunder 方法 | 状态 |
|
||||
|--------|------------|------|
|
||||
| `==` | `__eq__` | 仅类型推断识别 |
|
||||
| `!=` | `__ne__` | 仅类型推断识别 |
|
||||
| `<` | `__lt__` | 仅类型推断识别 |
|
||||
| `<=` | `__le__` | 仅类型推断识别 |
|
||||
| `>` | `__gt__` | 仅类型推断识别 |
|
||||
| `>=` | `__ge__` | 仅类型推断识别 |
|
||||
|
||||
### 反向运算符重载
|
||||
|
||||
> **实验性**:以下反向运算符 dunder 方法在编译器中被识别,但尚未实现自动调度。
|
||||
|
||||
| Dunder 方法 | 预期语义 | 状态 |
|
||||
|------------|---------|------|
|
||||
| `__radd__` | 右操作数加法 | 仅类型推断识别 |
|
||||
| `__rsub__` | 右操作数减法 | 仅类型推断识别 |
|
||||
| `__rmul__` | 右操作数乘法 | 仅类型推断识别 |
|
||||
| `__rtruediv__` | 右操作数真除法 | 仅类型推断识别 |
|
||||
| `__rfloordiv__` | 右操作数整除 | 仅类型推断识别 |
|
||||
| `__rmod__` | 右操作数取模 | 仅类型推断识别 |
|
||||
| `__rpow__` | 右操作数幂运算 | 仅类型推断识别 |
|
||||
|
||||
### 一元运算符重载
|
||||
|
||||
> **实验性**:以下一元运算符 dunder 方法在编译器中被识别,但尚未实现自动调度。
|
||||
|
||||
| 运算符 | Dunder 方法 | 状态 |
|
||||
|--------|------------|------|
|
||||
| `-a` | `__neg__` | 仅类型推断识别 |
|
||||
| `+a` | `__pos__` | 仅类型推断识别 |
|
||||
|
||||
## 可调用对象 `__call__`
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Counter:
|
||||
value: t.CInt
|
||||
|
||||
def __init__(self):
|
||||
self.value = 0
|
||||
|
||||
def __call__(self) -> t.CInt:
|
||||
self.value += 1
|
||||
return self.value
|
||||
|
||||
c: Counter = Counter()
|
||||
x: t.CInt = c() # x = 1
|
||||
y: t.CInt = c() # y = 2
|
||||
```
|
||||
|
||||
## 下标访问重载
|
||||
|
||||
### `__getitem__` — 读取
|
||||
|
||||
```python
|
||||
def __getitem__(self, key: t.CInt) -> t.CInt:
|
||||
return self.data[key]
|
||||
```
|
||||
|
||||
### `__setitem__` — 赋值
|
||||
|
||||
```python
|
||||
def __setitem__(self, key: t.CInt, value: t.CInt):
|
||||
self.data[key] = value
|
||||
```
|
||||
|
||||
### `__delitem__` — 删除
|
||||
|
||||
```python
|
||||
def __delitem__(self, key: t.CInt):
|
||||
pass
|
||||
```
|
||||
|
||||
## 布尔转换 `__bool__`
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Buffer:
|
||||
size: t.CInt
|
||||
data: list[t.CChar, 1024]
|
||||
|
||||
def __bool__(self) -> t.CBool:
|
||||
return self.size > 0
|
||||
|
||||
buf: Buffer = Buffer()
|
||||
if buf:
|
||||
serial.puts("buffer is not empty\n")
|
||||
```
|
||||
|
||||
## 长度 `__len__`
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Buffer:
|
||||
size: t.CInt
|
||||
|
||||
def __len__(self) -> t.CInt:
|
||||
return self.size
|
||||
|
||||
buf: Buffer = Buffer()
|
||||
n: t.CInt = len(buf)
|
||||
```
|
||||
|
||||
## 字符串转换 `__str__`
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class Point:
|
||||
x: t.CInt
|
||||
y: t.CInt
|
||||
|
||||
def __str__(self) -> t.CConst | t.CChar | t.CPtr:
|
||||
return "Point"
|
||||
|
||||
p: Point = Point(1, 2)
|
||||
print(p) # 调用 p.__str__()
|
||||
```
|
||||
|
||||
## 绝对值 `__abs__`
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class SignedValue:
|
||||
val: t.CInt
|
||||
|
||||
def __abs__(self) -> t.CInt:
|
||||
if self.val < 0:
|
||||
return -self.val
|
||||
return self.val
|
||||
|
||||
sv: SignedValue = SignedValue()
|
||||
sv.val = -42
|
||||
a: t.CInt = abs(sv) # a = 42
|
||||
```
|
||||
|
||||
## 迭代器协议
|
||||
|
||||
Viper 的迭代器协议与 Python 相同,但 `__next__` 的实现方式通过 `StopIteration` 异常来结束。
|
||||
|
||||
### `__iter__` 和 `__next__`
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class IntRange:
|
||||
start: t.CInt
|
||||
end: t.CInt
|
||||
current: t.CInt
|
||||
|
||||
def __init__(self, start: t.CInt, end: t.CInt):
|
||||
self.start = start
|
||||
self.end = end
|
||||
self.current = start
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self, stop_flag) -> t.CInt:
|
||||
if self.current >= self.end:
|
||||
raise StopIteration
|
||||
val: t.CInt = self.current
|
||||
self.current += 1
|
||||
return val
|
||||
```
|
||||
|
||||
### for 循环使用
|
||||
|
||||
```python
|
||||
r: IntRange = IntRange(0, 10)
|
||||
for i in r:
|
||||
print(i)
|
||||
```
|
||||
|
||||
编译器生成的等价逻辑:
|
||||
|
||||
```c
|
||||
IntRange r = IntRange_init(0, 10);
|
||||
IntRange iter = IntRange___iter__(&r);
|
||||
while (1) {
|
||||
i1 stop_flag = 0;
|
||||
int i = IntRange___next__(&iter, &stop_flag);
|
||||
if (stop_flag == 1) break;
|
||||
print_int(i);
|
||||
}
|
||||
```
|
||||
|
||||
## 上下文管理器
|
||||
|
||||
`with` 语句要求对象实现 `__enter__` 和 `__exit__` 方法:
|
||||
|
||||
```python
|
||||
@t.Object
|
||||
class File:
|
||||
fd: t.CInt
|
||||
path: t.CConst | t.CChar | t.CPtr
|
||||
|
||||
def __init__(self, path: t.CConst | t.CChar | t.CPtr, flags: t.CInt):
|
||||
self.path = path
|
||||
self.fd = -1
|
||||
|
||||
def __enter__(self):
|
||||
self.fd = fat32.open(self.path, self.flags)
|
||||
return self
|
||||
|
||||
def __exit__(self):
|
||||
fat32.close(self.fd)
|
||||
|
||||
def read(self) -> t.CInt:
|
||||
return fat32.read(self.fd)
|
||||
```
|
||||
|
||||
使用方式:
|
||||
|
||||
```python
|
||||
with File("/test.txt", FA_READ) as f:
|
||||
data: t.CInt = f.read()
|
||||
```
|
||||
|
||||
编译器会自动保证 `__exit__` 在 `with` 块结束时被调用。
|
||||
|
||||
## 析构 `__del__` / `__delete__`
|
||||
|
||||
```python
|
||||
del obj # 调用 obj.__del__() 或 obj.__delete__()
|
||||
del arr[idx] # 调用 arr.__delitem__(idx)
|
||||
```
|
||||
|
||||
## Dunder 方法完整参考
|
||||
|
||||
### 已实现自动调度的 Dunder 方法
|
||||
|
||||
| Dunder 方法 | 触发语法 | 说明 |
|
||||
|------------|---------|------|
|
||||
| `__init__` | `ClassName(args)` | 构造函数 |
|
||||
| `__before_init__` | 自动生成 | vtable 初始化,在 `__init__` 之前执行 |
|
||||
| `__add__` | `a + b` | 加法 |
|
||||
| `__sub__` | `a - b` | 减法 |
|
||||
| `__mul__` | `a * b` | 乘法 |
|
||||
| `__truediv__` | `a / b` | 真除法 |
|
||||
| `__floordiv__` | `a // b` | 整除 |
|
||||
| `__mod__` | `a % b` | 取模 |
|
||||
| `__pow__` | `a ** b` | 幂运算 |
|
||||
| `__iadd__` | `a += b` | 增量加法(回退到 `__add__`) |
|
||||
| `__isub__` | `a -= b` | 增量减法(回退到 `__sub__`) |
|
||||
| `__imul__` | `a *= b` | 增量乘法(回退到 `__mul__`) |
|
||||
| `__itruediv__` | `a /= b` | 增量真除法(回退到 `__truediv__`) |
|
||||
| `__ifloordiv__` | `a //= b` | 增量整除(回退到 `__floordiv__`) |
|
||||
| `__imod__` | `a %= b` | 增量取模(回退到 `__mod__`) |
|
||||
| `__ipow__` | `a **= b` | 增量幂运算(回退到 `__pow__`) |
|
||||
| `__call__` | `obj(args)` | 可调用对象 |
|
||||
| `__getitem__` | `obj[key]` | 下标读取 |
|
||||
| `__setitem__` | `obj[key] = value` | 下标赋值 |
|
||||
| `__delitem__` | `del obj[key]` | 下标删除 |
|
||||
| `__bool__` | `if obj:` | 布尔转换 |
|
||||
| `__len__` | `len(obj)` | 长度 |
|
||||
| `__str__` | `str(obj)` / `print(obj)` | 字符串转换 |
|
||||
| `__abs__` | `abs(obj)` | 绝对值 |
|
||||
| `__iter__` | `for x in obj:` | 获取迭代器 |
|
||||
| `__next__` | `for x in obj:` | 获取下一个值(标志位方式) |
|
||||
| `__enter__` | `with obj as x:` | 上下文管理器进入 |
|
||||
| `__exit__` | `with obj as x:` | 上下文管理器退出 |
|
||||
| `__del__` | `del obj` | 析构 |
|
||||
| `__delete__` | `del obj` | 删除描述符 |
|
||||
|
||||
### 声明支持但未实现自动调度的 Dunder 方法
|
||||
|
||||
| Dunder 方法 | 预期触发语法 | 当前状态 |
|
||||
|------------|------------|---------|
|
||||
| `__eq__` | `a == b` | 仅类型推断识别,比较运算不自动调度 |
|
||||
| `__ne__` | `a != b` | 同上 |
|
||||
| `__lt__` | `a < b` | 同上 |
|
||||
| `__le__` | `a <= b` | 同上 |
|
||||
| `__gt__` | `a > b` | 同上 |
|
||||
| `__ge__` | `a >= b` | 同上 |
|
||||
| `__neg__` | `-a` | 仅类型推断识别,一元负号不自动调度 |
|
||||
| `__pos__` | `+a` | 同上 |
|
||||
| `__radd__` | 右操作数加法 | 仅类型推断识别 |
|
||||
| `__rsub__` | 右操作数减法 | 同上 |
|
||||
| `__rmul__` | 右操作数乘法 | 同上 |
|
||||
| `__rtruediv__` | 右操作数真除法 | 同上 |
|
||||
| `__rfloordiv__` | 右操作数整除 | 同上 |
|
||||
| `__rmod__` | 右操作数取模 | 同上 |
|
||||
| `__rpow__` | 右操作数幂运算 | 同上 |
|
||||
256
wiki/07-control-flow.md
Normal file
256
wiki/07-control-flow.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# 07 - 控制流
|
||||
|
||||
Viper 支持 Python 风格的控制流语句,编译为 LLVM IR 的基本块和分支指令。
|
||||
|
||||
## 条件语句
|
||||
|
||||
### if / elif / else
|
||||
|
||||
```python
|
||||
if x > 0:
|
||||
y = 1
|
||||
elif x == 0:
|
||||
y = 0
|
||||
else:
|
||||
y = -1
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
if (x > 0) {
|
||||
y = 1;
|
||||
} else if (x == 0) {
|
||||
y = 0;
|
||||
} else {
|
||||
y = -1;
|
||||
}
|
||||
```
|
||||
|
||||
### 条件表达式
|
||||
|
||||
```python
|
||||
is_dir: t.CInt = 1 if (info.attr & AM_DIR) else 0
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
int is_dir = (info.attr & AM_DIR) ? 1 : 0;
|
||||
```
|
||||
|
||||
### None 检查
|
||||
|
||||
```python
|
||||
if BootInfo != None:
|
||||
paging.init(BootInfo.MemmapAddr, BootInfo.MemmapSize)
|
||||
else:
|
||||
paging.init(0, 0)
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
if (BootInfo != NULL) {
|
||||
paging_init(BootInfo->MemmapAddr, BootInfo->MemmapSize);
|
||||
} else {
|
||||
paging_init(0, 0);
|
||||
}
|
||||
```
|
||||
|
||||
## 循环语句
|
||||
|
||||
### while 循环
|
||||
|
||||
```python
|
||||
i: t.CInt = 0
|
||||
while i < 10:
|
||||
buf[i] = 0
|
||||
i += 1
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
int i = 0;
|
||||
while (i < 10) {
|
||||
buf[i] = 0;
|
||||
i += 1;
|
||||
}
|
||||
```
|
||||
|
||||
### while-else
|
||||
|
||||
```python
|
||||
while i < count:
|
||||
if found:
|
||||
break
|
||||
else:
|
||||
serial.puts("not found\n")
|
||||
```
|
||||
|
||||
`else` 块在循环正常结束(非 `break` 退出)时执行。
|
||||
|
||||
### 无限循环
|
||||
|
||||
```python
|
||||
while True:
|
||||
sched.Scheduler._yield()
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
while (1) {
|
||||
scheduler_yield();
|
||||
}
|
||||
```
|
||||
|
||||
### for 循环(range)(关于 for 循环的更多用法,参见 [06-oop.md 中 迭代器协议](06-oop.md#迭代器协议))
|
||||
|
||||
```python
|
||||
for i in range(10):
|
||||
buf[i] = 0
|
||||
|
||||
for i in range(5, 10):
|
||||
buf[i] = 0
|
||||
|
||||
for i in range(0, 100, 2):
|
||||
buf[i] = 0
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
for (int i = 0; i < 10; i++) {
|
||||
buf[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 5; i < 10; i++) {
|
||||
buf[i] = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; i += 2) {
|
||||
buf[i] = 0;
|
||||
}
|
||||
```
|
||||
|
||||
### for 循环(迭代器)
|
||||
|
||||
如果类实现了 `__iter__` 和 `__next__` 方法,可以使用 `for ... in` 迭代:
|
||||
|
||||
```python
|
||||
container: Iter = Iter()
|
||||
for item in container:
|
||||
process(item)
|
||||
```
|
||||
|
||||
### for-else
|
||||
|
||||
```python
|
||||
for i in range(count):
|
||||
if items[i] == target:
|
||||
break
|
||||
else:
|
||||
serial.puts("not found\n")
|
||||
```
|
||||
|
||||
### break 和 continue
|
||||
|
||||
```python
|
||||
while True:
|
||||
if done:
|
||||
break
|
||||
if skip:
|
||||
continue
|
||||
process()
|
||||
```
|
||||
|
||||
## match 语句
|
||||
|
||||
Viper 支持 Python 3.10+ 的 `match` 语句,编译为 C 的 `switch` 语句:
|
||||
|
||||
```python
|
||||
match self.ctype:
|
||||
case UI_LABEL:
|
||||
self.RenderLabel(sh)
|
||||
case UI_BUTTON:
|
||||
self.RenderButton(sh)
|
||||
case UI_PANEL:
|
||||
self.RenderPanel(sh)
|
||||
case _:
|
||||
pass
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
switch (self->ctype) {
|
||||
case UI_LABEL:
|
||||
self_RenderLabel(self, sh);
|
||||
break;
|
||||
case UI_BUTTON:
|
||||
self_RenderButton(self, sh);
|
||||
break;
|
||||
case UI_PANEL:
|
||||
self_RenderPanel(self, sh);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
### match 值匹配
|
||||
|
||||
```python
|
||||
match code:
|
||||
case 0:
|
||||
serial.puts("OK\n")
|
||||
case 1:
|
||||
serial.puts("Error\n")
|
||||
case _:
|
||||
serial.puts("Unknown\n")
|
||||
```
|
||||
|
||||
### match 或模式
|
||||
|
||||
```python
|
||||
match value:
|
||||
case 1 | 2 | 3:
|
||||
serial.puts("small\n")
|
||||
case _:
|
||||
serial.puts("other\n")
|
||||
```
|
||||
|
||||
### fallthrough(无 break)
|
||||
|
||||
默认每个 `case` 自动添加 `break`。如需 fallthrough,使用 `c.NoBreak`:
|
||||
|
||||
```python
|
||||
match value:
|
||||
case 1:
|
||||
do_one()
|
||||
c.NoBreak
|
||||
case 2:
|
||||
do_two()
|
||||
```
|
||||
|
||||
如果需要提前 break,需要使用 `c.Break()`,因为在 `Python` 中,`match` 分支不支持直接使用 `break`。
|
||||
|
||||
## assert 语句
|
||||
> 避免使用此语句,此语句未经充分测试,在多数情况下,编译器不知道如何展示结果。
|
||||
|
||||
```python
|
||||
assert ptr != None, "null pointer"
|
||||
```
|
||||
|
||||
编译为条件检查和错误报告。
|
||||
|
||||
## with 语句
|
||||
|
||||
`with` 语句用于资源管理,要求对象实现 `__enter__` 和 `__exit__` 方法:
|
||||
|
||||
```python
|
||||
with File("/test.txt", FA_READ) as f:
|
||||
data: t.CInt = f.read()
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
File* f = File_enter(File_new("/test.txt", FA_READ));
|
||||
int data = File_read(f);
|
||||
File_exit(f);
|
||||
```
|
||||
304
wiki/08-c-operations.md
Normal file
304
wiki/08-c-operations.md
Normal file
@@ -0,0 +1,304 @@
|
||||
# 08 - C 语言操作
|
||||
|
||||
Viper 通过 `c` 模块提供对 C 语言底层特性的访问,包括指针操作、内联汇编、预处理指令等。
|
||||
|
||||
## 指针操作
|
||||
|
||||
### 取地址 `c.Addr`
|
||||
|
||||
```python
|
||||
x: t.CInt = 42
|
||||
p: t.CInt | t.CPtr = c.Addr(x) # int* p = &x;
|
||||
```
|
||||
|
||||
对结构体成员取地址:
|
||||
|
||||
```python
|
||||
res: t.CInt = fat32.opendir("/", c.Addr(dp)) # res = fat32_opendir("/", &dp);
|
||||
```
|
||||
|
||||
对数组取地址:
|
||||
|
||||
```python
|
||||
viperlib.snprintf(c.Addr(buf), 64, "hello %d", 42) # snprintf(&buf, 64, "hello %d", 42);
|
||||
```
|
||||
|
||||
### 解引用 `c.Deref`
|
||||
|
||||
```python
|
||||
ptr: Sheet | t.CPtr = c.Addr(sheet_obj)
|
||||
obj: Sheet = c.Deref(ptr) # struct Sheet obj = *ptr;
|
||||
```
|
||||
|
||||
### 内存拷贝 `c.Load`
|
||||
|
||||
```python
|
||||
c.Load(ptr, value) # *ptr = *value;
|
||||
```
|
||||
|
||||
### 解引用赋值 `c.DerefAs`
|
||||
|
||||
```python
|
||||
c.DerefAs(ptr, value) # *ptr = value;
|
||||
```
|
||||
|
||||
|
||||
### 赋值 `c.Set`
|
||||
|
||||
```python
|
||||
c.Set(target, value) # target = value;
|
||||
```
|
||||
|
||||
## 内联汇编 `c.Asm`
|
||||
|
||||
Viper 提供了声明式的内联汇编语法,编译为 GCC 风格的 `__asm__ __volatile__` 语句。
|
||||
|
||||
### 基本用法
|
||||
|
||||
```python
|
||||
c.Asm("nop") # __asm__ __volatile__("nop");
|
||||
```
|
||||
|
||||
### 带操作数的汇编
|
||||
|
||||
使用 f-string 和 `c.AsmInp` / `c.AsmOut` 标记操作数:
|
||||
|
||||
```python
|
||||
saved_rdi: t.CUnsignedLong
|
||||
c.Asm(f"mov {c.AsmOut(saved_rdi, t.ASM_DESCR.OUTPUT_REG)}, rdi",
|
||||
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RDI])
|
||||
```
|
||||
|
||||
等价 C 代码:
|
||||
```c
|
||||
__asm__ __volatile__(
|
||||
"mov %0, rdi"
|
||||
: "=r"(saved_rdi)
|
||||
:
|
||||
: "memory", "rdi"
|
||||
);
|
||||
```
|
||||
|
||||
### 输入操作数 `c.AsmInp`
|
||||
|
||||
```python
|
||||
msg: t.CConst | t.CChar | t.CPtr = "Hello"
|
||||
c.Asm(f"mov rdi, {c.AsmInp(msg, t.ASM_DESCR.REG_ANY)}",
|
||||
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX])
|
||||
```
|
||||
|
||||
### 完整示例
|
||||
|
||||
```python
|
||||
c.Asm(f"""mov rdi, {c.AsmInp(msg, t.ASM_DESCR.REG_ANY)}
|
||||
call {c.AsmInp(log_info_fn, t.ASM_DESCR.REG_ANY)}""",
|
||||
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX,
|
||||
t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX,
|
||||
t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_RSI,
|
||||
t.ASM_DESCR.CLOBBER_R8, t.ASM_DESCR.CLOBBER_R9,
|
||||
t.ASM_DESCR.CLOBBER_R10, t.ASM_DESCR.CLOBBER_R11])
|
||||
```
|
||||
|
||||
### ASM_DESCR 约束字符
|
||||
|
||||
#### 操作数修饰符
|
||||
|
||||
| 常量 | 值 | 说明 |
|
||||
|------|----|------|
|
||||
| `MODIFIER_OUTPUT` | `=` | 输出操作数 |
|
||||
| `MODIFIER_READWRITE` | `+` | 读写操作数 |
|
||||
| `MODIFIER_INPUT` | `` | 输入操作数(默认) |
|
||||
| `MODIFIER_GLOBAL` | `&` | 全局操作数 |
|
||||
|
||||
#### 寄存器约束
|
||||
|
||||
| 常量 | 值 | 说明 |
|
||||
|------|----|------|
|
||||
| `REG_ANY` | `r` | 任何通用寄存器 |
|
||||
| `REG_EAX` / `REG_RAX` | `a` | EAX/RAX |
|
||||
| `REG_EBX` / `REG_RBX` | `b` | EBX/RBX |
|
||||
| `REG_ECX` / `REG_RCX` | `c` | ECX/RCX |
|
||||
| `REG_EDX` / `REG_RDX` | `d` | EDX/RDX |
|
||||
| `REG_ESI` / `REG_RSI` | `S` | ESI/RSI |
|
||||
| `REG_EDI` / `REG_RDI` | `D` | EDI/RDI |
|
||||
| `REG_XMM` | `x` | XMM 寄存器 |
|
||||
|
||||
#### 内存与立即数
|
||||
|
||||
| 常量 | 值 | 说明 |
|
||||
|------|----|------|
|
||||
| `MEMORY` | `m` | 内存操作数 |
|
||||
| `IMMEDIATE` | `i` | 立即数 |
|
||||
| `ANY` | `g` | 通用寄存器/内存/立即数 |
|
||||
|
||||
#### 预定义组合约束
|
||||
|
||||
| 常量 | 值 | 说明 |
|
||||
|------|----|------|
|
||||
| `OUTPUT_REG` | `=r` | 输出,通用寄存器 |
|
||||
| `OUTPUT_MEM` | `=m` | 输出,内存 |
|
||||
| `OUTPUT_EAX` | `=a` | 输出,EAX/RAX |
|
||||
| `INPUT_REG` | `r` | 输入,通用寄存器 |
|
||||
| `INPUT_MEM` | `m` | 输入,内存 |
|
||||
| `INPUT_IMM` | `i` | 输入,立即数 |
|
||||
|
||||
#### 破坏描述符
|
||||
|
||||
| 常量 | 说明 |
|
||||
|------|------|
|
||||
| `CLOBBER_EAX` / `CLOBBER_RAX` | 破坏 EAX/RAX |
|
||||
| `CLOBBER_EBX` / `CLOBBER_RBX` | 破坏 EBX/RBX |
|
||||
| `CLOBBER_ECX` / `CLOBBER_RCX` | 破坏 ECX/RCX |
|
||||
| `CLOBBER_EDX` / `CLOBBER_RDX` | 破坏 EDX/RDX |
|
||||
| `CLOBBER_ESI` / `CLOBBER_RSI` | 破坏 ESI/RSI |
|
||||
| `CLOBBER_EDI` / `CLOBBER_RDI` | 破坏 EDI/RDI |
|
||||
| `CLOBBER_CC` | 破坏条件码(标志寄存器) |
|
||||
| `CLOBBER_MEMORY` | 破坏内存 |
|
||||
| `CLOBBER_R8` ~ `CLOBBER_R15` | 破坏 R8~R15 |
|
||||
|
||||
## 预处理指令
|
||||
|
||||
Viper 通过 `c` 模块的函数调用实现 C 预处理指令。
|
||||
|
||||
### #define
|
||||
|
||||
```python
|
||||
c.CDefine("MAX_SIZE", 1024) # #define MAX_SIZE 1024
|
||||
```
|
||||
|
||||
上述方法容易引起未定义行为,至少是不便于理解,更常用的方式是使用类型注解:
|
||||
|
||||
```python
|
||||
MAX_SIZE: t.CDefine = 1024 # #define MAX_SIZE 1024
|
||||
```
|
||||
|
||||
### 条件编译
|
||||
|
||||
```python
|
||||
c.CIfndef(HEADER_H) # #ifndef HEADER_H
|
||||
c.CDefine(HEADER_H) # #define HEADER_H
|
||||
c.CEndif() # #endif
|
||||
```
|
||||
|
||||
```python
|
||||
c.CIfdef(DEBUG) # #ifdef DEBUG
|
||||
c.CEndif() # #endif
|
||||
```
|
||||
|
||||
```python
|
||||
c.CIf(VERSION > 2) # #if VERSION > 2
|
||||
c.CElif(VERSION > 1) # #elif VERSION > 1
|
||||
c.CElse() # #else
|
||||
c.CEndif() # #endif
|
||||
```
|
||||
|
||||
### #undef
|
||||
|
||||
```python
|
||||
c.Undef(MACRO_NAME) # #undef MACRO_NAME
|
||||
```
|
||||
|
||||
### #error
|
||||
|
||||
```python
|
||||
c.CError("Platform not supported") # #error "Platform not supported"
|
||||
```
|
||||
|
||||
### #pragma
|
||||
> 此关键字已不再支持
|
||||
```python
|
||||
c.CPragma("GCC diagnostic push") # #pragma GCC diagnostic push
|
||||
```
|
||||
|
||||
### ## 连接符
|
||||
> 此方法可能已经不再支持
|
||||
```python
|
||||
c.TokenPast("PREFIX_", "NAME") # PREFIX_ ## NAME
|
||||
```
|
||||
|
||||
由于编译器设计,以及不需要像 `C` 一样做大量字符替换,对于上述所有的条件宏都远比 `C` 差,实际工程中不推荐使用条件宏,后期将完善宏的设计,引入模板等。
|
||||
|
||||
## FFI 外部函数声明
|
||||
|
||||
Viper 的 FFI(Foreign Function Interface)**无需任何特殊语法**——仅靠返回类型注解中的 `t.State` 标记即可声明外部函数。`t.State` 是声明性标记类型,表示"仅声明不定义"(语义等价于 `t.CExport | t.CExtern`),编译器只生成 `declare` 原型,不生成 `define` 函数体,由链接器在外部库中解析符号。
|
||||
|
||||
### 基本声明
|
||||
|
||||
函数体为 `pass`,返回类型用 `RetType | t.State`:
|
||||
|
||||
```python
|
||||
def isr0() -> t.CVoid | t.State: pass # extern void isr0(); — 来自汇编或外部
|
||||
def getchar() -> t.CInt | t.State: pass # extern int getchar(); — 来自 libc
|
||||
```
|
||||
|
||||
编译为 LLVM IR:
|
||||
|
||||
```llvm
|
||||
declare void @"isr0"() ; 不加 SHA1 前缀(t.State 含 CExport 语义)
|
||||
declare i32 @"getchar"()
|
||||
```
|
||||
|
||||
### Win32 API 绑定示例
|
||||
|
||||
[includes/w32](../includes/w32) 中的 Win32 绑定全部通过 `t.State` 声明,零特殊语法:
|
||||
|
||||
```python
|
||||
def CreateFileA(lpFileName: LPCSTR, dwDesiredAccess: ULONG,
|
||||
dwShareMode: ULONG,
|
||||
lpSecurityAttributes: SECURITY_ATTRIBUTES | t.CPtr,
|
||||
dwCreationDisposition: ULONG,
|
||||
dwFlagsAndAttributes: ULONG,
|
||||
hTemplateFile: HANDLE) -> HANDLE | t.State:
|
||||
pass
|
||||
```
|
||||
|
||||
等价 C 声明:
|
||||
|
||||
```c
|
||||
HANDLE CreateFileA(LPCSTR, ULONG, ULONG, SECURITY_ATTRIBUTES*,
|
||||
ULONG, ULONG, HANDLE);
|
||||
```
|
||||
|
||||
链接时通过 `project.json` 指定外部库(如 `kernel32.lib`、`user32.lib`),链接器解析这些符号。
|
||||
|
||||
### `t.State` 与 `t.CExtern` 的区别
|
||||
|
||||
| 修饰 | SHA1 前缀 | 生成内容 | 用途 |
|
||||
|------|----------|---------|------|
|
||||
| 无修饰 | ✅ 加前缀 | `declare` + `define` | 模块私有函数 |
|
||||
| `t.CExtern` | ❌ 不加 | `declare`(引用外部) | 引用外部 C 函数 |
|
||||
| `t.CExport` | ❌ 不加 | `declare` + `define` | 对外暴露 API |
|
||||
| `t.State` | ❌ 不加 | 仅 `declare` | FFI 外部函数声明(= `CExport \| CExtern`) |
|
||||
|
||||
### 声明外部全局变量
|
||||
|
||||
外部全局变量通过 `t.CExtern` 注解声明:
|
||||
|
||||
```python
|
||||
errno: t.CExtern | t.CInt # extern int errno;
|
||||
```
|
||||
|
||||
详见 [02-type-system.md 的 t.State 章节](02-type-system.md#tstate-声明性标记) 和 [includes/w32](../includes/w32) 的 Win32 绑定实现。
|
||||
|
||||
## LLVM IR 内联
|
||||
|
||||
### c.LLVMIR
|
||||
|
||||
直接嵌入 LLVM IR 指令来实现高效且跨平台的汇编操作:
|
||||
|
||||
```python
|
||||
c.LLVMIR(f"add i32 {c.LInp(a)}, {c.LInp(b)}", t.CInt)
|
||||
```
|
||||
|
||||
### c.LInp / c.LOut
|
||||
|
||||
标记 LLVM IR 的输入/输出操作数:
|
||||
|
||||
```python
|
||||
c.LInp(expr) # 输入操作数
|
||||
c.LOut(expr) # 输出操作数
|
||||
```
|
||||
|
||||
## 运算符重载
|
||||
|
||||
`@t.Object` 类支持运算符重载,详见 [06-oop.md 中 运算符重载](06-oop.md#运算符重载)。
|
||||
186
wiki/09-exceptions.md
Normal file
186
wiki/09-exceptions.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# 09 - 异常处理
|
||||
|
||||
Viper 支持 Python 风格的 `try/except/finally` 异常处理,编译为基于 jmp_buf 和错误码的 C 级别异常处理机制。
|
||||
|
||||
## 异常处理机制
|
||||
|
||||
Viper 的异常处理不使用 C++ 风格的零开销异常,而是通过函数参数传递错误码和错误消息实现。编译器自动为可能抛出异常的函数添加 `__eh_msg_out__` 和 `__eh_code_out__` 参数。
|
||||
|
||||
## try / except
|
||||
|
||||
```python
|
||||
try:
|
||||
result = risky_operation()
|
||||
except ValueError:
|
||||
serial.puts("ValueError occurred\n")
|
||||
except OSError:
|
||||
serial.puts("OSError occurred\n")
|
||||
except Exception:
|
||||
serial.puts("Unknown error\n")
|
||||
```
|
||||
|
||||
### 内置异常类型
|
||||
|
||||
Viper 预定义了以下异常类型及其错误码:
|
||||
|
||||
| 异常类型 | 错误码 |
|
||||
|---------|--------|
|
||||
| `ValueError` | 1 |
|
||||
| `TypeError` | 2 |
|
||||
| `RuntimeError` | 3 |
|
||||
| `ZeroDivisionError` | 4 |
|
||||
| `IndexError` | 5 |
|
||||
| `KeyError` | 6 |
|
||||
| `IOError` | 7 |
|
||||
| `OSError` | 8 |
|
||||
| `AssertionError` | 9 |
|
||||
| `Exception` | 99 |
|
||||
|
||||
### 自定义异常
|
||||
|
||||
```python
|
||||
class DiskFullError(Exception):
|
||||
pass
|
||||
|
||||
class ReadOnlyError(DiskFullError):
|
||||
pass
|
||||
```
|
||||
|
||||
自定义异常会自动分配递增的错误码(从 100 开始),并支持继承匹配。
|
||||
|
||||
### 多异常捕获
|
||||
|
||||
```python
|
||||
try:
|
||||
result = operation()
|
||||
except (ValueError, TypeError):
|
||||
serial.puts("Value or Type error\n")
|
||||
```
|
||||
|
||||
### 获取异常信息
|
||||
|
||||
```python
|
||||
try:
|
||||
result = operation()
|
||||
except Exception as e:
|
||||
serial.puts("Error occurred\n")
|
||||
```
|
||||
|
||||
## try / finally
|
||||
|
||||
```python
|
||||
try:
|
||||
result = operation()
|
||||
finally:
|
||||
cleanup()
|
||||
```
|
||||
|
||||
`finally` 块无论是否发生异常都会执行。
|
||||
|
||||
## try / except / finally
|
||||
|
||||
```python
|
||||
try:
|
||||
result = operation()
|
||||
except ValueError:
|
||||
handle_value_error()
|
||||
finally:
|
||||
cleanup()
|
||||
```
|
||||
|
||||
## raise 语句
|
||||
|
||||
### 抛出异常
|
||||
|
||||
```python
|
||||
raise ValueError("invalid value")
|
||||
```
|
||||
|
||||
### 抛出自定义异常
|
||||
|
||||
```python
|
||||
raise MyCustomError("something went wrong")
|
||||
```
|
||||
|
||||
### 重新抛出异常
|
||||
|
||||
```python
|
||||
raise
|
||||
```
|
||||
|
||||
### StopIteration
|
||||
|
||||
`StopIteration` 有特殊处理,用于迭代器协议:
|
||||
|
||||
```python
|
||||
raise StopIteration
|
||||
```
|
||||
|
||||
## 异常处理的编译实现
|
||||
|
||||
### 函数签名变换
|
||||
|
||||
可能抛出异常的函数会自动添加错误输出参数:
|
||||
|
||||
```python
|
||||
def risky_operation() -> t.CInt:
|
||||
if error:
|
||||
raise ValueError("bad value")
|
||||
return 0
|
||||
```
|
||||
|
||||
编译后等价于:
|
||||
|
||||
```c
|
||||
int risky_operation(char** __eh_msg_out__, int* __eh_code_out__) {
|
||||
if (error) {
|
||||
*__eh_code_out__ = 1; // ValueError code
|
||||
*__eh_msg_out__ = "bad value";
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 调用点变换
|
||||
|
||||
```python
|
||||
try:
|
||||
result = risky_operation()
|
||||
except ValueError:
|
||||
handle_error()
|
||||
```
|
||||
|
||||
编译后等价于:
|
||||
|
||||
```c
|
||||
char* __eh_msg = NULL;
|
||||
int __eh_code = 0;
|
||||
int result = risky_operation(&__eh_msg, &__eh_code);
|
||||
if (__eh_msg != NULL) {
|
||||
if (__eh_code == 1) { // ValueError
|
||||
handle_error();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 异常继承匹配
|
||||
|
||||
异常匹配支持继承体系。捕获父类异常时,子类异常也会被匹配:
|
||||
|
||||
```python
|
||||
class FileSystemError(Exception):
|
||||
pass
|
||||
|
||||
class FileNotFoundError(FileSystemError):
|
||||
pass
|
||||
|
||||
class PermissionError(FileSystemError):
|
||||
pass
|
||||
|
||||
try:
|
||||
operation()
|
||||
except FileSystemError:
|
||||
# 也会捕获 FileNotFoundError 和 PermissionError
|
||||
handle_fs_error()
|
||||
```
|
||||
258
wiki/10-imports.md
Normal file
258
wiki/10-imports.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# 10 - 模块与导入系统
|
||||
|
||||
Viper 的模块系统基于 Python 的 `import` 语法,但编译时通过 TransPyC 的符号表和类型系统实现跨模块的类型解析。
|
||||
|
||||
## 导入语法
|
||||
|
||||
### 标准导入
|
||||
|
||||
```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 部分使用 x86_64 专有汇编进行加速,因此某些函数在其它架构可能需要手动重新编写。
|
||||
|
||||
```python
|
||||
import string
|
||||
|
||||
string.memset(c.Addr(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(c.Addr(buf), 64, "value=%d", 42) # snprintf(&buf, 64, "value=%d", 42);
|
||||
```
|
||||
|
||||
### `vpsdk` 模块
|
||||
|
||||
> vpsdk 模块 专供于基于 Viper 开发的示例操作系统 ViperOS,不可用于其它平台,且需要特殊配置。
|
||||
|
||||
ViperOS 应用 SDK,提供系统调用和窗口管理:
|
||||
|
||||
```python
|
||||
import vpsdk.window as window
|
||||
import vpsdk.process as process
|
||||
import vpsdk.dynlib as dynlib
|
||||
import vpsdk.syscall as syscall
|
||||
```
|
||||
|
||||
### `ast` 模块(自研解析器)
|
||||
|
||||
[includes/ast](../includes/ast) 是**完全自研的 Python 解析器**(词法器 + 语法器 + AST 构造,约 4176 LOC),覆盖 CPython 几乎全部语法。这是向 `TransPyV` 完全自举的关键组件——自举后编译器不再依赖 Python 运行时解析源文件。
|
||||
|
||||
```python
|
||||
from ast import parse, parse_expression
|
||||
|
||||
root: AST | t.CPtr = parse(src, pool) # 解析整个源文件
|
||||
expr: AST | t.CPtr = 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 文本生成库**(约 1500 LOC),封装类型/值/模块/函数/基本块/构造器的创建与打印。同样是自举的关键组件。
|
||||
|
||||
```python
|
||||
from llvmlite import LLVMTypeCore, LLVMModuleCore
|
||||
|
||||
type_core: LLVMTypeCore = LLVMTypeCore(pool)
|
||||
i32: LLVMType | t.CPtr = type_core.Int32()
|
||||
ptr_ty: LLVMType | t.CPtr = type_core.Ptr(i32)
|
||||
|
||||
mod_core: LLVMModuleCore = LLVMModuleCore(pool)
|
||||
mod: Module | t.CPtr = mod_core.NewModule("main")
|
||||
func: Function | t.CPtr = 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` 模块(NoVTable + PEP 695 泛型示范)
|
||||
|
||||
[includes/linkedlist](../includes/linkedlist.py) 提供链表实现,是 `@t.NoVTable` 非多态继承和 PEP 695 递归泛型继承的典范用例:
|
||||
|
||||
```python
|
||||
from linkedlist import LinkedNode, GSList
|
||||
|
||||
@t.NoVTable
|
||||
class Node(LinkedNode): # 非多态继承,零 vtable 开销
|
||||
value: t.CInt
|
||||
|
||||
lst: GSList[Node] | t.CPtr = GSList[Node]()
|
||||
node: Node | t.CPtr = 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 的 NoVTable 章节](06-oop.md#tnovtable-非多态继承)。
|
||||
|
||||
### `w32` 模块(Win32 FFI 绑定)
|
||||
|
||||
[includes/w32](../includes/w32) 提供 Win32 API 绑定,全部通过 `t.State` 的 FFI 机制声明(零特殊语法):
|
||||
|
||||
```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-外部函数声明)。
|
||||
|
||||
## 模块解析流程
|
||||
|
||||
### 编译时模块加载
|
||||
|
||||
Viper 的 `import` 是编译时操作,不涉及运行时模块加载。完整的模块解析流程与两阶段编译紧密耦合:
|
||||
|
||||
1. **阶段一**:从入口文件出发,通过 `import` 语句递归发现所有可达的源文件
|
||||
2. **阶段一**:对每个源文件生成 `<SHA1>.pyi` 签名存根和 `<SHA1>.stub.ll` LLVM IR 声明
|
||||
3. **阶段二**:加载所有 `.pyi` 和 `.stub.ll`,构建共享符号表
|
||||
4. **阶段二**:翻译源文件时,通过符号表解析跨模块的类型和函数引用
|
||||
5. **阶段二**:将被引用模块的 `.stub.ll` 声明嵌入到生成的 `.ll` 文件头部
|
||||
|
||||
### 类型存根文件(.pyi)
|
||||
|
||||
`.pyi` 文件是模块的声明接口,由 `PythonToStubConverter` 在阶段一生成。存根文件的内容规则:
|
||||
|
||||
| 元素 | 存根表示 |
|
||||
|------|---------|
|
||||
| `t.CDefine` 常量 | 保留完整定义(含赋值) |
|
||||
| `t.CDefine` 函数 | 保留完整函数体 |
|
||||
| 普通函数 | 仅签名 + `t.State` |
|
||||
| 全局变量 | 添加 `t.CExtern` |
|
||||
| 类定义 | 保留成员类型注解和方法签名 |
|
||||
|
||||
存根文件还自动添加宏守卫(`c.CIfndef`/`c.CEndif()`),防止重复包含。
|
||||
|
||||
### SHA1 命名空间与导入
|
||||
|
||||
每个模块的函数和结构体在 LLVM IR 中自动加上 SHA1 前缀,消除跨模块符号冲突。只有标记为 `t.CExport` 的函数保持原始名称。详见 [01-overview.md 中 SHA1 命名空间机制](01-overview.md#sha1-命名空间机制)。
|
||||
|
||||
### 符号表
|
||||
|
||||
所有模块的类型信息统一存储在 `SymbolTable` 中,包含:
|
||||
- 结构体/联合体/枚举定义
|
||||
- 函数签名
|
||||
- 全局变量
|
||||
- typedef 别名
|
||||
- `t.CDefine` 常量
|
||||
|
||||
### 跨模块类型引用
|
||||
|
||||
```python
|
||||
# 在 fat32_types.py 中定义
|
||||
class fat32_fileinfo:
|
||||
fname: list[t.CChar, 13]
|
||||
attr: t.CUInt8T
|
||||
file_size: t.CUInt32T
|
||||
|
||||
# 在其他模块中引用
|
||||
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` 指定的目录中搜索导入的模块。
|
||||
204
wiki/11-builtins.md
Normal file
204
wiki/11-builtins.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# 11 - 内置函数与运算符
|
||||
|
||||
Viper 支持 Python 内置函数和运算符,编译为对应的 C/LLVM 操作。
|
||||
|
||||
## 内置函数
|
||||
|
||||
### print
|
||||
|
||||
```python
|
||||
print("Hello, World!") # 调用 puts 或 printf
|
||||
print(x, y) # 多参数输出
|
||||
```
|
||||
|
||||
### len
|
||||
|
||||
```python
|
||||
n: t.CInt = len(array) # 获取数组长度(编译时常量)
|
||||
```
|
||||
|
||||
### sizeof
|
||||
|
||||
```python
|
||||
s: t.CSizeT = sizeof(t.CInt) # sizeof(int)
|
||||
s: t.CSizeT = obj.__sizeof__() # sizeof(obj)
|
||||
```
|
||||
|
||||
### abs
|
||||
|
||||
```python
|
||||
a: t.CInt = abs(x) # abs(x)
|
||||
```
|
||||
|
||||
### int / float / bool
|
||||
|
||||
```python
|
||||
i: t.CInt = int(3.14) # (int)3.14
|
||||
f: t.CFloat = float(42) # (float)42
|
||||
b: t.CBool = bool(x) # x != 0
|
||||
```
|
||||
|
||||
### min / max
|
||||
|
||||
```python
|
||||
m: t.CInt = min(a, b) # a < b ? a : b
|
||||
m: t.CInt = max(a, b) # a > b ? a : b
|
||||
```
|
||||
|
||||
### chr / ord
|
||||
|
||||
```python
|
||||
c: t.CChar = chr(65) # 'A' (整数转字符)
|
||||
n: t.CInt = ord('A') # 65 (字符转整数)
|
||||
```
|
||||
|
||||
### range
|
||||
|
||||
用于 `for` 循环:
|
||||
|
||||
```python
|
||||
for i in range(10): pass # for (int i = 0; i < 10; i++)
|
||||
for i in range(5, 10): pass # for (int i = 5; i < 10; i++)
|
||||
for i in range(0, 10, 2): pass # for (int i = 0; i < 10; i += 2)
|
||||
```
|
||||
|
||||
## 运算符
|
||||
|
||||
### 算术运算符
|
||||
|
||||
| Viper | C 等价 | 说明 |
|
||||
|-------|--------|------|
|
||||
| `a + b` | `a + b` | 加法 |
|
||||
| `a - b` | `a - b` | 减法 |
|
||||
| `a * b` | `a * b` | 乘法 |
|
||||
| `a / b` | `a / b` | 浮点除法 |
|
||||
| `a // b` | `a / b` | 整数除法 |
|
||||
| `a % b` | `a % b` | 取模 |
|
||||
| `a ** b` | `pow(a, b)` | 幂运算 |
|
||||
| `-a` | `-a` | 取负 |
|
||||
| `+a` | `+a` | 正号 |
|
||||
|
||||
### 位运算符
|
||||
|
||||
| Viper | C 等价 | 说明 |
|
||||
|-------|--------|------|
|
||||
| `a & b` | `a & b` | 按位与 |
|
||||
| `a \| b` | `a \| b` | 按位或 |
|
||||
| `a ^ b` | `a ^ b` | 按位异或 |
|
||||
| `~a` | `~a` | 按位取反 |
|
||||
| `a << n` | `a << n` | 左移 |
|
||||
| `a >> n` | `a >> n` | 右移 |
|
||||
|
||||
> **注意**:`|` 运算符在类型注解中表示类型组合,在表达式中表示按位或。编译器根据上下文区分。
|
||||
|
||||
### 比较运算符
|
||||
|
||||
| Viper | C 等价 | 说明 |
|
||||
|-------|--------|------|
|
||||
| `a == b` | `a == b` | 等于 |
|
||||
| `a != b` | `a != b` | 不等于 |
|
||||
| `a < b` | `a < b` | 小于 |
|
||||
| `a > b` | `a > b` | 大于 |
|
||||
| `a <= b` | `a <= b` | 小于等于 |
|
||||
| `a >= b` | `a >= b` | 大于等于 |
|
||||
|
||||
### 逻辑运算符
|
||||
|
||||
| Viper | C 等价 | 说明 |
|
||||
|-------|--------|------|
|
||||
| `a and b` | `a && b` | 逻辑与 |
|
||||
| `a or b` | `a \|\| b` | 逻辑或 |
|
||||
| `not a` | `!a` | 逻辑非 |
|
||||
|
||||
### 增强赋值运算符
|
||||
|
||||
| Viper | C 等价 |
|
||||
|-------|--------|
|
||||
| `a += b` | `a += b` |
|
||||
| `a -= b` | `a -= b` |
|
||||
| `a *= b` | `a *= b` |
|
||||
| `a //= b` | `a /= b` |
|
||||
| `a %= b` | `a %= b` |
|
||||
| `a <<= b` | `a <<= b` |
|
||||
| `a >>= b` | `a >>= b` |
|
||||
| `a &= b` | `a &= b` |
|
||||
| `a \|= b` | `a \|= b` |
|
||||
| `a ^= b` | `a ^= b` |
|
||||
|
||||
## 类型自动转换
|
||||
|
||||
Viper 在算术运算中自动进行类型提升:
|
||||
|
||||
1. **整数宽度提升**:较窄的整数类型自动扩展为较宽的类型
|
||||
- 有符号类型使用符号扩展(`sext`)
|
||||
- 无符号类型使用零扩展(`zext`)
|
||||
|
||||
2. **整数到浮点**:整数与浮点数运算时,整数自动转换为浮点
|
||||
|
||||
3. **浮点精度提升**:`float` 与 `double` 运算时,`float` 提升为 `double`
|
||||
|
||||
## 指针算术
|
||||
|
||||
指针与整数的加法支持自动计算偏移:
|
||||
|
||||
```python
|
||||
buf: t.CUInt32T | t.CPtr
|
||||
buf[i] = value # buf[i] = value; (自动计算偏移)
|
||||
```
|
||||
|
||||
## 数组下标
|
||||
|
||||
```python
|
||||
buf: list[t.CChar, 64]
|
||||
buf[0] = 'H' # buf[0] = 'H';
|
||||
buf[i] = value # buf[i] = value;
|
||||
|
||||
info.fname[0] # info.fname[0] (结构体数组成员访问)
|
||||
```
|
||||
|
||||
## 成员访问
|
||||
|
||||
```python
|
||||
obj.x # obj.x (直接成员)
|
||||
obj.method() # obj.method() (方法调用)
|
||||
ptr.x # ptr->x (指针自动解引用)
|
||||
```
|
||||
|
||||
## 字符串字面量
|
||||
|
||||
字符串字面量编译为 C 字符串常量(`const char*`),其为只读:
|
||||
|
||||
```python
|
||||
serial.puts("Hello") # 传递 const char*
|
||||
```
|
||||
|
||||
### 字符串索引
|
||||
|
||||
```python
|
||||
msg: t.CConst | str = "Hello"
|
||||
serial.puts(c.Addr(msg[4])) # 访问 msg[4] 的地址,输出 'o'
|
||||
```
|
||||
|
||||
## C 标准库函数
|
||||
|
||||
Viper 内置支持以下 C 标准库函数的直接调用:
|
||||
|
||||
| 函数 | 签名 |
|
||||
|------|------|
|
||||
| `strlen(s)` | `size_t strlen(const char* s)` |
|
||||
| `memset(s, c, n)` | `void* memset(void* s, int c, size_t n)` |
|
||||
| `memcpy(d, s, n)` | `void* memcpy(void* d, const void* s, size_t n)` |
|
||||
| `memmove(d, s, n)` | `void* memmove(void* d, const void* s, size_t n)` |
|
||||
| `memcmp(a, b, n)` | `int memcmp(const void* a, const void* b, size_t n)` |
|
||||
| `strcmp(a, b)` | `int strcmp(const char* a, const char* b)` |
|
||||
| `strncmp(a, b, n)` | `int strncmp(const char* a, const char* b, size_t n)` |
|
||||
| `strcpy(d, s)` | `char* strcpy(char* d, const char* s)` |
|
||||
| `strncpy(d, s, n)` | `char* strncpy(char* d, const char* s, size_t n)` |
|
||||
| `strcat(d, s)` | `char* strcat(char* d, const char* s)` |
|
||||
| `puts(s)` | `int puts(const char* s)` |
|
||||
| `atoi(s)` | `int atoi(const char* s)` |
|
||||
| `atol(s)` | `long atol(const char* s)` |
|
||||
| `abs(x)` | `int abs(int x)` |
|
||||
| `labs(x)` | `long labs(long x)` |
|
||||
|
||||
部分模式比如裸机没有 libc,则不能使用这些函数,即使环境满足,Viper 标准库也可能和 C 标准库冲突。
|
||||
252
wiki/12-project.md
Normal file
252
wiki/12-project.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# 12 - 项目配置与构建
|
||||
|
||||
Viper 项目使用 `project.json` 配置文件管理编译和链接参数。
|
||||
|
||||
## project.json 结构
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Kernel",
|
||||
"version": "1.0.0",
|
||||
"source_dir": "./Kernel",
|
||||
"temp_dir": "./temp",
|
||||
"output_dir": "./output",
|
||||
"compiler": {
|
||||
"cmd": "llc",
|
||||
"flags": ["-filetype=obj", "-mtriple=x86_64-none-elf", "-relocation-model=static", "-O2"]
|
||||
},
|
||||
"linker": {
|
||||
"cmd": "ld.lld.exe",
|
||||
"flags": [
|
||||
"-m", "elf_x86_64",
|
||||
"-T", "linker.ld",
|
||||
"--oformat", "elf64-x86-64",
|
||||
"--strip-all"
|
||||
],
|
||||
"output": "kernel.bin"
|
||||
},
|
||||
"includes": ["../../includes"],
|
||||
"target": {
|
||||
"triple": "x86_64-none-elf",
|
||||
"datalayout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
|
||||
},
|
||||
"options": {
|
||||
"slice_level": 3,
|
||||
"target": "llvm",
|
||||
"strict_mode": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 配置项说明
|
||||
|
||||
### 基本信息字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `name` | string | 项目名称 |
|
||||
| `version` | string | 项目版本 |
|
||||
| `source_dir` | string | 源代码目录(相对于 project.json 所在目录) |
|
||||
| `temp_dir` | string | 临时文件目录(存放 .pyi 存根等) |
|
||||
| `output_dir` | string | 输出文件目录 |
|
||||
|
||||
### 编译器配置
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `compiler.cmd` | string | 编译器命令(通常为 `llc`) |
|
||||
| `compiler.flags` | string[] | 编译器标志 |
|
||||
|
||||
常用 `llc` 标志:
|
||||
- `-filetype=obj`:输出目标文件
|
||||
- `-mtriple=x86_64-none-elf`:目标三元组
|
||||
- `-relocation-model=static`:静态重定位
|
||||
- `-O2`:优化级别
|
||||
|
||||
### 链接器配置
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `linker.cmd` | string | 链接器命令(通常为 `ld.lld.exe`) |
|
||||
| `linker.flags` | string[] | 链接器标志 |
|
||||
| `linker.output` | string | 输出文件名 |
|
||||
|
||||
常用 `ld.lld` 标志:
|
||||
- `-m elf_x86_64`:ELF x86_64 格式
|
||||
- `-T linker.ld`:链接脚本
|
||||
- `--oformat elf64-x86-64`:输出 ELF 格式
|
||||
- `--oformat binary`:输出原始二进制(用于内核)
|
||||
- `--strip-all`:去除所有符号信息
|
||||
|
||||
### 目标平台配置
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `target.triple` | string | LLVM 目标三元组 |
|
||||
| `target.datalayout` | string | LLVM 数据布局字符串 |
|
||||
|
||||
默认值:
|
||||
- triple: `x86_64-none-elf`
|
||||
- datalayout: `e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128`
|
||||
|
||||
### 包含路径
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `includes` | string[] | 模块搜索路径列表 |
|
||||
|
||||
### 编译选项
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `options.slice_level` | int | 切片优化级别(0-3) |
|
||||
| `options.target` | string | 编译目标(目前仅支持 `llvm`) |
|
||||
| `options.strict_mode` | bool | 严格模式 |
|
||||
|
||||
## 切片优化级别
|
||||
|
||||
| 级别 | 名称 | 说明 |
|
||||
|------|------|------|
|
||||
| `0` | `no_optimize` | 无优化 |
|
||||
| `1` | `conservative` | 保守优化 |
|
||||
| `2` | `moderate` | 适度优化 |
|
||||
| `3` | `aggressive` | 激进优化(默认) |
|
||||
| `s` | `size` | 体积优化 |
|
||||
|
||||
## 项目结构示例
|
||||
|
||||
### 内核项目
|
||||
|
||||
```
|
||||
VKernel/
|
||||
├── project.json # 项目配置
|
||||
├── linker.ld # 链接脚本
|
||||
├── Kernel/ # 源代码目录
|
||||
│ ├── main.py # 内核入口
|
||||
│ ├── bootinfo.py
|
||||
│ ├── intr/
|
||||
│ │ ├── gdt.py
|
||||
│ │ ├── idt.py
|
||||
│ │ └── syscall.py
|
||||
│ ├── drivers/
|
||||
│ │ ├── serial/
|
||||
│ │ ├── video/
|
||||
│ │ ├── storage/
|
||||
│ │ └── ...
|
||||
│ └── ...
|
||||
├── temp/ # 临时文件(自动生成)
|
||||
│ ├── <SHA1>.pyi # 签名存根(阶段一生成)
|
||||
│ ├── <SHA1>.stub.ll # LLVM IR 声明(阶段一生成)
|
||||
│ └── _sha1_map.txt # SHA1→源文件路径映射
|
||||
└── output/ # 输出文件(自动生成)
|
||||
├── kernel.bin # 内核二进制
|
||||
├── *.deps.json # 依赖信息
|
||||
└── linker.ld # 生成的链接脚本
|
||||
```
|
||||
|
||||
### 应用项目
|
||||
|
||||
```
|
||||
HelloWorld/
|
||||
├── project.json # 项目配置
|
||||
├── linker.ld # 链接脚本
|
||||
├── main.py # 应用入口
|
||||
├── temp/ # 临时文件
|
||||
└── output/ # 输出文件
|
||||
└── helloworld.elf # ELF 可执行文件
|
||||
```
|
||||
|
||||
### 库项目
|
||||
|
||||
```
|
||||
SerialLogger/
|
||||
├── project.json # 项目配置
|
||||
├── linker.ld # 链接脚本
|
||||
├── serial_logger.py # 库源码
|
||||
├── temp/ # 临时文件
|
||||
└── output/ # 输出文件
|
||||
```
|
||||
|
||||
## 链接脚本
|
||||
|
||||
ViperOS 使用自定义链接脚本控制内存布局:
|
||||
|
||||
```ld
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000; /* 加载地址 */
|
||||
|
||||
.text : {
|
||||
*(.text.startup)
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data*)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 编译命令
|
||||
|
||||
TransPyC 编译器的基本用法(注:适用于单个文件,而不适用于工程):
|
||||
|
||||
```bash
|
||||
python TransPyC.py -f InputFile -o OutputFile [options]
|
||||
```
|
||||
|
||||
### 命令行参数
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `-f` | 输入文件路径 |
|
||||
| `-o` | 输出文件路径 |
|
||||
| `-wh` | 头文件列表 |
|
||||
| `-debug` | 调试输出文件 |
|
||||
| `-cc` | 编译命令 |
|
||||
| `-cflags` | 编译标志 |
|
||||
| `-run` | 编译后运行 |
|
||||
| `-args` | 运行参数 |
|
||||
| `-h` | 辅助文件(C 或 Python) |
|
||||
|
||||
工程编译使用:
|
||||
```bash
|
||||
python Projectrans.py xxx.json
|
||||
```
|
||||
即可
|
||||
|
||||
## 增量编译
|
||||
|
||||
TransPyC 支持基于 SHA1 的增量编译,与两阶段编译模型紧密耦合:
|
||||
|
||||
### 阶段一增量
|
||||
|
||||
- 每个源文件按内容计算 SHA1 哈希(16位)
|
||||
- 如果 `<SHA1>.pyi` 和 `<SHA1>.stub.ll` 已存在,则跳过生成(缓存命中)
|
||||
- 只有源文件内容变化导致 SHA1 变化时才重新生成声明接口
|
||||
- `temp/_sha1_map.txt` 记录 SHA1 → 源文件路径的映射,格式为 `<sha1>:<relative_path>`
|
||||
|
||||
### 阶段二增量
|
||||
|
||||
- 阶段二加载 `temp/_sha1_map.txt` 重建 SHA1 映射
|
||||
- 过滤掉不在当前 SHA1 映射中的旧 `.pyi` 和 `.stub.ll` 文件
|
||||
- 共享符号表一次性构建,避免每个文件重复加载
|
||||
|
||||
### SHA1 命名空间与增量编译的协同
|
||||
|
||||
SHA1 同时服务于命名空间隔离和增量编译:
|
||||
- **命名空间**:非 `t.CExport` 函数和结构体使用 SHA1 前缀,消除符号冲突
|
||||
- **增量编译**:SHA1 不变则缓存命中,SHA1 变化则重新生成
|
||||
|
||||
详见 [01-overview.md 中 SHA1 命名空间机制](01-overview.md#sha1-命名空间机制)。
|
||||
252
wiki/13-bootstrapping.md
Normal file
252
wiki/13-bootstrapping.md
Normal file
@@ -0,0 +1,252 @@
|
||||
# TransPyC 自举可行性分析
|
||||
|
||||
> **状态**:进行中
|
||||
> **创建时间**:2026-06-26
|
||||
> **最后更新**:2026-06-30
|
||||
> **结论**:完全自举可行,核心障碍已被攻克,剩余工作明确
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景
|
||||
|
||||
自举(bootstrapping)指用 TransPyC 语言重写 TransPyC 编译器自身,然后用现有编译器编译,得到新的原生编译器。本文档分析自举的可行性、进展与剩余工作。
|
||||
|
||||
**当前目标**:完全自举。TransPyC 的定位是独立的系统级语言,不是"Python → 原生 的工具"。自举是语言成熟度的证明,也是脱离 Python 运行时性能限制的必经之路。
|
||||
|
||||
---
|
||||
|
||||
## 2. LOC 统计
|
||||
|
||||
### 2.1 编译器主体(lib 目录)
|
||||
|
||||
| 子目录 | 文件数 | LOC | 说明 |
|
||||
|--------|--------|-----|------|
|
||||
| `lib/core` | 64 | 28,610 | 编译器核心 |
|
||||
| ├─ `core/Handles` | 30 | 18,885 | AST 节点处理器(最大模块) |
|
||||
| ├─ `core/LLVMCG` | 8 | 2,782 | LLVM 代码生成 |
|
||||
| └─ `core/Translator` | 6 | 1,751 | 翻译器框架 |
|
||||
| `lib/Projectrans` | 6 | 4,371 | 项目级翻译(Phase1/Phase2) |
|
||||
| `lib/StubGen` | 4 | 1,187 | Stub 生成器 |
|
||||
| `lib/Shell` | 4 | 630 | CLI 入口 |
|
||||
| `lib/includes` | 2 | 1,609 | t.py(968) + c.py(641) 类型定义 |
|
||||
| `lib/utils` | 1 | 40 | 辅助 |
|
||||
| `lib/constants` | 1 | 36 | 配置 |
|
||||
| **lib 总计** | **80** | **36,483** | |
|
||||
|
||||
### 2.2 Top 15 文件
|
||||
|
||||
| 文件 | LOC |
|
||||
|------|-----|
|
||||
| `lib/core/Handles/HandlesExprCall.py` | 3,153 |
|
||||
| `lib/Projectrans/Phase2Translator.py` | 2,693 |
|
||||
| `lib/core/Handles/HandlesImports.py` | 1,994 |
|
||||
| `lib/core/Handles/HandlesFunctions.py` | 1,612 |
|
||||
| `lib/core/Handles/HandlesAssign.py` | 1,544 |
|
||||
| `lib/core/Handles/HandlesExprAttr.py` | 1,168 |
|
||||
| `lib/core/Handles/HandlesClassDef.py` | 1,039 |
|
||||
| `lib/core/Handles/HandlesFor.py` | 997 |
|
||||
| `lib/core/Handles/HandlesTypeMerge.py` | 991 |
|
||||
| `lib/includes/t.py` | 968 |
|
||||
| `lib/StubGen/Converter.py` | 893 |
|
||||
| `lib/core/Handles/HandlesExprBuiltin.py` | 868 |
|
||||
| `lib/Projectrans/DeclarationGenerator.py` | 845 |
|
||||
| `lib/core/Handles/HandlesBase.py` | 756 |
|
||||
| `lib/core/stub_generator.py` | 699 |
|
||||
|
||||
### 2.3 运行时库(includes 目录)
|
||||
|
||||
| 模块 | LOC | 说明 |
|
||||
|------|-----|------|
|
||||
| `includes/numpy/__init__.py` | 1,016 | ndarray n 维数组 |
|
||||
| `includes/zlib/` | ~1,900 | 完整 deflate/inflate/huffman/checksum |
|
||||
| `includes/ast/` | 4,176 | **自研 Python 解析器(自举组件)** |
|
||||
| `includes/llvmlite/` | ~1,500 | **自研 LLVM IR 生成库(自举组件)** |
|
||||
| `includes/linkedlist.py` | ~400 | 泛型链表(@t.NoVTable + PEP 695) |
|
||||
| `includes/vipermath.py` | 450 | 数学库 |
|
||||
| ...(30+ 文件) | ... | mpool/mbuddy/string/stdio/json/w32 等 |
|
||||
| **includes 总计** | **~12,000** | |
|
||||
|
||||
### 2.4 全项目总计
|
||||
|
||||
```
|
||||
全项目 .py 总计:~48,000 LOC
|
||||
```
|
||||
|
||||
**自举相关核心数字:**
|
||||
- 编译器主体(lib 不含 t.py/c.py):~34,874 LOC
|
||||
- 自举基础组件(includes/ast + includes/llvmlite):~5,700 LOC ✅ 已完成
|
||||
- 运行时库(includes 其余):~9,500 LOC
|
||||
|
||||
---
|
||||
|
||||
## 3. 自举进展(已完成的核心组件)
|
||||
|
||||
### 3.1 ✅ includes/ast —— Python 解析器自举
|
||||
|
||||
**状态**:完成,AstTest 通过(解析 CPython 几乎全部语法)
|
||||
|
||||
| 文件 | LOC | 功能 |
|
||||
|------|-----|------|
|
||||
| `__tokens.py` | ~600 | TokenType/Keyword/TokOp 枚举 + 查找表 |
|
||||
| `__nodes.py` | ~1,400 | AST 胖节点 + ASTVType/ASTCtx/OpKind CEnum |
|
||||
| `__lexer.py` | ~800 | 词法分析器 |
|
||||
| `__parser.py` | 4,176(含表格) | 递归下降语法分析器 |
|
||||
|
||||
**覆盖语法**:装饰器、async/await、match(含复杂模式 `[x,*rest]`/`{'type':t,**rest}`/`Point(x,y)`/`0|1|2`)、try/except/finally/raise from、with、lambda、列表/集合/字典/生成器推导式、f-string(含 `!r`/`!s` 转换和 `:>{width}` 格式说明)、链式比较、walrus、yield from、global/nonlocal、切片、嵌套三元。
|
||||
|
||||
**设计要点**:
|
||||
- 胖节点设计(所有节点共用一个 `AST` struct,用 `vtype` 区分)—— 避免 REnum 在 70+ 变体下的内存膨胀
|
||||
- 从 `mpool.MPool` 分配,零 Python 运行时依赖
|
||||
- API 与 Python `ast` 模块对齐:`parse(src, pool) -> AST`
|
||||
|
||||
### 3.2 ✅ includes/llvmlite —— LLVM IR 生成库自举
|
||||
|
||||
**状态**:完成,LLvmLiteTest 18/18 通过,TransPyV 自举实验跑通
|
||||
|
||||
| 文件 | 功能 |
|
||||
|------|------|
|
||||
| `__types.py` | `LLVMType` REnum + 类型构造/打印 |
|
||||
| `__values.py` | Value/Constant/SSA 值表示 |
|
||||
| `__function.py` | Function + BasicBlock + 参数管理 |
|
||||
| `__module.py` | Module 容器 + 目标三元组 + 输出 .ll |
|
||||
| `__builder.py` | IRBuilder(指令发射 + SSA 命名) |
|
||||
|
||||
**设计要点**:
|
||||
- `LLVMType` 用 `t.REnum`(tagged union)—— 少变体、无通用字段、match 分派的理想场景
|
||||
- 指令 opcode 用 `t.CEnum`(IROp)—— 类型安全且零开销
|
||||
- IR 文本生成用 `viperlib.snprintf` 格式化,避免手写逐字符拼接
|
||||
- 对象分配用普通类 + `mpool`,不持有全局 `mbuddy`
|
||||
|
||||
**验证**:[TransPyV/App/main.py](../TransPyV/App/main.py) 10 个测试覆盖类型打印、值打印、build_add、if-else、模块打印、lli 实际执行。TransPyV 程序运行时自己生成合法 IR,被 `lli` 成功执行。
|
||||
|
||||
### 3.3 ✅ 基础设施库
|
||||
|
||||
以下 includes/ 库为自举后的编译器提供数据结构和系统调用基础:
|
||||
|
||||
| 库 | 自举中的作用 |
|
||||
|----|-------------|
|
||||
| `linkedlist.py` | AST 节点树、符号表链、IR 指令链的容器(`@t.NoVTable` + PEP 695 递归泛型继承) |
|
||||
| `mpool.py` / `mbuddy.py` | 编译器内存管理(替代 Python GC) |
|
||||
| `string.py` / `viperlib.py` | 字符串处理 + snprintf(替代 Python 字符串方法) |
|
||||
| `w32/` | FFI 声明,自举后编译器调用 llc/clang/linker 的基础 |
|
||||
| `json/` | 符号表序列化(替代 pickle) |
|
||||
|
||||
---
|
||||
|
||||
## 4. 障碍分析(修正版)
|
||||
|
||||
原版本文档列出的"三大致命依赖"已被逐一攻克或降级:
|
||||
|
||||
### 4.1 ~~llvmlite 依赖(原:致命)~~ → ✅ 已解决
|
||||
|
||||
**原问题**:编译器通过 `llvmlite.ir` 生成 IR,TransPyC 无法调用 Python 库。
|
||||
|
||||
**解决**:`includes/llvmlite/` 用 TransPyC 自身实现了 LLVM IR 文本生成库,API 与 Python `llvmlite.ir` 对齐。采用"字符串拼 IR"路径(路径 A),不绑定 LLVM C API。18/18 测试通过,TransPyV 自举实验验证可生成可执行 IR。
|
||||
|
||||
### 4.2 ~~ast 模块依赖(原:致命)~~ → ✅ 已解决
|
||||
|
||||
**原问题**:编译器用 Python `ast` 模块解析源码,自举需自己写 Python 解析器(预计 5,000-10,000 LOC)。
|
||||
|
||||
**解决**:`includes/ast/` 4,176 LOC 实现完整 Python 解析器,AstTest 解析 CPython 凥乎全部语法通过。预言已兑现,且实际规模(4,176 LOC)低于预估下限(5,000 LOC)。
|
||||
|
||||
### 4.3 ~~动态反射 4241 处(原:致命)~~ → ⚠️ 降级为可控
|
||||
|
||||
**原问题**:`getattr`/`setattr`/`hasattr`/`isinstance`/`issubclass` 共 4,241 处,静态语言无法直接表达。
|
||||
|
||||
**修正分析**:4241 这个数字按 Python 语法 grep 统计,未区分"语义上需要反射"和"用反射偷懒"。实际分类:
|
||||
|
||||
| 用法 | 数量估计 | 是否真反射 | TransPyC 解法 |
|
||||
|------|----------|-----------|--------------|
|
||||
| `isinstance(node, ast.Xxx)` AST 节点分派 | ~2000 | ❌ 否 | 胖节点 `node.vtype == ASTVType.Xxx` 整数比较 |
|
||||
| `isinstance(x, ir.IntType)` IR 类型分派 | ~500 | ❌ 否 | REnum `match x: case LLVMType.Int:` tag 比较 |
|
||||
| `getattr(t, name, None)` 字符串→类型查表 | ~35 | ❌ 否 | 注册表 `t_type_registry: dict[str, t.CInt]` |
|
||||
| `CTypeInfo.__getattr__` 门面路由 | ~100 | ✅ 是 | 重构为 REnum + match 显式分派 |
|
||||
| `copy.deepcopy` / `pickle` | ~10 | ✅ 是 | 已解决(`__getstate__/__setstate__`) |
|
||||
| `hasattr` 防御性检查 | ~1500 | ⚠️ 多数可消除 | 显式字段声明 + None 检查 |
|
||||
|
||||
**真实反射障碍约 200-500 处**,集中体现在 `CTypeInfo` 门面模式(FIELD_ROUTES 动态路由到 `_ts`/`_sm`)。重构方案:用 REnum + match 替换 `__getattr__` 路由,已在 `includes/llvmlite/__types.py` 验证此范式可行。
|
||||
|
||||
### 4.4 Python 标准库依赖(原:严重)→ ⚠️ 部分已解决
|
||||
|
||||
| Python 库 | 用途 | TransPyC 现状 |
|
||||
|-----------|------|--------------|
|
||||
| `ast` | 源码解析 | ✅ `includes/ast/` |
|
||||
| `llvmlite.ir` | LLVM IR 生成 | ✅ `includes/llvmlite/` |
|
||||
| `json` | 序列化 | ✅ `includes/json/` |
|
||||
| `os`/`sys` | 文件/路径 | ⚠️ `includes/os/` + `w32/`(Windows) |
|
||||
| `subprocess` | 调用 llc/clang | ❌ 待实现(w32 CreateProcess 可基础) |
|
||||
| `pickle` | 符号表序列化 | ❌ 待实现(json 替代) |
|
||||
| `concurrent.futures` | 并行编译 | ❌ 待实现(w32 线程可基础) |
|
||||
| `re` | 正则表达式 | ❌ 待实现 |
|
||||
| `traceback` | 异常栈 | ❌ 待实现 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 自举路径
|
||||
|
||||
### 5.1 路径 A:完全自举(当前目标,可行)
|
||||
|
||||
**已完成**:
|
||||
1. ✅ `includes/ast/` Python 解析器(4,176 LOC)
|
||||
2. ✅ `includes/llvmlite/` LLVM IR 生成库(~1,500 LOC)
|
||||
3. ✅ 基础设施库(linkedlist/mpool/mbuddy/string/json/w32)
|
||||
|
||||
**剩余工作**:
|
||||
1. **CTypeInfo 门面重构**(~200-500 处真反射 → REnum + match)
|
||||
2. **lib/core 重写**(~20K LOC TransPyC,含 Handles/LLVMCG/Translator)
|
||||
3. **lib/Projectrans 重写**(~5K LOC,Phase1/Phase2/DeclarationGenerator)
|
||||
4. **lib/StubGen 重写**(~2K LOC)
|
||||
5. **Python 标准库缺口补齐**(os/subprocess/re/pickle,~5K LOC)
|
||||
|
||||
**预计新增代码量**:30-35K LOC TransPyC。与已完成的 includes/ast(4,176 LOC)+ includes/llvmlite(~1,500 LOC)+ 其他基础库为同一量级——已经证明过两次能做这件事。
|
||||
|
||||
### 5.2 关键技术验证点(已完成)
|
||||
|
||||
以下能力已在 includes/ 库中验证,证明 TransPyC 能承载编译器复杂度:
|
||||
|
||||
- **复杂泛型容器**:`linkedlist.py` 的递归泛型继承 `class GNode(GSListNode[GNode])`(C/C++/Rust 均无法如此简洁表达)
|
||||
- **复杂算法**:`zlib/` 的完整 deflate/inflate/huffman(~1,900 LOC 生产级压缩)
|
||||
- **n 维数组**:`numpy/__init__.py` 的 ndarray + 运算符重载 + `__new__` 堆分配
|
||||
- **平台 FFI**:`w32/` 的 Win32 API 绑定(file/memory/process/sync/console)
|
||||
- **LLVM IR 生成**:`llvmlite/` 的 REnum 类型系统 + IRBuilder + ModulePrint
|
||||
|
||||
---
|
||||
|
||||
## 6. 自举的意义
|
||||
|
||||
1. **性能**:当前 Python 编译器翻译 6 个文件 0.6s,全量编译 36K LOC 编译器自身时 Python 解释器开销显著。自举后编译器是原生码,编译速度数量级提升。
|
||||
|
||||
2. **部署**:当前分发需 Python 运行时 + llvmlite + ast 依赖。自举后只需一个可执行文件 + llc/clang——系统级语言编译器该有的形态。
|
||||
|
||||
3. **语言成熟度证明**:能写自己的编译器是系统级语言的"成人礼"。C、C++、Rust、Go、Zig 全都走过这条路。
|
||||
|
||||
4. **dogfooding 闭环**:自举后每次改编译器都用编译器自己编译自己,持续暴露 bug 和性能瓶颈,形成正反馈。`includes/ast` 和 `includes/llvmlite` 的开发过程已经产出了大量深层 bug 修复(REnum 变体名冲突、跨模块继承字段展平、NoVTable OOP 方法继承等)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 结论
|
||||
|
||||
| 维度 | 评估 |
|
||||
|------|------|
|
||||
| 理论可行性 | ✅ 可行(图灵完备) |
|
||||
| 实践可行性 | ✅ 完全自举可行(核心障碍已攻克) |
|
||||
| 当前进展 | ⚠️ 前端(ast)+ 后端(llvmlite)+ 基础库已就位,剩中间层(lib/core)重构 |
|
||||
| 剩余工作量 | ~30-35K LOC TransPyC 重写 + CTypeInfo 门面重构 |
|
||||
| 推荐策略 | 路径 A:完全自举,按 lib/core → lib/Projectrans → lib/StubGen 顺序推进 |
|
||||
|
||||
**核心判断**:原版本文档的"三大致命依赖"结论已全部失效——llvmlite 和 ast 已被 includes/ 下的自研库替代,4241 处反射中 90% 是工程偷懒而非语义需要。完全自举从"几乎不可能"变为"明确可行",剩余工作是体量问题而非技术障碍。
|
||||
|
||||
---
|
||||
|
||||
## 8. 附录:原版本文档的修正说明
|
||||
|
||||
本文档(2026-06-30 版)修正了 2026-06-26 原版的以下错误判断:
|
||||
|
||||
| 原版断言 | 修正 |
|
||||
|---------|------|
|
||||
| "完全自举几乎不可能" | 完全自举可行,核心障碍已攻克 |
|
||||
| "llvmlite 依赖致命" | 已被 `includes/llvmlite/` 解决(18/18 测试) |
|
||||
| "ast 模块依赖致命" | 已被 `includes/ast/` 解决(4,176 LOC,AstTest 通过) |
|
||||
| "4241 处动态反射致命" | 90% 是工程偷懒(isinstance 节点分派/IR 类型分派),真实反射约 200-500 处 |
|
||||
| "推荐路径 C:不自举" | 推荐路径 A:完全自举 |
|
||||
| "预计自举后 60K-80K LOC" | 修正为 30-35K LOC(基础组件已 5,700 LOC 完成) |
|
||||
3555
wiki/all.md
Normal file
3555
wiki/all.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user