🍋 Rex 编程语言

Rex 编程语言语法参考(一):语言基础语法

Rex 是一门静态类型、编译型编程语言,使用 Python 实现,基于 LLVM(llvmlite)作为编译器后端,采用 LLJIT 执行。解析器采用手写递归下降解析器配合 Pratt 表达式解析。并发采用真实操作系统线程模型(thread 命名空间,配套互斥锁与原子操作,见库语法文档)。

本文档覆盖语言核心语法(类型、字面量、运算符、控制流、函数、结构体、数组、错误处理、模块、FFI、泛型等)。 标准库与运行时库函数math/io/file/os/string/thread/map/set 等命名空间)见《Rex 编程语言语法参考(二):库语法》。 实战陷阱、性能优化、AOT、包管理、LSP、DAP、CLI 等工具与实现内容见《Rex 编程语言工具与实现参考》。

⚠️ 程序入口(必读):Rex 的可执行代码必须写在函数内,并以 fn main() { ... } 作为程序入口。 把 let/print 等语句直接写在文件顶层(函数外)不会被执行,编译器会报错 ("顶层不能出现可执行语句");只有函数声明而没有 fn main 时程序同样无法运行。 最简单的完整程序:

rex
fn main() {
    print("Hello, Rex!")
}

下文各节的语法片段为节选,默认省略了外层 fn main,复制运行时请放进 fn main 内; 可直接运行的完整示例见第 24 章。

1. 数据类型

内置类型

类型分类类型关键字LLVM 映射
有符号整数int(64位)i64
浮点数float(64位,双精度)double
布尔booli1
字符串stri8*(C风格)
指针ptri8*(通用)
空类型voidvoid(仅返回值)
结构体struct Name { ... }LLVM 结构体类型
数组T[] 注解 / [T, ...] 静态 / new T[N] 动态[N x T] / T*
字典Map[str, V] 注解(V ∈ int/float/str)i64(句柄)
集合Set[T] 注解(T ∈ int/float/str)i64(句柄)
列表List[T] 注解(语法糖,等价 T[]T*(动态数组)

类型系统精简:整数统一为有符号 64 位 int,浮点统一为 64 位 float。 已移除 int64uintuint64float64char 类型(不再向后兼容)。 字符用单字符字符串 "a" 表达。

字典类型Map[str, V] 是语言级字典(哈希表),LLVM 层表示为 i64 句柄, v1 仅支持字符串键、int/float/str 三种值类型(详见第 13 章「Map 字典」)。

集合类型Set[T] 是语言级集合(哈希表,元素去重),LLVM 层表示为 i64 句柄, 元素类型支持 int/float/str(详见第 14 章「Set 集合」)。

列表类型List[T]T[] 的语法糖(动态数组),解析期直接规范化为 T[], 复用动态数组机制(自动扩容、push、length、帧级释放)(见第 12 章)。

数组类型精简:数组变量用 T[] 类型注解声明(如 int[]float[][]), 不再用标量类型 int 注解数组(已移除,不再向后兼容)。

各类型声明速览(入门第一段代码):

rex
fn main() {
    // 标量类型
    let n: int = 42                   // 64 位有符号整数
    let f: float = 3.14               // 64 位双精度浮点数
    let b: bool = true                // 布尔
    let s: str = "Rex"                // 字符串(双引号定义)
    let p: ptr = null                 // 通用指针(FFI 用,可判空)

    // 复合类型(详见第 12~14 章)
    var arr: int[] = [1, 2, 3]        // 动态数组(自动扩容、越界检查)
    var m: Map[str, int] = {}         // 字典(字符串键 -> int 值)
    var set1: Set[str] = set.create() // 集合(元素去重)

    print(n, f, b, s)                 // 42 3.140000 1 Rex
    print(arr.len)                    // 3
    map.set(m, "k", 1)
    print(map.get(m, "k"))            // 1
    set1.add("x")
    print(set1.has("x"))              // 1
}

2. 字面量

整数字面量

rex
42
0
-100
1_000_000    // 下划线分隔,提高可读性(等价于 1000000)

下划线数字:1_000_000 等价于 1000000,下划线必须位于数字之间(1_0003_000.5 合法,_11_1__0 非法)。

浮点字面量

rex
3.14
0.5
.5       // 小数点前可省略

布尔字面量

rex
true
false

字符串字面量

rex
"hello"

字符串引号精简(不再向后兼容):字符串统一只用双引号定义, 已移除单引号字符串(如 'world')。使用单引号会得到词法错误提示: use double quotes for strings, e.g. "hello" instead of 'hello'

空值字面量

rex
null

数组字面量

rex
[1, 2, 3]
[1.1, 2.2, 3.3]

Map 字面量

rex
{}                              // 空 Map
{ "name": "Rex", "age": 30 }    // 键值对(键为字符串字面量/表达式)

非空 Map 字面量必须配合显式类型注解(如 var m: Map[str, int] = { "a": 1 }), 编译器依据注解确定值类型;空 Map 字面量 {} 也建议显式注解。

结构体字面量

rex
Point { x: 10.5, y: 20.5 }

结构体名必须以大写字母开头。

3. 注释

rex
// 单行注释

/*
多行块注释
可跨行
*/

4. 标识符

5. 关键字(共21个)

关键字用途
let不可变变量声明
var可变变量声明
fn函数声明
return从函数返回
if条件分支
else备选分支
whilewhile 循环
break退出循环
continue跳过本次迭代
forfor 循环(for-in 和 range)
import模块导入
from选择性导入
infor-in 循环关键字
try异常处理块
catch捕获异常块
throw抛出异常
new堆数组分配
defer延迟执行(仅单行形式)
struct结构体类型定义
extern外部函数接口(FFI)
as类型转换运算符

关键字精简(不再向后兼容):

  • 已移除 len 关键字,改用属性访问 arr.len 获取数组长度
  • 已移除 match/case/default 关键字,模式匹配改用 if-else 链实现
  • defer 仅支持单行形式 defer statement,不再支持大括号块
  • 内存管理关键字 deletescope 已移除(仅保留 new
  • 堆内存只通过 new 分配,生命周期自动绑定当前函数栈帧,函数返回时自动释放
  • 运行时函数返回的字符串同样由函数帧自动管理(帧内跟踪 + 帧末释放,详见第 12 章「字符串内存管理」)
  • 返回堆数组需用内置 move() 函数转移所有权
  • C 风格 for (...) 循环已移除,统一用 for i in range(start, end) 代替(不再向后兼容)
  • finally 关键字已移除,清理逻辑统一用 defer 表达;try 必须带至少一个 catch

6. 运算符

算术运算符

运算符Token示例
加法+a + b
减法-a - b
乘法*a * b
除法/a / b
幂运算**a ** 2
取模%a % b

比较运算符

运算符Token示例
等于==a == b
不等于!=a != b
小于<a < b
大于>a > b
小于等于<=a <= b
大于等于>=a >= b

逻辑运算符

运算符Token示例
逻辑与&&a && b
逻辑或||a || b

位运算符

运算符Token示例
位与&a & b
位或|a | b
位异或^a ^ b
位非(前缀)~~a
左移<<a << 2
右移>>a >> 2

前缀运算符

运算符Token示例
逻辑非!!flag
取负--value
取正(空操作)++value

类型转换

运算符Token示例
类型转换asx as floatf as int

复合赋值运算符

运算符Token示例
赋值=x = 5
加赋值+=x += 5
减赋值-=x -= 5
乘赋值*=x *= 2
除赋值/=x /= 3

复合赋值精简(不再向后兼容):

  • 已移除 %=**=&=|=^=<<=>>= 七个复合赋值运算符,统一用完整表达式替代:
  • x %= yx = x % y
  • x **= yx = x ** y
  • x &= yx = x & y
  • x |= yx = x | y
  • x ^= yx = x ^ y
  • x <<= yx = x << y
  • x >>= yx = x >> y
  • 使用被移除的运算符会得到词法错误提示:

Compound assignment operator '%=' has been removed(附带替换建议)

成员访问和索引

运算符Token示例
成员访问.p.xrect.widthmath.sin(1.0)(标准库命名空间调用)
索引[...]arr[0]arr[i][j]
错误传播(后置)?let x = bar()?(详见第 15 章)
箭头(返回类型)->fn foo() -> int

7. 运算符优先级(从低到高)

优先级运算符
1P_LOWEST默认
2P_OR||
3P_AND&&
4P_BIT_OR|
5P_BIT_XOR^
6P_BIT_AND&
7P_EQUALS==!=
8P_LESSGREATER<><=>=
9P_SHIFT<<>>
10P_SUM+-
11P_PRODUCT*/%
12P_EXPONENT**(幂运算)
13P_PREFIX-!~
14P_ASas(类型转换)
15P_CALL( 函数调用
16P_INDEX[ 索引
17P_DOT. 成员访问

8. 控制流

if / else

rex
if condition {
    // 语句体
}

if condition {
    // 语句体
} else {
    // 备选语句
}

while 循环

rex
while condition {
    // 语句体
    break     // 退出循环
    continue  // 跳过本次迭代
}

range() 整数范围循环

rex
for i in range(0, 10) {
    // 语句体(i 从 0 到 9)
}

// 单参数形式:range(end) 等价于 range(0, end)
for j in range(5) {
    // 语句体(j 从 0 到 4)
}

for-in 循环(遍历数组)

rex
// 简单遍历
for item in array {
    // 语句体
}

// 带索引遍历
for i, item in array {
    // 语句体
}

// 带 let/var 限定词
for let item in array { ... }
for var item in array { ... }

条件分支链(if / else if / else)

match 模式匹配已移除,改用 if-else 链实现同样的分支逻辑:

rex
if x == 1 {
    // 语句体
} else if x == 2 || x == 3 {
    // 多条件分支
} else {
    // 兜底分支
}

defer 延迟执行

rex
defer print("simple cleanup")

break / continue

return

rex
return            // 返回值,或从 void 函数返回
return expr       // 返回表达式值

9. 变量声明

rex
// 不可变常量
let pi: float = 3.14159

// 可变变量
var count: int = 0
var temperature: float = 25.5

// 赋值
x = 10
x += 5
arr[0] = 100      // 数组索引赋值
p.x = 30.5        // 结构体成员赋值

10. 函数声明

rex
// 基本函数
fn add(a: int, b: int) -> int {
    return a + b
}

// void 函数(无需返回类型)
fn greet(name: str) {
    print("Hello,", name)
}

// 自动返回类型推断(省略 ->type)
fn factorial(n: int) {
    if n <= 1 {
        return 1
    }
    return n * factorial(n - 1)
}

// 函数调用必须写在函数内(如 main)
fn main() {
    let r: int = add(5, 3)
    print(r)        // 8
}

11. 结构体

rex
// 定义结构体
struct Point {
    x: float
    y: float
}

struct Rect {
    width: int
    height: int
}

// 创建结构体实例
var p: Point = Point { x: 10.5, y: 20.5 }

// 成员访问和赋值
print(p.x)
p.x = 30.5

// 表达式字段
var r: Rect = Rect { width: 100, height: 200 }
var area: int = r.width * r.height

结构体字段可以是内置类型或其他结构体类型。结构体名必须以大写字母开头。

12. 数组

数组变量通过 T[] 类型注解声明(T 为元素类型,[] 表示数组;多维数组用 T[][]), 不再使用标量类型(如 int)注解数组。

静态数组(数组字面量)

rex
fn main() {
    let arr: int[] = [1, 2, 3, 4, 5]
    let floats: float[] = [1.1, 2.2, 3.3]

    // 索引
    print(arr[0])
    arr[2] = 30
}

字面量 + T[] 注解 = 动态数组var a: int[] = [1, 2, 3] 生成的是真正的动态数组 (带头元数据的堆分配,等价 new int[3]),具备 push/自动扩容/越界检查/帧末释放等 完整动态数组语义(2026-08-01 批次修复,此前字面量是静态只读全局数组,push 不可靠)。 无数组注解时(如 let a = [1, 2, 3])字面量保持静态语义。 多维限制int[][] 类型 + 数组字面量暂不支持转动态数组,回退静态语义。

List[T](动态数组语法糖)

List[T] 在解析期直接规范化为 T[],是 T[] 的别名语法:

rex
var a: List[int] = [1, 2, 3]   // 等价 var a: int[] = [1, 2, 3]
a.push(4)                        // 自动扩容
print(a.len)                  // 4
var b: List[str] = ["x", "y"]   // List[str] -> str[]
var c: List[List[int]]          // 嵌套:List[List[int]] -> int[][]

动态数组(堆分配)

rex
// 一维数组
var arr: int[] = new int[10]
arr[0] = 100
arr[5] = 500
// 函数返回时自动释放,无需 delete

// 多维数组
var matrix: int[][] = new int[3][4]
matrix[0][0] = 100

// 数组长度属性 .len
print("长度:", matrix.len)  // 返回元素个数

// 自动扩容:超出容量的写入会自动扩容
var dynamic: int[] = new int[3]
dynamic[5] = 60  // 自动扩容,length 变为 6
print("扩容后长度:", dynamic.len)  // 输出 6

扩容的内存安全语义

自动扩容采用不搬移旧内存的策略,保证外部指针在函数执行期间不会悬垂:

但通过旧指针读取到的是扩容前的旧快照,与 arr[i] 读取的新数据可能不一致

数组作为函数参数/返回值

rex
// 数组参数:按元素指针传递(动态数组)
fn sum(arr: int[], n: int) -> int {
    var total: int = 0
    for i in range(0, n) {
        total = total + arr[i]
    }
    return total
}

let data: int[] = new int[5]
data[0] = 1
data[1] = 2
data[2] = 3
data[3] = 4
data[4] = 5
print("sum =", sum(data, 5))  // 15

注意:数组参数按值传递(指针),函数内越界写入会触发边界错误,不会自动扩容。 调用方仍持有该数组的所有权,函数返回时由调用方释放。

内存管理

Rex 采用函数帧自动内存管理new 分配的内存生命周期绑定当前函数栈帧,函数返回时自动释放。 运行时函数返回的字符串同样采用帧级管理(见下方「字符串内存管理」)。

rex
// ✅ 推荐:new 分配,函数返回时自动释放
var arr: int[] = new int[10]
// 无需 delete,无需 scope

// 重新赋值:旧内存自动释放
arr = new int[20]
arr[0] = 100

// 可作为参数传给子函数(所有权仍在当前函数)
fn use(a: int[], n: int) -> int {
    return a[n - 1]
}
print("last =", use(arr, 1))

// 返回堆数组:必须用 move() 转移所有权给调用方
fn make_array() -> int[] {
    let result: int[] = new int[3]
    result[0] = 42
    return move(result)  // 所有权转移,函数返回时不会释放
}
var got: int[] = make_array()
print("got[0] =", got[0])  // got 现在拥有该内存,函数返回时释放

规则:

字符串内存管理

运行时函数返回的字符串与 new 数组一样采用函数帧自动内存管理(帧内跟踪 + 帧末释放):

rex
fn make_greeting() -> str {
    var s: str = string.concat("Hello, ", "Rex!")   // 分配字符串,登记到当前帧
    s = string.concat(s, " ")                       // 每次调用分配新字符串
    s = string.concat(s, "world")
    return s                                        // 返回值所有权移交调用方帧
}

fn main() {
    let g: str = make_greeting()   // g 由 main 的帧接管,main 返回时释放
    print(g)

    // 循环追加模式:字符串不可变,每次 concat 都是全量拷贝(O(n²))
    // 但内存不会泄漏——所有旧字符串在函数返回时统一释放
    var acc: str = ""
    var i: int = 0
    while i < 100 {
        acc = string.concat(acc, "x")
        i = i + 1
    }
    print(string.len(acc))
}

规则:

已知限制:字符串不可变,s = string.concat(s, "x") 在循环中是严格的 O(n²) 全量拷贝。 不做"就地追加"优化是因为它会破坏别名安全(let a = s 后若 s 被 realloc,a 会悬垂)。 实战建议:需要循环拼接大字符串时,使用 string.builder_* StringBuilder (追加零分配、tostr 一次性 O(n),见库语法文档),而非拆分逻辑。

编译期警告(帧池陷阱治理):编译器会检测循环(while / for-in / for range)体内 s = string.concat(s, ...)自我拼接模式并发出警告 (String concat in a loop reassigns 's' from itself: ...), 提示改用 StringBuilder。该警告不影响编译与运行,仅用于早期拦截 O(n²) + 帧内存累积陷阱。

中途释放(= []

大数组不再需要时,可用空数组字面量立即释放旧内存(变为 length 为 0 的空数组):

rex
// 分配 1GB 缓冲区(下划线数字可读性更好)
var buffer: int[] = new int[1_000_000_000]
buffer[0] = 100
// ... 处理前半部分 ...

// ⚡ 前半部分处理完,立即释放旧内存,削平内存峰值
buffer = []        // 等价于:释放旧内存 + buffer = new int[0]

// 后续逻辑内存占用极低
print("length =", buffer.len)   // 0

// 声明即空数组
var empty: int[] = []

安全机制

  • 边界检查:越界访问触发运行时错误
  • 空指针检测:释放/转移后的指针访问触发运行时错误
  • 自动释放:new 分配的数组与运行时返回的字符串都在函数返回时自动释放,无内存泄漏
  • 扩容稳定:自动扩容不移动旧内存块(延迟到函数帧末释放),FFI 持有的内部指针在函数内不会悬垂

禁止变量别名(隐式移动)

堆数组始终只有一个所有者。把堆数组变量赋给另一个变量时,所有权自动转移(隐式移动),源变量立即失效(置 null):

rex
var a: int[] = new int[3]
a[0] = 10

let b: int[] = a    // 隐式移动:a 的所有权转移给 b,a 置 null
print("b[0] =", b[0])   // 10
// a[0]            // ⚠️ a 已失效,访问触发空指针运行时错误

var x: int[] = new int[2]
var y: int[] = new int[2]
y = x              // 隐式移动:x 置 null,y 旧内存自动释放

静态数组(字面量 [1, 2, 3])是不可变全局常量,可自由共享,不适用上述规则。

复制数组(copy()

需要多份独立数据(如 b/c/d 各自持有 a 的副本)时,用内置 copy(arr) 深拷贝。 copy()借用语义:数组以参数身份传入,所有权不动,源数组保持有效, 可以重复调用 copy(a) 生成任意多个副本,互不影响:

rex
var a: int[] = new int[3]
a[0] = 10
a[1] = 20
a[2] = 30

let b: int[] = copy(a)   // 深拷贝:b 持有独立内存,a 仍有效
let c: int[] = copy(a)   // 再拷一份
let d: int[] = copy(a)

b[0] = 99                // 改副本不影响源与其他副本
print("a[0] =", a[0])    // 10(a 未失效)
print("c[0] =", c[0])    // 10
print("d[2] =", d[2])    // 30

类型匹配检查

编译器会检查类型注解与值的匹配,以下写法会产生编译错误:

rex
let a: int = [1, 2, 3]    // ❌ 错误:int 是标量,不能注解数组(应使用 int[])
let b: int = new int[5]   // ❌ 错误:同上
let c: int[] = 5          // ❌ 错误:int[] 是数组,不能赋标量值

13. Map 字典

Map[str, V] 是语言级字典(哈希表),LLVM 层表示为 i64 句柄。 v1 仅支持字符串键,值类型支持 int / float / str 三种;使用其他键/值类型会得到编译错误。

rex
// 声明与初始化
var scores: Map[str, int] = {}
var config: Map[str, str] = { "name": "Rex", "mode": "prod" }

// 写入 / 读取(命名空间调用,见库语法文档「map 函数」)
map.set(scores, "alice", 100)
map.set(scores, "bob", 85)
print(map.get(scores, "alice"))    // 100

// 查询 / 删除 / 长度
if map.has(scores, "bob") { ... }
map.delete(scores, "bob")
print(map.len(scores))             // 1
map.keys(scores)                   // 所有键(换行分隔的字符串)
map.clear(scores)                  // 清空
map.free(scores)                   // 提前释放(可省略,函数返回时自动释放)

方法糖:scores.set(k, v) / scores.get(k) / scores.has(k) / scores.delete(k) / scores.len() / scores.keys() / scores.clear() / scores.free() 等价于上方命名空间调用(见第 21 章)。

规则:

值类型约束Map[str, int] / Map[str, float] / Map[str, str] 三种组合可用; 使用 Map[int, ...](非字符串键)或其他值类型会得到编译错误。 完整 map.* 函数签名见库语法文档。

14. Set 集合

Set[T] 是语言级集合(哈希表,元素去重),LLVM 层表示为 i64 句柄。 v1 元素类型支持 int / float / str 三种;无集合字面量(避免与 {} Map 字面量歧义), 通过 set.create() 创建。

rex
var s: Set[int] = set.create()
s.add(10)             // 方法糖:等价 set.add(s, 10)
s.add(20)
s.add(10)             // 重复添加不生效(去重)
print(s.len())        // 2
print(s.has(10))      // true(bool)

var removed: bool = s.remove(10)   // true(返回是否原本存在)
s.clear()                          // 清空
s.free()                           // 提前释放(可省略,函数返回时自动释放)

// Set[str] / Set[float]
var names: Set[str] = set.create()
names.add("alice")
var fs: Set[float] = set.create()
fs.add(1.5)

规则:

等价于 set.add(s, x) 等命名空间调用

元素类型约束Set[int] / Set[float] / Set[str] 三种组合可用; 其他元素类型会得到编译错误。 完整 set.* 函数签名见库语法文档。

15. 错误处理

rex
// throw -- 抛出带有字符串消息的异常
throw "Something went wrong!"

// try-catch
try {
    throw "error message"
} catch err {
    print("Caught:", err)
}

// try-catch(无变量)
try {
    throw "error"
} catch {
    print("Caught an error")
}
rex
fn read_file() {
    defer print("cleanup")     // 函数返回/未捕获异常退出时执行
    try {
        throw "error"
    } catch {
        print("handled")
    }
}

异常语义(Bug 3 修复后):

错误传播(? 运算符 + Optional 返回类型)

深调用链的错误处理可用 ? 自动传播,避免层层手动 try-catch 包裹:

rex
// Optional 返回类型:函数可能失败
fn read_score(name: str) -> int? {
    if name == "" {
        throw "empty name"
    }
    return name.len * 10
}

fn compute_total() -> int? {
    let a: int = read_score("alice")?   // 失败自动传播:本函数立即返回默认值 0
    let b: int = read_score("bob")?
    return a + b
}

fn main() -> int {
    try {
        let total: int = compute_total()?
        print("total =", total)
    } catch err {
        print("caught:", err)           // 传播的错误被 catch 捕获
    }
    return 0
}

语义:

Backtrace(栈回溯):

Rex 在运行时自动记录函数调用链(函数名 + 当前语句行号)。当程序因未捕获异常运行时错误终止时,在错误信息前打印栈回溯(Traceback),从 main 入口逐层定位到出错位置:

text
Traceback (most recent call last):
  at main (line 9)
  at inner_throw (line 3)
Error: inner error

该类函数内未捕获异常/运行时错误的回溯退化为顶层链(调用方帧仍完整); 默认 -O 2 及以下与调试模式(DAP)全部保留。详见工具文档「生产级性能优化」。

16. 模块系统 / 导入

rex
// 选择性导入用户自定义模块
import add, subtract from "math.rex"
import multiply from "math.rex"

模块从 .rex 源文件编译,其导出的函数在导入作用域中可用。

包管理rex pkg install <目录>(源码运行等价 python main.py pkg install <目录>) 可将含 rex.toml 的包安装到 ~/.rex/packages/,安装后可用包路径导入:import "包名/模块"(详见工具文档「包管理」)。

标准库无需导入mathiofileospathstringrandomcollectionsdatetimetimethread 等标准库命名空间内置在编译器中,直接调用即可(如 math.sin(1.0)file.exists("a.txt")),不需要 import

17. FFI(外部函数接口)

rex
// 带库名的 extern
extern "kernel32" fn Sleep(ms: int) -> void

// 不带显式库名(搜索默认库)
extern fn clock() -> int

编译器生成 LLVM 函数声明,在链接时通过 C ABI 解析。

标准库函数内置:运行时库函数(math.sinio.write 等)已内置为命名空间函数,直接调用即可,无需 extern 声明。extern 仅用于调用真实 C 库函数,且必须显式指定返回类型(省略时默认 void)。

FFI 返回值空指针判断ptr/str 类型支持与 null==/!= 比较(C 指针相等语义), 用于判断 FFI 函数返回值是否为 NULL:

rex
let node: ptr = cJSON_Parse(text)
if node == null {
    print("解析失败")
}

FFI 返回字符串的内存语义extern 函数返回的 str 指向 C 库分配的内存, 不参与 Rex 帧字符串池管理(不会被自动释放)。 需要保有时用 string.dup() 拷贝为帧管理字符串,并调用 C 库自身的释放函数 (如 cJSON_Delete、或 shim 提供的 free 包装)释放原始内存,避免泄漏:

rex
let raw: str = cJSON_Print(node)   // cJSON 用 malloc 分配
let s: str = string.dup(raw)       // 拷贝为帧管理字符串
rex_json_free(raw)                 // 释放 C 库内存
return s

FFI 指针类型限制:Rex 的 ptr 是裸 i8*,无法在语言内解引用 C 结构体字段。 需要访问 C 结构体内部数据时,应在 C 侧编写薄 shim(辅助函数)暴露只读访问器, 或使用 C 库提供的 getter 函数(如 cJSON 的 cJSON_GetObjectItem/cJSON_GetStringValue), 或使用 mem 命名空间按偏移读写裸内存(见库语法文档)。

18. 字符串转义序列

基本转义

转义含义
\\反斜杠
\'单引号
\"双引号
\a响铃
\b退格
\f换页
\n换行
\r回车
\t制表
\v垂直制表
\0空字符

字符串统一使用双引号;单引号 ' 在双引号字符串内无需转义即可使用, \' 转义仍然有效(等价于 '),\" 用于输出双引号。

高级转义

类型格式示例
八进制\ooo(1-3位八进制)"\101" = A
十六进制\xNN(2位十六进制)"\x41" = A
Unicode 16位\uNNNN(4位十六进制)"\u4e2d" =
Unicode 32位\UNNNNNNNN(8位十六进制)"\U0001F600" = emoji
Unicode 名称\N{NAME}"\N{LATIN CAPITAL LETTER A}"

字符串插值(2026-08-01 新增)

字符串支持 {表达式} 插值(编译期展开为 string.concat 链), 表达式可为标识符、算术、函数调用等,支持 str/int/float/bool (后三者自动转字符串):

rex
let name: str = "Rex"
let age: int = 3
let msg: str = "Hello, {name}! You are {age} years old."  // "Hello, Rex! You are 3 years old."
print("score={98.5}, ok={true}")                          // "score=98.5, ok=true"
print("a+b={5 + 7}")                                      // "a+b=12"

字面花括号:用 {{/}} 转义(Python f-string 风格,无歧义): "{{literal}}" = {literal}。JSON 等含花括号的文本同理: json_parse("{{\"k\": 1}}")。 插值表达式内暂不支持嵌套字符串字面量(如 "{foo("x")}")。

19. 内建函数

函数/关键字签名描述
print(...)print(args...)打印到标准输出(Python风格,可变参数,自动空格分隔)
assert(cond, msg)assert(int/bool, str)断言:条件为假时打印 [Rex Runtime Error] Assertion failed: msg + 栈回溯并以退出码 1 终止(测试断言)
arr.lenarray.len -> int数组长度属性(返回元素个数)
range(start, end)range(int, int) -> 整数序列生成整数范围序列,仅用于 for i in range(start, end) 循环(也支持单参数 range(end)
new T[N]表达式在堆上分配类型 T、N 个元素的数组(生命周期绑定当前函数栈帧,返回时自动释放)
move(arr)move(array) -> T[]将堆数组所有权转移给调用方(返回堆数组的唯一方式),转移后原变量不可再用
copy(arr)copy(array) -> T[]深拷贝堆数组,返回一份独立的新数组;借用语义——源数组保持有效,可多次 copy 同一数组给多个变量。v1 支持 int[]/float[]/bool[](含多维),str[] 等元素暂不支持
true / false常量布尔字面量
null常量空指针
asexpr as Type数值类型间转换

20. 泛型(Generics)

Rex 支持泛型函数泛型结构体,采用编译期单态化(monomorphization): 泛型定义作为模板保存,每次以具体类型实例化时生成独立的 LLVM 函数/结构体, 相同 (名称, 类型参数) 组合只编译一次(缓存去重)。类型安全、零运行时开销。

20.1 泛型函数

rex
// 类型参数写在函数名后的 [ ] 中
fn identity[T](x: T) -> T {
    return x
}

fn max_of[T](a: T, b: T) -> T {
    if a > b {
        return a
    }
    return b
}

// 泛型 + 数组类型参数
fn sum[T](arr: T[], n: int) -> T {
    var total: T = arr[0]
    for i in range(1, n) {
        total = total + arr[i]
    }
    return total
}

fn main() {
    // 显式类型参数
    let a: int = identity[int](42)
    // 类型推断(从实参推导 T)
    let b: float = identity(3.14)
    print(max_of(3, 7))         // 7(T = int)
    print(max_of(2.5, 1.5))     // 2.5(T = float)
}

实例化 Box[T] 等,编译期自动替换为具体类型

20.2 泛型结构体

rex
struct Box[T] {
    value: T
}

struct Pair[A, B] {
    first: A
    second: B
}

struct Wrapper[T] {
    inner: Box[T]          // 嵌套泛型
}

fn get_value[T](b: Box[T]) -> T {
    return b.value
}

fn main() {
    var bi: Box[int] = Box[int] { value: 42 }
    print(bi.value)                    // 42
    bi.value = 100

    var p: Pair[int, float] = Pair[int, float] { first: 7, second: 1.5 }
    print(p.first, p.second)

    var w: Wrapper[int] = Wrapper[int] { inner: Box[int] { value: 9 } }
    print(w.inner.value)               // 9(链式成员访问)

    print(get_value(bi))               // 泛型函数接收泛型结构体(按值传递)
}

20.3 规则与限制

但不支持结构体元素(new Box[int][n] 会报错)

20.4 泛型约束

类型参数可声明约束,实例化时校验具体类型是否满足(显式类型参数与类型推断两条路径都校验):

rex
fn add[T: int|float](a: T, b: T) -> T {   // T 只能是 int 或 float
    return a + b
}

struct Box[T: int|float] {                 // 约束同样适用于泛型结构体
    value: T
}

add[int](3, 4)      // OK:int 满足约束
add(2.5, 3.5)       // OK:类型推断到 float
add("a", "b")       // 编译错误:str 不满足约束 'T: int|float'

fn fT: int, U: str

tests/generic_constraint_fail_test.rex(编译期拒绝)

21. 对象方法调用(Map / Set / 数组)

Map、Set 与动态数组支持方法语法(等价于既有命名空间调用):

rex
var m: Map[str, int] = {}
m.set("k", 42)          // 等价 map.set(m, "k", 42)
let v: int = m.get("k") // 等价 map.get(m, "k")
m.has("k") / m.delete("k") / m.len() / m.keys() / m.clear() / m.free()

var s: Set[int] = set.create()
s.add(10)               // 等价 set.add(s, 10)
s.remove(10) / s.has(10) / s.len() / s.clear() / s.free()

var arr: int[] = new int[2]
arr.push(30)            // 追加元素(自动扩容),等价 arr[arr.len] = 30

22. 多返回值

rex
fn minmax(a: int, b: int) -> (int, int) {
    if a < b { return a, b }
    return b, a
}

let (mn, mx) = minmax(3, 7)   // 解构:mn=3, mx=7
let (i, f, b) = mixed()       // 支持 (int, float, bool)

(多返回 str 与字符串帧池移交不兼容,编译报错);多返回函数不支持 ? 错误传播。

23. 整数除零检查

[Rex Runtime Error] Integer division by zero(含栈回溯,退出码 1), 防止 SIGFPE 崩溃;浮点除零为 IEEE 合法操作(返回 inf),不检查。

24. 完整代码示例

斐波那契数列

rex
fn fib(n: int) -> int {
    if n <= 1 {
        return n
    }
    return fib(n - 1) + fib(n - 2)
}

fn main() {
    let start = time.now()

    for i in range(0, 35) {
        print("fib(", i, ") =", fib(i))
    }

    print("time:", time.elapsed(start), "ms")
}

运算符测试

rex
fn main() {
    var a: int = 10
    var b: int = 3

    print("a + b =", a + b)
    print("a - b =", a - b)
    print("a * b =", a * b)
    print("a / b =", a / b)
    print("a % b =", a % b)
    print("a ** 2 =", a ** 2)

    a += 1
    print("a += 1 =", a)
    a -= 1
    print("a -= 1 =", a)

    a += 5
    print("a += 5 =", a)
}

结构体使用

rex
struct Point {
    x: float
    y: float
}

fn distance(p: Point) -> float {
    return math.sqrt(p.x ** 2 + p.y ** 2)
}

fn main() {
    var p: Point = Point { x: 3.0, y: 4.0 }
    print("distance =", distance(p))
}

错误处理

rex
fn divide(a: int, b: int) -> int {
    if b == 0 {
        throw "Division by zero"
    }
    return a / b
}

fn main() {
    defer print("Done")        // 替代 finally:函数退出时始终执行
    try {
        print(divide(10, 0))
    } catch err {
        print("Error:", err)
    }
}

条件分支链(if / else if / else)

rex
fn classify(n: int) -> str {
    if n == 0 {
        return "zero"
    } else if n == 1 || n == 2 || n == 3 {
        return "small"
    } else {
        return "large"
    }
}

defer 延迟执行

rex
fn main() {
    defer print("third")
    defer print("second")
    defer print("first")
    print("start")
}
// 输出:start, first, second, third

// defer 常用于确保函数返回前的清理(堆内存由函数帧自动管理,无需手动释放)
fn example() {
    var arr: int[] = new int[100]
    defer print("cleanup before return")

    if some_condition {
        return  // defer 会自动执行
    }

    // 其他代码...
}