# 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. 生成 .pyi(签名存根) │ │ │ │ 3. 构建结构体注册表 │ │ │ │ 4. 生成 .stub.ll(LLVM IR 声明) │ │ │ │ │ │ │ └────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────── 阶段二:代码翻译 ───────────────┐ │ │ │ │ │ │ │ 1. 加载所有 .pyi 和 .stub.ll │ │ │ │ 2. 构建共享符号表 │ │ │ │ 3. 收集内联函数符号 │ │ │ │ 4. 翻译源文件 → .ll(含代码) │ │ │ │ 5. llc 编译 → .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` 生成签名存根文件 `.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 声明文件 `.stub.ll`。 **声明生成规则**: | 元素 | 生成的 LLVM IR | |------|---------------| | 普通函数 | `declare void @".foo"(i32)` — 非 CExport 函数加 SHA1 前缀 | | CExport 函数 | `declare i32 @main()` — CExport 函数不加前缀,全局可见 | | 结构体 | `%".ClassName" = type { i32, i32* }` — 类型名加 SHA1 前缀 | | 全局变量 | `@varname = external global i32` | | `t.CDefine` 常量 | **不生成声明**(编译时常量,直接内联展开) | | `t.CTypedef` 别名 | **不生成声明**(类型别名,在类型解析时展开) | | 枚举 | 为每个枚举成员生成 `@__config_EnumName_member = external global i32` | **增量编译**:如果 `.pyi` 或 `.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. 输出 `.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 函数名 | 链接可见性 | 用途 | |------|---------------|-----------|------| | 无修饰 | `@.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` 为 `.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 %".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 @".ClassName.__before_init__"(%".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 %".Base" = type { i8*, i32 } ``` 同时,编译器会生成全局 vtable 变量: ```llvm @".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__` | 右操作数幂运算 | 同上 | # 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. **阶段一**:对每个源文件生成 `.pyi` 签名存根和 `.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 %".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/ # 临时文件(自动生成) │ ├── .pyi # 签名存根(阶段一生成) │ ├── .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位) - 如果 `.pyi` 和 `.stub.ll` 已存在,则跳过生成(缓存命中) - 只有源文件内容变化导致 SHA1 变化时才重新生成声明接口 - `temp/_sha1_map.txt` 记录 SHA1 → 源文件路径的映射,格式为 `:` ### 阶段二增量 - 阶段二加载 `temp/_sha1_map.txt` 重建 SHA1 映射 - 过滤掉不在当前 SHA1 映射中的旧 `.pyi` 和 `.stub.ll` 文件 - 共享符号表一次性构建,避免每个文件重复加载 ### SHA1 命名空间与增量编译的协同 SHA1 同时服务于命名空间隔离和增量编译: - **命名空间**:非 `t.CExport` 函数和结构体使用 SHA1 前缀,消除符号冲突 - **增量编译**:SHA1 不变则缓存命中,SHA1 变化则重新生成 详见 [01-overview.md 中 SHA1 命名空间机制](01-overview.md#sha1-命名空间机制)。