Files
TransPyC/wiki/all.md
2026-07-18 19:25:40 +08:00

3556 lines
98 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 01 - 语言概述与编译流程
## 语言定位
`Viper` 是一种 **系统级编程语言**,其语法基于 `Python`,但编译目标为 `LLVM IR`,最终生成原生机器码。`Viper` 不是 `Python` 的超集或子集,也不是"`C` 的语法糖"——它是一门拥有独立类型系统、独立编译模型、独立模块体系的语言。`Viper` 选择 `Python` 语法作为表达形式,是因为 `Python``AST` 可被标准库直接解析,从而将编译器的精力集中在语义翻译而非词法/语法分析上。同时也是因为 `Python` 是人类公认的可读性最高的语言。
而实际上,下文中所有提到的 `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` 类有对象语义,`@t.CVTable` 为多态,普通 `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`
- **存根驱动的模块系统**`.pyi` 存根文件实现跨模块类型解析,无需头文件
- **更多内容**:额外更多的不依赖操作系统的函数和语法
## 两阶段编译流程
Viper 的编译由 `Projectrans.py` 驱动,分为两个阶段。这是 Viper 编译模型的核心,理解两阶段编译是理解 `t.CDefine``t.CExport``t.CInline` 等关键概念的前提。
```
源文件 (.py) ──────────────────────────────────────────────────────
│ │
│ ┌─────────────── 阶段一:声明提取 ───────────────┐ │
│ │ │ │
│ │ 1. 计算源文件 SHA1 │ │
│ │ 2. 生成 <SHA1>.pyi签名存根 │ │
│ │ 3. 构建结构体注册表 │ │
│ │ 4. 生成 <SHA1>.stub.llLLVM 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.pySHA1 = a1b2c3d4e5f6g7h8中定义
class Point: x: t.CInt; y: t.CInt
def draw(p: Point) -> t.CVoid: ...
源文件 B.pySHA1 = 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` 链接为可执行文件
# 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.CUnginedInt` 等。
### 固定宽度整数类型
| 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
```
## 数组类型
使用 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
```
## 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 的,还是后面的注解的,而且容易触发未定义行为。
## 强制类型转换
在 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
```
# 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)
```
# 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
```
在结构体中也可以指定默认值。
# 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
```
## 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` 的,还是后面的注解的,而且容易触发未定义行为。
## 位域
使用 `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。
# 06 - 面向对象与运算符重载
Viper 中的 `class` 有两种语义:**数据布局**(结构体/联合体/枚举,见 [05-classes.md](05-classes.md))和**面向对象**。面向对象特性通过 `@t.Object` 装饰器启用,多态通过 `@t.CVTable` 装饰器启用。
## `@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__` 之前初始化 vtable
```llvm
declare void @"<SHA1>.ClassName.__before_init__"(%"<SHA1>.ClassName"*)
```
值得注意的是,`__before_init__` 并不帮忙初始化内存,而只是初始化虚表等准备工作,实际上,我们更希望用户能自己管理内存。实验性阶段内容中,我们通过内存池机制来帮助用户管理碎片化内容,包括部分小的结构体,但较大内容最好还是由用户手动处理。
## `@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 个 slotvtable 指针)
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__` | 右操作数幂运算 | 同上 |
# 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);
```
# 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` 差,实际工程中不推荐使用条件宏,后期将完善宏的设计,引入模板等。
## 仅声明 `c.State`
`c.State` 用于声明但不定义函数或变量:
```python
def isr0() -> t.CExtern | t.CVoid | c.State: pass # extern void isr0();
```
## 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#运算符重载)。
# 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()
```
# 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
```
## 模块解析流程
### 编译时模块加载
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` 函数 | 保留完整函数体 |
| 普通函数 | 仅签名 + `c.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` 指定的目录中搜索导入的模块。
# 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 标准库冲突。
# 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-命名空间机制)。