stage1 部分功能测试00,01,02,06成功

This commit is contained in:
zzy
2026-07-05 18:23:33 +08:00
parent 50b07074fb
commit 51d8510b79
30 changed files with 4336 additions and 59 deletions

408
stage1/spl.md Normal file
View File

@@ -0,0 +1,408 @@
# SPL — 语法与语义
## 概述
类 C 语法的系统编程语言,受 Rust/Zig 启发。编译为 SIRSPL 中间表示),一种基于栈的字节码。多阶段引导:
```
C → splc0(C) → splc1(SPL) → splc2(SPL) → ...
```
---
## 词法结构
### 注释
```
// 行注释
/* 块注释 */
```
### 标识符
`[a-zA-Z_][a-zA-Z0-9_]*`
### 关键字
```
as asm bool break catch comptime const continue
defer else enum errdefer false fn for
if loop match null ret struct
test true try type union var void
while _
```
### 字面量
| 种类 | 示例 | 类型 |
|-------------|---------------------------------|---------|
| 整数 | `42` `0xFF` `0b1010` `0o77` | i32 |
| 字符 | `'A'` `'\n'` `'\\'` | i32 |
| 字符串 | `"hello"` `"line\n"` | i8* |
| 布尔 | `true` `false` | bool |
| 空 | `null` | null/0/undefinded |
### 运算符
| 类别 | 符号 |
|------------|------------------------------------------------------------|
| 算术 | `+` `-` `*` `/` `%` |
| 位运算 | `&` `\|` `^` `~` `<<` `>>` |
| 比较 | `==` `!=` `<` `<=` `>` `>=` |
| 逻辑 | `&&` `\|\|` `!` |
| 赋值 | `=` `+=` `-=` `*=` `/=` `%=` `&=` `\|=` `^=` `<<=` `>>=` |
| 其他 | `&`(取地址) `*`(解引用) `.` `->` `<-` `..` `...` `:` `:=` `?` |
---
## 类型系统
### 基本类型
| 名称 | SIR 类型 | 大小(字节) |
|--------|-------------|-------------|
| void | SPL_VOID | 0 |
| bool | SPL_I32 | 4 |
| i8 | SPL_I8 | 1 |
| u8 | SPL_U8 | 1 |
| i16 | SPL_I16 | 2 |
| u16 | SPL_U16 | 2 |
| i32 | SPL_I32 | 4 |
| u32 | SPL_U32 | 4 |
| i64 | SPL_I64 | 8 |
| u64 | SPL_U64 | 8 |
| isize | SPL_ISIZE | sizeof(ptr) |
| usize | SPL_USIZE | sizeof(ptr) |
| f32 | SPL_F32 | 4 |
| f64 | SPL_F64 | 8 |
| ptr | SPL_PTR | 8 |
| _ | SPL_ ... | (推断) |
`_` 是通配符类型——用作类型推断的占位符。在大多数声明上下文中无效。
### 指针类型
写作 `*T`。示例:`*i32``*u8``*void`
同类型指针会去重。
### 数组类型
写作 `[N]T`。示例:`[10]i32`。数组是值类型(存在于栈槽或全局数据中)。`T` 可以是任意类型。
数组/复合类型 在VM堆上申请内存 `N * sizeof(T)`
### 切片类型
写作 `[]T`。示例:`[]i32``[]u8`。切片是数组的一段连续视图,底层实现为胖指针:
```
[]T = struct { ptr: *T, len: i32 }
```
栈上占 **2 个槽位**(指针 + 长度)。通过切片表达式从数组创建:
```
var arr: [10]i32 = ...;
var slice: []i32 = arr[3..7]; // 取 arr[3..7) 视图
var full: []i32 = arr[0..]; // 省略结束值 = 到末尾
```
此外,切片也可以从另一个切片再切片得到,长度限制为原切片的 len。
### 聚合类型
```
type Name = struct { field: Type, ... };
type Name = union { field: Type, ... };
type Name = enum { A, B, C, ... };
```
- **struct**:字段按顺序排列(默认由 SPL 定义布局,可通过 `#[extern("vm")]` 覆盖)
- **union**所有字段共享同一偏移sizeof = 最大字段大小)
- **enum**:无字段,值为从 0 开始的整数常量
聚合类型在堆上分配,如果需要栈分配那么栈上占用 `size` 个槽位struct 为各字段大小之和union 为最大字段大小enum 为 1
`type Name = ExistingType;` 形式用于定义简单类型别名,但目前仅支持聚合类型的别名定义。
---
## 声明
### 函数
```
fn name(param: Type, ...) ReturnType {
body...
}
fn name(param: Type, ...) ReturnType; // 前向声明一般来说不需要
```
函数参数在栈上按声明顺序从左到右排列。参数通过 `LADDR fp+idx` 访问。
### 原生函数VM 互操作)
```
#[extern("vm")]
fn vm_function(arg: Type, ...) ReturnType;
```
声明一个可通过 NCALL 调用的外部 VM 函数。运行时须链接或通过 `spl_syscall_register()` 注册实现。
### 类型别名
```
type Name = struct/union/enum { ... };
type Name = ExistingType;
```
### 变量
```
var name: Type = expr;
```
变量在当前函数栈帧上分配空间。声明时若带 `= expr` 则将表达式值存入栈槽。
### 常量
```
const name: Type = expr;
```
在 splc0 中常量也被分配栈空间(行为与 var 相同)。编译时可求值的常量会被记录到 `consts` 映射表,允许在局部作用域中引用。
### 短声明
```
var name := expr; // 类型推断为 expr 的 type
const name := const_expr; // 类型推断为 expr 的 type
```
语法糖:声明变量/常量并立即赋初始值,类型从表达式推断。
---
## 内置指令(@ 前缀)
`@` 开头的标识符用于编译器内置操作:
### @import
```
@import("path")
```
在编译时导入另一个 SPL 源文件。路径相对于当前源文件目录。用于模块化编译。
### @sizeof
```
@sizeof(T)
```
返回类型 `T` 的大小字节编译期常量。可用于分配内存、I/O 缓冲区。splc0 中暂未实现。
### @offsetof
```
@offsetof(T, field)
```
返回结构体 `T` 中字段 `field` 的字节偏移。实现泛型/运行时反射时有用。splc0 中暂未实现。
### @panic
```
@panic("message")
```
编译期中断输出错误信息并终止编译。splc0 中暂未实现。
### @assert
```
@assert(expr)
```
编译期断言——若 `expr` 为假则中断编译。splc0 中暂未实现。
### @embed
```
@embed("file")
```
在编译时将文件内容作为字节数组嵌入程序。splc0 中暂未实现。
---
## 语句
### 块
```
{ statement; statement; ... }
```
创建新作用域——局部声明的变量在退出时被丢弃符号表弹出且defer也是基于块作用域的
块可以返回值,只需要最后一个表达式没有分号。
### 表达式语句
```
expr;
```
### 赋值
```
name = expr;
name += expr;
name.field = expr;
name[expr] = expr;
```
支持 `=``+=``-=``*=``/=``%=``&=``|=``^=``<<=``>>=`
### 返回
```
ret expr;
ret; // void 返回(仅限 void 函数)
```
RET 指令会根据函数返回类型携带或不带返回值。
### If / Else
```
if expr statement
if expr statement else statement
```
`expr` 必须求值为 booli32或者成功语义。`statement` 可以是块 `{ }`
实现:`BZ` 跳转到 else 分支,`JMP` 跳过 else 分支。
### While
```
while expr { ... }
```
实现:在循环顶部求值 `expr``BZ` 跳转到循环结束,循环体末尾 `JMP` 跳回顶部。
### Loop
```
loop { ... }
```
无限循环。使用 `break` 退出,`continue` 重新开始。
### Break / Continue
```
break;
continue;
```
break 通过链表记录所有跳出位置,在循环结束后统一回填目标地址。
### Defer
```
defer { ... }
defer statement;
```
作用域退出时延迟执行
### For Range
```
for 0..N as i { body }
for slice as val { body }
for slice, 0.. as val, idx { body }
```
Range for 循环。支持三种形式:
1. **数值区间** `for begin..end as i``i``begin``end-1`,步长 1
2. **切片遍历** `for slice as val`:遍历切片的每个元素
3. **带索引的切片遍历** `for slice, 0.. as val, idx`:同时获得元素值和索引
展开实现:
```
// for 0..N as i { body }
var i: i32 = 0;
while i < N {
// body...
i = i + 1;
}
// for slice, 0.. as val, idx { body }
var idx: i32 = 0;
var _len: i32 = slice.len;
var _ptr: *T = slice.ptr;
while idx < _len {
var val: T = _ptr[idx];
// body...
idx = idx + 1;
}
```
---
## 表达式(优先级爬升)
### 运算符优先级表
| 优先级 | 运算符 | 结合性 |
|--------|-------------------------|----------|
| 1 | `\|\|` | 左结合 |
| 2 | `&&` | 左结合 |
| 3 | `\|` | 左结合 |
| 4 | `^` | 左结合 |
| 5 | `&` | 左结合 |
| 6 | `==` `!=` | 左结合 |
| 7 | `<` `<=` `>` `>=` | 左结合 |
| 8 | `<<` `>>` | 左结合 |
| 9 | `+` `-` | 左结合 |
| 10 | `*` `/` `%` | 左结合 |
| 前缀 | `-` `!` `~` `&` `*` | 右结合 |
| 后缀 | `.field` `[expr]` | 左结合 |
**`||``&&` 的短路求值**`||``BNZ` 左侧为真时跳过右侧;`&&``BZ` 左侧为假时跳过右侧。
### 主要表达式
```
字面量 → PUSH 立即数
标识符 → LADDR 局部变量地址(可选 LD64 取值)
标识符(args) → 函数调用
(expr) → 分组
```
- 标识符引用局部变量时:数组/聚合类型压入地址其他类型压入值LADDR + LD64
- 函数调用时 push 参数从左到右push 函数地址CALL nargs
### 后缀表达式
```
expr.field → 结构体成员访问(计算字节偏移)(可以单层解引用即 推断c语言的 -> 但是只能单层)
expr[expr] → 数组/指针索引
expr[begin..end] → 切片表达式(从数组/切片创建视图)
expr.* → 解引用LD64
```
- **`.field`**:根据类型布局计算字节偏移。结构体:地址 + 偏移,嵌套结构体保留地址。指针指向的结构体:先 LD64 解引用获取堆地址,再加字段偏移。
- **`[expr]`**:指针索引用元素字节大小做乘法,数组索引用槽位大小做乘法。最后 ADD 得到元素地址LD* 加载值。
### 前缀表达式
```
-expr → 算术取反NEG
!expr → 逻辑非expr == 0 → EQ + PUSH 0
~expr → 按位取反NOT
&expr → 取地址LADDR仅限标识符
```
---
## 调用约定
> 见 ../stage0/spl_ir.h
---
## SIR 指令集
> 见 ../stage0/spl_ir.h
---
## 内存模型
- **栈**:向上增长。每个槽位为 `spl_val_t`uintptr_t8 字节)。
- **帧指针**fp当前函数参数的基址。
- **栈指针**sp栈顶。
- **金丝雀**:存储在 `data[fp-1]`(若 fp > 0——对编译后的代码不可见。
- **全局数据**gdata按索引引用的字节块。字符串、静态数据等。
---
## 编译期执行Comptime
```
comptime { ... }
```
在编译时执行代码,通过将编译后的 .sir 文件加载到子 VM 中。通过以下系统调用实现:
```
vm_new() → ptr // 创建子 VM
vm_drop(vm) // 销毁子 VM
vm_load(vm, path) → ptr // 加载 .sir返回 prog
vm_push(vm, val) // 将参数压入子 VM 栈
vm_call(vm, name, nargs) // 调用函数
vm_run(vm) → i32 // 运行子 VM返回退出码
```
splc0 中尚未实现。)