语言指南
xxc 语言指南
> 本指南记录 xxc 工具链(解释器、字节码 VM、Windows PE / Linux x86-64
> ELF / aarch64 ELF 三目标原生编译器)已实现且经门矩阵验证的行为。需要
> 构建特性的功能在文中就地标注。
>
> 相关页:快速开始 ·
> 标准库与运算符参考 ·
> 并发编程指南 ·
> 实现与自举 ·
> English: Language Guide
概览
xxc 是一门可编译也可解释的跨平台全栈语言,设计目标:
- Go 式人体工学:语法简单、电池齐全、channel + 轻量任务;
- 无垃圾回收器:引用类型走确定性引用计数(ARC),其余皆为值类型;
- 多核:
spawn把任务放到 N 个 OS 线程上并行——解释器与编译产物同享; - 可解释:树遍历解释器零构建直接跑;字节码 VM 更快;原生编译器
经 xxc 自研汇编器与链接器直出机器码。
| 命令 | 后端 | 说明 |
|---|---|---|
| xxc run file.xxc | 树遍历解释器 | 全语言,语义基准 |
| xxc vm file.xxc | 扁平栈字节码 VM | 顺序语言全量,约 2.8 倍速 |
| xxc build file.xxc | 原生机器码(x86-64 PE/ELF、aarch64 ELF) | 自带汇编器与链接器 |
词法结构
- 源码为 UTF-8;
//行注释与/* ... */块注释。 - 换行结束语句(Go 风格);二元运算符必须与左操作数同行(
a +\nb
续行,a\n+ b 断句)。
- 标识符:
[A-Za-z_][A-Za-z0-9_]*。 - 关键字:`fn struct class interface enum if else while for in return
defer break continue true false nil or import spawn select case default
match`。
字面量
42 int(十进制 i64)
3.14 float(f64)
0xFF 十六进制 int
0b1010 二进制 int
0o777 八进制 int
"hello" 字符串
"n = {n}" 插值字符串(见下)
true false bool
nil 空值
字符串插值
字符串字面量内 {expr} 求值并插入显示形式(str(expr))。转义:
\n \t \r \" \\ \0 \{ \}——字面花括号用 \{ \}:
print("fib({n}) = {fib(n)}") // fib(10) = 55
print("brace: \{ not interpolation\}")
类型
| 类型 | 说明 |
|---|---|
| int | 64 位有符号整数 |
| float | 64 位浮点 |
| bool | true / false |
| string | UTF-8 字符串(不可变,共享) |
| nil | 空值 |
| array | Vec<Value>(引用共享,可变) |
| struct T | 值类型——绑定即拷贝 |
| class T | 引用类型——ARC 管理 |
| map | 可作字典用的动态对象 |
| 元组 | (v1, v2, ...)——不可变,值拷贝 |
| 枚举 | 带标签联合 |
| chan | 通道 |
| 函数 | 一等公民闭包 |
类型系统是渐进式:有标注处静态检查,未标注处动态;检查器只报
确定错误。
内存模型
两类内存,由声明形式选择:
struct Point { x: int; y: int } // 值类型
class Box { value: int } // 引用类型(ARC)
- 绑定
struct深拷贝(嵌套值类型一并拷贝),改副本不动原件。 - 绑定
class共享实例,走确定性 ARC:引用计数以原子操作维护
(x86 lock 指令 / a64 独占加载-存储对),计数归零立即释放——
无 tracing GC、无停顿,回收时机就是最后一个引用离开的时刻。归零
释放路径由开门差分(XXC_ARC_FREE=1)验证,分配失控护栏
XXC_ALLOC_LIMIT(严格十进制字节,默认 2GiB)兜底。
- 数组与字符串共享;字符串不可变故共享不可见。数组别名可观察
(b := a; push(b, 1) 会影响 a)。
p := Point{x: 1, y: 2}
q := p // 独立副本
q.x = 99 // p.x 仍是 1
c := Box{value: 10}
d := c // 同一实例,引用计数 +1
d.value = 77 // c.value 变为 77
refcount(v) 返回近似强计数(演示用)。
用 weak 打破循环
weak(obj) 返回不保活的弱引用;字段访问与方法调用透明升级,触碰
已消亡的弱引用是运行时错误。
class Node { value: int; next: int; prev: int }
head.next = tail
tail.prev = weak(head) // 不成环的反向引用
打印行原子性
print 语句的各部分(字符串/整数/浮点)在语句级所有者令牌锁下追加
进行缓冲区,遇换行一次性写出——任何后端上并发任务都不可能在行
中间撕开输出。该性质由并发梯子的 stress 多重集门逐字验证。
变量与赋值
x := 10 // 推导类型
y: int = 20 // 标注类型
y = 30 // 赋值
a, b := pair() // 解构元组返回值
c, d := (10, 20) // 或元组字面量
声明形式:name := expr、name: Type = expr。赋值目标:变量、字段
(obj.f = v)、索引(arr[i] = v、m["k"] = v)。
函数与闭包
fn add(a: int, b: int) -> int {
return a + b
}
fn fallible(path: string) -> string ! { // 可错函数
...
}
- 参数可标注(
a: int)或动态(a)。 -> T声明返回类型;!标记可错。- 函数是一等公民:可赋值、传参、返回。
函数字面量与闭包
double := fn(x) { return x * 2 }
apply := fn(f, x) { return f(x) }
makeAdder := fn(n) {
return fn(x) { return x + n } // 捕获 n
}
add10 := makeAdder(10)
print(add10(5)) // 15
闭包捕获外围作用域;被捕获变量在闭包生命周期内保持存活。
控制流
if x < 0 { ... } else if x == 0 { ... } else { ... }
while cond { ... }
for x in arr { ... } // 数组(值)
for c in "héllo" { ... } // 字符串(按码点)
for i in range(10) { ... } // 0..9
for i in range(2, 5) { ... }// 2..4
break
continue
错误处理
错误是普通值。三种构造:
fn inv(x: int) -> int ! {
if x == 0 { return error("divide by zero") }
return 100 / x
}
a := inv(5)? // 传播:错误从这里返回
b := inv(0) or { -1 } // 恢复:块尾表达式即值
print(inv(0)) // 裸错误值语句自动传播
f(...)?——调用产出错误时从外围函数返回(or块同样捕获传播);expr or { ... }——expr为错误时执行块;error(msg)构造错误值;每个错误携带源位置供诊断。
defer
fn work() {
f := open(path)?
defer f.close() // work() 退出时执行——正常或出错
...
}
defer 语句按注册逆序(LIFO)执行。
方法与接口
struct Rect { w: int; h: int }
fn (self: Rect) area() -> int { // 值接收者(拷贝)
return self.w * self.h
}
fn (self: Rect) scale(k: int) -> Rect { // 返回新值
return Rect{w: self.w * k, h: self.h * k}
}
class Stack { items: array }
fn (self: Stack) push(v) { push(self.items, v) } // 引用接收者
struct接收者收到副本;class接收者共享实例。- 调用语法:
r.area()、s.push(3)。
结构化接口——只要实现了方法集即满足接口,无需声明:
interface Shape { fn area(self) -> int }
fn describe(s: Shape) { print(s.area()) } // Rect、Box 皆可传入
泛型
fn identity<T>(x: T) -> T { return x }
fn first<T>(arr: array<T>) -> T { return arr[0] }
fn max<T>(a: T, b: T) -> T { if a > b { return a } return b }
struct Pair<A, B> { first: A; second: B }
fn (self: Stack<T>) push(v: T) { ... } // 泛型方法
类型参数运行时擦除;类型检查器在调用点推导(max(3, 7) 推 T=int;
max("a", "b") 推 T=string)。
枚举与模式匹配
enum Shape {
Circle(float),
Rect(float, float),
Point
}
fn area(s) -> float {
return match s {
Circle(r) => { 3.14159 * r * r }
Rect(w, h) => { w * h }
Point => { 0.0 }
}
}
- 变体可携带带类型的数据字段;单元变体无需括号。
- 构造用
Circle(5.0)或裸Point。 match按变体名分派并绑定数据(Circle(r)绑定r),臂体尾部
表达式即 match 的值。
_臂匹配一切;无_且不匹配是运行时错误。
元组
fn divmod(a: int, b: int) { // 隐式返回 (int, int)
return a / b, a % b
}
q, r := divmod(17, 5) // 解构:q=3, r=2
point := (10, 20)
print(point.0, point.1) // 用 .0 .1 索引
lo, hi := minmax(arr) // 任何元组返回调用
return x, y返回元组(x, y)。a, b := expr解构;元素数必须匹配。- 元组不可变、值拷贝。
Map
map() 创建可作字典的动态对象;键为字符串。
m := map()
m["name"] = "xxc"
m["version"] = 1.0
print(m["name"]) // xxc
print(m["missing"]) // nil
print(m.contains("name")) // true
print(m.keys()) // ["name", "version"]
print(m.values())
print(m.length) // 2
// 词频
freq := map()
for w in text.split(" ") {
if freq.contains(w) { freq[w] = freq[w] + 1 }
else { freq[w] = 1 }
}
任何 class 值都支持索引访问;map() 即空动态对象。键迭代顺序未定义。
字符串方法
s.length 元素数
s.contains(sub) bool
s.starts_with(sub) bool
s.ends_with(sub) bool
s.index_of(sub) 首个索引或 -1
s.slice(start, end) 子串 [start, end)
s.replace(old, new) 全部替换
s.trim() 去首尾空白
s.upper() s.lower()
s.split(sep) -> array
数组方法
arr.length
arr.push(v) 原地追加
arr.pop() 移除并返回末元素
arr.contains(v) bool
arr.index_of(v) 首个索引或 -1
内建:len(x)、push(arr, v)、range(n) / range(a, b)。
并发与异步 I/O
spawn、channel(有缓冲/汇合)、select、worker 池运行时、死锁检测,
以及异步 I/O 模块(std/fs、net/tcp、net/http),统一收录于
并发编程指南——解释器与
编译产物语义一致。
标准库
全部模块(std/time、std/json、std/random、std/os、std/regex、
std/crypto、std/base64、std/uri、std/fs、net/tcp、net/http、
db/sqlite、gui、js、webview)与完整运算符优先级表收录于
运算符
优先级(高到低):
| 级 | 运算符 | 类别 |
|---|---|---|
| 最高 | fn(...) 调用、. 字段、[...] 索引、后缀 ? | 后缀 |
| — | 一元 - ! ~ | 一元 |
| 7 | * / % | 乘除 |
| 6 | + - | 加减 |
| 5 | << >> & \| ^ | 位运算 |
| 4 | < <= > >= | 比较 |
| 3 | == != | 相等 |
| 2 | && | 与 |
| 1 | \|\| | 或 |
| 0 | =(赋值)、ch <- v(发送) | 赋值 |
&&/|| 短路。+ 加数字、拼接字符串(string + 非string 是类型
错误,用 str() 转换)。位运算作用于 int(& | ^ 亦可用于
bool)。整数除法截断;x / 0、x % 0 是运行时错误。
原生编译
xxc build file.xxc 把程序直接编译为原生机器码——不经过 C 编译
器、不调用 binutils:xxc 自带进程内汇编器(assembler.rs)与链接器
(Windows PE 用 pelinker.rs,静态 Linux ELF 用 elflinker.rs)。
| 主机 | 产物 | 说明 |
|---|---|---|
| Windows x86-64 | PE 可执行文件 | VEH 诊断、保护页 |
| Linux x86-64 | 静态 ELF64 | 纯 syscall,无 libc,无 PT_INTERP |
| Linux aarch64 | ELF64 aarch64 | 生成代码经 a64 后端(a64.rs)落地 |
fn fib(n: int) -> int {
if n < 2 { return n }
return fib(n - 2) + fib(n - 1)
}
fn main() {
print("fib(40) = {fib(40)}")
}
$ xxc build examples/bench.xxc && ./bench
fib(40) = 102334155
顺序语言全量可编译:struct(值)/ class(ARC)、map、数组、字符串、
闭包、错误(?/or/!)、defer、match、元组、泛型;`--features
concurrency` 下 spawn/channel/select 与解释器同一套 worker 池语义。
运行时(正本 rt/runtime.s)提供确定性 ARC 原子计数、归零释放门、
分配失控护栏与行原子打印。
交叉编译:Windows 上 XXC_TARGET=linux xxc build foo.xxc 产出 Linux
ELF;aarch64 Linux 主机默认即出原生 AArch64。
自举
整个工具链用 xxc 编写:`selfhost/lexer.xxc → parser.xxc → typeck →
gen.xxc → xas.xxc → xld,合并为 selfhost/xxcc_full.xxc`。链条自编译,
每次刷新验证不动点——链条对自身源码的输出与种子编译器的输出逐字节
一致(selfhost/build_selfhost.sh),ARC 关门/开门双态成立。
$ bash selfhost/build_selfhost.sh
...
CHAIN REFRESH OK / FIXPOINT OK
Rust 种子(src/)扮演 Go 语境的"上一个编译器":语言扩展先进种子,
不动点证明自举编译器在其余一切上语义等价。自举侧 xas/xld 与 Rust
侧 assembler/pelinker 的产物字节级对拍。