stage1 部分功能测试00,01,02,06成功
This commit is contained in:
408
stage1/spl.md
Normal file
408
stage1/spl.md
Normal file
@@ -0,0 +1,408 @@
|
||||
# SPL — 语法与语义
|
||||
|
||||
## 概述
|
||||
|
||||
类 C 语法的系统编程语言,受 Rust/Zig 启发。编译为 SIR(SPL 中间表示),一种基于栈的字节码。多阶段引导:
|
||||
|
||||
```
|
||||
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` 必须求值为 bool(i32),或者成功语义。`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_t,8 字节)。
|
||||
- **帧指针**(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 中尚未实现。)
|
||||
Reference in New Issue
Block a user