diff --git a/SPL.md b/SPL.md new file mode 100644 index 0000000..9a64e05 --- /dev/null +++ b/SPL.md @@ -0,0 +1,1175 @@ +# 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 语法规范 + +```peg +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 ; *) + + +# ================================================================ +# 函数 +# ================================================================ + +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、-、*、/、%: + +```text +t1 = type(e1), t2 = type(e2) +若 t1 == t2 且 t1 ∈ 数值类型: + 返回 t1 +否则若 t1 和 t2 为整数且其中一个是字面量(类型为 i32 或可拓宽): + 返回 max(t1, t2) // 字面量拓宽 +否则: + 错误 "类型不匹配" +比较 e1 == e2、<、> 等: +``` + +```text +t1 = type(e1), t2 = type(e2) +若 t1 和 t2 兼容(相同或允许隐式转换): + 返回 bool +否则: + 错误 +逻辑 &&、||: +``` + +```text +要求 e1 和 e2 均为 bool,返回 bool +位运算 &、|、^、<<、>>: +``` + +```text +同算术规则,但要求操作数为整数类型 +``` + +### 前缀运算 +-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") +词法成本极低:最多两字符即可区分所有实体,无需长关键字。 + +语义完全解耦: + +\# 仅用于控制流跳转目标。 + +! 仅用于附加在函数/变量上的编译指令或属性。 + +表达式内不出现 !:运算全部通过 @ 函数调用完成,无内置逻辑非运算符,避免歧义。 + +## 语法 +```peg +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 为 fnret_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 — 总是内联 简单的包装函数 + diff --git a/stage0/spl_cli.c b/stage0/spl_cli.c index 0d2384b..8b5f9cf 100644 --- a/stage0/spl_cli.c +++ b/stage0/spl_cli.c @@ -4,23 +4,32 @@ * Built-in syscalls are auto-registered via spl_syscall_register(). * * Usage: - * spl_cli [entry_point] + * spl_cli [-d] [entry_point] */ -#include "spl_ir.h" +#include "spl_mcode.h" #include "spl_syscall.h" #include "spl_vm.h" #include +#include int main(int argc, const char **argv) { - if (argc < 2) { - fprintf(stderr, "Usage: spl_cli [entry_point]\n"); + int debug_mode = 0; + int arg_idx = 1; + + if (argc >= 2 && strcmp(argv[1], "-d") == 0) { + debug_mode = 1; + arg_idx = 2; + } + + if (arg_idx >= argc) { + fprintf(stderr, "Usage: spl_cli [-d] [entry_point]\n"); return 1; } - const char *path = argv[1]; - const char *entry = argc >= 3 ? argv[2] : "main"; + const char *path = argv[arg_idx]; + const char *entry = argc >= arg_idx + 2 ? argv[arg_idx + 1] : "main"; spl_prog_t prog; if (spl_prog_load_from_file(path, &prog) != 0) { @@ -32,6 +41,7 @@ int main(int argc, const char **argv) { spl_vm_t vm; spl_vm_init(&vm); + if (debug_mode) spl_vm_set_debug(&vm, 1); if (spl_vm_load_prog(&vm, &prog) != 0) { fprintf(stderr, "vm: prog '%s' not found\n", entry); spl_prog_drop(&prog); diff --git a/stage0/spl_disasm.c b/stage0/spl_disasm.c index 092d9b5..b09d06c 100644 --- a/stage0/spl_disasm.c +++ b/stage0/spl_disasm.c @@ -3,7 +3,7 @@ * Usage: spl_disasm */ -#include "spl_ir.h" +#include "spl_mcode.h" #include int main(int argc, const char **argv) { diff --git a/stage0/spl_ir.c b/stage0/spl_mcode.c similarity index 98% rename from stage0/spl_ir.c rename to stage0/spl_mcode.c index 6671c89..e36651b 100644 --- a/stage0/spl_ir.c +++ b/stage0/spl_mcode.c @@ -1,4 +1,4 @@ -/* spl_ir.c — SIR binary serialization, deserialization, and utilities +/* spl_mcode.c — SPL VM machine code binary serialization, deserialization, and utilities * * Binary format (all metadata fields are spl_val_t = uint64_t LE): * [HEADER] magic(8) nfuncs(8) ninsns(8) nnatives(8) nstrs(8) ndata(8) @@ -9,7 +9,7 @@ * [STRTAB] each: slen(8) str(slen bytes, padded to 8) */ -#include "spl_ir.h" +#include "spl_mcode.h" void spl_prog_init(spl_prog_t *prog) { if (!prog) @@ -445,12 +445,10 @@ const char *opcode_name[] = { const char *spl_opcode_name(spl_opcode_t opcode) { return opcode_name[opcode]; } const char *spl_type_tag_name(spl_type_t type) { switch (type) { - case SPL_VOID: - return "void"; - case SPL_I8: - return "i8"; - case SPL_U8: - return "u8"; + case SPL_VOID: return "void"; + case SPL_BOOL: return "bool"; + case SPL_I8: return "i8"; + case SPL_U8: return "u8"; case SPL_I16: return "i16"; case SPL_U16: diff --git a/stage0/spl_ir.h b/stage0/spl_mcode.h similarity index 98% rename from stage0/spl_ir.h rename to stage0/spl_mcode.h index 844e4f7..3f94dc2 100644 --- a/stage0/spl_ir.h +++ b/stage0/spl_mcode.h @@ -1,8 +1,8 @@ -/* spl_ir.h - SPL Intermediate Representation: instruction set and binary format +/* spl_mcode.h - SPL VM Machine Code: instruction set and binary format */ -#ifndef __SPL_IR_H__ -#define __SPL_IR_H__ +#ifndef __SPL_MCODE_H__ +#define __SPL_MCODE_H__ #include "include/core_map.h" #include "include/core_vec.h" @@ -14,6 +14,7 @@ typedef intptr_t isize; typedef enum { SPL_VOID, + SPL_BOOL, SPL_I8, SPL_U8, SPL_I16, @@ -220,4 +221,4 @@ const char *spl_type_tag_name(spl_type_t type); void spl_ins_dump(spl_ins_t *ins, spl_val_t addr); -#endif /* __SPL_IR_H__ */ +#endif /* __SPL_MCODE_H__ */ diff --git a/stage0/spl_syscall.c b/stage0/spl_syscall.c index 8cf88d7..1da61ff 100644 --- a/stage0/spl_syscall.c +++ b/stage0/spl_syscall.c @@ -10,7 +10,7 @@ #include "spl_syscall.h" #include "include/core_map.h" #include "include/core_vec.h" -#include "spl_ir.h" +#include "spl_mcode.h" #include "spl_vm.h" #include @@ -128,7 +128,7 @@ static spl_val_t vm_read_file(int nargs, spl_val_t *args) { const char *path = (const char *)(uintptr_t)args[0]; if (path == nullptr) { fprintf(stderr, "filepath can't be null"); - return 1; + return 0; } FILE *f = fopen(path, "rb"); if (!f) { diff --git a/stage0/spl_syscall.h b/stage0/spl_syscall.h index 109dcca..f6e023d 100644 --- a/stage0/spl_syscall.h +++ b/stage0/spl_syscall.h @@ -12,7 +12,7 @@ #ifndef __SPL_SYSCALL_H__ #define __SPL_SYSCALL_H__ -#include "spl_ir.h" +#include "spl_mcode.h" /* Register all known built-in syscalls into prog->natives[]. * Entries whose name matches a known syscall get their impl_fn set; diff --git a/stage0/spl_vm.c b/stage0/spl_vm.c index c11d8de..f468b5d 100644 --- a/stage0/spl_vm.c +++ b/stage0/spl_vm.c @@ -5,7 +5,7 @@ */ #include "spl_vm.h" -#include "spl_ir.h" +#include "spl_mcode.h" #include #include @@ -45,6 +45,7 @@ static int spl_type_size(spl_type_t t) { switch (t) { case SPL_VOID: return 0; + case SPL_BOOL: case SPL_I8: case SPL_U8: return 1; @@ -80,6 +81,16 @@ static int spl_type_size(spl_type_t t) { return -1; \ } while (0) +#define CHECK_ADDR(addr, label) do { \ + if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \ + fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", \ + label, vm->ip - 1, (void*)(uintptr_t)(addr), vm->sp, vm->fp); \ + spl_vm_stackdump(vm, vm->sp); \ + spl_vm_backtrace(vm, vm->fp); \ + vm->exit_code = 1; return -1; \ + } \ +} while(0) + /* ================================================================ * Stack push/pop (stacks.data is pre-allocated in init) * ================================================================ */ @@ -547,6 +558,7 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) { vm->prog = NULL; vm->trace = 0; vm->debug = 1; + vm->debug_addr = 0; vm->exit_code = 0; } @@ -584,6 +596,7 @@ void spl_vm_set_debug(spl_vm_t *vm, int enabled) { if (!vm) return; vm->debug = enabled ? 1 : 0; + vm->debug_addr = enabled ? 1 : 0; } #define STACK_CANARY(vm) (vm)->stacks.data[(vm)->fp - 1] @@ -922,6 +935,7 @@ int spl_vm_run_once(spl_vm_t *vm) { /* ========== Indirect Memory (load/store with types) ========== */ case SPL_LOAD: { void *_addr = (void *)POP(); + CHECK_ADDR(_addr, "LOAD"); spl_val_t _v = 0; usize _sz = spl_type_size(ins->type); memcpy(&_v, _addr, _sz); @@ -936,6 +950,7 @@ int spl_vm_run_once(spl_vm_t *vm) { case SPL_STORE: { spl_val_t _v = POP(); void *_addr = (void *)POP(); + CHECK_ADDR(_addr, "STORE"); memcpy(_addr, &_v, spl_type_size(ins->type)); break; } diff --git a/stage0/spl_vm.h b/stage0/spl_vm.h index 2205812..22771c2 100644 --- a/stage0/spl_vm.h +++ b/stage0/spl_vm.h @@ -4,7 +4,7 @@ #define __SPL_VM_H__ #include "include/core_vec.h" -#include "spl_ir.h" +#include "spl_mcode.h" #include #define SPL_STACK_CANARY ((spl_val_t)0xDEADBEEFCAFEBABEull) @@ -30,6 +30,7 @@ typedef struct { int exit_code; int trace; /* non-zero to print each instruction */ int debug; /* non-zero to enable canary checks */ + int debug_addr; /* non-zero to check for low-address memory access */ spl_prog_t *prog; char error_msg[1024]; struct { diff --git a/stage1/spl.md b/stage1/spl.md deleted file mode 100644 index 35ea20d..0000000 --- a/stage1/spl.md +++ /dev/null @@ -1,452 +0,0 @@ -# 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;` 形式用于定义简单类型别名,但目前仅支持聚合类型的别名定义。 - -### 聚合类型拓展与match -``` -type Expr = enum { - Int: i32, - Add: struct { left: *Expr, right: *Expr }, - fn eval(self: *Expr) i32 { - match self { - .Int(val) => ret val, - .Add(left, right) => ret eval(left) + eval(right), - } - ret 0; - } -} - -type Point = struct { - x: i32, - y: i32, - - type Test = enum { - TestEnum0, - TestEnum1 - } - - fn init(x: i32, y: i32) Point { - ret Point { .x = x, .y = y }; - } - - fn dump(self: *Point) void { - vm_printf("Point: %d %d", self.x, self.y); - } -} -``` -这里面包括聚合类型声明除了成员以外任何东西,即整个编译模块也属于聚合类型,将类型抽象化。 -其中这里面还包括调用时支持第一个参数self匹配类型时自动填充。 -暂时默认全部pub即全部暴露没有不暴露的。 - -复合enum支持match等语法解包 -``` -match self { - .Int(val) => ret val, - .Add(left, right) => ret eval(left) + eval(right), -} -``` - ---- - -## 声明 - -### 函数 -``` -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 中尚未实现。)