xxc

可编译可解释的系统级语言 - 确定性 ARC 无 GC / 全自举工具链 / 三后端逐字节一致

📂 开发工具#编程语言#编译器#自举#跨平台#无GC#并发#开发工具📅 2026-10-9

语言指南

📅 2026-10-9

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 的产物字节级对拍。