stage1 重构代码
This commit is contained in:
305
SPL.md
305
SPL.md
@@ -5,11 +5,10 @@
|
||||
SPL是一个从零构建的自举编译器项目。引导链:
|
||||
|
||||
```
|
||||
stage0/spl_vm.c — SIR 虚拟机(C 语言实现)
|
||||
stage1/spc0.c — SPL→SIR 编译器(C 语言实现,引导用)
|
||||
stage1/spc1.spl — SPL→SIR 编译器(SPL 语言实现,自举第一版)
|
||||
stage0/spl_vm.c — SIR 虚拟机 (C 语言实现)
|
||||
stage1/splc0.c — SPL→SIR 编译器 (C 语言实现,引导用)
|
||||
stage1/splc1.spl — SPL→SIR 编译器 (SPL 语言实现,自举第一版)
|
||||
...将来...
|
||||
spc2.spl → spc3.spl → ... → 完全自举
|
||||
```
|
||||
|
||||
# SPL 语法规范
|
||||
@@ -26,14 +25,16 @@ Root <- skip ContainerMembers eof
|
||||
|
||||
ContainerMembers <- ContainerDeclaration*
|
||||
|
||||
ContainerDeclaration
|
||||
<- FnDecl
|
||||
/ TypeDecl
|
||||
/ VarDecl
|
||||
/ ConstDecl
|
||||
/ ComptimeStmt
|
||||
/ DirectiveBlock (* @init { } / #test { } *)
|
||||
ContainerDeclaration <- AttrList? DeclarationBody
|
||||
|
||||
DeclarationBody (* LL(1): FIRST 集合互斥 *)
|
||||
<- FnDecl (* fn ... *)
|
||||
/ TypeDecl (* type X = ... *)
|
||||
/ MemberDecl (* IDENTIFIER [: TypeExpr] *)
|
||||
/ VarDecl (* var x: T *)
|
||||
/ ConstDecl (* const x: T = xxx *)
|
||||
/ ComptimeStmt (* comptime ... *)
|
||||
/ Block (* @init { } / #test { } *)
|
||||
|
||||
# ================================================================
|
||||
# @ / # 属性列表 (对称设计)
|
||||
@@ -53,8 +54,6 @@ DirectiveHead
|
||||
|
||||
DirectiveBlock
|
||||
<- DirectiveHead Block (* @init { } / #test { } *)
|
||||
(* 注意: @assert(x); @dbg(x); 是表达式语句, 走 ExprStatement → BuiltinExpr, 不经过这里 *)
|
||||
|
||||
|
||||
# ================================================================
|
||||
# comptime — 编译期执行 / 断言 (仅容器层)
|
||||
@@ -70,7 +69,7 @@ ComptimeStmt
|
||||
# ================================================================
|
||||
|
||||
FnDecl
|
||||
<- AttrList? KEYWORD_fn IDENTIFIER LPAREN ParamDeclList RPAREN TypeExpr?
|
||||
<- KEYWORD_fn IDENTIFIER LPAREN ParamDeclList RPAREN TypeExpr?
|
||||
(SEMICOLON / Block)
|
||||
|
||||
ParamDeclList <- (ParamDecl COMMA)* (ParamDecl / DOT3 COMMA?)?
|
||||
@@ -85,7 +84,7 @@ ParamDecl
|
||||
# ================================================================
|
||||
|
||||
TypeDecl
|
||||
<- AttrList? KEYWORD_type IDENTIFIER EQUAL TypeBody (* @packed type Vec = struct { ... } *)
|
||||
<- KEYWORD_type IDENTIFIER EQUAL TypeBody (* @packed type Vec = struct { ... } *)
|
||||
|
||||
TypeBody
|
||||
<- KEYWORD_struct LBRACE AggregateBody RBRACE
|
||||
@@ -96,22 +95,14 @@ TypeBody
|
||||
AggregateBody <- AggregateItem*
|
||||
|
||||
AggregateItem
|
||||
<- MethodDecl (* fn ... *)
|
||||
/ TypeDecl (* type X = ... *)
|
||||
/ VarDecl (* var x: T *)
|
||||
/ ComptimeStmt (* comptime ... *)
|
||||
/ MemberDecl (* IDENTIFIER [: TypeExpr] *)
|
||||
<- ContainerDeclaration
|
||||
|
||||
(* 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
|
||||
|
||||
<- IDENTIFIER (COLON TypeExpr)? (COMMA / SEMICOLON)?
|
||||
|
||||
# ================================================================
|
||||
# 变量 / 常量
|
||||
@@ -119,12 +110,12 @@ MethodDecl
|
||||
# ================================================================
|
||||
|
||||
VarDecl
|
||||
<- AttrList? KEYWORD_var IDENTIFIER
|
||||
<- KEYWORD_var IDENTIFIER
|
||||
(COLON TypeExpr / COLON_ASSIGN Expr)?
|
||||
(EQUAL Expr)? SEMICOLON (* @volatile var flag: i32; *)
|
||||
|
||||
ConstDecl
|
||||
<- AttrList? KEYWORD_const IDENTIFIER
|
||||
<- KEYWORD_const IDENTIFIER
|
||||
(COLON TypeExpr / COLON_ASSIGN Expr)?
|
||||
EQUAL Expr SEMICOLON
|
||||
|
||||
@@ -139,6 +130,7 @@ BlockItem <- Statement
|
||||
|
||||
Statement (* LL(1): 14 分支互斥 *)
|
||||
<- IfStatement
|
||||
/ IfVarStatement
|
||||
/ WhileStatement
|
||||
/ LoopStatement
|
||||
/ ForStatement
|
||||
@@ -153,41 +145,42 @@ Statement (* LL(1): 14 分支互
|
||||
|
||||
ExprStatement <- Expr SEMICOLON
|
||||
|
||||
|
||||
# ---- if ----
|
||||
# if else
|
||||
|
||||
IfStatement
|
||||
<- KEYWORD_if Expr BlockOrStmt (KEYWORD_else BlockOrStmt)?
|
||||
<- KEYWORD_if Expr Block ElsePart?
|
||||
|
||||
BlockOrStmt <- Block / Statement
|
||||
IfVarStatement
|
||||
<- KEYWORD_if KEYWORD_var DOT IDENTIFIER (LBRACKET IDENTIFIER RBRACKET)? EQUAL Expr
|
||||
Block ElsePart?
|
||||
|
||||
ElsePart
|
||||
<- KEYWORD_else ( Block (* else { ... } *)
|
||||
/ IfStatement (* else if ... *)
|
||||
/ IfVarStatement (* else if var ... *)
|
||||
)
|
||||
|
||||
# ---- while / loop / for ----
|
||||
|
||||
WhileStatement <- KEYWORD_while Expr BlockOrStmt
|
||||
WhileStatement <- KEYWORD_while Expr Block
|
||||
|
||||
LoopStatement <- KEYWORD_loop BlockOrStmt
|
||||
LoopStatement <- KEYWORD_loop Block
|
||||
|
||||
ForStatement
|
||||
<- KEYWORD_for Expr (COMMA Expr)* KEYWORD_as IDENTIFIER (COMMA IDENTIFIER)* BlockOrStmt
|
||||
<- KEYWORD_for Expr (COMMA Expr)* KEYWORD_as
|
||||
IDENTIFIER (COMMA IDENTIFIER)* Block
|
||||
|
||||
# ---- match ----
|
||||
|
||||
MatchStatement <- KEYWORD_match Expr LBRACE MatchArm* RBRACE
|
||||
|
||||
MatchArm
|
||||
<- MatchPat (COMMA MatchPat)* FAT_ARROW Statement
|
||||
/ UNDERSCORE FAT_ARROW Statement
|
||||
<- MatchPat FAT_R_ARROW Statement
|
||||
/ UNDERSCORE FAT_R_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
|
||||
|
||||
<- DOT IDENTIFIER (LBRACKET IDENTIFIER RBRACKET)? (* 仅允许 .Item 或 .Item[bind] *)
|
||||
/ Expr (* 字面量或变量常量 *)
|
||||
|
||||
# ---- 跳转 ----
|
||||
|
||||
@@ -198,7 +191,7 @@ ContinueStatement <- KEYWORD_continue SEMICOLON
|
||||
|
||||
# ---- defer ----
|
||||
|
||||
DeferStatement <- KEYWORD_defer BlockOrStmt
|
||||
DeferStatement <- KEYWORD_defer (Block / Statement)
|
||||
|
||||
|
||||
# ================================================================
|
||||
@@ -215,16 +208,11 @@ 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
|
||||
|
||||
|
||||
@@ -366,7 +354,8 @@ COMMA <- ',' SEMICOLON <- ';'
|
||||
COLON <- ':' DOT <- '.'
|
||||
DOT2 <- '..' DOT3 <- '...'
|
||||
AT <- '@' SHARP <- '#'
|
||||
FAT_ARROW <- '=>'
|
||||
FAT_R_ARROW <- '=>' FAT_D_ARROW <- '<=>'
|
||||
R_ARROW <- '<-' L_ARROW <- '->' D_ARROW '<->'
|
||||
|
||||
EQUAL <- '='
|
||||
COLON_ASSIGN <- ':='
|
||||
@@ -428,18 +417,18 @@ eof <- !.
|
||||
|
||||
零静默原则: 任何可能出错或危险的构造至少产生一条警告,绝无静默通过。严格模式下所有警告视为错误。
|
||||
|
||||
无未定义行为: 所有行为必须完全定义,否则为编译错误(或宽松模式下的警告)。任何不安全操作均需显式标记。
|
||||
无未定义行为: 所有行为必须完全定义,否则为编译错误 (或宽松模式下的警告)。任何不安全操作均需显式标记。
|
||||
|
||||
内置数据类型: 语言内置区间 a..b 和切片 []T,它们是真实的结构体,拥有明确的内部字段,用于迭代和切片操作。
|
||||
|
||||
## 容器层(文件 = 匿名 struct)
|
||||
## 容器层 (文件 = 匿名 struct)
|
||||
|
||||
文件视为匿名 struct,顶层声明顺序处理。
|
||||
|
||||
声明 静态约束 动态语义
|
||||
FnDecl 名称唯一,签名完整。 仅定义。
|
||||
TypeDecl 同作用域名称唯一。 定义类型别名或聚合体,编译时解析。
|
||||
VarDecl(容器级) var: 必须初始化,类型完整。const: 初始化必须编译期可求值。 const 编译时计算;var 启动初始化一次。
|
||||
VarDecl (容器级) var: 必须初始化,类型完整。const: 初始化必须编译期可求值。 const 编译时计算;var 启动初始化一次。
|
||||
ConstDecl 必须编译期可求值。 编译期常量。
|
||||
ComptimeStmt 内部代码全部在编译时执行。 编译时执行,可生成声明。
|
||||
DirectiveBlock @id { } / #id { } 为扩展占位,无预定义行为。未识别指令触发警告/错误。 同左。
|
||||
@@ -450,7 +439,7 @@ DirectiveBlock @id { } / #id { } 为扩展占位,无预定义行为。未识
|
||||
|
||||
@name(args) / #name(args): 内置调用,出现在表达式位置。
|
||||
|
||||
语义: 完全由语言版本或库注册决定。当前所有均视为未识别,产生警告(宽松) 或错误(严格)。
|
||||
语义: 完全由语言版本或库注册决定。当前所有均视为未识别,产生警告 (宽松) 或错误 (严格)。
|
||||
|
||||
## 函数
|
||||
text
|
||||
@@ -459,9 +448,9 @@ AttrList? fn IDENTIFIER ( ParamDeclList ) TypeExpr? ( ; | Block )
|
||||
|
||||
返回类型: 省略即 void。仅有 ; 表示外部声明。
|
||||
|
||||
调用: 实参与形参数量、类型必须完全匹配(无隐式可变参数)。
|
||||
调用: 实参与形参数量、类型必须完全匹配 (无隐式可变参数)。
|
||||
|
||||
执行: 新作用域 → 形参绑定实参 → 执行 Block → 遇 ret expr 返回(类型匹配),或 void 函数自然结束返回。
|
||||
执行: 新作用域 → 形参绑定实参 → 执行 Block → 遇 ret expr 返回 (类型匹配),或 void 函数自然结束返回。
|
||||
|
||||
## 类型声明与聚合体
|
||||
### 别名
|
||||
@@ -475,19 +464,19 @@ type T = TypeExpr — 完全同义。
|
||||
访问: expr.field。若 expr 是 struct 值,直接取字段;若为指针,自动解引用一层再取字段。多级指针必须连续 .*。
|
||||
|
||||
### union
|
||||
字段共享内存,直接读取视为不安全。当前版本只允许通过 match 解构读取,且必须穷举所有可能变体(或通配 _)。
|
||||
字段共享内存,直接读取视为不安全。当前版本只允许通过 match 解构读取,且必须穷举所有可能变体 (或通配 _)。
|
||||
|
||||
### enum
|
||||
变体: variant 或 variant : Type。
|
||||
|
||||
构造: EnumName.variant 或带 (payload)。
|
||||
|
||||
匹配: match 必须穷举(或含 _),否则编译错误。
|
||||
匹配: match 必须穷举 (或含 _),否则编译错误。
|
||||
|
||||
## 变量与常量
|
||||
var x: T 或 var x := init: 可变量。
|
||||
|
||||
局部变量未初始化: 必须显式标注类型 var x: T;(无 =)。宽松模式警告,严格模式错误。绝不静默。
|
||||
局部变量未初始化: 必须显式标注类型 var x: T; (无 =)。宽松模式警告,严格模式错误。绝不静默。
|
||||
|
||||
const x: T = expr 或 const x := expr: 不可变量,必须初始化,一次绑定。
|
||||
|
||||
@@ -496,7 +485,7 @@ const x: T = expr 或 const x := expr: 不可变量,必须初始化,一次
|
||||
## 块与语句
|
||||
块 { ... } 引入作用域,可为表达式: 尾表达式无分号则块值即其值,否则 void。
|
||||
|
||||
### 控制流(强制大括号体)
|
||||
### 控制流 (强制大括号体)
|
||||
if
|
||||
if 条件 { ... } [else { ... }]
|
||||
|
||||
@@ -511,13 +500,13 @@ loop
|
||||
loop { ... }: 无限循环,break 退出。
|
||||
|
||||
for
|
||||
语法(PEG 已定义):
|
||||
语法 (PEG 已定义):
|
||||
|
||||
ForRange 是逗号分隔的表达式列表。每个表达式在启动阶段只能是内置可迭代对象:
|
||||
|
||||
区间 a..b 或 a..(无右端点): 内置类型 Range,内部字段 begin 和 end(end 可为 none 表示无界)。迭代产生从 begin 开始递增的整数,直到 end(不含)。若为 a..,则产生无界序列。
|
||||
区间 a..b 或 a.. (无右端点): 内置类型 Range,内部字段 begin 和 end (end 可为 none 表示无界)。迭代产生从 begin 开始递增的整数,直到 end (不含)。若为 a..,则产生无界序列。
|
||||
|
||||
切片/数组 expr(类型为 []T 或 [N]T): 内置切片类型,内部结构为 ptr: *T, len: usize。迭代依次产生每个元素,类型为 T。
|
||||
切片/数组 expr (类型为 []T 或 [N]T): 内置切片类型,内部结构为 ptr: *T, len: usize。迭代依次产生每个元素,类型为 T。
|
||||
|
||||
语义:
|
||||
|
||||
@@ -527,7 +516,7 @@ as 后的变量列表长度必须等于 n,否则编译错误。
|
||||
|
||||
每个 Ei 必须是上述内置可迭代对象,否则编译错误。
|
||||
|
||||
并行迭代: 每次迭代从每个 Ei 中各自取出一个值,按顺序绑定到对应变量(只读,作用域在循环体内)。
|
||||
并行迭代: 每次迭代从每个 Ei 中各自取出一个值,按顺序绑定到对应变量 (只读,作用域在循环体内)。
|
||||
|
||||
循环继续直到任意一个序列耗尽。若序列长度不同,最短的耗尽时循环停止,忽略其余序列剩余元素。
|
||||
|
||||
@@ -535,13 +524,13 @@ as 后的变量列表长度必须等于 n,否则编译错误。
|
||||
|
||||
长度协调:
|
||||
|
||||
区间 a..b 具有确定长度 b - a(若 b >= a,否则为 0)。
|
||||
区间 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 替代,但语言仍接受)。
|
||||
若循环中只有无界序列而没有有限序列,则循环无限进行 (此时需 loop 替代,但语言仍接受)。
|
||||
|
||||
示例:
|
||||
// 单区间
|
||||
@@ -550,7 +539,7 @@ for 0..5 as i { ... } // i: 0,1,2,3,4
|
||||
// 单切片
|
||||
for my_slice as elem { ... }
|
||||
|
||||
// 切片 + 索引(用户显式提供从0开始的区间)
|
||||
// 切片 + 索引 (用户显式提供从0开始的区间)
|
||||
for my_slice, 0.. as elem, idx { ... } // idx 与 elem 一一对应
|
||||
|
||||
// 两个等长切片
|
||||
@@ -562,7 +551,7 @@ for my_slice, 0.. as elem // 双序列却只有一个变量
|
||||
match
|
||||
用于枚举或 union。
|
||||
|
||||
臂: .variant [bind] => { ... },bind 仅支持 [id](绑定整个载荷)。
|
||||
臂: .variant [bind] => { ... },bind 仅支持 [id] (绑定整个载荷)。
|
||||
|
||||
必须穷举或含 _,否则编译错误。
|
||||
|
||||
@@ -623,7 +612,7 @@ comptime { ... } / comptime expr;: 编译时求值,不可引用运行时变量
|
||||
## 类型表达式
|
||||
基础类型: void, bool, 整数/浮点类型。
|
||||
|
||||
指针: *T, *_(任意指针),[N]T(数组),[]T(切片)。
|
||||
指针: *T, *_ (任意指针),[N]T (数组),[]T (切片)。
|
||||
|
||||
函数类型: fn(T1, T2) RetType。
|
||||
|
||||
@@ -647,13 +636,13 @@ null → ?T。
|
||||
区间类型 Range
|
||||
语法 a..b 或 a..。
|
||||
|
||||
内部结构: { begin: i64, end: ?i64 }(具体整数类型可能由上下文决定,默认为 i64)。
|
||||
内部结构: { 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),如果配对序列也是无界则无限循环。
|
||||
用作迭代器时: 产生从 begin 到 end-1 的整数 (若 end 为 null 则无穷)。在 for 中与其他序列配合时,无界的区间自动取其配对序列的长度作为上界 (等于 0..other.len),如果配对序列也是无界则无限循环。
|
||||
|
||||
切片类型 []T
|
||||
内部结构: { ptr: *T, len: usize }。
|
||||
@@ -681,17 +670,17 @@ a..: begin = a, end = null。
|
||||
|
||||
所有内置类型的内部表示与分类
|
||||
|
||||
类型兼容性与隐式转换规则(附警告/错误表格)
|
||||
类型兼容性与隐式转换规则 (附警告/错误表格)
|
||||
|
||||
表达式与语句的类型推导/检查规则(伪代码)
|
||||
表达式与语句的类型推导/检查规则 (伪代码)
|
||||
|
||||
特殊类型的处理(指针、区间、切片等)
|
||||
特殊类型的处理 (指针、区间、切片等)
|
||||
|
||||
原则: 所有可能不安全或信息丢失的隐式转换均产生警告;不允许静默转换。最终严格模式下警告将变为错误。
|
||||
|
||||
## 内置基础类型
|
||||
### 整数类型
|
||||
类型 大小(位) 表示 对齐
|
||||
类型 大小 (位) 表示 对齐
|
||||
i8, u8 8 二进制补码 / 无符号 1 字节
|
||||
i16, u16 16 同上 2 字节
|
||||
i32, u32 32 同上 4 字节
|
||||
@@ -721,7 +710,7 @@ void: 大小为 0,表示无值,仅用于函数返回或指针。
|
||||
### 数组类型
|
||||
[N]T: 固定长度数组,长度为编译期常量 N,元素类型 T。连续内存布局,大小 = N * sizeof(T)。
|
||||
|
||||
数组可隐式转换为切片(见隐式转换)。
|
||||
数组可隐式转换为切片 (见隐式转换)。
|
||||
|
||||
### 切片类型
|
||||
[]T: 切片,内部结构 { ptr: *T, len: usize }。值类型同聚合类型行为。
|
||||
@@ -735,7 +724,7 @@ a..b 或 a..: 类型为 Range,内部结构 { begin: isize, end: ?isize }。
|
||||
|
||||
a..b: end 为 b。
|
||||
|
||||
a..: end 为 null(无界)。
|
||||
a..: end 为 null (无界)。
|
||||
|
||||
Range 是值类型同聚合类型行为。
|
||||
|
||||
@@ -744,23 +733,23 @@ Range 是值类型同聚合类型行为。
|
||||
### 函数类型
|
||||
fn(参数类型列表) 返回类型
|
||||
|
||||
函数值本身的大小和表示未指定(闭包待定),但函数名作为标识符使用时具有指针语义(类似函数指针)。
|
||||
函数值本身的大小和表示未指定 (闭包待定),但函数名作为标识符使用时具有指针语义 (类似函数指针)。
|
||||
|
||||
### 可选类型(暂时不需要实现)
|
||||
?T: 可为 null 的类型。内部表示同 T 但附加一个判别(可能通过 null 指针表示,视 T 而定)。?T 的大小和对齐与 T 相同或扩展为可容纳 null 的形式(具体实现定义)。
|
||||
?T: 可为 null 的类型。内部表示同 T 但附加一个判别 (可能通过 null 指针表示,视 T 而定)。?T 的大小和对齐与 T 相同或扩展为可容纳 null 的形式 (具体实现定义)。
|
||||
|
||||
null 字面量只能出现在需要 ?T 的上下文中。
|
||||
|
||||
### 自定义聚合类型
|
||||
struct: 字段连续排列(可能有对齐填充),每个字段有自己的类型。赋值是逐字段拷贝。
|
||||
struct: 字段连续排列 (可能有对齐填充),每个字段有自己的类型。赋值是逐字段拷贝。
|
||||
|
||||
union: 所有字段共享起始地址,大小等于最大字段(加上对齐)。直接字段读取被视为不安全,需通过 match 解构。
|
||||
union: 所有字段共享起始地址,大小等于最大字段 (加上对齐)。直接字段读取被视为不安全,需通过 match 解构。
|
||||
|
||||
enum: 带标签的联合体,每个变体可有载荷。大小实现定义,但需容纳判别式及最大载荷。
|
||||
|
||||
## 类型分类
|
||||
### 值类型(复制语义)
|
||||
所有基本标量类型,或者说底层寄存器类型(整数、浮点、bool)
|
||||
### 值类型 (复制语义)
|
||||
所有基本标量类型,或者说底层寄存器类型 (整数、浮点、bool)
|
||||
|
||||
struct
|
||||
|
||||
@@ -775,7 +764,7 @@ struct
|
||||
### 引用/指针类型
|
||||
\*T、\*_
|
||||
|
||||
函数指针(内部类似 \*const fn(...))
|
||||
函数指针 (内部类似 \*const fn(...))
|
||||
|
||||
### 特殊类型
|
||||
void: 无法实例化,仅用于返回或指针目标。
|
||||
@@ -786,17 +775,17 @@ null: 不是独立类型,仅用于初始化或赋值给 ?T。
|
||||
下表中,“允许”表示可自动转换,否则需要显式 as 转换。警告列表明编译器必须输出诊断信息,不可静默。
|
||||
|
||||
源类型 目标类型 允许? 警告? 备注
|
||||
T(任意) T 是 无 相同类型
|
||||
T (任意) T 是 无 相同类型
|
||||
*T *_ 是 警告: “丢失类型信息”
|
||||
*_ *T 是 警告: “不安全的指针重解释”
|
||||
[N]T []T 是 无 数组到切片强制转换
|
||||
整数字面量 整数类型 U 是(若值在 U 范围内) 无 字面量自动拓宽
|
||||
整数字面量 整数类型 U 是 (若值在 U 范围内) 无 字面量自动拓宽
|
||||
i32 i64 否 — 需显式 as i64,防止意外
|
||||
i64 i32 否 — 窄化必须显式
|
||||
null ?T 是 无 空值初始化
|
||||
?T T 否 — 需显式解包(如 orelse,但语言暂未定义,将来扩展)
|
||||
?T T 否 — 需显式解包 (如 orelse,但语言暂未定义,将来扩展)
|
||||
T ?T 是 无 提升为可选
|
||||
浮点字面量 f32 是(值可表示则) 无
|
||||
浮点字面量 f32 是 (值可表示则) 无
|
||||
f64 f32 否 — 窄化需显式
|
||||
bool 整数 否 —
|
||||
整数 bool 否 —
|
||||
@@ -805,7 +794,7 @@ bool 整数 否 —
|
||||
|
||||
隐式转换不会嵌套传递。例如 *T 到 *_ 是警告转换,但不因此进一步允许 *_ 到 **T 的隐式转换。
|
||||
|
||||
字面量拓宽仅适用于整数字面量直接出现在需要更宽整数类型的上下文(如赋值给 i64 变量,或作为 Range 的边界,Range 内部为 isize,所以 0..5 中的 0 和 5 会拓宽为 isize)。
|
||||
字面量拓宽仅适用于整数字面量直接出现在需要更宽整数类型的上下文 (如赋值给 i64 变量,或作为 Range 的边界,Range 内部为 isize,所以 0..5 中的 0 和 5 会拓宽为 isize)。
|
||||
|
||||
所有其他未列出的类型转换均需显式 as。
|
||||
|
||||
@@ -821,7 +810,7 @@ true / false → bool
|
||||
|
||||
null → 必须从上下文推导出 ?T,无法推导则报错。
|
||||
|
||||
字符串字面量 → []u8(具体待定)
|
||||
字符串字面量 → []u8 (具体待定)
|
||||
|
||||
### 二元运算
|
||||
算术 e1 + e2、-、*、/、%:
|
||||
@@ -830,7 +819,7 @@ null → 必须从上下文推导出 ?T,无法推导则报错。
|
||||
t1 = type(e1), t2 = type(e2)
|
||||
若 t1 == t2 且 t1 ∈ 数值类型:
|
||||
返回 t1
|
||||
否则若 t1 和 t2 为整数且其中一个是字面量(类型为 i32 或可拓宽):
|
||||
否则若 t1 和 t2 为整数且其中一个是字面量 (类型为 i32 或可拓宽):
|
||||
返回 max(t1, t2) // 字面量拓宽
|
||||
否则:
|
||||
错误 "类型不匹配"
|
||||
@@ -839,7 +828,7 @@ t1 = type(e1), t2 = type(e2)
|
||||
|
||||
```text
|
||||
t1 = type(e1), t2 = type(e2)
|
||||
若 t1 和 t2 兼容(相同或允许隐式转换):
|
||||
若 t1 和 t2 兼容 (相同或允许隐式转换):
|
||||
返回 bool
|
||||
否则:
|
||||
错误
|
||||
@@ -862,8 +851,8 @@ t1 = type(e1), t2 = type(e2)
|
||||
|
||||
~e: e 必须为整数,结果同类型。
|
||||
|
||||
&e: e 必须为可寻址左值(变量、字段、*解引用等)。若 e 类型为 T,结果为 *T。
|
||||
注意: 没有 * 前缀运算符(解引用仅后缀 .*)。
|
||||
&e: e 必须为可寻址左值 (变量、字段、*解引用等)。若 e 类型为 T,结果为 *T。
|
||||
注意: 没有 * 前缀运算符 (解引用仅后缀 .*)。
|
||||
|
||||
### 后缀运算
|
||||
e.\*: 要求 e 类型为 \*T,结果为 T,且作为左值。
|
||||
@@ -878,19 +867,19 @@ e.field:
|
||||
|
||||
e[i]:
|
||||
|
||||
e 类型为 [N]T 或 []T 或 *T(视为指向单个元素或数组起始),i 为整数。结果为 T 左值。
|
||||
e 类型为 [N]T 或 []T 或 *T (视为指向单个元素或数组起始),i 为整数。结果为 T 左值。
|
||||
|
||||
e[a..b]:
|
||||
|
||||
e 类型为 [N]T 或 []T,a 和 b 为整数(可省略 b)。结果为 []T。
|
||||
e 类型为 [N]T 或 []T,a 和 b 为整数 (可省略 b)。结果为 []T。
|
||||
|
||||
e(args):
|
||||
|
||||
e 类型必须为函数类型 fn(T1, T2, ...) Ret。实参类型须与形参兼容(允许隐式转换)。结果为 Ret。
|
||||
e 类型必须为函数类型 fn(T1, T2, ...) Ret。实参类型须与形参兼容 (允许隐式转换)。结果为 Ret。
|
||||
|
||||
e as Type:
|
||||
|
||||
要求源类型与目标类型间存在合法显式转换(包括整数窄化、指针转换等)。结果为 Type。
|
||||
要求源类型与目标类型间存在合法显式转换 (包括整数窄化、指针转换等)。结果为 Type。
|
||||
|
||||
### 主要表达式
|
||||
标识符: 查找作用域,返回其声明类型。
|
||||
@@ -905,7 +894,7 @@ Type 必须是 struct 类型。检查字段数量是否齐全,每个 ei 类型
|
||||
|
||||
comptime e: e 必须在编译时可求值,类型同 e。
|
||||
|
||||
内置调用 @id(args) / #id(args): 未定义则警告/错误(按模式),否则行为由对应内置定义决定。
|
||||
内置调用 @id(args) / #id(args): 未定义则警告/错误 (按模式),否则行为由对应内置定义决定。
|
||||
|
||||
## 语句类型规则
|
||||
### 变量声明
|
||||
@@ -913,7 +902,7 @@ var x: T = e;: e 的类型必须兼容 T。x 获得类型 T,可变。
|
||||
|
||||
var x := e;: 推导类型为 e 的类型,x 可变。
|
||||
|
||||
var x: T;: 无初始化,x 具有类型 T。警告(宽松模式)或错误(严格模式)。不可用于 const。
|
||||
var x: T;: 无初始化,x 具有类型 T。警告 (宽松模式)或错误 (严格模式)。不可用于 const。
|
||||
|
||||
const x: T = e;: 同上兼容性,x 不可变。
|
||||
|
||||
@@ -923,15 +912,15 @@ const x := e;: 推导类型,不可变。
|
||||
x = e;: x 必须是可变变量,e 的类型兼容 x 的类型。
|
||||
|
||||
### 控制流
|
||||
if cond { ... } else { ... }: cond 必须为 bool。若 if 用作表达式,两分支块的类型必须相同(或均为 void)。
|
||||
if cond { ... } else { ... }: cond 必须为 bool。若 if 用作表达式,两分支块的类型必须相同 (或均为 void)。
|
||||
|
||||
while cond { ... }: cond 必须为 bool。
|
||||
|
||||
loop { ... }: 无类型限制。
|
||||
|
||||
for range_list as var_list { ... }(见下节)。
|
||||
for range_list as var_list { ... } (见下节)。
|
||||
|
||||
match e { arms }: e 的类型为枚举或 union。每个模式臂的类型(若 match 用作表达式)必须一致。必须穷举。
|
||||
match e { arms }: e 的类型为枚举或 union。每个模式臂的类型 (若 match 用作表达式)必须一致。必须穷举。
|
||||
|
||||
### for 循环详细类型规则
|
||||
语法: for E1, E2, ... as v1, v2, ... { ... }
|
||||
@@ -940,25 +929,25 @@ ForRange 提供表达式列表 [E1, E2, ...]。as 后标识符列表 [v1, v2, ..
|
||||
|
||||
如果动态类型动态长度,则警告具体行为待定,整个语法待定,下面将是暂时语法。
|
||||
```
|
||||
每个 Ei 的类型必须为内置可迭代类型(当前启动阶段仅包括 Range 和 []T / [N]T)。若 Ei 的类型不是这两者之一,编译错误。
|
||||
每个 Ei 的类型必须为内置可迭代类型 (当前启动阶段仅包括 Range 和 []T / [N]T)。若 Ei 的类型不是这两者之一,编译错误。
|
||||
|
||||
对于 Ei:
|
||||
|
||||
若为 Range: 每次迭代产出的值类型为 isize(Range 的边界类型,这里默认为 isize)。
|
||||
若为 Range: 每次迭代产出的值类型为 isize (Range 的边界类型,这里默认为 isize)。
|
||||
|
||||
若为 []T 或 [N]T: 产出元素类型为 T。
|
||||
|
||||
相应地,vi 被推导为只读变量,其类型为对应 Ei 产出的值类型。
|
||||
|
||||
所有序列的长度必须可静态协调(见下文),否则编译错误(或警告,宽松模式下允许无界)。
|
||||
所有序列的长度必须可静态协调 (见下文),否则编译错误 (或警告,宽松模式下允许无界)。
|
||||
|
||||
长度协调规则:
|
||||
|
||||
若所有序列均为有界(Range 有 end 不为 null,或切片长度已知),则循环次数为最短长度。
|
||||
若所有序列均为有界 (Range 有 end 不为 null,或切片长度已知),则循环次数为最短长度。
|
||||
|
||||
若存在无界 Range(如 0..),则要求循环中至少有一个有界序列,且该有界序列的长度将作为无界序列的上限。例如 for slice, 0.. as elem, idx,0.. 的长度由 slice.len 决定。
|
||||
若存在无界 Range (如 0..),则要求循环中至少有一个有界序列,且该有界序列的长度将作为无界序列的上限。例如 for slice, 0.. as elem, idx,0.. 的长度由 slice.len 决定。
|
||||
|
||||
若只有无界序列,则为无限循环(合法,但通常应使用 loop)。
|
||||
若只有无界序列,则为无限循环 (合法,但通常应使用 loop)。
|
||||
```
|
||||
示例:
|
||||
|
||||
@@ -968,7 +957,7 @@ for my_slice, 0.. as elem, idx { ... }
|
||||
// 0..: Range 无界 → idx: i64,长度由 my_slice 决定
|
||||
```
|
||||
## 类型系统限制
|
||||
无隐式类型提升(除字面量整数拓宽和数组到切片外)。
|
||||
无隐式类型提升 (除字面量整数拓宽和数组到切片外)。
|
||||
|
||||
无默认初始化: var x: T; 不初始化,警告/错误。
|
||||
|
||||
@@ -992,9 +981,9 @@ ABI 剥离:跨函数调用和返回统一由 @abi.call / @abi.ret 封装,内
|
||||
## 词法前缀与语义
|
||||
前缀 语义 示例
|
||||
@ 全局函数名、类型名、内置函数调用 @main, @Vec2, @arith.add
|
||||
% 局部变量(虚拟寄存器) %sum, %ptr
|
||||
% 局部变量 (虚拟寄存器) %sum, %ptr
|
||||
\# 基本块标签 \#entry, \#loop_body
|
||||
! 编译/链接标记(元数据) !export("C"), !section(".text")
|
||||
! 编译/链接标记 (元数据) !export("C"), !section(".text")
|
||||
词法成本极低:最多两字符即可区分所有实体,无需长关键字。
|
||||
|
||||
语义完全解耦:
|
||||
@@ -1029,8 +1018,6 @@ Stmt ← VarDef ';'
|
||||
|
||||
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
|
||||
@@ -1058,13 +1045,13 @@ CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefine
|
||||
## 内置函数库
|
||||
|
||||
## 验证规则约束
|
||||
单一定值:每个 %name 在其函数内只被赋值一次(%x = … 或作为参数),且使用前必须支配所有使用点。
|
||||
单一定值:每个 %name 在其函数内只被赋值一次 (%x = … 或作为参数),且使用前必须支配所有使用点。
|
||||
|
||||
类型匹配:@arith.add(i32)(%a, %b) 要求 %a 和 %b 的类型都是 i32 绝对的类型匹配。
|
||||
|
||||
块终止:每个基本块以 br 或 ret 结束,目标 #label 必须在当前函数内存在。
|
||||
|
||||
函数存在性:所有调用的 @ 函数(如 @arith.add)必须来自库。
|
||||
函数存在性:所有调用的 @ 函数 (如 @arith.add)必须来自库。
|
||||
|
||||
## 完整内置函数库 (@ 调用)
|
||||
### 算术运算
|
||||
@@ -1074,8 +1061,8 @@ CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefine
|
||||
@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.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 绝对值
|
||||
### 位运算
|
||||
@@ -1085,8 +1072,8 @@ CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefine
|
||||
@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.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。
|
||||
@@ -1094,7 +1081,7 @@ CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefine
|
||||
函数签名 说明
|
||||
@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.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 大于等于
|
||||
@@ -1105,60 +1092,71 @@ CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefine
|
||||
@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)
|
||||
@cast.bitcast(SRC_T, DST_T)(%a:SRC_T) -> DST_T 位模式重解释 (类型大小必须相等)
|
||||
@cast.ptr2int(P_T, INT_T)(%ptr:P_T) -> INT_T 指针到整数
|
||||
@cast.int2ptr(INT_T, P_T)(%val:INT_T) -> P_T 整数到指针
|
||||
@case.int2float(INT_T, F_T)(%val:INT_T) -> F_T 整数到浮点数
|
||||
@case.float2int(F_T, INT_T)(%val:F_T) -> INT_T 浮点数到整数
|
||||
@cast.bool2int(INT_T)(%cond:bool) -> INT_T 布尔转整数 (true->1, false->0)
|
||||
### 内存操作
|
||||
函数签名 说明
|
||||
@mem.alloca(T)(%count:i32) -> ptr<T> 栈上分配 %count * sizeof(T) 字节,返回对齐的指针
|
||||
@mem.alloca(T)(%count:usize) -> ptr<T> 栈上分配 %count * sizeof(T) 字节,返回对齐的指针
|
||||
@mem.load(T)(%ptr:ptr<T>) -> T 从内存加载类型为 T 的值
|
||||
@mem.store(T)(%ptr:ptr<T>, %val:T) 存储值到内存
|
||||
@mem.offset(T)(%ptr:ptr<T>, %offset:i32) -> ptr<T> 指针算术,以 sizeof(T) 为单位偏移
|
||||
@mem.copy(%dst:ptr<void>, %src:ptr<void>, %size:i32) 内存块复制
|
||||
@mem.set(%dst:ptr<void>, %val:u8, %size:i32) 内存块填充
|
||||
@mem.fence(%ordering:u32) 内存屏障(见原子操作节)
|
||||
@mem.offset(T)(%ptr:ptr<T>, %offset:isize) -> ptr<T> 指针算术,以 sizeof(T) 为单位偏移
|
||||
@mem.copy(%dst:ptr<void>, %src:ptr<void>, %size:usize) 内存块复制
|
||||
@mem.set(%dst:ptr<void>, %val:u8, %size:usize) 内存块填充
|
||||
@mem.fence(%ordering:isize) 内存屏障 (见原子操作节)
|
||||
### 类型信息查询
|
||||
所有查询在编译期求值,返回整数。
|
||||
|
||||
函数签名 说明
|
||||
@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 返回聚合类型的字段数量
|
||||
@type.const(T)(LITERAL) -> T LITERAL是源语言层面的字面量表示
|
||||
@type.bitsizeof(T)() -> usize 返回类型 T 的位大小
|
||||
@type.sizeof(T)() -> usize 返回类型 T 的字节大小
|
||||
@type.alignof(T)() -> usize 返回类型 T 的对齐要求
|
||||
@type.offsetof(T)(%field_index:usize) -> usize 聚合类型 T 中第 field_index 个字段的字节偏移 (字段从0编号)
|
||||
@type.field_count(T)() -> usize 返回聚合类型的字段数量
|
||||
### 聚合类型操作
|
||||
函数签名 说明
|
||||
@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 调用接口
|
||||
用于跨函数边界,封装调用约定。
|
||||
@agg.construct(T)(%f1: T1, %f2: T2, ...) -> T 从各个字段值构造一个结构体/联合体值。T 为具体的聚合类型。
|
||||
- 参数个数必须等于类型 T 的字段总数。
|
||||
- 每个参数的类型必须与对应字段的类型精确匹配 (无隐式转换)。
|
||||
- 适用于 struct 和 union。对于 union,参数只能有一个 (因为只有一个活跃字段),该参数类型必须与某个字段类型兼容 (见联合体处理)。
|
||||
|
||||
函数签名 说明
|
||||
@abi.call(FT)(%fn:FT, %args...) -> %ret 按目标平台调用约定调用函数指针 %fn,FT 为 fn<param_types...->ret_type>
|
||||
@abi.ret(T)(%val?) 按目标平台调用约定从当前函数返回,T 为返回值类型,void 时不带参数
|
||||
内部函数若未使用 !export 标记,可自由使用直接 ret 指令而不经过 @abi.ret,此时编译器可完全自定义内部调用协议。
|
||||
@agg.extract(T, FIELD_INDEX)(%val:T) -> FIELD_T 从聚合值中提取指定索引的字段。FIELD_INDEX 为编译期常量。
|
||||
- FIELD_INDEX 必须为非负整数常量,小于类型 T 的字段数。
|
||||
- 对于 struct:返回对应字段的值,类型为字段声明类型。
|
||||
- 对于 union:由于所有字段共享存储,允许用任意有效字段索引提取,返回值类型为对应字段声明类型。这本身已包含“按不同类型解读同一块存储”的语义,无需额外的 bitcast (但也可结合 @cast.bitcast 使用)。
|
||||
|
||||
@agg.insert(T, FIELD_INDEX)(%agg:T, %field:FIELD_T) -> T 替换聚合值中指定字段,返回新值 (不可变更新)。T 必须为 struct。
|
||||
- 仅适用于 struct 类型。union 是不可变更新无意义 (因为只有一个活跃字段,应使用 construct 重新创建)。
|
||||
- FIELD_INDEX 为合法字段索引,%field 类型必须与该字段类型一致。
|
||||
- 返回的 T 值其他字段保持不变。
|
||||
|
||||
### 原子操作(可选,现在不实现)
|
||||
原子操作需要一个 ordering 参数,类型为 u32,使用常量表示内存顺序(可参照 C11 内存模型定义,例如 0=relaxed, 1=acquire, 2=release, 3=acq_rel, 4=seq_cst)。
|
||||
原子操作需要一个 ordering 参数,类型为 u32,使用常量表示内存顺序 (可参照 C11 内存模型定义,例如 0=relaxed, 1=acquire, 2=release, 3=acq_rel, 4=seq_cst)
|
||||
|
||||
函数签名 说明
|
||||
@atomic.load(T)(%ptr:ptr<T>, %ordering:u32) -> T 原子加载
|
||||
@atomic.store(T)(%ptr:ptr<T>, %val:T, %ordering:u32) 原子存储
|
||||
@atomic.rmw_add(T)(%ptr:ptr<T>, %val:T, %ordering:u32) -> T 原子交换加(返回旧值)
|
||||
@atomic.rmw_add(T)(%ptr:ptr<T>, %val:T, %ordering:u32) -> T 原子交换加 (返回旧值)
|
||||
@atomic.rmw_sub(T)(%ptr:ptr<T>, %val:T, %ordering:u32) -> T 原子交换减
|
||||
@atomic.rmw_and(T), or, xor, xchg 等类似
|
||||
@atomic.cmpxchg(T)(%ptr:ptr<T>, %expected:T, %desired:T, %ordering_success:u32, %ordering_failure:u32) -> {old:T, ok:bool} 原子比较交换,返回旧值和成功标志(通过 struct 返回)
|
||||
### 控制流扩展(间接跳转、选择)
|
||||
@atomic.cmpxchg(T)(%ptr:ptr<T>, %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 触发运行时陷阱
|
||||
@control.select(T)(%cond:bool, %true_val:T, %false_val:T) -> T 选择操作 (无分支,类似三元运算符)
|
||||
@control.br(bool, label, label) -> ! 条件分支:根据第一个参数跳转到第二或第三个(标签或者地址)。终止函数 (调用后控制流不返回)。
|
||||
@control.jmp(label) -> ! 无条件跳转(跳转到标签或者地址)。终止函数。
|
||||
@control.call(FT)(%fn:FT, %args...) -> %ret 按目标平台调用约定调用函数指针 %fn,FT 为 fn<param_types...->ret_type>
|
||||
@control.ret(T)(%val?) 按目标平台调用约定从当前函数返回,T 为返回值类型,void 时不带参数
|
||||
@control.unreachable() -> ! 标记不可达代码
|
||||
@control.trap() -> ! 触发运行时陷阱
|
||||
### 调试与内省
|
||||
函数签名 说明
|
||||
@dbg.breakpoint() 插入调试断点
|
||||
@dbg.declare(%var:%) 声明局部变量的调试信息(可被后端忽略)
|
||||
@dbg.declare(%var:%) 声明局部变量的调试信息 (可被后端忽略)
|
||||
|
||||
## 完整标记系统 (! 前缀)
|
||||
标记附加在函数定义前,用逗号分隔。所有标记均不影响 IR 控制流语义,仅向后端传递元数据。
|
||||
@@ -1172,4 +1170,3 @@ CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefine
|
||||
!naked — 无函数序言/尾声 中断向量、系统调用包装
|
||||
!noinline — 禁止内联 调试或特殊性能需求
|
||||
!alwaysinline — 总是内联 简单的包装函数
|
||||
|
||||
|
||||
61
stage0/include/color.h
Normal file
61
stage0/include/color.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @file color.h
|
||||
* @brief ANSI终端颜色控制码定义
|
||||
*
|
||||
* 提供跨平台的终端文本颜色和样式控制支持
|
||||
*/
|
||||
|
||||
#ifndef __SCC_TERMINAL_COLOR_H__
|
||||
#define __SCC_TERMINAL_COLOR_H__
|
||||
|
||||
/* clang-format off */
|
||||
/// @name 前景色控制码
|
||||
/// @{
|
||||
#define ANSI_FG_BLACK "\33[30m" ///< 黑色前景
|
||||
#define ANSI_FG_RED "\33[31m" ///< 红色前景
|
||||
#define ANSI_FG_GREEN "\33[32m" ///< 绿色前景
|
||||
#define ANSI_FG_YELLOW "\33[33m" ///< 黄色前景
|
||||
#define ANSI_FG_BLUE "\33[34m" ///< 蓝色前景
|
||||
#define ANSI_FG_MAGENTA "\33[35m" ///< 品红色前景
|
||||
#define ANSI_FG_CYAN "\33[36m" ///< 青色前景
|
||||
#define ANSI_FG_WHITE "\33[37m" ///< 白色前景
|
||||
/// @}
|
||||
|
||||
/// @name 背景色控制码
|
||||
/// @{
|
||||
#define ANSI_BG_BLACK "\33[40m" ///< 黑色背景
|
||||
#define ANSI_BG_RED "\33[41m" ///< 红色背景
|
||||
#define ANSI_BG_GREEN "\33[42m" ///< 绿色背景
|
||||
#define ANSI_BG_YELLOW "\33[43m" ///< 黄色背景
|
||||
#define ANSI_BG_BLUE "\33[44m" ///< 蓝色背景
|
||||
#define ANSI_BG_MAGENTA "\33[45m" ///< 品红色背景(注:原始代码此处应为45m)
|
||||
#define ANSI_BG_CYAN "\33[46m" ///< 青色背景
|
||||
#define ANSI_BG_WHITE "\33[47m" ///< 白色背景
|
||||
/// @}
|
||||
|
||||
/// @name 文字样式控制码
|
||||
/// @{
|
||||
#define ANSI_UNDERLINED "\33[4m" ///< 下划线样式
|
||||
#define ANSI_BOLD "\33[1m" ///< 粗体样式
|
||||
#define ANSI_NONE "\33[0m" ///< 重置所有样式
|
||||
/// @}
|
||||
/* clang-format on */
|
||||
|
||||
/**
|
||||
* @def ANSI_FMT
|
||||
* @brief 安全文本格式化宏
|
||||
* @param str 目标字符串
|
||||
* @param fmt ANSI格式序列(可组合多个样式)
|
||||
*
|
||||
* @note 当定义ANSI_FMT_DISABLE时自动禁用颜色输出
|
||||
* @code
|
||||
* printf(ANSI_FMT("Warning!", ANSI_FG_YELLOW ANSI_BOLD));
|
||||
* @endcode
|
||||
*/
|
||||
#ifndef ANSI_FMT_DISABLE
|
||||
#define ANSI_FMT(str, fmt) fmt str ANSI_NONE ///< 启用样式包裹
|
||||
#else
|
||||
#define ANSI_FMT(str, fmt) str ///< 禁用样式输出
|
||||
#endif
|
||||
|
||||
#endif /* __SCC_TERMINAL_COLOR_H__ */
|
||||
@@ -9,7 +9,8 @@
|
||||
#ifndef nullptr
|
||||
#define nullptr NULL
|
||||
#endif
|
||||
typedef size_t usize;
|
||||
typedef uintptr_t usize;
|
||||
typedef intptr_t isize;
|
||||
|
||||
#define MAP_TYPEOF __typeof__
|
||||
|
||||
@@ -83,11 +84,9 @@ static inline usize map_hash_str(const char *s) {
|
||||
#define map_put(map, _key, _val) \
|
||||
do { \
|
||||
/* 扩容 */ \
|
||||
if ((map).cap == 0 || \
|
||||
(map).size * 128 / (map).cap >= MAP_DEFAULT_LOAD_FACTOR) { \
|
||||
if ((map).cap == 0 || (map).size * 128 / (map).cap >= MAP_DEFAULT_LOAD_FACTOR) { \
|
||||
usize new_cap = (map).cap == 0 ? 8 : (map).cap * 2; \
|
||||
MAP_SLOT(MAP_TYPEOF((map).data->key), \
|
||||
MAP_TYPEOF((map).data->val)) *new_data = \
|
||||
MAP_SLOT(MAP_TYPEOF((map).data->key), MAP_TYPEOF((map).data->val)) *new_data = \
|
||||
calloc(new_cap, sizeof(*new_data)); \
|
||||
if (!new_data) \
|
||||
abort(); \
|
||||
@@ -116,8 +115,7 @@ static inline usize map_hash_str(const char *s) {
|
||||
(map).data[_idx].val = _val; \
|
||||
break; \
|
||||
} \
|
||||
if ((map).data[_idx].state == __MAP_SLOT_DELETED && \
|
||||
_first_del == (usize) - 1) \
|
||||
if ((map).data[_idx].state == __MAP_SLOT_DELETED && _first_del == (usize) - 1) \
|
||||
_first_del = _idx; \
|
||||
_idx = (_idx + 1) & _mask; \
|
||||
} \
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define __vec_free free
|
||||
#define __vec_memcpy memcpy
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -214,8 +215,7 @@ typedef size_t usize;
|
||||
* @brief 获取第 idx 个元素的指针(void*)
|
||||
* @return 指向元素的指针,需转换为具体类型使用
|
||||
*/
|
||||
#define vec_sized_at_ptr(vec, elem_size, idx) \
|
||||
((void *)((char *)(vec).data + (idx) * (elem_size)))
|
||||
#define vec_sized_at_ptr(vec, elem_size, idx) ((void *)((char *)(vec).data + (idx) * (elem_size)))
|
||||
|
||||
/**
|
||||
* @def vec_sized_foreach(vec, elem_size, elem_ptr_var, block)
|
||||
|
||||
88
stage0/include/log.c
Normal file
88
stage0/include/log.c
Normal file
@@ -0,0 +1,88 @@
|
||||
#include "log.h"
|
||||
|
||||
static inline int log_snprintf(char *s, size_t n, const char *format, ...) {
|
||||
int ret;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
ret = log_vsnprintf(s, n, format, args);
|
||||
va_end(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int log_default_handler(logger_t *module, log_level_t level, const char *file, int line,
|
||||
const char *func, const char *fmt, ...) {
|
||||
const char *level_str;
|
||||
int offset = 0;
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
/* clang-format off */
|
||||
switch (level) {
|
||||
case LOG_LEVEL_DEBUG: level_str = "DEBUG"; break;
|
||||
case LOG_LEVEL_INFO: level_str = "INFO "; break;
|
||||
case LOG_LEVEL_WARN: level_str = "WARN "; break;
|
||||
case LOG_LEVEL_ERROR: level_str = "ERROR"; break;
|
||||
case LOG_LEVEL_FATAL: level_str = "FATAL"; break;
|
||||
case LOG_LEVEL_TRACE: level_str = "TRACE"; break;
|
||||
default: level_str = "NOTSET"; break;
|
||||
}
|
||||
/// @note: 定义 __LOG_NO_COLOR__ 会取消颜色输出
|
||||
#ifndef __LOG_NO_COLOR__
|
||||
const char *color_code;
|
||||
switch (level) {
|
||||
case LOG_LEVEL_DEBUG: color_code = ANSI_FG_CYAN; break;
|
||||
case LOG_LEVEL_INFO: color_code = ANSI_FG_GREEN; break;
|
||||
case LOG_LEVEL_TRACE: color_code = ANSI_FG_BLUE; break;
|
||||
case LOG_LEVEL_WARN: color_code = ANSI_FG_YELLOW; break;
|
||||
case LOG_LEVEL_ERROR: color_code = ANSI_FG_RED; break;
|
||||
case LOG_LEVEL_FATAL: color_code = ANSI_FG_RED ANSI_UNDERLINED; break;
|
||||
default: color_code = ANSI_NONE;
|
||||
}
|
||||
/* clang-format on */
|
||||
offset = log_snprintf(module->buf, sizeof(module->buf),
|
||||
ANSI_BOLD "%s[%s] %s - %s:%d in %s()" ANSI_NONE " ", color_code,
|
||||
level_str, module->name, file, line, func);
|
||||
#else
|
||||
offset = log_snprintf(module->buf, sizeof(module->buf), "[%s] %s - %s:%d in %s() ", level_str,
|
||||
module->name, file, line, func);
|
||||
#endif
|
||||
/* 然后写入用户消息(如果有) */
|
||||
if (fmt && fmt[0]) {
|
||||
log_vsnprintf(module->buf + offset, sizeof(module->buf) - offset, fmt, args);
|
||||
}
|
||||
va_end(args);
|
||||
log_puts(module->buf);
|
||||
// for clangd warning
|
||||
// clang-analyzer-deadcode.DeadStores
|
||||
(void)color_code;
|
||||
(void)level_str;
|
||||
if (level & LOG_LEVEL_FATAL) {
|
||||
log_abort();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
logger_t __default_logger_root = {
|
||||
.name = "root",
|
||||
.level = LOG_LEVEL_ALL,
|
||||
.handler = log_default_handler,
|
||||
};
|
||||
|
||||
void init_logger(logger_t *logger, const char *name) {
|
||||
logger->name = name;
|
||||
logger->handler = log_default_handler;
|
||||
log_set_level(logger, LOG_LEVEL_ALL);
|
||||
}
|
||||
|
||||
void log_set_level(logger_t *logger, int level) {
|
||||
if (logger)
|
||||
logger->level = level;
|
||||
else
|
||||
__default_logger_root.level = level;
|
||||
}
|
||||
|
||||
void log_set_handler(logger_t *logger, log_handler handler) {
|
||||
if (logger)
|
||||
logger->handler = handler;
|
||||
else
|
||||
__default_logger_root.handler = handler;
|
||||
}
|
||||
193
stage0/include/log.h
Normal file
193
stage0/include/log.h
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* @file log.h
|
||||
* @brief 日志系统核心模块(支持多级日志、断言和异常处理)
|
||||
*/
|
||||
|
||||
#ifndef __SCC_LOG_IMPL_H__
|
||||
#define __SCC_LOG_IMPL_H__
|
||||
|
||||
#include "color.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __SCC_LOG_IMPL_USE_STD_IMPL__
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#define log_vsnprintf vsnprintf
|
||||
#define log_puts puts
|
||||
#define log_abort abort
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__ // GCC, Clang
|
||||
#define __scc_log_unreachable() (__builtin_unreachable())
|
||||
#elif defined _MSC_VER // MSVC
|
||||
#define __scc_log_unreachable() (__assume(false))
|
||||
#elif defined __SCC_BUILTIN_UNREACHEABLE__ // The SCC Compiler (my compiler)
|
||||
#define __scc_log_unreachable() (__scc_builtin_unreachable())
|
||||
#else
|
||||
#define __scc_log_unreachable() ((void)0)
|
||||
#endif
|
||||
|
||||
#ifndef log_vsnprintf
|
||||
#define log_vsnprintf(...)
|
||||
#warning "log_vsnprintf not defined"
|
||||
#endif
|
||||
|
||||
#ifndef log_puts
|
||||
#define log_puts(...)
|
||||
#warning "log_puts not defined"
|
||||
#endif
|
||||
|
||||
#ifndef log_abort
|
||||
#define log_abort(...)
|
||||
#warning "log_abort not defined"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 日志级别枚举
|
||||
*
|
||||
* 定义日志系统的输出级别和组合标志位
|
||||
*/
|
||||
typedef enum log_level {
|
||||
LOG_LEVEL_NOTSET = 0, ///< 未设置级别(继承默认配置)
|
||||
LOG_LEVEL_DEBUG = 1 << 0, ///< 调试信息(开发阶段详细信息)
|
||||
LOG_LEVEL_INFO = 1 << 1, ///< 常规信息(系统运行状态)
|
||||
LOG_LEVEL_WARN = 1 << 2, ///< 警告信息(潜在问题提示)
|
||||
LOG_LEVEL_ERROR = 1 << 3, ///< 错误信息(可恢复的错误)
|
||||
LOG_LEVEL_FATAL = 1 << 4, ///< 致命错误(导致程序终止的严重错误)
|
||||
LOG_LEVEL_TRACE = 1 << 5, ///< 追踪(性能追踪或者栈帧追踪)
|
||||
LOG_LEVEL_ALL = 0xFF, ///< 全级别标志(组合所有日志级别)
|
||||
} log_level_t;
|
||||
|
||||
#ifndef LOGGER_MAX_BUF_SIZE
|
||||
#define LOGGER_MAX_BUF_SIZE 512 ///< 单条日志最大缓冲区尺寸
|
||||
#endif
|
||||
|
||||
typedef struct logger logger_t;
|
||||
|
||||
typedef int (*log_handler)(logger_t *module, log_level_t level, const char *file, int line,
|
||||
const char *func, const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* @brief 日志器实例结构体
|
||||
*
|
||||
* 每个日志器实例维护独立的配置和缓冲区
|
||||
*/
|
||||
struct logger {
|
||||
const char *name; ///< 日志器名称(用于模块区分)
|
||||
log_level_t level; ///< 当前设置的日志级别
|
||||
union {
|
||||
log_handler handler;
|
||||
void *user_handler;
|
||||
}; ///< 日志处理回调函数
|
||||
void *user_data; ///< 用户自定义数据
|
||||
char buf[LOGGER_MAX_BUF_SIZE]; ///< 格式化缓冲区
|
||||
};
|
||||
|
||||
int log_default_handler(logger_t *module, log_level_t level, const char *file, int line,
|
||||
const char *func, const char *fmt, ...);
|
||||
extern logger_t __default_logger_root;
|
||||
#ifndef LOG_DEFAULT_HANDLER
|
||||
#define LOG_DEFAULT_HANDLER &__default_logger_root
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief 初始化日志实例 其余参数设置为默认值
|
||||
* @param[in] logger 日志器实例指针
|
||||
* @param[in] name 日志器名称(nullptr表示获取默认日志器名称)
|
||||
*/
|
||||
void init_logger(logger_t *logger, const char *name);
|
||||
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
* @param[in] logger 目标日志器实例
|
||||
* @param[in] level 要设置的日志级别(可组合多个级别)
|
||||
*/
|
||||
void log_set_level(logger_t *logger, int level);
|
||||
|
||||
/**
|
||||
* @brief 设置自定义日志处理器
|
||||
* @param[in] logger 目标日志器实例
|
||||
* @param[in] handler 自定义处理函数(nullptr恢复默认处理)
|
||||
*/
|
||||
void log_set_handler(logger_t *logger, log_handler handler);
|
||||
|
||||
#ifndef LOG_MAX_MAROC_BUF_SIZE
|
||||
#define LOG_MAX_MAROC_BUF_SIZE LOGGER_MAX_BUF_SIZE ///< 宏展开缓冲区尺寸
|
||||
#endif
|
||||
|
||||
#define SCC_LOG_HANDLE_ARGS(_module_, _level_, ...) \
|
||||
(_module_), (_level_), __FILE__, __LINE__, __func__, ##__VA_ARGS__
|
||||
|
||||
#define SCC_LOG_IMPL(_module_, _level_, _fmt_, ...) \
|
||||
do { \
|
||||
/* TODO check _module_ is nullptr */ \
|
||||
if ((_module_)->handler && ((_module_)->level & (_level_))) \
|
||||
(_module_)->handler(SCC_LOG_HANDLE_ARGS(_module_, _level_, _fmt_, ##__VA_ARGS__)); \
|
||||
} while (0)
|
||||
|
||||
/* clang-format off */
|
||||
/// @name 模块日志宏
|
||||
/// @{
|
||||
#define MLOG_NOTSET(module, ...)SCC_LOG_IMPL(module, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志
|
||||
#define MLOG_DEBUG(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别)
|
||||
#define MLOG_INFO(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志)
|
||||
#define MLOG_WARN(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题)
|
||||
#define MLOG_ERROR(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误)
|
||||
#define MLOG_FATAL(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前)
|
||||
#define MLOG_TRACE(module, ...) SCC_LOG_IMPL(module, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪)
|
||||
/// @}
|
||||
|
||||
/// @name 快捷日志宏
|
||||
/// @{
|
||||
#define LOG_NOTSET(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_NOTSET, __VA_ARGS__) ///< 未分类日志
|
||||
#define LOG_DEBUG(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_DEBUG, __VA_ARGS__) ///< 调试日志(需启用DEBUG级别)
|
||||
#define LOG_INFO(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_INFO, __VA_ARGS__) ///< 信息日志(常规运行日志)
|
||||
#define LOG_WARN(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_WARN, __VA_ARGS__) ///< 警告日志(潜在问题)
|
||||
#define LOG_ERROR(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_ERROR, __VA_ARGS__) ///< 错误日志(可恢复错误)
|
||||
#define LOG_FATAL(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_FATAL, __VA_ARGS__) ///< 致命错误日志(程序终止前)
|
||||
#define LOG_TRACE(...) SCC_LOG_IMPL(LOG_DEFAULT_HANDLER, LOG_LEVEL_TRACE, __VA_ARGS__) ///< 追踪日志(调用栈跟踪)
|
||||
/// @}
|
||||
/* clang-format on */
|
||||
|
||||
/**
|
||||
* @def _Assert
|
||||
* @brief 断言检查内部宏
|
||||
* @param cond 检查条件表达式
|
||||
* @param ... 错误信息参数(格式字符串+参数)
|
||||
*/
|
||||
#define _Assert(cond, ...) \
|
||||
((void)((cond) || (__default_logger_root.handler(SCC_LOG_HANDLE_ARGS( \
|
||||
&__default_logger_root, LOG_LEVEL_FATAL, __VA_ARGS__)), \
|
||||
log_abort(), __scc_log_unreachable(), 0)))
|
||||
|
||||
/// @name 断言工具宏
|
||||
/// @{
|
||||
#define __INNERSCC_LOG_IMPL_STR(str) #str
|
||||
#define _SCC_LOG_IMPL_STR(str) __INNERSCC_LOG_IMPL_STR(str)
|
||||
#define AssertFmt(cond, format, ...) \
|
||||
_Assert(cond, "Assertion Failure: " format, ##__VA_ARGS__) ///< 带格式的断言检查
|
||||
#define PanicFmt(format, ...) _Assert(0, "Panic: " format, ##__VA_ARGS__) ///< 立即触发致命错误
|
||||
#define Assert(cond) AssertFmt(cond, "cond is `" _SCC_LOG_IMPL_STR(cond) "`") ///< 基础断言检查
|
||||
#define Panic(...) PanicFmt(__VA_ARGS__) ///< 触发致命错误(带自定义消息)
|
||||
#define TODO() PanicFmt("TODO please implement me") ///< 标记未实现代码(触发致命错误)
|
||||
#define UNREACHABLE() PanicFmt("UNREACHABLE") ///< 触发致命错误(代码不可达)
|
||||
#define FIXME(str) PanicFmt("FIXME " _SCC_LOG_IMPL_STR(str)) ///< 提醒开发者修改代码(触发致命错误)
|
||||
/// @}
|
||||
|
||||
/**
|
||||
* @brief 静态断言(编译时)
|
||||
*
|
||||
* 利用数组大小不能为负的特性
|
||||
* 或使用 _Static_assert (C11)
|
||||
*/
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define StaticAssert _Static_assert
|
||||
#else
|
||||
#define StaticAssert(cond, msg) extern char __static_assertion[(cond) ? 1 : -1]
|
||||
#endif
|
||||
|
||||
#ifdef __SCC_LOG_IMPL_IMPORT_SRC__
|
||||
#include "log.c"
|
||||
#endif
|
||||
|
||||
#endif /* __SCC_LOG_IMPL_H__ */
|
||||
10
stage0/include/utils.h
Normal file
10
stage0/include/utils.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef __UTILS_H__
|
||||
#define __UTILS_H__
|
||||
|
||||
#define __SCC_LOG_IMPL_USE_STD_IMPL__
|
||||
#include "log.h"
|
||||
|
||||
#include "core_map.h"
|
||||
#include "core_vec.h"
|
||||
|
||||
#endif /* __UTILS_H__ */
|
||||
@@ -370,49 +370,6 @@ int spl_prog_add_data(spl_prog_t *prog, void *ptr, usize size) {
|
||||
return vec_size(prog->gdata);
|
||||
}
|
||||
|
||||
spl_val_t spl_prog_emit(spl_prog_t *prog, uint16_t opcode, uint16_t type, spl_val_t imm) {
|
||||
spl_ins_t ins = {opcode, type, imm};
|
||||
spl_val_t addr = vec_size(prog->insns);
|
||||
vec_push(prog->insns, ins);
|
||||
return addr;
|
||||
}
|
||||
|
||||
int spl_prog_add_func_simple(spl_prog_t *prog, const char *name, spl_val_t nargs) {
|
||||
spl_func_t func = {0};
|
||||
if (!prog || !name)
|
||||
return -1;
|
||||
func.name = strdup(name);
|
||||
func.idx_of_strtab = 0;
|
||||
func.nargs = nargs;
|
||||
func.ninsns = 0;
|
||||
func.address = vec_size(prog->insns);
|
||||
vec_push(prog->funcs, func);
|
||||
return (int)vec_size(prog->funcs) - 1;
|
||||
}
|
||||
|
||||
int spl_prog_update_func(spl_prog_t *prog, int func_idx, const char *name, spl_val_t nargs) {
|
||||
if (!prog || func_idx < 0 || func_idx >= (int)vec_size(prog->funcs))
|
||||
return spl_prog_add_func_simple(prog, name, nargs);
|
||||
spl_func_t *func = &vec_at(prog->funcs, func_idx);
|
||||
func->address = vec_size(prog->insns);
|
||||
func->nargs = nargs;
|
||||
return func_idx;
|
||||
}
|
||||
|
||||
void spl_prog_end_func(spl_prog_t *prog, int func_idx) {
|
||||
if (!prog || func_idx < 0 || func_idx >= (int)vec_size(prog->funcs))
|
||||
return;
|
||||
spl_func_t *func = &vec_at(prog->funcs, func_idx);
|
||||
func->ninsns = vec_size(prog->insns) - func->address;
|
||||
}
|
||||
|
||||
int spl_prog_add_str(spl_prog_t *prog, const char *str) {
|
||||
if (!prog || !str)
|
||||
return -1;
|
||||
vec_push(prog->strtab, strdup(str));
|
||||
return (int)vec_size(prog->strtab) - 1;
|
||||
}
|
||||
|
||||
spl_func_t *spl_prog_get_func(spl_prog_t *prog, const char *name) {
|
||||
if (!prog || !name)
|
||||
return NULL;
|
||||
@@ -445,10 +402,14 @@ 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_BOOL: return "bool";
|
||||
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:
|
||||
|
||||
@@ -208,13 +208,6 @@ int spl_prog_add_native(spl_prog_t *prog, spl_native_t *native);
|
||||
spl_func_t *spl_prog_get_func(spl_prog_t *prog, const char *name);
|
||||
spl_native_t *spl_prog_get_native(spl_prog_t *prog, const char *name);
|
||||
|
||||
/* High-level program construction helpers */
|
||||
spl_val_t spl_prog_emit(spl_prog_t *prog, uint16_t opcode, uint16_t type, spl_val_t imm);
|
||||
int spl_prog_add_func_simple(spl_prog_t *prog, const char *name, spl_val_t nargs);
|
||||
int spl_prog_update_func(spl_prog_t *prog, int func_idx, const char *name, spl_val_t nargs);
|
||||
void spl_prog_end_func(spl_prog_t *prog, int func_idx);
|
||||
int spl_prog_add_str(spl_prog_t *prog, const char *str);
|
||||
|
||||
/* Opcode name lookup for debugging/dumping */
|
||||
const char *spl_opcode_name(spl_opcode_t opcode);
|
||||
const char *spl_type_tag_name(spl_type_t type);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "include/acutest.h"
|
||||
#include "spl_ir.h"
|
||||
#include "spl_mcode.h"
|
||||
#include "spl_vm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <string.h>
|
||||
|
||||
/* ================================================================
|
||||
* Binary-writing helpers (mirrors spl_ir.c internal format)
|
||||
* Binary-writing helpers (mirrors spl_mcode.c internal format)
|
||||
* ================================================================ */
|
||||
|
||||
/* Write spl_val_t (8 LE bytes), advance pointer */
|
||||
|
||||
1
stage1/spl_ast.c
Normal file
1
stage1/spl_ast.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "spl_ast.h"
|
||||
305
stage1/spl_ast.h
Normal file
305
stage1/spl_ast.h
Normal file
@@ -0,0 +1,305 @@
|
||||
#ifndef __SPL_AST_H__
|
||||
#define __SPL_AST_H__
|
||||
|
||||
#include "../stage0/include/utils.h"
|
||||
#include "spl_lexer.h"
|
||||
#include "spl_tok.h"
|
||||
#include <math.h>
|
||||
|
||||
typedef enum {
|
||||
SPL_AST_CONTAINER_MEMBER,
|
||||
SPL_AST_FN_DECL,
|
||||
SPL_AST_TYPE_DECL,
|
||||
SPL_AST_VAR_DECL,
|
||||
SPL_AST_CONST_DECL,
|
||||
SPL_AST_MEMBER_DECL,
|
||||
SPL_AST__COMPTIME_STMT, /*不实现*/
|
||||
SPL_AST__DIRECTIVE_BLOCK, /*不实现*/
|
||||
|
||||
SPL_AST_BLOCK,
|
||||
SPL_AST_BLOCK_ITEM,
|
||||
SPL_AST_EXPR,
|
||||
SPL_AST_TYPE_EXPR,
|
||||
SPL_AST_ATTR_LIST,
|
||||
} spl_ast_node_kind_t;
|
||||
|
||||
typedef struct {
|
||||
const char *fname;
|
||||
int line;
|
||||
int col;
|
||||
} spl_ast_loc_t;
|
||||
|
||||
struct spl_ast_node;
|
||||
typedef struct spl_ast_node spl_ast_node_t;
|
||||
typedef usize spl_ast_node_ref_t;
|
||||
typedef VEC(spl_ast_node_ref_t) spl_ast_node_ref_vec_t;
|
||||
struct spl_ast_node {
|
||||
spl_ast_node_kind_t kind;
|
||||
spl_ast_loc_t loc;
|
||||
union {
|
||||
spl_ast_node_ref_vec_t container_member;
|
||||
|
||||
spl_ast_node_ref_vec_t attr_list;
|
||||
|
||||
struct {
|
||||
spl_ast_node_ref_t attr_list;
|
||||
const char *name;
|
||||
spl_ast_node_ref_t param_list;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
spl_ast_node_ref_t block;
|
||||
} fn_decl;
|
||||
spl_ast_node_ref_vec_t param_list;
|
||||
struct {
|
||||
spl_ast_node_ref_t attr_list;
|
||||
const char *name;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
} param_decl;
|
||||
|
||||
struct {
|
||||
spl_ast_node_ref_t attr_list;
|
||||
const char *name;
|
||||
enum {
|
||||
SPL_AST_TYPE_STRUCT,
|
||||
SPL_AST_TYPE_UNION,
|
||||
SPL_AST_TYPE_ENUM,
|
||||
SPL_AST_TYPE_TYPE_EXPR,
|
||||
};
|
||||
union {
|
||||
spl_ast_node_ref_t type_expr;
|
||||
spl_ast_node_ref_t aggregate_list;
|
||||
};
|
||||
} type_decl;
|
||||
struct {
|
||||
spl_ast_node_ref_t attr_list;
|
||||
const char *name;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
} member_decl;
|
||||
|
||||
struct {
|
||||
spl_ast_node_ref_t attr_list;
|
||||
const char *name;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
spl_ast_node_ref_t expr;
|
||||
} var_decl;
|
||||
struct {
|
||||
spl_ast_node_ref_t attr_list;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
const char *name;
|
||||
spl_ast_node_ref_t expr;
|
||||
} const_decl;
|
||||
|
||||
spl_ast_node_ref_vec_t block;
|
||||
struct {
|
||||
enum {
|
||||
SPL_AST_IF_STATEMENT,
|
||||
SPL_AST_IFVAR_STATEMENT,
|
||||
SPL_AST_WHILE_STATEMENT,
|
||||
SPL_AST_LOOP_STATEMENT,
|
||||
SPL_AST_FOR_STATEMENT,
|
||||
SPL_AST_MATCH_STATEMENT,
|
||||
SPL_AST_RET_STATEMENT,
|
||||
SPL_AST_BREAK_STATEMENT,
|
||||
SPL_AST_CONTINUE_STATEMENT,
|
||||
SPL_AST_DEFER_STATEMENT,
|
||||
SPL_AST_VARDECL,
|
||||
SPL_AST_TYPEDECL,
|
||||
SPL_AST_EXPR_STATEMENT,
|
||||
SPL_PACED_EXPR,
|
||||
} kind;
|
||||
union {
|
||||
struct {
|
||||
spl_ast_node_ref_t expr;
|
||||
spl_ast_node_ref_t if_block;
|
||||
spl_ast_node_ref_t else_block;
|
||||
} if_statement;
|
||||
struct {
|
||||
spl_ast_node_ref_t packed_expr;
|
||||
spl_ast_node_ref_t if_block;
|
||||
spl_ast_node_ref_t else_block;
|
||||
} ifvar_statement;
|
||||
struct {
|
||||
spl_ast_node_ref_t expr;
|
||||
spl_ast_node_ref_t while_block;
|
||||
} while_statement;
|
||||
struct {
|
||||
spl_ast_node_ref_t loop_block;
|
||||
} loop_statement;
|
||||
struct {
|
||||
spl_ast_node_ref_vec_t expr_vec;
|
||||
VEC(char *) ident_vec;
|
||||
spl_ast_node_ref_t block;
|
||||
} for_statement;
|
||||
struct {
|
||||
spl_ast_node_ref_t expr;
|
||||
spl_ast_node_ref_vec_t paced_exprs;
|
||||
spl_ast_node_ref_vec_t statements;
|
||||
} match_statement;
|
||||
struct {
|
||||
spl_ast_node_ref_t expr;
|
||||
} ret_statement;
|
||||
struct {
|
||||
} break_statement;
|
||||
struct {
|
||||
} continue_statement;
|
||||
struct {
|
||||
spl_ast_node_ref_t block_or_statement;
|
||||
} defer_statement;
|
||||
spl_ast_node_ref_t var_decl;
|
||||
spl_ast_node_ref_t type_decl;
|
||||
spl_ast_node_ref_t expr_statement;
|
||||
|
||||
struct {
|
||||
const char *ident;
|
||||
const char *bind_ident;
|
||||
spl_ast_node_ref_t expr;
|
||||
} packed_expr;
|
||||
};
|
||||
} block_item;
|
||||
|
||||
struct {
|
||||
enum {
|
||||
SPL_AST_ASSIGN_EXPR,
|
||||
SPL_AST_BOOLOR_EXPR,
|
||||
SPL_AST_BOOLAND_EXPR,
|
||||
SPL_AST_BITOR_EXPR,
|
||||
SPL_AST_BITXOR_EXPR,
|
||||
SPL_AST_BITAND_EXPR,
|
||||
SPL_AST_CMPEQ_EXPR,
|
||||
SPL_AST_CMP_EXPR,
|
||||
SPL_AST_RANGE_EXPR,
|
||||
SPL_AST_SHIFT_EXPR,
|
||||
SPL_AST_ADD_EXPR,
|
||||
SPL_AST_MUL_EXPR,
|
||||
SPL_AST_PREFIX_EXPR,
|
||||
SPL_AST_POSTFIX_EXPR, /*left ref node*/
|
||||
SPL_AST_PRIMARY_EXPR, /*left ref node*/
|
||||
} op;
|
||||
struct {
|
||||
spl_ast_node_ref_t left;
|
||||
spl_ast_node_ref_t right;
|
||||
} op_expr;
|
||||
} expr;
|
||||
struct {
|
||||
enum {
|
||||
SPL_AST_CALL_EXPR,
|
||||
SPL_AST_IDENT_EXPR,
|
||||
SPL_AST_DEREF_EXPR,
|
||||
SPL_AST_INDEX_EXPR,
|
||||
SPL_AST_SLICE_EXPR,
|
||||
SPL_AST_AS_EXPR,
|
||||
} kind;
|
||||
union {
|
||||
spl_ast_node_ref_vec_t call_expr;
|
||||
const char *indet_expr;
|
||||
spl_ast_node_ref_t index_expr;
|
||||
struct {
|
||||
spl_ast_node_ref_t begin;
|
||||
spl_ast_node_ref_t end;
|
||||
} slice_expr;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
};
|
||||
} postfix_expr;
|
||||
struct {
|
||||
enum {
|
||||
SPL_AST_INTEGER,
|
||||
SPL_AST_FLOAT,
|
||||
SPL_AST_CHAR_LIT,
|
||||
SPL_AST_STRING_LIT,
|
||||
SPL_AST_TRUE,
|
||||
SPL_AST_FALSE,
|
||||
SPL_AST_NULL,
|
||||
SPL_AST_IDENT,
|
||||
SPL_AST_ARGGREGATE_INIT,
|
||||
SPL_AST_EXPR_EXPR,
|
||||
SPL_AST_ARRAY_LIT,
|
||||
SPL_AST_BUILTIN_EXPR,
|
||||
SPL_AST_BLOCK_EXPR,
|
||||
};
|
||||
union {
|
||||
isize integer_expr;
|
||||
double float_expr;
|
||||
char char_lit_expr;
|
||||
const char *string_lit_expr;
|
||||
const char *ident;
|
||||
|
||||
/*aggregate_init_item*/
|
||||
spl_ast_node_ref_vec_t aggregate_init_expr;
|
||||
|
||||
spl_ast_node_ref_t expr;
|
||||
struct {
|
||||
isize integer;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
spl_ast_node_ref_vec_t expr_list;
|
||||
} array_lit_expr;
|
||||
struct {
|
||||
const char *ident;
|
||||
spl_ast_node_ref_vec_t expr_list;
|
||||
} builtin_expr;
|
||||
spl_ast_node_ref_t block_expr;
|
||||
};
|
||||
} primary_expr;
|
||||
struct {
|
||||
const char *ident;
|
||||
spl_ast_node_ref_t expr;
|
||||
} aggregate_init_item;
|
||||
|
||||
struct {
|
||||
/* ASTERISK = 1, [] = 2, else = 0*/
|
||||
int pointer;
|
||||
/* if array_size != 0 then pointer == 2 */
|
||||
int array_size;
|
||||
enum {
|
||||
SPL_AST_BASE_TYPE_FN,
|
||||
SPL_AST_BASE_TYPE_PATH,
|
||||
} kind;
|
||||
|
||||
const char *spl_base_type;
|
||||
union {
|
||||
spl_ast_node_ref_vec_t type_path;
|
||||
struct {
|
||||
spl_ast_node_ref_t param_list;
|
||||
spl_ast_node_ref_t type_expr;
|
||||
} fn_type;
|
||||
};
|
||||
} type_expr;
|
||||
struct {
|
||||
enum {
|
||||
SPL_AST_TYPE_VOID,
|
||||
SPL_AST_TYPE_BOOL,
|
||||
SPL_AST_TYPE_I8,
|
||||
SPL_AST_TYPE_U8,
|
||||
SPL_AST_TYPE_I16,
|
||||
SPL_AST_TYPE_U16,
|
||||
SPL_AST_TYPE_I32,
|
||||
SPL_AST_TYPE_U32,
|
||||
SPL_AST_TYPE_I64,
|
||||
SPL_AST_TYPE_U64,
|
||||
SPL_AST_TYPE_ISIZE,
|
||||
SPL_AST_TYPE_USIZE,
|
||||
SPL_AST_TYPE__F32, /*不实现*/
|
||||
SPL_AST_TYPE__F64, /*不实现*/
|
||||
SPL_AST_TYPE_PTR,
|
||||
SPL_AST_TYPE_ANY,
|
||||
SPL_AST_TYPE_IDENT,
|
||||
} kind;
|
||||
const char *ident;
|
||||
} type_atom;
|
||||
};
|
||||
};
|
||||
|
||||
typedef VEC(spl_ast_node_t) spl_ast_node_vec_t;
|
||||
typedef struct {
|
||||
int parsed;
|
||||
spl_tok_vec_t input;
|
||||
spl_ast_node_vec_t buckets;
|
||||
spl_ast_node_ref_t root;
|
||||
} spl_ast_t;
|
||||
|
||||
void spl_ast_init(spl_ast_t *ast, const spl_tok_vec_t *tok_vec /*move*/);
|
||||
void spl_ast_drop(spl_ast_t *ast);
|
||||
|
||||
void spl_ast_prase(spl_ast_t *ast);
|
||||
void spl_ast_valid(spl_ast_t *ast);
|
||||
void spl_ast_dump(spl_ast_t *ast, spl_ast_node_ref_t node);
|
||||
|
||||
#endif /* __SPL_AST_H__ */
|
||||
@@ -1,264 +0,0 @@
|
||||
/* spl_comp.c — SPL compiler main logic and codegen helpers */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include "spl_lex_util.h"
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---- Compiler context init/drop ---- */
|
||||
|
||||
void spl_comp_init(spl_comp_t *ctx) {
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
vec_init(ctx->toks);
|
||||
vec_init(ctx->scopes);
|
||||
vec_init(ctx->funcs);
|
||||
map_init(ctx->const_values, MAP_HASH_STR, MAP_CMP_STR);
|
||||
spl_prog_init(&ctx->prog);
|
||||
ctx->error_msg[0] = '\0';
|
||||
ctx->parse_context[0] = '\0';
|
||||
spl_emit_init(&ctx->emit, &ctx->prog);
|
||||
spl_type_ctx_init(&ctx->tctx);
|
||||
ctx->current_ret_type_idx = -1;
|
||||
}
|
||||
|
||||
void spl_comp_drop(spl_comp_t *ctx) {
|
||||
if (!ctx)
|
||||
return;
|
||||
spl_tok_vec_drop(&ctx->toks);
|
||||
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
|
||||
vec_free(ctx->scopes);
|
||||
vec_for(ctx->funcs, i) {
|
||||
free(ctx->funcs.data[i].name);
|
||||
free(ctx->funcs.data[i].param_type_indices);
|
||||
free(ctx->funcs.data[i].param_names);
|
||||
}
|
||||
vec_free(ctx->funcs);
|
||||
map_free(ctx->const_values);
|
||||
spl_prog_drop(&ctx->prog);
|
||||
free(ctx->break_patches);
|
||||
spl_emit_drop(&ctx->emit);
|
||||
spl_type_ctx_drop(&ctx->tctx);
|
||||
}
|
||||
|
||||
void spl_comp_reset(spl_comp_t *ctx) {
|
||||
spl_tok_vec_drop(&ctx->toks);
|
||||
vec_init(ctx->toks);
|
||||
vec_for(ctx->scopes, i) { vec_free(vec_at(ctx->scopes, i).vars); }
|
||||
vec_free(ctx->scopes);
|
||||
vec_init(ctx->scopes);
|
||||
ctx->scope_depth = 0;
|
||||
ctx->tok_idx = 0;
|
||||
ctx->has_error = 0;
|
||||
ctx->error_msg[0] = '\0';
|
||||
ctx->current_func_idx = -1;
|
||||
ctx->current_ret_type_idx = -1;
|
||||
fa_init(&ctx->emit.frame);
|
||||
ctx->in_loop = 0;
|
||||
ctx->break_patch_count = 0;
|
||||
ctx->break_patch_cap = 0;
|
||||
ctx->continue_target = 0;
|
||||
ctx->defer_count = 0;
|
||||
ctx->next_gdata_idx = 0;
|
||||
ctx->addr_of_mode = 0;
|
||||
free(ctx->break_patches);
|
||||
ctx->break_patches = NULL;
|
||||
vec_free(ctx->emit.fixups);
|
||||
vec_init(ctx->emit.fixups);
|
||||
}
|
||||
|
||||
void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...) {
|
||||
if (ctx->has_error)
|
||||
return;
|
||||
|
||||
char body[COMP_ERROR_MAX - 64];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(body, sizeof(body), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (ctx->error_line > 0)
|
||||
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "line %zu:%zu: %s", ctx->error_line,
|
||||
ctx->error_col, body);
|
||||
else
|
||||
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "%s", body);
|
||||
ctx->has_error = 1;
|
||||
}
|
||||
|
||||
void spl_comp_err_tok(spl_comp_t *ctx, spl_tok_t *tok, const char *fmt, ...) {
|
||||
if (ctx->has_error)
|
||||
return;
|
||||
|
||||
char body[COMP_ERROR_MAX - 64];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(body, sizeof(body), fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if (tok) {
|
||||
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "line %zu:%zu: %s", tok->line, tok->col, body);
|
||||
} else {
|
||||
snprintf(ctx->error_msg, COMP_ERROR_MAX - 1, "%s", body);
|
||||
}
|
||||
ctx->has_error = 1;
|
||||
}
|
||||
|
||||
/* ---- Scope management ---- */
|
||||
|
||||
void spl_push_scope(spl_comp_t *ctx) {
|
||||
spl_scope_t scope;
|
||||
vec_init(scope.vars);
|
||||
scope.depth = ++ctx->scope_depth;
|
||||
vec_push(ctx->scopes, scope);
|
||||
}
|
||||
|
||||
void spl_pop_scope(spl_comp_t *ctx) {
|
||||
if (vec_size(ctx->scopes) > 0) {
|
||||
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
||||
vec_for(scope->vars, i) { free(vec_at(scope->vars, i).name); }
|
||||
vec_free(scope->vars);
|
||||
ctx->scope_depth--;
|
||||
ctx->scopes.size--;
|
||||
} else {
|
||||
ctx->scope_depth--;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Variable management ---- */
|
||||
|
||||
int spl_declare_var(spl_comp_t *ctx, const char *name, int type_idx, int is_const) {
|
||||
spl_var_info_t var;
|
||||
memset(&var, 0, sizeof(var));
|
||||
var.name = strdup(name);
|
||||
var.type_idx = type_idx;
|
||||
var.is_const = is_const;
|
||||
var.depth = ctx->scope_depth;
|
||||
var.offset = fa_alloc(&ctx->emit.frame, spl_type_size(&ctx->tctx, type_idx));
|
||||
|
||||
if (vec_size(ctx->scopes) > 0) {
|
||||
spl_scope_t *scope = &vec_at(ctx->scopes, vec_size(ctx->scopes) - 1);
|
||||
vec_push(scope->vars, var);
|
||||
}
|
||||
return var.offset;
|
||||
}
|
||||
|
||||
spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name) {
|
||||
for (int i = (int)vec_size(ctx->scopes) - 1; i >= 0; i--) {
|
||||
spl_scope_t *scope = &vec_at(ctx->scopes, i);
|
||||
for (int j = (int)vec_size(scope->vars) - 1; j >= 0; j--) {
|
||||
if (strcmp(vec_at(scope->vars, j).name, name) == 0)
|
||||
return &vec_at(scope->vars, j);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int spl_get_var_offset(spl_comp_t *ctx, const char *name) {
|
||||
spl_var_info_t *v = spl_lookup_var(ctx, name);
|
||||
return v ? v->offset : -1;
|
||||
}
|
||||
|
||||
/* ---- Function management ---- */
|
||||
|
||||
int spl_declare_func(spl_comp_t *ctx, const char *name, int ret_type_idx, int nparams,
|
||||
int is_extern, int is_pub) {
|
||||
vec_for(ctx->funcs, i) {
|
||||
spl_func_info_t *existing = &vec_at(ctx->funcs, i);
|
||||
if (strcmp(existing->name, name) == 0) {
|
||||
existing->ret_type_idx = ret_type_idx;
|
||||
existing->nparams = nparams;
|
||||
existing->is_extern = is_extern;
|
||||
existing->is_pub = is_pub;
|
||||
existing->func_idx =
|
||||
spl_prog_update_func(&ctx->prog, existing->func_idx, name, nparams);
|
||||
return (int)i;
|
||||
}
|
||||
}
|
||||
|
||||
spl_func_info_t fi;
|
||||
memset(&fi, 0, sizeof(fi));
|
||||
fi.name = strdup(name);
|
||||
fi.ret_type_idx = ret_type_idx;
|
||||
fi.nparams = nparams;
|
||||
fi.is_extern = is_extern;
|
||||
fi.is_pub = is_pub;
|
||||
|
||||
if (is_extern)
|
||||
fi.func_idx = -1;
|
||||
else
|
||||
fi.func_idx = spl_prog_add_func_simple(&ctx->prog, name, nparams);
|
||||
|
||||
vec_push(ctx->funcs, fi);
|
||||
return (int)vec_size(ctx->funcs) - 1;
|
||||
}
|
||||
|
||||
int spl_lookup_func(spl_comp_t *ctx, const char *name) {
|
||||
{
|
||||
int current = ctx->tctx.current_type_idx;
|
||||
while (current >= 0) {
|
||||
spl_type_item_vec_t *items = spl_type_items(&ctx->tctx, current);
|
||||
if (items) {
|
||||
vec_for(*items, j) {
|
||||
spl_type_item_t *it = &vec_at(*items, j);
|
||||
if (it->item_kind == ITEM_METHOD && it->name && strcmp(it->name, name) == 0)
|
||||
return it->method.func_idx;
|
||||
}
|
||||
}
|
||||
current = vec_at(ctx->tctx.types, current).parent_type_idx;
|
||||
}
|
||||
}
|
||||
|
||||
vec_for(ctx->funcs, j) {
|
||||
if (strcmp(vec_at(ctx->funcs, j).name, name) == 0)
|
||||
return (int)j;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int spl_ensure_native(spl_comp_t *ctx, const char *name) {
|
||||
vec_for(ctx->prog.natives, ni) {
|
||||
if (strcmp(vec_at(ctx->prog.natives, ni).name, name) == 0)
|
||||
return (int)ni;
|
||||
}
|
||||
spl_native_t nat;
|
||||
nat.name = strdup(name);
|
||||
nat.idx_of_strtab = 0;
|
||||
nat.impl_fn = NULL;
|
||||
vec_push(ctx->prog.natives, nat);
|
||||
return (int)vec_size(ctx->prog.natives) - 1;
|
||||
}
|
||||
|
||||
/* ---- String/data management ---- */
|
||||
|
||||
int spl_add_string(spl_comp_t *ctx, const char *str) {
|
||||
return spl_add_global_data(ctx, (void *)str, strlen(str) + 1);
|
||||
}
|
||||
|
||||
int spl_add_global_data(spl_comp_t *ctx, void *data, usize size) {
|
||||
return spl_prog_add_data(&ctx->prog, data, size);
|
||||
}
|
||||
|
||||
/* ---- Register runtime natives ---- */
|
||||
|
||||
void spl_comp_register(spl_prog_t *prog) { (void)prog; }
|
||||
|
||||
/* ---- Main compilation ---- */
|
||||
|
||||
int spl_compile(spl_comp_t *ctx, const char *source, const char *fname) {
|
||||
ctx->toks = spl_lex(source, fname);
|
||||
ctx->tok_idx = 0;
|
||||
ctx->fname = fname;
|
||||
ctx->source = source;
|
||||
|
||||
while (ctx->tok_idx < vec_size(ctx->toks) &&
|
||||
vec_at(ctx->toks, ctx->tok_idx).type == TOK_ENDLINE)
|
||||
ctx->tok_idx++;
|
||||
|
||||
spl_parse_prog(ctx);
|
||||
if (ctx->has_error)
|
||||
return -1;
|
||||
|
||||
emit_patch_call_fixups(&ctx->emit, &ctx->prog);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/* spl_comp.h — SPL compiler: type system, parser, codegen */
|
||||
#ifndef __SPL_COMP_H__
|
||||
#define __SPL_COMP_H__
|
||||
|
||||
#include "../stage0/spl_ir.h"
|
||||
#include "spl_lexer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "spl_emit.h"
|
||||
#include "spl_type.h"
|
||||
|
||||
/* Forward declarations */
|
||||
typedef struct spl_comp spl_comp_t;
|
||||
|
||||
/* ============================================================
|
||||
* Scope / symbol table
|
||||
* ============================================================ */
|
||||
|
||||
typedef struct spl_var_info {
|
||||
char *name;
|
||||
int type_idx;
|
||||
int offset;
|
||||
int is_const;
|
||||
int depth;
|
||||
} spl_var_info_t;
|
||||
typedef VEC(spl_var_info_t) spl_var_vec_t;
|
||||
|
||||
typedef struct spl_scope {
|
||||
spl_var_vec_t vars;
|
||||
int depth;
|
||||
} spl_scope_t;
|
||||
typedef VEC(spl_scope_t) spl_scope_vec_t;
|
||||
|
||||
typedef struct spl_func_info {
|
||||
char *name;
|
||||
int ret_type_idx;
|
||||
int *param_type_indices;
|
||||
char **param_names;
|
||||
int nparams;
|
||||
int func_idx;
|
||||
int is_extern;
|
||||
int is_pub;
|
||||
} spl_func_info_t;
|
||||
typedef VEC(spl_func_info_t) spl_func_info_vec_t;
|
||||
|
||||
/* ============================================================
|
||||
* Compiler context
|
||||
* ============================================================ */
|
||||
|
||||
#define COMP_ERROR_MAX 256
|
||||
#define DEFER_MAX 64
|
||||
|
||||
typedef struct {
|
||||
usize body_start;
|
||||
usize jmp_exit;
|
||||
int depth;
|
||||
int count_at_decl;
|
||||
} spl_defer_entry_t;
|
||||
|
||||
typedef struct spl_comp {
|
||||
/* Lexer output */
|
||||
spl_tok_vec_t toks;
|
||||
usize tok_idx;
|
||||
const char *fname;
|
||||
const char *source;
|
||||
|
||||
/* Program output */
|
||||
spl_prog_t prog;
|
||||
|
||||
/* IR emission + frame allocator */
|
||||
spl_emit_t emit;
|
||||
|
||||
/* Error state */
|
||||
char error_msg[COMP_ERROR_MAX];
|
||||
int has_error;
|
||||
usize error_line;
|
||||
usize error_col;
|
||||
|
||||
/* Scopes */
|
||||
spl_scope_vec_t scopes;
|
||||
int scope_depth;
|
||||
|
||||
/* Function table */
|
||||
spl_func_info_vec_t funcs;
|
||||
|
||||
/* Type context — first-class type arena + namespace */
|
||||
spl_type_ctx_t tctx;
|
||||
|
||||
/* Current function context */
|
||||
int current_func_idx;
|
||||
int current_ret_type_idx;
|
||||
char parse_context[COMP_ERROR_MAX]; /* current parsing context for error messages */
|
||||
|
||||
/* Loop context for break/continue */
|
||||
int in_loop;
|
||||
usize *break_patches;
|
||||
usize break_patch_count;
|
||||
usize break_patch_cap;
|
||||
usize continue_target;
|
||||
|
||||
/* Defer stack */
|
||||
spl_defer_entry_t defer_stack[DEFER_MAX];
|
||||
int defer_count;
|
||||
|
||||
/* Global data index for string literals */
|
||||
int next_gdata_idx;
|
||||
|
||||
/* Const values */
|
||||
MAP(const char *, spl_val_t) const_values;
|
||||
|
||||
int addr_of_mode;
|
||||
} spl_comp_t;
|
||||
|
||||
/* Initialize/destroy compiler context */
|
||||
void spl_comp_init(spl_comp_t *ctx);
|
||||
void spl_comp_drop(spl_comp_t *ctx);
|
||||
|
||||
/* Main compilation entry: source → .sir */
|
||||
int spl_compile(spl_comp_t *ctx, const char *source, const char *fname);
|
||||
|
||||
/* Reset for a new compilation */
|
||||
void spl_comp_reset(spl_comp_t *ctx);
|
||||
|
||||
/* Error reporting */
|
||||
void spl_comp_error(spl_comp_t *ctx, const char *fmt, ...);
|
||||
void spl_comp_err_tok(spl_comp_t *ctx, spl_tok_t *tok, const char *fmt, ...);
|
||||
|
||||
/* ============================================================
|
||||
* Parser functions (spl_parser.c)
|
||||
* ============================================================ */
|
||||
|
||||
void spl_parse_prog(spl_comp_t *ctx);
|
||||
void parse_type_decl(spl_comp_t *ctx);
|
||||
|
||||
/* ============================================================
|
||||
* Expression functions (spl_expr.c)
|
||||
* ============================================================ */
|
||||
|
||||
enum {
|
||||
PREC_MIN = 0,
|
||||
PREC_ASSIGN = 1,
|
||||
PREC_LOGOR = 2,
|
||||
PREC_LOGAND = 3,
|
||||
PREC_OR = 4,
|
||||
PREC_XOR = 5,
|
||||
PREC_AND = 6,
|
||||
PREC_CMPEQ = 7,
|
||||
PREC_CMP = 8,
|
||||
PREC_SHIFT = 9,
|
||||
PREC_ADD = 10,
|
||||
PREC_MUL = 11,
|
||||
PREC_PREFIX = 12,
|
||||
PREC_POSTFIX = 13
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int type_idx;
|
||||
int is_lvalue;
|
||||
} spl_expr_result_t;
|
||||
|
||||
spl_expr_result_t spl_parse_expr(spl_comp_t *ctx, int min_prec);
|
||||
spl_expr_result_t spl_parse_struct_literal(spl_comp_t *ctx, int type_idx);
|
||||
|
||||
/* Match arm pattern comparison */
|
||||
int spl_emit_match_enum_cmp(spl_comp_t *ctx, int enum_type_idx, int val_offset, int by_value);
|
||||
void spl_emit_match_value_cmp(spl_comp_t *ctx, int val_offset);
|
||||
|
||||
/* ============================================================
|
||||
* Statement functions (spl_stmt.c)
|
||||
* ============================================================ */
|
||||
|
||||
void spl_parse_stmt(spl_comp_t *ctx);
|
||||
void spl_parse_block(spl_comp_t *ctx);
|
||||
spl_expr_result_t spl_parse_block_expr(spl_comp_t *ctx);
|
||||
|
||||
/* ============================================================
|
||||
* Remaining helpers in spl_comp.c
|
||||
* ============================================================ */
|
||||
|
||||
/* Variable management */
|
||||
int spl_declare_var(spl_comp_t *ctx, const char *name, int type_idx, int is_const);
|
||||
spl_var_info_t *spl_lookup_var(spl_comp_t *ctx, const char *name);
|
||||
int spl_get_var_offset(spl_comp_t *ctx, const char *name);
|
||||
|
||||
/* Function management */
|
||||
int spl_declare_func(spl_comp_t *ctx, const char *name, int ret_type_idx, int nparams,
|
||||
int is_extern, int is_pub);
|
||||
int spl_lookup_func(spl_comp_t *ctx, const char *name);
|
||||
int spl_ensure_native(spl_comp_t *ctx, const char *name);
|
||||
|
||||
/* String/data management */
|
||||
int spl_add_string(spl_comp_t *ctx, const char *str);
|
||||
int spl_add_global_data(spl_comp_t *ctx, void *data, usize size);
|
||||
|
||||
/* Scope management */
|
||||
void spl_push_scope(spl_comp_t *ctx);
|
||||
void spl_pop_scope(spl_comp_t *ctx);
|
||||
|
||||
/* Register runtime natives (stub) */
|
||||
void spl_comp_register(spl_prog_t *prog);
|
||||
|
||||
#endif /* __SPL_COMP_H__ */
|
||||
@@ -1,282 +0,0 @@
|
||||
/* spl_emit.c — IR emission: frame allocator + Layer 1/2 ops */
|
||||
|
||||
#include "spl_emit.h"
|
||||
#include "spl_comp.h"
|
||||
#include "spl_type.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ============================================================
|
||||
* Lifecycle
|
||||
* ============================================================ */
|
||||
|
||||
void spl_emit_init(spl_emit_t *e, spl_prog_t *prog) {
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->prog = prog;
|
||||
vec_init(e->fixups);
|
||||
}
|
||||
|
||||
void spl_emit_drop(spl_emit_t *e) {
|
||||
if (!e)
|
||||
return;
|
||||
vec_free(e->fixups);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Frame allocator — non-inline
|
||||
* ============================================================ */
|
||||
|
||||
int fa_alloc_type(spl_frame_alloc_t *fa, spl_type_ctx_t *tctx, int type_idx) {
|
||||
return fa_alloc(fa, spl_type_size(tctx, type_idx));
|
||||
}
|
||||
|
||||
spl_val_t emit_raw(spl_emit_t *e, uint16_t opcode, uint16_t type, spl_val_t imm) {
|
||||
return spl_prog_emit(e->prog, opcode, type, imm);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Layer 1 — single-instruction semantic wrappers
|
||||
* ============================================================ */
|
||||
|
||||
void emit_drop(spl_emit_t *e) { emit_raw(e, SPL_DROP, SPL_VOID, 0); }
|
||||
void emit_dup(spl_emit_t *e) { emit_raw(e, SPL_DUP, SPL_VOID, 0); }
|
||||
void emit_swap(spl_emit_t *e) { emit_raw(e, SPL_SWAP, SPL_VOID, 0); }
|
||||
void emit_rot(spl_emit_t *e) { emit_raw(e, SPL_ROT, SPL_VOID, 0); }
|
||||
void emit_pick(spl_emit_t *e, int n) { emit_raw(e, SPL_PICK, SPL_VOID, n); }
|
||||
|
||||
void emit_push_i32(spl_emit_t *e, int v) { emit_raw(e, SPL_PUSH, SPL_I32, (spl_val_t)v); }
|
||||
void emit_push_usize(spl_emit_t *e, usize v) { emit_raw(e, SPL_PUSH, SPL_USIZE, (spl_val_t)v); }
|
||||
void emit_push_u64(spl_emit_t *e, uint64_t v) { emit_raw(e, SPL_PUSH, SPL_U64, (spl_val_t)v); }
|
||||
void emit_push_f64(spl_emit_t *e, uint64_t v) { emit_raw(e, SPL_PUSH, SPL_F64, (spl_val_t)v); }
|
||||
void emit_push_ptr(spl_emit_t *e, spl_val_t v) { emit_raw(e, SPL_PUSH, SPL_PTR, v); }
|
||||
void emit_push_type(spl_emit_t *e, uint16_t bt, spl_val_t v) { emit_raw(e, SPL_PUSH, bt, v); }
|
||||
|
||||
void emit_load_ptr(spl_emit_t *e) { emit_raw(e, SPL_LOAD, SPL_PTR, 0); }
|
||||
void emit_load_usize(spl_emit_t *e) { emit_raw(e, SPL_LOAD, SPL_USIZE, 0); }
|
||||
void emit_load_type(spl_emit_t *e, uint16_t bt) { emit_raw(e, SPL_LOAD, bt, 0); }
|
||||
|
||||
void emit_store_i32(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_I32, 0); }
|
||||
void emit_store_ptr(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_PTR, 0); }
|
||||
void emit_store_usize(spl_emit_t *e) { emit_raw(e, SPL_STORE, SPL_USIZE, 0); }
|
||||
void emit_store_type(spl_emit_t *e, uint16_t bt) { emit_raw(e, SPL_STORE, bt, 0); }
|
||||
|
||||
void emit_add_usize(spl_emit_t *e) { emit_raw(e, SPL_ADD, SPL_USIZE, 0); }
|
||||
void emit_add_u64(spl_emit_t *e) { emit_raw(e, SPL_ADD, SPL_U64, 0); }
|
||||
void emit_mul_u64(spl_emit_t *e) { emit_raw(e, SPL_MUL, SPL_U64, 0); }
|
||||
void emit_sub_usize(spl_emit_t *e) { emit_raw(e, SPL_SUB, SPL_USIZE, 0); }
|
||||
|
||||
void emit_ult_usize(spl_emit_t *e) { emit_raw(e, SPL_ULT, SPL_USIZE, 0); }
|
||||
|
||||
void emit_jmp(spl_emit_t *e, spl_val_t offset) { emit_raw(e, SPL_JMP, SPL_VOID, offset); }
|
||||
spl_val_t emit_jmp_here(spl_emit_t *e) { return emit_raw(e, SPL_JMP, SPL_VOID, 0); }
|
||||
spl_val_t emit_bz_here(spl_emit_t *e) { return emit_raw(e, SPL_BZ, SPL_VOID, 0); }
|
||||
spl_val_t emit_bnz_here(spl_emit_t *e) { return emit_raw(e, SPL_BNZ, SPL_VOID, 0); }
|
||||
|
||||
void emit_patch(spl_emit_t *e, spl_val_t addr, spl_val_t target) {
|
||||
if (addr < vec_size(e->prog->insns))
|
||||
vec_at(e->prog->insns, addr).imm = target;
|
||||
}
|
||||
|
||||
void emit_patch_here(spl_emit_t *e, spl_val_t addr) {
|
||||
spl_val_t here = vec_size(e->prog->insns);
|
||||
emit_patch(e, addr, here - addr - 1);
|
||||
}
|
||||
|
||||
void emit_laddr(spl_emit_t *e, int offset) { emit_raw(e, SPL_LADDR, SPL_PTR, offset); }
|
||||
void emit_gaddr(spl_emit_t *e, int idx) { emit_raw(e, SPL_GADDR, SPL_PTR, idx); }
|
||||
|
||||
void emit_call(spl_emit_t *e, int nargs) { emit_raw(e, SPL_CALL, SPL_VOID, nargs); }
|
||||
void emit_ncall(spl_emit_t *e, int nargs) { emit_raw(e, SPL_NCALL, SPL_VOID, nargs); }
|
||||
|
||||
void emit_alloc(spl_emit_t *e, int slots) { emit_raw(e, SPL_ALLOC, SPL_VOID, slots); }
|
||||
void emit_binop(spl_emit_t *e, uint16_t op, uint16_t bt) { emit_raw(e, op, bt, 0); }
|
||||
void emit_dbg_void(spl_emit_t *e) { emit_raw(e, SPL_DBG, SPL_VOID, 0); }
|
||||
void emit_dbg_usize(spl_emit_t *e) { emit_raw(e, SPL_DBG, SPL_USIZE, 0); }
|
||||
|
||||
/* ============================================================
|
||||
* Layer 2 — semantic-level helpers
|
||||
* ============================================================ */
|
||||
|
||||
void emit_frame_copy(spl_emit_t *e, int dest_offset, usize nslots) {
|
||||
for (usize i = 0; i < nslots; i++) {
|
||||
if (i < nslots - 1)
|
||||
emit_dup(e);
|
||||
if (i > 0) {
|
||||
emit_push_usize(e, i * sizeof(spl_val_t));
|
||||
emit_add_usize(e);
|
||||
}
|
||||
emit_load_ptr(e);
|
||||
emit_laddr(e, dest_offset + (int)(i * sizeof(spl_val_t)));
|
||||
emit_swap(e);
|
||||
emit_store_ptr(e);
|
||||
}
|
||||
}
|
||||
|
||||
void emit_copy_addr_to_addr(spl_emit_t *e, usize nslots, int depth) {
|
||||
for (usize i = 0; i < nslots; i++) {
|
||||
emit_dup(e);
|
||||
if (i > 0) {
|
||||
emit_push_usize(e, i * sizeof(spl_val_t));
|
||||
emit_add_usize(e);
|
||||
}
|
||||
emit_load_ptr(e);
|
||||
emit_pick(e, 2 + depth);
|
||||
if (i > 0) {
|
||||
emit_push_usize(e, i * sizeof(spl_val_t));
|
||||
emit_add_usize(e);
|
||||
}
|
||||
emit_swap(e);
|
||||
emit_store_ptr(e);
|
||||
}
|
||||
emit_drop(e);
|
||||
emit_drop(e);
|
||||
}
|
||||
|
||||
void emit_load_to_var(spl_emit_t *e, spl_type_ctx_t *tctx, int ptr_slot_offset, usize byte_offset,
|
||||
int data_type_idx, int var_offset) {
|
||||
emit_laddr(e, ptr_slot_offset);
|
||||
emit_load_ptr(e);
|
||||
emit_ptr_add(e, byte_offset);
|
||||
if (data_type_idx >= 0 && !spl_type_is_scalar(tctx, data_type_idx) &&
|
||||
spl_type_size(tctx, data_type_idx) > sizeof(spl_val_t)) {
|
||||
emit_frame_copy(e, var_offset, spl_type_slot_count(tctx, data_type_idx));
|
||||
} else {
|
||||
uint16_t bt = spl_type_emit_type(tctx, data_type_idx);
|
||||
emit_load_type(e, bt);
|
||||
emit_laddr(e, var_offset);
|
||||
emit_swap(e);
|
||||
emit_store_type(e, bt);
|
||||
}
|
||||
}
|
||||
|
||||
void emit_store_to_laddr(spl_emit_t *e, int offset, uint16_t type) {
|
||||
emit_laddr(e, offset);
|
||||
emit_swap(e);
|
||||
emit_store_type(e, type);
|
||||
}
|
||||
|
||||
void emit_ptr_add(spl_emit_t *e, usize byte_off) {
|
||||
if (byte_off > 0) {
|
||||
emit_push_usize(e, byte_off);
|
||||
emit_add_usize(e);
|
||||
}
|
||||
}
|
||||
|
||||
spl_val_t emit_call_with_fixup(spl_emit_t *e, int nargs, int func_idx) {
|
||||
spl_val_t fixup_addr = emit_raw(e, SPL_PUSH, SPL_PTR, 0);
|
||||
emit_call(e, nargs);
|
||||
spl_fixup_entry_t fe = {fixup_addr, func_idx};
|
||||
vec_push(e->fixups, fe);
|
||||
return fixup_addr;
|
||||
}
|
||||
|
||||
void emit_return(spl_emit_t *e, spl_type_ctx_t *tctx, int ret_type_idx) {
|
||||
if (spl_type_needs_multi_slot(tctx, ret_type_idx)) {
|
||||
emit_laddr(e, (int)sizeof(spl_val_t));
|
||||
emit_raw(e, SPL_RET, SPL_PTR, 0);
|
||||
} else {
|
||||
uint16_t rt = spl_type_emit_type(tctx, ret_type_idx);
|
||||
emit_raw(e, SPL_RET, rt, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void emit_patch_call_fixups(spl_emit_t *e, spl_prog_t *prog) {
|
||||
for (usize i = 0; i < vec_size(e->fixups); i++) {
|
||||
spl_fixup_entry_t *fe = &vec_at(e->fixups, i);
|
||||
if (fe->func_idx >= 0 && fe->func_idx < (int)vec_size(prog->funcs)) {
|
||||
spl_val_t addr = vec_at(prog->funcs, fe->func_idx).address;
|
||||
vec_at(prog->insns, fe->insn_idx).imm = addr;
|
||||
} else {
|
||||
fprintf(stderr, "WARN: fixup func_idx=%d out of bounds [0,%zu), insn_idx=%zu\n",
|
||||
fe->func_idx, vec_size(prog->funcs), fe->insn_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spl_emit_ret(spl_comp_t *ctx, int ret_type_idx) {
|
||||
if (spl_type_needs_multi_slot(&ctx->tctx, ret_type_idx)) {
|
||||
emit_laddr(&ctx->emit, (int)sizeof(spl_val_t));
|
||||
emit_swap(&ctx->emit);
|
||||
emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, ret_type_idx), 0);
|
||||
}
|
||||
emit_return(&ctx->emit, &ctx->tctx, ret_type_idx);
|
||||
}
|
||||
|
||||
void spl_emit_store_init(spl_comp_t *ctx, int var_offset, int var_type_idx) {
|
||||
if (var_type_idx >= 0 && (spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_STRUCT ||
|
||||
spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ENUM)) {
|
||||
usize sz = spl_type_size(&ctx->tctx, var_type_idx);
|
||||
if (sz <= sizeof(spl_val_t)) {
|
||||
emit_store_to_laddr(&ctx->emit, var_offset,
|
||||
spl_type_emit_type(&ctx->tctx, var_type_idx));
|
||||
} else {
|
||||
usize nslots = (sz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t);
|
||||
emit_frame_copy(&ctx->emit, var_offset, nslots);
|
||||
}
|
||||
} else if (var_type_idx >= 0 && spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_ARRAY) {
|
||||
int elem_idx = spl_type_elem_type(&ctx->tctx, var_type_idx);
|
||||
spl_type_t bt = spl_type_emit_type(&ctx->tctx, elem_idx);
|
||||
usize stride = spl_type_elem_stride(&ctx->tctx, elem_idx);
|
||||
int arr_len = (int)spl_type_array_len(&ctx->tctx, var_type_idx);
|
||||
for (int i = arr_len - 1; i >= 0; i--) {
|
||||
emit_laddr(&ctx->emit, var_offset + (int)(i * stride));
|
||||
emit_swap(&ctx->emit);
|
||||
if (elem_idx >= 0 && !spl_type_is_scalar(&ctx->tctx, elem_idx)) {
|
||||
emit_copy_addr_to_addr(&ctx->emit, spl_type_slot_count(&ctx->tctx, elem_idx), 0);
|
||||
} else {
|
||||
emit_store_type(&ctx->emit, bt);
|
||||
}
|
||||
}
|
||||
} else if (var_type_idx >= 0 && spl_type_kind(&ctx->tctx, var_type_idx) == TYPE_SLICE) {
|
||||
emit_store_to_laddr(&ctx->emit, var_offset + (int)sizeof(spl_val_t), SPL_USIZE);
|
||||
emit_store_to_laddr(&ctx->emit, var_offset, SPL_PTR);
|
||||
} else {
|
||||
spl_type_t bt = spl_type_emit_type(&ctx->tctx, var_type_idx);
|
||||
emit_store_to_laddr(&ctx->emit, var_offset, bt);
|
||||
}
|
||||
}
|
||||
|
||||
void spl_emit_defer(spl_comp_t *ctx) {
|
||||
if (ctx->defer_count >= DEFER_MAX)
|
||||
return;
|
||||
|
||||
spl_val_t jmp_skip = emit_jmp_here(&ctx->emit);
|
||||
|
||||
spl_defer_entry_t *e = &ctx->defer_stack[ctx->defer_count];
|
||||
e->body_start = jmp_skip + 1;
|
||||
e->jmp_exit = 0;
|
||||
e->depth = ctx->scope_depth;
|
||||
e->count_at_decl = ctx->defer_count;
|
||||
ctx->defer_count++;
|
||||
}
|
||||
|
||||
void spl_emit_defer_epilogue(spl_comp_t *ctx, int depth) {
|
||||
for (int i = 0; i < ctx->defer_count; i++) {
|
||||
if (ctx->defer_stack[i].depth != depth)
|
||||
continue;
|
||||
spl_defer_entry_t *e = &ctx->defer_stack[i];
|
||||
|
||||
spl_val_t skip_addr = e->body_start - 1;
|
||||
spl_val_t skip_target = e->jmp_exit + 1;
|
||||
emit_patch(&ctx->emit, skip_addr, skip_target - skip_addr - 1);
|
||||
}
|
||||
|
||||
for (int i = ctx->defer_count - 1; i >= 0; i--) {
|
||||
if (ctx->defer_stack[i].depth != depth)
|
||||
continue;
|
||||
spl_defer_entry_t *e = &ctx->defer_stack[i];
|
||||
|
||||
spl_val_t here = vec_size(ctx->prog.insns);
|
||||
spl_val_t jmp_offset = (spl_val_t)((isize)e->body_start - (isize)here - 1);
|
||||
emit_jmp(&ctx->emit, jmp_offset);
|
||||
|
||||
if (e->jmp_exit > 0)
|
||||
emit_patch(&ctx->emit, e->jmp_exit, here + 1 - e->jmp_exit - 1);
|
||||
}
|
||||
|
||||
int new_count = 0;
|
||||
for (int i = 0; i < ctx->defer_count; i++) {
|
||||
if (ctx->defer_stack[i].depth != depth)
|
||||
ctx->defer_stack[new_count++] = ctx->defer_stack[i];
|
||||
}
|
||||
ctx->defer_count = new_count;
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
#ifndef __SPL_EMIT_H__
|
||||
#define __SPL_EMIT_H__
|
||||
|
||||
#include "../stage0/spl_ir.h"
|
||||
#include "spl_type.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* ============================================================
|
||||
* Frame allocator
|
||||
* ============================================================ */
|
||||
|
||||
typedef struct {
|
||||
int current_bytes;
|
||||
int peak_bytes;
|
||||
} spl_frame_alloc_t;
|
||||
|
||||
static inline void fa_init(spl_frame_alloc_t *fa) {
|
||||
fa->current_bytes = 0;
|
||||
fa->peak_bytes = 0;
|
||||
}
|
||||
|
||||
static inline int fa_alloc(spl_frame_alloc_t *fa, usize byte_size) {
|
||||
usize aligned = (byte_size + sizeof(spl_val_t) - 1) & ~(sizeof(spl_val_t) - 1);
|
||||
if (aligned < sizeof(spl_val_t))
|
||||
aligned = sizeof(spl_val_t);
|
||||
int offset = fa->current_bytes;
|
||||
fa->current_bytes += (int)aligned;
|
||||
if (fa->current_bytes > fa->peak_bytes)
|
||||
fa->peak_bytes = fa->current_bytes;
|
||||
return offset;
|
||||
}
|
||||
|
||||
int fa_alloc_type(spl_frame_alloc_t *fa, spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
static inline void fa_reset_to(spl_frame_alloc_t *fa, int mark) { fa->current_bytes = mark; }
|
||||
|
||||
static inline int fa_alloc_slots(spl_frame_alloc_t *fa, usize nslots) {
|
||||
return fa_alloc(fa, nslots * sizeof(spl_val_t));
|
||||
}
|
||||
|
||||
static inline int fa_alloc_temp(spl_frame_alloc_t *fa, usize nslots) {
|
||||
return fa_alloc_slots(fa, nslots);
|
||||
}
|
||||
|
||||
static inline void fa_free(spl_frame_alloc_t *fa, int mark) { fa->current_bytes = mark; }
|
||||
|
||||
/* ============================================================
|
||||
* Emit context
|
||||
* ============================================================ */
|
||||
|
||||
typedef struct {
|
||||
spl_val_t insn_idx;
|
||||
int func_idx;
|
||||
} spl_fixup_entry_t;
|
||||
|
||||
typedef struct {
|
||||
spl_prog_t *prog;
|
||||
spl_frame_alloc_t frame;
|
||||
VEC(spl_fixup_entry_t) fixups;
|
||||
} spl_emit_t;
|
||||
|
||||
void spl_emit_init(spl_emit_t *e, spl_prog_t *prog);
|
||||
void spl_emit_drop(spl_emit_t *e);
|
||||
|
||||
/* ============================================================
|
||||
* Layer 1 — single-instruction semantic wrappers
|
||||
* ============================================================ */
|
||||
|
||||
/* Stack ops */
|
||||
void emit_drop(spl_emit_t *e);
|
||||
void emit_dup(spl_emit_t *e);
|
||||
void emit_swap(spl_emit_t *e);
|
||||
void emit_rot(spl_emit_t *e);
|
||||
void emit_pick(spl_emit_t *e, int n);
|
||||
|
||||
/* Push */
|
||||
void emit_push_i32(spl_emit_t *e, int v);
|
||||
void emit_push_usize(spl_emit_t *e, usize v);
|
||||
void emit_push_u64(spl_emit_t *e, uint64_t v);
|
||||
void emit_push_f64(spl_emit_t *e, uint64_t v);
|
||||
void emit_push_ptr(spl_emit_t *e, spl_val_t v);
|
||||
void emit_push_type(spl_emit_t *e, uint16_t bt, spl_val_t v);
|
||||
|
||||
/* Load */
|
||||
void emit_load_ptr(spl_emit_t *e);
|
||||
void emit_load_usize(spl_emit_t *e);
|
||||
void emit_load_type(spl_emit_t *e, uint16_t bt);
|
||||
|
||||
/* Store */
|
||||
void emit_store_i32(spl_emit_t *e);
|
||||
void emit_store_ptr(spl_emit_t *e);
|
||||
void emit_store_usize(spl_emit_t *e);
|
||||
void emit_store_type(spl_emit_t *e, uint16_t bt);
|
||||
|
||||
/* Arithmetic */
|
||||
void emit_add_usize(spl_emit_t *e);
|
||||
void emit_add_u64(spl_emit_t *e);
|
||||
void emit_mul_u64(spl_emit_t *e);
|
||||
void emit_sub_usize(spl_emit_t *e);
|
||||
|
||||
/* Compare */
|
||||
void emit_ult_usize(spl_emit_t *e);
|
||||
|
||||
/* Branch */
|
||||
void emit_jmp(spl_emit_t *e, spl_val_t offset);
|
||||
spl_val_t emit_jmp_here(spl_emit_t *e);
|
||||
spl_val_t emit_bz_here(spl_emit_t *e);
|
||||
spl_val_t emit_bnz_here(spl_emit_t *e);
|
||||
void emit_patch(spl_emit_t *e, spl_val_t addr, spl_val_t target);
|
||||
void emit_patch_here(spl_emit_t *e, spl_val_t addr);
|
||||
|
||||
/* Address */
|
||||
void emit_laddr(spl_emit_t *e, int offset);
|
||||
void emit_gaddr(spl_emit_t *e, int idx);
|
||||
|
||||
/* Call */
|
||||
void emit_call(spl_emit_t *e, int nargs);
|
||||
void emit_ncall(spl_emit_t *e, int nargs);
|
||||
|
||||
/* Misc */
|
||||
void emit_alloc(spl_emit_t *e, int slots);
|
||||
void emit_binop(spl_emit_t *e, uint16_t op, uint16_t bt);
|
||||
void emit_dbg_void(spl_emit_t *e);
|
||||
void emit_dbg_usize(spl_emit_t *e);
|
||||
|
||||
/* Low-level emit (raw opcode + type + imm) */
|
||||
spl_val_t emit_raw(spl_emit_t *e, uint16_t opcode, uint16_t type, spl_val_t imm);
|
||||
|
||||
/* ============================================================
|
||||
* Layer 2 — semantic-level helpers
|
||||
* ============================================================ */
|
||||
|
||||
void emit_frame_copy(spl_emit_t *e, int dest_offset, usize nslots);
|
||||
void emit_copy_addr_to_addr(spl_emit_t *e, usize nslots, int depth);
|
||||
void emit_load_to_var(spl_emit_t *e, spl_type_ctx_t *tctx, int ptr_slot_offset, usize byte_offset,
|
||||
int data_type_idx, int var_offset);
|
||||
void emit_store_to_laddr(spl_emit_t *e, int offset, uint16_t type);
|
||||
void emit_ptr_add(spl_emit_t *e, usize byte_off);
|
||||
spl_val_t emit_call_with_fixup(spl_emit_t *e, int nargs, int func_idx);
|
||||
void emit_return(spl_emit_t *e, spl_type_ctx_t *tctx, int ret_type_idx);
|
||||
|
||||
/* Patch all fixups */
|
||||
void emit_patch_call_fixups(spl_emit_t *e, spl_prog_t *prog);
|
||||
|
||||
/* ============================================================
|
||||
* Layer 3 — compiler-level helpers (need spl_comp_t for tctx)
|
||||
* ============================================================ */
|
||||
|
||||
struct spl_comp;
|
||||
void spl_emit_ret(struct spl_comp *ctx, int ret_type_idx);
|
||||
void spl_emit_store_init(struct spl_comp *ctx, int var_offset, int var_type_idx);
|
||||
void spl_emit_defer(struct spl_comp *ctx);
|
||||
void spl_emit_defer_epilogue(struct spl_comp *ctx, int depth);
|
||||
|
||||
#endif /* __SPL_EMIT_H__ */
|
||||
1624
stage1/spl_expr.c
1624
stage1/spl_expr.c
File diff suppressed because it is too large
Load Diff
@@ -1,142 +0,0 @@
|
||||
/* spl_lex_util.c — Lexer utility functions */
|
||||
|
||||
#include "spl_lex_util.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
spl_tok_t *peek(spl_comp_t *ctx) { return &vec_at(ctx->toks, ctx->tok_idx); }
|
||||
|
||||
spl_tok_t *advance(spl_comp_t *ctx) {
|
||||
spl_tok_t *t = &vec_at(ctx->toks, ctx->tok_idx);
|
||||
if (t->type != TOK_EOF)
|
||||
ctx->tok_idx++;
|
||||
return t;
|
||||
}
|
||||
|
||||
int expect(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
spl_tok_t *tok = peek(ctx);
|
||||
if (tok->type == type) {
|
||||
advance(ctx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Extract token text for display (up to 40 chars) */
|
||||
char val_buf[64];
|
||||
usize display_len = tok->len < 40 ? tok->len : 40;
|
||||
memcpy(val_buf, tok->lexeme, display_len);
|
||||
val_buf[display_len] = '\0';
|
||||
/* Replace newlines with \n for display */
|
||||
for (usize i = 0; i < display_len; i++) {
|
||||
if (val_buf[i] == '\n')
|
||||
val_buf[i] = ' ';
|
||||
}
|
||||
|
||||
if (ctx->parse_context[0]) {
|
||||
spl_comp_err_tok(ctx, tok, "%s: expected %s, got %s '%s'", ctx->parse_context,
|
||||
spl_tok_type_name(type), spl_tok_type_name(tok->type), val_buf);
|
||||
} else {
|
||||
spl_comp_err_tok(ctx, tok, "expected %s, got %s '%s'", spl_tok_type_name(type),
|
||||
spl_tok_type_name(tok->type), val_buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int match(spl_comp_t *ctx, spl_tok_type_t type) {
|
||||
if (peek(ctx)->type == type) {
|
||||
advance(ctx);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void skip_nl(spl_comp_t *ctx) {
|
||||
while (peek(ctx)->type == TOK_ENDLINE)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
int spl_parse_int_literal(spl_comp_t *ctx, int *val) {
|
||||
int negate = 0;
|
||||
if (peek(ctx)->type == TOK_SUB) {
|
||||
negate = 1;
|
||||
advance(ctx);
|
||||
}
|
||||
if (peek(ctx)->type != TOK_INT_LITERAL) {
|
||||
if (negate)
|
||||
ctx->tok_idx--; /* un-consume the SUB token */
|
||||
return 0;
|
||||
}
|
||||
spl_tok_t *tok = advance(ctx);
|
||||
long long v = strtoll(tok->lexeme, NULL, 0);
|
||||
*val = negate ? -(int)v : (int)v;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *spl_tok_type_name(spl_tok_type_t type) {
|
||||
switch (type) {
|
||||
#define X(name, enum_name, dummy) \
|
||||
case enum_name: \
|
||||
return #name;
|
||||
KEYWORD_TABLE
|
||||
TOKEN_TABLE
|
||||
#undef X
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
void spl_tok_dump(spl_tok_t *tok) {
|
||||
if (!tok)
|
||||
return;
|
||||
|
||||
/* Extract token text for display (up to 40 chars) */
|
||||
char buf[64];
|
||||
usize display_len = tok->len < 40 ? tok->len : 40;
|
||||
memcpy(buf, tok->lexeme, display_len);
|
||||
buf[display_len] = '\0';
|
||||
/* Replace newlines with \n for display */
|
||||
for (usize i = 0; i < display_len; i++) {
|
||||
if (buf[i] == '\n')
|
||||
buf[i] = ' ';
|
||||
}
|
||||
|
||||
printf("%s:%zu:%zu %-20s '%s'", tok->fname ? tok->fname : "", tok->line, tok->col,
|
||||
spl_tok_type_name(tok->type), buf);
|
||||
|
||||
if (tok->type == TOK_INT_LITERAL) {
|
||||
printf(" [int]");
|
||||
} else if (tok->type == TOK_STRING_LITERAL) {
|
||||
printf(" [string]");
|
||||
} else if (tok->type == TOK_CHAR_LITERAL) {
|
||||
printf(" [char]");
|
||||
} else if (tok->type == TOK_FLOAT_LITERAL) {
|
||||
printf(" [float]");
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void spl_tok_vec_dump(spl_tok_vec_t *toks) {
|
||||
if (!toks)
|
||||
return;
|
||||
printf("=== TOKEN DUMP (%zu tokens) ===\n", vec_size(*toks));
|
||||
vec_for(*toks, i) {
|
||||
spl_tok_t *tok = &vec_at(*toks, i);
|
||||
printf("%4zu: ", i);
|
||||
spl_tok_dump(tok);
|
||||
}
|
||||
}
|
||||
|
||||
void spl_tok_vec_drop(spl_tok_vec_t *toks) {
|
||||
if (toks) {
|
||||
vec_free(*toks);
|
||||
}
|
||||
}
|
||||
|
||||
usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size) {
|
||||
if (!tok || !buf || buf_size == 0)
|
||||
return 0;
|
||||
usize nlen = tok->len < buf_size - 1 ? tok->len : buf_size - 1;
|
||||
memcpy(buf, tok->lexeme, nlen);
|
||||
buf[nlen] = '\0';
|
||||
return nlen;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/* spl_lex_util.h — Shared token helper utilities */
|
||||
#ifndef __SPL_LEX_UTIL_H__
|
||||
#define __SPL_LEX_UTIL_H__
|
||||
|
||||
#include "spl_comp.h"
|
||||
|
||||
/* Token stream access */
|
||||
spl_tok_t *peek(spl_comp_t *ctx);
|
||||
spl_tok_t *advance(spl_comp_t *ctx);
|
||||
|
||||
/* Token matching helpers */
|
||||
int expect(spl_comp_t *ctx, spl_tok_type_t type);
|
||||
int match(spl_comp_t *ctx, spl_tok_type_t type);
|
||||
void skip_nl(spl_comp_t *ctx);
|
||||
|
||||
/* Token introspection */
|
||||
const char *spl_tok_type_name(spl_tok_type_t type);
|
||||
void spl_tok_dump(spl_tok_t *tok);
|
||||
void spl_tok_vec_dump(spl_tok_vec_t *toks);
|
||||
void spl_tok_vec_drop(spl_tok_vec_t *toks);
|
||||
usize spl_tok_copy_name(spl_tok_t *tok, char *buf, usize buf_size);
|
||||
const char *spl_tok_loc_str(spl_comp_t *ctx);
|
||||
|
||||
/* Parse an integer literal value, handling optional '-' prefix for negatives.
|
||||
* Returns 1 on success (value in *val), 0 if current token isn't an integer.
|
||||
* On success, advances past all consumed tokens. On failure, does not advance. */
|
||||
int spl_parse_int_literal(spl_comp_t *ctx, int *val);
|
||||
|
||||
#endif /* __SPL_LEX_UTIL_H__ */
|
||||
@@ -2,130 +2,7 @@
|
||||
#ifndef __SPL_LEXER_H__
|
||||
#define __SPL_LEXER_H__
|
||||
|
||||
#include "../stage0/spl_ir.h"
|
||||
|
||||
/* clang-format off */
|
||||
#define KEYWORD_TABLE \
|
||||
X(as , KW_AS , SPL_V0) \
|
||||
X(asm , KW_ASM , SPL_V0) \
|
||||
X(bool , KW_BOOL , SPL_V0) \
|
||||
X(break , KW_BREAK , SPL_V0) \
|
||||
X(catch , KW_CATCH , SPL_V0) \
|
||||
X(comptime , KW_COMPTIME , SPL_V0) \
|
||||
X(const , KW_CONST , SPL_V0) \
|
||||
X(continue , KW_CONTINUE , SPL_V0) \
|
||||
X(defer , KW_DEFER , SPL_V0) \
|
||||
X(else , KW_ELSE , SPL_V0) \
|
||||
X(enum , KW_ENUM , SPL_V0) \
|
||||
X(extern , KW_EXTERN , SPL_V0) \
|
||||
X(false , KW_FALSE , SPL_V0) \
|
||||
X(fn , KW_FN , SPL_V0) \
|
||||
X(for , KW_FOR , SPL_V0) \
|
||||
X(if , KW_IF , SPL_V0) \
|
||||
X(loop , KW_LOOP , SPL_V0) \
|
||||
X(match , KW_MATCH , SPL_V0) \
|
||||
X(null , KW_NULL , SPL_V0) \
|
||||
X(pub , KW_PUB , SPL_V0) \
|
||||
X(ret , KW_RET , SPL_V0) \
|
||||
X(struct , KW_STRUCT , SPL_V0) \
|
||||
X(test , KW_TEST , SPL_V0) \
|
||||
X(true , KW_TRUE , SPL_V0) \
|
||||
X(try , KW_TRY , SPL_V0) \
|
||||
X(type , KW_TYPE , SPL_V0) \
|
||||
X(union , KW_UNION , SPL_V0) \
|
||||
X(var , KW_VAR , SPL_V0) \
|
||||
X(void , KW_VOID , SPL_V0) \
|
||||
X(while , KW_WHILE , SPL_V0) \
|
||||
X(_ , KW_ANY , SPL_V0) \
|
||||
// KEYWORD_TABLE
|
||||
|
||||
#define TOKEN_TABLE \
|
||||
X(unknown , TOK_UNKNOWN , SPL_V0 ) \
|
||||
X(EOF , TOK_EOF , SPL_V0 ) \
|
||||
X(blank , TOK_BLANK , SPL_V0 ) \
|
||||
X(endline , TOK_ENDLINE , SPL_V0 ) \
|
||||
X("#" , TOK_SHARP , SPL_V0 ) \
|
||||
X("@" , TOK_AT , SPL_V0 ) \
|
||||
X("==" , TOK_EQ , SPL_V0 ) \
|
||||
X("=" , TOK_ASSIGN , SPL_V0 ) \
|
||||
X("+=" , TOK_ASSIGN_ADD , SPL_V0 ) \
|
||||
X("+" , TOK_ADD , SPL_V0 ) \
|
||||
X("-=" , TOK_ASSIGN_SUB , SPL_V0 ) \
|
||||
X("->" , TOK_RIGHT_ARRAY , SPL_V0 ) \
|
||||
X("<-" , TOK_LEFT_ARRAY , SPL_V0 ) \
|
||||
X("-" , TOK_SUB , SPL_V0 ) \
|
||||
X("*=" , TOK_ASSIGN_MUL , SPL_V0 ) \
|
||||
X("*" , TOK_MUL , SPL_V0 ) \
|
||||
X("/=" , TOK_ASSIGN_DIV , SPL_V0 ) \
|
||||
X("/" , TOK_DIV , SPL_V0 ) \
|
||||
X("//" , TOK_LINE_COMMENT , SPL_V0 ) \
|
||||
X("/* */" , TOK_BLOCK_COMMENT , SPL_V0 ) \
|
||||
X("%=" , TOK_ASSIGN_MOD , SPL_V0 ) \
|
||||
X("%" , TOK_MOD , SPL_V0 ) \
|
||||
X("&&" , TOK_AND_AND , SPL_V0 ) \
|
||||
X("&=" , TOK_ASSIGN_AND , SPL_V0 ) \
|
||||
X("&" , TOK_AND , SPL_V0 ) \
|
||||
X("||" , TOK_OR_OR , SPL_V0 ) \
|
||||
X("|=" , TOK_ASSIGN_OR , SPL_V0 ) \
|
||||
X("|" , TOK_OR , SPL_V0 ) \
|
||||
X("^=" , TOK_ASSIGN_XOR , SPL_V0 ) \
|
||||
X("^" , TOK_XOR , SPL_V0 ) \
|
||||
X("<<=" , TOK_ASSIGN_L_SH , SPL_V0 ) \
|
||||
X("<<" , TOK_L_SH , SPL_V0 ) \
|
||||
X("<=" , TOK_LE , SPL_V0 ) \
|
||||
X("<" , TOK_LT , SPL_V0 ) \
|
||||
X(">>=" , TOK_ASSIGN_R_SH , SPL_V0 ) \
|
||||
X(">>" , TOK_R_SH , SPL_V0 ) \
|
||||
X(">=" , TOK_GE , SPL_V0 ) \
|
||||
X(">" , TOK_GT , SPL_V0 ) \
|
||||
X("!" , TOK_NOT , SPL_V0 ) \
|
||||
X("!=" , TOK_NEQ , SPL_V0 ) \
|
||||
X("~" , TOK_BIT_NOT , SPL_V0 ) \
|
||||
X("[" , TOK_L_BRACKET , SPL_V0 ) \
|
||||
X("]" , TOK_R_BRACKET , SPL_V0 ) \
|
||||
X("(" , TOK_L_PAREN , SPL_V0 ) \
|
||||
X(")" , TOK_R_PAREN , SPL_V0 ) \
|
||||
X("{" , TOK_L_BRACE , SPL_V0 ) \
|
||||
X("}" , TOK_R_BRACE , SPL_V0 ) \
|
||||
X(";" , TOK_SEMICOLON , SPL_V0 ) \
|
||||
X("," , TOK_COMMA , SPL_V0 ) \
|
||||
X(":" , TOK_COLON , SPL_V0 ) \
|
||||
X(":=" , TOK_COLON_ASSIGN , SPL_V0 ) \
|
||||
X("." , TOK_DOT , SPL_V0 ) \
|
||||
X(".." , TOK_RANGE , SPL_V0 ) \
|
||||
X("..." , TOK_ELLIPSIS , SPL_V0 ) \
|
||||
X("?" , TOK_COND , SPL_V0 ) \
|
||||
X(ident , TOK_IDENT , SPL_V0 ) \
|
||||
X(int , TOK_INT_LITERAL , SPL_V0 ) \
|
||||
X(float , TOK_FLOAT_LITERAL , SPL_V0 ) \
|
||||
X(char , TOK_CHAR_LITERAL , SPL_V0 ) \
|
||||
X(string , TOK_STRING_LITERAL , SPL_V0 ) \
|
||||
// TOKEN_TABLE
|
||||
/* clang-format on */
|
||||
|
||||
/* spl_tok_type_t — KEYWORD_TABLE + TOKEN_TABLE 展开 */
|
||||
/* clang-format off */
|
||||
typedef enum {
|
||||
#define X(name, enum_name, dummy) enum_name,
|
||||
KEYWORD_TABLE
|
||||
#undef X
|
||||
#define X(name, enum_name, dummy) enum_name,
|
||||
TOKEN_TABLE
|
||||
#undef X
|
||||
} spl_tok_type_t;
|
||||
/* clang-format on */
|
||||
|
||||
typedef struct {
|
||||
spl_tok_type_t type;
|
||||
const char *lexeme;
|
||||
usize len; /* token length in bytes */
|
||||
const char *fname;
|
||||
usize offset;
|
||||
usize line;
|
||||
usize col;
|
||||
} spl_tok_t;
|
||||
|
||||
typedef VEC(spl_tok_t) spl_tok_vec_t;
|
||||
#include "spl_tok.h"
|
||||
|
||||
/* Lexer entry point */
|
||||
spl_tok_vec_t spl_lex(const char *source, const char *fname);
|
||||
|
||||
@@ -1,753 +0,0 @@
|
||||
/* spl_parser.c — Top-level parser: function declarations, type declarations, etc. */
|
||||
|
||||
#include "spl_comp.h"
|
||||
#include "spl_lex_util.h"
|
||||
#include <string.h>
|
||||
|
||||
/* ============================================================
|
||||
* Parse function definition
|
||||
* ============================================================ */
|
||||
|
||||
enum { MAX_PARAMS = 64 };
|
||||
|
||||
static int parse_params_decl(spl_comp_t *ctx, char pnames[][256], int ptypes[]) {
|
||||
int nparams = 0;
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||
{
|
||||
usize saved = ctx->tok_idx;
|
||||
spl_tok_t *ptok = advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (ptok->type == KW_VOID && peek(ctx)->type == TOK_R_PAREN) {
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
return 0;
|
||||
}
|
||||
ctx->tok_idx = saved;
|
||||
}
|
||||
while (1) {
|
||||
if (peek(ctx)->type == TOK_ELLIPSIS) {
|
||||
advance(ctx);
|
||||
break;
|
||||
}
|
||||
spl_tok_t *pname = advance(ctx);
|
||||
spl_tok_copy_name(pname, pnames[nparams], 256);
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COLON) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
ptypes[nparams] = spl_type_parse(&ctx->tctx, ctx);
|
||||
} else {
|
||||
ptypes[nparams] = spl_type_basic(&ctx->tctx, SPL_I32);
|
||||
}
|
||||
nparams++;
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect(ctx, TOK_R_PAREN);
|
||||
return nparams;
|
||||
}
|
||||
|
||||
static int parse_fn_body(spl_comp_t *ctx, const char *fn_name, int ret_type_idx, int nparams,
|
||||
char pnames[][256], int ptypes[], int is_pub) {
|
||||
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "function '%s'", fn_name);
|
||||
int fi = spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 0, is_pub);
|
||||
|
||||
{
|
||||
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
||||
f->param_type_indices = calloc(nparams, sizeof(int));
|
||||
f->param_names = calloc(nparams, sizeof(char *));
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
f->param_type_indices[i] = ptypes[i];
|
||||
f->param_names[i] = strdup(pnames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->current_func_idx = fi;
|
||||
ctx->current_ret_type_idx = ret_type_idx;
|
||||
fa_init(&ctx->emit.frame);
|
||||
|
||||
spl_push_scope(ctx);
|
||||
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
spl_declare_var(ctx, pnames[i], ptypes[i], 0);
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
|
||||
spl_val_t alloc_addr = vec_size(ctx->prog.insns);
|
||||
emit_alloc(&ctx->emit, 0);
|
||||
|
||||
while (!ctx->has_error && peek(ctx)->type != TOK_R_BRACE && peek(ctx)->type != TOK_EOF) {
|
||||
spl_parse_stmt(ctx);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
/* Error recovery: skip to matching } if has_error caused early exit */
|
||||
if (ctx->has_error) {
|
||||
int depth = 1;
|
||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
spl_tok_type_t tt = peek(ctx)->type;
|
||||
if (tt == TOK_L_BRACE)
|
||||
depth++;
|
||||
else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
break;
|
||||
} else if (tt == TOK_EOF)
|
||||
break;
|
||||
advance(ctx);
|
||||
}
|
||||
if (peek(ctx)->type == TOK_R_BRACE)
|
||||
advance(ctx);
|
||||
else
|
||||
return 0;
|
||||
} else {
|
||||
if (!expect(ctx, TOK_R_BRACE))
|
||||
return 0;
|
||||
}
|
||||
|
||||
int total_phys_slots = 0;
|
||||
for (int i = 0; i < nparams; i++) {
|
||||
usize psz = spl_type_size(&ctx->tctx, ptypes[i]);
|
||||
total_phys_slots += (int)((psz + sizeof(spl_val_t) - 1) / sizeof(spl_val_t));
|
||||
}
|
||||
if (spl_type_needs_multi_slot(&ctx->tctx, ret_type_idx)) {
|
||||
usize min_bytes = spl_type_slot_count(&ctx->tctx, ret_type_idx) * sizeof(spl_val_t);
|
||||
if ((usize)ctx->emit.frame.peak_bytes < min_bytes)
|
||||
ctx->emit.frame.peak_bytes = (int)min_bytes;
|
||||
}
|
||||
int alloc_slots = ctx->emit.frame.peak_bytes / (int)sizeof(spl_val_t) - total_phys_slots;
|
||||
if (alloc_slots < 0 || alloc_slots > 65536) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"WARN: parse_fn_body: suspicious alloc_slots=%d (peak_bytes=%d, phys_slots=%d)\n",
|
||||
alloc_slots, ctx->emit.frame.peak_bytes, total_phys_slots);
|
||||
}
|
||||
emit_patch(&ctx->emit, alloc_addr, alloc_slots);
|
||||
}
|
||||
|
||||
spl_emit_defer_epilogue(ctx, ctx->scope_depth);
|
||||
spl_pop_scope(ctx);
|
||||
|
||||
emit_return(&ctx->emit, &ctx->tctx, ctx->current_ret_type_idx);
|
||||
{
|
||||
spl_func_info_t *f = &vec_at(ctx->funcs, fi);
|
||||
spl_prog_end_func(&ctx->prog, f->func_idx);
|
||||
}
|
||||
ctx->current_func_idx = -1;
|
||||
ctx->current_ret_type_idx = -1;
|
||||
return fi;
|
||||
}
|
||||
|
||||
static void parse_fn_decl(spl_comp_t *ctx, int is_extern, int is_pub) {
|
||||
advance(ctx); /* fn */
|
||||
skip_nl(ctx);
|
||||
|
||||
spl_tok_t *fname_tok = advance(ctx);
|
||||
char fn_name[256];
|
||||
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
|
||||
skip_nl(ctx);
|
||||
if (!expect(ctx, TOK_L_PAREN))
|
||||
return;
|
||||
|
||||
char pnames[MAX_PARAMS][256];
|
||||
int ptypes[MAX_PARAMS];
|
||||
int nparams = parse_params_decl(ctx, pnames, ptypes);
|
||||
skip_nl(ctx);
|
||||
|
||||
int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||
ret_type_idx = spl_type_parse(&ctx->tctx, ctx);
|
||||
if (ret_type_idx < 0)
|
||||
ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
int fi;
|
||||
|
||||
if (is_extern) {
|
||||
fi = spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 1, is_pub);
|
||||
spl_ensure_native(ctx, fn_name);
|
||||
if (ctx->tctx.current_type_idx == 0)
|
||||
spl_type_add_method(&ctx->tctx, 0, fn_name, fi);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||
advance(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (peek(ctx)->type == TOK_SEMICOLON) {
|
||||
advance(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
|
||||
fi = parse_fn_body(ctx, fn_name, ret_type_idx, nparams, pnames, ptypes, is_pub);
|
||||
if (ctx->tctx.current_type_idx == 0)
|
||||
spl_type_add_method(&ctx->tctx, 0, fn_name, fi);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Parse method declaration inside a type body
|
||||
* ============================================================ */
|
||||
|
||||
static void parse_method_decl(spl_comp_t *ctx, int container_type_idx) {
|
||||
advance(ctx); /* fn */
|
||||
skip_nl(ctx);
|
||||
spl_tok_t *mname_tok = advance(ctx);
|
||||
char mname[256];
|
||||
spl_tok_copy_name(mname_tok, mname, sizeof(mname));
|
||||
const char *cname = spl_type_name(&ctx->tctx, container_type_idx);
|
||||
char qualified[512];
|
||||
snprintf(qualified, sizeof(qualified), "%s.%s", cname ? cname : "anon", mname);
|
||||
|
||||
skip_nl(ctx);
|
||||
if (!expect(ctx, TOK_L_PAREN))
|
||||
return;
|
||||
|
||||
char pnames[MAX_PARAMS][256];
|
||||
int ptypes[MAX_PARAMS];
|
||||
int nparams = parse_params_decl(ctx, pnames, ptypes);
|
||||
skip_nl(ctx);
|
||||
|
||||
int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||
ret_type_idx = spl_type_parse(&ctx->tctx, ctx);
|
||||
if (ret_type_idx < 0)
|
||||
ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
int fi = parse_fn_body(ctx, qualified, ret_type_idx, nparams, pnames, ptypes, 0);
|
||||
|
||||
spl_type_add_method(&ctx->tctx, container_type_idx, mname, fi);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Parse type container body (shared for struct, union, enum)
|
||||
* ============================================================ */
|
||||
|
||||
static void parse_type_body(spl_comp_t *ctx, int container_type_idx, int is_enum) {
|
||||
if (peek(ctx)->type != TOK_L_BRACE)
|
||||
return;
|
||||
advance(ctx); /* { */
|
||||
|
||||
int saved_current = ctx->tctx.current_type_idx;
|
||||
ctx->tctx.current_type_idx = container_type_idx;
|
||||
|
||||
/* === Pass 1: Parse all nested type declarations first === */
|
||||
{
|
||||
usize saved = ctx->tok_idx;
|
||||
int depth = 1;
|
||||
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
spl_tok_type_t tt = peek(ctx)->type;
|
||||
if (tt == TOK_L_BRACE) {
|
||||
depth++;
|
||||
advance(ctx);
|
||||
} else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
break;
|
||||
advance(ctx);
|
||||
} else if (tt == KW_TYPE && depth == 1) {
|
||||
parse_type_decl(ctx);
|
||||
} else if (tt == TOK_EOF) {
|
||||
break;
|
||||
} else {
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
ctx->tok_idx = saved;
|
||||
}
|
||||
|
||||
/* === Pre-register method names for forward references === */
|
||||
{
|
||||
const char *cname = spl_type_name(&ctx->tctx, container_type_idx);
|
||||
if (cname) {
|
||||
usize saved = ctx->tok_idx;
|
||||
int depth = 1;
|
||||
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
spl_tok_type_t tt = peek(ctx)->type;
|
||||
if (tt == TOK_L_BRACE) {
|
||||
depth++;
|
||||
advance(ctx);
|
||||
} else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
break;
|
||||
advance(ctx);
|
||||
} else if (tt == KW_FN && depth == 1) {
|
||||
advance(ctx); /* fn */
|
||||
skip_nl(ctx);
|
||||
spl_tok_t *name_tok = advance(ctx);
|
||||
char mname[256];
|
||||
spl_tok_copy_name(name_tok, mname, sizeof(mname));
|
||||
|
||||
/* Count parameters for forward reference */
|
||||
int pcount = 0;
|
||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||
advance(ctx); /* ( */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type != TOK_R_PAREN) {
|
||||
pcount = 1;
|
||||
for (;;) {
|
||||
advance(ctx); /* skip param name or type token */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_R_PAREN)
|
||||
break;
|
||||
if (peek(ctx)->type == TOK_COMMA) {
|
||||
advance(ctx); /* , */
|
||||
skip_nl(ctx);
|
||||
pcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
advance(ctx); /* ) */
|
||||
}
|
||||
|
||||
char qualified[512];
|
||||
snprintf(qualified, sizeof(qualified), "%s.%s", cname, mname);
|
||||
int fi = spl_declare_func(ctx, qualified, -1, pcount, 0, 0);
|
||||
spl_type_add_method(&ctx->tctx, container_type_idx, mname, fi);
|
||||
} else if (tt == TOK_EOF) {
|
||||
break;
|
||||
} else {
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
ctx->tok_idx = saved;
|
||||
}
|
||||
}
|
||||
|
||||
/* === Pass 2: Parse fields/variants and methods === */
|
||||
{
|
||||
int depth = 1;
|
||||
while (!ctx->has_error && depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
skip_nl(ctx);
|
||||
spl_tok_type_t tt = peek(ctx)->type;
|
||||
if (tt == TOK_L_BRACE) {
|
||||
depth++;
|
||||
advance(ctx);
|
||||
} else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
advance(ctx);
|
||||
break;
|
||||
}
|
||||
advance(ctx);
|
||||
} else if (tt == KW_TYPE && depth == 1) {
|
||||
parse_type_decl(ctx);
|
||||
} else if (tt == KW_VAR && depth == 1 && !is_enum) {
|
||||
advance(ctx); /* var */
|
||||
skip_nl(ctx);
|
||||
spl_tok_t *ftok = advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COLON) {
|
||||
advance(ctx); /* : */
|
||||
skip_nl(ctx);
|
||||
int ftype = spl_type_parse(&ctx->tctx, ctx);
|
||||
char fname[256];
|
||||
spl_tok_copy_name(ftok, fname, sizeof(fname));
|
||||
if (ftype >= 0)
|
||||
spl_type_add_var(&ctx->tctx, container_type_idx, fname, ftype);
|
||||
}
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
|
||||
advance(ctx);
|
||||
} else if (tt == TOK_IDENT && depth == 1) {
|
||||
if (is_enum) {
|
||||
spl_tok_t *vtok = advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COLON) {
|
||||
advance(ctx); /* : */
|
||||
skip_nl(ctx);
|
||||
int dtype = spl_type_parse(&ctx->tctx, ctx);
|
||||
char vname[256];
|
||||
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
||||
spl_type_add_variant(&ctx->tctx, container_type_idx, vname,
|
||||
dtype >= 0 ? dtype : -1);
|
||||
} else if (tt == TOK_EOF) {
|
||||
break;
|
||||
} else {
|
||||
char vname[256];
|
||||
spl_tok_copy_name(vtok, vname, sizeof(vname));
|
||||
spl_type_add_variant(&ctx->tctx, container_type_idx, vname, -1);
|
||||
}
|
||||
} else {
|
||||
spl_tok_t *ftok = advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_COLON) {
|
||||
advance(ctx); /* : */
|
||||
skip_nl(ctx);
|
||||
int ftype = spl_type_parse(&ctx->tctx, ctx);
|
||||
char fname[256];
|
||||
spl_tok_copy_name(ftok, fname, sizeof(fname));
|
||||
if (ftype >= 0)
|
||||
spl_type_add_field(&ctx->tctx, container_type_idx, fname, ftype);
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON || peek(ctx)->type == TOK_COMMA)
|
||||
advance(ctx);
|
||||
} else if (tt == KW_FN && depth == 1) {
|
||||
spl_type_compute_layout(&ctx->tctx, container_type_idx);
|
||||
parse_method_decl(ctx, container_type_idx);
|
||||
} else {
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx->tctx.current_type_idx = saved_current;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Parse type declaration
|
||||
* ============================================================ */
|
||||
|
||||
void parse_type_decl(spl_comp_t *ctx) {
|
||||
advance(ctx); /* type */
|
||||
spl_tok_t *name_tok = advance(ctx);
|
||||
char tname[256];
|
||||
spl_tok_copy_name(name_tok, tname, sizeof(tname));
|
||||
snprintf(ctx->parse_context, sizeof(ctx->parse_context), "type '%s'", tname);
|
||||
|
||||
skip_nl(ctx);
|
||||
if (!expect(ctx, TOK_ASSIGN))
|
||||
return;
|
||||
skip_nl(ctx);
|
||||
|
||||
int parent_type_idx = ctx->tctx.current_type_idx;
|
||||
|
||||
/* Check if already pre-registered in Pass 0 */
|
||||
int existing = spl_type_resolve(&ctx->tctx, tname);
|
||||
|
||||
if (peek(ctx)->type == KW_STRUCT) {
|
||||
advance(ctx);
|
||||
int ti;
|
||||
if (existing >= 0)
|
||||
ti = existing;
|
||||
else
|
||||
ti = spl_type_struct(&ctx->tctx, tname);
|
||||
if (parent_type_idx >= 0 && existing < 0)
|
||||
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||
skip_nl(ctx);
|
||||
parse_type_body(ctx, ti, 0);
|
||||
} else if (peek(ctx)->type == KW_UNION) {
|
||||
advance(ctx);
|
||||
int ti;
|
||||
if (existing >= 0)
|
||||
ti = existing;
|
||||
else
|
||||
ti = spl_type_union(&ctx->tctx, tname);
|
||||
if (parent_type_idx >= 0 && existing < 0)
|
||||
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||
skip_nl(ctx);
|
||||
parse_type_body(ctx, ti, 1);
|
||||
} else if (peek(ctx)->type == KW_ENUM) {
|
||||
advance(ctx);
|
||||
int ti;
|
||||
if (existing >= 0)
|
||||
ti = existing;
|
||||
else
|
||||
ti = spl_type_enum(&ctx->tctx, tname);
|
||||
if (parent_type_idx >= 0 && existing < 0)
|
||||
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||
skip_nl(ctx);
|
||||
parse_type_body(ctx, ti, 1);
|
||||
} else if (peek(ctx)->type == TOK_IDENT ||
|
||||
(peek(ctx)->type >= KW_AS && peek(ctx)->type <= KW_ANY)) {
|
||||
int base = spl_type_parse(&ctx->tctx, ctx);
|
||||
if (base >= 0) {
|
||||
spl_type_alias(&ctx->tctx, tname, base);
|
||||
}
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Pre-registration pass: register all names before filling bodies
|
||||
* ============================================================ */
|
||||
|
||||
/* Skip a { ... } function body */
|
||||
static void skip_fn_body(spl_comp_t *ctx) {
|
||||
int depth = 1;
|
||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
spl_tok_type_t tt = peek(ctx)->type;
|
||||
if (tt == TOK_L_BRACE)
|
||||
depth++;
|
||||
else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
break;
|
||||
} else if (tt == TOK_EOF)
|
||||
break;
|
||||
advance(ctx);
|
||||
}
|
||||
if (peek(ctx)->type == TOK_R_BRACE)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* Pre-register a type name (and nested type names), skip the body */
|
||||
static void pre_register_type_decl(spl_comp_t *ctx) {
|
||||
advance(ctx); /* type */
|
||||
spl_tok_t *name_tok = advance(ctx);
|
||||
char tname[256];
|
||||
spl_tok_copy_name(name_tok, tname, sizeof(tname));
|
||||
skip_nl(ctx);
|
||||
if (!expect(ctx, TOK_ASSIGN))
|
||||
return;
|
||||
skip_nl(ctx);
|
||||
|
||||
int parent_type_idx = ctx->tctx.current_type_idx;
|
||||
|
||||
int ti = -1;
|
||||
if (peek(ctx)->type == KW_STRUCT) {
|
||||
advance(ctx);
|
||||
ti = spl_type_struct(&ctx->tctx, tname);
|
||||
} else if (peek(ctx)->type == KW_ENUM) {
|
||||
advance(ctx);
|
||||
ti = spl_type_enum(&ctx->tctx, tname);
|
||||
} else if (peek(ctx)->type == KW_UNION) {
|
||||
advance(ctx);
|
||||
ti = spl_type_union(&ctx->tctx, tname);
|
||||
}
|
||||
|
||||
if (ti >= 0) {
|
||||
if (parent_type_idx >= 0)
|
||||
spl_type_add_nested(&ctx->tctx, parent_type_idx, tname, ti);
|
||||
|
||||
/* Register nested type names inside the body */
|
||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
||||
int saved_current = ctx->tctx.current_type_idx;
|
||||
ctx->tctx.current_type_idx = ti;
|
||||
int depth = 1;
|
||||
advance(ctx); /* { */
|
||||
while (depth > 0 && ctx->tok_idx < vec_size(ctx->toks)) {
|
||||
spl_tok_type_t tt = peek(ctx)->type;
|
||||
if (tt == TOK_L_BRACE) {
|
||||
depth++;
|
||||
advance(ctx);
|
||||
} else if (tt == TOK_R_BRACE) {
|
||||
depth--;
|
||||
if (depth == 0)
|
||||
break;
|
||||
advance(ctx);
|
||||
} else if (tt == KW_TYPE && depth == 1) {
|
||||
pre_register_type_decl(ctx);
|
||||
} else if (tt == TOK_EOF) {
|
||||
break;
|
||||
} else {
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
if (peek(ctx)->type == TOK_R_BRACE)
|
||||
advance(ctx);
|
||||
ctx->tctx.current_type_idx = saved_current;
|
||||
}
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* Pre-register a function name and signature, skip the body */
|
||||
static void pre_register_fn_decl(spl_comp_t *ctx, int is_extern) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
spl_tok_t *fname_tok = advance(ctx);
|
||||
char fn_name[256];
|
||||
spl_tok_copy_name(fname_tok, fn_name, sizeof(fn_name));
|
||||
skip_nl(ctx);
|
||||
if (!expect(ctx, TOK_L_PAREN))
|
||||
return;
|
||||
|
||||
char pnames[MAX_PARAMS][256];
|
||||
int ptypes[MAX_PARAMS];
|
||||
int nparams = parse_params_decl(ctx, pnames, ptypes);
|
||||
skip_nl(ctx);
|
||||
|
||||
int ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||
if (peek(ctx)->type != TOK_SEMICOLON && peek(ctx)->type != TOK_L_BRACE) {
|
||||
ret_type_idx = spl_type_parse(&ctx->tctx, ctx);
|
||||
if (ret_type_idx < 0)
|
||||
ret_type_idx = spl_type_basic(&ctx->tctx, SPL_VOID);
|
||||
skip_nl(ctx);
|
||||
}
|
||||
|
||||
if (is_extern) {
|
||||
spl_declare_func(ctx, fn_name, ret_type_idx, nparams, 1, 0);
|
||||
spl_ensure_native(ctx, fn_name);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||
advance(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (peek(ctx)->type == TOK_SEMICOLON) {
|
||||
advance(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
|
||||
if (peek(ctx)->type == TOK_L_BRACE) {
|
||||
skip_fn_body(ctx);
|
||||
}
|
||||
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_SEMICOLON)
|
||||
advance(ctx);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Parse top-level program
|
||||
* ============================================================ */
|
||||
|
||||
void spl_parse_prog(spl_comp_t *ctx) {
|
||||
/* Pass 0: Pre-register all type names, function signatures, and variables */
|
||||
{
|
||||
usize saved = ctx->tok_idx;
|
||||
while (!ctx->has_error && peek(ctx)->type != TOK_EOF) {
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_EOF)
|
||||
break;
|
||||
|
||||
switch (peek(ctx)->type) {
|
||||
case KW_TYPE:
|
||||
pre_register_type_decl(ctx);
|
||||
break;
|
||||
case KW_FN:
|
||||
pre_register_fn_decl(ctx, 0);
|
||||
break;
|
||||
case KW_PUB: {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == KW_FN)
|
||||
pre_register_fn_decl(ctx, 0);
|
||||
else if (peek(ctx)->type == KW_TYPE)
|
||||
pre_register_type_decl(ctx);
|
||||
break;
|
||||
}
|
||||
case TOK_AT:
|
||||
case TOK_SHARP: {
|
||||
int tt = peek(ctx)->type;
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (tt == TOK_AT) {
|
||||
if (peek(ctx)->type == KW_EXTERN) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
advance(ctx);
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_R_PAREN)
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (peek(ctx)->type == TOK_L_BRACKET) {
|
||||
advance(ctx);
|
||||
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
||||
advance(ctx);
|
||||
if (peek(ctx)->type == TOK_R_BRACKET)
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == KW_FN)
|
||||
pre_register_fn_decl(ctx, 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Skip unrecognized tokens (stmts, etc.) */
|
||||
advance(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->tok_idx = saved;
|
||||
}
|
||||
|
||||
/* Pass 1: Fill all bodies */
|
||||
while (!ctx->has_error && peek(ctx)->type != TOK_EOF) {
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_EOF)
|
||||
break;
|
||||
|
||||
switch (peek(ctx)->type) {
|
||||
case KW_FN:
|
||||
parse_fn_decl(ctx, 0, 0);
|
||||
break;
|
||||
case KW_TYPE:
|
||||
parse_type_decl(ctx);
|
||||
break;
|
||||
case KW_PUB: {
|
||||
advance(ctx); /* skip pub */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == KW_FN)
|
||||
parse_fn_decl(ctx, 0, 1);
|
||||
else if (peek(ctx)->type == KW_TYPE)
|
||||
parse_type_decl(ctx);
|
||||
break;
|
||||
}
|
||||
case TOK_AT:
|
||||
case TOK_SHARP: {
|
||||
int tt = peek(ctx)->type;
|
||||
advance(ctx); /* @ or # */
|
||||
skip_nl(ctx);
|
||||
if (tt == TOK_AT) {
|
||||
if (peek(ctx)->type == KW_EXTERN) {
|
||||
advance(ctx); /* extern */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_L_PAREN) {
|
||||
advance(ctx); /* ( */
|
||||
skip_nl(ctx);
|
||||
advance(ctx); /* target name (e.g. "vm") */
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == TOK_R_PAREN)
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (peek(ctx)->type == TOK_L_BRACKET) {
|
||||
advance(ctx);
|
||||
while (peek(ctx)->type != TOK_R_BRACKET && peek(ctx)->type != TOK_EOF)
|
||||
advance(ctx);
|
||||
if (peek(ctx)->type == TOK_R_BRACKET)
|
||||
advance(ctx);
|
||||
}
|
||||
}
|
||||
skip_nl(ctx);
|
||||
if (peek(ctx)->type == KW_FN)
|
||||
parse_fn_decl(ctx, 1, 0);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
usize prev = ctx->tok_idx;
|
||||
spl_parse_stmt(ctx);
|
||||
if (ctx->tok_idx == prev)
|
||||
advance(ctx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Recompute all type layouts now that forward references are resolved */
|
||||
for (int i = 0; i < (int)vec_size(ctx->tctx.types); i++) {
|
||||
spl_type_compute_layout(&ctx->tctx, i);
|
||||
}
|
||||
}
|
||||
1156
stage1/spl_stmt.c
1156
stage1/spl_stmt.c
File diff suppressed because it is too large
Load Diff
122
stage1/spl_tok.h
Normal file
122
stage1/spl_tok.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/* spl_tok.h — Token type definitions (extracted from spl_lexer.h) */
|
||||
#ifndef __SPL_TOK_H__
|
||||
#define __SPL_TOK_H__
|
||||
|
||||
#include "../stage0/include/utils.h"
|
||||
|
||||
/* clang-format off */
|
||||
#define KEYWORD_TABLE \
|
||||
X(as , KW_AS , SPL_V0) \
|
||||
X(bool , KW_BOOL , SPL_V0) \
|
||||
X(break , KW_BREAK , SPL_V0) \
|
||||
X(comptime , KW_COMPTIME , SPL_V0) \
|
||||
X(const , KW_CONST , SPL_V0) \
|
||||
X(continue , KW_CONTINUE , SPL_V0) \
|
||||
X(defer , KW_DEFER , SPL_V0) \
|
||||
X(else , KW_ELSE , SPL_V0) \
|
||||
X(enum , KW_ENUM , SPL_V0) \
|
||||
X(false , KW_FALSE , SPL_V0) \
|
||||
X(fn , KW_FN , SPL_V0) \
|
||||
X(for , KW_FOR , SPL_V0) \
|
||||
X(if , KW_IF , SPL_V0) \
|
||||
X(loop , KW_LOOP , SPL_V0) \
|
||||
X(match , KW_MATCH , SPL_V0) \
|
||||
X(null , KW_NULL , SPL_V0) \
|
||||
X(ret , KW_RET , SPL_V0) \
|
||||
X(struct , KW_STRUCT , SPL_V0) \
|
||||
X(true , KW_TRUE , SPL_V0) \
|
||||
X(type , KW_TYPE , SPL_V0) \
|
||||
X(union , KW_UNION , SPL_V0) \
|
||||
X(var , KW_VAR , SPL_V0) \
|
||||
X(void , KW_VOID , SPL_V0) \
|
||||
X(while , KW_WHILE , SPL_V0) \
|
||||
X(_ , KW_ANY , SPL_V0) \
|
||||
// KEYWORD_TABLE
|
||||
|
||||
#define TOKEN_TABLE \
|
||||
X(unknown , TOK_UNKNOWN , SPL_V0 ) \
|
||||
X(EOF , TOK_EOF , SPL_V0 ) \
|
||||
X(blank , TOK_BLANK , SPL_V0 ) \
|
||||
X(endline , TOK_ENDLINE , SPL_V0 ) \
|
||||
X("#" , TOK_SHARP , SPL_V0 ) \
|
||||
X("@" , TOK_AT , SPL_V0 ) \
|
||||
X("==" , TOK_EQ , SPL_V0 ) \
|
||||
X("=" , TOK_ASSIGN , SPL_V0 ) \
|
||||
X("+=" , TOK_ASSIGN_ADD , SPL_V0 ) \
|
||||
X("+" , TOK_ADD , SPL_V0 ) \
|
||||
X("-=" , TOK_ASSIGN_SUB , SPL_V0 ) \
|
||||
X("->" , TOK_RIGHT_ARRAY , SPL_V0 ) \
|
||||
X("<-" , TOK_LEFT_ARRAY , SPL_V0 ) \
|
||||
X("-" , TOK_SUB , SPL_V0 ) \
|
||||
X("*=" , TOK_ASSIGN_MUL , SPL_V0 ) \
|
||||
X("*" , TOK_MUL , SPL_V0 ) \
|
||||
X("/=" , TOK_ASSIGN_DIV , SPL_V0 ) \
|
||||
X("/" , TOK_DIV , SPL_V0 ) \
|
||||
X("//" , TOK_LINE_COMMENT , SPL_V0 ) \
|
||||
X("/* */" , TOK_BLOCK_COMMENT , SPL_V0 ) \
|
||||
X("%=" , TOK_ASSIGN_MOD , SPL_V0 ) \
|
||||
X("%" , TOK_MOD , SPL_V0 ) \
|
||||
X("&&" , TOK_AND_AND , SPL_V0 ) \
|
||||
X("&=" , TOK_ASSIGN_AND , SPL_V0 ) \
|
||||
X("&" , TOK_AND , SPL_V0 ) \
|
||||
X("||" , TOK_OR_OR , SPL_V0 ) \
|
||||
X("|=" , TOK_ASSIGN_OR , SPL_V0 ) \
|
||||
X("|" , TOK_OR , SPL_V0 ) \
|
||||
X("^=" , TOK_ASSIGN_XOR , SPL_V0 ) \
|
||||
X("^" , TOK_XOR , SPL_V0 ) \
|
||||
X("<<=" , TOK_ASSIGN_L_SH , SPL_V0 ) \
|
||||
X("<<" , TOK_L_SH , SPL_V0 ) \
|
||||
X("<=" , TOK_LE , SPL_V0 ) \
|
||||
X("<" , TOK_LT , SPL_V0 ) \
|
||||
X(">>=" , TOK_ASSIGN_R_SH , SPL_V0 ) \
|
||||
X(">>" , TOK_R_SH , SPL_V0 ) \
|
||||
X(">=" , TOK_GE , SPL_V0 ) \
|
||||
X(">" , TOK_GT , SPL_V0 ) \
|
||||
X("!" , TOK_NOT , SPL_V0 ) \
|
||||
X("!=" , TOK_NEQ , SPL_V0 ) \
|
||||
X("~" , TOK_BIT_NOT , SPL_V0 ) \
|
||||
X("[" , TOK_L_BRACKET , SPL_V0 ) \
|
||||
X("]" , TOK_R_BRACKET , SPL_V0 ) \
|
||||
X("(" , TOK_L_PAREN , SPL_V0 ) \
|
||||
X(")" , TOK_R_PAREN , SPL_V0 ) \
|
||||
X("{" , TOK_L_BRACE , SPL_V0 ) \
|
||||
X("}" , TOK_R_BRACE , SPL_V0 ) \
|
||||
X(";" , TOK_SEMICOLON , SPL_V0 ) \
|
||||
X("," , TOK_COMMA , SPL_V0 ) \
|
||||
X(":" , TOK_COLON , SPL_V0 ) \
|
||||
X(":=" , TOK_COLON_ASSIGN , SPL_V0 ) \
|
||||
X("." , TOK_DOT , SPL_V0 ) \
|
||||
X(".." , TOK_RANGE , SPL_V0 ) \
|
||||
X("..." , TOK_ELLIPSIS , SPL_V0 ) \
|
||||
X("=>" , TOK_FAT_ARROW , SPL_V0 ) \
|
||||
X("?" , TOK_COND , SPL_V0 ) \
|
||||
X(ident , TOK_IDENT , SPL_V0 ) \
|
||||
X(int , TOK_INT_LITERAL , SPL_V0 ) \
|
||||
X(float , TOK_FLOAT_LITERAL , SPL_V0 ) \
|
||||
X(char , TOK_CHAR_LITERAL , SPL_V0 ) \
|
||||
X(string , TOK_STRING_LITERAL , SPL_V0 ) \
|
||||
// TOKEN_TABLE
|
||||
/* clang-format on */
|
||||
|
||||
typedef enum {
|
||||
#define X(name, enum_name, dummy) enum_name,
|
||||
KEYWORD_TABLE
|
||||
#undef X
|
||||
#define X(name, enum_name, dummy) enum_name,
|
||||
TOKEN_TABLE
|
||||
#undef X
|
||||
} spl_tok_type_t;
|
||||
|
||||
typedef struct {
|
||||
spl_tok_type_t type;
|
||||
const char *lexeme;
|
||||
usize len;
|
||||
const char *fname;
|
||||
usize offset;
|
||||
usize line;
|
||||
usize col;
|
||||
} spl_tok_t;
|
||||
|
||||
typedef VEC(spl_tok_t) spl_tok_vec_t;
|
||||
|
||||
#endif /* __SPL_TOK_H__ */
|
||||
1010
stage1/spl_type.c
1010
stage1/spl_type.c
File diff suppressed because it is too large
Load Diff
@@ -1,213 +0,0 @@
|
||||
#ifndef __SPL_TYPE_H__
|
||||
#define __SPL_TYPE_H__
|
||||
|
||||
#include "../stage0/spl_ir.h"
|
||||
#include "spl_lexer.h"
|
||||
|
||||
/* ============================================================
|
||||
* Type kinds
|
||||
* ============================================================ */
|
||||
|
||||
typedef enum {
|
||||
TYPE_VOID,
|
||||
TYPE_BASIC,
|
||||
TYPE_PTR,
|
||||
TYPE_ARRAY,
|
||||
TYPE_SLICE,
|
||||
TYPE_STRUCT,
|
||||
TYPE_UNION,
|
||||
TYPE_ENUM,
|
||||
TYPE_NAME,
|
||||
TYPE_FN,
|
||||
TYPE_COUNT,
|
||||
} spl_type_kind_t;
|
||||
|
||||
#define ENUM_TAG_SIZE 4 /* discriminator tag byte size */
|
||||
|
||||
/* ============================================================
|
||||
* Item kinds — unified member storage
|
||||
* ============================================================ */
|
||||
|
||||
typedef enum {
|
||||
ITEM_FIELD, /* aggregate_field : struct/union field */
|
||||
ITEM_VARIANT, /* enum_field : enum variant */
|
||||
ITEM_METHOD, /* method : type method */
|
||||
ITEM_NESTED_TYPE, /* nested_type : child type decl */
|
||||
ITEM_VAR, /* aggregate_field : type-level var */
|
||||
} spl_type_item_kind_t;
|
||||
|
||||
/* ============================================================
|
||||
* Type item — one member/variant/method/nested-type
|
||||
* ============================================================ */
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
spl_type_item_kind_t item_kind;
|
||||
union {
|
||||
struct {
|
||||
int type_idx; /* field type */
|
||||
usize offset; /* byte offset (for array: array_len) */
|
||||
} aggregate_field;
|
||||
struct {
|
||||
int type_idx; /* data type, -1 if none */
|
||||
isize value; /* discriminator value */
|
||||
} enum_field;
|
||||
struct {
|
||||
int func_idx; /* index in spl_comp_t.funcs */
|
||||
} method;
|
||||
struct {
|
||||
int type_idx; /* child type index */
|
||||
} nested_type;
|
||||
};
|
||||
} spl_type_item_t;
|
||||
|
||||
typedef VEC(spl_type_item_t) spl_type_item_vec_t;
|
||||
|
||||
/* ============================================================
|
||||
* Type info — one type definition
|
||||
*
|
||||
* PTR : items[0].aggregate_field.type_idx = elem
|
||||
* ARRAY : items[0].aggregate_field.type_idx = elem,
|
||||
* items[0].aggregate_field.offset = len
|
||||
* SLICE : same as PTR
|
||||
* STRUCT : items = ITEM_FIELD for each field
|
||||
* UNION : items = ITEM_FIELD for each field
|
||||
* ENUM : items = ITEM_VARIANT for each variant
|
||||
* NAME : items[0].aggregate_field.type_idx = alias target
|
||||
* ============================================================ */
|
||||
|
||||
typedef struct spl_type_info {
|
||||
char *name;
|
||||
spl_type_kind_t kind;
|
||||
spl_type_t basic_type; /* for TYPE_BASIC */
|
||||
spl_type_item_vec_t items;
|
||||
usize byte_size; /* total byte size (cached) */
|
||||
usize slot_count; /* stack slot count (cached) */
|
||||
int resolved; /* layout computed */
|
||||
int parent_type_idx; /* enclosing type, -1 for root */
|
||||
} spl_type_info_t;
|
||||
|
||||
typedef VEC(spl_type_info_t) spl_type_info_vec_t;
|
||||
|
||||
/* ============================================================
|
||||
* Type context — type arena + namespace
|
||||
*
|
||||
* types[0] = root (compilation unit type)
|
||||
* Types form a tree via parent_type_idx on each type info.
|
||||
* current_type_idx tracks the innermost type being parsed.
|
||||
* ============================================================ */
|
||||
|
||||
typedef struct {
|
||||
spl_type_info_vec_t types;
|
||||
MAP(const char *, int) type_map; /* name → type_idx 加速查找 */
|
||||
int root_type_idx; /* 编译单元自身 = 0 */
|
||||
int current_type_idx; /* 当前作用域类型 */
|
||||
int basic_cache[SPL_TYPE_COUNT]; /* memoized basic-type → type_idx */
|
||||
} spl_type_ctx_t;
|
||||
|
||||
/* ============================================================
|
||||
* Lifecycle
|
||||
* ============================================================ */
|
||||
|
||||
void spl_type_ctx_init(spl_type_ctx_t *tctx);
|
||||
void spl_type_ctx_drop(spl_type_ctx_t *tctx);
|
||||
|
||||
/* ============================================================
|
||||
* Constructors — all return type_idx (index into tctx->types)
|
||||
* ============================================================ */
|
||||
|
||||
int spl_type_basic(spl_type_ctx_t *tctx, spl_type_t bt);
|
||||
int spl_type_ptr(spl_type_ctx_t *tctx, int elem_type_idx);
|
||||
int spl_type_array(spl_type_ctx_t *tctx, int elem_type_idx, usize len);
|
||||
int spl_type_slice(spl_type_ctx_t *tctx, int elem_type_idx);
|
||||
int spl_type_struct(spl_type_ctx_t *tctx, const char *name);
|
||||
int spl_type_union(spl_type_ctx_t *tctx, const char *name);
|
||||
int spl_type_enum(spl_type_ctx_t *tctx, const char *name);
|
||||
int spl_type_alias(spl_type_ctx_t *tctx, const char *name, int target_type_idx);
|
||||
int spl_type_fn(spl_type_ctx_t *tctx, int params_type_idx, int ret_type_idx);
|
||||
|
||||
/* ============================================================
|
||||
* Item management — add members to aggregate types
|
||||
* ============================================================ */
|
||||
|
||||
void spl_type_add_field(spl_type_ctx_t *tctx, int type_idx, const char *name, int field_type_idx);
|
||||
void spl_type_add_var(spl_type_ctx_t *tctx, int type_idx, const char *name, int var_type_idx);
|
||||
void spl_type_add_variant(spl_type_ctx_t *tctx, int type_idx, const char *name, int data_type_idx);
|
||||
void spl_type_add_method(spl_type_ctx_t *tctx, int type_idx, const char *name, int func_idx);
|
||||
void spl_type_add_nested(spl_type_ctx_t *tctx, int parent_idx, const char *name,
|
||||
int child_type_idx);
|
||||
|
||||
/* ============================================================
|
||||
* Layout — compute byte_size / slot_count, set resolved
|
||||
* ============================================================ */
|
||||
|
||||
void spl_type_compute_layout(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* ============================================================
|
||||
* Accessors — get info from type_idx
|
||||
* ============================================================ */
|
||||
|
||||
spl_type_kind_t spl_type_kind(spl_type_ctx_t *tctx, int type_idx);
|
||||
spl_type_t spl_type_basic_type(spl_type_ctx_t *tctx, int type_idx);
|
||||
const char *spl_type_name(spl_type_ctx_t *tctx, int type_idx);
|
||||
usize spl_type_size(spl_type_ctx_t *tctx, int type_idx);
|
||||
usize spl_type_slot_count(spl_type_ctx_t *tctx, int type_idx);
|
||||
usize spl_type_elem_stride(spl_type_ctx_t *tctx, int elem_type_idx);
|
||||
|
||||
/* Element type for PTR / ARRAY / SLICE / NAME (reads items[0]) */
|
||||
int spl_type_elem_type(spl_type_ctx_t *tctx, int type_idx);
|
||||
usize spl_type_array_len(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* Scalar: fits in one slot, supports ==/!= directly */
|
||||
int spl_type_is_scalar(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* Integer type check */
|
||||
int spl_type_is_integer(spl_type_t bt);
|
||||
|
||||
/* True for types that support inline literal {} syntax */
|
||||
int spl_type_has_inline_literal(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* True for multi-slot type — alias for needs_multi_slot for semantic clarity */
|
||||
int spl_type_is_aggregate(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* Multi-slot: aggregate type needing >1 stack slots */
|
||||
int spl_type_needs_multi_slot(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* Follow alias chain to underlying type */
|
||||
int spl_type_resolve_underlying(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* SPL_IR opcode type tag for LOAD/STORE */
|
||||
spl_type_t spl_type_emit_type(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* Debug string */
|
||||
const char *spl_type_str(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* ============================================================
|
||||
* Item iteration — filter by item_kind
|
||||
* ============================================================ */
|
||||
|
||||
int spl_type_item_count(spl_type_ctx_t *tctx, int type_idx, spl_type_item_kind_t kind);
|
||||
spl_type_item_t *spl_type_item_at(spl_type_ctx_t *tctx, int type_idx, int item_index);
|
||||
/* Returns pointer to first item of given kind, with *count set */
|
||||
spl_type_item_t *spl_type_first_of_kind(spl_type_ctx_t *tctx, int type_idx,
|
||||
spl_type_item_kind_t kind, int *count);
|
||||
/* Get raw items vec for direct iteration (avoids double-lookup) */
|
||||
spl_type_item_vec_t *spl_type_items(spl_type_ctx_t *tctx, int type_idx);
|
||||
|
||||
/* ============================================================
|
||||
* Type resolution — name → type_idx
|
||||
* ============================================================ */
|
||||
|
||||
/* Walk current → parent chain, then type_map */
|
||||
int spl_type_resolve(spl_type_ctx_t *tctx, const char *name);
|
||||
/* Resolve within a specific parent type's ITEM_NESTED_TYPE items */
|
||||
int spl_type_resolve_in(spl_type_ctx_t *tctx, int parent_idx, const char *name);
|
||||
|
||||
/* ============================================================
|
||||
* Forward declaration for spl_parse_type
|
||||
* ============================================================ */
|
||||
|
||||
struct spl_comp;
|
||||
int spl_type_parse(spl_type_ctx_t *tctx, struct spl_comp *ctx);
|
||||
|
||||
#endif /* __SPL_TYPE_H__ */
|
||||
@@ -1,13 +1,4 @@
|
||||
/* splc0.c — Stage 1 SPL compiler (bootstrap)
|
||||
* Usage:
|
||||
* splc0 <input.spl> <output.sir> — compile
|
||||
* splc0 --dump-tokens <input.spl> — dump tokens
|
||||
* splc0 --help — help
|
||||
*/
|
||||
|
||||
#include "../stage0/spl_ir.h"
|
||||
#include "spl_comp.h"
|
||||
#include "spl_lex_util.h"
|
||||
/* splc0.c — SPL compiler CLI */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -33,75 +24,19 @@ static char *read_file(const char *path, long *out_len) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int cmd_dump_tokens(int argc, char **argv) {
|
||||
if (argc < 1) {
|
||||
fprintf(stderr, "Usage: splc0 --dump-tokens <file.spl>\n");
|
||||
return 1;
|
||||
}
|
||||
long len;
|
||||
char *src = read_file(argv[0], &len);
|
||||
if (!src)
|
||||
return 1;
|
||||
|
||||
spl_tok_vec_t toks = spl_lex(src, argv[0]);
|
||||
spl_tok_vec_dump(&toks);
|
||||
spl_tok_vec_drop(&toks);
|
||||
free(src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_compile(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: splc0 <input.spl> <output.sir>\n");
|
||||
return 1;
|
||||
}
|
||||
const char *inpath = argv[0];
|
||||
const char *outpath = argv[1];
|
||||
|
||||
long len;
|
||||
char *src = read_file(inpath, &len);
|
||||
if (!src)
|
||||
return 1;
|
||||
|
||||
spl_comp_t ctx;
|
||||
spl_comp_init(&ctx);
|
||||
|
||||
int ret = 0;
|
||||
if (spl_compile(&ctx, src, inpath) != 0) {
|
||||
fprintf(stderr, "compilation failed: %s\n", ctx.error_msg);
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (spl_prog_store_to_file(outpath, &ctx.prog) != 0) {
|
||||
fprintf(stderr, "failed to write '%s'\n", outpath);
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
printf("compiled %s -> %s\n", inpath, outpath);
|
||||
|
||||
cleanup:
|
||||
spl_comp_drop(&ctx);
|
||||
free(src);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: splc0 [--dump-tokens|--help] <input.spl> [output.sir]\n");
|
||||
fprintf(stderr, "Usage: splc0 [--dump <flags>] <in> [out]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "--dump-tokens") == 0) {
|
||||
return cmd_dump_tokens(argc - 2, argv + 2);
|
||||
}
|
||||
if (strcmp(argv[1], "--dump") == 0)
|
||||
// return cmd_dump(argc - 2, argv + 2);
|
||||
return 0;
|
||||
if (strcmp(argv[1], "--help") == 0) {
|
||||
printf("SPL Compiler (stage1 bootstrap)\n");
|
||||
printf(" splc0 <input.spl> <output.sir> - compile\n");
|
||||
printf(" splc0 --dump-tokens <file.spl> - dump token stream\n");
|
||||
printf("splc0 <in> <out> compile\n");
|
||||
printf("splc0 --dump <f> <file> dump: tokens,cst,ast,ir,mcode,all\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return cmd_compile(argc - 1, argv + 1);
|
||||
// return cmd_compile(argc - 1, argv + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* spc_vm.c — VM launcher for stage 1 */
|
||||
/* spc_vm.c 鈥?VM launcher for stage 1 */
|
||||
|
||||
#include "../stage0/spl_ir.h"
|
||||
#include "../stage0/spl_mcode.h"
|
||||
#include "../stage0/spl_syscall.h"
|
||||
#include "../stage0/spl_vm.h"
|
||||
#include "spl_comp.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -11,28 +10,31 @@
|
||||
int main(int argc, const char **argv) {
|
||||
int argi = 1;
|
||||
|
||||
if (argi >= argc) {
|
||||
fprintf(stderr, "Usage: spc_vm <file.sir> [entry] [--trace]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *path = argv[argi++];
|
||||
|
||||
/* Parse flags: --entry <name>, --trace */
|
||||
/* Parse flags: --entry <name>, --trace, -d */
|
||||
const char *entry = "main";
|
||||
int trace = 0;
|
||||
int debug_addr = 0;
|
||||
for (int i = argi; i < argc; i += 1) {
|
||||
if (strcmp(argv[i], "--entry") == 0 && i + 1 < argc) {
|
||||
entry = argv[++i];
|
||||
} else if (strcmp(argv[i], "--trace") == 0) {
|
||||
trace = 1;
|
||||
} else if (strcmp(argv[i], "-d") == 0) {
|
||||
debug_addr = 1;
|
||||
} else if (argv[i][0] != '-') {
|
||||
argi = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Adjust argv so the SPL program sees path as argv[0] and
|
||||
* remaining positional args as argv[1..], just like a native exe. */
|
||||
int spl_argc = argc - (argi - 1);
|
||||
if (argi >= argc) {
|
||||
fprintf(stderr, "Usage: spc_vm [-d] [--trace] <file.sir> [args...]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *path = argv[argi++];
|
||||
const char **spl_argv = argv + (argi - 1);
|
||||
int spl_argc = (int)(argc - (argi - 1));
|
||||
|
||||
spl_prog_t prog;
|
||||
if (spl_prog_load_from_file(path, &prog) != 0) {
|
||||
@@ -41,7 +43,6 @@ int main(int argc, const char **argv) {
|
||||
}
|
||||
|
||||
spl_syscall_register(&prog);
|
||||
spl_comp_register(&prog);
|
||||
|
||||
spl_vm_t vm;
|
||||
spl_vm_init(&vm);
|
||||
@@ -56,6 +57,7 @@ int main(int argc, const char **argv) {
|
||||
return 1;
|
||||
}
|
||||
spl_vm_set_trace(&vm, trace);
|
||||
if (debug_addr) spl_vm_set_debug(&vm, 1);
|
||||
|
||||
int ret = spl_vm_run_until(&vm, 0);
|
||||
spl_vm_drop(&vm);
|
||||
|
||||
@@ -24,8 +24,8 @@ type Expr = enum {
|
||||
|
||||
fn eval(self: *Expr) i32 {
|
||||
match self {
|
||||
.Int(val) => ret val,
|
||||
.Add(left, right) => ret eval(left) + eval(right),
|
||||
.Int[val] => ret val,
|
||||
.Add[.left = left, .right = right] => ret eval(left) + eval(right),
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ fn test_optional_match() i32 {
|
||||
var o: Optional = Optional { .Some = 42 };
|
||||
|
||||
match o {
|
||||
.Some(val) => {
|
||||
.Some[val] => {
|
||||
if val != 42 { ret 1; }
|
||||
},
|
||||
.None => {
|
||||
@@ -90,7 +90,7 @@ fn test_optional_match() i32 {
|
||||
o = Optional { .None };
|
||||
var is_none: i32 = 0;
|
||||
match o {
|
||||
.Some(val) => {},
|
||||
.Some[val] => {},
|
||||
.None => { is_none = 1; }
|
||||
}
|
||||
if is_none != 1 { ret 3; }
|
||||
@@ -98,7 +98,7 @@ fn test_optional_match() i32 {
|
||||
/* 多次提取不同值 */
|
||||
o = Optional { .Some = 99 };
|
||||
match o {
|
||||
.Some(val) => {
|
||||
.Some[val] => {
|
||||
if val != 99 { ret 4; }
|
||||
},
|
||||
.None => { ret 5; }
|
||||
@@ -115,10 +115,10 @@ fn test_shape_match() i32 {
|
||||
/* Circle: 单数据 */
|
||||
var s: Shape = Shape { .Circle = 10 };
|
||||
match s {
|
||||
.Circle(r) => {
|
||||
.Circle[r] => {
|
||||
if r != 10 { ret 1; }
|
||||
},
|
||||
.Rect(w, h) => {
|
||||
.Rect[.x = w, .y = h] => {
|
||||
ret 2;
|
||||
}
|
||||
}
|
||||
@@ -126,8 +126,8 @@ fn test_shape_match() i32 {
|
||||
/* Rect: 结构体数据,绑定为 (x, y) 对应 Point 的字段 */
|
||||
s = Shape { .Rect = Point { .x = 3, .y = 4 } };
|
||||
match s {
|
||||
.Circle(r) => { ret 3; },
|
||||
.Rect(w, h) => {
|
||||
.Circle[r] => { ret 3; },
|
||||
.Rect[.x = w, .y = h] => {
|
||||
if w != 3 { ret 4; }
|
||||
if h != 4 { ret 5; }
|
||||
}
|
||||
@@ -143,16 +143,16 @@ fn test_shape_match() i32 {
|
||||
fn test_action_result_match() i32 {
|
||||
var r: ActionResult = ActionResult { .Success = 200 };
|
||||
match r {
|
||||
.Success(code) => {
|
||||
.Success[code] => {
|
||||
if code != 200 { ret 1; }
|
||||
},
|
||||
.NotFound => {
|
||||
ret 2;
|
||||
},
|
||||
.Timeout(ms) => {
|
||||
.Timeout[ms] => {
|
||||
ret 3;
|
||||
},
|
||||
.Error(msg) => {
|
||||
.Error[msg] => {
|
||||
ret 4;
|
||||
}
|
||||
}
|
||||
@@ -160,21 +160,21 @@ fn test_action_result_match() i32 {
|
||||
r = ActionResult { .NotFound };
|
||||
var found: i32 = 1;
|
||||
match r {
|
||||
.Success(code) => { found = 0; },
|
||||
.Success[code] => { found = 0; },
|
||||
.NotFound => { },
|
||||
.Timeout(ms) => { found = 0; },
|
||||
.Error(msg) => { found = 0; }
|
||||
.Timeout[ms] => { found = 0; },
|
||||
.Error[msg] => { found = 0; }
|
||||
}
|
||||
if found != 1 { ret 5; }
|
||||
|
||||
r = ActionResult { .Timeout = 5000 };
|
||||
match r {
|
||||
.Success(code) => { ret 6; },
|
||||
.Success[code] => { ret 6; },
|
||||
.NotFound => { ret 7; },
|
||||
.Timeout(ms) => {
|
||||
.Timeout[ms] => {
|
||||
if ms != 5000 { ret 8; }
|
||||
},
|
||||
.Error(msg) => { ret 9; }
|
||||
.Error[msg] => { ret 9; }
|
||||
}
|
||||
|
||||
ret 0;
|
||||
@@ -252,7 +252,7 @@ fn test_match_in_loop() i32 {
|
||||
}
|
||||
|
||||
match o {
|
||||
.Some(val) => {
|
||||
.Some[val] => {
|
||||
sum = sum + val;
|
||||
},
|
||||
.None => { }
|
||||
|
||||
@@ -253,13 +253,13 @@ fn test_enum_complex() i32 {
|
||||
|
||||
/* Verify active variant */
|
||||
match s {
|
||||
.Active(val) => {
|
||||
.Active[val] => {
|
||||
if val != 42 { ret 1; }
|
||||
},
|
||||
.Inactive => {
|
||||
ret 2;
|
||||
},
|
||||
.Pending(px, py) => {
|
||||
.Pending[.x = px, .y = py] => {
|
||||
ret 3;
|
||||
}
|
||||
}
|
||||
@@ -268,18 +268,18 @@ fn test_enum_complex() i32 {
|
||||
var s2: Status = Status { .Inactive };
|
||||
var is_inactive: i32 = 0;
|
||||
match s2 {
|
||||
.Active(val) => {},
|
||||
.Active[val] => {},
|
||||
.Inactive => { is_inactive = 1; },
|
||||
.Pending(px, py) => {}
|
||||
.Pending[.x = px, .y = py] => {}
|
||||
}
|
||||
if is_inactive != 1 { ret 4; }
|
||||
|
||||
/* Test Pending variant with struct data */
|
||||
var s3: Status = Status { .Pending = Point { .x = 7, .y = 8 } };
|
||||
match s3 {
|
||||
.Active(val) => { ret 5; },
|
||||
.Active[val] => { ret 5; },
|
||||
.Inactive => { ret 6; },
|
||||
.Pending(px, py) => {
|
||||
.Pending[.x = px, .y = py] => {
|
||||
if px != 7 { ret 7; }
|
||||
if py != 8 { ret 8; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user