A C-level systems language that compiles to C. Syntax inspired by Rust, semantics mapped 1:1 to C. Built-in Boehm GC with opt-out (--no-gc).
Vox 是一个对 C 零阻抗的系统语言。不引入虚拟机、不隐藏内存模型。每个 Vox 表达式直接映射到 C 表达式。目标:用更现代的语法写 C,同时保留 C 的全部能力(指针算术、C 宏、标准库)。
- 1:1 C 映射 —
i32=int32_t,*T=T*,[T; N]=T[N]。没有隐式 box、没有 vtable、没有胖指针。 - C 是一等公民 — 12 个预处理关键字(
#define/#ifndef等)是原生语法,printf/scanf整段透传支持 PRId32 等宏。 - GC 可选 — 默认
GC_malloc/GC_free(Boehm),--no-gc切到malloc/free。 - 自举友好 — AST 结构简单,编译管线线性,生成纯 C 不依赖额外 runtime。
# 构建编译器
git clone https://github.com/summerTBD/VoxLanguage
cd VoxLanguage
cargo build --release
# 编译 Vox 程序
./target/release/vox-language.exe hello.vox # → hello.c + hello.exe
./target/release/vox-language.exe hello.vox --out build # → build/hello.c + build/hello.exe
./target/release/vox-language.exe hello.vox --no-gc # 不用 GC,malloc/free
./target/release/vox-language.exe hello.vox --check # 只类型检查,不编译输入 .vox 源码
│
├─ main.rs: read_to_string
├─ lib.rs: expand_mods() — 展开 mod "file.vox"(递归 + visited 去重)
├─ lib.rs: replace_defines() — 收集 #define 名称,文本替换(跳过字符串值 + #define 行自身)
├─ main.rs: include_str!("../prelude.vox") — 拼接 prelude 到源码顶部
▼
Lexer (vox_lexer.rs)
└─ 逐字符扫描 → Token 流(TokenKind + 行列号)
├─ C 预处理指令 → read_cpp_directive() → CppLine
├─ printf/scanf → read_balanced_parens() → Printf(String)/Scanf(String)
└─ 标识符匹配 20+ 关键字
▼
Parser (vox_parser.rs)
└─ 递归下降 → Program { items: Vec<TopLevelItem> }
├─ TopLevelItem: CppLine | Struct | Enum | Function | Const | Static
└─ 按源文件顺序保存(C 宏和声明顺序不丢失)
▼
TypeChecker (vox_typeck.rs)
└─ Program 遍历 → 类型推导 + 错误报告
├─ register_defines() — #define 名称标记为已知标识符
├─ 跨宽度算术 → wider() 自动提升
├─ 字面量 → 整数族/浮点族隐式转换
└─ Printf/Scanf → 返回 Int(Signed, 32),不检查参数
▼
Codegen (vox_codegen.rs)
└─ Program.items 顺序遍历 → C 源码字符串
├─ #include <stdint.h> <inttypes.h> <stdio.h> <stdlib.h> <string.h>
├─ extern fn → 跳过 C 声明(标准头文件提供)
├─ CppLine → 原样输出
└─ 表达式编译 → compile_expr() / compile_stmt()
▼
gcc (MinGW-w64)
└─ gcc hello.c -o hello.exe [-I vendor/include vendor/libgc.a]
TokenKind — 20+ 关键字 + 标点 + Printf(String) + Scanf(String)
TopLevelItem — CppLine | Struct | Enum | Function | Const | Static
Type — Int(Signedness, u8) | Float(u8) | Bool | Char | Str | Void | Ptr | Array | Adt
Expression — IntLiteral | FloatLiteral | StringLiteral | Identifier | Binary | Call |
StructLiteral | FieldAccess | Not | New | AddrOf | Deref | Cast | Sizeof |
ArrayLiteral | Index | Printf | Scanf
Statement — Let | Return | Expr | If | While | Assign | Store | StoreField | StoreIndex |
Match | For | Break | Continue | Define | CppDirective
12 个宏关键字在 lexer 层识别,parser 层作为 CppLine(String) 保存,codegen 原样透传:
#define #undef #include
#ifdef #ifndef #if
#else #elif #endif
#pragma #error #line
#define 名称还会在 replace_defines() 中做文本替换(两遍扫描),这样 Vox 代码中可直接使用宏名作为数组大小、常量值等。
Lexer 在遇到标识符 printf 或 scanf 后紧跟 ( 时,调用 read_balanced_parens() 整段捕获直到匹配的 ),生成 Printf(String) / Scanf(String) token。Parser 将其作为 Expression::Printf / Expression::Scanf,Codegen 直接输出 printf(...) / scanf(...)。
这支持 C 字符串拼接——PRId32 来自 <inttypes.h>,无需在 Vox 侧定义:
printf("i32 = %" PRId32 "\n", a); // → C: printf("i32 = %" PRId32 "\n", a);
编译器自动 #include 五个标准头文件:
| 头文件 | 提供 |
|---|---|
<stdint.h> |
int8_t uint64_t 等整型 typedef |
<inttypes.h> |
PRId8 PRIu64 等 printf 格式宏 |
<stdio.h> |
FILE* printf scanf fopen fread ... |
<stdlib.h> |
malloc free atoi exit ... |
<string.h> |
memcpy memset strlen strcmp ... |
prelude.vox 中所有 extern fn 声明只用于 Vox 类型检查,codegen 不会为其生成 C 前向声明(因为标准头文件已提供),避免签名冲突(如 fopen 在 Vox 中声明为 *void,而 stdio.h 中为 FILE*)。
用户自定义的 extern fn 同样不会生成 C 声明——需用户自行 #include 对应头文件。
stdio.h: puts getchar fopen fclose fgetc fputc fgets fputs fread fwrite fseek ftell rewind fflush feof ferror perror remove rename
stdlib.h: malloc calloc realloc free atoi atol atof strtol strtoul strtod abs rand srand system exit
string.h: memcpy memmove memset memcmp memchr strlen strcpy strncpy strcat strncat strcmp strncmp strcoll strchr strrchr strspn strcspn strpbrk strstr strtok strerror
| Vox 类型 | C 类型 | 说明 |
|---|---|---|
i8 i16 i32 i64 |
int8_t ... int64_t |
有符号整数 |
u8 u16 u32 u64 |
uint8_t ... uint64_t |
无符号整数 |
f32 f64 |
float double |
浮点 |
char |
char |
字符 |
bool |
int |
布尔(C 兼容) |
str |
const char* |
字符串(不可变 C 字符串) |
void |
void |
空类型 |
*T |
T* |
指针 |
[T; N] |
T[N] |
定长数组 |
- 字面量 → 同族任意宽度:
let a: i8 = 10;✅,let b: f32 = 3.14;✅ - 整数跨宽度运算:
i32 + u64 → u64(wider()自动提升) - 整数 + 浮点:暂不自动转换,需显式
as - 变量间:必须严格类型匹配
let x: i32 = 42; // 不可变
let mut y: i32 = 0; // 可变
y = 100;
fn add(a: i32, b: i32): i32 {
return a + b;
}
extern fn my_c_func(x: i32): *void; // C 函数,不生成声明
struct Point {
x: i32,
y: i32,
}
let p: Point = Point { x: 10, y: 20 }; // 栈分配
let hp: *Point = new Point { x: 1, y: 2 }; // 堆分配(GC_malloc 或 malloc)
p.x; hp.x; // 字段访问(栈用点,堆也用点,codegen 自动加 ->)
enum Color { Red, Green = 5, Blue } // Red=0, Green=5, Blue=6
let c: Color = Color.Green;
match c {
Red -> { puts("red"); }
Green -> { puts("green"); }
Blue -> { puts("blue"); }
}
if x > 0 { ... } else { ... }
while x < 10 { x = x + 1; }
for (let mut i: i32 = 0; i < 10; i = i + 1) {
if i == 3 { continue; }
if i == 7 { break; }
}
let p: *i32 = &x; // 取地址 → C: &x
let v: i32 = *p; // 解引用 → C: *p
*p = 100; // 写入 → C: *p = 100;
let arr: [i32; 3] = [1, 2, 3];
arr[0]; arr[1] = 42; // 索引
p + 1; p == 0; // 指针算术,null 检查
42 as i64 // 类型转换 → C: (int64_t)42
sizeof(Point) // → C: sizeof(struct Point)
const BUF: i32 = 4096; // → C: static const int32_t BUF = 4096;
static mut COUNTER: i32 = 0; // → C: static int32_t COUNTER = 0;
mod "utils.vox"; // 相对路径展开,支持嵌套,自动去重
mod "math/vec.vox";
| 编译选项 | new 展开为 |
释放 |
|---|---|---|
| 默认(GC) | GC_malloc(sizeof(T)) |
GC_free / 自动回收 |
--no-gc |
malloc(sizeof(T)) |
必须手动 free(ptr) |
GC 编译时 gcc 额外链接 vendor/include(头文件)和 vendor/libgc.a(Boehm GC 静态库)。
VoxLanguage/
├── Cargo.toml # Rust 项目配置(default-run = vox-language)
├── prelude.vox # C 标准函数声明(extern fn,仅供类型检查)
├── example.vox # 示例程序
├── README.md # 本文档
│
├── src/
│ ├── main.rs # 入口:参数解析 + expand_mods + replace_defines + prelude 注入
│ │ # Lexer → Parser → TypeChecker → Codegen → gcc → run
│ ├── lib.rs # expand_mods() replace_defines() replace_ident() is_ident_char()
│ ├── vox_token.rs # TokenKind enum(关键字 + 标点 + Printf/Scanf)
│ ├── vox_lexer.rs # Lexer: next_token() 主循环 + read_cpp_directive() + read_balanced_parens()
│ ├── vox_ast.rs # AST: TopLevelItem Program Type Expression Statement StructDef EnumDef ...
│ ├── vox_parser.rs # Parser: parse_program() 递归下降,format_cpp_directive()
│ ├── vox_typeck.rs # TypeChecker: register_defines() check() wider() 字面量隐式转换
│ ├── vox_codegen.rs # Codegen: compile() emit_function_decl() compile_expr() compile_stmt()
│ │ # type_to_c() ret_type_to_c() alloc_fn() free_fn()
│ └── bin/
│ └── voxlsp.rs # LSP: JSON-RPC over stdin/stdout,调 voxc --check 子进程
│
├── bin/ # 发布目录(VS Code 扩展 + exe)
│ ├── package.json # VS Code 扩展清单
│ ├── extension.js # 扩展入口(调 ./voxlsp.exe)
│ ├── syntaxes/
│ │ └── vox.tmLanguage.json # TextMate 语法高亮
│ └── icon.png
│
└── vendor/ # Boehm GC(voxc.exe 同级目录)
├── include/ # gc.h ...
└── libgc.a
#开头 →read_cpp_directive()读到换行,匹配 12 个宏指令名- 标识符
printf/scanf+(→read_balanced_parens()整段透传 - 数字字面量 → 支持
0x前缀十六进制,.判浮点 - 字符串 → 双引号,支持
\"\\\n\t转义 - 注释 → 仅
//行注释
parse_program()顶层循环:检查#→CppLine,检查struct/enum/fn/const/static/extern→ 对应 AST 节点#define→Statement::Define(名称+值),其他#→Statement::CppDirective(整行).字段访问与->:Vox 统一用.,codegen 自动判断指针类型加->new Struct { ... }→Expression::New,codegen 生成alloc_fn()(sizeof(struct T))return;无值 →Statement::Return(None)
#define名称 →register_defines()注册到self.defines,类型检测时通过Expression::Printf/Expression::Scanf→ 返回Int(Signed, 32)- 跨宽度算术 →
Type::wider():取最大位宽,有符号+无符号=无符号 - 字面量隐式转换 → 仅整数族和浮点族各自内部允许
- extern fn → 不生成 C 前向声明(标准头文件提供)
- main 函数 → 返回类型强制
int(C 标准要求) - struct 字段访问 → 检测
Type::Ptr,自动->,否则. new T { ... }→(struct T*)alloc_fn()(sizeof(struct T))+ 逐字段赋值- 头文件顺序 →
stdint.hinttypes.hstdio.hstdlib.hstring.h[+gc.h]
expand_mods(source, base_dir)— mod 展开 + visited 去重replace_defines(source)— 两遍扫描:收集名称 → 替换(跳过#define行和字符串值)include_str!("../prelude.vox")— 拼接 preludeLexer::new()+Parser::new()→ProgramTypeChecker::new()+register_defines()+check()- 如果
--check→exit(0) Codegen::new(!no_gc)+compile()→ C 源码std::fs::write(c_path, c_code)gcc c_path -o exe_path [-I vendor/include vendor/libgc.a]- 运行 exe 并打印输出
- 通过标准输入/输出 JSON-RPC 通信
textDocument/didChange/didOpen/didSave→ 写临时文件到源文件目录 →voxc --check tmp.vox子进程- 解析 stderr 中的错误信息,转换行列号(Vox 报的是 prelude 注入后的行号,需减去 prelude 行数)
- URL 解码(
%3A→:)处理 Windows 盘符路径
cargo build # debug 构建 → target/debug/
cargo build --release # release 构建 → target/release/# bin/ 目录结构(给用户)
bin/
├── voxc.exe # 编译器(或 vox-language.exe)
├── voxlsp.exe # LSP
├── vendor/include/ # gc.h
├── vendor/libgc.a # Boehm GC 静态库
├── package.json # VS Code 扩展清单
├── extension.js # 扩展入口
├── syntaxes/vox.tmLanguage.json
└── icon.png将 bin/ 目录创建 Junction 到 VS Code 扩展目录:
New-Item -ItemType Junction -Path "$env:USERPROFILE\.vscode\extensions\vox.vox-language-0.1.0" -Target "D:\MyProjects\VoxLanguage\bin"扩展仅 8 行 JS,通过 __dirname 定位 voxlsp.exe,零配置。
- 无泛型 —
Vec<T>等需要单态化支持(AST 已预留Type::Adt { args }字段) - 无 variadic — 仅
printf/scanf特殊支持,fprintf/sprintf暂不可用 - 无闭包/lambda
- 无 trait/interface
- match 仅支持枚举 — 不支持整数/字符串匹配
- for 循环仅 C 风格 — 无
for x in iter - 结构体全堆分配 —
new Struct { }始终调alloc_fn(),无栈new - bool = int — 未使用 C99
_Bool/stdbool.h - fopen 返回
*void— prelude 中声明为*void而非FILE*,实际使用需强转
- Rust 1.70+ — 编译器构建
- gcc (MinGW-w64 on Windows) — C 编译
- Boehm GC —
vendor/中的预编译静态库(可选,--no-gc可跳过)
MIT