docs(wiki): 改建第 12 章为编译系统,统一 long 宽度口径并补齐章节编号

- 12-project.md 改建为 12-compilation-system.md:介绍编译系统总览、工程配置
  project.vpj 与 viperbuild 元编程;同步 README 与第 17 章的引用
- README.MD:新增「C-LIKE 语法(逐步支持)」小节——自研语法器 includes/ast 将逐步
  支持 C-LIKE 写法,双端写法与语义长期维护,但实际工程仍推荐 ABC-LIKE;流水线章节
  去掉「Python 单文件模式 / project.json」旧措辞,改述工程根来源并补 --src 与 viperbuild
- 02-type-system.md:long / unsigned long 位宽改为平台相关(Windows LLP64=32、
  LP64=64)并补注权威实现;修正 stdint.LONGLONG / ULONGLONG 展开;统一整型 None 判空写法
- 05-classes.md:补 t.Anonymous 废弃告警;07-control-flow.md:for-else 改为已实现措辞
- 15/16/17/18 章重编号为 14/15/16/17,填补 14-oo-refactor 删除后的编号空洞,
  同步各章标题与全仓引用
This commit is contained in:
TermiNexus committed 2026-10-02 22:05:18 +08:00
1 parent 1ac88f3241
commit 947c1b35e5
12 files changed
+324 -331

No files matched your search

+31 -17
View File
@@ -20,7 +20,7 @@ Viper 的类型系统通过 `t` 模块提供,所有类型注解继承 `t.CType
| `int` | `t.CInt` | `i32` |
| `uint` | `t.CUnsignedInt` | `i32` |
| `short` / `ushort` | `t.CShort` / `t.CUnsignedShort` | `i16` |
| `long` / `ulong` | `t.CLong` / `t.CUnsignedLong` | `i32`(Windows LLP64)或 `i64` |
| `long` / `ulong` | `t.CLong` / `t.CUnsignedLong` | `i32`(Windows LLP64)/ `i64`(LP64) |
| `longlong` / `ulonglong` | `t.CLongLong` / `t.CUnsignedLongLong` | `i64` |
| `char` | `t.CChar` | `i8` |
| `uchar` | `t.CUnsignedChar` | `i8` |
@@ -107,13 +107,16 @@ s: []char = "hi" # 长度推导 ≡ t.CArray[char, None]
| `t.CChar` | `char` | 8 | 是 |
| `t.CShort` | `short` | 16 | 是 |
| `t.CInt` | `int` | 32 | 是 |
| `t.CLong` | `long` | 64 | 是 |
| `t.CLong` | `long` | 32 / 64 | 是 |
| `t.CUnsignedChar` | `unsigned char` | 8 | 否 |
| `t.CUnsignedShort` | `unsigned short` | 16 | 否 |
| `t.CUnsigned` / `t.CUnsignedInt` | `unsigned int` | 32 | 否 |
| `t.CUnsignedLong` | `unsigned long` | 64 | 否 |
| `t.CUnsignedLong` | `unsigned long` | 32 / 64 | 否 |
| `t.CSignedChar` | `signed char` | 8 | 是 |
> **平台相关(`long` / `unsigned long`)**:宽度随目标平台——Windows(LLP64)下为 **32 位**(对齐 `ULONG`/`DWORD`),其余目标(LP64 / 裸机 ELF)为 **64 位**;`long long` / `unsigned long long` 恒为 64 位。
> TPC 由 `configure_platform(triple)` 设置 `_LONG_SIZE` / `_UNSIGNED_LONG_SIZE`(`TransPyC/lib/includes/t.py`),TPV 由 `TypeGenResolve._long_llvm_ty` 按同一口径给出(Windows triple ⇒ `i32`,否则 `i64`)。
值得注意的是,t.CUnsigned 一般不单独使用,而是配合其它有符号类型,替换表示无符号,比如 `t.CUnsigned | t.CInt`,由于为了便于使用一些常用类型,则将其组合为常用的 `t.CUnsignedInt` 等。
### 固定宽度整数类型
@@ -177,8 +180,8 @@ s: []char = "hi" # 长度推导 ≡ t.CArray[char, None]
| `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.LONGLONG` | `t.CLongLong` | - |
| `stdint.ULONGLONG` | `t.CUnsignedLongLong` | - |
| `stdint.FLOAT` | `t.CFloat` | - |
| `stdint.DOUBLE` | `t.CDouble` | - |
@@ -357,26 +360,37 @@ if p is not None: ... # ✅ → icmp ne <ptr>, null
if p == 0: ... # ❌ 不用:虽同样编译为 icmp eq <ptr>, null,但语义要绕"指针 vs 整型"特例分支
```
**以整型承载地址/句柄的字段判空,相反**:必须与整型字面量比较(`!= 0` / `== 0`)。
例:`llvmlite.LLVMType.Pointee` 是 `i64`(地址值),写 `x.Pointee is not None` **判不出空**——
两侧编译器对"值类型 vs None"的处理是:TPC 直接折成常量(`is not None` → `i1 1`、`is None` → `i1 0`,
`TransPyC/lib/core/Handles/HandlesExprOps.py:383-395`);TPV 把 `None` 物化为数值 0 后按整数比较
(`TransPyV/App/lib/core/Gen/Expr/ExprGenOps.vp:684-713`)。⇒ 这类判空写 `!= 0` 才两侧同效。
**以整型承载地址/句柄的字段判空**:整型上的 `None` **隐式表示 0**,两侧编译器都按此判定:
反例现场(守卫写出去了、但运行期没触发)与逐字 IR 证据:`Debugger/rawptr-migration-and-fastfail.md` §36。
```python
x is not None # ≡ x != 0 x != None # ≡ x != 0
x is None # ≡ x == 0 x == None # ≡ x == 0
```
即:这类判空既可写 `!= 0` / `== 0`,也可写 `is not None` / `is None`(以及 `!= None` / `== None`),**两侧同效**。
- **TPC**:`TransPyC/lib/core/Handles/HandlesExprOps.py:400-420` —— 一侧是 `IntType`、另一侧是空指针常量(`None`)时,
把 `None` 侧换成该整型的常量 0 再发 `icmp eq`,`is not` / `!=` 取反。该分支刻意排在「指针 vs 整型」特例之前,
否则 `i8` 会命中 `Gen._load(null)`(从空指针 load)。
- **TPV**:`TransPyV/App/lib/core/Gen/Expr/ExprGenOps.vp:649-694` —— 同样把 `None` 侧物化为整数 0 后走 `icmp eq/ne`
(`None is x` 与 `x is None` 两个方向、`is` 与 `==` 都覆盖)。
> 历史:早期 TPC 把「整型 vs None」直接折成常量(`is not None` → `i1 1`、`is None` → `i1 0`),
> 于是「整型守卫」恒真/恒假、判不出空;而 TPV 已按 0 比较 ⇒ 两侧不一致。现已统一为「整数上的 None = 0」。
> 修复前的现场与逐字 IR 证据见 `Debugger/rawptr-migration-and-fastfail.md` §36。
#### 双侧 IR 证据(判据写法 → 生成码)
| 写法 | 静态类型 | 生成 IR | 结论 |
|---|---|---|---|
| `p is None` / `p == 0` | 指针 | `icmp eq <ptr>, null` | 有效(命名 `is_cmp` / `cmpresult`) |
| `x is not None` | **整型 i64** | **无 icmp**,折成常量 `i1 1` | **失效**(守卫恒真) |
| `x is None` | **整型 i64** | **无 icmp**,折成常量 `i1 0` | **失效**(守卫恒假 → 死代码) |
| `x != 0` / `x == 0` | 整型 i64 | `icmp ne/eq i64 %x, 0` | 有效(两侧同效) |
| `x is not None` / `x != None` / `x != 0` | 整型 | `icmp ne <int> %x, 0` | 有效(两侧同效) |
| `x is None` / `x == None` / `x == 0` | 整型 | `icmp eq <int> %x, 0` | 有效(两侧同效) |
逐字实证(`x = elem_ptr.Ty.Pointee`,`LLVMType*` 的 REnum 载荷槽,偏移 8、`load i64`):
`TransPyV/output/818a0677d1e7fe22.ll:17979-17991` 中指针侧真的有 `icmp`,整型侧却只有
`%"and.result.4" = phi i1 [0, %"endif.19"], [1, %"and.rhs0.4"]`(`and.rhs0.4` 块内 `load i64` 后直接 `br`)。
逐字实证(**语义统一之前**的历史现场):`x = elem_ptr.Ty.Pointee`(`LLVMType*` 的 REnum 载荷槽,偏移 8、`load i64`)时,
`TransPyV/output/818a0677d1e7fe22.ll:17979-17991` 中指针侧有 `icmp`,整型侧却只有
`%"and.result.4" = phi i1 [0, %"endif.19"], [1, %"and.rhs0.4"]`(`and.rhs0.4` 块内 `load i64` 后直接 `br`)
——那是 TPC 把「整型 vs None」折成常量的结果;现已统一为整数 0 比较,该现场不再出现。
#### 读"对象首字"(vptr)的唯一可用写法
+17 -1
View File
@@ -85,6 +85,22 @@ class Config:
obj_ptr: MyClass* = &MyClass(arg1, arg2)
```
### 匿名结构体成员(❌)
> WARNING: **警告:** `t.Anonymous` 已废弃。由于 Viper 在声明结构体,枚举等处不需要关键字,因此 `t.Anonymouse` 是不需要使用的。此关键字不再维护,强行使用可能导致未定义行为。
继承 `t.Anonymous` 的类作为匿名成员嵌入:
```python
class Header(t.Anonymous):
magic: uint32
version: uint32
class Packet:
Header # 匿名嵌入,可直接访问 Packet.magic
length: uint32
```
### 字节序标记
> 待实现(尚无字节序处理代码)。
@@ -265,7 +281,7 @@ novirtual class GNode(GSListNode[GNode]): # 递归泛型继承
# 此时 GNode.Next: GNode* —— 强类型,零转型
```
这是 C(`void*` + cast)、C++(模板继承但无 `novirtual` 精细控制)、Rust(无继承,需 trait bound)**当然都可以表达,但是无法这样简洁**的能力。
这是 C(`void*` + cast)、C++(模板继承但无 `novirtual` 精细控制)、Rust(无继承,需 trait bound)均无法如此简洁表达的能力。
### 实例化
+1 -1
View File
@@ -209,7 +209,7 @@ novirtual class GNode(LinkedNode): # 非多态继承
子类调用父类方法时,实际机制为**调用点解析**:编译器沿 MRO(方法解析顺序)定位到**定义该方法**的父类,按字节偏移调整 `this` 指针后 `bitcast`,在调用点**直接调用**父类实现(无 wrapper 生成、无尾调用)。
> 这里的 MRO 为「深度优先 + 声明顺序 + 去重」(含自身),**不是** Python 的 C3 线性化;详见 [15-multiple-inheritance.md](15-multiple-inheritance.md)。
> 这里的 MRO 为「深度优先 + 声明顺序 + 去重」(含自身),**不是** Python 的 C3 线性化;详见 [14-multiple-inheritance.md](14-multiple-inheritance.md)。
### 跨模块继承
+3 -2
View File
@@ -143,7 +143,7 @@ for item in container:
### for-else
> 期望实现——当前编译器不生成 orelse 代码,else 块会被忽略
`for` 支持 `else` 子句,与 `while-else` 一样是语言的一部分:
```python
for i in range(count):
@@ -153,6 +153,8 @@ else:
serial.puts("not found\n")
```
`else` 块在循环**正常结束**(迭代耗尽,非 `break` 退出)时执行;`break` 会跳过 else。
### break 和 continue
```python
@@ -240,7 +242,6 @@ match value:
## assert 语句
> 避免使用此语句,此语句未经充分测试,在多数情况下,编译器不知道如何展示结果。
> 需要断言时**推荐使用 `testcheck` 库的断言**(`import testcheck`,核心断言为 `testcheck.check(cond, ok_msg, fail_msg)`,另有 `begin`/`section`/`ok`/`fail`/`info`/`end`/`summary`)。
> 期望实现——当前语句分派无 assert 分支
```python
+1 -1
View File
@@ -2,7 +2,7 @@
> 本章待修订(机制与实现存在偏差,见各节标注)
>
> 说明:本章讲的是**语言层 `try/except`**(错误码 + 错误消息出参)。运行期崩溃 / 按键时打印的**调用栈回溯**是另一套机制,见 [17-traceback.md](17-traceback.md)。
> 说明:本章讲的是**语言层 `try/except`**(错误码 + 错误消息出参)。运行期崩溃 / 按键时打印的**调用栈回溯**是另一套机制,见 [16-traceback.md](16-traceback.md)。
Viper 支持 Python 风格的 `try/except/finally` 异常处理,采用基于错误码 + 错误消息出参的传播机制(不使用 C++ 风格的零开销异常)。
+252
View File
@@ -0,0 +1,252 @@
# 12 - 编译系统
本章介绍 Viper 的编译系统:从源码到可执行产物的流水线、工程配置 `project.vpj`,以及**用 Viper 自己写构建配置**的元编程内建库 `viperbuild`。
## 编译系统总览
Viper 目前有两条编译路径,二者共享同一套语言语义:
| 编译器 | 实现语言 | 定位 |
|--------|----------|------|
| **TPC**(TransPyC) | Python | 种子编译器:纯 Python 实现,用于自举起点与快速试验 |
| **TPV**(TransPyV) | Viper | 自举产物:由 TPC 编译出的 Viper 原生编译器,日常构建走它 |
两阶段编译模型:
- **Phase 1(声明阶段)**:从入口出发按 `import` 递归建依赖图 → 1a 注册 struct/enum/union → 1b-pre 生成 `.vpi` 签名存根 → 1b 全量翻译生成 `.stub.ll`(声明)。
- **Phase 2(翻译与链接)**
- **Phase A-pre-vpi / Phase A-pre**:加载依赖 `.vpi`,预翻译准备
- **Phase A**:主循环多遍收敛翻译,`.stub.ll` / `.text.ll` 在此写盘
- **Phase B**:`llc` 并行编译 `.ll` → `.obj`
- **Phase B+**:缺失 includes 按需编译
- **Phase C**:链接(链接器由工程配置的 `linker.cmd` 决定)
### SHA1 命名空间与增量编译
- 每个源文件按内容算 SHA1(取前 16 个十六进制字符);`temp` 下按 `{temp}/{sha1[0:2]}/` 切片存放 `<SHA1>.vpi` / `.stub.ll` / `.text.ll` / `.deps.txt`。
- **命名空间**:未 `export` 的函数与结构体带 SHA1 前缀,消除跨模块符号冲突。
- **增量**:SHA1 不变则缓存命中直接复用;内容变化才重生成。映射表在内存 `Sha1Store` / `StubMergerStore` 中多遍收敛构建,`temp/_sha1_map.txt` 仅供人读。
- 旧产物清理:`--clear-cache`(只清 temp)/ `--rebuild-includes`(只清 includes 预编译缓存)。
## 工程配置 `project.vpj`
工程使用 `project.vpj`(JSON)管理编译与链接参数。
```json
{
"name": "Kernel",
"version": "1.0.0",
"source_dir": "./Kernel",
"build_dir": "./.tpv_build",
"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"],
"static_libs": [],
"dynamic_libs": [],
"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,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}
}
```
### 配置项说明
| 字段 | 类型 | 说明 |
|------|------|------|
| `name` / `version` | string | 项目名 / 版本 |
| `source_dir` | string | 源代码目录(相对 `project.vpj` 所在目录) |
| `build_dir` | string | 构建目录(默认 `./.tpv_build`);`temp` / `output` 均由它派生 |
| `compiler.cmd` / `compiler.flags` | string / string[] | 编译器命令(通常 `llc`)与标志 |
| `linker.cmd` / `linker.flags` | string / string[] | 链接器命令与标志 |
| `linker.static_libs` | string[] | 静态库 / 目标文件(`.a` / `.obj`) |
| `linker.dynamic_libs` | string[] | 动态库(`.dll` / `.so`) |
| `linker.output` | string | 输出文件名 |
| `includes` | string[] | 模块搜索路径列表(多目录按序查找) |
| `target.triple` | string | LLVM 目标三元组 |
| `target.datalayout` | string | LLVM 数据布局字符串 |
| `options.slice_level` | int | 切片优化级别(见下表) |
| `options.sha1_slice_level` | int | SHA1 切片存储级别(默认 1;≥1 时产物按 `{temp}/{sha1[0:2]}/` 存放) |
| `options.target` | string | 编译目标(目前仅 `llvm`) |
| `options.strict_mode` | bool | 严格模式 |
**无统一默认 triple**:由各工程自行指定(TransPyV 自身为 `x86_64-pc-windows-gnu`;ViperOS 内核为 `x86_64-none-elf`)。`target.datalayout` 与 `target.triple` 会写入 `.ll` 模块头(`target triple` / `target datalayout`)。
常用 `llc` 标志:`-filetype=obj`(出目标文件)、`-mtriple=...`、`-relocation-model=pic|static`、`-O0..3`。
常用 `ld.lld` 标志:`-m elf_x86_64`、`-T linker.ld`、`--oformat elf64-x86-64|binary`(内核用 binary)、`--strip-all`。
### 切片优化级别
| 级别 | 名称 | 说明 |
|------|------|------|
| `0` | `no_optimize` | 无优化 |
| `1` | `conservative` | 保守优化 |
| `2` | `moderate` | 适度优化 |
| `3` | `aggressive` | 激进优化(默认) |
| `s` | `size` | 体积优化 |
### 项目结构与产物布局
```
HelloWorld/
├── project.vpj # 项目配置
├── main.vp # 入口
└── .tpv_build/ # 构建目录(build_dir,自动生成)
├── temp/ # 临时文件
│ └── <sha1[0:2]>/ # SHA1 切片子目录(sha1_slice_level ≥ 1)
│ ├── <SHA1>.vpi # 签名存根
│ ├── <SHA1>.stub.ll # LLVM IR 声明
│ ├── <SHA1>.text.ll # 含代码的 .ll
│ └── <SHA1>.deps.txt # 依赖信息
└── output/ # 输出文件
└── helloworld.exe
```
内核类工程另含 `linker.ld` 链接脚本(`ENTRY` + `.text/.rodata/.data/.bss` 段控),由工程自行维护。
## 编译命令
### TPC(Python 种子编译器)
```bash
python TransPyC.py -f InputFile -o OutputFile [options]
```
| 参数 | 说明 |
|------|------|
| `-f` | 输入文件路径 |
| `-o` | 输出文件路径 |
| `-wh` | 头文件列表 |
| `-debug` | 调试输出文件 |
| `-cc` / `-cflags` | 编译命令 / 编译标志 |
| `-run` / `-args` | 编译后运行 / 运行参数 |
| `-h` | 辅助文件(C 或 Python) |
### TPV(自举产物)
```bash
# 工程模式:按 project.vpj 构建
TransPyV.exe --project project.vpj [--clean] [--run]
# viperbuild 模式:入口文件自带构建配置(无需 project.vpj)
TransPyV.exe --src App/main.vp [--run]
```
常用开关(完整清单见 [TransPyV/README.md](../TransPyV/README.md)):
| 开关 | 说明 |
|------|------|
| `--project <path>` | `project.vpj` 路径(不给则在当前目录、上级目录查找) |
| `--src <path>` | 入口文件;构建配置由依赖闭包内的 `viperbuild` 提供(与 `--project` 互斥) |
| `--clean` | 清理 temp / output / includes 预编译缓存 |
| `--rebuild-includes` | 只删 includes 预编译缓存,强制重编 includes |
| `--clear-cache` | 只清 temp 缓存(`.vpi` / `.stub.ll` / `.text.ll`) |
| `--phase 1\|2\|all` | 1 = 只做 stub 分离;2 = 只做 IR 组合 + 编译;all = 全部(默认) |
| `--run` | 编译成功后立即运行产物 |
| `--asan` | ASan 模式(`opt -passes=asan` 插桩;运行库须由 `linker.static_libs` 声明,否则 fail-fast) |
| `--traceback` | 生成栈回溯支持(DWARF 元数据 + `<exe>.stackmap`),见 [16-traceback.md](16-traceback.md) |
| `--detection` / `--detection-deep` | MemBuddy 分配检测(`0xCDCD` 标记 + 探针) |
| `--no-llvmpeephole` | 关闭 FunctionPrint 的本地窥孔(T2 store/load 转发、T3 空 bitcast) |
| `--repl` | 启动交互式 REPL,见 [15-repl.md](15-repl.md) |
| `--nowarn-register` | 抑制 Phase A 依赖类 WARN |
| `--version` / `--help` | 版本 / 帮助 |
## viperbuild —— 用 Viper 写构建配置(元编程)
`viperbuild` 是**编译器内建的编译期宏库**(与 `t` / `c` / `ctraits` 同级),它让构建配置直接写在 Viper 源码里,免去额外维护 `project.vpj` 的负担。它**不产生任何运行时代码**。
### 定位与边界
| 扩展名 | 是什么 | 编译器 | Vipeye(LSP) |
|--------|--------|--------|---------------|
| `.vp` | 可执行 Viper 源码 | 编译 | 推导 |
| `.vpi` | 模块签名存根(编译器自产自销) | 读取(跨模块类型主通道) | 推导 |
| `.vpnc` | **不可执行的 `.vp`** | **完全无视** | 推导 |
| `.vpnci` | **不可执行的 `.vpi`** | **完全无视** | 推导 |
`.vpnc` / `.vpnci` 是「Vipeye(基于 pyright 的 VSCode Viper LSP)专用、编译器零感知」的松散 Viper DSL,**不是**为 `viperbuild` 专设。
`viperbuild` 本身也**不是** `.vpnc`:它没有磁盘模块与 `.vpi`,是编译器直接拦截的内建名(`_check_import_sha1` 对 `viperbuild` 豁免)。仓库里的 [includes/viperbuild.vpnci](../includes/viperbuild.vpnci) 只是给 Vipeye 看的存根。
### 使用方法
配置写在**入口文件依赖闭包内**的任一 `.vp` 模块里(不必在入口文件本身):
```python
import viperbuild as vb
COMMON_FLAGS = ["-O0", "-g"]
proj = vb.Project("TransPyV", "1.0.0") # 也支持 Project(name=..., version=...)
proj.source = "./App" # 缺省 = 入口文件所在目录
proj.buildroot = "./.tpv_build" # 缺省 = source + "/.tpv_build"
proj.includes = ["../includes"] # 缺省 = 从 exe 目录向上 0..3 层探测 includes
proj.compiler = ["llc", "-filetype=obj", "-relocation-model=pic", *COMMON_FLAGS]
proj.linker = ["clang++", "-fuse-ld=lld", "-lmsvcrt", "-lucrt", "-g"]
proj.static_libs = [".../libclang_rt.asan_dynamic-x86_64.dll.a"]
proj.dynamic_libs = []
proj.output = "TransPyV.exe" # 缺省 = 入口文件名 + ".exe"
proj.build(triple="x86_64-pc-windows-gnu",
datalayout="e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128")
```
也可以批量赋值:
```python
proj.config(source="./App", buildroot="./.tpv_build", includes=["../includes"], ...)
```
`build()` 的目标平台三种写法(**互斥**):
- `build(triple=..., datalayout=...)`:显式指定,**必须成对给出**;
- `build(selfmachine=True)`:运行时探测本机架构(当前支持 AMD64 →
`x86_64-pc-windows-gnu` + 对应 datalayout);
- `build()`:什么都不给,等价 `selfmachine=True`。
`selfmachine=True` 与显式 `triple`/`datalayout` **同时给 ⇒ fatal**。
### 支持的常量表达式
字段右值只接受常量表达式:
- 字符串字面量;同文件先定义的字符串常量 / `define` 常量(引用);
- 字符串 `+` 拼接;
- `list[str]` 字面量,支持 `*名字` 展开(引用同文件的列表常量);
- 列表之间 `+` 拼接。
### 约束与 fail-fast
- 一个依赖闭包内**只允许一个** Project 对象,出现第二个即 fatal。
- Project 是**宏类型**:只允许「字段赋值 / `config()` / `build()`」,**禁止读取**(当右值用即 fatal)。
- `source` 缺省为入口文件所在目录;显式指定时,**入口文件必须位于其下**,否则 fatal。
- 没找到 `import viperbuild`、配置损坏(类型不符 / 未知字段 / 用了未定义常量)、`build()` 缺失或重复调用、`--project` 与 `--src` 同时给 —— 一律 fatal,**不静默兜底**。
### `--src` 的语义
`--src <入口文件>` 时:
1. **Pass 0**:从入口文件出发 BFS 展开**依赖闭包**(搜索根 = 入口目录 + 当前目录;stdlib 模块跳过),识别 `import viperbuild` 并提取配置,写回编译器内部配置;
2. 随后按**工程模式**构建:扫描 `source_dir` 下的 `.vp` 一起编译链接(**没有**「只编一个文件」的独立单文件模式)。
翻译期 `viperbuild` 的宏语句(`proj = vb.Project(...)` / `proj.<字段> = ...` / `proj.config()` / `proj.build()`)会由翻译器**整体擦除**,不生成任何 IR;`import viperbuild` 本身也只记账不产码。
> Pass 0 的实现位于 [TransPyV/App/lib/core/builtin/ViperBuild.vp](../TransPyV/App/lib/core/builtin/ViperBuild.vp)。
## 平台说明
当前工具链与运行时验证以 **Windows** 为主(`llc` + mingw `clang++`),原因是计划与精力有限,并非语言 / 编译器设计上的限制;实际大量代码可跨平台复用,后续按需扩展。
-296
View File
@@ -1,296 +0,0 @@
# 12 - 项目配置与构建
Viper 项目使用 `project.vpj` 配置文件管理编译和链接参数(Python 单文件模式仍接受 `project.json`)。
## project.vpj 结构
```json
{
"name": "Kernel",
"version": "1.0.0",
"source_dir": "./Kernel",
"build_dir": "./.tpv_build",
"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,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}
}
```
## 配置项说明
### 基本信息字段
| 字段 | 类型 | 说明 |
|------|------|------|
| `name` | string | 项目名称 |
| `version` | string | 项目版本 |
| `source_dir` | string | 源代码目录(相对于 project.vpj 所在目录) |
| `build_dir` | string | 构建目录(默认 `./.tpv_build`);`temp`/`output` 均由它派生 |
### 编译器配置
| 字段 | 类型 | 说明 |
|------|------|------|
| `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`;TransPyV 宿主工程为 `clang++`) |
| `linker.flags` | string[] | 链接器标志 |
| `linker.static_libs` | string[] | 静态库/目标文件列表(`.a`/`.obj`) |
| `linker.dynamic_libs` | string[] | 动态库列表(`.dll`/`.so`) |
| `linker.output` | string | 输出文件名 |
支持 DSO(动态共享对象)构建:开启后产物为 `*_dso.ll`。
常用 `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**——由各工程 `project.vpj` 自行指定(TransPyV 自身为 `x86_64-pc-windows-gnu`;ViperOS 内核为 `x86_64-none-elf`)。`target.datalayout` 同样随工程配置,内核工程常用:
`e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128`
> **平台说明**:当前工具链与运行时验证以 **Windows** 为主(`llc` + mingw `clang++`),原因是**计划与精力有限**,并非语言/编译器设计上的限制;**实际大量代码可跨平台复用**,后续按需扩展到其它平台。
### 包含路径
| 字段 | 类型 | 说明 |
|------|------|------|
| `includes` | string[] | 模块搜索路径列表 |
### 编译选项
| 字段 | 类型 | 说明 |
|------|------|------|
| `options.slice_level` | int | 切片优化级别(0-3) |
| `options.sha1_slice_level` | int | SHA1 切片存储级别(默认 1;≥1 时产物按 `{temp}/{sha1[0:2]}/` 子目录存放) |
| `options.target` | string | 编译目标(目前仅支持 `llvm`) |
| `options.strict_mode` | bool | 严格模式 |
## 切片优化级别
| 级别 | 名称 | 说明 |
|------|------|------|
| `0` | `no_optimize` | 无优化 |
| `1` | `conservative` | 保守优化 |
| `2` | `moderate` | 适度优化 |
| `3` | `aggressive` | 激进优化(默认) |
| `s` | `size` | 体积优化 |
## 项目结构示例
### 内核项目
```
VKernel/
├── project.vpj # 项目配置
├── linker.ld # 链接脚本
├── Kernel/ # 源代码目录
│ ├── main.vp # 内核入口
│ ├── bootinfo.vp
│ ├── intr/
│ │ ├── gdt.vp
│ │ ├── idt.vp
│ │ └── syscall.vp
│ ├── drivers/
│ │ ├── serial/
│ │ ├── video/
│ │ ├── storage/
│ │ └── ...
│ └── ...
├── .tpv_build/ # 构建目录(build_dir,自动生成)
│ ├── temp/ # 临时文件
│ │ ├── <sha1[0:2]>/ # SHA1 切片子目录(sha1_slice_level ≥ 1)
│ │ │ ├── <SHA1>.vpi # 签名存根
│ │ │ ├── <SHA1>.stub.ll # LLVM IR 声明
│ │ │ ├── <SHA1>.text.ll # 含代码的 .ll
│ │ │ └── <SHA1>.deps.txt # 依赖信息
│ │ └── _sha1_map.txt # SHA1→源文件路径映射(仅人类可读)
│ └── output/ # 输出文件
│ ├── kernel.bin # 内核二进制
│ ├── *.deps.txt # 依赖信息
│ └── linker.ld # 生成的链接脚本
```
### 应用项目
```
HelloWorld/
├── project.vpj # 项目配置
├── linker.ld # 链接脚本
├── main.vp # 应用入口
└── .tpv_build/ # 构建目录(自动生成)
├── temp/ # 临时文件
└── output/ # 输出文件
└── helloworld.elf # ELF 可执行文件
```
### 库项目
```
SerialLogger/
├── project.vpj # 项目配置
├── linker.ld # 链接脚本
├── serial_logger.vp # 库源码
└── .tpv_build/ # 构建目录(自动生成)
├── 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) |
工程编译使用(`--project` 指定工程文件):
```bash
python Projectrans.py --project project.vpj
```
即可
## TransPyV 命令行开关
用自举产物 `TransPyV.exe` 编译工程时的常用开关(完整清单见 [TransPyV/README.md](../TransPyV/README.md)):
| 开关 | 说明 |
|------|------|
| `--project <path>` | `project.vpj` 路径(不给则在当前目录、上级目录查找) |
| `--clean` | 清理 temp / output / includes 预编译缓存 |
| `--rebuild-includes` | 只删 includes 预编译缓存,强制重编 includes |
| `--clear-cache` | 只清 temp 缓存(`.vpi` / `.stub.ll` / `.text.ll`) |
| `--phase 1\|2\|all` | 1 = 只做 stub 分离;2 = 只做 IR 组合 + 编译;all = 全部(默认) |
| `--run` | 编译成功后立即运行产物 |
| `--asan` | ASan 模式(`opt -passes=asan` 插桩;运行库须由 `linker.static_libs` 声明,否则 fail-fast) |
| `--traceback` | 生成栈回溯支持(DWARF 元数据 + `<exe>.stackmap` 旁文件),见 [17-traceback.md](17-traceback.md) |
| `--detection` / `--detection-deep` | MemBuddy 分配检测(`0xCDCD` 标记 + 探针) |
| `--no-llvmpeephole` | 关闭 FunctionPrint 的本地窥孔(T2 store/load 转发、T3 空 bitcast) |
| `--backend llvm\|mlir` | 后端选择(`llvmlite` / `mlirlite`) |
| `--repl` | 启动交互式 REPL,见 [16-repl.md](16-repl.md) |
| `--nowarn-register` | 抑制 Phase A 依赖类 WARN |
| `--version` / `--help` | 版本 / 帮助 |
## 构建流程(真实骨架)
工程构建的实际流水线:
- **Phase 1(声明阶段)**:扫描入口与依赖并建依赖图 → 1a 结构体注册 → 1b 生成 `.vpi` 并翻译生成 stub
- **Phase 2(翻译与链接)**
- **Phase A-pre-vpi / Phase A-pre**:加载 `.vpi`,预翻译准备
- **Phase A**:主循环多遍收敛翻译,`.stub.ll`/`.text.ll` 在此写盘
- **Phase B**:`llc` 并行编译 `.ll` → `.o`
- **Phase B+**:缺失 includes 按需编译
- **Phase C**:链接(`linker.cmd` 决定链接器)
## 增量编译
TransPyC 支持基于 SHA1 的增量编译,与两阶段编译模型紧密耦合:
### 阶段一增量
- 每个源文件按内容计算 SHA1 哈希(16 个十六进制字符,即 64 位截断)
- 如果 `<SHA1>.vpi` 和 `<SHA1>.stub.ll` 已存在,则跳过生成(缓存命中)
- 只有源文件内容变化导致 SHA1 变化时才重新生成声明接口
- SHA1 → 源文件路径映射在内存 `Sha1Store` 中多遍收敛构建;`temp/_sha1_map.txt`(格式 `<sha1>:<relative_path>`)仅人类可读
### 阶段二增量
- 阶段二(Phase A 主循环)在内存 `Sha1Store`/`StubMergerStore` 中多遍收敛构建并复用映射,不依赖 `_sha1_map.txt`
- 旧产物清理使用 `--clear-cache`
- 共享符号表为内存 `StubMergerStore`,按需加载避免每个文件重复加载
### SHA1 命名空间与增量编译的协同
SHA1 同时服务于命名空间隔离和增量编译:
- **命名空间**:未 `export` 的函数(内部非 `t.CExport`)和结构体使用 SHA1 前缀,消除符号冲突
- **增量编译**:SHA1 不变则缓存命中,SHA1 变化则重新生成
详见 [README.MD 中 SHA1 命名空间机制](README.MD#sha1-命名空间机制)。
@@ -1,4 +1,4 @@
# Viper 多继承支持方案
# 14 - Viper 多继承支持方案
> 背景:Viper 语法层已支持 `class X(A, B)`(`ClassDef.bases` 是列表,parser 正常解析),
> 已支持 `class X(A, B)` 多继承:`StructEntry` 含 `ParentName`(首父兼容)+ `ParentNames` + `ParentCount`。
+1 -1
View File
@@ -1,4 +1,4 @@
# 16 - 交互式 REPL
# 15 - 交互式 REPL
`TransPyV` 内置一个交互式 REPL:把每一行/每一块 Viper 片段**即时编译成 DLL 并加载调用**,把结果回显出来。它支持会话变量(M2)与跨片段会话函数(M3),适合快速试验语法、验证表达式与类型行为。
+1 -1
View File
@@ -1,4 +1,4 @@
# 17 - 栈回溯与调试信息
# 16 - 栈回溯与调试信息
TransPyV 里的 "traceback" 有**两套完全不同的东西**,务必区分:
@@ -1,6 +1,6 @@
# 18 - 旗舰项目
# 17 - 旗舰项目
前面 17 章讲的是 **Viper 这门语言**本身。本章介绍用 Viper 写出来的几个**真实、完整的工程**——它们既是语言能力的实证,也是编译器最重要的回归对象。
前面 16 章讲的是 **Viper 这门语言**本身。本章介绍用 Viper 写出来的几个**真实、完整的工程**——它们既是语言能力的实证,也是编译器最重要的回归对象。
| 项目 | 位置 | 定位 |
|------|------|------|
@@ -63,7 +63,7 @@ TransPyV/
exe\TransPyV.exe --project ViperApps\ViperMNIST\project.vpj --clean --run
```
命令行参数详见 [12-project.md](12-project.md);自举过程与可行性分析详见 [13-bootstrapping.md](13-bootstrapping.md);交互式 REPL 见 [16-repl.md](16-repl.md)。
命令行参数详见 [12-compilation-system.md](12-compilation-system.md);自举过程与可行性分析详见 [13-bootstrapping.md](13-bootstrapping.md);交互式 REPL 见 [15-repl.md](15-repl.md)。
> 自举注意点:自举后模块级全局变量带 **SHA1 前缀**(模块隔离),像 `_g_gen` 这类单例必须**逐模块赋值**,不能只在 `main.vp` 里赋裸名,否则部分模块会读到自己的 `None`。
+13 -7
View File
@@ -10,7 +10,7 @@
而实际上,下文中所有提到的 `C` 语言都是为了便于使用 `C` 的底层开发者理解。由于历史原因,在旧版本中我们使用 `C` 来作为中间语言,如同旧版本的 `C++` 一样,但现在 `target="c"` 的路径已经完全删除,`C` 路径永久不再受维护。
> **成熟度与当前任务(重要)**:Viper 的**语法在最新版本已经相当稳定**,对**现实工程已经可用**——[18-flagship-project.md](18-flagship-project.md) 中的 TransPyV(自举编译器)、ViperOS(裸机操作系统)、ViperMNIST(深度学习)等都是实证。因此当前的主要工作**不是**继续改动语法,而是**更正内部的存量代码**:把历史遗留的旧写法(`t.C*` / `c.*` / `T | t.CPtr` 等)迁移到新语法,详见 [02-type-system.md 类型书写规范](02-type-system.md#类型书写规范新语法推荐) 与仓库根目录的 `新规范`。
> **成熟度与当前任务(重要)**:Viper 的**语法在最新版本已经相当稳定**,对**现实工程已经可用**——[17-flagship-project.md](17-flagship-project.md) 中的 TransPyV(自举编译器)、ViperOS(裸机操作系统)、ViperMNIST(深度学习)等都是实证。因此当前的主要工作**不是**继续改动语法,而是**更正内部的存量代码**:把历史遗留的旧写法(`t.C*` / `c.*` / `T | t.CPtr` 等)迁移到新语法,详见 [02-type-system.md 类型书写规范](02-type-system.md#类型书写规范新语法推荐) 与仓库根目录的 `新规范`。
>
> **Viper 的前身是一个玩具项目**(把 Python 转成 C),**但我们期望 Viper 不再是**:它拥有独立的类型系统、两阶段编译模型、`SHA1` 命名空间模块体系与自举编译器,目标是成为一门可用于真实系统开发的编程语言。
@@ -48,9 +48,15 @@ Viper 编译后的代码在底层等价于 `C` 编译后的机器码(都经过
> **FFI/导出新写法**:推荐使用语言软关键字 `extern`/`export`——`extern def foo(...) -> T` 仅声明外部函数(实现来自外部),`export def` 以裸名导出符号。解析器会把它们改写为返回注解中的存储类标记(`extern` → `T | t.State`,`export` → `t.CExport`),因此 `t.State`/`t.CExtern`/`t.CExport` 标记并未废弃,仍是内部承载(手写可用)。
### C-LIKE 语法(逐步支持)
Viper 的语法形态以 `Python`(ABC 系)为蓝本,但自研语法器 [includes/ast](../includes/ast) **将逐步支持 C-LIKE 写法**(C 风格的分隔与声明形式)。作者认为这是**值得投入**的方向,因此**两种写法及其语义都会被长期维护、保持一致**——不存在弃用某一端的计划。
就**实际工程**而言,仍然**推荐 ABC-LIKE 写法**:它与 `Python` 习惯一致、可读性最好,也是本仓库全部工程与文档采用的形态。
## 两阶段编译流程
Viper 的编译由 `Projectrans.py` 驱动,分为两个阶段。这是 Viper 编译模型的核心,理解两阶段编译是理解 `t.CDefine`、`t.CExport`、`t.CInline` 等关键概念的前提。
Viper 的编译由入口驱动(TPC:`Projectrans.py`;TPV:`App/main.vp`),分为两个阶段。这是 Viper 编译模型的核心,理解两阶段编译是理解 `t.CDefine`、`t.CExport`、`t.CInline` 等关键概念的前提。
```
源文件 (.vp) ──────────────────────────────────────────────────────
@@ -84,7 +90,7 @@ Viper 的编译由 `Projectrans.py` 驱动,分为两个阶段。这是 Viper
#### 步骤 1:可达文件发现与拓扑排序
从入口文件(`main.vp` 或 `project.vpj` 指定;Python 单文件模式仍接受 `project.json`)出发,通过 `import` 语句递归遍历,找出所有可达的 `.vp` 源文件。然后对文件进行拓扑排序,确保被依赖的模块先被处理。
从工程根出发,通过 `import` 语句递归遍历,找出所有可达的 `.vp` 源文件,再对文件进行拓扑排序,确保被依赖的模块先被处理。工程根由工程配置的 `source_dir` 指定(`TransPyV` 与 `TransPyV/Test` 用 `project.vpj`,`Test/*` 用同格式的 `project.json`);也可用 `--src <入口文件>`,此时构建配置由依赖闭包内的 `viperbuild` 提供(见 [12-compilation-system.md](12-compilation-system.md))。**不存在「只编一个文件」的单文件模式。**
```
main.vp → import serial → import drivers.serial.uart.serial → ...
@@ -165,7 +171,7 @@ sha1 = hashlib.sha1(content.encode('utf-8')).hexdigest()[:16]
#### 步骤 4:翻译源文件
对每个源文件,使用 `TransPyC` 翻译器将 Python AST 翻译为 LLVM IR:
对每个源文件,使用对应翻译器(TPC `TransPyC` / TPV `lib/core/Gen`)将 AST 翻译为 LLVM IR:
1. 将对应模块的 `.stub.ll` 声明嵌入到生成的 `.ll` 文件头部
2. 所有被引用模块的 `.stub.ll` 声明也被嵌入
3. 翻译 AST 节点为 LLVM IR 指令
@@ -412,9 +418,9 @@ Viper **原本的语法完全符合 CPython**——源文件是合法的 Python
## 章节延伸
- 交互式 REPL(`--repl`)见 [16-repl.md](16-repl.md);
- 栈回溯与调试信息(`--traceback` / DWARF)见 [17-traceback.md](17-traceback.md);
- 用 Viper 写成的旗舰工程(TransPyV / VET / ViperOS / ViperMNIST)见 [18-flagship-project.md](18-flagship-project.md)。
- 交互式 REPL(`--repl`)见 [15-repl.md](15-repl.md);
- 栈回溯与调试信息(`--traceback` / DWARF)见 [16-traceback.md](16-traceback.md);
- 用 Viper 写成的旗舰工程(TransPyV / VET / ViperOS / ViperMNIST)见 [17-flagship-project.md](17-flagship-project.md)。
## Hello World 示例(标准写法)