Compare commits
4 Commits
8ac59dfaa1
...
314100afbc
| Author | SHA1 | Date | |
|---|---|---|---|
| 314100afbc | |||
| a56b4cbb70 | |||
| ca7cf221c8 | |||
| 2ccee5f1cf |
66
SPL.md
66
SPL.md
@@ -432,7 +432,7 @@ fn name(x: i32 = 0) { ... }
|
||||
声明 静态约束 动态语义
|
||||
FnDecl 名称唯一,签名完整。 仅定义。
|
||||
TypeDecl 同作用域名称唯一。 定义类型别名或聚合体,编译时解析。
|
||||
VarDecl (容器级) var: 必须初始化,类型完整。const: 初始化必须编译期可求值。 const 编译时计算;var 启动初始化一次。
|
||||
VarDecl var: 必须初始化,类型完整。 const: 初始化必须编译期可求值。 const 编译时计算;var 启动初始化一次。
|
||||
ConstDecl 必须编译期可求值。 编译期常量。
|
||||
ComptimeStmt 内部代码全部在编译时执行。 编译时执行,可生成声明。
|
||||
DirectiveBlock @id { } / #id { } 为扩展占位,无预定义行为。未识别指令触发警告/错误。 同左。
|
||||
@@ -478,12 +478,21 @@ type T = TypeExpr — 完全同义。
|
||||
|
||||
匹配: match 必须穷举 (或含 _),否则编译错误。
|
||||
|
||||
### 聚合体内的成员函数(就是普通函数)
|
||||
在 struct / union / enum 体内的 `fn` 声明就是普通函数,进入该聚合 def 的成员列表(与字段、变体并列)。**无"方法"概念、无隐式接收者、无 self 魔法**:需要实例时,实例以普通参数显式传入(首参命名 self 只是习惯,无特殊语义)。
|
||||
|
||||
- 聚合类型 (以及文件 $root) 同时是**命名空间**,其内 fn 即该命名空间下的具名函数。
|
||||
- 调用**只能命名空间限定**: `TypeName.fn(args)`(如 `Vec.at(&vec, i)`)。**禁止 `expr.fn(args)` 用表达式实例点调其成员函数**。
|
||||
- 命名空间内相互调用用裸名 `fn(...)`。
|
||||
- `.` 仅用于字段/变体/路径选择 (`vec.len`、`.Some[5]`、`Token.Tag.kw_null`),不是函数调用通道。
|
||||
- 函数名、字段名、变体名同存于聚合 def 的成员列表,名称互斥(同一聚合内同名冲突为编译错误)。
|
||||
|
||||
## 名称解析
|
||||
|
||||
1. 函数内部 (参数 + 块局部,每个 {} 建立子 scope)。
|
||||
2. $root:文件聚合的 scope,所有顶层声明 (fn/type/var/const) 都是其成员。
|
||||
|
||||
无独立"全局"命名空间。$root 抽象不写死:@import(xxx) 返回的匿名结构即该模块当时的 $root,多文件时切换。字段/变体名不在 scope map 中 (存于聚合 def 的成员列表),裸名查找天然不命中字段;路径 a.b.c 首段裸名查 scope、后续段查聚合成员。
|
||||
无独立"全局"命名空间。$root 抽象不写死:@import(xxx) 返回的匿名结构即该模块当时的 $root,多文件时切换。字段/变体/命名空间函数名不在 scope map 中 (存于聚合 def 的成员列表),裸名查找天然不命中字段/函数;命名空间函数只能经 `TypeName.fn` 限定调用,禁止 `expr.fn(...)` 实例点调;路径 a.b.c 首段裸名查 scope、后续段查聚合成员。
|
||||
|
||||
## 变量与常量
|
||||
var x: T 或 var x := init: 可变量。
|
||||
@@ -585,7 +594,9 @@ defer { ... }: 退出作用域时以后进先出执行。
|
||||
|
||||
浮点 f64,布尔 bool。
|
||||
|
||||
null -> ?T 隐式转换。
|
||||
字符 'c'(含转义)默认 u8,视为整数字面量可参与拓宽。
|
||||
|
||||
null:空指针字面量。当前语言无 ?T,null 仅用于指针或切片的比较、赋值、初始化及聚合字段;其他用法为错误。
|
||||
|
||||
### 运算符
|
||||
算术/位运算: 操作数类型必须完全一致或由字面量推导,无隐式提升。
|
||||
@@ -615,6 +626,8 @@ expr(args) 可调用 函数调用
|
||||
|
||||
结构体字面量: Type { .f1 = v1, ... }。
|
||||
|
||||
匿名聚合字面量: .{ .f1 = v1, ... }。省去显式类型名,必须出现在能由上下文推导目标聚合类型的语境 (变量初始化带类型、赋值、字段值等);无法推导目标类型则编译错误。切片可用 .{ .ptr = p, .len = n } 构造。
|
||||
|
||||
块表达式: { ... }。
|
||||
|
||||
comptime { ... } / comptime expr;: 编译时求值,不可引用运行时变量。
|
||||
@@ -628,6 +641,8 @@ comptime { ... } / comptime expr;: 编译时求值,不可引用运行时变量
|
||||
|
||||
函数类型: fn(T1, T2) RetType。
|
||||
|
||||
裸 `_` 与 `ptr` 关键字不能作为独立类型 (单独出现为编译错误),`_` 只能以 `*_` 形式作指针目标,语义为"任意指针";`ptr` 关键字当前保留未启用。
|
||||
|
||||
*_ 的特殊规则:
|
||||
|
||||
*T -> *_: 允许,必须警告。
|
||||
@@ -638,9 +653,7 @@ comptime { ... } / comptime expr;: 编译时求值,不可引用运行时变量
|
||||
|
||||
其他隐式转换:
|
||||
|
||||
整数字面量适配更大类型。
|
||||
|
||||
null -> ?T。
|
||||
null -> 指针/切片空值。
|
||||
|
||||
其余必须显式 as。
|
||||
|
||||
@@ -648,13 +661,15 @@ null -> ?T。
|
||||
区间类型 Range
|
||||
语法 a..b 或 a..。
|
||||
|
||||
内部结构: { begin: i64, end: ?i64 } (具体整数类型可能由上下文决定,默认为 i64)。
|
||||
内部结构: { begin: <int>, end: <int> },其中 <int> 为边界整数类型 (左右边界类型一致,默认/通常为 isize 或 usize;无界端 end 存为空值表示无穷)。
|
||||
|
||||
a..b: begin = a, end = b。
|
||||
|
||||
a..: begin = a, end = null。
|
||||
a..: begin = a, end = 空值 (无界)。
|
||||
|
||||
用作迭代器时: 产生从 begin 到 end-1 的整数 (若 end 为 null 则无穷)。在 for 中与其他序列配合时,无界的区间自动取其配对序列的长度作为上界 (等于 0..other.len),如果配对序列也是无界则无限循环。
|
||||
Range 是一等类型 (值类型,同聚合行为),其 begin / end 可作为字段直接访问 (expr.begin / expr.end),访问类型即边界整数类型。
|
||||
|
||||
用作迭代器时: 产生从 begin 到 end-1 的整数 (若 end 为空值则无穷),元素类型即边界整数类型 (与 begin/end 一致)。在 for 中与其他序列配合时,无界的区间自动取其配对序列的长度作为上界 (等于 0..other.len),如果配对序列也是无界则无限循环。
|
||||
|
||||
切片类型 []T
|
||||
内部结构: { ptr: *T, len: usize }。
|
||||
@@ -732,13 +747,13 @@ void: 大小为 0,表示无值,仅用于函数返回或指针。
|
||||
从数组构造: array[a..b] 产生切片。
|
||||
|
||||
### 区间类型
|
||||
a..b 或 a..: 类型为 Range,内部结构 { begin: isize, end: ?isize }。
|
||||
a..b 或 a..: 类型为 Range,内部结构 { begin: <int>, end: <int> },<int> 为边界整数类型 (左右边界一致,默认 isize 或 usize;无界端 end 存空值)。
|
||||
|
||||
a..b: end 为 b。
|
||||
|
||||
a..: end 为 null (无界)。
|
||||
a..: end 为空值 (无界)。
|
||||
|
||||
Range 是值类型同聚合类型行为。
|
||||
Range 是值类型同聚合类型行为,begin / end 可作为字段直接访问。
|
||||
|
||||
用于迭代和切片边界。
|
||||
|
||||
@@ -747,10 +762,10 @@ fn(参数类型列表) 返回类型
|
||||
|
||||
函数值本身的大小和表示未指定 (闭包待定),但函数名作为标识符使用时具有指针语义 (类似函数指针)。
|
||||
|
||||
### 可选类型(暂时不需要实现)
|
||||
### 可选类型(预留,当前不实现)
|
||||
?T: 可为 null 的类型。内部表示同 T 但附加一个判别 (可能通过 null 指针表示,视 T 而定)。?T 的大小和对齐与 T 相同或扩展为可容纳 null 的形式 (具体实现定义)。
|
||||
|
||||
null 字面量只能出现在需要 ?T 的上下文中。
|
||||
当前阶段未实现 ?T,null 以"指针/切片的空值"语义生效 (见"特殊类型"与"类型兼容性")。
|
||||
|
||||
### 聚合类型
|
||||
struct: 字段连续排列 (可能有对齐填充),每个字段有自己的类型。赋值是逐字段拷贝。
|
||||
@@ -772,7 +787,7 @@ enum: 带标签的联合体,每个变体可有载荷。大小实现定义,
|
||||
### 特殊类型
|
||||
void: 无法实例化,仅用于返回或指针目标。
|
||||
|
||||
null: 不是独立类型,仅用于初始化或赋值给 ?T。
|
||||
null: 不是独立类型,是"指针/切片空值"字面量,仅用于指针或切片的初始化、赋值、比较及聚合字段。
|
||||
|
||||
## 类型兼容性与隐式转换
|
||||
下表中,“允许”表示可自动转换,否则需要显式 as 转换。警告列表明编译器必须输出诊断信息,不可静默。
|
||||
@@ -782,12 +797,10 @@ T (任意) T 是 无 相同类型
|
||||
*T *_ 是 警告: “丢失类型信息”
|
||||
*_ *T 是 警告: “不安全的指针重解释”
|
||||
[N]T []T 是 无 数组到切片强制转换
|
||||
整数字面量 整数类型 U 是 (若值在 U 范围内) 无 字面量自动拓宽
|
||||
整数字面量 整数类型 U 是 (若值在 U 范围内) 无 字面量自动拓宽 (含字符 'c' 视为 u8)
|
||||
i32 i64 否 — 需显式 as i64,防止意外
|
||||
i64 i32 否 — 窄化必须显式
|
||||
null ?T 是 无 空值初始化
|
||||
?T T 否 — 需显式解包 (如 orelse,但语言暂未定义,将来扩展)
|
||||
T ?T 是 无 提升为可选
|
||||
null *T / []T 是 无 空指针/空切片初始化、比较、赋值、聚合字段
|
||||
浮点字面量 f32 是 (值可表示则) 无
|
||||
f64 f32 否 — 窄化需显式
|
||||
bool 整数 否 —
|
||||
@@ -795,9 +808,11 @@ bool 整数 否 —
|
||||
|
||||
注:
|
||||
|
||||
?T (可选类型) 为预留特性,当前阶段未实现;null 语义以"指针/切片的空值"生效,见本表 null 行。
|
||||
|
||||
隐式转换不会嵌套传递。例如 *T 到 *_ 是警告转换,但不因此进一步允许 *_ 到 **T 的隐式转换。
|
||||
|
||||
字面量拓宽仅适用于整数字面量直接出现在需要更宽整数类型的上下文 (如赋值给 i64 变量,或作为 Range 的边界,Range 内部为 isize,所以 0..5 中的 0 和 5 会拓宽为 isize)。
|
||||
字面量拓宽仅适用于整数字面量直接出现在需要更宽整数类型的上下文 (如赋值给 i64 变量,或作为 Range 的边界,Range 内部为 isize,所以 0..5 中的 0 和 5 会拓宽为 isize;字符 'c' 同视为整数字面量)。
|
||||
|
||||
所有其他未列出的类型转换均需显式 as。
|
||||
|
||||
@@ -811,7 +826,7 @@ FLOAT -> f64
|
||||
|
||||
true / false -> bool
|
||||
|
||||
null -> 必须从上下文推导出 ?T,无法推导则报错。
|
||||
null -> 必须从上下文推导出 *T 或 []T 的空值 (当前无 ?T),用于比较/赋值/字段;无法推导或用于非指针上下文则报错。
|
||||
|
||||
字符串字面量 -> []u8 (具体待定)
|
||||
|
||||
@@ -936,7 +951,7 @@ ForRange 提供表达式列表 [E1, E2, ...]。as 后标识符列表 [v1, v2, ..
|
||||
|
||||
对于 Ei:
|
||||
|
||||
若为 Range: 每次迭代产出的值类型为 isize (Range 的边界类型,这里默认为 isize)。
|
||||
若为 Range: 每次迭代产出的值类型为 Range 的边界整数类型 (即 begin/end 的类型,默认 isize 或 usize)。
|
||||
|
||||
若为 []T 或 [N]T: 产出元素类型为 T。
|
||||
|
||||
@@ -946,7 +961,7 @@ ForRange 提供表达式列表 [E1, E2, ...]。as 后标识符列表 [v1, v2, ..
|
||||
|
||||
长度协调规则:
|
||||
|
||||
若所有序列均为有界 (Range 有 end 不为 null,或切片长度已知),则循环次数为最短长度。
|
||||
若所有序列均为有界 (Range 有 end 非空值,或切片长度已知),则循环次数为最短长度。
|
||||
|
||||
若存在无界 Range (如 0..),则要求循环中至少有一个有界序列,且该有界序列的长度将作为无界序列的上限。例如 for slice, 0.. as elem, idx,0.. 的长度由 slice.len 决定。
|
||||
|
||||
@@ -960,8 +975,6 @@ for my_slice, 0.. as elem, idx { ... }
|
||||
// 0..: Range 无界 -> idx: i64,长度由 my_slice 决定
|
||||
```
|
||||
## 类型系统限制
|
||||
无隐式类型提升 (除字面量整数拓宽和数组到切片外)。
|
||||
|
||||
无默认初始化: var x: T; 不初始化,警告/错误。
|
||||
|
||||
无隐式 deref 多级: . 仅自动解引用一层。
|
||||
@@ -1008,7 +1021,7 @@ Function ← AttributeList? 'func' GLOBAL_IDENT '(' ParamDefs? ')' '->' Type
|
||||
AttributeList ← Attribute (',' Attribute)*
|
||||
Attribute ← '!' IDENTIFIER ( '(' Args? ')' )? // 标记,如 !export("C")
|
||||
|
||||
GLOBAL_IDENT ← '@' IDENTIFIER
|
||||
GLOBAL_IDENT ← IDENTIFIER
|
||||
|
||||
ParamDefs ← LOCAL_IDENT ':' Type (',' LOCAL_IDENT ':' Type)*
|
||||
Block ← '{' Stmt* '}'
|
||||
@@ -1153,6 +1166,7 @@ CONSTANT ← INTEGER | FLOAT | STRING | 'true' | 'false' | 'null' | 'undefine
|
||||
@control.br(bool, label, label) -> ! 条件分支:根据第一个参数跳转到第二或第三个(标签或者地址)。终止函数 (调用后控制流不返回)。
|
||||
@control.jmp(label) -> ! 无条件跳转(跳转到标签或者地址)。终止函数。
|
||||
@control.call(FT)(%fn:FT, %args...) -> %ret 按目标平台调用约定调用函数指针 %fn,FT 为 fn<param_types...->ret_type>
|
||||
@control.param(T)(%idx: usize) -> T 返回idx的函数参数(编译期检查是否越界)
|
||||
@control.ret(T)(%val?) 按目标平台调用约定从当前函数返回,T 为返回值类型,void 时不带参数
|
||||
@control.unreachable() -> ! 标记不可达代码
|
||||
@control.trap() -> ! 触发运行时陷阱
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
vm = [
|
||||
"stage0/spl_ir.c",
|
||||
"stage0/spl_mcode.c",
|
||||
"stage0/spl_syscall.c",
|
||||
"stage0/spl_vm.c",
|
||||
]
|
||||
|
||||
splc0_part = [
|
||||
"stage1/spl_comp.c",
|
||||
"stage1/spl_ir.c",
|
||||
"stage1/spl_ast.c",
|
||||
"stage1/spl_lexer.c",
|
||||
"stage1/spl_dumptree.c",
|
||||
"stage1/spl_type.c",
|
||||
"stage1/spl_parser.c",
|
||||
"stage1/spl_lex_util.c",
|
||||
"stage1/spl_expr.c",
|
||||
"stage1/spl_stmt.c",
|
||||
"stage1/spl_sema.c",
|
||||
"stage1/spl_ast2ir.c",
|
||||
"stage1/spl_ir2vm.c",
|
||||
]
|
||||
|
||||
exe = {
|
||||
"spl_cli": ["stage0/spl_cli.c"] + vm,
|
||||
"splc_cli": ["stage1/splc_cli.c"] + vm + splc0_part,
|
||||
"splc_cli": ["stage1/splc_cli.c"] + vm,
|
||||
"splc0": ["stage1/splc0.c"] + vm + splc0_part,
|
||||
"test": ["stage0/test_spl_vm.c"] + vm,
|
||||
"spl_disasm": ["stage0/spl_disasm.c"] + vm,
|
||||
|
||||
689
stage0/spl_cli.c
689
stage0/spl_cli.c
@@ -1,65 +1,686 @@
|
||||
/* spl_cli.c — SIR VM launcher
|
||||
/* spl_cli.c — SIR VM launcher + gdb-style interactive debugger
|
||||
*
|
||||
* Loads a compiled .sir binary and runs it via the SIR VM.
|
||||
* Built-in syscalls are auto-registered via spl_syscall_register().
|
||||
* Usage: spl_cli [options] <file.sir> [args...]
|
||||
* -d, --debug stack-canary checks
|
||||
* -g, --debug-cli interactive debugger (REPL)
|
||||
* --entry <name> entry function (default: main)
|
||||
* --trace print every instruction
|
||||
* -h, --help show help
|
||||
*
|
||||
* Usage:
|
||||
* spl_cli [-d] <file.sir> [entry_point]
|
||||
* spl 程序 argv:argv[0]=file.sir,argv[1..]=args。
|
||||
* REPL 配合 splc0 -g(.sir 尾部内嵌 debug 段:IR 行 + VAR 行)可显示
|
||||
* 当前 IR 节点/源行/局部变量。
|
||||
*/
|
||||
|
||||
#include "spl_mcode.h"
|
||||
#include "spl_syscall.h"
|
||||
#include "spl_vm.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ================================================================
|
||||
* 命令行解析
|
||||
* ================================================================ */
|
||||
|
||||
typedef struct {
|
||||
int debug;
|
||||
int debug_cli;
|
||||
int trace;
|
||||
const char *entry;
|
||||
const char *path; /* file.sir */
|
||||
const char **args; /* spl 程序参数(file.sir 之后) */
|
||||
int nargs;
|
||||
} cli_opts_t;
|
||||
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "Usage: spl_cli [options] <file.sir> [args...]\n"
|
||||
" -d, --debug stack-canary checks\n"
|
||||
" -g, --debug-cli interactive debugger (REPL)\n"
|
||||
" --entry <name> entry function (default: main)\n"
|
||||
" --trace print every instruction\n"
|
||||
" -h, --help show help\n");
|
||||
}
|
||||
|
||||
/* options 可穿插;`--` 终止 option 解析(本身不作为 file/args)。
|
||||
* 第一个非 option 为 file.sir,其后为 spl args。 */
|
||||
static int parse_args(int argc, const char **argv, cli_opts_t *o) {
|
||||
memset(o, 0, sizeof *o);
|
||||
o->entry = "main";
|
||||
int i = 1;
|
||||
for (; i < argc; i++) {
|
||||
const char *a = argv[i];
|
||||
if (a[0] == '-' && a[1]) {
|
||||
if (strcmp(a, "--") == 0) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
if (strcmp(a, "-d") == 0 || strcmp(a, "--debug") == 0) {
|
||||
o->debug = 1;
|
||||
} else if (strcmp(a, "-g") == 0 || strcmp(a, "--debug-cli") == 0) {
|
||||
o->debug_cli = 1;
|
||||
} else if (strcmp(a, "--trace") == 0) {
|
||||
o->trace = 1;
|
||||
} else if (strcmp(a, "--entry") == 0) {
|
||||
if (i + 1 >= argc) {
|
||||
fprintf(stderr, "spl_cli: --entry needs a name\n");
|
||||
return -1;
|
||||
}
|
||||
o->entry = argv[++i];
|
||||
} else if (strcmp(a, "-h") == 0 || strcmp(a, "--help") == 0) {
|
||||
usage();
|
||||
return -1;
|
||||
} else {
|
||||
fprintf(stderr, "spl_cli: unknown option '%s'\n", a);
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* 跳过前导 `--`(pipeline 调用会带 `--` 分隔 file 与 args) */
|
||||
while (i < argc && strcmp(argv[i], "--") == 0)
|
||||
i++;
|
||||
if (i >= argc) {
|
||||
fprintf(stderr, "spl_cli: missing <file.sir>\n");
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
o->path = argv[i++];
|
||||
while (i < argc && strcmp(argv[i], "--") == 0)
|
||||
i++;
|
||||
o->args = argv + i;
|
||||
o->nargs = argc - i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* Debug map(splc0 -g 追加在 .sir 尾部的文本段)
|
||||
* IR <ip> <node_ref> <line> <kind>
|
||||
* VAR <func> <name> <fp_offset> <tid> <is_param>
|
||||
* ================================================================ */
|
||||
|
||||
typedef struct {
|
||||
usize ip;
|
||||
usize ref;
|
||||
int line;
|
||||
char kind[64];
|
||||
} dbg_irline_t;
|
||||
typedef VEC(dbg_irline_t) dbg_irline_vec_t;
|
||||
|
||||
typedef struct {
|
||||
char func[128];
|
||||
char name[128];
|
||||
usize offset;
|
||||
usize tid;
|
||||
int is_param;
|
||||
} dbg_var_t;
|
||||
typedef VEC(dbg_var_t) dbg_var_vec_t;
|
||||
|
||||
typedef struct {
|
||||
dbg_irline_vec_t irlines;
|
||||
dbg_var_vec_t vars;
|
||||
int loaded;
|
||||
} dbg_map_t;
|
||||
|
||||
static void dbg_map_load(dbg_map_t *m, const char *text) {
|
||||
memset(m, 0, sizeof *m);
|
||||
vec_init(m->irlines);
|
||||
vec_init(m->vars);
|
||||
m->loaded = 0;
|
||||
if (!text)
|
||||
return;
|
||||
const char *p = strstr(text, "SPLDBG");
|
||||
if (!p)
|
||||
return;
|
||||
p += 6;
|
||||
while (p && *p) {
|
||||
const char *nl = strchr(p, '\n');
|
||||
size_t len = nl ? (size_t)(nl - p) : strlen(p);
|
||||
char line[512];
|
||||
if (len >= sizeof line)
|
||||
len = sizeof line - 1;
|
||||
memcpy(line, p, len);
|
||||
line[len] = 0;
|
||||
if (strncmp(line, "IR ", 3) == 0) {
|
||||
dbg_irline_t ir;
|
||||
memset(&ir, 0, sizeof ir);
|
||||
sscanf(line + 3, "%zu %zu %d %63s", &ir.ip, &ir.ref, &ir.line, ir.kind);
|
||||
vec_push(m->irlines, ir);
|
||||
} else if (strncmp(line, "VAR ", 4) == 0) {
|
||||
dbg_var_t v;
|
||||
memset(&v, 0, sizeof v);
|
||||
sscanf(line + 4, "%127s %127s %zu %zu %d", v.func, v.name, &v.offset, &v.tid,
|
||||
&v.is_param);
|
||||
vec_push(m->vars, v);
|
||||
}
|
||||
if (!nl)
|
||||
break;
|
||||
p = nl + 1;
|
||||
}
|
||||
m->loaded = 1;
|
||||
}
|
||||
|
||||
/* 当前 ip 所属 IR 节点行(ip 恰好是某节点首指令) */
|
||||
static const dbg_irline_t *irline_at_exact(dbg_map_t *m, usize ip) {
|
||||
for (usize i = 0; i < m->irlines.size; i++)
|
||||
if (m->irlines.data[i].ip == ip)
|
||||
return &m->irlines.data[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* 调试器核心
|
||||
* ================================================================ */
|
||||
|
||||
typedef struct {
|
||||
spl_vm_t *vm;
|
||||
spl_prog_t *prog;
|
||||
const char *entry;
|
||||
dbg_map_t map;
|
||||
usize last_ptr; /* p 最近读到的值(x 无参重读) */
|
||||
int quit;
|
||||
} dbg_t;
|
||||
|
||||
/* ---- 位置/指令显示辅助 ---- */
|
||||
|
||||
static const char *fn_name_at(spl_prog_t *prog, usize ip) {
|
||||
for (usize i = 0; i < vec_size(prog->funcs); i++) {
|
||||
spl_func_t *f = &vec_at(prog->funcs, i);
|
||||
if (ip >= f->address && ip < f->address + f->ninsns)
|
||||
return f->name ? f->name : "?";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
/* ip 所在函数的指令范围 [*start, *end) */
|
||||
static void cur_func_bounds(spl_prog_t *prog, usize ip, usize *start, usize *end) {
|
||||
*start = 0;
|
||||
*end = vec_size(prog->insns);
|
||||
for (usize i = 0; i < vec_size(prog->funcs); i++) {
|
||||
spl_func_t *f = &vec_at(prog->funcs, i);
|
||||
if (ip >= f->address && ip < f->address + f->ninsns) {
|
||||
*start = f->address;
|
||||
*end = f->address + f->ninsns;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 格式化一条指令(不含行号前缀) */
|
||||
static void fmt_insn(const spl_ins_t *ins, char *buf, size_t n) {
|
||||
snprintf(buf, n, "%-11s %-5s %zu", spl_opcode_name(ins->opcode), spl_type_tag_name(ins->type),
|
||||
ins->imm);
|
||||
}
|
||||
|
||||
static void dbg_show_location(dbg_t *d) {
|
||||
spl_vm_t *vm = d->vm;
|
||||
if (vm->ip >= vec_size(vm->prog->insns)) {
|
||||
printf(" (end of program)\n");
|
||||
return;
|
||||
}
|
||||
char ib[80];
|
||||
fmt_insn(&vec_at(vm->prog->insns, vm->ip), ib, sizeof ib);
|
||||
printf(" %s @ ip=%zu %s [sp=%zu fp=%zu cp=%zu]\n", fn_name_at(vm->prog, vm->ip), vm->ip,
|
||||
ib, vm->sp, vm->fp, vm->cp);
|
||||
const dbg_irline_t *ir = irline_at_exact(&d->map, vm->ip);
|
||||
if (ir)
|
||||
printf(" -> ir node#%zu line %d %s\n", ir->ref, ir->line, ir->kind);
|
||||
}
|
||||
|
||||
static void dbg_handle_status(dbg_t *d, int r) {
|
||||
if (r == 1) {
|
||||
printf(" program halted, exit_code=%d\n", d->vm->exit_code);
|
||||
d->quit = 1;
|
||||
} else if (r == 2) {
|
||||
printf(" breakpoint hit\n");
|
||||
} else if (r == -1) {
|
||||
printf(" VM error: %s\n", d->vm->error_msg[0] ? d->vm->error_msg : "(no detail)");
|
||||
d->quit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 变量查找(p / x 复用) ---- */
|
||||
|
||||
static dbg_var_t *dbg_find_var(dbg_t *d, const char *fn, const char *name) {
|
||||
for (usize i = 0; i < d->map.vars.size; i++) {
|
||||
dbg_var_t *v = &d->map.vars.data[i];
|
||||
if (strcmp(v->func, fn) == 0 && strcmp(v->name, name) == 0)
|
||||
return v;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* 命令
|
||||
* ================================================================ */
|
||||
|
||||
static int cmd_help(dbg_t *d, const char *args);
|
||||
static int cmd_run(dbg_t *d, const char *args);
|
||||
static int cmd_step(dbg_t *d, const char *args);
|
||||
static int cmd_next(dbg_t *d, const char *args);
|
||||
static int cmd_finish(dbg_t *d, const char *args);
|
||||
static int cmd_break(dbg_t *d, const char *args);
|
||||
static int cmd_delete(dbg_t *d, const char *args);
|
||||
static int cmd_info(dbg_t *d, const char *args);
|
||||
static int cmd_list(dbg_t *d, const char *args);
|
||||
static int cmd_backtrace(dbg_t *d, const char *args);
|
||||
static int cmd_stack(dbg_t *d, const char *args);
|
||||
static int cmd_x(dbg_t *d, const char *args);
|
||||
static int cmd_p(dbg_t *d, const char *args);
|
||||
static int cmd_quit(dbg_t *d, const char *args);
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
const char *alias;
|
||||
const char *usage;
|
||||
const char *help;
|
||||
int (*fn)(dbg_t *, const char *);
|
||||
} dbg_cmd_t;
|
||||
|
||||
static const dbg_cmd_t cmds[] = {
|
||||
/* 会话 */
|
||||
{"help", "h", "[cmd]", "list commands / help", cmd_help},
|
||||
{"quit", "q", "", "quit debugger", cmd_quit},
|
||||
/* 执行 */
|
||||
{"run", "r", "", "run to next breakpoint or exit", cmd_run},
|
||||
{"continue", "c", "", "alias of run", cmd_run},
|
||||
{"step", "s", "", "step one instruction (into calls)", cmd_step},
|
||||
{"next", "n", "", "step over a call", cmd_next},
|
||||
{"finish", "f", "", "run until current function returns", cmd_finish},
|
||||
/* 断点 */
|
||||
{"break", "b", "<fn|ip>", "set breakpoint (no arg: list)", cmd_break},
|
||||
{"delete", "d", "", "clear all breakpoints", cmd_delete},
|
||||
/* 信息 */
|
||||
{"info", "i", "b|stack|locals|args|var", "breakpoints/stack/locals", cmd_info},
|
||||
{"list", "l", "[N]", "disassemble ip±N of current function", cmd_list},
|
||||
{"backtrace", "bt", "", "print call stack backtrace", cmd_backtrace},
|
||||
{"stack", "st", "", "dump stack words", cmd_stack},
|
||||
/* 内存/变量 */
|
||||
{"x", "x", "<var|addr|fp+N>", "read memory (no arg: last pointer)", cmd_x},
|
||||
{"print", "p", "<var>", "print local variable (needs splc0 -g)", cmd_p},
|
||||
{NULL, NULL, NULL, NULL, NULL},
|
||||
};
|
||||
|
||||
static const dbg_cmd_t *find_cmd(const char *name) {
|
||||
for (const dbg_cmd_t *c = cmds; c->name; c++)
|
||||
if (!strcmp(c->name, name) || (c->alias && !strcmp(c->alias, name)))
|
||||
return c;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int cmd_help(dbg_t *d, const char *args) {
|
||||
(void)d;
|
||||
if (*args) {
|
||||
const dbg_cmd_t *c = find_cmd(args);
|
||||
if (c) {
|
||||
printf(" %-10s %-22s %s", c->name, c->usage, c->help);
|
||||
if (c->alias)
|
||||
printf(" (alias: %s)", c->alias);
|
||||
printf("\n");
|
||||
} else {
|
||||
printf(" unknown command '%s'\n", args);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
printf("commands:\n");
|
||||
for (const dbg_cmd_t *c = cmds; c->name; c++) {
|
||||
printf(" %-10s %-22s %s", c->name, c->usage, c->help);
|
||||
if (c->alias)
|
||||
printf(" (alias: %s)", c->alias);
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 执行 ---- */
|
||||
|
||||
static int ip_is_breakpoint(dbg_t *d) {
|
||||
for (usize i = 0; i < vec_size(d->vm->breakpoints); i++)
|
||||
if (d->vm->breakpoints.data[i] == (usize)d->vm->ip)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_run(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
if (ip_is_breakpoint(d)) /* 越过当前断点指令,避免原地重停 */
|
||||
spl_vm_skip_breakpoint(d->vm);
|
||||
dbg_handle_status(d, spl_vm_run_until(d->vm, 0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_step(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
dbg_handle_status(d, spl_vm_run_once(d->vm));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_next(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
/* 执行 1 条;若进入被调(cp 增),继续执行到返回原深度 */
|
||||
usize base_cp = d->vm->cp;
|
||||
int r = spl_vm_run_once(d->vm);
|
||||
if (r)
|
||||
return dbg_handle_status(d, r), 0;
|
||||
while (d->vm->cp > base_cp) {
|
||||
r = spl_vm_run_once(d->vm);
|
||||
if (r)
|
||||
return dbg_handle_status(d, r), 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_finish(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
usize base_cp = d->vm->cp;
|
||||
while (d->vm->cp >= base_cp) {
|
||||
int r = spl_vm_run_once(d->vm);
|
||||
if (r)
|
||||
return dbg_handle_status(d, r), 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 断点 ---- */
|
||||
|
||||
static int is_all_digits(const char *s) {
|
||||
if (!*s)
|
||||
return 0;
|
||||
for (; *s; s++)
|
||||
if (!isdigit((unsigned char)*s))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cmd_break(dbg_t *d, const char *args) {
|
||||
if (!*args) {
|
||||
printf(" ip breakpoints:\n");
|
||||
for (usize i = 0; i < vec_size(d->vm->breakpoints); i++)
|
||||
printf(" %zu\n", d->vm->breakpoints.data[i]);
|
||||
printf(" fn breakpoints:\n");
|
||||
for (usize i = 0; i < vec_size(d->vm->fn_breakpoints); i++)
|
||||
printf(" %s\n", d->vm->fn_breakpoints.data[i]);
|
||||
return 0;
|
||||
}
|
||||
while (*args && isspace((unsigned char)*args))
|
||||
args++;
|
||||
if (is_all_digits(args)) {
|
||||
usize ip = (usize)strtoul(args, NULL, 10);
|
||||
spl_vm_add_breakpoint(d->vm, ip);
|
||||
printf(" breakpoint at ip=%zu\n", ip);
|
||||
} else {
|
||||
spl_vm_add_breakpoint_fn(d->vm, args);
|
||||
printf(" breakpoint at function '%s'\n", args);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_delete(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
spl_vm_clear_breakpoints(d->vm);
|
||||
printf(" breakpoints cleared\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 信息 ---- */
|
||||
|
||||
/* 列出当前函数变量:args_flag=1 列参数,local_flag=1 列局部 */
|
||||
static void list_vars(dbg_t *d, int args_flag, int local_flag) {
|
||||
if (!d->map.loaded) {
|
||||
printf(" no debug info (compile with splc0 -g)\n");
|
||||
return;
|
||||
}
|
||||
const char *fn = fn_name_at(d->prog, d->vm->ip);
|
||||
int any = 0;
|
||||
for (usize i = 0; i < d->map.vars.size; i++) {
|
||||
dbg_var_t *v = &d->map.vars.data[i];
|
||||
if (strcmp(v->func, fn) != 0)
|
||||
continue;
|
||||
if ((v->is_param && !args_flag) || (!v->is_param && !local_flag))
|
||||
continue;
|
||||
printf(" %-20s @ fp+%-5zu %s\n", v->name, v->offset, v->is_param ? "[param]" : "[local]");
|
||||
any = 1;
|
||||
}
|
||||
if (!any)
|
||||
printf(" (no matching variables in %s)\n", fn);
|
||||
}
|
||||
|
||||
static int cmd_info(dbg_t *d, const char *args) {
|
||||
while (*args && isspace((unsigned char)*args))
|
||||
args++;
|
||||
if (!strncmp(args, "b", 1) || !strncmp(args, "break", 5))
|
||||
return cmd_break(d, "");
|
||||
if (!strncmp(args, "st", 2) || !strncmp(args, "stack", 5))
|
||||
spl_vm_stackdump(d->vm, d->vm->sp);
|
||||
else if (!strncmp(args, "l", 1) || !strncmp(args, "local", 5))
|
||||
list_vars(d, 0, 1);
|
||||
else if (!strncmp(args, "a", 1) || !strncmp(args, "arg", 3))
|
||||
list_vars(d, 1, 0);
|
||||
else if (!strncmp(args, "v", 1) || !strncmp(args, "var", 3))
|
||||
list_vars(d, 1, 1);
|
||||
else
|
||||
printf(" usage: info b|stack|locals|args|var\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* l [N]:当前函数内 ip±N 反汇编 */
|
||||
static int cmd_list(dbg_t *d, const char *args) {
|
||||
usize ip = d->vm->ip;
|
||||
long half = 5;
|
||||
if (*args)
|
||||
half = strtol(args, NULL, 10);
|
||||
if (half < 0)
|
||||
half = 0;
|
||||
usize fstart, fend;
|
||||
cur_func_bounds(d->prog, ip, &fstart, &fend);
|
||||
usize lo = (ip > (usize)half) ? ip - (usize)half : fstart;
|
||||
usize hi = ip + (usize)half;
|
||||
if (hi >= fend)
|
||||
hi = fend - 1;
|
||||
if (lo < fstart)
|
||||
lo = fstart;
|
||||
for (usize i = lo; i <= hi; i++) {
|
||||
char ib[80];
|
||||
fmt_insn(&vec_at(d->prog->insns, i), ib, sizeof ib);
|
||||
printf("%s%6zu: %s", i == ip ? "=>" : " ", i, ib);
|
||||
const dbg_irline_t *ir = irline_at_exact(&d->map, i);
|
||||
if (ir)
|
||||
printf(" || node#%zu line %d %s", ir->ref, ir->line, ir->kind);
|
||||
printf("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_backtrace(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
spl_vm_backtrace(d->vm, d->vm->fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_stack(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
spl_vm_stackdump(d->vm, d->vm->sp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---- 内存 / 变量 ---- */
|
||||
|
||||
static int cmd_x(dbg_t *d, const char *args) {
|
||||
while (*args && isspace((unsigned char)*args))
|
||||
args++;
|
||||
unsigned long long addr = 0;
|
||||
if (*args) {
|
||||
if (strncmp(args, "fp+", 3) == 0 || strncmp(args, "sp+", 3) == 0) {
|
||||
int is_fp = args[0] == 'f';
|
||||
unsigned long long off = strtoull(args + 3, NULL, 0);
|
||||
addr = (uintptr_t)((char *)&d->vm->stacks.data[is_fp ? d->vm->fp : d->vm->sp] + off);
|
||||
printf(" (%s+%llu)\n", is_fp ? "fp" : "sp", off);
|
||||
} else if (d->map.loaded && !isdigit((unsigned char)*args)) {
|
||||
const char *fn = fn_name_at(d->prog, d->vm->ip);
|
||||
dbg_var_t *v = dbg_find_var(d, fn, args);
|
||||
if (v) {
|
||||
addr = (uintptr_t)((char *)&d->vm->stacks.data[d->vm->fp] + v->offset);
|
||||
printf(" (%s @ fp+%zu)\n", v->name, v->offset);
|
||||
} else {
|
||||
printf(" no variable '%s' in %s\n", args, fn);
|
||||
}
|
||||
} else {
|
||||
addr = strtoull(args, NULL, 0);
|
||||
}
|
||||
} else {
|
||||
addr = (unsigned long long)d->last_ptr;
|
||||
}
|
||||
if (!addr) {
|
||||
printf(" usage: x <var|addr|fp+N> (no arg: re-read last printed pointer)\n");
|
||||
return 0;
|
||||
}
|
||||
unsigned char *sbase = (unsigned char *)d->vm->stacks.data;
|
||||
usize slen = d->vm->config.max_stack_depth * sizeof(spl_val_t);
|
||||
printf(" addr=%#llx stack=[%p,+%zu) in=%d\n", addr, sbase, slen,
|
||||
addr >= (uintptr_t)sbase && addr < (uintptr_t)(sbase + slen));
|
||||
unsigned char *p = (unsigned char *)(uintptr_t)addr;
|
||||
printf(" ");
|
||||
for (int i = 0; i < 32; i++)
|
||||
printf("%02x ", p[i]);
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_p(dbg_t *d, const char *args) {
|
||||
while (*args && isspace((unsigned char)*args))
|
||||
args++;
|
||||
if (!*args) {
|
||||
printf(" usage: p <varname>\n");
|
||||
return 0;
|
||||
}
|
||||
if (!d->map.loaded) {
|
||||
printf(" no debug info (compile with splc0 -g)\n");
|
||||
return 0;
|
||||
}
|
||||
const char *fn = fn_name_at(d->prog, d->vm->ip);
|
||||
dbg_var_t *v = dbg_find_var(d, fn, args);
|
||||
if (!v) {
|
||||
printf(" no variable '%s' in %s\n", args, fn);
|
||||
return 0;
|
||||
}
|
||||
spl_val_t *addr = (spl_val_t *)((char *)&d->vm->stacks.data[d->vm->fp] + v->offset);
|
||||
printf(" %s = %zu (0x%zx) @ fp+%zu\n", v->name, *addr, *addr, v->offset);
|
||||
d->last_ptr = (usize)*addr; /* 供 x 无参重读 */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_quit(dbg_t *d, const char *args) {
|
||||
(void)args;
|
||||
d->quit = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void repl_loop(dbg_t *d) {
|
||||
char line[1024];
|
||||
while (!d->quit) {
|
||||
dbg_show_location(d);
|
||||
printf("spl-dbg> ");
|
||||
fflush(stdout);
|
||||
if (!fgets(line, sizeof line, stdin))
|
||||
break;
|
||||
char *nl = strchr(line, '\n');
|
||||
if (nl)
|
||||
*nl = 0;
|
||||
char *p = line;
|
||||
while (*p && isspace((unsigned char)*p))
|
||||
p++;
|
||||
if (!*p)
|
||||
continue;
|
||||
/* 拆命令名 + 参数 */
|
||||
char *sp = p;
|
||||
while (*sp && !isspace((unsigned char)*sp))
|
||||
sp++;
|
||||
char saved = *sp;
|
||||
*sp = 0;
|
||||
const dbg_cmd_t *c = find_cmd(p);
|
||||
*sp = saved;
|
||||
char *args = sp;
|
||||
while (*args && isspace((unsigned char)*args))
|
||||
args++;
|
||||
if (c) {
|
||||
c->fn(d, args);
|
||||
} else {
|
||||
printf(" unknown command '%s' (h for help)\n", p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int debug_mode = 0;
|
||||
int arg_idx = 1;
|
||||
|
||||
if (argc >= 2 && strcmp(argv[1], "-d") == 0) {
|
||||
debug_mode = 1;
|
||||
arg_idx = 2;
|
||||
}
|
||||
|
||||
if (arg_idx >= argc) {
|
||||
fprintf(stderr, "Usage: spl_cli [-d] <file.sir> [entry_point]\n");
|
||||
cli_opts_t o;
|
||||
if (parse_args(argc, argv, &o) != 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char *path = argv[arg_idx];
|
||||
const char *entry = argc >= arg_idx + 2 ? argv[arg_idx + 1] : "main";
|
||||
|
||||
spl_prog_t prog;
|
||||
if (spl_prog_load_from_file(path, &prog) != 0) {
|
||||
fprintf(stderr, "spl_cli: cannot load '%s'\n", path);
|
||||
if (spl_prog_load_from_file(o.path, &prog) != 0) {
|
||||
fprintf(stderr, "spl_cli: cannot load '%s'\n", o.path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spl_syscall_register(&prog);
|
||||
|
||||
spl_vm_t vm;
|
||||
spl_vm_init(&vm);
|
||||
if (debug_mode) spl_vm_set_debug(&vm, 1);
|
||||
if (o.debug)
|
||||
spl_vm_set_debug(&vm, 1);
|
||||
if (o.trace)
|
||||
spl_vm_set_trace(&vm, 1);
|
||||
if (spl_vm_load_prog(&vm, &prog) != 0) {
|
||||
fprintf(stderr, "vm: prog '%s' not found\n", entry);
|
||||
spl_prog_drop(&prog);
|
||||
return 1;
|
||||
}
|
||||
if (spl_vm_prepare(&vm, entry, argc, argv, NULL) != 0) {
|
||||
fprintf(stderr, "vm: entry point '%s' not found\n", entry);
|
||||
fprintf(stderr, "spl_cli: failed to load prog\n");
|
||||
spl_vm_drop(&vm);
|
||||
spl_prog_drop(&prog);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ret = spl_vm_run_until(&vm, 0);
|
||||
/* spl 程序 argv:argv[0]=file.sir(程序名),argv[1..]=args */
|
||||
const char **spl_argv = (const char **)malloc(sizeof(char *) * (size_t)(o.nargs + 1));
|
||||
if (!spl_argv) {
|
||||
spl_vm_drop(&vm);
|
||||
spl_prog_drop(&prog);
|
||||
return 1;
|
||||
}
|
||||
spl_argv[0] = o.path;
|
||||
for (int i = 0; i < o.nargs; i++)
|
||||
spl_argv[i + 1] = o.args[i];
|
||||
|
||||
if (spl_vm_prepare(&vm, o.entry, o.nargs + 1, spl_argv, NULL) != 0) {
|
||||
fprintf(stderr, "spl_cli: entry point '%s' not found\n", o.entry);
|
||||
free(spl_argv);
|
||||
spl_vm_drop(&vm);
|
||||
spl_prog_drop(&prog);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
if (o.debug_cli) {
|
||||
dbg_t d;
|
||||
d.vm = &vm;
|
||||
d.prog = &prog;
|
||||
d.entry = o.entry;
|
||||
d.quit = 0;
|
||||
d.last_ptr = 0;
|
||||
dbg_map_load(&d.map, prog.debug);
|
||||
repl_loop(&d);
|
||||
vec_free(d.map.irlines);
|
||||
vec_free(d.map.vars);
|
||||
} else {
|
||||
ret = spl_vm_run_until(&vm, 0);
|
||||
if (ret == 2)
|
||||
fprintf(stderr, "spl_cli: hit breakpoint (run with -g to debug)\n");
|
||||
}
|
||||
|
||||
free(spl_argv);
|
||||
spl_vm_drop(&vm);
|
||||
spl_prog_drop(&prog);
|
||||
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "spl_cli: VM error (exit_code=%d)\n", vm.exit_code);
|
||||
return (int)vm.exit_code;
|
||||
}
|
||||
if (ret < 0)
|
||||
return 1;
|
||||
return (int)vm.exit_code;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ void spl_prog_drop(spl_prog_t *prog) {
|
||||
vec_for(prog->strtab, i) { free((void *)vec_at(prog->strtab, i)); }
|
||||
vec_free(prog->strtab);
|
||||
map_free(prog->symtab);
|
||||
free(prog->debug);
|
||||
prog->debug = NULL;
|
||||
}
|
||||
|
||||
/* ---- LE read/write helpers ---- */
|
||||
@@ -201,6 +203,23 @@ int spl_prog_load_from_file(const char *fname, spl_prog_t *prog) {
|
||||
vec_push(prog->gdata, entry);
|
||||
}
|
||||
|
||||
/* ---- debug 段(splc0 -g 追加在文件尾部) ---- */
|
||||
prog->debug = NULL;
|
||||
prog->debug_size = 0;
|
||||
{
|
||||
ptrdiff_t used = p - data;
|
||||
if (used >= 0 && (usize)used < (usize)len) {
|
||||
size_t dlen = (size_t)len - (size_t)used;
|
||||
char *dbg = (char *)malloc(dlen + 1);
|
||||
if (dbg) {
|
||||
memcpy(dbg, data + used, dlen);
|
||||
dbg[dlen] = 0;
|
||||
prog->debug = dbg;
|
||||
prog->debug_size = dlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(data);
|
||||
return 0;
|
||||
}
|
||||
@@ -343,6 +362,14 @@ int spl_prog_store_to_file(const char *fname, spl_prog_t *prog) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spl_prog_add_instr(spl_prog_t *prog, uint8_t opcode, uint8_t type, spl_val_t imm) {
|
||||
if (!prog)
|
||||
return 0;
|
||||
spl_ins_t ins = (spl_ins_t){.opcode = opcode, .type = type, .imm = imm};
|
||||
vec_push(prog->insns, ins);
|
||||
return vec_size(prog->insns);
|
||||
}
|
||||
|
||||
int spl_prog_add_func(spl_prog_t *prog, spl_func_t *func) {
|
||||
if (!prog || !func)
|
||||
return 0;
|
||||
|
||||
@@ -193,6 +193,8 @@ typedef struct spl_prog {
|
||||
spl_data_t gdata;
|
||||
spl_strtab_t strtab;
|
||||
spl_symtab_t symtab;
|
||||
char *debug; /* 文件尾部 debug 段(splc0 -g 追加的文本),无则 NULL */
|
||||
usize debug_size;
|
||||
} spl_prog_t;
|
||||
|
||||
void spl_prog_init(spl_prog_t *prog);
|
||||
@@ -201,6 +203,7 @@ void spl_prog_drop(spl_prog_t *prog);
|
||||
int spl_prog_load_from_file(const char *fname, spl_prog_t *prog);
|
||||
int spl_prog_store_to_file(const char *fname, spl_prog_t *prog);
|
||||
|
||||
int spl_prog_add_instr(spl_prog_t *prog, uint8_t opcode, uint8_t type, spl_val_t imm);
|
||||
int spl_prog_add_data(spl_prog_t *prog, void *ptr, usize size);
|
||||
int spl_prog_add_func(spl_prog_t *prog, spl_func_t *func);
|
||||
int spl_prog_add_native(spl_prog_t *prog, spl_native_t *native);
|
||||
|
||||
@@ -200,6 +200,7 @@ static spl_val_t vm_printf(int nargs, spl_val_t *args) {
|
||||
for (usize j = 0; j < args[arg_idx]; ++j) {
|
||||
vec_push(buffer, ((const char *)args[arg_idx + 1])[j]);
|
||||
}
|
||||
arg_idx += 1; /* %.*s 消耗 2 个实参(len, data),遇 % 已 +1,这里补 1 */
|
||||
i += 2;
|
||||
} break;
|
||||
case 'd':
|
||||
|
||||
107
stage0/spl_vm.c
107
stage0/spl_vm.c
@@ -81,15 +81,17 @@ static int spl_type_size(spl_type_t t) {
|
||||
return -1; \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_ADDR(addr, label) do { \
|
||||
if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \
|
||||
fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", \
|
||||
label, vm->ip - 1, (void*)(uintptr_t)(addr), vm->sp, vm->fp); \
|
||||
spl_vm_stackdump(vm, vm->sp); \
|
||||
spl_vm_backtrace(vm, vm->fp); \
|
||||
vm->exit_code = 1; return -1; \
|
||||
} \
|
||||
} while(0)
|
||||
#define CHECK_ADDR(addr, label) \
|
||||
do { \
|
||||
if (vm->debug_addr && (uintptr_t)(addr) < 0x1000) { \
|
||||
fprintf(stderr, "vm: %s at ip=%zd: LOW ADDR=%p sp=%zd fp=%zd\n", label, vm->ip - 1, \
|
||||
(void *)(uintptr_t)(addr), vm->sp, vm->fp); \
|
||||
spl_vm_stackdump(vm, vm->sp); \
|
||||
spl_vm_backtrace(vm, vm->fp); \
|
||||
vm->exit_code = 1; \
|
||||
return -1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/* ================================================================
|
||||
* Stack push/pop (stacks.data is pre-allocated in init)
|
||||
@@ -546,19 +548,22 @@ void spl_vm_init_ex(spl_vm_t *vm, int stack_size, int call_depth) {
|
||||
#endif
|
||||
if (!vm)
|
||||
return;
|
||||
vm->config.max_stack_depth = stack_size > 0 ? stack_size : 1024 * 8;
|
||||
vm->config.max_stack_depth = stack_size > 0 ? stack_size : 1024 * 1024 * 8;
|
||||
vm->config.max_call_depth = call_depth > 0 ? call_depth : 128;
|
||||
|
||||
vec_init(vm->stacks);
|
||||
vec_realloc(vm->stacks, (usize)vm->config.max_stack_depth);
|
||||
vec_init(vm->frames);
|
||||
vec_realloc(vm->frames, (usize)vm->config.max_call_depth);
|
||||
vec_init(vm->breakpoints);
|
||||
vec_init(vm->fn_breakpoints);
|
||||
|
||||
vm->sp = vm->fp = vm->ip = vm->cp = vm->gp = 0;
|
||||
vm->prog = NULL;
|
||||
vm->trace = 0;
|
||||
vm->debug = 1;
|
||||
vm->debug_addr = 0;
|
||||
vm->skip_bp = 0;
|
||||
vm->exit_code = 0;
|
||||
}
|
||||
|
||||
@@ -569,6 +574,68 @@ void spl_vm_drop(spl_vm_t *vm) {
|
||||
return;
|
||||
vec_free(vm->stacks);
|
||||
vec_free(vm->frames);
|
||||
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
|
||||
free(vm->fn_breakpoints.data[i]);
|
||||
vec_free(vm->fn_breakpoints);
|
||||
vec_free(vm->breakpoints);
|
||||
}
|
||||
|
||||
void spl_vm_add_breakpoint(spl_vm_t *vm, usize ip) {
|
||||
if (!vm)
|
||||
return;
|
||||
for (usize i = 0; i < vec_size(vm->breakpoints); i++)
|
||||
if (vm->breakpoints.data[i] == ip)
|
||||
return;
|
||||
vec_push(vm->breakpoints, ip);
|
||||
}
|
||||
|
||||
void spl_vm_add_breakpoint_fn(spl_vm_t *vm, const char *name) {
|
||||
if (!vm || !name)
|
||||
return;
|
||||
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
|
||||
if (strcmp(vm->fn_breakpoints.data[i], name) == 0)
|
||||
return;
|
||||
vec_push(vm->fn_breakpoints, strdup(name));
|
||||
}
|
||||
|
||||
void spl_vm_clear_breakpoints(spl_vm_t *vm) {
|
||||
if (!vm)
|
||||
return;
|
||||
vm->breakpoints.size = 0;
|
||||
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
|
||||
free(vm->fn_breakpoints.data[i]);
|
||||
vm->fn_breakpoints.size = 0;
|
||||
}
|
||||
|
||||
void spl_vm_skip_breakpoint(spl_vm_t *vm) {
|
||||
if (vm)
|
||||
vm->skip_bp = 1;
|
||||
}
|
||||
|
||||
/* 前向声明(定义在文件尾部 dump 区域) */
|
||||
static const char *func_name_by_ip(spl_prog_t *prog, spl_val_t ip);
|
||||
|
||||
/* 函数名断点命中:addr 所在函数名是否在断点表 */
|
||||
static int fn_breakpoint_hit(spl_vm_t *vm, spl_val_t addr) {
|
||||
if (!vm || !vm->prog || !vec_size(vm->fn_breakpoints))
|
||||
return 0;
|
||||
const char *fn = func_name_by_ip(vm->prog, addr);
|
||||
if (!fn)
|
||||
return 0;
|
||||
for (usize i = 0; i < vec_size(vm->fn_breakpoints); i++)
|
||||
if (strcmp(vm->fn_breakpoints.data[i], fn) == 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ip 断点命中:vm->ip(即将执行的指令)在断点表 */
|
||||
static int ip_breakpoint_hit(spl_vm_t *vm) {
|
||||
if (!vm || !vec_size(vm->breakpoints))
|
||||
return 0;
|
||||
for (usize i = 0; i < vec_size(vm->breakpoints); i++)
|
||||
if (vm->breakpoints.data[i] == (usize)vm->ip)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
@@ -678,6 +745,16 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 断点:即将执行的指令命中 ip 断点 → 暂停(返回 2)。
|
||||
* skip_bp 置位时执行当前指令(continue 越过当前断点)。 */
|
||||
if (ip_breakpoint_hit(vm)) {
|
||||
if (vm->skip_bp) {
|
||||
vm->skip_bp = 0;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
ins = &vec_at(prog->insns, vm->ip);
|
||||
vm->ip++;
|
||||
|
||||
@@ -870,14 +947,20 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
case SPL_CALL: {
|
||||
spl_val_t _nargs = ins->imm;
|
||||
spl_val_t _addr = POP();
|
||||
int _hit = fn_breakpoint_hit(vm, _addr);
|
||||
spl_vm_call(vm, _addr, _nargs);
|
||||
if (_hit)
|
||||
return 2; /* 已进入函数,暂停在函数入口 */
|
||||
break;
|
||||
}
|
||||
|
||||
case SPL_CALLI: {
|
||||
spl_val_t _addr = POP();
|
||||
spl_val_t _nargs = POP();
|
||||
int _hit = fn_breakpoint_hit(vm, _addr);
|
||||
spl_vm_call(vm, _addr, _nargs);
|
||||
if (_hit)
|
||||
return 2;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -906,6 +989,8 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
}
|
||||
|
||||
case SPL_HALT:
|
||||
if (ins->imm)
|
||||
vm->exit_code = (int)ins->imm;
|
||||
return 1;
|
||||
|
||||
/* ========== Stack / Frame Local Memory ========== */
|
||||
@@ -1053,7 +1138,7 @@ int spl_vm_run_once(spl_vm_t *vm) {
|
||||
}
|
||||
|
||||
case SPL_BK: {
|
||||
return 0; /* breakpoint: pause execution */
|
||||
return 2; /* breakpoint: pause execution */
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -28,9 +28,12 @@ typedef struct {
|
||||
uintptr_t cp; // call pointer
|
||||
uintptr_t ip; // instr pointer
|
||||
int exit_code;
|
||||
int trace; /* non-zero to print each instruction */
|
||||
int debug; /* non-zero to enable canary checks */
|
||||
int debug_addr; /* non-zero to check for low-address memory access */
|
||||
int trace; /* non-zero to print each instruction */
|
||||
int debug; /* non-zero to enable canary checks */
|
||||
int debug_addr; /* non-zero to check for low-address memory access */
|
||||
int skip_bp; /* non-zero: run_once 忽略下一次断点(continue 越过当前断点指令) */
|
||||
VEC(usize) breakpoints; /* ip 断点(执行到该指令前暂停,run_once 返回 2) */
|
||||
VEC(char *) fn_breakpoints; /* 函数名断点(CALL/CALLI 目标函数入口暂停) */
|
||||
spl_prog_t *prog;
|
||||
char error_msg[1024];
|
||||
struct {
|
||||
@@ -61,6 +64,13 @@ void spl_vm_set_debug(spl_vm_t *vm, int enabled);
|
||||
int spl_vm_run_once(spl_vm_t *vm);
|
||||
int spl_vm_run_until(spl_vm_t *vm, size_t step);
|
||||
|
||||
/* 断点:ip 断点 / 函数名断点(CALL/CALLI 目标函数入口)。命中时 run_once 返回 2。 */
|
||||
void spl_vm_add_breakpoint(spl_vm_t *vm, usize ip);
|
||||
void spl_vm_add_breakpoint_fn(spl_vm_t *vm, const char *name);
|
||||
void spl_vm_clear_breakpoints(spl_vm_t *vm);
|
||||
/* 让 run_once 执行当前指令(忽略一次断点命中);continue 越过当前断点用 */
|
||||
void spl_vm_skip_breakpoint(spl_vm_t *vm);
|
||||
|
||||
void spl_vm_dump_instr(spl_vm_t *vm, spl_val_t ip);
|
||||
void spl_vm_stackdump(spl_vm_t *vm, spl_val_t sp);
|
||||
int spl_vm_backtrace(spl_vm_t *vm, spl_val_t fp);
|
||||
|
||||
@@ -147,11 +147,12 @@ static spl_tok_t *peek_next(parser_t *p) {
|
||||
}
|
||||
|
||||
static void parse_error(parser_t *p, spl_tok_t *t, const char *msg) {
|
||||
(void)p;
|
||||
p->failed = 1; /* 出错即停(上层循环以 !p->failed 提前退出) */
|
||||
const char *fn = (t && t->fname) ? t->fname : "<unknown>";
|
||||
usize line = t ? t->line : 0;
|
||||
usize col = t ? t->col : 0;
|
||||
LOG_FATAL("%s:%llu:%llu: error: %s", fn, (unsigned long long)line, (unsigned long long)col, msg);
|
||||
LOG_ERROR("%s:%llu:%llu: error: %s", fn, (unsigned long long)line, (unsigned long long)col,
|
||||
msg);
|
||||
}
|
||||
|
||||
static int expect(parser_t *p, spl_tok_type_t type, const char *what) {
|
||||
@@ -222,7 +223,7 @@ static spl_ast_node_ref_t wrap_op_expr(parser_t *p, spl_ast_node_ref_t inner, in
|
||||
/* ================================================================
|
||||
* 字面量解 * ================================================================ */
|
||||
|
||||
/* 整数字面量:去下划线后按0 解析x/0o/0b/十进制,忽略 0b 特判*/
|
||||
/* 整数字面量:去下划线后按 0x/0o/0b/十进制解析 */
|
||||
static isize parse_int_literal(const spl_tok_t *t) {
|
||||
char buf[128];
|
||||
usize j = 0;
|
||||
@@ -231,6 +232,11 @@ static isize parse_int_literal(const spl_tok_t *t) {
|
||||
buf[j++] = t->lexeme[i];
|
||||
}
|
||||
buf[j] = 0;
|
||||
/* strtoll base 0 不认 0b/0o 前缀:单独处理 */
|
||||
if (j >= 3 && buf[0] == '0' && (buf[1] == 'b' || buf[1] == 'B'))
|
||||
return (isize)strtoll(buf + 2, NULL, 2);
|
||||
if (j >= 3 && buf[0] == '0' && (buf[1] == 'o' || buf[1] == 'O'))
|
||||
return (isize)strtoll(buf + 2, NULL, 8);
|
||||
return (isize)strtoll(buf, NULL, 0);
|
||||
}
|
||||
|
||||
@@ -636,7 +642,7 @@ static spl_ast_node_ref_vec_t parse_block(parser_t *p) {
|
||||
vec_init(vec);
|
||||
if (!expect(p, TOK_L_BRACE, "expected '{'"))
|
||||
return vec;
|
||||
while (1) {
|
||||
while (!p->failed) {
|
||||
spl_tok_t *t = peek(p);
|
||||
if (!t || t->type == TOK_EOF) {
|
||||
parse_error(p, t, "block missing '}'");
|
||||
@@ -1780,10 +1786,11 @@ static spl_ast_node_ref_t parse_type_atom(parser_t *p) {
|
||||
kind = SPL_AST_TYPE_USIZE;
|
||||
else if (IS_TY("ptr"))
|
||||
kind = SPL_AST_TYPE_PTR;
|
||||
else if (IS_TY("f32") || IS_TY("f64")) {
|
||||
parse_error(p, t, "not implemented: f32/f64 type");
|
||||
kind = SPL_AST_TYPE_IDENT;
|
||||
} else {
|
||||
else if (IS_TY("f32"))
|
||||
kind = SPL_AST_TYPE__F32;
|
||||
else if (IS_TY("f64"))
|
||||
kind = SPL_AST_TYPE__F64;
|
||||
else {
|
||||
kind = SPL_AST_TYPE_IDENT;
|
||||
}
|
||||
#undef IS_TY
|
||||
@@ -1826,7 +1833,7 @@ void spl_ast_prase(spl_ast_t *ast) {
|
||||
|
||||
spl_ast_node_ref_vec_t members;
|
||||
vec_init(members);
|
||||
while (1) {
|
||||
while (!p.failed) {
|
||||
spl_tok_t *t = peek(&p);
|
||||
if (!t || t->type == TOK_EOF)
|
||||
break;
|
||||
|
||||
@@ -35,6 +35,8 @@ 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;
|
||||
|
||||
usize resolved_def_id;
|
||||
union {
|
||||
struct {
|
||||
spl_ast_node_ref_vec_t attr_list; /* attr_item */
|
||||
|
||||
3247
stage1/spl_ast2ir.c
3247
stage1/spl_ast2ir.c
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,26 @@
|
||||
#ifndef __SPL_AST2IR_H__
|
||||
#define __SPL_AST2IR_H__
|
||||
|
||||
#include "spl_ir.h"
|
||||
#include "spl_sema.h"
|
||||
|
||||
/* 全局 var/const 的 def → gdata 向量中的 value 节点索引 */
|
||||
typedef struct {
|
||||
spl_def_id_t def_id;
|
||||
usize gdata_idx;
|
||||
} spl_ast2ir_gref_t;
|
||||
|
||||
typedef struct {
|
||||
const spl_sema_t *sema;
|
||||
spl_ir_t ir;
|
||||
VEC(char *) owned_names; /* 本模块 malloc 的函数名,drop 时释放 */
|
||||
VEC(spl_ast2ir_gref_t) gdata_ref; /* def_id → gdata value 节点索引 */
|
||||
int err_count;
|
||||
} spl_ast2ir_t;
|
||||
|
||||
void spl_ast2ir_init(spl_ast2ir_t *ast2ir, const spl_sema_t *sema);
|
||||
void spl_ast2ir_drop(spl_ast2ir_t *ast2ir);
|
||||
|
||||
void spl_ast2ir_run(spl_ast2ir_t *ast2ir);
|
||||
|
||||
#endif /* __SPL_AST2IR_H__ */
|
||||
|
||||
28
stage1/spl_builtin.c
Normal file
28
stage1/spl_builtin.c
Normal file
@@ -0,0 +1,28 @@
|
||||
/* spl_builtin.c — 内置函数集中注册表实现 */
|
||||
|
||||
#include "spl_builtin.h"
|
||||
#include "../stage0/include/utils.h"
|
||||
#include <string.h>
|
||||
|
||||
static const spl_builtin_t k_builtins[] = {
|
||||
{"sizeof", SPL_BUILTIN_SIZE_OF, 1, 1, 0},
|
||||
{"bitsizeof", SPL_BUILTIN_BITSIZE_OF, 1, 1, 0},
|
||||
{"alignof", SPL_BUILTIN_ALIGN_OF, 1, 1, 0},
|
||||
{"offsetof", SPL_BUILTIN_OFFSET_OF, 2, 2, 0},
|
||||
{"field_count", SPL_BUILTIN_FIELD_COUNT, 1, 1, 0},
|
||||
{"dbg", SPL_BUILTIN_DBG, 0, -1, 1},
|
||||
{"assert", SPL_BUILTIN_ASSERT, 1, 1, 1},
|
||||
{"import", SPL_BUILTIN_IMPORT, 1, 1, 1},
|
||||
};
|
||||
|
||||
const spl_builtin_t *spl_builtin_lookup(const char *name) {
|
||||
if (!name)
|
||||
return NULL;
|
||||
for (usize i = 0; i < sizeof(k_builtins) / sizeof(k_builtins[0]); i++) {
|
||||
if (strcmp(k_builtins[i].name, name) == 0)
|
||||
return &k_builtins[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int spl_builtin_count(void) { return (int)(sizeof(k_builtins) / sizeof(k_builtins[0])); }
|
||||
43
stage1/spl_builtin.h
Normal file
43
stage1/spl_builtin.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* spl_builtin.h — 内置函数集中注册表
|
||||
*
|
||||
* @builtin 名称/类别/参数/返回类型的单一事实来源。
|
||||
* sema(类型推导)与 ast2ir(IR 发射)各自按 kind 分发实现。
|
||||
*/
|
||||
#ifndef __SPL_BUILTIN_H__
|
||||
#define __SPL_BUILTIN_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SPL_BUILTIN_SIZE_OF, /* @sizeof(T) -> usize */
|
||||
SPL_BUILTIN_BITSIZE_OF, /* @bitsizeof(T) -> usize */
|
||||
SPL_BUILTIN_ALIGN_OF, /* @alignof(T) -> usize */
|
||||
SPL_BUILTIN_OFFSET_OF, /* @offsetof(T, field) -> usize */
|
||||
SPL_BUILTIN_FIELD_COUNT, /* @field_count(T) -> usize */
|
||||
SPL_BUILTIN_DBG, /* @dbg(args...) -> void */
|
||||
SPL_BUILTIN_ASSERT, /* @assert(cond) -> void */
|
||||
SPL_BUILTIN_IMPORT, /* @import(path) -> 模块 (预留) */
|
||||
SPL_BUILTIN_COUNT
|
||||
} spl_builtin_kind_t;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
spl_builtin_kind_t kind;
|
||||
int min_args; /* 参数个数下限 */
|
||||
int max_args; /* 参数个数上限,-1 = 不限 */
|
||||
int ret_is_void; /* 1 = void 返回 */
|
||||
} spl_builtin_t;
|
||||
|
||||
/* 按名称查找内置;未注册返回 NULL */
|
||||
const spl_builtin_t *spl_builtin_lookup(const char *name);
|
||||
|
||||
/* 内置总数 */
|
||||
int spl_builtin_count(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __SPL_BUILTIN_H__ */
|
||||
518
stage1/spl_ir.c
518
stage1/spl_ir.c
@@ -1 +1,519 @@
|
||||
/* spl_ir.c — function-based SIR IR (arena 容器 + 文本 dump) */
|
||||
|
||||
#include "spl_ir.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* ---- 内置函数名字表(与 spl_ir_kind_t 枚举一一对应) ---- */
|
||||
static const char *const ir_kind_names[] = {
|
||||
#define X(a, b, c) #a,
|
||||
SPL_IR_FN_TABLE
|
||||
#undef X
|
||||
};
|
||||
|
||||
void spl_ir_init(spl_ir_t *ir) {
|
||||
memset(ir, 0, sizeof *ir);
|
||||
vec_init(ir->funcs);
|
||||
vec_init(ir->gdata);
|
||||
/* func ref 0 保留为 error,占位 */
|
||||
spl_ir_func_t f0;
|
||||
memset(&f0, 0, sizeof f0);
|
||||
vec_push(ir->funcs, f0);
|
||||
}
|
||||
|
||||
static void node_drop_vecs(spl_ir_node_t *n) {
|
||||
if (!n)
|
||||
return;
|
||||
switch (n->kind) {
|
||||
case SPL_IR_AGG_CONSTRUCT:
|
||||
vec_free(n->agg_construct.fields);
|
||||
break;
|
||||
case SPL_IR_CONTROL_CALL:
|
||||
vec_free(n->control_call.params);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void spl_ir_drop(spl_ir_t *ir) {
|
||||
for (usize i = 0; i < ir->funcs.size; i++) {
|
||||
spl_ir_func_t *f = &ir->funcs.data[i];
|
||||
for (usize j = 0; j < f->nodes.size; j++)
|
||||
node_drop_vecs(&f->nodes.data[j]);
|
||||
vec_free(f->nodes);
|
||||
vec_free(f->labels);
|
||||
vec_free(f->dbg_vars);
|
||||
}
|
||||
vec_free(ir->funcs);
|
||||
for (usize i = 0; i < ir->gdata.size; i++)
|
||||
node_drop_vecs(&ir->gdata.data[i]);
|
||||
vec_free(ir->gdata);
|
||||
}
|
||||
|
||||
spl_ir_func_ref_t spl_ir_alloc_fn(spl_ir_t *ir) {
|
||||
spl_ir_func_t f;
|
||||
memset(&f, 0, sizeof f);
|
||||
vec_init(f.nodes);
|
||||
vec_init(f.labels);
|
||||
vec_init(f.dbg_vars);
|
||||
vec_push(ir->funcs, f);
|
||||
return ir->funcs.size - 1;
|
||||
}
|
||||
|
||||
spl_ir_node_ref_t spl_ir_alloc_node(spl_ir_t *ir, spl_ir_func_ref_t fn_id) {
|
||||
if (!fn_id || fn_id >= ir->funcs.size)
|
||||
return 0;
|
||||
spl_ir_func_t *f = &ir->funcs.data[fn_id];
|
||||
spl_ir_node_t n;
|
||||
memset(&n, 0, sizeof n);
|
||||
if (f->nodes.size == 0) {
|
||||
vec_push(f->nodes, n); /* 占位:node ref 0 保留为 error */
|
||||
}
|
||||
vec_push(f->nodes, n);
|
||||
return f->nodes.size - 1;
|
||||
}
|
||||
|
||||
spl_ir_node_t *spl_ir_node(spl_ir_t *ir, spl_ir_func_ref_t fn_id, spl_ir_node_ref_t node_id) {
|
||||
if (!fn_id || fn_id >= ir->funcs.size)
|
||||
return NULL;
|
||||
spl_ir_func_t *f = &ir->funcs.data[fn_id];
|
||||
if (!node_id || node_id >= f->nodes.size)
|
||||
return NULL;
|
||||
return &f->nodes.data[node_id];
|
||||
}
|
||||
|
||||
spl_ir_func_t *spl_ir_func(spl_ir_t *ir, spl_ir_func_ref_t fn_id) {
|
||||
if (!fn_id || fn_id >= ir->funcs.size)
|
||||
return NULL;
|
||||
return &ir->funcs.data[fn_id];
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
* dump
|
||||
* ================================================================ */
|
||||
|
||||
static void ir_type_dump(const spl_type_t *ty, spl_type_id_t tid) {
|
||||
spl_type_node_t *n = spl_type_node((spl_type_t *)ty, tid);
|
||||
if (!n) {
|
||||
printf("?");
|
||||
return;
|
||||
}
|
||||
switch (n->kind) {
|
||||
case SPL_TYPE_VOID:
|
||||
printf("void");
|
||||
break;
|
||||
case SPL_TYPE_BOOL:
|
||||
printf("bool");
|
||||
break;
|
||||
case SPL_TYPE_INT:
|
||||
printf("%s%zu", n->int_type.is_signed ? "i" : "u", n->int_type.bits);
|
||||
break;
|
||||
case SPL_TYPE_FLOAT:
|
||||
printf("f%zu", n->float_type.bits);
|
||||
break;
|
||||
case SPL_TYPE_PTR:
|
||||
printf("*");
|
||||
ir_type_dump(ty, n->ptr_pointee);
|
||||
break;
|
||||
case SPL_TYPE_SLICE:
|
||||
printf("[]");
|
||||
ir_type_dump(ty, n->slice_element);
|
||||
break;
|
||||
case SPL_TYPE_RANGE:
|
||||
printf("range[");
|
||||
ir_type_dump(ty, n->range_element);
|
||||
printf("]");
|
||||
break;
|
||||
case SPL_TYPE_ARRAY:
|
||||
printf("[%zu]", n->array_type.len);
|
||||
ir_type_dump(ty, n->array_type.element);
|
||||
break;
|
||||
case SPL_TYPE_STRUCT:
|
||||
printf("struct#%zu", tid);
|
||||
break;
|
||||
case SPL_TYPE_UNION:
|
||||
printf("union#%zu", tid);
|
||||
break;
|
||||
case SPL_TYPE_ENUM:
|
||||
printf("enum#%zu", tid);
|
||||
break;
|
||||
case SPL_TYPE_FN:
|
||||
printf("fn<");
|
||||
for (usize i = 0; i < n->fn_type.params.size; i++) {
|
||||
if (i)
|
||||
printf(",");
|
||||
ir_type_dump(ty, n->fn_type.params.data[i]);
|
||||
}
|
||||
printf("->");
|
||||
ir_type_dump(ty, n->fn_type.ret);
|
||||
printf(">");
|
||||
break;
|
||||
case SPL_TYPE_ID:
|
||||
ir_type_dump(ty, n->type_id);
|
||||
break;
|
||||
default:
|
||||
printf("?%zu", tid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *node_name(spl_ir_kind_t k) {
|
||||
if ((usize)k < sizeof(ir_kind_names) / sizeof(ir_kind_names[0]))
|
||||
return ir_kind_names[k];
|
||||
return "?";
|
||||
}
|
||||
|
||||
static int node_produces_value(spl_ir_kind_t k) {
|
||||
switch (k) {
|
||||
case SPL_IR_MEM_STORE:
|
||||
case SPL_IR_MEM_COPY:
|
||||
case SPL_IR_MEM_SET:
|
||||
case SPL_IR_MEM_FENCE:
|
||||
case SPL_IR_CONTROL_BR:
|
||||
case SPL_IR_CONTROL_JMP:
|
||||
case SPL_IR_CONTROL_RET:
|
||||
case SPL_IR_CONTROL_UNREACHABLE:
|
||||
case SPL_IR_CONTROL_TRAP:
|
||||
case SPL_IR_DBG_BREAKPOINT:
|
||||
case SPL_IR_DBG_DECLARE:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_ref(spl_ir_node_ref_t r) { printf("%%%zu", r); }
|
||||
|
||||
static void dump_node(const spl_type_t *ty, const spl_ir_node_t *n) {
|
||||
switch (n->kind) {
|
||||
case SPL_IR_TYPE_CONST:
|
||||
printf("@type.const(");
|
||||
ir_type_dump(ty, n->type_const.tid);
|
||||
printf(")(");
|
||||
{
|
||||
spl_type_node_t *t = spl_type_node((spl_type_t *)ty, n->type_const.tid);
|
||||
if (t && t->kind == SPL_TYPE_INT)
|
||||
printf("%lld", (long long)n->type_const.int_lit);
|
||||
else if (t && t->kind == SPL_TYPE_FLOAT)
|
||||
printf("%g", n->type_const.float_lit);
|
||||
else if (t && t->kind == SPL_TYPE_FN)
|
||||
printf("@fn#%zu", n->type_const.fn);
|
||||
else if (t && (t->kind == SPL_TYPE_PTR || t->kind == SPL_TYPE_SLICE)) {
|
||||
if (n->type_const.cstr_lit)
|
||||
printf("\"%s\"", n->type_const.cstr_lit);
|
||||
else
|
||||
printf("0");
|
||||
} else if (t && t->kind == SPL_TYPE_BOOL)
|
||||
printf("%lld", (long long)n->type_const.int_lit);
|
||||
else
|
||||
printf("?");
|
||||
}
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_ARITH_ADD:
|
||||
case SPL_IR_ARITH_SUB:
|
||||
case SPL_IR_ARITH_MUL:
|
||||
case SPL_IR_ARITH_DIV:
|
||||
case SPL_IR_ARITH_REM:
|
||||
case SPL_IR_ARITH_AND:
|
||||
case SPL_IR_ARITH_OR:
|
||||
case SPL_IR_ARITH_XOR:
|
||||
case SPL_IR_ARITH_SHL:
|
||||
case SPL_IR_ARITH_SHR:
|
||||
printf("@%s(", node_name(n->kind));
|
||||
ir_type_dump(ty, n->arith.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->arith.left);
|
||||
if (n->arith.right) {
|
||||
printf(", ");
|
||||
dump_ref(n->arith.right);
|
||||
}
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_ARITH_NEG:
|
||||
case SPL_IR_ARITH_ABS:
|
||||
case SPL_IR_ARITH_NOT:
|
||||
printf("@%s(", node_name(n->kind));
|
||||
ir_type_dump(ty, n->arith.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->arith.left);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CMP_EQ:
|
||||
case SPL_IR_CMP_NE:
|
||||
case SPL_IR_CMP_LT:
|
||||
case SPL_IR_CMP_LE:
|
||||
case SPL_IR_CMP_GT:
|
||||
case SPL_IR_CMP_GE:
|
||||
printf("@%s(", node_name(n->kind));
|
||||
ir_type_dump(ty, n->cmp.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->cmp.a);
|
||||
printf(", ");
|
||||
dump_ref(n->cmp.b);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CAST_TRUNC:
|
||||
case SPL_IR_CAST_ZEXT:
|
||||
case SPL_IR_CAST_SEXT:
|
||||
case SPL_IR_CAST_FEXT:
|
||||
case SPL_IR_CAST_FTRUNC:
|
||||
case SPL_IR_CAST_BITCAST:
|
||||
case SPL_IR_CAST_PTR2INT:
|
||||
case SPL_IR_CAST_INT2PTR:
|
||||
case SPL_IR_CAST_BOOL2INT:
|
||||
case SPL_IR_CASE_INT2FLOAT:
|
||||
case SPL_IR_CASE_FLOAT2INT:
|
||||
printf("@%s(", node_name(n->kind));
|
||||
ir_type_dump(ty, n->cast.from_tid);
|
||||
printf(", ");
|
||||
ir_type_dump(ty, n->cast.to_tid);
|
||||
printf(")(");
|
||||
dump_ref(n->cast.val);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_ALLOCA:
|
||||
printf("@mem.alloca(");
|
||||
ir_type_dump(ty, n->mem_alloc.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->mem_alloc.count);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_LOAD:
|
||||
printf("@mem.load(");
|
||||
ir_type_dump(ty, n->mem_load.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->mem_load.ptr);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_STORE:
|
||||
printf("@mem.store(");
|
||||
ir_type_dump(ty, n->mem_store.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->mem_store.ptr);
|
||||
printf(", ");
|
||||
dump_ref(n->mem_store.val);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_OFFSET:
|
||||
printf("@mem.offset(");
|
||||
ir_type_dump(ty, n->mem_offset.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->mem_offset.ptr);
|
||||
printf(", ");
|
||||
dump_ref(n->mem_offset.offset);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_FIELD_PTR:
|
||||
printf("@mem.field_ptr(");
|
||||
ir_type_dump(ty, n->mem_field_ptr.tid);
|
||||
printf(", %zu)(", (size_t)n->mem_field_ptr.field_idx);
|
||||
dump_ref(n->mem_field_ptr.agg);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_GLOBAL_ALLOC:
|
||||
printf("@mem.global_alloc(gdata[%zu])()", (size_t)n->mem_global_alloc.const_node);
|
||||
break;
|
||||
case SPL_IR_MEM_COPY:
|
||||
printf("@mem.copy()(");
|
||||
dump_ref(n->mem_copy.dst);
|
||||
printf(", ");
|
||||
dump_ref(n->mem_copy.src);
|
||||
printf(", ");
|
||||
dump_ref(n->mem_copy.size);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_SET:
|
||||
printf("@mem.set()(");
|
||||
dump_ref(n->mem_set.dst);
|
||||
printf(", ");
|
||||
dump_ref(n->mem_set.val);
|
||||
printf(", ");
|
||||
dump_ref(n->mem_set.size);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_MEM_FENCE:
|
||||
printf("@mem.fence()(");
|
||||
dump_ref(n->mem_fence.ordering);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_TYPE_BITSIZEOF:
|
||||
printf("@type.bitsizeof(");
|
||||
ir_type_dump(ty, n->bitsizeof.tid);
|
||||
printf(")()");
|
||||
break;
|
||||
case SPL_IR_TYPE_SIZEOF:
|
||||
printf("@type.sizeof(");
|
||||
ir_type_dump(ty, n->ir_sizeof.tid);
|
||||
printf(")()");
|
||||
break;
|
||||
case SPL_IR_TYPE_ALIGNOF:
|
||||
printf("@type.alignof(");
|
||||
ir_type_dump(ty, n->ir_alignof.tid);
|
||||
printf(")()");
|
||||
break;
|
||||
case SPL_IR_TYPE_OFFSETOF:
|
||||
printf("@type.offsetof(");
|
||||
ir_type_dump(ty, n->ir_offsetof.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->ir_offsetof.field_idx);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_TYPE_FIELD_COUNT:
|
||||
printf("@type.field_count(");
|
||||
ir_type_dump(ty, n->field_count.tid);
|
||||
printf(")()");
|
||||
break;
|
||||
case SPL_IR_AGG_CONSTRUCT:
|
||||
printf("@agg.construct(");
|
||||
ir_type_dump(ty, n->agg_construct.tid);
|
||||
printf(")(");
|
||||
for (usize i = 0; i < n->agg_construct.fields.size; i++) {
|
||||
if (i)
|
||||
printf(", ");
|
||||
dump_ref(n->agg_construct.fields.data[i]);
|
||||
}
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_AGG_EXTRACT:
|
||||
printf("@agg.extract(");
|
||||
ir_type_dump(ty, n->agg_extract.tid);
|
||||
printf(", %lld)(", (long long)n->agg_extract.field_idx);
|
||||
dump_ref(n->agg_extract.val);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_AGG_INSERT:
|
||||
printf("@agg.insert(");
|
||||
ir_type_dump(ty, n->agg_insert.tid);
|
||||
printf(", %lld)(", (long long)n->agg_insert.field_idx);
|
||||
dump_ref(n->agg_insert.agg);
|
||||
printf(", ");
|
||||
dump_ref(n->agg_insert.field);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CONTROL_SELECT:
|
||||
printf("@control.select(");
|
||||
ir_type_dump(ty, n->control_select.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->control_select.cond);
|
||||
printf(", ");
|
||||
dump_ref(n->control_select.true_val);
|
||||
printf(", ");
|
||||
dump_ref(n->control_select.false_val);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CONTROL_BR:
|
||||
printf("@control.br()(");
|
||||
dump_ref(n->control_br.cond);
|
||||
printf(", ");
|
||||
dump_ref(n->control_br.true_label);
|
||||
printf(", ");
|
||||
dump_ref(n->control_br.false_label);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CONTROL_JMP:
|
||||
printf("@control.jmp()(");
|
||||
dump_ref(n->control_jmp.label);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CONTROL_CALL:
|
||||
printf("@control.call(");
|
||||
ir_type_dump(ty, n->control_call.tid);
|
||||
printf(")(");
|
||||
dump_ref(n->control_call.func);
|
||||
for (usize i = 0; i < n->control_call.params.size; i++) {
|
||||
printf(", ");
|
||||
dump_ref(n->control_call.params.data[i]);
|
||||
}
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CONTROL_PARAM:
|
||||
printf("@control.param(");
|
||||
ir_type_dump(ty, n->control_param.tid);
|
||||
printf(")(%lld)", (long long)n->control_param.idx);
|
||||
break;
|
||||
case SPL_IR_CONTROL_RET:
|
||||
printf("@control.ret(");
|
||||
ir_type_dump(ty, n->control_ret.tid);
|
||||
printf(")(");
|
||||
if (n->control_ret.val)
|
||||
dump_ref(n->control_ret.val);
|
||||
printf(")");
|
||||
break;
|
||||
case SPL_IR_CONTROL_UNREACHABLE:
|
||||
printf("@control.unreachable()()");
|
||||
break;
|
||||
case SPL_IR_CONTROL_TRAP:
|
||||
printf("@control.trap()()");
|
||||
break;
|
||||
case SPL_IR_DBG_BREAKPOINT:
|
||||
printf("@dbg.breakpoint()()");
|
||||
break;
|
||||
case SPL_IR_DBG_DECLARE:
|
||||
printf("@dbg.declare()()");
|
||||
break;
|
||||
default:
|
||||
printf("@%s()()", node_name(n->kind));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dump_func(const spl_type_t *ty, const spl_ir_func_t *f) {
|
||||
printf("func @%s", f->name ? f->name : "?");
|
||||
if (f->fn_tid) {
|
||||
spl_type_node_t *t = spl_type_node((spl_type_t *)ty, f->fn_tid);
|
||||
if (t && t->kind == SPL_TYPE_FN) {
|
||||
printf("(");
|
||||
for (usize i = 0; i < t->fn_type.params.size; i++) {
|
||||
if (i)
|
||||
printf(", ");
|
||||
ir_type_dump(ty, t->fn_type.params.data[i]);
|
||||
}
|
||||
printf(") -> ");
|
||||
ir_type_dump(ty, t->fn_type.ret);
|
||||
}
|
||||
}
|
||||
printf(" {\n");
|
||||
usize li = 0;
|
||||
for (usize i = 1; i < f->nodes.size; i++) {
|
||||
if (li < f->labels.size && f->labels.data[li] == i) {
|
||||
printf("#bb%zu:\n", li);
|
||||
li++;
|
||||
}
|
||||
printf(" ");
|
||||
if (node_produces_value(f->nodes.data[i].kind))
|
||||
printf("%%%zu = ", i);
|
||||
dump_node(ty, &f->nodes.data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
if (li < f->labels.size && f->labels.data[li] == f->nodes.size)
|
||||
printf("#bb%zu:\n", li);
|
||||
printf("}\n");
|
||||
}
|
||||
|
||||
void spl_ir_dump(spl_ir_t *ir, const spl_type_t *ty) {
|
||||
printf("; SPL IR module (%zu funcs)\n", ir->funcs.size - 1);
|
||||
for (usize i = 1; i < ir->funcs.size; i++)
|
||||
dump_func(ty, &ir->funcs.data[i]);
|
||||
if (ir->gdata.size) {
|
||||
printf("; global data (%zu)\n", ir->gdata.size);
|
||||
for (usize i = 0; i < ir->gdata.size; i++) {
|
||||
printf("; gdata[%zu] = ", i);
|
||||
dump_node(ty, &ir->gdata.data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *spl_ir_kind_name(spl_ir_kind_t kind) {
|
||||
static const char *const names[] = {
|
||||
#define X(a, b, c) #a,
|
||||
SPL_IR_FN_TABLE
|
||||
#undef X
|
||||
};
|
||||
if ((usize)kind < sizeof(names) / sizeof(names[0]))
|
||||
return names[kind];
|
||||
return "?";
|
||||
}
|
||||
|
||||
167
stage1/spl_ir.h
167
stage1/spl_ir.h
@@ -2,6 +2,7 @@
|
||||
#define __SPL_IR_H__
|
||||
|
||||
#include "../stage0/include/utils.h"
|
||||
#include "spl_type.h"
|
||||
|
||||
/* clang-format off */
|
||||
#define SPL_IR_FN_TABLE \
|
||||
@@ -36,9 +37,11 @@
|
||||
X(case.int2float, V0, SPL_IR_CASE_INT2FLOAT) \
|
||||
X(case.float2int, V0, SPL_IR_CASE_FLOAT2INT) \
|
||||
X(mem.alloca, V0, SPL_IR_MEM_ALLOCA) \
|
||||
X(mem.global_alloc, V0, SPL_IR_MEM_GLOBAL_ALLOC) \
|
||||
X(mem.load, V0, SPL_IR_MEM_LOAD) \
|
||||
X(mem.store, V0, SPL_IR_MEM_STORE) \
|
||||
X(mem.offset, V0, SPL_IR_MEM_OFFSET) \
|
||||
X(mem.field, V0, SPL_IR_MEM_FIELD_PTR) \
|
||||
X(mem.copy, V0, SPL_IR_MEM_COPY) \
|
||||
X(mem.set, V0, SPL_IR_MEM_SET) \
|
||||
X(mem.fence, V0, SPL_IR_MEM_FENCE) \
|
||||
@@ -64,6 +67,7 @@
|
||||
X(control.br, V0, SPL_IR_CONTROL_BR) \
|
||||
X(control.jmp, V0, SPL_IR_CONTROL_JMP) \
|
||||
X(control.call, V0, SPL_IR_CONTROL_CALL) \
|
||||
X(control.param, V0, SPL_IR_CONTROL_PARAM) \
|
||||
X(control.ret, V0, SPL_IR_CONTROL_RET) \
|
||||
X(control.unreachable, V0, SPL_IR_CONTROL_UNREACHABLE) \
|
||||
X(control.trap, V0, SPL_IR_CONTROL_TRAP) \
|
||||
@@ -80,14 +84,147 @@ typedef enum {
|
||||
} spl_ir_kind_t;
|
||||
/* clang-format on */
|
||||
|
||||
typedef struct {
|
||||
spl_ir_kind_t kind;
|
||||
} spl_ir_node_t;
|
||||
typedef VEC(spl_ir_node_t) spl_ir_node_vec_t;
|
||||
|
||||
typedef usize spl_ir_node_ref_t; /* 0 is error */
|
||||
typedef VEC(spl_ir_node_ref_t) spl_ir_node_ref_vec_t;
|
||||
|
||||
typedef usize spl_ir_func_ref_t; /* 0 is error */
|
||||
|
||||
typedef struct {
|
||||
spl_ir_kind_t kind;
|
||||
usize src_ref; /* AST 节点 ref(调试行号用,ast2ir 注解) */
|
||||
union {
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t left;
|
||||
spl_ir_node_ref_t right;
|
||||
} arith;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t a;
|
||||
spl_ir_node_ref_t b;
|
||||
} cmp;
|
||||
struct {
|
||||
spl_type_id_t from_tid;
|
||||
spl_type_id_t to_tid;
|
||||
spl_ir_node_ref_t val;
|
||||
} cast;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t count;
|
||||
} mem_alloc;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t const_node;
|
||||
} mem_global_alloc;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t ptr;
|
||||
} mem_load;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t ptr;
|
||||
spl_ir_node_ref_t val;
|
||||
} mem_store;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t ptr;
|
||||
spl_ir_node_ref_t offset;
|
||||
} mem_offset;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t agg;
|
||||
usize field_idx;
|
||||
} mem_field_ptr;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t dst;
|
||||
spl_ir_node_ref_t src;
|
||||
spl_ir_node_ref_t size;
|
||||
} mem_copy;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t dst;
|
||||
spl_ir_node_ref_t val;
|
||||
spl_ir_node_ref_t size;
|
||||
} mem_set;
|
||||
struct {
|
||||
spl_ir_node_ref_t ordering;
|
||||
} mem_fence;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
union {
|
||||
usize int_lit;
|
||||
double float_lit;
|
||||
const char *cstr_lit;
|
||||
char ch_lit;
|
||||
spl_ir_func_ref_t fn;
|
||||
};
|
||||
} type_const;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
} bitsizeof;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
} ir_sizeof;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
} ir_alignof;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t field_idx;
|
||||
} ir_offsetof;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
} field_count;
|
||||
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_vec_t fields;
|
||||
} agg_construct;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_type_id_t field_tid;
|
||||
usize field_idx;
|
||||
spl_ir_node_ref_t val;
|
||||
} agg_extract;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
usize field_idx;
|
||||
spl_ir_node_ref_t agg;
|
||||
spl_ir_node_ref_t field;
|
||||
} agg_insert;
|
||||
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t cond;
|
||||
spl_ir_node_ref_t true_val;
|
||||
spl_ir_node_ref_t false_val;
|
||||
} control_select;
|
||||
struct {
|
||||
spl_ir_node_ref_t cond;
|
||||
spl_ir_node_ref_t true_label;
|
||||
spl_ir_node_ref_t false_label;
|
||||
} control_br;
|
||||
struct {
|
||||
spl_ir_node_ref_t label;
|
||||
} control_jmp;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t func;
|
||||
spl_ir_node_ref_vec_t params;
|
||||
} control_call;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t idx;
|
||||
} control_param;
|
||||
struct {
|
||||
spl_type_id_t tid;
|
||||
spl_ir_node_ref_t val;
|
||||
} control_ret;
|
||||
};
|
||||
} spl_ir_node_t;
|
||||
typedef VEC(spl_ir_node_t) spl_ir_node_vec_t;
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
SPL_IR_ATTR_NONE,
|
||||
@@ -100,17 +237,32 @@ typedef struct {
|
||||
};
|
||||
} spl_ir_attr_t;
|
||||
typedef VEC(spl_ir_attr_t) spl_ir_attr_vec_t;
|
||||
|
||||
/* 调试:函数内变量(名 → 内存位置)。alloca 节点或参数槽。 */
|
||||
typedef struct {
|
||||
const char *name; /* 指向 AST 字符串,不拥有 */
|
||||
spl_ir_node_ref_t alloca; /* 非参数:alloca 节点 ref */
|
||||
spl_type_id_t tid;
|
||||
int is_param; /* 参数则用 param_idx */
|
||||
usize param_idx;
|
||||
usize offset; /* fp 字节偏移(LADDR imm 语义;ir2vm 填) */
|
||||
} spl_ir_dbg_var_t;
|
||||
typedef VEC(spl_ir_dbg_var_t) spl_ir_dbg_var_vec_t;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
spl_ir_attr_t attr;
|
||||
spl_type_id_t fn_tid;
|
||||
spl_ir_node_vec_t nodes;
|
||||
spl_ir_node_ref_vec_t labels;
|
||||
spl_ir_dbg_var_vec_t dbg_vars; /* 调试变量表(fp 偏移在 ir2vm 填) */
|
||||
} spl_ir_func_t;
|
||||
typedef usize spl_ir_func_ref_t; /* 0 is error */
|
||||
|
||||
typedef VEC(spl_ir_func_t) spl_ir_func_vec_t;
|
||||
|
||||
typedef struct {
|
||||
spl_ir_func_vec_t funcs;
|
||||
spl_ir_node_vec_t gdata;
|
||||
} spl_ir_t;
|
||||
|
||||
void spl_ir_init(spl_ir_t *ir);
|
||||
@@ -122,6 +274,7 @@ spl_ir_func_ref_t spl_ir_alloc_fn(spl_ir_t *ir);
|
||||
spl_ir_node_t *spl_ir_node(spl_ir_t *ir, spl_ir_func_ref_t fn_id, spl_ir_node_ref_t node_id);
|
||||
spl_ir_func_t *spl_ir_func(spl_ir_t *ir, spl_ir_func_ref_t fn_id);
|
||||
|
||||
void spl_ir_dump(spl_ir_t *ir);
|
||||
void spl_ir_dump(spl_ir_t *ir, const spl_type_t *ty);
|
||||
const char *spl_ir_kind_name(spl_ir_kind_t kind);
|
||||
|
||||
#endif /* __SPL_IR_H__ */
|
||||
|
||||
1426
stage1/spl_ir2vm.c
Normal file
1426
stage1/spl_ir2vm.c
Normal file
File diff suppressed because it is too large
Load Diff
101
stage1/spl_ir2vm.h
Normal file
101
stage1/spl_ir2vm.h
Normal file
@@ -0,0 +1,101 @@
|
||||
#ifndef __SPL_IR2VM_H__
|
||||
#define __SPL_IR2VM_H__
|
||||
|
||||
#include "spl_ir.h"
|
||||
#include "spl_type.h"
|
||||
|
||||
/*
|
||||
* ================================================================
|
||||
* SPL VM ABI (ir2vm 的正式约定;布局知识唯一来源)
|
||||
* ================================================================
|
||||
*
|
||||
* 栈值槽 spl_val_t = sizeof(usize) = 8 字节。
|
||||
* SIR 指令 type 只表达标量 tag(SPL_I8..SPL_PTR),聚合无 tag——
|
||||
* 聚合值在 vreg 中是字节块,整体搬运用 NCALL vm_memcpy。
|
||||
*
|
||||
* ── 函数栈帧(字节坐标系,基址 = (char*)&stacks[fp])─────────────
|
||||
*
|
||||
* [canary] fp-8 .. 0 (VM CALL 自动插入,ir2vm 不触碰)
|
||||
* [params 区] fp+0 .. fp+Na (Na = C ABI 参数区字节数)
|
||||
* [locals / vreg] fp+Na .. fp+Na+L
|
||||
*
|
||||
* ALLOC ceil((Na+L)/8) 一条 prologue;epilogue 走 RET。
|
||||
* LADDR(imm) 的 imm 是字节偏移;canary 在 fp-1 槽,编译码不可见。
|
||||
*
|
||||
* ── 参数区 = C ABI(聚合值按值) ─────────────────────────────────
|
||||
* 标量参数:align_up(off,8) 后占 8 字节(一槽)。
|
||||
* 聚合参数:按聚合 size 排布跨多槽(align8 + size)。
|
||||
* 调用方:标量 ld/PUSH 一槽;聚合逐 8 字节块压栈(聚合 vreg 强制 8 对齐)。
|
||||
* 被调方:标量参数映射参数槽;聚合参数在 prologue memcpy 参数区 → param vreg,
|
||||
* emit_value(param) 取 vreg 地址。参数槽数 = ceil(param_bytes/8) = CALL nargs。
|
||||
*
|
||||
* ── locals = 虚拟寄存器区 ────────────────────────────────────────
|
||||
* 每个产生值的 IR 节点 = 一个 vreg(locals 区按类型对齐的字节块)。
|
||||
* 纯值节点(type.const / mem.global_alloc / mem.alloca / 折叠 sizeof 等)不落 vreg,
|
||||
* 引用处重算(PUSH / GADDR / LADDR);其余产生值节点落 vreg,引用处 LOAD。
|
||||
* 聚合 vreg 引用处返回其地址(LADDR),搬运经 vm_memcpy。
|
||||
* mem.alloca(tid)(cnt) 的 vreg 槽即缓冲区本体,节点值 = LADDR(vreg_off)。
|
||||
* 所有运算走 load/store:ld A; ld B; op; st Dst;立即数直接 PUSH。
|
||||
*
|
||||
* ── 调用 ──────────────────────────────────────────────────────────
|
||||
* 调用方:逐参数压栈(标量一槽;聚合按 C ABI 逐 8 字节块)→ push fn_addr(或
|
||||
* native_idx) → CALL n / NCALL n。返回值落 call 节点 vreg。
|
||||
* 原生函数:IR 中 nodes 为空的函数(@extern 声明)→ 注册进 prog.natives,
|
||||
* 加载时由 spl_syscall_register 填 impl;调用改 PUSH nat_idx + NCALL。
|
||||
* 变参 nargs = 实际参数槽数。
|
||||
*
|
||||
* ── enum(不展开,直接保留)─────────────────────────────────────
|
||||
* IR 层保留 enum 类型(不再展开为 struct)。enum 布局 = tag(usize, offset 0)
|
||||
* + payload(offset 8,最大变体)。构造:agg.construct(enum)(tag, payload);
|
||||
* match:先取 tag 判定变体,再 extract(enum, 1) 一跳取 payload,
|
||||
* field_tid = 变体具体类型(由 ast2ir 传入)。
|
||||
*
|
||||
* ── 返回 ──────────────────────────────────────────────────────────
|
||||
* 标量:ld vreg(val); RET(tag)(RET 的 type 决定 VM 是否弹出返回值)。
|
||||
* 聚合返回 = sret(自展开,不改 VM):
|
||||
* 被调函数签名尾部追加隐藏 *T 参数(最后一个);返回时把聚合 vreg
|
||||
* memcpy 到 sret 地址,RET(void)。
|
||||
* 调用方在 locals 预留聚合槽(= call 节点 vreg),压其地址为最后实参;
|
||||
* call 完成后聚合值已在该槽。
|
||||
* 返回 void:RET(SPL_VOID)。
|
||||
*
|
||||
* ── 全局数据 ──────────────────────────────────────────────────────
|
||||
* 任何聚合类型下(含 $root 顶层)的 var/const 属全局数据区。gdata 是
|
||||
* spl_ir_node_vec_t,每条 = 一个 value 节点(type.const:标量折叠值,
|
||||
* 聚合/无 init 零)。ir2vm 遍历求值 → SIR gdata 条目(字节 blob)。
|
||||
* mem.global_alloc(tid, const_node) 的 const_node = gdata 向量索引,
|
||||
* 降级为 GADDR(idx)。符号(def) → gdata 索引的解析在 ast2ir 收集期
|
||||
* (gdata_ref 表),ir2vm 按索引直接用。
|
||||
*
|
||||
* ── 类型布局(C ABI,唯一实现处)────────────────────────────────
|
||||
* type_align / type_size / field_offset:标量按 bits/8 对齐;
|
||||
* struct 顺序对齐 + 尾填充;union 取最大字段;enum = tag(8) + 最大 payload;
|
||||
* slice/range = [ptr, len] 各 8 字节;array = len * elem。
|
||||
*
|
||||
* ── 分支 ──────────────────────────────────────────────────────────
|
||||
* control.br/jmp/select 的 label 是 IR node ref(基本块首指令)。两遍发射:
|
||||
* 第一遍逐节点发指令并记录 label 节点 → 指令地址;第二遍回填
|
||||
* JMP/BZ/BNZ 相对偏移 imm = target_addr - (jmp_addr + 1)。
|
||||
* ================================================================
|
||||
*/
|
||||
|
||||
/* 调试映射:每函数 IR 节点 → 首条指令 ip(调试器显示当前 IR 节点用) */
|
||||
typedef struct {
|
||||
spl_ir_func_ref_t fid;
|
||||
VEC(usize) node_first_ip; /* 索引 = IR 节点 ref,值 = 该节点首指令绝对 ip */
|
||||
} spl_ir2vm_fdbg_t;
|
||||
typedef VEC(spl_ir2vm_fdbg_t) spl_ir2vm_fdbg_vec_t;
|
||||
|
||||
typedef struct {
|
||||
const spl_ir_t *ir;
|
||||
const spl_type_t *type;
|
||||
spl_ir2vm_fdbg_vec_t fdbg; /* ir2vm_run 后填充;splc0 -g 读取生成 debug 段 */
|
||||
} spl_ir2vm_t;
|
||||
|
||||
void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type);
|
||||
void spl_ir2vm_drop(spl_ir2vm_t *ctx);
|
||||
|
||||
int spl_ir2vm_run(spl_ir2vm_t *ctx, const char *outpath); /* 返回错误数 */
|
||||
void spl_ir2vm_dump(spl_ir2vm_t *ctx);
|
||||
|
||||
#endif /* __SPL_IR2VM_H__ */
|
||||
@@ -270,7 +270,8 @@ spl_tok_vec_t spl_lex(const char *source, const char *fname) {
|
||||
/* Hex literal */
|
||||
offset += 2;
|
||||
col += 2;
|
||||
while (offset < len && isxdigit((unsigned char)source[offset])) {
|
||||
while (offset < len &&
|
||||
(isxdigit((unsigned char)source[offset]) || source[offset] == '_')) {
|
||||
offset++;
|
||||
col++;
|
||||
}
|
||||
@@ -280,7 +281,8 @@ spl_tok_vec_t spl_lex(const char *source, const char *fname) {
|
||||
/* Binary literal */
|
||||
offset += 2;
|
||||
col += 2;
|
||||
while (offset < len && (source[offset] == '0' || source[offset] == '1')) {
|
||||
while (offset < len && (source[offset] == '0' || source[offset] == '1' ||
|
||||
source[offset] == '_')) {
|
||||
offset++;
|
||||
col++;
|
||||
}
|
||||
@@ -290,7 +292,8 @@ spl_tok_vec_t spl_lex(const char *source, const char *fname) {
|
||||
/* Octal literal */
|
||||
offset += 2;
|
||||
col += 2;
|
||||
while (offset < len && source[offset] >= '0' && source[offset] <= '7') {
|
||||
while (offset < len && ((source[offset] >= '0' && source[offset] <= '7') ||
|
||||
source[offset] == '_')) {
|
||||
offset++;
|
||||
col++;
|
||||
}
|
||||
@@ -299,7 +302,8 @@ spl_tok_vec_t spl_lex(const char *source, const char *fname) {
|
||||
}
|
||||
|
||||
/* Decimal integer or float */
|
||||
while (offset < len && isdigit((unsigned char)source[offset])) {
|
||||
while (offset < len &&
|
||||
(isdigit((unsigned char)source[offset]) || source[offset] == '_')) {
|
||||
offset++;
|
||||
col++;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// WRITE BY AI
|
||||
// WRITE BY AI
|
||||
/* spl_sema.c SPL 语义分析
|
||||
*
|
||||
* 两趟 AST 遍历(splc0 显式先 run 再 check):
|
||||
@@ -9,6 +9,7 @@
|
||||
* 命名实体放 def_table,两张表 id 空间完全独立。
|
||||
*/
|
||||
#include "spl_sema.h"
|
||||
#include "spl_builtin.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -112,11 +113,11 @@ static spl_type_id_t find_type(spl_sema_t *sema, const char *name) {
|
||||
/* ---- 聚合成员登记(def 层 agg_def) ---- */
|
||||
|
||||
static void agg_add_member(spl_sema_t *sema, spl_def_id_t def, const char *name, spl_type_id_t type,
|
||||
spl_scope_id_t scope) {
|
||||
spl_scope_id_t scope, spl_def_id_t member_def) {
|
||||
spl_def_node_t *d = def_at(sema, def);
|
||||
if (!d || d->kind != SPL_DEF_AGG)
|
||||
return;
|
||||
spl_var_def_t v = {name, 0, type, scope};
|
||||
spl_var_def_t v = {name, member_def, type, scope};
|
||||
vec_push(d->agg_def, v);
|
||||
}
|
||||
|
||||
@@ -281,6 +282,9 @@ static spl_type_id_t resolve_type_expr(spl_sema_t *sema, spl_ast_node_ref_t ref)
|
||||
(atom->type_atom.kind == SPL_AST_TYPE_ANY || atom->type_atom.kind == SPL_AST_TYPE_PTR))
|
||||
sema_error(sema, ref, "bare `_` type only allowed as pointer target `*_`");
|
||||
}
|
||||
/* AST 注解:该 type_expr 解析到的 def(聚合/别名/内置为 0;ast2ir 据此直接取类型) */
|
||||
if (base)
|
||||
n->resolved_def_id = def_for_type(sema, base);
|
||||
return base;
|
||||
}
|
||||
|
||||
@@ -331,6 +335,7 @@ static agg_handle_t agg_prepare(spl_sema_t *sema, spl_ast_node_ref_t ref) {
|
||||
spl_def_id_t def = def_alloc(sema, SPL_DEF_AGG);
|
||||
def_at(sema, def)->type_id = tid;
|
||||
vec_init(def_at(sema, def)->agg_def);
|
||||
te->resolved_def_id = def; /* 注解:该 type_expr 解析到的 def */
|
||||
h.tid = tid;
|
||||
h.def = def;
|
||||
return h;
|
||||
@@ -366,13 +371,20 @@ static void agg_resolve(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_type_id_t
|
||||
m->member_decl.type_expr ? resolve_type_expr(sema, m->member_decl.type_expr) : 0;
|
||||
/* resolve_type_expr 可能 alloc 新类型 → 两表 realloc,须重新取节点 */
|
||||
spl_type_node_t *tn = spl_type_node(&sema->type, tid);
|
||||
/* 每个 MEMBER_DECL 建一个 SPL_DEF_MEMBER def(结构字段/枚举变体) */
|
||||
spl_type_id_t mtype = 0;
|
||||
if (tn->kind == SPL_TYPE_ENUM) {
|
||||
vec_push(tn->enum_type.variants, ft ? ft : tid);
|
||||
agg_add_member(sema, def, m->member_decl.name, ft ? ft : tid, agg_scope);
|
||||
mtype = ft ? ft : tid; /* 哨兵:无载荷变体 = 枚举自身 tid */
|
||||
vec_push(tn->enum_type.variants, mtype);
|
||||
} else {
|
||||
mtype = ft;
|
||||
vec_push(tn->agg_field_types, ft);
|
||||
agg_add_member(sema, def, m->member_decl.name, ft, agg_scope);
|
||||
}
|
||||
spl_def_id_t mdef = def_alloc(sema, SPL_DEF_MEMBER);
|
||||
def_at(sema, mdef)->type_id = mtype;
|
||||
def_at(sema, mdef)->var_def.name = m->member_decl.name;
|
||||
agg_add_member(sema, def, m->member_decl.name, mtype, agg_scope, mdef);
|
||||
m->resolved_def_id = mdef; /* 注解:member_decl → 其 SPL_DEF_MEMBER def */
|
||||
break;
|
||||
}
|
||||
case SPL_AST_FN_DECL:
|
||||
@@ -416,13 +428,19 @@ static spl_type_id_t build_agg(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_sco
|
||||
|
||||
/* 变量/常量/绑定节点:建 VAR def(不包 SPL_TYPE_ID) */
|
||||
static void register_binding(spl_sema_t *sema, const char *name, spl_ast_node_ref_t type_expr,
|
||||
spl_scope_id_t scope, spl_def_id_t agg_def) {
|
||||
spl_scope_id_t scope, spl_def_id_t agg_def,
|
||||
spl_ast_node_ref_t node_ref) {
|
||||
spl_type_id_t t = type_expr ? resolve_type_expr(sema, type_expr) : 0;
|
||||
spl_def_id_t def = new_var_def(sema, name, t, scope);
|
||||
if (!spl_sema_scope_insert(sema, scope, name, def))
|
||||
sema_error(sema, 0, "duplicate symbol");
|
||||
if (agg_def)
|
||||
agg_add_member(sema, agg_def, name, t, scope);
|
||||
agg_add_member(sema, agg_def, name, t, scope, def);
|
||||
if (node_ref) {
|
||||
spl_ast_node_t *nn = node_at(sema->ast, node_ref);
|
||||
if (nn)
|
||||
nn->resolved_def_id = def; /* 注解:var/const 声明 → 其 VAR def */
|
||||
}
|
||||
}
|
||||
|
||||
static void sema_var_decl(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_scope_id_t scope,
|
||||
@@ -430,7 +448,7 @@ static void sema_var_decl(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_scope_id
|
||||
spl_ast_node_t *n = node_at(sema->ast, ref);
|
||||
if (!n)
|
||||
return;
|
||||
register_binding(sema, n->var_decl.name, n->var_decl.type_expr, scope, agg_def);
|
||||
register_binding(sema, n->var_decl.name, n->var_decl.type_expr, scope, agg_def, ref);
|
||||
}
|
||||
|
||||
static void sema_const_decl(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_scope_id_t scope,
|
||||
@@ -438,7 +456,14 @@ static void sema_const_decl(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_scope_
|
||||
spl_ast_node_t *n = node_at(sema->ast, ref);
|
||||
if (!n)
|
||||
return;
|
||||
register_binding(sema, n->const_decl.name, n->const_decl.type_expr, scope, agg_def);
|
||||
register_binding(sema, n->const_decl.name, n->const_decl.type_expr, scope, agg_def, ref);
|
||||
/* 标记 const:赋值检查拒绝写入 */
|
||||
spl_ast_node_t *nn = node_at(sema->ast, ref);
|
||||
if (nn && nn->resolved_def_id) {
|
||||
spl_def_node_t *d = def_at(sema, nn->resolved_def_id);
|
||||
if (d)
|
||||
d->var_def.is_const = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* fn 声明:建 fn type + FN_PARAMS def + 登记名字。不建 fn_scope(由 check 新建)。
|
||||
@@ -477,7 +502,8 @@ static void sema_fn_decl(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_scope_id_
|
||||
if (!spl_sema_scope_insert(sema, scope, n->fn_decl.name, fdef))
|
||||
sema_error(sema, ref, "duplicate function name");
|
||||
if (agg_def)
|
||||
agg_add_member(sema, agg_def, n->fn_decl.name, fid, scope);
|
||||
agg_add_member(sema, agg_def, n->fn_decl.name, fid, scope, fdef);
|
||||
n->resolved_def_id = fdef; /* 注解:fn 声明 → 其 FN_PARAMS def */
|
||||
}
|
||||
|
||||
static void sema_type_decl(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_scope_id_t scope,
|
||||
@@ -510,10 +536,11 @@ static void sema_type_decl(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_scope_i
|
||||
if (!spl_sema_scope_insert(sema, scope, n->type_decl.name, def))
|
||||
sema_error(sema, ref, "duplicate type name");
|
||||
if (agg_def)
|
||||
agg_add_member(sema, agg_def, n->type_decl.name, tid, scope);
|
||||
agg_add_member(sema, agg_def, n->type_decl.name, tid, scope, def);
|
||||
/* 嵌套类型提升到文件作用域:兄弟聚合可裸名引用(如 Lexer 方法内 Tag.eof) */
|
||||
if (agg_def)
|
||||
spl_sema_scope_insert(sema, sema->root_scope, n->type_decl.name, def);
|
||||
n->resolved_def_id = def; /* 注解:type_decl → 其 AGG/ALIAS def */
|
||||
}
|
||||
|
||||
/* ================================================================
|
||||
@@ -561,6 +588,29 @@ static bool expr_is_int_literal(spl_sema_t *sema, spl_ast_node_ref_t ref) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 浮点字面量(含负号前缀):允许拓宽/窄化到目标浮点类型 */
|
||||
static bool expr_is_float_literal(spl_sema_t *sema, spl_ast_node_ref_t ref) {
|
||||
spl_ast_node_t *n = node_at(sema->ast, ref);
|
||||
if (!n || n->kind != SPL_AST_EXPR)
|
||||
return false;
|
||||
switch (n->expr.op) {
|
||||
case SPL_AST_PRIMARY_EXPR: {
|
||||
spl_ast_node_t *pn = node_at(sema->ast, n->expr.op_expr.left);
|
||||
int k = pn && pn->kind == SPL_AST_EXPR ? (int)pn->primary_expr.kind : -1;
|
||||
return k == SPL_AST_FLOAT;
|
||||
}
|
||||
case SPL_AST_PREFIX_EXPR:
|
||||
return expr_is_float_literal(sema, n->expr.op_expr.left);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* 字面量(整型或浮点):检查时允许类型拓宽 */
|
||||
static bool expr_is_literal(spl_sema_t *sema, spl_ast_node_ref_t ref) {
|
||||
return expr_is_int_literal(sema, ref) || expr_is_float_literal(sema, ref);
|
||||
}
|
||||
|
||||
/* 打开 SPL_TYPE_ID 绑定节点,返回真实类型(newtype 预留;当前无调用产生该节点) */
|
||||
static spl_type_id_t underlying(spl_sema_t *sema, spl_type_id_t tid) {
|
||||
spl_type_node_t *t = type_node(sema, tid);
|
||||
@@ -631,6 +681,9 @@ static int type_compatible(spl_sema_t *sema, spl_type_id_t dst, spl_type_id_t sr
|
||||
/* 字面量值已隐含满足范围(检查时由调用方保证),此处允许拓宽 */
|
||||
return 1;
|
||||
}
|
||||
/* 浮点字面量可赋值到目标浮点(f64 字面量 → f32 变量,值可表示则允许) */
|
||||
if (is_literal && d->kind == SPL_TYPE_FLOAT && s->kind == SPL_TYPE_FLOAT)
|
||||
return 1;
|
||||
/* isize/usize(机器字宽)与 i32 混合:比较/算术/传参可用(splc1 与旧测试事实标准) */
|
||||
if (d->kind == SPL_TYPE_INT && s->kind == SPL_TYPE_INT) {
|
||||
int dbits = d->int_type.bits, sbits = s->int_type.bits;
|
||||
@@ -753,8 +806,8 @@ static spl_type_id_t infer_binary_cmp(spl_sema_t *sema, spl_ast_node_ref_t ref,
|
||||
return 0;
|
||||
}
|
||||
int w = 0;
|
||||
if (type_compatible(sema, l, r, expr_is_int_literal(sema, e->expr.op_expr.right), &w) ||
|
||||
type_compatible(sema, r, l, expr_is_int_literal(sema, e->expr.op_expr.left), &w)) {
|
||||
if (type_compatible(sema, l, r, expr_is_literal(sema, e->expr.op_expr.right), &w) ||
|
||||
type_compatible(sema, r, l, expr_is_literal(sema, e->expr.op_expr.left), &w)) {
|
||||
return spl_type_bool(&sema->type);
|
||||
}
|
||||
sema_error(sema, ref, "type mismatch in comparison");
|
||||
@@ -900,7 +953,7 @@ static spl_type_id_t infer_agg_init(spl_sema_t *sema, spl_ast_node_ref_t ref, sp
|
||||
int w = 0;
|
||||
if (!expr_is_null_literal(sema, item->aggregate_init_item.expr) && et &&
|
||||
!type_compatible(sema, mt, et,
|
||||
expr_is_int_literal(sema, item->aggregate_init_item.expr), &w)) {
|
||||
expr_is_literal(sema, item->aggregate_init_item.expr), &w)) {
|
||||
sema_error(sema, iref, "field type mismatch in aggregate literal");
|
||||
}
|
||||
}
|
||||
@@ -923,8 +976,7 @@ static spl_type_id_t infer_array_lit(spl_sema_t *sema, spl_ast_node_ref_t ref, s
|
||||
int w = 0;
|
||||
if (!type_compatible(
|
||||
sema, elem, et,
|
||||
expr_is_int_literal(sema, pn->primary_expr.array_lit_expr.expr_list.data[i]),
|
||||
&w)) {
|
||||
expr_is_literal(sema, pn->primary_expr.array_lit_expr.expr_list.data[i]), &w)) {
|
||||
sema_error(sema, pn->primary_expr.array_lit_expr.expr_list.data[i],
|
||||
"element type mismatch in array literal");
|
||||
}
|
||||
@@ -937,14 +989,32 @@ static spl_type_id_t infer_array_lit(spl_sema_t *sema, spl_ast_node_ref_t ref, s
|
||||
/* 标识符:scope 查找(def → 类型) */
|
||||
static spl_type_id_t infer_ident(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_ast_node_t *n) {
|
||||
spl_ast_node_t *pn = n->kind == SPL_AST_EXPR ? node_at(sema->ast, n->expr.op_expr.left) : n;
|
||||
spl_type_id_t t = find_type(sema, pn->primary_expr.ident);
|
||||
spl_def_id_t def = find_bare(sema, pn->primary_expr.ident);
|
||||
spl_type_id_t t = def_type(sema, def);
|
||||
if (!t) {
|
||||
sema_error(sema, ref, "undefined identifier");
|
||||
return 0;
|
||||
}
|
||||
/* 注解:IDENT 引用 → 其符号 def(var 用 VAR、fn 用 FN_PARAMS 等) */
|
||||
pn->resolved_def_id = def;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* 表达式 ref 解析到的 def:取内层节点注解;裸 ident 兜底 find_bare */
|
||||
static spl_def_id_t expr_def_at(spl_sema_t *sema, spl_ast_node_ref_t ref) {
|
||||
spl_ast_node_t *n = node_at(sema->ast, ref);
|
||||
if (!n || n->kind != SPL_AST_EXPR)
|
||||
return 0;
|
||||
spl_ast_node_t *inner = node_at(sema->ast, n->expr.op_expr.left);
|
||||
if (!inner)
|
||||
return 0;
|
||||
if (inner->resolved_def_id)
|
||||
return inner->resolved_def_id;
|
||||
if (n->expr.op == SPL_AST_PRIMARY_EXPR && inner->primary_expr.kind == SPL_AST_IDENT)
|
||||
return find_bare(sema, inner->primary_expr.ident);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static spl_type_id_t infer_postfix(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_ast_node_t *n);
|
||||
static spl_type_id_t infer_prefix(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_ast_node_t *n);
|
||||
static void check_stmt(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_sema_ctx_t ctx);
|
||||
@@ -980,16 +1050,35 @@ static spl_type_id_t infer_primary(spl_sema_t *sema, spl_ast_node_ref_t ref, spl
|
||||
return infer_expr(sema, pn->primary_expr.expr);
|
||||
case SPL_AST_ARRAY_LIT:
|
||||
return infer_array_lit(sema, ref, n);
|
||||
case SPL_AST_BUILTIN_EXPR:
|
||||
/* 内置调用:先推断实参,类型按名称约定 */
|
||||
for (usize i = 0; i < pn->primary_expr.builtin_expr.expr_list.size; i++)
|
||||
case SPL_AST_BUILTIN_EXPR: {
|
||||
/* 内置调用:类型按注册表约定,参数按需推断(offsetof 字段名参数非表达式) */
|
||||
const spl_builtin_t *bi = spl_builtin_lookup(pn->primary_expr.builtin_expr.ident);
|
||||
if (!bi) {
|
||||
sema_error(sema, ref, "unknown builtin");
|
||||
return 0;
|
||||
}
|
||||
usize nargs = pn->primary_expr.builtin_expr.expr_list.size;
|
||||
if (nargs < (usize)bi->min_args || (bi->max_args >= 0 && nargs > (usize)bi->max_args)) {
|
||||
char buf[96];
|
||||
snprintf(buf, sizeof buf, "builtin @%s: arg count mismatch", bi->name);
|
||||
sema_error(sema, ref, buf);
|
||||
return 0;
|
||||
}
|
||||
for (usize i = 0; i < nargs; i++) {
|
||||
/* offsetof 第二参数为字段标识符,不按表达式推断 */
|
||||
if (bi->kind == SPL_BUILTIN_OFFSET_OF && i == 1)
|
||||
continue;
|
||||
infer_expr(sema, pn->primary_expr.builtin_expr.expr_list.data[i]);
|
||||
if (strcmp(pn->primary_expr.builtin_expr.ident, "sizeof") == 0)
|
||||
return spl_type_int(&sema->type, sizeof(usize) * 8, 0);
|
||||
if (strcmp(pn->primary_expr.builtin_expr.ident, "dbg") == 0 ||
|
||||
strcmp(pn->primary_expr.builtin_expr.ident, "assert") == 0)
|
||||
}
|
||||
if (bi->ret_is_void)
|
||||
return spl_type_void(&sema->type);
|
||||
return spl_type_void(&sema->type);
|
||||
if (bi->kind == SPL_BUILTIN_SIZE_OF || bi->kind == SPL_BUILTIN_BITSIZE_OF ||
|
||||
bi->kind == SPL_BUILTIN_ALIGN_OF || bi->kind == SPL_BUILTIN_OFFSET_OF ||
|
||||
bi->kind == SPL_BUILTIN_FIELD_COUNT)
|
||||
return spl_type_int(&sema->type, sizeof(usize) * 8, 0);
|
||||
sema_error(sema, ref, "unsupported builtin");
|
||||
return 0;
|
||||
}
|
||||
case SPL_AST_BLOCK_EXPR: {
|
||||
/* 块表达式:有独立 scope;逐语句检查,返回末尾表达式类型(无则 void) */
|
||||
spl_ast_node_ref_vec_t *items = &pn->primary_expr.block_expr;
|
||||
@@ -1080,6 +1169,8 @@ static spl_type_id_t infer_postfix(spl_sema_t *sema, spl_ast_node_ref_t ref, spl
|
||||
/* 指针自动解引用一层 */
|
||||
if (t && t->kind == SPL_TYPE_PTR)
|
||||
base = underlying(sema, t->ptr_pointee);
|
||||
/* 注解:FIELD 节点 → 被访问聚合(自动解引用后)的 AGG def;非聚合为 0 */
|
||||
pf->resolved_def_id = def_for_type(sema, base);
|
||||
t = type_node(sema, base);
|
||||
if (!t) {
|
||||
sema_error(sema, ref, "field access on non-aggregate");
|
||||
@@ -1171,6 +1262,8 @@ static spl_type_id_t infer_postfix(spl_sema_t *sema, spl_ast_node_ref_t ref, spl
|
||||
sema_error(sema, ref, "call requires function type");
|
||||
return 0;
|
||||
}
|
||||
/* 注解:CALL 节点 → 被调 fn 的 def(从 callee primary 节点/裸名解析) */
|
||||
pf->resolved_def_id = expr_def_at(sema, pf->postfix_expr.primary_expr);
|
||||
spl_ast_node_ref_vec_t *args = &pf->postfix_expr.call_expr;
|
||||
/* 变参 fn(如 vm_printf)最后一个形参 name=NULL 且类型可为 0 */
|
||||
/* 注意:infer_expr 可能新分配类型使 type_table realloc,t 会失效,须每次刷新 */
|
||||
@@ -1182,7 +1275,7 @@ static spl_type_id_t infer_postfix(spl_sema_t *sema, spl_ast_node_ref_t ref, spl
|
||||
int w = 0;
|
||||
if (!expr_is_null_literal(sema, args->data[i]) && at &&
|
||||
!type_compatible(sema, t->fn_type.params.data[i], at,
|
||||
expr_is_int_literal(sema, args->data[i]), &w)) {
|
||||
expr_is_literal(sema, args->data[i]), &w)) {
|
||||
sema_error(sema, args->data[i], "argument type mismatch");
|
||||
}
|
||||
}
|
||||
@@ -1258,10 +1351,24 @@ static spl_type_id_t infer_expr(spl_sema_t *sema, spl_ast_node_ref_t ref) {
|
||||
sema_error(sema, ref, "bad assignment");
|
||||
return 0;
|
||||
}
|
||||
/* 不可写检查:赋值目标为 const 变量时拒绝 */
|
||||
{
|
||||
spl_ast_node_t *lhs = node_at(sema->ast, n->expr.op_expr.left);
|
||||
if (lhs && lhs->kind == SPL_AST_EXPR && lhs->expr.op == SPL_AST_PRIMARY_EXPR) {
|
||||
spl_ast_node_t *lpn = node_at(sema->ast, lhs->expr.op_expr.left);
|
||||
if (lpn && lpn->kind == SPL_AST_EXPR &&
|
||||
lpn->primary_expr.kind == SPL_AST_IDENT && lpn->resolved_def_id) {
|
||||
spl_def_node_t *ld = def_at(sema, lpn->resolved_def_id);
|
||||
if (ld && ld->kind == SPL_DEF_VAR && ld->var_def.is_const) {
|
||||
sema_error(sema, ref, "cannot assign to const");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int w = 0;
|
||||
if (!expr_is_null_literal(sema, n->expr.op_expr.right) &&
|
||||
!type_compatible(sema, l, r, expr_is_int_literal(sema, n->expr.op_expr.right),
|
||||
&w)) {
|
||||
!type_compatible(sema, l, r, expr_is_literal(sema, n->expr.op_expr.right), &w)) {
|
||||
sema_error(sema, ref, "assignment type mismatch");
|
||||
return 0;
|
||||
}
|
||||
@@ -1320,7 +1427,7 @@ static void check_var_init(spl_sema_t *sema, spl_ast_node_ref_t ref, const char
|
||||
if (dt) {
|
||||
int w = 0;
|
||||
if (!expr_is_null_literal(sema, init_expr) && it &&
|
||||
!type_compatible(sema, dt, it, expr_is_int_literal(sema, init_expr), &w)) {
|
||||
!type_compatible(sema, dt, it, expr_is_literal(sema, init_expr), &w)) {
|
||||
sema_error(sema, ref, "initializer type mismatch");
|
||||
} else if (w) {
|
||||
sema_warn(sema, ref, "pointer conversion involves wildcard");
|
||||
@@ -1386,7 +1493,7 @@ static spl_type_id_t infer_agg_init_with_type(spl_sema_t *sema, spl_ast_node_ref
|
||||
int w = 0;
|
||||
if (!expr_is_null_literal(sema, item->aggregate_init_item.expr) && et &&
|
||||
!type_compatible(sema, mt, et,
|
||||
expr_is_int_literal(sema, item->aggregate_init_item.expr), &w)) {
|
||||
expr_is_literal(sema, item->aggregate_init_item.expr), &w)) {
|
||||
sema_error(sema, iref, "field type mismatch in aggregate literal");
|
||||
}
|
||||
}
|
||||
@@ -1451,6 +1558,33 @@ static void check_match(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_ast_node_t
|
||||
sema_error(sema, ref, "match: arm pattern/body count mismatch");
|
||||
return;
|
||||
}
|
||||
/* 穷举检查:enum 必须覆盖所有变体,或含通配 _(含表达式的整数 match 免检) */
|
||||
{
|
||||
spl_def_id_t adef = def_for_type(sema, mtype);
|
||||
spl_def_node_t *ad = adef ? &sema->type.def_table.data[adef] : NULL;
|
||||
int is_enum_agg = ad && ad->kind == SPL_DEF_AGG;
|
||||
if (is_enum_agg) {
|
||||
int has_wild = 0;
|
||||
usize covered = 0;
|
||||
for (usize i = 0; i < arms->size; i++) {
|
||||
spl_ast_node_t *pk = node_at(sema->ast, arms->data[i]);
|
||||
if (!pk)
|
||||
continue;
|
||||
/* _ 通配:ident 与 expr 均为空 */
|
||||
if (!pk->packed_expr.ident && !pk->packed_expr.expr) {
|
||||
has_wild = 1;
|
||||
break;
|
||||
}
|
||||
if (pk->packed_expr.ident) {
|
||||
/* 变体被覆盖:匹配臂中出现的合法变体计数 */
|
||||
if (agg_member_type(sema, mtype, pk->packed_expr.ident))
|
||||
covered++;
|
||||
}
|
||||
}
|
||||
if (!has_wild && covered < ad->agg_def.size)
|
||||
sema_error(sema, ref, "match must be exhaustive");
|
||||
}
|
||||
}
|
||||
for (usize i = 0; i < arms->size; i++) {
|
||||
spl_ast_node_t *pk = node_at(sema->ast, arms->data[i]);
|
||||
if (!pk)
|
||||
@@ -1476,7 +1610,7 @@ static void check_match(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_ast_node_t
|
||||
spl_type_id_t et = infer_expr(sema, pk->packed_expr.expr);
|
||||
int w = 0;
|
||||
if (is_int_tid(sema, mtype) && et &&
|
||||
!type_compatible(sema, mtype, et, expr_is_int_literal(sema, pk->packed_expr.expr),
|
||||
!type_compatible(sema, mtype, et, expr_is_literal(sema, pk->packed_expr.expr),
|
||||
&w)) {
|
||||
sema_error(sema, pk->packed_expr.expr, "match arm pattern type mismatch");
|
||||
}
|
||||
@@ -1584,7 +1718,7 @@ static void check_stmt(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_sema_ctx_t
|
||||
if (ctx.ret) {
|
||||
if (!expr_is_null_literal(sema, rd->block_item.ret_statement.expr) && et &&
|
||||
!type_compatible(sema, ctx.ret, et,
|
||||
expr_is_int_literal(sema, rd->block_item.ret_statement.expr),
|
||||
expr_is_literal(sema, rd->block_item.ret_statement.expr),
|
||||
NULL)) {
|
||||
sema_error(sema, rd->block_item.ret_statement.expr, "ret type mismatch");
|
||||
}
|
||||
@@ -1623,11 +1757,21 @@ static void check_block(spl_sema_t *sema, spl_ast_node_ref_vec_t *block, spl_sco
|
||||
if (b->block_item.kind == SPL_AST_VARDECL) {
|
||||
spl_ast_node_t *vd = node_at(sema->ast, b->block_item.var_decl);
|
||||
if (vd)
|
||||
register_binding(sema, vd->var_decl.name, vd->var_decl.type_expr, scope, 0);
|
||||
register_binding(sema, vd->var_decl.name, vd->var_decl.type_expr, scope, 0,
|
||||
b->block_item.var_decl);
|
||||
} else if (b->block_item.kind == SPL_AST_CONSTDECL) {
|
||||
spl_ast_node_t *cd = node_at(sema->ast, b->block_item.const_decl);
|
||||
if (cd)
|
||||
register_binding(sema, cd->const_decl.name, cd->const_decl.type_expr, scope, 0);
|
||||
if (cd) {
|
||||
register_binding(sema, cd->const_decl.name, cd->const_decl.type_expr, scope, 0,
|
||||
b->block_item.const_decl);
|
||||
/* 标记 const:赋值检查拒绝写入 */
|
||||
spl_ast_node_t *cn = node_at(sema->ast, b->block_item.const_decl);
|
||||
if (cn && cn->resolved_def_id) {
|
||||
spl_def_node_t *cdd = def_at(sema, cn->resolved_def_id);
|
||||
if (cdd)
|
||||
cdd->var_def.is_const = 1;
|
||||
}
|
||||
}
|
||||
} else if (b->block_item.kind == SPL_AST_TYPEDECL) {
|
||||
spl_ast_node_t *td = node_at(sema->ast, b->block_item.type_decl);
|
||||
if (td)
|
||||
@@ -1664,6 +1808,10 @@ static void check_fn_body(spl_sema_t *sema, spl_ast_node_ref_t ref, spl_def_id_t
|
||||
if (pv->name) {
|
||||
spl_def_id_t pdef = new_var_def(sema, pv->name, pv->type_id, fn_scope);
|
||||
spl_sema_scope_insert(sema, fn_scope, pv->name, pdef);
|
||||
/* 注解:param_decl → 其在 fn_scope 的 VAR def */
|
||||
spl_ast_node_t *pd = node_at(sema->ast, n->fn_decl.param_list.data[i]);
|
||||
if (pd)
|
||||
pd->resolved_def_id = pdef;
|
||||
}
|
||||
}
|
||||
check_block(sema, &n->fn_decl.block, fn_scope, ctx);
|
||||
@@ -1745,7 +1893,7 @@ void spl_sema_run(spl_sema_t *sema) {
|
||||
continue;
|
||||
if (!spl_sema_scope_insert(sema, sema->root_scope, m->type_decl.name, h.def))
|
||||
sema_error(sema, mref, "duplicate type name");
|
||||
agg_add_member(sema, sema->root_def, m->type_decl.name, h.tid, sema->root_scope);
|
||||
agg_add_member(sema, sema->root_def, m->type_decl.name, h.tid, sema->root_scope, h.def);
|
||||
} else if (te) {
|
||||
spl_type_id_t tid = resolve_type_expr(sema, m->type_decl.type_expr);
|
||||
spl_def_id_t def = def_alloc(sema, SPL_DEF_ALIAS);
|
||||
@@ -1757,7 +1905,7 @@ void spl_sema_run(spl_sema_t *sema) {
|
||||
d->type_def.scope_id = sema->root_scope;
|
||||
if (!spl_sema_scope_insert(sema, sema->root_scope, m->type_decl.name, def))
|
||||
sema_error(sema, mref, "duplicate type name");
|
||||
agg_add_member(sema, sema->root_def, m->type_decl.name, tid, sema->root_scope);
|
||||
agg_add_member(sema, sema->root_def, m->type_decl.name, tid, sema->root_scope, def);
|
||||
}
|
||||
}
|
||||
/* 1b. fn 名(建 fn type + def) */
|
||||
@@ -1779,6 +1927,9 @@ void spl_sema_run(spl_sema_t *sema) {
|
||||
sema_var_decl(sema, mref, sema->root_scope, sema->root_def);
|
||||
else if (m->kind == SPL_AST_CONST_DECL)
|
||||
sema_const_decl(sema, mref, sema->root_scope, sema->root_def);
|
||||
else if (m->kind == SPL_AST_MEMBER_DECL && m->member_decl.name)
|
||||
register_binding(sema, m->member_decl.name, m->member_decl.type_expr, sema->root_scope,
|
||||
sema->root_def, mref);
|
||||
}
|
||||
sema->current_scope = sema->root_scope;
|
||||
}
|
||||
|
||||
@@ -211,6 +211,8 @@ static const char *def_kind_name(spl_def_node_t *d) {
|
||||
return "builtin";
|
||||
case SPL_DEF_VAR:
|
||||
return "var";
|
||||
case SPL_DEF_MEMBER:
|
||||
return "member";
|
||||
case SPL_DEF_FN_PARAMS:
|
||||
return "fn_params";
|
||||
case SPL_DEF_AGG:
|
||||
@@ -296,6 +298,9 @@ void spl_type_def_dump(spl_type_t *type, spl_def_id_t id) {
|
||||
case SPL_DEF_VAR:
|
||||
printf(" var=%s", d->var_def.name ? d->var_def.name : "?");
|
||||
break;
|
||||
case SPL_DEF_MEMBER:
|
||||
printf(" member=%s", d->var_def.name ? d->var_def.name : "?");
|
||||
break;
|
||||
case SPL_DEF_ALIAS:
|
||||
case SPL_DEF_DISTINCT:
|
||||
printf(" type=%s", d->type_def.name ? d->type_def.name : "?");
|
||||
|
||||
@@ -58,6 +58,7 @@ typedef struct {
|
||||
spl_def_id_t def_id;
|
||||
spl_type_id_t type_id;
|
||||
usize scope_id;
|
||||
int is_const; /* var_def 是否为 const 声明(只读) */
|
||||
} spl_var_def_t;
|
||||
typedef VEC(spl_var_def_t) spl_var_def_vec_t;
|
||||
|
||||
@@ -65,6 +66,7 @@ typedef struct {
|
||||
enum {
|
||||
SPL_DEF_NONE,
|
||||
SPL_DEF_BUILTIN,
|
||||
SPL_DEF_MEMBER,
|
||||
SPL_DEF_VAR,
|
||||
SPL_DEF_FN_PARAMS,
|
||||
SPL_DEF_AGG, // include enum variants
|
||||
|
||||
168
stage1/splc0.c
168
stage1/splc0.c
@@ -11,6 +11,8 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "spl_ast.h"
|
||||
#include "spl_ast2ir.h"
|
||||
#include "spl_ir2vm.h"
|
||||
#include "spl_lexer.h"
|
||||
#include "spl_sema.h"
|
||||
#include "spl_tok.h"
|
||||
@@ -61,6 +63,11 @@ static void dump_ast(const char *src, const char *fname) {
|
||||
spl_ast_t ast;
|
||||
spl_ast_init(&ast, &toks);
|
||||
spl_ast_prase(&ast);
|
||||
if (ast.parsed < 0) {
|
||||
printf("parse failed, skip AST dump\n");
|
||||
spl_ast_drop(&ast);
|
||||
return;
|
||||
}
|
||||
spl_ast_valid(&ast);
|
||||
spl_ast_dump(&ast, ast.root);
|
||||
spl_ast_drop(&ast);
|
||||
@@ -71,6 +78,11 @@ static void dump_sema(const char *src, const char *fname) {
|
||||
spl_ast_t ast;
|
||||
spl_ast_init(&ast, &toks);
|
||||
spl_ast_prase(&ast);
|
||||
if (ast.parsed < 0) {
|
||||
printf("parse failed, skip sema dump\n");
|
||||
spl_ast_drop(&ast);
|
||||
return;
|
||||
}
|
||||
spl_ast_valid(&ast);
|
||||
spl_sema_t sema;
|
||||
spl_sema_init(&sema);
|
||||
@@ -102,6 +114,39 @@ static void dump_sema(const char *src, const char *fname) {
|
||||
spl_ast_drop(&ast);
|
||||
}
|
||||
|
||||
static void dump_ir(const char *src, const char *fname) {
|
||||
spl_tok_vec_t toks = spl_lex(src, fname);
|
||||
spl_ast_t ast;
|
||||
spl_ast_init(&ast, &toks);
|
||||
spl_ast_prase(&ast);
|
||||
if (ast.parsed < 0) {
|
||||
printf("parse failed, skip IR\n");
|
||||
spl_ast_drop(&ast);
|
||||
return;
|
||||
}
|
||||
spl_ast_valid(&ast);
|
||||
spl_sema_t sema;
|
||||
spl_sema_init(&sema);
|
||||
sema.ast = *
|
||||
spl_sema_run(&sema);
|
||||
spl_sema_check(&sema);
|
||||
if (sema.error_count) {
|
||||
printf("sema errors=%d, skip IR\n", sema.error_count);
|
||||
spl_sema_drop(&sema);
|
||||
spl_ast_drop(&ast);
|
||||
return;
|
||||
}
|
||||
spl_ast2ir_t a2ir;
|
||||
spl_ast2ir_init(&a2ir, &sema);
|
||||
spl_ast2ir_run(&a2ir);
|
||||
if (a2ir.err_count)
|
||||
printf("ast2ir errors=%d\n", a2ir.err_count);
|
||||
spl_ir_dump(&a2ir.ir, &sema.type);
|
||||
spl_ast2ir_drop(&a2ir);
|
||||
spl_sema_drop(&sema);
|
||||
spl_ast_drop(&ast);
|
||||
}
|
||||
|
||||
static int cmd_dump(const char *flags, const char *path) {
|
||||
long len;
|
||||
char *src = read_file(path, &len);
|
||||
@@ -110,38 +155,147 @@ static int cmd_dump(const char *flags, const char *path) {
|
||||
int do_tokens = strstr(flags, "tokens") != NULL || strcmp(flags, "all") == 0;
|
||||
int do_ast = strstr(flags, "ast") != NULL || strcmp(flags, "all") == 0;
|
||||
int do_sema = strstr(flags, "sema") != NULL || strcmp(flags, "all") == 0;
|
||||
int do_ir = strstr(flags, "ir") != NULL || strcmp(flags, "all") == 0;
|
||||
if (do_tokens)
|
||||
dump_tokens(src, path);
|
||||
if (do_ast)
|
||||
dump_ast(src, path);
|
||||
if (do_sema)
|
||||
dump_sema(src, path);
|
||||
if (do_ir)
|
||||
dump_ir(src, path);
|
||||
free(src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static spl_ast_node_t *ast_node_at(spl_ast_t *ast, spl_ast_node_ref_t ref) {
|
||||
if (ref == 0 || ref >= ast->buckets.size)
|
||||
return NULL;
|
||||
return &ast->buckets.data[ref];
|
||||
}
|
||||
|
||||
static int ast_line(spl_ast_t *ast, spl_ast_node_ref_t ref) {
|
||||
spl_ast_node_t *n = ast_node_at(ast, ref);
|
||||
return n ? n->loc.line : 0;
|
||||
}
|
||||
|
||||
/* -g:在 .sir 尾部追加 debug 段(文本:IR 行 + VAR 行),spl_cli -g 读取 */
|
||||
static void gen_debug_map(const char *outpath, spl_ast_t *ast, const spl_ir_t *ir,
|
||||
const spl_ir2vm_t *ir2vm) {
|
||||
FILE *f = fopen(outpath, "ab");
|
||||
if (!f)
|
||||
return;
|
||||
fprintf(f, "SPLDBG\n");
|
||||
for (usize i = 0; i < ir2vm->fdbg.size; i++) {
|
||||
const spl_ir2vm_fdbg_t *fd = &ir2vm->fdbg.data[i];
|
||||
if (fd->fid >= ir->funcs.size)
|
||||
continue;
|
||||
const spl_ir_func_t *fn = &ir->funcs.data[fd->fid];
|
||||
const char *fname = fn->name ? fn->name : "?";
|
||||
for (usize ref = 1; ref < fd->node_first_ip.size; ref++) {
|
||||
usize ip = fd->node_first_ip.data[ref];
|
||||
if (ip == (usize)-1)
|
||||
continue;
|
||||
const spl_ir_node_t *n = &fn->nodes.data[ref];
|
||||
fprintf(f, "IR %zu %zu %d %s\n", ip, ref, ast_line(ast, n->src_ref),
|
||||
spl_ir_kind_name(n->kind));
|
||||
}
|
||||
for (usize j = 0; j < fn->dbg_vars.size; j++) {
|
||||
const spl_ir_dbg_var_t *dv = &fn->dbg_vars.data[j];
|
||||
if (!dv->name)
|
||||
continue;
|
||||
fprintf(f, "VAR %s %s %zu %zu %d\n", fname, dv->name, dv->offset, dv->tid,
|
||||
dv->is_param);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
static int compile_spl(const char *src, const char *fname, const char *outpath, int gen_debug) {
|
||||
spl_tok_vec_t toks = spl_lex(src, fname);
|
||||
spl_ast_t ast;
|
||||
spl_ast_init(&ast, &toks);
|
||||
spl_ast_prase(&ast);
|
||||
if (ast.parsed < 0) {
|
||||
printf("parse failed, no output\n");
|
||||
spl_ast_drop(&ast);
|
||||
return 1;
|
||||
}
|
||||
spl_ast_valid(&ast);
|
||||
spl_sema_t sema;
|
||||
spl_sema_init(&sema);
|
||||
sema.ast = *
|
||||
spl_sema_run(&sema);
|
||||
spl_sema_check(&sema);
|
||||
if (sema.error_count) {
|
||||
printf("sema errors=%d, no output\n", sema.error_count);
|
||||
spl_sema_drop(&sema);
|
||||
spl_ast_drop(&ast);
|
||||
return 1;
|
||||
}
|
||||
spl_ast2ir_t a2ir;
|
||||
spl_ast2ir_init(&a2ir, &sema);
|
||||
spl_ast2ir_run(&a2ir);
|
||||
if (a2ir.err_count) {
|
||||
printf("ast2ir errors=%d, no output\n", a2ir.err_count);
|
||||
spl_ast2ir_drop(&a2ir);
|
||||
spl_sema_drop(&sema);
|
||||
spl_ast_drop(&ast);
|
||||
return 1;
|
||||
}
|
||||
spl_ir2vm_t ir2vm;
|
||||
spl_ir2vm_init(&ir2vm, &a2ir.ir, &sema.type);
|
||||
int rc = spl_ir2vm_run(&ir2vm, outpath);
|
||||
if (gen_debug && rc == 0)
|
||||
gen_debug_map(outpath, &ast, &a2ir.ir, &ir2vm);
|
||||
spl_ir2vm_drop(&ir2vm);
|
||||
spl_ast2ir_drop(&a2ir);
|
||||
spl_sema_drop(&sema);
|
||||
spl_ast_drop(&ast);
|
||||
return rc ? 1 : 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]\n");
|
||||
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]");
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0) {
|
||||
LOG_INFO("splc0 <in> <out> compile (.spl -> .sir, 阶段 B)\n");
|
||||
LOG_INFO("splc0 --dump <flags> <file> dump: tokens,ast,sema,all\n");
|
||||
LOG_INFO("splc0 <in> <out> compile (.spl -> .sir, stage B)");
|
||||
LOG_INFO("splc0 --dump <flags> <file> dump: tokens,ast,sema,ir,all");
|
||||
return 0;
|
||||
}
|
||||
int argi = 1;
|
||||
if (argi >= argc) {
|
||||
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]\n");
|
||||
LOG_FATAL("Usage: splc0 [--dump <flags>] <in> [out]");
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(argv[argi], "--dump") == 0) {
|
||||
if (argc < argi + 3) {
|
||||
LOG_INFO("splc0: --dump need <flags> <file>\n");
|
||||
LOG_INFO("splc0: --dump need <flags> <file>");
|
||||
return 1;
|
||||
}
|
||||
return cmd_dump(argv[argi + 1], argv[argi + 2]);
|
||||
}
|
||||
LOG_FATAL("splc0: compile todo\n");
|
||||
return 1;
|
||||
/* splc0 [-g] <in> <out> */
|
||||
if (argc < argi + 2) {
|
||||
LOG_FATAL("Usage: splc0 <in> <out> [-g] or splc0 --dump <flags> <file>\n");
|
||||
return 1;
|
||||
}
|
||||
int gen_debug = 0;
|
||||
if (strcmp(argv[argi], "-g") == 0) {
|
||||
gen_debug = 1;
|
||||
argi++;
|
||||
}
|
||||
if (argc < argi + 2) {
|
||||
LOG_FATAL("Usage: splc0 <in> <out> [-g] or splc0 --dump <flags> <file>\n");
|
||||
return 1;
|
||||
}
|
||||
long len;
|
||||
char *src = read_file(argv[argi], &len);
|
||||
if (!src)
|
||||
return 1;
|
||||
int rc = compile_spl(src, argv[argi], argv[argi + 1], gen_debug);
|
||||
free(src);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/* spc_vm.c launcher for stage 1 */
|
||||
|
||||
#include "../stage0/spl_mcode.h"
|
||||
#include "../stage0/spl_syscall.h"
|
||||
#include "../stage0/spl_vm.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
int argi = 1;
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
fprintf(stderr, "spc_vm: cannot load '%s'\n", path);
|
||||
return 1;
|
||||
}
|
||||
|
||||
spl_syscall_register(&prog);
|
||||
|
||||
spl_vm_t vm;
|
||||
spl_vm_init(&vm);
|
||||
if (spl_vm_load_prog(&vm, &prog) != 0) {
|
||||
fprintf(stderr, "vm: prog '%s' not found\n", entry);
|
||||
spl_prog_drop(&prog);
|
||||
return 1;
|
||||
}
|
||||
if (spl_vm_prepare(&vm, entry, spl_argc, spl_argv, NULL) != 0) {
|
||||
fprintf(stderr, "vm: entry point '%s' not found\n", entry);
|
||||
spl_prog_drop(&prog);
|
||||
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);
|
||||
spl_prog_drop(&prog);
|
||||
|
||||
return (int)vm.exit_code;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/* ===== 模块1:核心基础 =====
|
||||
* test00_basic — 最小程序 / 返回码
|
||||
* 难度:1/5
|
||||
* 验证点:函数定义、整型返回、ret 语句
|
||||
*/
|
||||
fn main() i32 {
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* ===== 模块1:核心基础 =====
|
||||
* test01_literals — 全部字面量形式
|
||||
* 难度:1/5
|
||||
* 验证点:十进制/十六进制/二进制/八进制整数、
|
||||
* 字符字面量、字符串字面量、布尔字面量、null
|
||||
*/
|
||||
fn main() i32 {
|
||||
/* 整数:十进制 */
|
||||
var a: i32 = 42;
|
||||
if a != 42 { ret 1; }
|
||||
|
||||
/* 整数:十六进制 */
|
||||
var b: i32 = 0xFF;
|
||||
if b != 255 { ret 2; }
|
||||
|
||||
/* 整数:二进制 */
|
||||
var c: i32 = 0b1010;
|
||||
if c != 10 { ret 3; }
|
||||
|
||||
/* 整数:八进制 */
|
||||
var d: i32 = 0o77;
|
||||
if d != 63 { ret 4; }
|
||||
|
||||
/* 字符字面量 */
|
||||
var ch: i32 = 'A';
|
||||
if ch != 65 { ret 5; }
|
||||
|
||||
/* 字符转义 */
|
||||
var nl: i32 = '\n';
|
||||
if nl != 10 { ret 6; }
|
||||
|
||||
/* 布尔字面量 */
|
||||
var t: bool = true;
|
||||
if !t { ret 7; }
|
||||
var f: bool = false;
|
||||
if f { ret 8; }
|
||||
|
||||
/* null 指针 */
|
||||
var np: *i32 = null;
|
||||
if np != null { ret 9; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/* ===== 模块1:核心基础 =====
|
||||
* test02_variables — 变量与常量声明
|
||||
* 难度:1/5
|
||||
* 验证点:var 声明 + 类型注解、var 声明 + 初始化、
|
||||
* const 声明、var := 短声明、const := 短声明
|
||||
*/
|
||||
fn main() i32 {
|
||||
/* var 带类型注解与初始化 */
|
||||
var a: i32 = 10;
|
||||
if a != 10 { ret 1; }
|
||||
|
||||
/* var 先声明后赋值 */
|
||||
var b: i32;
|
||||
b = 20;
|
||||
if b != 20 { ret 2; }
|
||||
|
||||
/* const 声明 */
|
||||
const c: i32 = 30;
|
||||
if c != 30 { ret 3; }
|
||||
|
||||
/* var := 短声明(类型推断) */
|
||||
var d := 40;
|
||||
if d != 40 { ret 4; }
|
||||
|
||||
/* const := 短声明 */
|
||||
const e := 50;
|
||||
if e != 50 { ret 5; }
|
||||
|
||||
/* 多类型变量 */
|
||||
var i8v: i8 = 127;
|
||||
if i8v != 127 { ret 6; }
|
||||
|
||||
var u8v: u8 = 255;
|
||||
if u8v != 255 { ret 7; }
|
||||
|
||||
var i16v: i16 = 32767;
|
||||
if i16v != 32767 { ret 8; }
|
||||
|
||||
var u16v: u16 = 65535;
|
||||
if u16v != 65535 { ret 9; }
|
||||
|
||||
var u32v: u32 = 4294967295;
|
||||
if u32v != 4294967295 { ret 10; }
|
||||
|
||||
/* 指针类型变量 */
|
||||
var x: i32 = 100;
|
||||
var p: *i32 = &x;
|
||||
if p == null { ret 11; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/* ===== 模块2:表达式 =====
|
||||
* test03_arithmetic — 算术运算
|
||||
* 难度:1/5
|
||||
* 验证点:加、减、乘、除、取模、负数、混合运算优先级
|
||||
*/
|
||||
fn main() i32 {
|
||||
/* 加法 */
|
||||
var a := 12 + 34;
|
||||
if a != 46 { ret 1; }
|
||||
|
||||
/* 减法 */
|
||||
var b := 100 - 23;
|
||||
if b != 77 { ret 2; }
|
||||
|
||||
/* 乘法 */
|
||||
var c := 7 * 8;
|
||||
if c != 56 { ret 3; }
|
||||
|
||||
/* 除法 */
|
||||
var d := 100 / 3;
|
||||
if d != 33 { ret 4; }
|
||||
|
||||
/* 取模 */
|
||||
var e := 100 % 3;
|
||||
if e != 1 { ret 5; }
|
||||
|
||||
/* 负数 */
|
||||
var f := -5;
|
||||
if f != -5 { ret 6; }
|
||||
|
||||
/* 负数运算 */
|
||||
var g := -10 + 3;
|
||||
if g != -7 { ret 7; }
|
||||
|
||||
/* 运算优先级:乘优先于加 */
|
||||
var h := 2 + 3 * 4;
|
||||
if h != 14 { ret 8; }
|
||||
|
||||
/* 括号改变优先级 */
|
||||
var i := (2 + 3) * 4;
|
||||
if i != 20 { ret 9; }
|
||||
|
||||
/* 连续运算 */
|
||||
var j := 1 + 2 + 3 + 4 + 5;
|
||||
if j != 15 { ret 10; }
|
||||
|
||||
/* 混合运算 */
|
||||
var k := 10 * 2 - 8 / 4;
|
||||
if k != 18 { ret 11; }
|
||||
|
||||
/* 负数取模 */
|
||||
var l := -7 % 3;
|
||||
if l != -1 { ret 12; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
/* ===== 模块2:表达式 =====
|
||||
* test04_operators — 比较/逻辑/位运算 + 短路求值
|
||||
* 难度:2/5
|
||||
* 验证点:== != < <= > >=、&& || !、& | ^ ~ << >>、&&/|| 短路
|
||||
*/
|
||||
fn main() i32 {
|
||||
/* ---- 比较运算 ---- */
|
||||
if (1 < 2) {} else { ret 1; }
|
||||
if (3 > 1) {} else { ret 2; }
|
||||
if (2 == 2) {} else { ret 3; }
|
||||
if (2 != 3) {} else { ret 4; }
|
||||
if (2 <= 2) {} else { ret 5; }
|
||||
if (2 <= 3) {} else { ret 6; }
|
||||
if (3 >= 3) {} else { ret 7; }
|
||||
if (3 >= 2) {} else { ret 8; }
|
||||
|
||||
/* 比较结果为 0/1 */
|
||||
var eq := (42 == 42);
|
||||
if !eq { ret 9; }
|
||||
var ne := (42 == 43);
|
||||
if ne { ret 10; }
|
||||
var lt := (5 < 10);
|
||||
if !lt { ret 11; }
|
||||
|
||||
/* ---- 逻辑运算 ---- */
|
||||
if (true) {} else { ret 12; }
|
||||
if (!false) {} else { ret 13; }
|
||||
if (true && true) {} else { ret 14; }
|
||||
if (false || true) {} else { ret 15; }
|
||||
|
||||
/* 逻辑非 */
|
||||
var not_t := !true;
|
||||
if not_t { ret 16; }
|
||||
var not_f := !false;
|
||||
if !not_f { ret 17; }
|
||||
|
||||
// /* ---- 短路求值 ---- */
|
||||
// var short1 := 0;
|
||||
// var short2 := 0;
|
||||
// if (false && { short1 = 1; true; }) {} else {}
|
||||
// if short1 != 0 { ret 18; } /* 右侧未执行 */
|
||||
|
||||
/* ---- 位运算 ---- */
|
||||
var band := 0xFF & 0x0F;
|
||||
if band != 0x0F { ret 19; }
|
||||
|
||||
var bor := 0xF0 | 0x0F;
|
||||
if bor != 0xFF { ret 20; }
|
||||
|
||||
var bxor := 0xFF ^ 0x0F;
|
||||
if bxor != 0xF0 { ret 21; }
|
||||
|
||||
var bnot := ~0xFF;
|
||||
/* ~0xFF 在 32 位下 = 0xFFFFFF00,即 -256 */
|
||||
if bnot != -256 { ret 22; }
|
||||
|
||||
/* 左移 */
|
||||
var shl := 1 << 3;
|
||||
if shl != 8 { ret 23; }
|
||||
|
||||
/* 右移 */
|
||||
var shr := 16 >> 2;
|
||||
if shr != 4 { ret 24; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/* ===== 模块3:控制流 =====
|
||||
* test05_control — 分支与循环
|
||||
* 难度:2/5
|
||||
* 验证点:if/else、while、loop、break、continue
|
||||
*/
|
||||
fn main() i32 {
|
||||
/* ---- if/else ---- */
|
||||
var x: i32 = 10;
|
||||
if x > 5 {
|
||||
x = 0;
|
||||
} else {
|
||||
x = 1;
|
||||
}
|
||||
if x != 0 { ret 1; }
|
||||
|
||||
/* else 分支 */
|
||||
var y: i32 = 1;
|
||||
if y > 5 {
|
||||
y = 10;
|
||||
} else {
|
||||
y = 20;
|
||||
}
|
||||
if y != 20 { ret 2; }
|
||||
|
||||
/* if 不带 else */
|
||||
var z: i32 = 0;
|
||||
if z == 0 {
|
||||
z = 99;
|
||||
}
|
||||
if z != 99 { ret 3; }
|
||||
|
||||
/* ---- while ---- */
|
||||
var i: i32 = 0;
|
||||
while i < 5 {
|
||||
i = i + 1;
|
||||
}
|
||||
if i != 5 { ret 4; }
|
||||
|
||||
/* while 条件为 false */
|
||||
var j: i32 = 0;
|
||||
while j > 0 {
|
||||
j = j + 1;
|
||||
}
|
||||
if j != 0 { ret 5; }
|
||||
|
||||
/* ---- loop / break ---- */
|
||||
var k: i32 = 0;
|
||||
loop {
|
||||
k = k + 1;
|
||||
if k >= 3 { break; }
|
||||
}
|
||||
if k != 3 { ret 6; }
|
||||
|
||||
/* ---- continue ---- */
|
||||
var n: i32 = 0;
|
||||
var m: i32 = 0;
|
||||
while n < 5 {
|
||||
n = n + 1;
|
||||
if n == 3 { continue; }
|
||||
m = m + 1;
|
||||
}
|
||||
if n != 5 { ret 7; }
|
||||
if m != 4 { ret 8; }
|
||||
|
||||
/* ---- 嵌套控制流 ---- */
|
||||
var total: i32 = 0;
|
||||
var p: i32 = 0;
|
||||
while p < 3 {
|
||||
var q: i32 = 0;
|
||||
while q < 4 {
|
||||
total = total + 1;
|
||||
q = q + 1;
|
||||
}
|
||||
p = p + 1;
|
||||
}
|
||||
if total != 12 { ret 9; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/* ===== 模块3:控制流 =====
|
||||
* test06_for — for 区间循环
|
||||
* 难度:2/5
|
||||
* 验证点:for begin..end as i、for slice as val、for slice,0.. as val,idx
|
||||
*/
|
||||
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||
|
||||
fn main() i32 {
|
||||
/* for 数值区间:for 0..N as i */
|
||||
var sum: i32 = 0;
|
||||
for 0..5 as i {
|
||||
sum = sum + i;
|
||||
}
|
||||
/* 0+1+2+3+4 = 10 */
|
||||
if sum != 10 { ret 1; }
|
||||
|
||||
/* for 数值区间:非 0 起始 */
|
||||
var sum2: i32 = 0;
|
||||
for 3..7 as i {
|
||||
sum2 = sum2 + i;
|
||||
}
|
||||
/* 3+4+5+6 = 18 */
|
||||
if sum2 != 18 { ret 2; }
|
||||
|
||||
/* for 遍历切片 */
|
||||
var arr: [4]i32 = [4]i32{10, 20, 30, 40};
|
||||
var sl: []i32 = arr[0..];
|
||||
var total: i32 = 0;
|
||||
for sl as val {
|
||||
total = total + val;
|
||||
}
|
||||
/* 10+20+30+40 = 100 */
|
||||
if total != 100 { ret 3; }
|
||||
|
||||
/* for 遍历切片 + 索引 */
|
||||
var arr2: [3]i32 = [3]i32{100, 200, 300};
|
||||
var sl2: []i32 = arr2[0..];
|
||||
var sum_val: i32 = 0;
|
||||
var sum_idx: i32 = 0;
|
||||
for sl2, 0.. as val, idx {
|
||||
sum_val = sum_val + val;
|
||||
sum_idx = sum_idx + idx;
|
||||
}
|
||||
if sum_val != 600 { ret 4; }
|
||||
if sum_idx != 3 { ret 5; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/* ===== 模块4:指针 =====
|
||||
* test07_pointers — 指针操作
|
||||
* 难度:2/5
|
||||
* 验证点:& 取地址、* 解引用、null 空指针、指针自动解引用(->)、.* 后缀解引用
|
||||
*/
|
||||
fn inc(ptr: *i32) void {
|
||||
*ptr = *ptr + 1;
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* ---- & 取地址和 * 解引用 ---- */
|
||||
var x: i32 = 42;
|
||||
var p: *i32 = &x;
|
||||
var v := p.*;
|
||||
if v != 42 { ret 1; }
|
||||
|
||||
/* 通过指针修改 */
|
||||
p.* = 99;
|
||||
if x != 99 { ret 2; }
|
||||
|
||||
/* 指针作为函数参数 */
|
||||
var y: i32 = 5;
|
||||
inc(&y);
|
||||
if y != 6 { ret 3; }
|
||||
|
||||
/* ---- null 空指针 ---- */
|
||||
var np: *i32 = null;
|
||||
if np != null { ret 4; }
|
||||
if np == null {} else { ret 5; }
|
||||
|
||||
/* ---- 指针自动解引用(ptr.field 相当于 ptr->field) ---- */
|
||||
type Point = struct {
|
||||
a: i32,
|
||||
b: i32,
|
||||
};
|
||||
|
||||
var pt: Point;
|
||||
pt.a = 10;
|
||||
pt.b = 20;
|
||||
var pp: *Point = &pt;
|
||||
if pp.a != 10 { ret 6; }
|
||||
if pp.b != 20 { ret 7; }
|
||||
|
||||
/* 通过指针修改字段 */
|
||||
pp.a = 30;
|
||||
if pt.a != 30 { ret 8; }
|
||||
|
||||
/* ---- 指向数组元素的指针 ---- */
|
||||
var buf: [4]i32 = [4]i32{1, 2, 3, 4};
|
||||
// @dbg(); /* 数组初始化后的栈状态 */
|
||||
var elem_ptr: *i32 = &buf[0];
|
||||
// @dbg(); /* &buf[0] 之后:栈上应有指针值 */
|
||||
var loaded := elem_ptr.*;
|
||||
// @dbg(); /* 解引用后:loaded 值 */
|
||||
if loaded != 1 { ret 9; }
|
||||
|
||||
elem_ptr = &buf[1];
|
||||
// @dbg(); /* &buf[1] 之后 */
|
||||
var loaded2 := elem_ptr.*;
|
||||
// @dbg(); /* 解引用后:loaded2 值 */
|
||||
if loaded2 != 2 { ret 10; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/* ===== 模块5:复合类型 =====
|
||||
* test08_arrays — 数组
|
||||
* 难度:2/5
|
||||
* 验证点:数组字面量 [N]T{...}、数组索引、数组元素修改
|
||||
*/
|
||||
fn main() i32 {
|
||||
/* 数组字面量 */
|
||||
var arr: [3]i32 = [3]i32{1, 2, 3};
|
||||
if arr[0] != 1 { ret 1; }
|
||||
if arr[1] != 2 { ret 2; }
|
||||
if arr[2] != 3 { ret 3; }
|
||||
|
||||
/* 数组元素修改 */
|
||||
arr[1] = 99;
|
||||
if arr[1] != 99 { ret 4; }
|
||||
|
||||
/* 数组求和 */
|
||||
var nums: [5]i32 = [5]i32{10, 20, 30, 40, 50};
|
||||
var sum: i32 = 0;
|
||||
sum = sum + nums[0];
|
||||
sum = sum + nums[1];
|
||||
sum = sum + nums[2];
|
||||
sum = sum + nums[3];
|
||||
sum = sum + nums[4];
|
||||
if sum != 150 { ret 5; }
|
||||
|
||||
/* 数组通过循环访问(while) */
|
||||
var vals: [4]i32 = [4]i32{2, 4, 6, 8};
|
||||
var i: i32 = 0;
|
||||
var s: i32 = 0;
|
||||
while i < 4 {
|
||||
s = s + vals[i];
|
||||
i = i + 1;
|
||||
}
|
||||
if s != 20 { ret 6; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* ===== 模块5:复合类型 =====
|
||||
* test09_slices — 切片
|
||||
* 难度:2/5
|
||||
* 验证点:arr[begin..end] 创建切片、arr[begin..] 到末尾、
|
||||
* 切片索引访问、slice.len、slice.ptr
|
||||
*/
|
||||
fn main() i32 {
|
||||
/* 从数组创建切片 */
|
||||
var arr: [5]i32 = [5]i32{10, 20, 30, 40, 50};
|
||||
var slice: []i32 = arr[1..4];
|
||||
|
||||
/* 切片长度 */
|
||||
if slice.len != 3 { ret 1; }
|
||||
|
||||
/* 切片元素访问 */
|
||||
if slice[0] != 20 { ret 2; }
|
||||
if slice[1] != 30 { ret 3; }
|
||||
if slice[2] != 40 { ret 4; }
|
||||
|
||||
/* 省略结束值:arr[begin..] */
|
||||
var full: []i32 = arr[0..];
|
||||
if full.len != 5 { ret 5; }
|
||||
if full[0] != 10 { ret 6; }
|
||||
if full[4] != 50 { ret 7; }
|
||||
|
||||
/* 从开头到中间 */
|
||||
var head: []i32 = arr[0..3];
|
||||
if head.len != 3 { ret 8; }
|
||||
if head[0] != 10 { ret 9; }
|
||||
|
||||
/* 切片遍历(配合 for)*/
|
||||
var nums: [3]i32 = [3]i32{100, 200, 300};
|
||||
var sl: []i32 = nums[0..];
|
||||
var total: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while i < sl.len {
|
||||
total = total + sl[i];
|
||||
i = i + 1;
|
||||
}
|
||||
if total != 600 { ret 10; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
/* ===== 模块5:复合类型 =====
|
||||
* test10_struct — 结构体
|
||||
* 难度:2/5
|
||||
* 验证点:struct 定义、字段访问、内联 type、嵌套结构体
|
||||
*/
|
||||
type Pair = struct {
|
||||
a: i32,
|
||||
b: i32,
|
||||
}
|
||||
|
||||
type Three = struct {
|
||||
a: i64,
|
||||
b: i64,
|
||||
c: i64,
|
||||
}
|
||||
|
||||
type Tag = enum {
|
||||
Unknown,
|
||||
BlockComment,
|
||||
LineComment,
|
||||
Ident,
|
||||
Number,
|
||||
}
|
||||
|
||||
type Token = struct {
|
||||
loc_start: i32,
|
||||
loc_end: i32,
|
||||
tag: Tag,
|
||||
}
|
||||
|
||||
fn make_three(a: i64, b: i64, c: i64) Three {
|
||||
ret Three { .a = a, .b = b, .c = c };
|
||||
}
|
||||
|
||||
fn sum_three(t: Three) i64 {
|
||||
ret t.a + t.b + t.c;
|
||||
}
|
||||
|
||||
fn make_token(s: i32, e: i32, t: Tag) Token {
|
||||
ret Token { .loc_start = s, .loc_end = e, .tag = t };
|
||||
}
|
||||
|
||||
fn test_ret_enum_struct() i32 {
|
||||
var tok := make_token(10, 20, Tag.BlockComment);
|
||||
if tok.loc_start != 10 { ret 1; }
|
||||
if tok.loc_end != 20 { ret 2; }
|
||||
if tok.tag != Tag.BlockComment { ret 3; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_nested_ret_enum() i32 {
|
||||
var tok := make_token(1, 5, Tag.Ident);
|
||||
if tok.tag != Tag.Ident { ret 1; }
|
||||
var tok2 := make_token(6, 9, Tag.Number);
|
||||
if tok2.tag != Tag.Number { ret 2; }
|
||||
if tok.tag != Tag.Ident { ret 3; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_copy_token() i32 {
|
||||
var tok := make_token(0, 100, Tag.LineComment);
|
||||
var c := tok;
|
||||
if c.loc_start != 0 { ret 1; }
|
||||
if c.tag != Tag.LineComment { ret 2; }
|
||||
c.tag = Tag.Ident;
|
||||
if tok.tag != Tag.LineComment { ret 3; }
|
||||
if c.tag != Tag.Ident { ret 4; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_pass_token(t: Token) i32 {
|
||||
if t.tag != Tag.Number { ret 1; }
|
||||
if t.loc_start != 42 { ret 2; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_param_pass_token() i32 {
|
||||
var tok := make_token(42, 99, Tag.Number);
|
||||
var r := test_pass_token(tok);
|
||||
if r != 0 { ret r; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_get_tag(tok: Token) Tag {
|
||||
ret tok.tag;
|
||||
}
|
||||
|
||||
fn test_chain_func_call() i32 {
|
||||
var tok := make_token(1, 2, Tag.BlockComment);
|
||||
var tg := test_get_tag(tok);
|
||||
if tg != Tag.BlockComment { ret 1; }
|
||||
var tok2 := make_token(3, 4, Tag.LineComment);
|
||||
var tg2 := test_get_tag(tok2);
|
||||
if tg2 != Tag.LineComment { ret 2; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_ret_multi_slot_struct() i32 {
|
||||
var t := make_three(10, 20, 30);
|
||||
if t.a != 10 { ret 1; }
|
||||
if t.b != 20 { ret 2; }
|
||||
if t.c != 30 { ret 3; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_pass_struct_by_value() i32 {
|
||||
var t := make_three(10, 20, 30);
|
||||
var sum := sum_three(t);
|
||||
if sum != 60 { ret 1; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_copy_struct() i32 {
|
||||
var t := make_three(10, 20, 30);
|
||||
var t2: Three;
|
||||
t2 = t;
|
||||
if t2.a != 10 { ret 1; }
|
||||
t2.a = 99;
|
||||
if t.a != 10 { ret 2; }
|
||||
if t2.a != 99 { ret 3; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_multi_calls() i32 {
|
||||
var ta := make_three(5, 10, 15);
|
||||
var tb := make_three(20, 25, 30);
|
||||
if ta.a + ta.b + ta.c != 30 { ret 1; }
|
||||
if tb.a + tb.b + tb.c != 75 { ret 2; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_literal_arg() i32 {
|
||||
var sum := sum_three(Three { .a = 7, .b = 8, .c = 9 });
|
||||
if sum != 24 { ret 1; }
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* struct 字段访问 */
|
||||
var p: Pair;
|
||||
p.a = 1;
|
||||
p.b = 2;
|
||||
if p.a != 1 { ret 1; }
|
||||
if p.b != 2 { ret 2; }
|
||||
|
||||
/* 同一类型复用 */
|
||||
var p2: Pair;
|
||||
p2.a = 10;
|
||||
p2.b = 20;
|
||||
if p2.a + p2.b != 30 { ret 3; }
|
||||
|
||||
/* 不影响其他实例 */
|
||||
if p.a != 1 { ret 4; }
|
||||
|
||||
/* 内联 type 定义(在函数内) */
|
||||
type Triple = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
}
|
||||
|
||||
var t: Triple;
|
||||
t.x = 5;
|
||||
t.y = 10;
|
||||
t.z = 15;
|
||||
if t.x + t.y + t.z != 30 { ret 5; }
|
||||
|
||||
/* 结构体字段运算 */
|
||||
var calc: Pair;
|
||||
calc.a = 7;
|
||||
calc.b = 3;
|
||||
var r := calc.a * calc.b + calc.a - calc.b;
|
||||
if r != 25 { ret 6; }
|
||||
|
||||
/* 结构体嵌套 */
|
||||
type Outer = struct {
|
||||
inner: Inner,
|
||||
extra: i32,
|
||||
type Inner = struct {
|
||||
val: i32,
|
||||
}
|
||||
}
|
||||
|
||||
var o: Outer;
|
||||
o.inner.val = 42;
|
||||
o.extra = 58;
|
||||
if o.inner.val != 42 { ret 7; }
|
||||
if o.extra != 58 { ret 8; }
|
||||
|
||||
/* 多 slot 结构体返回值测试 */
|
||||
var r9 := test_ret_multi_slot_struct();
|
||||
if r9 != 0 { ret 100; }
|
||||
var r10 := test_pass_struct_by_value();
|
||||
if r10 != 0 { ret 101; }
|
||||
var r11 := test_copy_struct();
|
||||
if r11 != 0 { ret 102; }
|
||||
var r12 := test_multi_calls();
|
||||
if r12 != 0 { ret 103; }
|
||||
var r13 := test_literal_arg();
|
||||
if r13 != 0 { ret 104; }
|
||||
|
||||
/* 含 enum 字段的结构体返回值测试 */
|
||||
var r14 := test_ret_enum_struct();
|
||||
if r14 != 0 { ret 110; }
|
||||
var r15 := test_nested_ret_enum();
|
||||
if r15 != 0 { ret 111; }
|
||||
var r16 := test_copy_token();
|
||||
if r16 != 0 { ret 112; }
|
||||
var r17 := test_param_pass_token();
|
||||
if r17 != 0 { ret 113; }
|
||||
var r18 := test_chain_func_call();
|
||||
if r18 != 0 { ret 114; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/* ===== 模块5:复合类型 =====
|
||||
* test11_enum — 枚举
|
||||
* 难度:3/5
|
||||
* 验证点:enum 定义、简单枚举值、带数据枚举变体
|
||||
*/
|
||||
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||
|
||||
type Color = enum {
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
}
|
||||
|
||||
type Expr = enum {
|
||||
val: i32,
|
||||
tag: Tag,
|
||||
|
||||
type Tag = enum {
|
||||
TagA,
|
||||
TagB,
|
||||
TagC,
|
||||
}
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
vm_printf("enum values: %d %d %d\n", Color.Red, Color.Green, Color.Blue);
|
||||
vm_printf("enum values: %d %d %d\n", Expr.Tag.TagA, Expr.Tag.TagB, Expr.Tag.TagC);
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/* ===== 模块6:函数 =====
|
||||
* test12_functions — 函数调用
|
||||
* 难度:2/5
|
||||
* 验证点:函数定义与调用、参数传递、返回值、多个参数
|
||||
*/
|
||||
fn add(a: i32, b: i32) i32 {
|
||||
ret a + b;
|
||||
}
|
||||
|
||||
fn sub(a: i32, b: i32) i32 {
|
||||
ret a - b;
|
||||
}
|
||||
|
||||
fn mul(a: i32, b: i32) i32 {
|
||||
ret a * b;
|
||||
}
|
||||
|
||||
fn identity(x: i32) i32 {
|
||||
ret x;
|
||||
}
|
||||
|
||||
fn addr(x: *i32) *i32 {
|
||||
ret x;
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* 函数调用 */
|
||||
var r1 := add(3, 4);
|
||||
if r1 != 7 { ret 1; }
|
||||
|
||||
var r2 := sub(10, 3);
|
||||
if r2 != 7 { ret 2; }
|
||||
|
||||
var r3 := mul(6, 7);
|
||||
if r3 != 42 { ret 3; }
|
||||
|
||||
/* 函数嵌套调用 */
|
||||
var r4 := add(mul(2, 3), sub(10, 4));
|
||||
if r4 != 12 { ret 4; }
|
||||
|
||||
/* 函数返回值的传递 */
|
||||
var r5 := identity(99);
|
||||
if r5 != 99 { ret 5; }
|
||||
|
||||
/* 多个参数 */
|
||||
var r6 := add(add(1, 2), add(3, 4));
|
||||
if r6 != 10 { ret 6; }
|
||||
|
||||
/* 返回地址 */
|
||||
var r7 := addr(&r6);
|
||||
if r7 != &r6 { ret 7; }
|
||||
if r7.* != 10 { ret 8; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* ===== 模块6:函数 =====
|
||||
* test13_extern — 外部 VM 函数与字符串
|
||||
* 难度:2/5
|
||||
* 验证点:@extern(vm) 声明、vm_printf 调用、
|
||||
* 字符串字面量、字符串参数传递
|
||||
*/
|
||||
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||
@extern(vm) fn vm_strlen(s: *u8) i32;
|
||||
@extern(vm) fn vm_strcmp(a: *u8, b: *u8) i32;
|
||||
|
||||
fn main() i32 {
|
||||
/* vm_printf 输出测试 */
|
||||
vm_printf("hello spl\n");
|
||||
vm_printf("%d %d %d\n", 1, 2, 3);
|
||||
vm_printf("chars: %c%c\n", 'A', 'B');
|
||||
|
||||
/* 字符串字面量用于 extern */
|
||||
var len := vm_strlen("hello");
|
||||
if len != 5 { ret 1; }
|
||||
|
||||
/* 字符串比较 */
|
||||
var cmp := vm_strcmp("abc", "abc");
|
||||
if cmp != 0 { ret 2; }
|
||||
|
||||
var cmp2 := vm_strcmp("abc", "def");
|
||||
if cmp2 == 0 { ret 3; }
|
||||
|
||||
/* 字符串作为格式化参数 */
|
||||
vm_printf("string test: %s %s\n", "foo", "bar");
|
||||
|
||||
/* vm_printf 返回 void(不关心返回值)*/
|
||||
vm_printf("all extern tests done\n");
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/* ===== 模块7:高级特性 =====
|
||||
* test14_assignment — 复合赋值
|
||||
* 难度:2/5
|
||||
* 验证点:+= -= *= /= %= &= |= ^= <<= >>=
|
||||
*/
|
||||
fn main() i32 {
|
||||
var x: i32 = 10;
|
||||
|
||||
/* += */
|
||||
x += 5;
|
||||
if x != 15 { ret 1; }
|
||||
|
||||
/* -= */
|
||||
x -= 3;
|
||||
if x != 12 { ret 2; }
|
||||
|
||||
/* *= */
|
||||
x *= 2;
|
||||
if x != 24 { ret 3; }
|
||||
|
||||
/* /= */
|
||||
x /= 4;
|
||||
if x != 6 { ret 4; }
|
||||
|
||||
/* %= */
|
||||
var y: i32 = 17;
|
||||
y %= 5;
|
||||
if y != 2 { ret 5; }
|
||||
|
||||
/* &= */
|
||||
var a: i32 = 0xFF;
|
||||
a &= 0x0F;
|
||||
if a != 0x0F { ret 6; }
|
||||
|
||||
/* |= */
|
||||
var b: i32 = 0xF0;
|
||||
b |= 0x0F;
|
||||
if b != 0xFF { ret 7; }
|
||||
|
||||
/* ^= */
|
||||
var c: i32 = 0xFF;
|
||||
c ^= 0xF0;
|
||||
if c != 0x0F { ret 8; }
|
||||
|
||||
/* <<= */
|
||||
var d: i32 = 1;
|
||||
d <<= 4;
|
||||
if d != 16 { ret 9; }
|
||||
|
||||
/* >>= */
|
||||
var e: i32 = 64;
|
||||
e >>= 3;
|
||||
if e != 8 { ret 10; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/* ===== 模块7:高级特性 =====
|
||||
* test15_defer — 延迟执行
|
||||
* 难度:3/5
|
||||
* 验证点:defer 语句、defer 块、多个 defer(逆序执行)、
|
||||
* 函数中 defer、块作用域 defer
|
||||
*/
|
||||
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||
|
||||
fn with_cleanup() void {
|
||||
defer vm_printf(" inner defer\n");
|
||||
vm_printf(" inside with_cleanup\n");
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
vm_printf("--- defer test ---\n");
|
||||
|
||||
/* defer 语句 */
|
||||
defer vm_printf("defer1\n");
|
||||
vm_printf("after defer1\n");
|
||||
|
||||
/* defer 块 */
|
||||
defer {
|
||||
vm_printf("defer block\n");
|
||||
}
|
||||
vm_printf("after defer block\n");
|
||||
|
||||
/* 函数内 defer */
|
||||
with_cleanup();
|
||||
vm_printf("after with_cleanup\n");
|
||||
|
||||
/* 多个 defer 应逆序执行出作用域应该立刻执行,包括循环作用域 */
|
||||
{
|
||||
defer vm_printf("block defer last\n");
|
||||
defer vm_printf("block defer middle\n");
|
||||
defer vm_printf("block defer first (should print third)\n");
|
||||
}
|
||||
|
||||
/* 多个 defer 应逆序执行 */
|
||||
defer vm_printf("defer last\n");
|
||||
defer vm_printf("defer middle\n");
|
||||
defer vm_printf("defer first (should print third)\n");
|
||||
|
||||
vm_printf("--- defer test end ---\n");
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/* ===== 模块7:高级特性 =====
|
||||
* test16_methods — 类型关联方法
|
||||
* 难度:3/5
|
||||
* 验证点:struct 方法、enum 方法、self 参数自动填充、方法调用
|
||||
*/
|
||||
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||
|
||||
type Point = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
|
||||
fn init(x: i32, y: i32) Point {
|
||||
ret Point { .x = x, .y = y };
|
||||
}
|
||||
|
||||
fn dump(self: *Point) void {
|
||||
vm_printf("Point(%d, %d)\n", self.x, self.y);
|
||||
}
|
||||
}
|
||||
|
||||
type Expr = enum {
|
||||
Int: i32,
|
||||
Add: struct { left: *Expr, right: *Expr },
|
||||
|
||||
fn eval(self: *Expr) i32 {
|
||||
match self {
|
||||
.Int[val] => { ret val; },
|
||||
.Add[sub] => { ret eval(sub.left) + eval(sub.right); }
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* struct 方法调用 */
|
||||
var p: Point = Point.init(3, 4);
|
||||
p.dump(&p);
|
||||
|
||||
/* enum 方法 + match */
|
||||
var expr_l := Expr { .Int = 3 };
|
||||
var expr_r := Expr { .Int = 4 };
|
||||
var expr := Expr { .Add = .{ .left = expr_l, .right = expr_r } };
|
||||
var result := expr.eval(&expr);
|
||||
vm_printf("eval result: %d\n", result);
|
||||
if result != 7 { ret 1; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/* ===== 模块8:综合 =====
|
||||
* test17_integration — 综合测试
|
||||
* 难度:3/5
|
||||
* 验证点:多特性组合——指针 + 结构体 + 函数 + 循环 + 数组 + 切片
|
||||
*/
|
||||
@extern(vm) fn vm_printf(fmt: *u8, ...) void;
|
||||
|
||||
type Point = struct {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
/* 计算点积 */
|
||||
fn dot(a: *Point, b: *Point) i32 {
|
||||
ret a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
/* 递归斐波那契 */
|
||||
fn fib(n: i32) i32 {
|
||||
if n <= 1 { ret n; }
|
||||
ret fib(n - 1) + fib(n - 2);
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* 综合:指针 + 结构体 + 函数 */
|
||||
var p1: Point;
|
||||
p1.x = 3;
|
||||
p1.y = 4;
|
||||
|
||||
var p2: Point;
|
||||
p2.x = 5;
|
||||
p2.y = 6;
|
||||
|
||||
var d := dot(&p1, &p2);
|
||||
/* 3*5 + 4*6 = 15 + 24 = 39 */
|
||||
if d != 39 { ret 1; }
|
||||
|
||||
/* 综合:数组 + while 循环 */
|
||||
var arr: [5]i32 = [5]i32{1, 2, 3, 4, 5};
|
||||
var sum: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while i < 5 {
|
||||
sum = sum + arr[i];
|
||||
i = i + 1;
|
||||
}
|
||||
if sum != 15 { ret 2; }
|
||||
|
||||
/* 综合:切片 + for */
|
||||
var sl: []i32 = arr[1..4];
|
||||
var sl_sum: i32 = 0;
|
||||
for sl as val {
|
||||
sl_sum = sl_sum + val;
|
||||
}
|
||||
if sl_sum != 9 { ret 3; } /* 2+3+4 */
|
||||
|
||||
/* 综合:递归 */
|
||||
var f := fib(10);
|
||||
if f != 55 { ret 4; }
|
||||
|
||||
/* 综合:loop/break/continue */
|
||||
var n: i32 = 0;
|
||||
var count: i32 = 0;
|
||||
loop {
|
||||
n = n + 1;
|
||||
if n == 2 { continue; }
|
||||
count = count + 1;
|
||||
if n >= 5 { break; }
|
||||
}
|
||||
/* 执行 n=1,3,4,5 共 4 次 (n=2 被 continue) */
|
||||
if count != 4 { ret 5; }
|
||||
|
||||
/* 综合:指针修改结构体 */
|
||||
var pp: *Point = &p1;
|
||||
pp.x = 10;
|
||||
pp.y = 20;
|
||||
if p1.x != 10 { ret 6; }
|
||||
if p1.y != 20 { ret 7; }
|
||||
|
||||
/* 综合:嵌套控制流 */
|
||||
var mat: [3]i32 = [3]i32{1, 2, 3};
|
||||
var outer_sum: i32 = 0;
|
||||
var j: i32 = 0;
|
||||
while j < 3 {
|
||||
var k: i32 = 0;
|
||||
while k < 3 {
|
||||
if mat[j] > mat[k] {
|
||||
outer_sum = outer_sum + 1;
|
||||
}
|
||||
k = k + 1;
|
||||
}
|
||||
j = j + 1;
|
||||
}
|
||||
if outer_sum != 3 { ret 8; }
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,488 +0,0 @@
|
||||
/* ===== 模块4:match 语句 =====
|
||||
* test18_match — match 枚举匹配 + 整数匹配(类似 switch)
|
||||
* 难度:3/5
|
||||
* 验证点:枚举匹配、带数据绑定、结构体字段绑定、枚举指针、
|
||||
* 整数自变量匹配、默认分支 _
|
||||
*/
|
||||
|
||||
/* ---- 简单枚举(无数据) ---- */
|
||||
type Color = enum {
|
||||
Red;
|
||||
Green;
|
||||
Blue;
|
||||
}
|
||||
|
||||
/* ---- 带 i32 数据的枚举 ---- */
|
||||
type Optional = enum {
|
||||
Some: i32;
|
||||
None;
|
||||
}
|
||||
|
||||
/* ---- 带结构体数据的枚举 ---- */
|
||||
type Point = struct {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
type Shape = enum {
|
||||
Circle: i32;
|
||||
Rect: Point;
|
||||
}
|
||||
|
||||
/* ---- 多变体枚举 ---- */
|
||||
type ActionResult = enum {
|
||||
Success: i32;
|
||||
NotFound;
|
||||
Timeout: i32;
|
||||
Error: *u8;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 1: 无数据枚举匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_color_match() i32 {
|
||||
var c: Color = Color { .Red };
|
||||
var val: i32 = 0;
|
||||
|
||||
match c {
|
||||
.Red => { val = 1; },
|
||||
.Green => { val = 2; },
|
||||
.Blue => { val = 3; }
|
||||
}
|
||||
if val != 1 { ret 1; }
|
||||
|
||||
c = Color { .Green };
|
||||
match c {
|
||||
.Red => { val = 0; },
|
||||
.Green => { val = 2; },
|
||||
.Blue => { val = 0; }
|
||||
}
|
||||
if val != 2 { ret 2; }
|
||||
|
||||
c = Color { .Blue };
|
||||
match c {
|
||||
.Red => { val = 0; },
|
||||
.Green => { val = 0; },
|
||||
.Blue => { val = 3; }
|
||||
}
|
||||
if val != 3 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 2: 带 i32 数据枚举匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_optional_match() i32 {
|
||||
var o: Optional = Optional { .Some = 42 };
|
||||
|
||||
match o {
|
||||
.Some[val] => {
|
||||
if val != 42 { ret 1; }
|
||||
},
|
||||
.None => {
|
||||
ret 2;
|
||||
}
|
||||
}
|
||||
|
||||
o = Optional { .None };
|
||||
var is_none: i32 = 0;
|
||||
match o {
|
||||
.Some[val] => {},
|
||||
.None => { is_none = 1; }
|
||||
}
|
||||
if is_none != 1 { ret 3; }
|
||||
|
||||
/* 多次提取不同值 */
|
||||
o = Optional { .Some = 99 };
|
||||
match o {
|
||||
.Some[val] => {
|
||||
if val != 99 { ret 4; }
|
||||
},
|
||||
.None => { ret 5; }
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 3: 带结构体数据枚举匹配(多字段绑定)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_shape_match() i32 {
|
||||
/* Circle: 单数据 */
|
||||
var s: Shape = Shape { .Circle = 10 };
|
||||
match s {
|
||||
.Circle[r] => {
|
||||
if r != 10 { ret 1; }
|
||||
},
|
||||
.Rect[p] => {
|
||||
ret 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Rect: 结构体数据,绑定为 (x, y) 对应 Point 的字段 */
|
||||
s = Shape { .Rect = Point { .x = 3, .y = 4 } };
|
||||
match s {
|
||||
.Circle[r] => { ret 3; },
|
||||
.Rect[p] => {
|
||||
if p.x != 3 { ret 4; }
|
||||
if p.y != 4 { ret 5; }
|
||||
}
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 4: 多数据变体枚举匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_action_result_match() i32 {
|
||||
var r: ActionResult = ActionResult { .Success = 200 };
|
||||
match r {
|
||||
.Success[code] => {
|
||||
if code != 200 { ret 1; }
|
||||
},
|
||||
.NotFound => {
|
||||
ret 2;
|
||||
},
|
||||
.Timeout[ms] => {
|
||||
ret 3;
|
||||
},
|
||||
.Error[msg] => {
|
||||
ret 4;
|
||||
}
|
||||
}
|
||||
|
||||
r = ActionResult { .NotFound };
|
||||
var found: i32 = 1;
|
||||
match r {
|
||||
.Success[code] => { found = 0; },
|
||||
.NotFound => { },
|
||||
.Timeout[ms] => { found = 0; },
|
||||
.Error[msg] => { found = 0; }
|
||||
}
|
||||
if found != 1 { ret 5; }
|
||||
|
||||
r = ActionResult { .Timeout = 5000 };
|
||||
match r {
|
||||
.Success[code] => { ret 6; },
|
||||
.NotFound => { ret 7; },
|
||||
.Timeout[ms] => {
|
||||
if ms != 5000 { ret 8; }
|
||||
},
|
||||
.Error[msg] => { ret 9; }
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 5: 枚举指针匹配
|
||||
* ============================================================ */
|
||||
|
||||
fn test_ptr_match() i32 {
|
||||
var c: Color = Color { .Green };
|
||||
var p: *Color = &c;
|
||||
|
||||
match p {
|
||||
.Red => { ret 1; },
|
||||
.Green => { },
|
||||
.Blue => { ret 2; }
|
||||
}
|
||||
|
||||
/* 修改后通过指针匹配 */
|
||||
c = Color { .Blue };
|
||||
match p {
|
||||
.Red => { ret 3; },
|
||||
.Green => { ret 4; },
|
||||
.Blue => { }
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 6: match 作为函数返回值
|
||||
* ============================================================ */
|
||||
|
||||
fn classify_color(c: Color) i32 {
|
||||
match c {
|
||||
.Red => { ret 1; },
|
||||
.Green => { ret 2; },
|
||||
.Blue => { ret 3; }
|
||||
}
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn test_match_in_func() i32 {
|
||||
var r: i32;
|
||||
|
||||
r = classify_color(Color { .Red });
|
||||
if r != 1 { ret 1; }
|
||||
|
||||
r = classify_color(Color { .Green });
|
||||
if r != 2 { ret 2; }
|
||||
|
||||
r = classify_color(Color { .Blue });
|
||||
if r != 3 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 7: match 嵌套在循环中
|
||||
* ============================================================ */
|
||||
|
||||
fn test_match_in_loop() i32 {
|
||||
var i: i32 = 0;
|
||||
var sum: i32 = 0;
|
||||
|
||||
while i < 3 {
|
||||
var o: Optional;
|
||||
if i == 0 {
|
||||
o = Optional { .Some = 10 };
|
||||
} else if i == 1 {
|
||||
o = Optional { .Some = 20 };
|
||||
} else {
|
||||
o = Optional { .Some = 30 };
|
||||
}
|
||||
|
||||
match o {
|
||||
.Some[val] => {
|
||||
sum = sum + val;
|
||||
},
|
||||
.None => { }
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
if sum != 60 { ret 1; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 8: 整数 match(类似 switch)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match() i32 {
|
||||
var x: i32 = 2;
|
||||
var result: i32 = 0;
|
||||
|
||||
match x {
|
||||
1 => { result = 10; },
|
||||
2 => { result = 20; },
|
||||
3 => { result = 30; }
|
||||
}
|
||||
if result != 20 { ret 1; }
|
||||
|
||||
/* 匹配第一个值 */
|
||||
x = 1;
|
||||
match x {
|
||||
1 => { result = 100; },
|
||||
2 => { result = 200; },
|
||||
3 => { result = 300; }
|
||||
}
|
||||
if result != 100 { ret 2; }
|
||||
|
||||
/* 匹配最后一个值 */
|
||||
x = 3;
|
||||
match x {
|
||||
1 => { result = 1000; },
|
||||
2 => { result = 2000; },
|
||||
3 => { result = 3000; }
|
||||
}
|
||||
if result != 3000 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 9: 整数 match 带默认分支 _
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match_default() i32 {
|
||||
var x: i32 = 99;
|
||||
var result: i32 = 0;
|
||||
|
||||
match x {
|
||||
1 => { result = 1; },
|
||||
2 => { result = 2; },
|
||||
_ => { result = 99; }
|
||||
}
|
||||
if result != 99 { ret 1; }
|
||||
|
||||
/* 默认分支未触发 */
|
||||
x = 1;
|
||||
match x {
|
||||
1 => { result = 1; },
|
||||
2 => { result = 2; },
|
||||
_ => { result = 99; }
|
||||
}
|
||||
if result != 1 { ret 2; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 10: 整数 match 多个值跳转到相同逻辑
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match_multi() i32 {
|
||||
var x: i32 = 2;
|
||||
var result: i32 = 0;
|
||||
|
||||
/* 每个分支独立 */
|
||||
match x {
|
||||
0 => { result = 0; },
|
||||
1 => { result = 1; },
|
||||
2 => { result = 2; },
|
||||
3 => { result = 3; }
|
||||
}
|
||||
if result != 2 { ret 1; }
|
||||
|
||||
/* 负数和零 */
|
||||
x = -1;
|
||||
match x {
|
||||
-1 => { result = -1; },
|
||||
0 => { result = 0; },
|
||||
1 => { result = 1; }
|
||||
}
|
||||
if result != -1 { ret 2; }
|
||||
|
||||
x = 0;
|
||||
match x {
|
||||
-1 => { result = -1; },
|
||||
0 => { result = 0; },
|
||||
1 => { result = 1; }
|
||||
}
|
||||
if result != 0 { ret 3; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 11a: match 多值 fallthrough: 1, 2, 3 => body
|
||||
* ============================================================ */
|
||||
|
||||
fn test_int_match_fallthrough() i32 {
|
||||
var x: i32;
|
||||
var r: i32;
|
||||
|
||||
/* 三个值映射到同一个 body */
|
||||
x = 1; r = 0;
|
||||
match x {
|
||||
1, 2, 3 => { r = 10; },
|
||||
4, 5 => { r = 20; }
|
||||
}
|
||||
if r != 10 { ret 1; }
|
||||
|
||||
x = 3;
|
||||
match x {
|
||||
1, 2, 3 => { r = 10; },
|
||||
4, 5 => { r = 20; }
|
||||
}
|
||||
if r != 10 { ret 2; }
|
||||
|
||||
x = 5;
|
||||
match x {
|
||||
1, 2, 3 => { r = 10; },
|
||||
4, 5 => { r = 20; }
|
||||
}
|
||||
if r != 20 { ret 3; }
|
||||
|
||||
/* 单一值(非fallthrough)仍然正常 */
|
||||
x = 7; r = 0;
|
||||
match x {
|
||||
1 => { r = 1; },
|
||||
7 => { r = 7; }
|
||||
}
|
||||
if r != 7 { ret 4; }
|
||||
|
||||
/* 多个负数值 */
|
||||
x = -2; r = 0;
|
||||
match x {
|
||||
-3, -2, -1 => { r = 100; },
|
||||
0, 1 => { r = 200; }
|
||||
}
|
||||
if r != 100 { ret 5; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 11b: match with body 中修改变量
|
||||
* ============================================================ */
|
||||
|
||||
fn test_match_with_side_effects() i32 {
|
||||
var x: i32 = 3;
|
||||
var acc: i32 = 0;
|
||||
|
||||
match x {
|
||||
1 => { acc = acc + 1; },
|
||||
2 => { acc = acc + 2; },
|
||||
3 => { acc = acc + 3; },
|
||||
4 => { acc = acc + 4; }
|
||||
}
|
||||
if acc != 3 { ret 1; }
|
||||
|
||||
/* 再次 match 同一个变量 */
|
||||
match x {
|
||||
1 => { acc = acc + 1; },
|
||||
2 => { acc = acc + 2; },
|
||||
3 => { acc = acc + 3; },
|
||||
4 => { acc = acc + 4; }
|
||||
}
|
||||
if acc != 6 { ret 2; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 主函数
|
||||
* ============================================================ */
|
||||
|
||||
fn main() i32 {
|
||||
var r: i32;
|
||||
|
||||
r = test_color_match();
|
||||
if r != 0 { ret r; }
|
||||
|
||||
r = test_optional_match();
|
||||
if r != 0 { ret r + 10; }
|
||||
|
||||
r = test_shape_match();
|
||||
if r != 0 { ret r + 20; }
|
||||
|
||||
r = test_action_result_match();
|
||||
if r != 0 { ret r + 30; }
|
||||
|
||||
r = test_ptr_match();
|
||||
if r != 0 { ret r + 40; }
|
||||
|
||||
r = test_match_in_func();
|
||||
if r != 0 { ret r + 50; }
|
||||
|
||||
r = test_match_in_loop();
|
||||
if r != 0 { ret r + 60; }
|
||||
|
||||
r = test_int_match();
|
||||
if r != 0 { ret r + 70; }
|
||||
|
||||
r = test_int_match_default();
|
||||
if r != 0 { ret r + 80; }
|
||||
|
||||
r = test_int_match_multi();
|
||||
if r != 0 { ret r + 90; }
|
||||
|
||||
r = test_match_with_side_effects();
|
||||
if r != 0 { ret r + 100; }
|
||||
|
||||
r = test_int_match_fallthrough();
|
||||
if r != 0 { ret r + 110; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/* ===== 进阶数组与切片 =====
|
||||
* test19_hardarray — 多维数组、多维切片、切片构造、字符串测试
|
||||
* 难度:4/5
|
||||
* 验证点:多维数组索引与修改、多维切片、切片字段读写、扁平指针访问、字符串切片
|
||||
*/
|
||||
fn test_string() i32 {
|
||||
/* ============================
|
||||
* String test: str is []u8
|
||||
* ============================ */
|
||||
var data: *u8 = "hello";
|
||||
var s: []u8 = .{ .ptr = data, .len = 5 };
|
||||
|
||||
if s.len != 5 { ret 100; }
|
||||
if s[0] != 104 { ret 101; } /* 'h' */
|
||||
if s[1] != 101 { ret 102; } /* 'e' */
|
||||
if s[4] != 111 { ret 103; } /* 'o' */
|
||||
|
||||
/* Slice the string slice */
|
||||
var sub: []u8 = s[1..4];
|
||||
if sub.len != 3 { ret 104; }
|
||||
if sub[0] != 101 { ret 105; } /* 'e' */
|
||||
if sub[2] != 108 { ret 106; } /* 'l' */
|
||||
|
||||
/* Modify through slice → original changes */
|
||||
s[0] = 72; /* 'H' */
|
||||
if data[0] != 72 { ret 107; }
|
||||
|
||||
/* Full slice */
|
||||
var full: []u8 = s[0..];
|
||||
if full.len != 5 { ret 108; }
|
||||
if full[0] != 72 { ret 109; }
|
||||
|
||||
/* Construct slice from pointer via field assignment */
|
||||
var s2: []u8;
|
||||
s2.ptr = data;
|
||||
s2.len = 3;
|
||||
if s2.len != 3 { ret 110; }
|
||||
if s2[0] != 72 { ret 111; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
fn main() i32 {
|
||||
/* ============================
|
||||
* Part 1: 多维数组(逐元素初始化)
|
||||
* ============================ */
|
||||
var matrix: [2][3]i32;
|
||||
matrix[0][0] = 1; matrix[0][1] = 2; matrix[0][2] = 3;
|
||||
matrix[1][0] = 4; matrix[1][1] = 5; matrix[1][2] = 6;
|
||||
|
||||
if matrix[0][0] != 1 { ret 1; }
|
||||
if matrix[0][1] != 2 { ret 2; }
|
||||
if matrix[0][2] != 3 { ret 3; }
|
||||
if matrix[1][0] != 4 { ret 4; }
|
||||
if matrix[1][1] != 5 { ret 5; }
|
||||
if matrix[1][2] != 6 { ret 6; }
|
||||
|
||||
/* 元素修改 */
|
||||
matrix[0][0] = 10;
|
||||
matrix[1][2] = 60;
|
||||
if matrix[0][0] != 10 { ret 7; }
|
||||
if matrix[1][2] != 60 { ret 8; }
|
||||
|
||||
/* ============================
|
||||
* Part 2: 嵌套循环遍历多维数组
|
||||
* ============================ */
|
||||
var big: [3][4]i32;
|
||||
big[0][0] = 1; big[0][1] = 2; big[0][2] = 3; big[0][3] = 4;
|
||||
big[1][0] = 5; big[1][1] = 6; big[1][2] = 7; big[1][3] = 8;
|
||||
big[2][0] = 9; big[2][1] = 10; big[2][2] = 11; big[2][3] = 12;
|
||||
|
||||
var total: i32 = 0;
|
||||
var i: i32 = 0;
|
||||
while i < 3 {
|
||||
var j: i32 = 0;
|
||||
while j < 4 {
|
||||
total = total + big[i][j];
|
||||
j = j + 1;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
if total != 78 { ret 9; }
|
||||
|
||||
/* ============================
|
||||
* Part 3: &取地址 + 扁平指针访问
|
||||
* ============================ */
|
||||
var flat: *i32 = &matrix[0][0];
|
||||
if flat[0] != 10 { ret 10; }
|
||||
if flat[1] != 2 { ret 11; }
|
||||
if flat[2] != 3 { ret 12; }
|
||||
if flat[3] != 4 { ret 13; }
|
||||
if flat[4] != 5 { ret 14; }
|
||||
if flat[5] != 60 { ret 15; }
|
||||
|
||||
/* ============================
|
||||
* Part 4: 多维数组切片
|
||||
* ============================ */
|
||||
var row: []i32 = matrix[0][0..3];
|
||||
if row.len != 3 { ret 16; }
|
||||
if row[0] != 10 { ret 17; }
|
||||
if row[1] != 2 { ret 18; }
|
||||
if row[2] != 3 { ret 19; }
|
||||
|
||||
/* 切取第二行 */
|
||||
var row2: []i32 = matrix[1][0..];
|
||||
if row2.len != 3 { ret 20; }
|
||||
if row2[0] != 4 { ret 21; }
|
||||
if row2[2] != 60 { ret 22; }
|
||||
|
||||
/* ============================
|
||||
* Part 5: 切片的切片
|
||||
* ============================ */
|
||||
var sub: []i32 = row[1..3];
|
||||
if sub.len != 2 { ret 23; }
|
||||
if sub[0] != 2 { ret 24; }
|
||||
if sub[1] != 3 { ret 25; }
|
||||
|
||||
/* ============================
|
||||
* Part 6: 切片字段读写
|
||||
* ============================ */
|
||||
var arr: [4]i32;
|
||||
arr[0] = 100; arr[1] = 200; arr[2] = 300; arr[3] = 400;
|
||||
var custom: []i32;
|
||||
custom.ptr = &arr[1];
|
||||
custom.len = 2;
|
||||
|
||||
if custom.len != 2 { ret 26; }
|
||||
if custom[0] != 200 { ret 27; }
|
||||
if custom[1] != 300 { ret 28; }
|
||||
|
||||
/* 修改切片长度 */
|
||||
custom.len = 3;
|
||||
if custom.len != 3 { ret 29; }
|
||||
if custom[2] != 400 { ret 30; }
|
||||
|
||||
/* ============================
|
||||
* Part 7: 空范围 / 全切片
|
||||
* ============================ */
|
||||
var full: []i32 = arr[0..];
|
||||
if full.len != 4 { ret 31; }
|
||||
if full[0] != 100 { ret 32; }
|
||||
if full[3] != 400 { ret 33; }
|
||||
|
||||
/* ============================
|
||||
* Part 8: 从指针构造切片
|
||||
* ============================ */
|
||||
var data: [5]i32;
|
||||
data[0] = 10; data[1] = 20; data[2] = 30; data[3] = 40; data[4] = 50;
|
||||
var p: *i32 = &data[2];
|
||||
var from_ptr: []i32 = .{ .ptr = p, .len = 2 };
|
||||
var from_ptr2: []i32;
|
||||
from_ptr2.ptr = p;
|
||||
from_ptr2.len = 2;
|
||||
|
||||
if from_ptr.len != 2 { ret 34; }
|
||||
if from_ptr[0] != 30 { ret 35; }
|
||||
if from_ptr[1] != 40 { ret 36; }
|
||||
|
||||
if from_ptr2.len != 2 { ret 37; }
|
||||
if from_ptr2[0] != 30 { ret 38; }
|
||||
if from_ptr2[1] != 40 { ret 39; }
|
||||
|
||||
/* ============================
|
||||
* Part 9: 切片元素修改反映到原始数组
|
||||
* ============================ */
|
||||
row2[0] = 99;
|
||||
if matrix[1][0] != 99 { ret 40; }
|
||||
|
||||
/* ============================
|
||||
* Part 10: 1D 数组字面量仍然正常
|
||||
* ============================ */
|
||||
var literal: [3]i32 = [3]i32{10, 20, 30};
|
||||
if literal[0] != 10 { ret 41; }
|
||||
if literal[1] != 20 { ret 42; }
|
||||
if literal[2] != 30 { ret 43; }
|
||||
|
||||
/* ============================
|
||||
* Part 11: 字符串切片测试
|
||||
* ============================ */
|
||||
var r: i32 = test_string();
|
||||
if r != 0 { ret r; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
@@ -1,320 +0,0 @@
|
||||
/* ===== 复杂类型嵌套综合测试 =====
|
||||
* test20_complex — 结构体嵌套、切片、数组、方法、类型别名、枚举等
|
||||
* 难度:5/5
|
||||
*/
|
||||
/* ---- 基础结构体 ---- */
|
||||
type Point = struct {
|
||||
var x: i32;
|
||||
var y: i32;
|
||||
}
|
||||
|
||||
/* ---- 嵌套结构体 ---- */
|
||||
type Rect = struct {
|
||||
var min: Point;
|
||||
var max: Point;
|
||||
}
|
||||
|
||||
/* ---- 含切片字段的结构体 (核心 bug 测试) ---- */
|
||||
type Buffer = struct {
|
||||
var data: []u8;
|
||||
var len: usize;
|
||||
}
|
||||
|
||||
/* ---- 含数组字段的结构体 ---- */
|
||||
type MatrixRow = struct {
|
||||
var items: [4]i32;
|
||||
}
|
||||
|
||||
/* ---- 含指针字段的结构体 ---- */
|
||||
type Node = struct {
|
||||
var ptr: *i32;
|
||||
var val: i32;
|
||||
}
|
||||
|
||||
/* ---- 多层级嵌套:结构体里的结构体里的切片 ---- */
|
||||
type Bundle = struct {
|
||||
var name: *u8;
|
||||
var buf: Buffer;
|
||||
var row: MatrixRow;
|
||||
var pt: Point;
|
||||
}
|
||||
|
||||
/* ---- 枚举含数据 ---- */
|
||||
type Status = enum {
|
||||
Active: i32;
|
||||
Inactive;
|
||||
Pending: Point;
|
||||
}
|
||||
|
||||
/* ---- 含方法的结构体 (方法定义在结构体内部) ---- */
|
||||
type Counter = struct {
|
||||
var val: i32;
|
||||
|
||||
fn inc(self: *Counter) i32 {
|
||||
self.val = self.val + 1;
|
||||
ret self.val;
|
||||
}
|
||||
|
||||
fn add(self: *Counter, n: i32) i32 {
|
||||
self.val = self.val + n;
|
||||
ret self.val;
|
||||
}
|
||||
|
||||
fn reset(self: *Counter) {
|
||||
self.val = 0;
|
||||
}
|
||||
|
||||
fn get(self: *Counter) i32 {
|
||||
ret self.val;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 1: 切片在结构体内部初始化 (修复的核心 bug)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_slice_in_struct() i32 {
|
||||
var raw: [4]u8;
|
||||
raw[0] = 65; raw[1] = 66; raw[2] = 67; raw[3] = 68;
|
||||
|
||||
/* Bug fix: { .ptr = ..., .len = ... } inside struct literal */
|
||||
var b: Buffer = Buffer { .data = .{ .ptr = &raw[0], .len = 4 }, .len = 4 };
|
||||
|
||||
if b.len != 4 { ret 1; }
|
||||
if b.data[0] != 65 { ret 2; }
|
||||
if b.data[1] != 66 { ret 3; }
|
||||
if b.data[3] != 68 { ret 4; }
|
||||
|
||||
/* Modify through slice — verify reflection */
|
||||
b.data[0] = 90;
|
||||
if raw[0] != 90 { ret 5; }
|
||||
|
||||
/* Initialize with shorter slice */
|
||||
var b2: Buffer = Buffer { .data = .{ .ptr = &raw[2], .len = 2 }, .len = 2 };
|
||||
if b2.len != 2 { ret 6; }
|
||||
if b2.data[0] != 67 { ret 7; }
|
||||
|
||||
/* Slice field assignment via field access */
|
||||
b2.data.ptr = &raw[0];
|
||||
b2.data.len = 4;
|
||||
if b2.data[0] != 90 { ret 8; }
|
||||
if b2.data.len != 4 { ret 9; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 2: 结构体含数组字段
|
||||
* ============================================================ */
|
||||
|
||||
fn test_struct_with_array() i32 {
|
||||
var mr: MatrixRow = MatrixRow { .items = [4]i32{10, 20, 30, 40} };
|
||||
|
||||
if mr.items[0] != 10 { ret 1; }
|
||||
if mr.items[1] != 20 { ret 2; }
|
||||
if mr.items[2] != 30 { ret 3; }
|
||||
if mr.items[3] != 40 { ret 4; }
|
||||
|
||||
/* 修改数组元素 */
|
||||
mr.items[2] = 99;
|
||||
if mr.items[2] != 99 { ret 5; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 3: 嵌套结构体初始化
|
||||
* ============================================================ */
|
||||
|
||||
fn test_nested_struct() i32 {
|
||||
var p: Point = Point { .x = 5, .y = 10 };
|
||||
if p.x != 5 { ret 1; }
|
||||
if p.y != 10 { ret 2; }
|
||||
|
||||
/* 嵌套结构体字面量 */
|
||||
var r: Rect = Rect {
|
||||
.min = Point { .x = 1, .y = 2 },
|
||||
.max = Point { .x = 3, .y = 4 }
|
||||
};
|
||||
if r.min.x != 1 { ret 3; }
|
||||
if r.min.y != 2 { ret 4; }
|
||||
if r.max.x != 3 { ret 5; }
|
||||
if r.max.y != 4 { ret 6; }
|
||||
|
||||
/* 修改嵌套字段 */
|
||||
r.min.x = 100;
|
||||
if r.min.x != 100 { ret 7; }
|
||||
if r.min.y != 2 { ret 8; } /* unchanged */
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 4: 结构体成员方法 (实例方法调用)
|
||||
* ============================================================ */
|
||||
|
||||
fn test_struct_method() i32 {
|
||||
var c: Counter = Counter { .val = 0 };
|
||||
|
||||
/* 实例方法调用 c.inc(&c) */
|
||||
var r1: i32 = c.inc(&c);
|
||||
if r1 != 1 { ret 1; }
|
||||
if c.val != 1 { ret 2; }
|
||||
|
||||
/* 带参数方法调用 c.add(&c, n) */
|
||||
var r2: i32 = c.add(&c, 5);
|
||||
if r2 != 6 { ret 3; }
|
||||
if c.val != 6 { ret 4; }
|
||||
|
||||
/* 连续调用 (显式 self) */
|
||||
c.reset(&c);
|
||||
if c.val != 0 { ret 5; }
|
||||
|
||||
c.add(&c, 10);
|
||||
c.inc(&c);
|
||||
var r3: i32 = c.get(&c);
|
||||
if r3 != 11 { ret 6; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 5: 结构体含指针字段
|
||||
* ============================================================ */
|
||||
|
||||
fn test_ptr_in_struct() i32 {
|
||||
var v: i32 = 42;
|
||||
var n: Node = Node { .ptr = &v, .val = 99 };
|
||||
|
||||
if n.val != 99 { ret 1; }
|
||||
if n.ptr[0] != 42 { ret 2; }
|
||||
|
||||
/* 通过指针修改 */
|
||||
v = 100;
|
||||
if n.ptr[0] != 100 { ret 3; }
|
||||
|
||||
/* 通过指针在结构体内修改 */
|
||||
n.ptr[0] = 200;
|
||||
if v != 200 { ret 4; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 6: 多层级复杂嵌套
|
||||
* ============================================================ */
|
||||
|
||||
fn test_complex_nesting() i32 {
|
||||
var str_data: [5]u8;
|
||||
str_data[0] = 72; str_data[1] = 101;
|
||||
str_data[2] = 108; str_data[3] = 108; str_data[4] = 111;
|
||||
|
||||
var bundle: Bundle = Bundle {
|
||||
.name = &str_data[0],
|
||||
.buf = Buffer { .data = .{ .ptr = &str_data[1], .len = 3 }, .len = 3 },
|
||||
.row = MatrixRow { .items = [4]i32{1, 2, 3, 4} },
|
||||
.pt = Point { .x = -5, .y = 15 }
|
||||
};
|
||||
|
||||
/* Verify name */
|
||||
if bundle.name[0] != 72 { ret 1; }
|
||||
if bundle.name[4] != 111 { ret 2; }
|
||||
|
||||
/* Verify nested slice */
|
||||
if bundle.buf.len != 3 { ret 3; }
|
||||
if bundle.buf.data[0] != 101 { ret 4; }
|
||||
if bundle.buf.data[2] != 108 { ret 5; }
|
||||
|
||||
/* Verify array field */
|
||||
if bundle.row.items[0] != 1 { ret 6; }
|
||||
if bundle.row.items[3] != 4 { ret 7; }
|
||||
|
||||
/* Verify nested struct field */
|
||||
if bundle.pt.x != -5 { ret 8; }
|
||||
if bundle.pt.y != 15 { ret 9; }
|
||||
|
||||
/* Modify nested slice */
|
||||
bundle.buf.data[1] = 87; /* 'W' */
|
||||
if str_data[2] != 87 { ret 10; }
|
||||
|
||||
/* Modify nested array */
|
||||
bundle.row.items[2] = 33;
|
||||
if bundle.row.items[2] != 33 { ret 11; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 测试 7: 枚举含结构体数据
|
||||
* ============================================================ */
|
||||
|
||||
fn test_enum_complex() i32 {
|
||||
var s: Status = Status { .Active = 42 };
|
||||
|
||||
/* Verify active variant */
|
||||
match s {
|
||||
.Active[val] => {
|
||||
if val != 42 { ret 1; }
|
||||
},
|
||||
.Inactive => {
|
||||
ret 2;
|
||||
},
|
||||
.Pending[p] => {
|
||||
ret 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* Test Inactive variant */
|
||||
var s2: Status = Status { .Inactive };
|
||||
var is_inactive: i32 = 0;
|
||||
match s2 {
|
||||
.Active[val] => {},
|
||||
.Inactive => { is_inactive = 1; },
|
||||
.Pending[p] => {}
|
||||
}
|
||||
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; },
|
||||
.Inactive => { ret 6; },
|
||||
.Pending[p] => {
|
||||
if p.x != 7 { ret 7; }
|
||||
if p.y != 8 { ret 8; }
|
||||
}
|
||||
}
|
||||
|
||||
ret 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 主函数
|
||||
* ============================================================ */
|
||||
|
||||
fn main() i32 {
|
||||
var r: i32;
|
||||
|
||||
r = test_slice_in_struct();
|
||||
if r != 0 { ret r; }
|
||||
|
||||
r = test_struct_with_array();
|
||||
if r != 0 { ret r + 100; }
|
||||
|
||||
r = test_nested_struct();
|
||||
if r != 0 { ret r + 200; }
|
||||
|
||||
r = test_struct_method();
|
||||
if r != 0 { ret r + 300; }
|
||||
|
||||
r = test_ptr_in_struct();
|
||||
if r != 0 { ret r + 400; }
|
||||
|
||||
r = test_complex_nesting();
|
||||
if r != 0 { ret r + 500; }
|
||||
|
||||
r = test_enum_complex();
|
||||
if r != 0 { ret r + 600; }
|
||||
|
||||
ret 0;
|
||||
}
|
||||
Reference in New Issue
Block a user