40 KiB
SPL — Self-bootstrapping/System Programming Language
项目概述
SPL是一个从零构建的自举编译器项目。引导链:
stage0/spl_vm.c — SIR 虚拟机(C 语言实现)
stage1/spc0.c — SPL→SIR 编译器(C 语言实现,引导用)
stage1/spc1.spl — SPL→SIR 编译器(SPL 语言实现,自举第一版)
...将来...
spc2.spl → spc3.spl → ... → 完全自举
SPL 语法规范
Root <- skip ContainerMembers eof
# ================================================================
# 容器层 (Container Level)
# 文件 = 匿名 struct。所有声明 = 容器成员。
# LL(1): KEYWORD / AT / SHARP 分派
# ================================================================
ContainerMembers <- ContainerDeclaration*
ContainerDeclaration
<- FnDecl
/ TypeDecl
/ VarDecl
/ ConstDecl
/ ComptimeStmt
/ DirectiveBlock (* @init { } / #test { } *)
# ================================================================
# @ / # 属性列表 (对称设计)
# @ 前缀 = 编译期 # 前缀 = 运行期
# 属性可修饰任意声明: fn / type / var / const / param
# ================================================================
AttrList <- DirectiveHead+
DirectiveHead
<- (AT / SHARP) IDENTIFIER (LPAREN ExprList? RPAREN)?
# ================================================================
# 独立 @ / # 块 (仅容器层)
# ================================================================
DirectiveBlock
<- DirectiveHead Block (* @init { } / #test { } *)
(* 注意: @assert(x); @dbg(x); 是表达式语句, 走 ExprStatement → BuiltinExpr, 不经过这里 *)
# ================================================================
# comptime — 编译期执行 / 断言 (仅容器层)
# ================================================================
ComptimeStmt
<- KEYWORD_comptime Block (* comptime { code } *)
/ KEYWORD_comptime Expr SEMICOLON (* comptime <expr>; *)
# ================================================================
# 函数
# ================================================================
FnDecl
<- AttrList? KEYWORD_fn IDENTIFIER LPAREN ParamDeclList RPAREN TypeExpr?
(SEMICOLON / Block)
ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)?
ParamDecl
<- AttrList? IDENTIFIER COLON TypeExpr (* @capture x: *_ *)
# ================================================================
# 类型声明
# struct / union / enum 共享同一套容器成员规则
# ================================================================
TypeDecl
<- AttrList? KEYWORD_type IDENTIFIER EQUAL TypeBody (* @packed type Vec = struct { ... } *)
TypeBody
<- KEYWORD_struct LBRACE AggregateBody RBRACE
/ KEYWORD_union LBRACE AggregateBody RBRACE
/ KEYWORD_enum LBRACE AggregateBody RBRACE
/ TypeExpr (* 别名 *)
AggregateBody <- AggregateItem*
AggregateItem
<- MethodDecl (* fn ... *)
/ TypeDecl (* type X = ... *)
/ VarDecl (* var x: T *)
/ ComptimeStmt (* comptime ... *)
/ MemberDecl (* IDENTIFIER [: TypeExpr] *)
(* struct: 字段, 必须 IDENTIFIER : TypeExpr (语义层检查)
union: 联合体字段, 同上
enum: 变体, IDENTIFIER : TypeExpr 或 纯 IDENTIFIER (朴素变体) *)
MemberDecl
<- AttrList? IDENTIFIER (COLON TypeExpr)? (COMMA / SEMICOLON)?
MethodDecl
<- AttrList? KEYWORD_fn IDENTIFIER LPAREN ParamDeclList RPAREN TypeExpr? Block
# ================================================================
# 变量 / 常量
# 容器层 = 成员变量; 函数体内 = 局部变量; 语法相同
# ================================================================
VarDecl
<- AttrList? KEYWORD_var IDENTIFIER
(COLON TypeExpr / COLON_ASSIGN Expr)?
(EQUAL Expr)? SEMICOLON (* @volatile var flag: i32; *)
ConstDecl
<- AttrList? KEYWORD_const IDENTIFIER
(COLON TypeExpr / COLON_ASSIGN Expr)?
EQUAL Expr SEMICOLON
# ================================================================
# 块 / 语句层 (Block & Statement — 仅函数体内)
# ================================================================
Block <- LBRACE BlockItem* Expr? RBRACE
BlockItem <- Statement
Statement (* LL(1): 14 分支互斥 *)
<- IfStatement
/ WhileStatement
/ LoopStatement
/ ForStatement
/ MatchStatement
/ RetStatement
/ BreakStatement
/ ContinueStatement
/ DeferStatement
/ VarDecl
/ TypeDecl
/ ExprStatement
ExprStatement <- Expr SEMICOLON
# ---- if ----
IfStatement
<- KEYWORD_if Expr BlockOrStmt (KEYWORD_else BlockOrStmt)?
BlockOrStmt <- Block / Statement
# ---- while / loop / for ----
WhileStatement <- KEYWORD_while Expr BlockOrStmt
LoopStatement <- KEYWORD_loop BlockOrStmt
ForStatement
<- KEYWORD_for Expr (COMMA Expr)* KEYWORD_as IDENTIFIER (COMMA IDENTIFIER)* BlockOrStmt
# ---- match ----
MatchStatement <- KEYWORD_match Expr LBRACE MatchArm* RBRACE
MatchArm
<- MatchPat (COMMA MatchPat)* FAT_ARROW Statement
/ UNDERSCORE FAT_ARROW Statement
MatchPat (* LL(1): . / _ / Expr *)
<- DOT IDENTIFIER BindSpec?
/ Expr
BindSpec
<- LBRACKET IDENTIFIER RBRACKET
/ LBRACKET DOT IDENTIFIER EQUAL IDENTIFIER
(COMMA DOT IDENTIFIER EQUAL IDENTIFIER)* RBRACKET
# ---- 跳转 ----
RetStatement <- KEYWORD_ret Expr? SEMICOLON
BreakStatement <- KEYWORD_break SEMICOLON
ContinueStatement <- KEYWORD_continue SEMICOLON
# ---- defer ----
DeferStatement <- KEYWORD_defer BlockOrStmt
# ================================================================
# 表达式 (优先级爬升, 无左递归)
# ================================================================
Expr <- AssignExpr
AssignExpr <- BoolOrExpr (AssignOp AssignExpr)?
BoolOrExpr <- BoolAndExpr (BOOL_OR BoolAndExpr)*
BoolAndExpr <- BitOrExpr (BOOL_AND BitOrExpr)*
BitOrExpr <- BitXorExpr (PIPE BitXorExpr)*
BitXorExpr <- BitAndExpr (CARET BitAndExpr)*
BitAndExpr <- CmpEqExpr (AMPERSAND CmpEqExpr)*
CmpEqExpr <- CmpExpr ((EQ_EQ / BANG_EQUAL) CmpExpr)*
CmpExpr <- RangeExpr ((L_ARROW / L_ARROW_EQ / R_ARROW / R_ARROW_EQ) RangeExpr)*
# 新增 RangeExpr: 支持 a..b 和 a..
RangeExpr <- ShiftExpr (DOT2 ShiftExpr?)?
ShiftExpr <- AddExpr ((L_ARROW2 / R_ARROW2) AddExpr)*
AddExpr <- MulExpr ((PLUS / MINUS) MulExpr)*
MulExpr <- PrefixExpr ((ASTERISK / SLASH / PERCENT) PrefixExpr)*
PrefixExpr <- PrefixOp* PostfixExpr
PrefixOp <- MINUS / BANG / TILDE / AMPERSAND / ASTERISK
# ---- 后缀 ----
PostfixExpr
<- PrimaryExpr
( LPAREN ExprList RPAREN (* 函数调用 *)
/ DOT IDENTIFIER (* 字段/方法 *)
/ DOT ASTERISK (* 解引用 *)
/ LBRACKET Expr RBRACKET (* 索引 *)
/ LBRACKET Expr DOT2 Expr? RBRACKET (* 切片 *)
/ KEYWORD_as TypeExpr (* 类型转换 *)
)*
ExprList <- (Expr COMMA)* Expr?
# ---- 主要表达式 (LL(1)) ----
PrimaryExpr
<- INTEGER
/ FLOAT
/ CHAR_LITERAL
/ STRING_LITERAL
/ TRUE / FALSE / NULL_KW
/ IDENTIFIER (LBRACE StructInitList? RBRACE)? (* 变量引用 / 结构体字面量 *)
/ LPAREN Expr RPAREN
/ ArrayLiteral
/ BuiltinExpr
/ Block (* 块表达式 *)
ArrayLiteral <- LBRACKET INTEGER RBRACKET TypeExpr LBRACE ExprList? RBRACE
StructInitList <- StructInit (COMMA StructInit)* COMMA?
StructInit <- DOT IDENTIFIER (EQUAL Expr)?
BuiltinExpr
<- (AT / SHARP) IDENTIFIER LPAREN ExprList? RPAREN (* @sizeof(T) / #runtime(x) *)
# ================================================================
# 类型表达式 (Type Expression, LL(1), 无左递归)
# ================================================================
TypeExpr <- PrefixTypeOp* TypeBase
TypeBase <- FnTypeExpr / TypePath
PrefixTypeOp (* LL(1): * / [ *)
<- ASTERISK
/ LBRACKET (RBRACKET / INTEGER RBRACKET) (* [] 或 [N] *)
FnTypeExpr <- KEYWORD_fn LPAREN TypeExprList? RPAREN TypeExpr
TypeExprList <- (TypeExpr COMMA)* TypeExpr?
TypePath <- TypeAtom (DOT TypeAtom)*
TypeAtom (* LL(1): 关键词 / IDENTIFIER / _ *)
<- KEYWORD_void / KEYWORD_bool
/ KEYWORD_i8 / KEYWORD_u8 / KEYWORD_i16 / KEYWORD_u16
/ KEYWORD_i32 / KEYWORD_u32 / KEYWORD_i64 / KEYWORD_u64
/ KEYWORD_isize / KEYWORD_usize
/ KEYWORD_f32 / KEYWORD_f64 / KEYWORD_ptr
/ UNDERSCORE
/ IDENTIFIER
(* *_ = "any pointer", 唯一支持自动类型提升和隐式转换的指针类型。
语义上 *_ 可赋值给任意 *T, 任意 *T 可赋值给 *_。
# ================================================================
# 运算符
# ================================================================
AssignOp <- EQUAL / PLUS_EQ / MINUS_EQ / ASTERISK_EQ / SLASH_EQ / PERCENT_EQ
/ AMPERSAND_EQ / PIPE_EQ / CARET_EQ / L_ARROW2_EQ / R_ARROW2_EQ
BOOL_OR <- PIPE PIPE
BOOL_AND <- AMPERSAND AMPERSAND
EQ_EQ <- EQUAL EQUAL
BANG_EQUAL <- BANG EQUAL
# ================================================================
# 关键词 Token
# ================================================================
KEYWORD_fn <- 'fn' !IDENTIFIER_CHAR
KEYWORD_type <- 'type' !IDENTIFIER_CHAR
KEYWORD_var <- 'var' !IDENTIFIER_CHAR
KEYWORD_const <- 'const' !IDENTIFIER_CHAR
KEYWORD_if <- 'if' !IDENTIFIER_CHAR
KEYWORD_else <- 'else' !IDENTIFIER_CHAR
KEYWORD_while <- 'while' !IDENTIFIER_CHAR
KEYWORD_loop <- 'loop' !IDENTIFIER_CHAR
KEYWORD_for <- 'for' !IDENTIFIER_CHAR
KEYWORD_as <- 'as' !IDENTIFIER_CHAR
KEYWORD_match <- 'match' !IDENTIFIER_CHAR
KEYWORD_ret <- 'ret' !IDENTIFIER_CHAR
KEYWORD_break <- 'break' !IDENTIFIER_CHAR
KEYWORD_continue <- 'continue' !IDENTIFIER_CHAR
KEYWORD_defer <- 'defer' !IDENTIFIER_CHAR
KEYWORD_struct <- 'struct' !IDENTIFIER_CHAR
KEYWORD_union <- 'union' !IDENTIFIER_CHAR
KEYWORD_enum <- 'enum' !IDENTIFIER_CHAR
KEYWORD_comptime <- 'comptime' !IDENTIFIER_CHAR
KEYWORD_void <- 'void' !IDENTIFIER_CHAR
KEYWORD_bool <- 'bool' !IDENTIFIER_CHAR
KEYWORD_i8 <- 'i8' !IDENTIFIER_CHAR
KEYWORD_u8 <- 'u8' !IDENTIFIER_CHAR
KEYWORD_i16 <- 'i16' !IDENTIFIER_CHAR
KEYWORD_u16 <- 'u16' !IDENTIFIER_CHAR
KEYWORD_i32 <- 'i32' !IDENTIFIER_CHAR
KEYWORD_u32 <- 'u32' !IDENTIFIER_CHAR
KEYWORD_i64 <- 'i64' !IDENTIFIER_CHAR
KEYWORD_u64 <- 'u64' !IDENTIFIER_CHAR
KEYWORD_isize <- 'isize' !IDENTIFIER_CHAR
KEYWORD_usize <- 'usize' !IDENTIFIER_CHAR
KEYWORD_f32 <- 'f32' !IDENTIFIER_CHAR
KEYWORD_f64 <- 'f64' !IDENTIFIER_CHAR
KEYWORD_ptr <- 'ptr' !IDENTIFIER_CHAR
TRUE <- 'true' !IDENTIFIER_CHAR
FALSE <- 'false' !IDENTIFIER_CHAR
NULL_KW <- 'null' !IDENTIFIER_CHAR
# ================================================================
# 符号 Token
# ================================================================
LPAREN <- '(' RPAREN <- ')'
LBRACKET <- '[' RBRACKET <- ']'
LBRACE <- '{' RBRACE <- '}'
COMMA <- ',' SEMICOLON <- ';'
COLON <- ':' DOT <- '.'
DOT2 <- '..' DOT3 <- '...'
AT <- '@' SHARP <- '#'
FAT_ARROW <- '=>'
EQUAL <- '='
COLON_ASSIGN <- ':='
PLUS <- '+' MINUS <- '-'
ASTERISK <- '*' SLASH <- '/'
PERCENT <- '%'
AMPERSAND <- '&' PIPE <- '|'
CARET <- '^' TILDE <- '~'
BANG <- '!'
L_ARROW <- '<' R_ARROW <- '>'
L_ARROW2 <- '<<' R_ARROW2 <- '>>'
L_ARROW_EQ <- '<='
R_ARROW_EQ <- '>='
PLUS_EQ <- '+=' MINUS_EQ <- '-='
ASTERISK_EQ <- '*=' SLASH_EQ <- '/='
PERCENT_EQ <- '%=' AMPERSAND_EQ <- '&='
PIPE_EQ <- '|=' CARET_EQ <- '^='
L_ARROW2_EQ <- '<<=' R_ARROW2_EQ <- '>>='
# ================================================================
# 字面量 & 基础
# ================================================================
skip <- ([ \t\r\n] / LINE_COMMENT / BLOCK_COMMENT)*
IDENTIFIER <- [a-zA-Z_][a-zA-Z0-9_]*
IDENTIFIER_CHAR <- [a-zA-Z0-9_]
INTEGER <- DECIMAL / HEX / BINARY / OCTAL
DECIMAL <- [1-9][0-9_]* / '0'
HEX <- '0' [xX] [0-9a-fA-F][0-9a-fA-F_]*
BINARY <- '0' [bB] [01][01_]*
OCTAL <- '0' [oO] [0-7][0-7_]*
FLOAT <- [0-9]+ '.' [0-9]+
CHAR_LITERAL <- "'" (CHAR_PLAIN / CHAR_ESCAPE) "'"
CHAR_PLAIN <- [^\\'\n]
CHAR_ESCAPE <- '\\' [ntr\\'"0]
STRING_LITERAL <- '"' (STRING_CHUNK)* '"'
STRING_CHUNK <- [^\\"\n]+ / '\\' [ntr\\'"0]
LINE_COMMENT <- '//' [^\n]*
BLOCK_COMMENT <- '/*' (!'*/' .)* '*/'
eof <- !.
SPL 语义规范
设计总则
编译器微内核: 核心只提供类型系统、基本控制流、指针模型和内置数据类型。其余特性由库在编译期实现。
扩展点 @ / #: 预留的指令和内置函数接口。当前无任何预定义指令,未识别者在宽松模式下警告,严格模式下错误。
零静默原则: 任何可能出错或危险的构造至少产生一条警告,绝无静默通过。严格模式下所有警告视为错误。
无未定义行为: 所有行为必须完全定义,否则为编译错误(或宽松模式下的警告)。任何不安全操作均需显式标记。
内置数据类型: 语言内置区间 a..b 和切片 []T,它们是真实的结构体,拥有明确的内部字段,用于迭代和切片操作。
容器层(文件 = 匿名 struct)
文件视为匿名 struct,顶层声明顺序处理。
声明 静态约束 动态语义 FnDecl 名称唯一,签名完整。 仅定义。 TypeDecl 同作用域名称唯一。 定义类型别名或聚合体,编译时解析。 VarDecl(容器级) var: 必须初始化,类型完整。const: 初始化必须编译期可求值。 const 编译时计算;var 启动初始化一次。 ConstDecl 必须编译期可求值。 编译期常量。 ComptimeStmt 内部代码全部在编译时执行。 编译时执行,可生成声明。 DirectiveBlock @id { } / #id { } 为扩展占位,无预定义行为。未识别指令触发警告/错误。 同左。
属性与内置调用
@name / #name: 属性标记,修饰声明。
@name(args) / #name(args): 内置调用,出现在表达式位置。
语义: 完全由语言版本或库注册决定。当前所有均视为未识别,产生警告(宽松) 或错误(严格)。
函数
text AttrList? fn IDENTIFIER ( ParamDeclList ) TypeExpr? ( ; | Block ) 参数: 全部不可变。需要可变时通过 *T 传递。
返回类型: 省略即 void。仅有 ; 表示外部声明。
调用: 实参与形参数量、类型必须完全匹配(无隐式可变参数)。
执行: 新作用域 → 形参绑定实参 → 执行 Block → 遇 ret expr 返回(类型匹配),或 void 函数自然结束返回。
类型声明与聚合体
别名
type T = TypeExpr — 完全同义。
struct
字段必须 name: Type,不可省略。名称唯一。
构造: T { .f1 = e1, ... },必须全部字段显式初始化。
访问: expr.field。若 expr 是 struct 值,直接取字段;若为指针,自动解引用一层再取字段。多级指针必须连续 .*。
union
字段共享内存,直接读取视为不安全。当前版本只允许通过 match 解构读取,且必须穷举所有可能变体(或通配 _)。
enum
变体: variant 或 variant : Type。
构造: EnumName.variant 或带 (payload)。
匹配: match 必须穷举(或含 _),否则编译错误。
变量与常量
var x: T 或 var x := init: 可变量。
局部变量未初始化: 必须显式标注类型 var x: T;(无 =)。宽松模式警告,严格模式错误。绝不静默。
const x: T = expr 或 const x := expr: 不可变量,必须初始化,一次绑定。
赋值 x = expr: x 必须是 var 且类型兼容。
块与语句
块 { ... } 引入作用域,可为表达式: 尾表达式无分号则块值即其值,否则 void。
控制流(强制大括号体)
if if 条件 { ... } [else { ... }]
条件必须 bool,不外加括号。分支体必须 { }。
作表达式时两分支值类型一致。
while while 条件 { ... }: 条件 bool,体必须 { }。
loop loop { ... }: 无限循环,break 退出。
for 语法(PEG 已定义):
ForRange 是逗号分隔的表达式列表。每个表达式在启动阶段只能是内置可迭代对象:
区间 a..b 或 a..(无右端点): 内置类型 Range,内部字段 begin 和 end(end 可为 none 表示无界)。迭代产生从 begin 开始递增的整数,直到 end(不含)。若为 a..,则产生无界序列。
切片/数组 expr(类型为 []T 或 [N]T): 内置切片类型,内部结构为 ptr: *T, len: usize。迭代依次产生每个元素,类型为 T。
语义:
ForRange 产生一个表达式列表 E1, E2, ..., En。
as 后的变量列表长度必须等于 n,否则编译错误。
每个 Ei 必须是上述内置可迭代对象,否则编译错误。
并行迭代: 每次迭代从每个 Ei 中各自取出一个值,按顺序绑定到对应变量(只读,作用域在循环体内)。
循环继续直到任意一个序列耗尽。若序列长度不同,最短的耗尽时循环停止,忽略其余序列剩余元素。
所有变量只读,不可赋值。循环体必须为 { ... }。
长度协调:
区间 a..b 具有确定长度 b - a(若 b >= a,否则为 0)。
切片 []T 的长度由其 len 字段确定。
区间 a.. 无界,其长度由循环中其他序列的最短长度决定。例如 for my_slice, 0.. as elem, idx 中,0.. 将提供与 my_slice 等长的索引序列,因为循环在 my_slice 耗尽时终止。实际上 0.. 等价于 0..my_slice.len。
若循环中只有无界序列而没有有限序列,则循环无限进行(此时需 loop 替代,但语言仍接受)。
示例: // 单区间 for 0..5 as i { ... } // i: 0,1,2,3,4
// 单切片 for my_slice as elem { ... }
// 切片 + 索引(用户显式提供从0开始的区间) for my_slice, 0.. as elem, idx { ... } // idx 与 elem 一一对应
// 两个等长切片 for slice_a, slice_b as a, b { ... }
// ❌ 错误: 变量个数不匹配 for my_slice as elem, idx // 单序列却有两个变量 for my_slice, 0.. as elem // 双序列却只有一个变量 match 用于枚举或 union。
臂: .variant [bind] => { ... },bind 仅支持 [id](绑定整个载荷)。
必须穷举或含 _,否则编译错误。
作表达式时各臂值类型一致。
跳转 ret expr?: 类型匹配检查。
break: 仅循环内有效。
continue: 仅循环内有效。
defer defer { ... }: 退出作用域时以后进先出执行。
表达式
字面量
整数默认 i32,可被推导为更大类型,否则显式 as。
浮点 f64,布尔 bool。
null → ?T 隐式转换。
运算符
算术/位运算: 操作数类型必须完全一致或由字面量推导,无隐式提升。
比较: 结果为 bool,操作数类型匹配。
逻辑 && ||: 短路,操作数须为 bool。
前缀运算符 运算符 要求 结果
- 数值 同类型 ! bool bool ~ 整数 同类型 & 可寻址左值 T 解引用仅有后缀形式 .,无前缀 * 运算符。(*ptr) 不合法。
后缀运算符 运算 约束 说明 expr.* expr 为 *T 解引用,得 T 左值 expr.field struct 或指针 指针时自动解引用一层再取字段 expr[i] 数组/切片/指针,i 整数 元素左值 expr[a..b] 数组/切片/指针 切片操作,产生 []T,内部设置 ptr 和 len expr as Type 合法转换 类型转换 expr(args) 可调用 函数调用 7.3 主要表达式 标识符: 返回绑定的值或左值。
结构体字面量: Type { .f1 = v1, ... }。
块表达式: { ... }。
comptime { ... } / comptime expr;: 编译时求值,不可引用运行时变量。
内置调用: @name(args) / #name(args)。
类型表达式
基础类型: void, bool, 整数/浮点类型。
指针: *T, *_(任意指针),[N]T(数组),[]T(切片)。
函数类型: fn(T1, T2) RetType。
*_ 的特殊规则:
*T → *_: 允许,必须警告。
*_ → *T: 允许,必须警告。
这些警告无法全局关闭,未来提供显式抑制。
其他隐式转换:
整数字面量适配更大类型。
null → ?T。
其余必须显式 as。
内置数据类型详解
区间类型 Range 语法 a..b 或 a..。
内部结构: { begin: i64, end: ?i64 }(具体整数类型可能由上下文决定,默认为 i64)。
a..b: begin = a, end = b。
a..: begin = a, end = null。
用作迭代器时: 产生从 begin 到 end-1 的整数(若 end 为 null 则无穷)。在 for 中与其他序列配合时,无界的区间自动取其配对序列的长度作为上界(等于 0..other.len),如果配对序列也是无界则无限循环。
切片类型 []T 内部结构: { ptr: *T, len: usize }。
字面量创建: 切片字面量 &[N]T{...} 或从数组切片 array[a..b] 得到。
用作迭代器时: 依次产出 ptr 开始的 len 个元素,类型为 T。
安全性保证
未初始化变量: 宽松警告,严格错误。
空指针解引用: 无法证明非空则警告,严式要求安全解包。
数组越界: 编译时无法证明边界则编译错误。
整数溢出: 编译期常量运算溢出为错误;运行时溢出默认陷阱,将来 @unsafe 块内允许回绕。
未识别指令: 警告/错误。
@unsafe 块: 预留,用于显式允许不安全操作。
SPL 类型约束
前言
本文档定义语言的静态类型系统,包括:
所有内置类型的内部表示与分类
类型兼容性与隐式转换规则(附警告/错误表格)
表达式与语句的类型推导/检查规则(伪代码)
特殊类型的处理(指针、区间、切片等)
原则: 所有可能不安全或信息丢失的隐式转换均产生警告;不允许静默转换。最终严格模式下警告将变为错误。
内置基础类型
整数类型
类型 大小(位) 表示 对齐 i8, u8 8 二进制补码 / 无符号 1 字节 i16, u16 16 同上 2 字节 i32, u32 32 同上 4 字节 i64, u64 64 同上 8 字节 isize, usize 指针宽度 同上 同指针 未注明类型的整数字面量默认类型为 i32,但可根据上下文变化且需要灵活性, 比如说所有的ixxx都支持只列出最常见且能映射到c99的类型。
浮点类型
类型 大小 表示 对齐 f32 32 位 IEEE 754 单精度 4 字节 f64 64 位 IEEE 754 双精度 8 字节 浮点字面量默认类型为 f64。
布尔类型
bool: 大小为 1 字节,值只能是 true (1) 或 false (0)。
空类型
void: 大小为 0,表示无值,仅用于函数返回或指针。
复合类型
指针类型
*T: 指向类型 T 的指针,大小等于 usize,对齐同 usize。
*_: 任意指针,内部表示与 *void 相同,但携带“类型已丢失”标记。不能直接解引用,必须转换为具体指针后才能解引用。
数组类型
[N]T: 固定长度数组,长度为编译期常量 N,元素类型 T。连续内存布局,大小 = N * sizeof(T)。
数组可隐式转换为切片(见隐式转换)。
切片类型
[]T: 切片,内部结构 { ptr: *T, len: usize }。值类型同聚合类型行为。
切片不可为 null;空切片用 len == 0 表示。
从数组构造: array[a..b] 产生切片。
区间类型
a..b 或 a..: 类型为 Range,内部结构 { begin: isize, end: ?isize }。
a..b: end 为 b。
a..: end 为 null(无界)。
Range 是值类型同聚合类型行为。
用于迭代和切片边界。
函数类型
fn(参数类型列表) 返回类型
函数值本身的大小和表示未指定(闭包待定),但函数名作为标识符使用时具有指针语义(类似函数指针)。
可选类型(暂时不需要实现)
?T: 可为 null 的类型。内部表示同 T 但附加一个判别(可能通过 null 指针表示,视 T 而定)。?T 的大小和对齐与 T 相同或扩展为可容纳 null 的形式(具体实现定义)。
null 字面量只能出现在需要 ?T 的上下文中。
自定义聚合类型
struct: 字段连续排列(可能有对齐填充),每个字段有自己的类型。赋值是逐字段拷贝。
union: 所有字段共享起始地址,大小等于最大字段(加上对齐)。直接字段读取被视为不安全,需通过 match 解构。
enum: 带标签的联合体,每个变体可有载荷。大小实现定义,但需容纳判别式及最大载荷。
类型分类
值类型(复制语义)
所有基本标量类型,或者说底层寄存器类型(整数、浮点、bool)
struct
数组 [N]T
切片 []T
区间 Range
赋值、传参会复制整个值。修改副本不影响原值。
引用/指针类型
*T、*_
函数指针(内部类似 *const fn(...))
特殊类型
void: 无法实例化,仅用于返回或指针目标。
null: 不是独立类型,仅用于初始化或赋值给 ?T。
类型兼容性与隐式转换
下表中,“允许”表示可自动转换,否则需要显式 as 转换。警告列表明编译器必须输出诊断信息,不可静默。
源类型 目标类型 允许? 警告? 备注 T(任意) T 是 无 相同类型 *T *_ 是 警告: “丢失类型信息” *_ *T 是 警告: “不安全的指针重解释” [N]T []T 是 无 数组到切片强制转换 整数字面量 整数类型 U 是(若值在 U 范围内) 无 字面量自动拓宽 i32 i64 否 — 需显式 as i64,防止意外 i64 i32 否 — 窄化必须显式 null ?T 是 无 空值初始化 ?T T 否 — 需显式解包(如 orelse,但语言暂未定义,将来扩展) T ?T 是 无 提升为可选 浮点字面量 f32 是(值可表示则) 无 f64 f32 否 — 窄化需显式 bool 整数 否 — 整数 bool 否 —
注:
隐式转换不会嵌套传递。例如 *T 到 *_ 是警告转换,但不因此进一步允许 *_ 到 **T 的隐式转换。
字面量拓宽仅适用于整数字面量直接出现在需要更宽整数类型的上下文(如赋值给 i64 变量,或作为 Range 的边界,Range 内部为 isize,所以 0..5 中的 0 和 5 会拓宽为 isize)。
所有其他未列出的类型转换均需显式 as。
表达式类型推导规则
以下用伪代码描述每个表达式类型的推断方法。若类型检查失败,则为编译错误。
字面量
INTEGER → i32
FLOAT → f64
true / false → bool
null → 必须从上下文推导出 ?T,无法推导则报错。
字符串字面量 → []u8(具体待定)
二元运算
算术 e1 + e2、-、*、/、%:
t1 = type(e1), t2 = type(e2)
若 t1 == t2 且 t1 ∈ 数值类型:
返回 t1
否则若 t1 和 t2 为整数且其中一个是字面量(类型为 i32 或可拓宽):
返回 max(t1, t2) // 字面量拓宽
否则:
错误 "类型不匹配"
比较 e1 == e2、<、> 等:
t1 = type(e1), t2 = type(e2)
若 t1 和 t2 兼容(相同或允许隐式转换):
返回 bool
否则:
错误
逻辑 &&、||:
要求 e1 和 e2 均为 bool,返回 bool
位运算 &、|、^、<<、>>:
同算术规则,但要求操作数为整数类型
前缀运算
-e: e 必须为数值类型,结果同类型。
!e: e 必须为 bool,结果为 bool。
~e: e 必须为整数,结果同类型。
&e: e 必须为可寻址左值(变量、字段、*解引用等)。若 e 类型为 T,结果为 T。 注意: 没有 * 前缀运算符(解引用仅后缀 .)。
后缀运算
e.*: 要求 e 类型为 *T,结果为 T,且作为左值。
e.field:
若 e 的类型为 struct S,则字段类型为声明类型。
若 e 的类型为 *S,则自动解引用一层,效果等同于 e.*.field,结果类型为字段类型。
多级指针必须连续 .*: p.*.*.field。
e[i]:
e 类型为 [N]T 或 []T 或 *T(视为指向单个元素或数组起始),i 为整数。结果为 T 左值。
e[a..b]:
e 类型为 [N]T 或 []T,a 和 b 为整数(可省略 b)。结果为 []T。
e(args):
e 类型必须为函数类型 fn(T1, T2, ...) Ret。实参类型须与形参兼容(允许隐式转换)。结果为 Ret。
e as Type:
要求源类型与目标类型间存在合法显式转换(包括整数窄化、指针转换等)。结果为 Type。
主要表达式
标识符: 查找作用域,返回其声明类型。
结构体字面量 Type { .f1 = e1, ... }:
Type 必须是 struct 类型。检查字段数量是否齐全,每个 ei 类型与字段类型兼容。返回 Type。
块表达式 { stmts; expr? }:
若末尾有 expr,块类型为该 expr 类型;否则为 void。
comptime e: e 必须在编译时可求值,类型同 e。
内置调用 @id(args) / #id(args): 未定义则警告/错误(按模式),否则行为由对应内置定义决定。
语句类型规则
变量声明
var x: T = e;: e 的类型必须兼容 T。x 获得类型 T,可变。
var x := e;: 推导类型为 e 的类型,x 可变。
var x: T;: 无初始化,x 具有类型 T。警告(宽松模式)或错误(严格模式)。不可用于 const。
const x: T = e;: 同上兼容性,x 不可变。
const x := e;: 推导类型,不可变。
赋值
x = e;: x 必须是可变变量,e 的类型兼容 x 的类型。
控制流
if cond { ... } else { ... }: cond 必须为 bool。若 if 用作表达式,两分支块的类型必须相同(或均为 void)。
while cond { ... }: cond 必须为 bool。
loop { ... }: 无类型限制。
for range_list as var_list { ... }(见下节)。
match e { arms }: e 的类型为枚举或 union。每个模式臂的类型(若 match 用作表达式)必须一致。必须穷举。
for 循环详细类型规则
语法: for E1, E2, ... as v1, v2, ... { ... }
ForRange 提供表达式列表 [E1, E2, ...]。as 后标识符列表 [v1, v2, ...] 长度必须相等,否则编译错误。
如果动态类型动态长度,则警告具体行为待定,整个语法待定,下面将是暂时语法。
每个 Ei 的类型必须为内置可迭代类型(当前启动阶段仅包括 Range 和 []T / [N]T)。若 Ei 的类型不是这两者之一,编译错误。
对于 Ei:
若为 Range: 每次迭代产出的值类型为 isize(Range 的边界类型,这里默认为 isize)。
若为 []T 或 [N]T: 产出元素类型为 T。
相应地,vi 被推导为只读变量,其类型为对应 Ei 产出的值类型。
所有序列的长度必须可静态协调(见下文),否则编译错误(或警告,宽松模式下允许无界)。
长度协调规则:
若所有序列均为有界(Range 有 end 不为 null,或切片长度已知),则循环次数为最短长度。
若存在无界 Range(如 0..),则要求循环中至少有一个有界序列,且该有界序列的长度将作为无界序列的上限。例如 for slice, 0.. as elem, idx,0.. 的长度由 slice.len 决定。
若只有无界序列,则为无限循环(合法,但通常应使用 loop)。
示例:
for my_slice, 0.. as elem, idx { ... }
// my_slice: []T → elem: T
// 0..: Range 无界 → idx: i64,长度由 my_slice 决定
类型系统限制
无隐式类型提升(除字面量整数拓宽和数组到切片外)。
无默认初始化: var x: T; 不初始化,警告/错误。
无隐式 deref 多级: . 仅自动解引用一层。
*_ 转换必定警告,不可忽略。
所有未列出的隐式转换均错误,需显式 as。
IR
设计原则
微内核:IR 核心只有函数、基本块、变量和函数调用,无内置指令语义。
全函数式:算术、内存、控制流、ABI 等所有操作均通过调用标准库 @ops 和 @abi 函数完成。
底层控制:类型系统支持任意宽度整数、浮点,结构体/联合体布局由字段声明显式控制。
ABI 剥离:跨函数调用和返回统一由 @abi.call / @abi.ret 封装,内部函数可自由优化布局。
词法前缀与语义
前缀 语义 示例 @ 全局函数名、类型名、内置函数调用 @main, @Vec2, @arith.add % 局部变量(虚拟寄存器) %sum, %ptr # 基本块标签 #entry, #loop_body ! 编译/链接标记(元数据) !export("C"), !section(".text") 词法成本极低:最多两字符即可区分所有实体,无需长关键字。
语义完全解耦:
# 仅用于控制流跳转目标。
! 仅用于附加在函数/变量上的编译指令或属性。
表达式内不出现 !:运算全部通过 @ 函数调用完成,无内置逻辑非运算符,避免歧义。
语法
IRModule ← (Function | TypeDef)*
TypeDef ← 'type' GLOBAL_IDENT '=' Type ';'
Function ← AttributeList? 'func' GLOBAL_IDENT '(' ParamDefs? ')' '->' Type Block
AttributeList ← Attribute (',' Attribute)*
Attribute ← '!' IDENTIFIER ( '(' Args? ')' )? // 标记,如 !export("C")
GLOBAL_IDENT ← '@' IDENTIFIER
ParamDefs ← LOCAL_IDENT ':' Type (',' LOCAL_IDENT ':' Type)*
Block ← '{' Stmt* '}'
Stmt ← VarDef ';'
/ CallStmt ';'
/ Branch
/ Return
/ Label
VarDef ← LOCAL_IDENT '=' Expr // 严格 SSA 单次定义
CallStmt ← Expr // 忽略返回值
Branch ← 'br' LOCAL_IDENT ',' LABEL ',' LABEL
Return ← 'ret' (LOCAL_IDENT | CONSTANT)? ';'
Label ← LABEL ':'
Expr ← CallExpr | LOCAL_IDENT | CONSTANT
CallExpr ← GLOBAL_IDENT ( '(' TypeArgs ')' )? '(' Args? ')'
TypeArgs ← Type (',' Type)*
Args ← Expr (',' Expr)*
Type ← IntType | FloatType | 'bool' | 'void'
| 'ptr' '<' Type '>'
| 'array' '<' Type ',' INTEGER '>'
| 'struct' '<' FieldList '>'
| 'union' '<' FieldList '>'
| 'fn' '<' '('TypeList?')' '->' Type '>'
| GLOBAL_IDENT
IntType ← ('i' / 'u') [1-9][0-9]*
FloatType ← 'f' [1-9][0-9]*
FieldList ← (IDENTIFIER ':' Type (',' IDENTIFIER ':' Type)*)?
TypeList ← Type (',' Type)*
LOCAL_IDENT ← '%' IDENTIFIER
LABEL ← '#' IDENTIFIER
IDENTIFIER ← [a-zA-Z_][a-zA-Z0-9_]*
CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefined'
内置函数库
验证规则约束
单一定值:每个 %name 在其函数内只被赋值一次(%x = … 或作为参数),且使用前必须支配所有使用点。
类型匹配:@arith.add(i32)(%a, %b) 要求 %a 和 %b 的类型都是 i32 绝对的类型匹配。
块终止:每个基本块以 br 或 ret 结束,目标 #label 必须在当前函数内存在。
函数存在性:所有调用的 @ 函数(如 @arith.add)必须来自库。
完整内置函数库 (@ 调用)
算术运算
要求操作数类型 T 为整数或浮点,返回类型为 T。
函数签名 说明 @arith.add(T)(%a:T, %b:T) -> T 加法 @arith.sub(T)(%a:T, %b:T) -> T 减法 @arith.mul(T)(%a:T, %b:T) -> T 乘法 @arith.div(T)(%a:T, %b:T) -> T 除法(整数截断向零,浮点为 IEEE 除法) @arith.rem(T)(%a:T, %b:T) -> T 取余(仅整数,符号跟随被除数) @arith.neg(T)(%a:T) -> T 取负 @arith.abs(T)(%a:T) -> T 绝对值
位运算
操作数类型 T 必须为整数,返回类型 T。
函数签名 说明 @arith.and(T)(%a:T, %b:T) -> T 按位与 @arith.or(T)(%a:T, %b:T) -> T 按位或 @arith.xor(T)(%a:T, %b:T) -> T 按位异或 @arith.shl(T)(%a:T, %b:T) -> T 左移,%b 为移位量(类型同 T) @arith.shr(T)(%a:T, %b:T) -> T 右移(算术或逻辑由 T 的有无符号决定) @arith.not(T)(%a:T) -> T 按位取反
比较运算
操作数类型 T 必须一致,返回 bool。
函数签名 说明 @cmp.eq(T)(%a:T, %b:T) -> bool 相等 @cmp.ne(T)(%a:T, %b:T) -> bool 不等 @cmp.lt(T)(%a:T, %b:T) -> bool 小于(有符号/无符号根据 T) @cmp.le(T)(%a:T, %b:T) -> bool 小于等于 @cmp.gt(T)(%a:T, %b:T) -> bool 大于 @cmp.ge(T)(%a:T, %b:T) -> bool 大于等于
类型转换
函数签名 说明 @cast.trunc(SRC_T, DST_T)(%a:SRC_T) -> DST_T 截断整数或浮点窄化 @cast.zext(SRC_T, DST_T)(%a:SRC_T) -> DST_T 无符号扩展整数 @cast.sext(SRC_T, DST_T)(%a:SRC_T) -> DST_T 有符号扩展整数 @cast.fext(SRC_T, DST_T)(%a:SRC_T) -> DST_T 浮点扩展 @cast.ftrunc(SRC_T, DST_T)(%a:SRC_T) -> DST_T 浮点截断 @cast.bitcast(SRC_T, DST_T)(%a:SRC_T) -> DST_T 位模式重解释(类型大小必须相等) @cast.ptrtoint(P_T, INT_T)(%ptr:P_T) -> INT_T 指针到整数 @cast.inttoptr(INT_T, P_T)(%val:INT_T) -> P_T 整数到指针 @cast.bool_to_int(INT_T)(%cond:bool) -> INT_T 布尔转整数(true->1, false->0)
内存操作
函数签名 说明 @mem.alloca(T)(%count:i32) -> ptr 栈上分配 %count * sizeof(T) 字节,返回对齐的指针 @mem.load(T)(%ptr:ptr) -> T 从内存加载类型为 T 的值 @mem.store(T)(%ptr:ptr, %val:T) 存储值到内存 @mem.offset(T)(%ptr:ptr, %offset:i32) -> ptr 指针算术,以 sizeof(T) 为单位偏移 @mem.copy(%dst:ptr, %src:ptr, %size:i32) 内存块复制 @mem.set(%dst:ptr, %val:u8, %size:i32) 内存块填充 @mem.fence(%ordering:u32) 内存屏障(见原子操作节)
类型信息查询
所有查询在编译期求值,返回整数。
函数签名 说明 @type.sizeof(T)() -> i32 返回类型 T 的字节大小 @type.alignof(T)() -> i32 返回类型 T 的对齐要求 @type.offsetof(T)(%field_index:i32) -> i32 聚合类型 T 中第 field_index 个字段的字节偏移(字段从0编号) @type.field_count(T)() -> i32 返回聚合类型的字段数量
聚合类型操作
函数签名 说明 @agg.extract(T, FIELD_INDEX)(%val:T) -> FIELD_T 从 struct 中提取第 FIELD_INDEX 个字段(常量索引) @agg.insert(T, FIELD_INDEX)(%agg:T, %field:FIELD_T) -> T 替换 struct 中指定字段,返回新 struct @agg.extract_union(T, FIELD_IDENT)(%val:T) -> FIELD_T 从 union 中读取指定字段(需确保当前活跃) @agg.insert_union(T, FIELD_IDENT)(%payload:FIELD_T) -> T 创建 union 值,将载荷写入指定字段
ABI 调用接口
用于跨函数边界,封装调用约定。
函数签名 说明 @abi.call(FT)(%fn:FT, %args...) -> %ret 按目标平台调用约定调用函数指针 %fn,FT 为 fn<param_types...->ret_type> @abi.ret(T)(%val?) 按目标平台调用约定从当前函数返回,T 为返回值类型,void 时不带参数 内部函数若未使用 !export 标记,可自由使用直接 ret 指令而不经过 @abi.ret,此时编译器可完全自定义内部调用协议。
原子操作(可选,现在不实现)
原子操作需要一个 ordering 参数,类型为 u32,使用常量表示内存顺序(可参照 C11 内存模型定义,例如 0=relaxed, 1=acquire, 2=release, 3=acq_rel, 4=seq_cst)。
函数签名 说明 @atomic.load(T)(%ptr:ptr, %ordering:u32) -> T 原子加载 @atomic.store(T)(%ptr:ptr, %val:T, %ordering:u32) 原子存储 @atomic.rmw_add(T)(%ptr:ptr, %val:T, %ordering:u32) -> T 原子交换加(返回旧值) @atomic.rmw_sub(T)(%ptr:ptr, %val:T, %ordering:u32) -> T 原子交换减 @atomic.rmw_and(T), or, xor, xchg 等类似 @atomic.cmpxchg(T)(%ptr:ptr, %expected:T, %desired:T, %ordering_success:u32, %ordering_failure:u32) -> {old:T, ok:bool} 原子比较交换,返回旧值和成功标志(通过 struct 返回)
控制流扩展(间接跳转、选择)
函数签名 说明 @control.select(T)(%cond:bool, %true_val:T, %false_val:T) -> T 选择操作(无分支,类似三元运算符) @control.unreachable() -> void 标记不可达代码 @control.trap() -> void 触发运行时陷阱
调试与内省
函数签名 说明 @dbg.breakpoint() 插入调试断点 @dbg.declare(%var:%) 声明局部变量的调试信息(可被后端忽略)
完整标记系统 (! 前缀)
标记附加在函数定义前,用逗号分隔。所有标记均不影响 IR 控制流语义,仅向后端传递元数据。
标记 参数 含义 使用场景 !link("export") — 符号对外可见 库的公开 API !link("import") — 符号来自外部模块 调用外部库 !link("weak") — 弱符号 可被覆盖的默认实现 !abi("C") 调用约定名称 指定跨函数调用的 ABI 与 C 代码交互 !symbol("name") 字符串 自定义导出符号名 避免名称混淆 !naked — 无函数序言/尾声 中断向量、系统调用包装 !noinline — 禁止内联 调试或特殊性能需求 !alwaysinline — 总是内联 简单的包装函数