Cavvy 文档
文档还在测试阶段且未编写完,遇到任何问题可以提出issue,也可以提PR帮助我们丰富文档
欢迎阅读 Cavvy 编程语言文档。
Cavvy(曾用名 EOL — Ethernos Object Language)是一门静态类型、面向对象的编程语言编译器。它使用 Rust 编写,将 .cay 源码编译为 LLVM IR,再通过捆绑的 LLVM/MinGW 工具链生成原生可执行文件。
- 当前版本:6.1.0(来自
.verinfo) - 源文件扩展名:
.cay - 旧名称:EOL(
.eol扩展名和 CI 中的eol-*为历史遗留) - 许可证:GPL3
快速上手
public int main() {
println("Hello, Cavvy!");
return 0;
}
cayc hello.cay
.\hello.exe
完整的安装和构建指南见快速开始。
编译流水线
.cay 源码
→ 预处理器(#include, #define, #ifdef)
→ 词法分析器(logos)
→ 解析器(递归下降)
→ 语义分析(类型检查 + 符号解析)
→ IR 生成(自定义 SSA IR)
→ LLVM IR 文本生成
→ clang → 原生机器码 .exe
核心特性
| 特性 | 描述 |
|---|---|
| 面向对象 | 类、继承、接口、运行时多态(vtable) |
| 静态类型 | 编译时类型安全,强类型检查 |
| C 预处理器 | #include、#define、#ifdef 等 |
| FFI | 直接调用 C ABI 函数 |
| CayBC 字节码 | JVM 风格字节码,支持 JIT/AOT |
| Cavly 包管理器 | 依赖管理、构建、测试 |
| RCPL | 交互式编程环境 |
| LSP | 语言服务器协议支持 |
| 16 个 CLI 入口 | 编译、检查、运行、分析、设置和包管理等 |
| Result 错误处理 | Result<T, E>、?、panic 和 abort |
文档导航
文档约定
本文档中的代码块带有特殊标记:
cay— 语法检查(cay-check)cay run— 编译并运行cay ignore— 跳过测试- 所有示例都经过自动化测试,确保准确
Cavvy 5.2.0~6.1.0 版本演进
本文档把 5.2.0 到 6.1.0 的功能变化按用户可见的主题汇总。每个版本的完整变更仍以对应目录中的发布说明、破坏性变更、迁移指南和已知问题为准。
版本路线
| 版本 | 核心主题 | 主要用户影响 |
|---|---|---|
| 5.2.0 | 诊断、嵌入式 LLVM、分析工具 | 统一 CayError/CayResult,新增 cay-ast、cay-pl、cay-sir,扩展 --use-embedded-llc |
| 5.3.0 | 调用语法、泛型推断、资源管理 | 支持 Type::method()、省略 new、智能指针和自动 RAII、-g |
| 5.4.0 | 内存映射文件 | 新增跨平台 Mmap/MmapSlice 零拷贝访问 |
| 6.1.0 | 显式错误传播 | 新增 Result<T,E>、Error 层级、?、panic 和 abort |
5.2.0:诊断和工具链
CayError直接实现 miette 诊断接口,错误码从错误消息中独立出来。- 测试辅助函数直接返回
Vec<CayError>;新代码应按error_code判断错误,不要匹配完整文本。 --use-embedded-llc可用于更多编译/运行入口;Linux 下可自动构建缺失的libcayrt-linux.a。cay-ast输出 AST,cay-pl输出预处理结果,cay-sir输出语义 IR;这些工具适合诊断宏、类型解析和符号绑定问题。- 泛型替换支持嵌套类型、泛型返回值和接口 vtable 后缀。
5.3.0:调用、资源和调试
// 静态调用可以使用类型限定名
int value = Integer::parseInt("42");
// 简单构造场景可以省略 new
Box<int> box = Box<int>(value);
std::sys提供进程、环境变量和命令行参数访问。ArrayList<T>、vector<T>和迭代器提供容器基础设施。UniquePtr<T>、ScopedPtr<T>、Rc<T>、WeakPtr<T>提供独占、作用域、共享和弱引用所有权模型。- 作用域退出时会触发受支持对象的析构;需要转移所有权时使用
UniquePtr.move()或release(),不要重复delete。 -g生成调试信息;内联 IR 支持atomicrmw和cmpxchg。
5.4.0:内存映射文件
Mmap.mapReadOnly(path) 和 Mmap.mapReadWrite(path, size) 返回 MmapResult<T>。使用映射前必须检查 isOk(),完成后调用 sync()(写映射)和 unmap()。MmapSlice 只是底层映射的视图,解除映射后不可继续使用。
6.1.0:Result 错误传播
Result<int, String> result = Result<int, String>.ok(42);
if (result.isOk()) {
int value = result.unwrap();
}
- 使用
Result<T,E>.ok(value)和Result<T,E>.err(error)构造结果。 isOk/isErr用于分支,unwrap/unwrapErr用于已确认分支,unwrapOr用于默认值,expect用于必须成功的不变量。?只能出现在返回兼容Result的函数中,并会把错误分支直接传播给调用方。std::Error、std::IOError和std::ParseError为错误分类、系统错误码、位置和描述提供统一接口。panic用于带消息终止,abort用于直接终止;它们不是可恢复错误处理机制。
升级顺序
- 从 5.2.0 开始先按错误码更新诊断和测试断言。
- 从 5.3.0 开始检查资源所有权,选择智能指针或显式所有权转移。
- 从 5.4.0 开始检查 mmap 的失败、越界和生命周期分支。
- 升级到 6.1.0 后,把可恢复错误统一为
Result<T,E>,再逐步引入?。
详细文档:
Cavvy 6.1.0 发布说明
发布日期:2026-07-14
6.1.0 建立非异常式错误处理体系,以泛型 Result<T, E>、错误类型层级和 ? 运算符实现显式、可组合的错误传播。版本号及各工具构建号由根目录 .verinfo 统一管理。
Cavvy 6.1.0 新特性详解
Result 错误处理
- 新增泛型
std::Result<T, E>,提供ok、err、isOk、isErr、unwrap、unwrapOr、unwrapErr和expect。 - 新增
std::Error类型层级及std::IOError、std::ParseError,可携带错误分类、原始系统错误码、位置和描述。 - 新增
?运算符,在函数中自动传播错误并保持返回类型检查。 - 新增
panic与abort内建函数。
Result 的核心操作是 ok、err、isOk、isErr、unwrap、unwrapOr、unwrapErr 和 expect。错误类型层级还提供 std::ParseError,用于携带解析位置和源代码片段。
Result<int, String> result = Result<int, String>.ok(42);
if (result.isOk()) {
println(String.valueOf(result.unwrap()));
}
Result 采用显式值/错误分支,不依赖异常、堆分配、RTTI 或栈回退,适合与 5.3/5.4 的资源管理和 I/O API 组合使用。
Cavvy 6.1.0 Bug 修复清单
- 修复 Result 错误传播的 LLVM IR 生成。
- 修复 Result 返回类型推断和调用表达式处理。
- 修复
?运算符在词法、语法、语义和代码生成阶段的衔接问题。 - 补充 Result、
?、panic和abort的示例及集成测试。
Cavvy 6.1.0 破坏性变更
Result API
错误处理 API 以 Result<T, E> 为规范形式。旧版专用结果容器应逐步迁移到泛型 Result,并显式声明错误类型。
? 运算符返回类型
使用 ? 的函数必须返回能够承载被传播错误的 Result 类型;不能在普通值返回函数中直接使用。
终止函数
unwrap、expect 和 panic 在错误分支会终止当前执行路径。需要可恢复流程时应使用 map、andThen 或显式分支。
Cavvy 6.1.0 迁移指南
本文档面向从 5.4.x 或更早版本升级的项目。6.1.0 的核心迁移是将可恢复错误统一为 Result<T, E>。
推荐模式
public Result<int, IOError> readValue(string path) {
Result<int, IOError> result = Result<int, IOError>.ok(42);
int value = result?;
return Result<int, IOError>.ok(value);
}
- 将文件和系统调用的返回值改为
Result<Value, IOError>,不要用空值作为唯一错误信号。 - 用
Result.ok(value)与Result.err(error)构造结果。 - 对必须成功的内部不变量使用
expect("说明");对用户输入和 I/O 使用?或显式isErr检查。 - 将
MmapResult、FileResult等专用结果逐步统一为Result<T, E>。 - 完整重建编译器:
cargo build --release,然后执行cargo test --release --verbose。 - 检查所有
Result<T, E>的两个类型参数,并确保使用?的函数返回兼容的Result。 - 更新文档、示例和 CI 中硬编码的旧版本号;版本来源以根目录
.verinfo为准。
Cavvy 6.1.0 已知问题
- Result 的错误码和错误类型层级仍在持续标准化,跨库边界建议保留原始错误码。
?运算符目前主要覆盖 Result 错误传播,其他可恢复控制流类型尚未统一。panic/abort的终止行为和诊断输出依赖目标平台,嵌入式或无控制台环境应谨慎使用。
Cavvy 5.4.0 发布说明
发布日期:2026-07-13
5.4.0 完成系统级 I/O 的内存映射文件能力,提供跨平台、RAII 管理的零拷贝文件访问。
Cavvy 5.4.0 新特性详解
内存映射文件
新增 std::Mmap 与 std::MmapSlice:
mapReadOnly(path)和mapReadWrite(path, size)返回映射结果。- 支持
data()、size()、sync()、unmap()以及零拷贝slice()。 MmapSlice支持按偏移get/set。- Windows 使用
CreateFileMappingA/MapViewOfFile,Linux 使用mmap/munmap/msync。 - 析构时自动解除映射并释放系统句柄。
新增 examples/test_mmap.cay 和文件库集成测试,覆盖空文件、越界、持久化和 64KB 压力场景。
Cavvy 5.4.0 Bug 修复清单
- 修复泛型类重载构造器解析,避免无关重载覆盖泛型构造器。
- 修复整数到指针的显式转换 IR,正确生成
inttoptr。 - 修复
Mmap.unmap和INVALID_HANDLE比较相关的目标类型处理。
Cavvy 5.4.0 破坏性变更
本版本没有计划中的语言语法破坏性变更。新增文件映射 API 使用 Result 风格返回错误;调用方应处理映射失败、空文件和越界访问,不应假定系统映射一定成功。
Cavvy 5.4.0 迁移指南
- 执行
cargo build --release后运行cargo test --release --verbose。 - 大文件随机访问可使用
Mmap.mapReadOnly(path),需要写回时使用mapReadWrite并在完成后调用sync()。 - 将
MmapSlice的偏移限制在0 <= offset < size(),并妥善处理Result错误。 - 不要在
unmap()后继续使用关联的MmapSlice。
Cavvy 5.4.0 已知问题
- 映射文件的最大可用大小受操作系统地址空间、文件系统和进程权限限制。
- 映射区域在未调用
sync()时的持久化时机由操作系统决定。 Mmap的错误类型将在后续泛型Result<T, E>版本中进一步统一。
Cavvy 5.3.0 发布说明
发布日期:2026-07-11
5.3.0 聚焦语言调用语法、泛型推断、资源管理和调试体验。版本引入智能指针与自动 RAII,并补充系统库和容器基础设施。
Cavvy 5.3.0 新特性详解
语言与泛型
- 支持
ClassName::staticMethod(args)及命名空间限定的静态调用。 - 支持省略
new的ClassName(args)和ClassName<T>(args)实例化。 - 增强泛型静态工厂、实例方法返回类型和链式调用的类型推断。
- 增加代码风格警告,并改善无源码位置错误的调试信息。
标准库与资源管理
- 新增
std::sys,封装进程、环境变量和命令行参数。 - 新增泛型
std::ArrayList、std::vector及迭代器支持。 - 新增
UniquePtr、ScopedPtr、Rc和WeakPtr,并支持作用域退出时自动析构。 - 新增
eprint、eprintln、exit和数组分配内建能力。
工具链与测试
- 编译器支持
-g,生成可供 GDB 等调试器使用的调试信息。 - 内联 IR 解析支持
atomicrmw与cmpxchg。 - 新增智能指针、容器、静态调用和系统库示例及集成测试。
- 更新 EBNF 和 ESSO 文档。
Cavvy 5.3.0 Bug 修复清单
- 修复 Itanium C++ ABI 名称修饰中
E终止符位置错误。 - native/abstract 方法和构造函数只生成
declare,不再错误生成define。 - 修复 interop 类默认对象头、默认构造函数和命名空间内声明问题。
- 修复命名参数、可变参数及结构体实例方法的调用点类型/名称不匹配。
- 统一 RAII、
Optional注入使用 Itanium D1 析构符号。 - 缓存链式调用对象表达式,避免带副作用表达式被重复求值。
- 修正文档测试编码、预处理器说明和实现状态记录。
Cavvy 5.3.0 破坏性变更
语法歧义与调用解析
类名调用现在优先解析为省略 new 的构造调用;若旧代码依赖同名函数或变量的特殊解析,请使用明确的 new、命名空间或类型标注。
资源生命周期
带析构函数的局部对象会在作用域正常退出及函数返回前自动析构。依赖对象继续存活到更外层作用域的代码应显式转移所有权或使用 release()。
工具链参数
Windows MSVC 目标链接器改为 rust-lld。依赖系统链接器路径的自定义构建脚本需要相应调整。
Cavvy 5.3.0 迁移指南
- 先执行
cargo build --release,再运行集成测试。 - 需要调试时使用
cayc -g program.cay。 - 将手写的资源释放逻辑检查为 RAII 语义,避免重复释放;需要放弃托管时使用智能指针的
release()。 - 可将
new Type(args)逐步改写为Type(args),但跨模块或存在重载时建议保留显式new。 - 使用静态方法时可改为
Type::method(args),嵌套命名空间使用完整限定名。
Cavvy 5.3.0 已知问题
Rc循环引用检测主要面向 Debug/诊断场景,生产代码仍应使用WeakPtr主动打破循环。- 智能指针与复杂嵌套泛型组合的诊断信息仍可能不够精确。
-g生成的调试信息依赖目标平台的 LLVM/调试器版本,Windows 与 Linux 的显示效果可能不同。
Cavvy 5.2.0 发布说明
发布日期: 2026-07-04
版本代号: 5.2.0 (从 5.1.1 升级)
概述
Cavvy 5.2.0 是一次以基础设施重构和工具链增强为核心的版本更新。本版本对错误诊断系统进行了彻底的重构,移除了四层转换链,使 CayError 直接实现 miette::Diagnostic,大幅提升错误信息的可读性和可维护性。同时,嵌入式 LLVM 工具链(llc/lld)得到全面完善,REPL 和输入解析功能获得重要扩展,泛型系统进一步巩固。
本次更新的核心主题:
- 诊断系统重构: CayError 统一为 PascalCase,删除四层转换链,显式 error_code 字段,测试用例迁移到
Vec<CayError> - 嵌入式 LLVM 工具链:
--use-embedded-llc选项扩展到cay-rcpl和cay-run,ir2exe 嵌入式链接全面修复(系统库、入口点、动态链接器) - 泛型系统巩固: 全局泛型类型替换函数、接口 vtable 泛型后缀支持、递归嵌套泛型替换
- REPL 与输入解析增强:
pub/priv修饰符支持、fn风格方法定义 - 新子命令工具:
cay-ast、cay-pl、cay-sir三个调试分析工具 - 运行时库自动构建: Linux 平台自动检测并编译
libcayrt-linux.a
文档导航
- 新特性详解 - 所有新增语言特性与工具链功能
- Bug 修复清单 - 按模块分类的修复记录
- 破坏性变更 - 需要用户注意的兼容性变更
- 迁移指南 - 从旧版本升级的操作步骤
- 已知问题 - 本版本已知限制与规避方案
Cavvy 5.2.0 新特性详解
目录
诊断系统重构
1. CayError 直接实现 miette::Diagnostic
核心改动(76 个文件,+1336/-2566 行):
5.2.0 对错误诊断系统进行了彻底的扁平化重构。此前,一个编译错误需要经过四层包装转换:CompilerError → DisplayDiagnostic → 旧 Diagnostic → 旧 DiagnosticCollector 才能到达用户界面。现在 CayError 直接实现 miette::Diagnostic,所有中间层全部删除。
之前: cayError → CompilerError → DisplayDiagnostic → Diagnostic → miette 输出
之后: CayError ─────────────────────────────────────────────→ miette 输出 (直接)
关键改进:
| 方面 | 之前 | 之后 |
|---|---|---|
| 类型名称 | cayError / cayResult(小驼峰) | CayError / CayResult(PascalCase) |
| 错误识别 | message.contains() 字符串匹配 | 显式 error_code 字段,构造时指定 |
| 诊断链 | 4 层包装转换 | CayError 直接实现 miette::Diagnostic |
| 文件写入 | print_diagnostics 写入 debug 文件 | 纯内存处理,无副作用 |
| 测试断言 | DiagnosticCollector 收集 | Vec<CayError> 直接断言 |
| 代码量 | ~2566 行 | ~1336 行(减少 48%) |
错误码示例:
每个 CayError 变体现在携带一个显式的 error_code 字段:
#![allow(unused)]
fn main() {
// 之前:必须通过字符串包含判断
assert!(err.to_string().contains("type mismatch"));
// 之后:通过 error_code 精确匹配
assert_eq!(err.error_code, "E0001");
}
测试迁移:
所有集成测试从 DiagnosticCollector 迁移到 Vec<CayError>,断言更直接、更可靠:
#![allow(unused)]
fn main() {
// 之前
let collector = compile_eol_expect_error("test.cay");
assert!(collector.has_error_containing("type mismatch"));
// 之后
let errors: Vec<CayError> = compile_eol_expect_error("test.cay");
assert!(errors.iter().any(|e| e.error_code == "E0001"));
}
2. 模块路径统一
将所有错误、诊断相关的导入从 error 和 diagnostic 模块统一迁移到 miette_diagnostic 模块,完成项目诊断系统的整合。
嵌入式 LLVM 工具链增强
1. --use-embedded-llc 选项扩展
在 5.1.0 引入的 --use-llc-lld 基础上,本版本将其重命名为 --use-embedded-llc 并扩展支持到更多子命令:
| 工具 | 新增选项 | 说明 |
|---|---|---|
cayc | --use-embedded-llc | 使用内置 llc+lld 替代 clang(已有) |
cay-ir | --use-embedded-llc | IR 生成后直接编译(已有) |
cay-rcpl | --use-embedded-llc | 新增:交互式环境中使用嵌入式工具链 |
cay-run | --use-embedded-llc | 新增:编译运行时使用嵌入式工具链 |
2. ir2exe 嵌入式链接全面修复
修复了嵌入式链接模式下的一系列链接问题:
- 系统库搜索路径: 为
ld.lld添加默认系统库搜索路径 - C 运行时启动文件: 自动检测并添加 crt 启动文件,提供正确的入口点
- 动态链接器: 补充链接
dl库并自动设置动态链接器路径 - 链接参数顺序: 调整链接参数顺序确保符号解析正确
3. LLVM C API 内存安全修复
修复了 Windows 环境下调用 LLVMDisposeMessage 导致访问冲突的崩溃问题。由于 LLVM C API 在 Windows 上对 LLVMDisposeMessage 的实现存在兼容性问题(某些版本中返回的字符串不是由 malloc 分配的),此版本将该调用注释掉并添加了详细注释说明原因。
泛型系统巩固
1. 全局泛型类型替换函数
新增 substitute_type_params 工具函数,提供全局统一的泛型类型替换能力:
#![allow(unused)]
fn main() {
// 将所有泛型参数 T, U 替换为实际类型
let substituted = substitute_type_params(&generic_type, &type_args);
}
2. 递归嵌套泛型替换
重构泛型返回类型解析逻辑,支持递归替换嵌套泛型。例如 Pair<Box<int>, String> 中的嵌套泛型现在能被正确展开。
3. 接口 vtable 泛型后缀支持
修复接口 vtable 查找时未处理泛型后缀的问题。当接口方法包含泛型参数时,vtable 槽位名称现在能正确包含泛型后缀信息,确保动态分发在泛型接口下正常工作。
4. 调用方法返回值泛型替换
为调用方法自动替换返回值中的泛型参数,确保泛型方法的调用方获得正确的具体类型。
REPL 与输入解析增强
1. 访问修饰符支持
RCPL 输入解析器新增对 pub 和 priv 修饰符的解析支持,允许在交互式环境中定义带访问控制的类和成员:
>> pub class MyClass {
.. priv int x;
.. pub int getX() { return this.x; }
.. }
2. fn 风格方法定义
扩展输入解析器支持 fn 关键字风格的方法定义,与文件中的语法保持一致:
>> pub fn hello() -> void {
.. println("Hello from REPL!");
.. }
3. 帮助信息排版优化
优化了 cay-rcpl 和 cay-run 的帮助信息排版对齐,提升命令行交互体验。
运行时库自动构建
自动编译 libcayrt-linux.a
ir2exe 现在能够在 Linux 平台上自动检测并编译 Cavvy 运行时库:
- 当目标平台为 Linux 且缺失对应运行时库时,自动执行构建脚本
- 无需手动预编译运行时库,简化跨平台使用体验
- 编译产物缓存,避免重复构建
新子命令工具
1. cay-ast(AST 可视化工具)
输出解析后的抽象语法树(AST),支持格式化显示和 JSON 格式导出:
cay-ast program.cay # 树状格式输出
cay-ast program.cay --json # JSON 格式输出
2. cay-pl(预处理输出工具)
展示预处理后的源代码,便于调试宏展开和条件编译:
cay-pl program.cay # 输出预处理后源码
cay-pl program.cay --lines # 带行号输出
3. cay-sir(语义 IR 工具)
展示语义分析后的中间表示,帮助理解类型解析和符号绑定:
cay-sir program.cay # 输出语义 IR
4. 类型序列化支持
为核心 AST/IR 数据结构添加 Serialize 派生支持,所有三个新工具均支持 --json 选项输出结构化数据,便于与其他工具集成。
基础设施
1. 增量编译关闭
全局关闭 Rust 增量编译,修复 Windows 环境下多线程编译导致的竞态错误。增量编译在大型项目中虽然能加速重编译,但在 Windows 上会因文件锁定竞争导致偶发编译失败。
2. 版本号升级
所有 14 个组件版本号从 5.1.x 系列统一升级到 5.2.0,构建号同步更新:
| 组件 | 版本 | 构建号 |
|---|---|---|
| cayc / cay-ir / ir2exe | 5.2.0 | 47 |
| cay-check | 5.2.0 | 39 |
| cay-run | 5.2.0 | 23 |
| cavly | 5.2.0 | 13 |
| cay-pre / cay-bcgen / cay-dt / cay-dp / cay-rcpl | 5.2.0 | 12 |
| cay-setup | 5.2.0 | 2 |
| cay-ast / cay-pl / cay-sir | 5.2.0 | 2 |
3. 迭代器协议完成
标记 ROADMAP 中的迭代器协议为已完成状态,相关实现已在之前的版本中落地。
4. 调试模块跟踪修复
取消忽略并正确跟踪 debug_common 模块,确保该模块参与版本控制和 CI 构建。
Cavvy 5.2.0 Bug 修复清单
本文件按模块分类列出 5.2.0 版本中修复的所有问题。
编译器核心
诊断系统
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| cayError 类型名称不一致 | 统一重命名为 CayError(PascalCase),与 Rust 命名规范一致 | c567ff9 |
| 错误识别依赖字符串匹配 | 新增 error_code 显式字段,消灭 message.contains() 模式 | c567ff9 |
| 错误传播链过长 | 删除四层转换链(CompilerError / DisplayDiagnostic / 旧 Diagnostic / DiagnosticCollector) | c567ff9 |
| 诊断模块路径分散 | 统一迁移到 miette_diagnostic 模块,移除废弃旧模块引用 | 7157da3 |
line_col_to_offset 对中文/emoji 的处理 | 新增多字节字符正确性验证测试 | c567ff9 |
泛型系统
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| 缺少全局泛型类型替换 | 新增 substitute_type_params 工具函数 | 0dd32b2 |
| 嵌套泛型未递归替换 | 重构泛型返回类型解析,支持递归替换 | 0dd32b2 |
| 接口 vtable 未处理泛型后缀 | 修复 vtable 查找时泛型后缀匹配逻辑 | 0dd32b2 |
| 调用方返回值类型未替换 | 自动替换调用方法返回值中的泛型参数 | 0dd32b2 |
嵌入式 LLVM 工具链
ir2exe
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| 嵌入式链接缺系统库路径 | 为 ld.lld 添加默认系统库搜索路径 | b292408 |
| 缺少 C 运行时启动文件 | 自动检测并添加 crt 启动文件以提供正确入口点 | b292408 |
| 缺少 dl 库链接 | 补充链接 dl 库并自动设置动态链接器路径 | b292408 |
| 链接参数顺序错误 | 调整链接参数顺序确保符号解析正确 | b292408 |
| Linux 缺失运行时库 | 自动执行构建脚本编译 libcayrt-linux.a | defebed |
LLVM C API 兼容性
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| Windows 下 LLVMDisposeMessage 崩溃 | 注释掉不安全的 error_msg 内存释放逻辑,添加注释说明原因 | b30f9b2 |
| IR 验证时产生空字符串警告 | 优化处理逻辑,仅在消息非空且有效时打印警告 | b30f9b2 |
工具链
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| debug_common 模块被 git 忽略 | 修正 .gitignore,取消忽略并跟踪 debug_common 模块 | 2e284e0 |
| cay-rcpl 缺少 –use-embedded-llc | 新增嵌入式 llc 选项支持 | e665ab9 |
| cay-run 缺少 –use-embedded-llc | 新增嵌入式 llc 选项支持 | e665ab9 |
| REPL 输入解析器不识别访问修饰符 | 扩展解析器支持 pub/priv 修饰符 | e665ab9 |
| REPL 输入解析器不识别 fn 风格方法 | 扩展解析器支持 fn 关键字风格定义 | e665ab9 |
| 帮助信息排版不对齐 | 优化帮助信息的排版对齐 | e665ab9 |
基础设施
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| Windows 多线程编译竞态错误 | 全局关闭增量编译 | 0dd32b2 |
Cavvy 5.2.0 破坏性变更
本文档列出从 5.1.x 升级到 5.2.0 时需要注意的兼容性变更。
1. cayError / cayResult 重命名为 CayError / CayResult
变更: 核心错误类型和结果类型已从 cayError / cayResult(小驼峰)重命名为 CayError / CayResult(PascalCase),与 Rust 命名规范保持一致。
影响: 所有引用 cayError 或 cayResult 的代码需要更新。
迁移方式:
- use cavvy::error::cayError;
+ use cavvy::miette_diagnostic::CayError;
- type Result<T> = cayResult<T>;
+ type Result<T> = CayResult<T>;
相关 Commit: c567ff9
2. CayError 变体现在需要显式指定 error_code
变更: 每个 CayError 变体新增 error_code: &'static str 字段,构造时必须显式提供错误码。
影响: 所有直接构造 CayError 的地方需要添加 error_code 参数。旧版本中依赖 message.contains() 字符串匹配的错误判断逻辑需要更新为 error_code 匹配。
迁移方式:
- let err = cayError::TypeMismatch { expected: "int", found: "String" };
+ let err = CayError::TypeMismatch { expected: "int", found: "String", error_code: "E0001" };
错误码匹配代替字符串匹配:
- assert!(format!("{}", err).contains("type mismatch"));
+ assert_eq!(err.error_code, "E0001");
相关 Commit: c567ff9
3. DiagnosticCollector 已删除
变更: DiagnosticCollector 结构体和相关 API 已被完全删除。测试用例使用 Vec<CayError> 替代。
影响: 所有使用 DiagnosticCollector 的测试代码需要迁移。
迁移方式:
- let collector = compile_eol_expect_error("test.cay");
- assert!(collector.has_error_containing("type mismatch"));
+ let errors: Vec<CayError> = compile_eol_expect_error("test.cay");
+ assert!(errors.iter().any(|e| e.error_code == "E0001"));
相关 Commit: c567ff9
4. print_diagnostics 不再写入 debug 文件
变更: print_diagnostics 函数移除了文件写入逻辑,现在仅进行内存处理和终端输出。
影响: 任何依赖诊断系统写入 debug 文件进行分析的工作流需要调整。
相关 Commit: c567ff9
5. 错误/诊断模块导入路径变更
变更: 所有错误和诊断相关的导入从 error 和 diagnostic 模块迁移到 miette_diagnostic 模块。
影响: 导入路径需要更新。
迁移方式:
- use cavvy::error::CayError;
- use cavvy::diagnostic::print_diagnostics;
+ use cavvy::miette_diagnostic::{CayError, print_diagnostics};
相关 Commit: 7157da3
6. 增量编译已全局关闭
变更: Cargo.toml 中新增配置全局关闭 Rust 增量编译(incremental = false)。
影响: 重复编译速度可能略有下降(因为不再利用增量缓存)。这是为了修复 Windows 平台上的多线程编译竞态错误。
相关 Commit: 0dd32b2
7. llc/lld 选项名称变更
变更: 之前版本中的 --use-llc-lld 选项在部分工具有不同的命名方式,本版本统一为 --use-embedded-llc。
影响: 如果使用了旧的选项名称,需要更新。
相关 Commit: e665ab9
Cavvy 5.2.0 迁移指南
本指南帮助开发者从 Cavvy 5.1.x 升级到 5.2.0。
快速检查清单
- 全局搜索
cayError替换为CayError - 全局搜索
cayResult替换为CayResult - 更新所有
CayError构造调用,添加error_code字段 - 替换所有
message.contains()错误断言为error_code匹配 - 将
DiagnosticCollector测试迁移到Vec<CayError> - 更新导入路径:
error/diagnostic→miette_diagnostic - 更新 CLI 参数:
--use-llc-lld→--use-embedded-llc(如果使用过) - 启用
cargo build --release完整重编译(因增量编译已关闭)
逐步迁移
第一步:更新类型名称
全局替换:
| 旧名称 | 新名称 |
|---|---|
cayError | CayError |
cayResult | CayResult |
cayError::* | CayError::* |
# 使用 sed 或类似工具进行批量替换
find . -name "*.rs" -exec sed -i 's/\bcayError\b/CayError/g' {} +
find . -name "*.rs" -exec sed -i 's/\bcayResult\b/CayResult/g' {} +
第二步:更新导入路径
- use cavvy::error::*;
+ use cavvy::miette_diagnostic::*;
主要的公开 API 重新导出保持不变,如果之前使用的是 cavvy::error::CayError,只需更改为 cavvy::miette_diagnostic::CayError 即可。
第三步:更新错误构造
查找所有 CayError:: 的构造调用,为每个变体添加 error_code 字段。错误码格式为 E 加四位数字(如 E0001、E0002)。
可根据错误类型按以下规则选择 error_code(推荐):
| 变体类型 | error_code 前缀 |
|---|---|
| 类型错误 | E0001 - E0099 |
| 语法错误 | E0100 - E0199 |
| 语义错误 | E0200 - E0399 |
| 内部错误 | E1000+ |
第四步:更新测试断言
// 旧方式:字符串匹配
- let collector = compile_eol_expect_error("test.cay");
- assert!(collector.has_error_containing("type mismatch"));
// 新方式:error_code 匹配
+ let errors: Vec<CayError> = compile_eol_expect_error("test.cay");
+ assert!(errors.iter().any(|e| e.error_code == "E0001"));
第五步:检查 CLI 脚本
如果 CI 脚本或其他自动化流程中使用了 --use-llc-lld 参数,请更新为 --use-embedded-llc。
回滚方案
如果需要临时回退到 5.1.x,请执行:
git checkout v5.1.0 # 或 5.1.1 之前的分支
cargo build --release
注意:5.2.0 的诊断系统变更涉及 76 个文件,回退后如果切换回来需要完整重编译。
Cavvy 5.2.0 已知问题
本文件列出 5.2.0 版本中已知的限制与规避方案。
1. Windows 下 LLVMDisposeMessage 内存释放不可用
影响范围: 嵌入式 llc 模式(--use-embedded-llc)下 IR 解析出错时
问题描述: LLVM C API 在 Windows 环境下调用 LLVMDisposeMessage 可能导致访问冲突崩溃。这是由于 LLVM 某些 Windows 构建版本中,LLVMCreateMessage 返回的字符串并非由 malloc 分配,导致 LLVMDisposeMessage(内部调用 free)行为未定义。
当前状态: 已将对应内存释放调用注释掉,存在轻微内存泄漏(仅在错误路径上)。
规避方案:
- 错误路径泄漏仅发生在 IR 验证失败时,不影响正常编译流程
- 如果频繁触发此问题,建议避免在 Windows 上使用
--use-embedded-llc,改用默认的 clang 后端
计划修复版本: 5.3.0(等待 LLVM 官方修复或找到更安全的释放策略)
2. Windows 多线程编译竞态
影响范围: 所有 Windows 构建
问题描述: Rust 增量编译在 Windows 上因文件锁定竞争导致偶发编译失败。目前已在全局关闭增量编译(codegen-units = 1 结合 incremental = false)。
当前状态: 增量编译已关闭,编译速度有所下降但稳定性已恢复。
规避方案: 无。这是 Rust 编译器在 Windows 上的已知问题。
3. 错误码尚未完全标准化
影响范围: 诊断系统
问题描述: 虽然本版本引入了 error_code 字段,但错误码的分配尚未完全标准化,部分变体可能使用临时错误码。
当前状态: 错误码方案已启用但仍在演进中。
计划修复版本: 5.3.0(发布完整的错误码参考文档)
4. cay-ast / cay-pl / cay-sir 构建号为 2
影响范围: 新工具
问题描述: 三个新工具的构建号(build 2)显著低于其他工具的构建号(如 cayc 为 build 47),表示这些工具处于早期阶段。
当前状态: 功能基本可用但可能缺少边缘情况处理。
5. 关闭增量编译后的重编译性能
影响范围: 所有开发者
问题描述: 增量编译关闭后,增量修改后的重编译时间可能比之前长 1.5x-2x。
规避方案:
- 考虑使用
sccache进行编译缓存 - 对于日常开发,可选择性开启
codegen-units(风险自负)
Cavvy 5.1.0 发布说明
发布日期: 2026-06-10
版本代号: 正式版 5.1.0 (从 5.1.0-rc.1 升级)
概述
Cavvy 5.1.0 是一次重大版本更新,引入了多项核心语言特性、完整的跨平台支持重构、全新的官方文档站,以及大量编译器基础设施的改进。本版本从 5.1.0-Alpha 历经多个迭代,最终达到生产就绪状态。
本次更新的核心主题:
- 语言特性: Lambda 表达式、指针完整支持、泛型类型系统、接口动态分发
- 工具链: Cavly 包管理器、llc+lld 工具链支持、调试工具 cay-dt / cay-dp
- 跨平台: Windows / Linux 双平台完整编译与运行支持
- 文档: 全新 mdBook 官方文档站,完整 API 参考
文档导航
- 新特性详解 - 所有新增语言特性与工具链功能
- Bug 修复清单 - 按模块分类的修复记录
- 破坏性变更 - 需要用户注意的兼容性变更
- 迁移指南 - 从旧版本升级的操作步骤
- 已知问题 - 本版本已知限制与规避方案
Cavvy 5.1.0 新特性详解
目录
语言特性
1. Lambda 表达式与函数式接口
Cavvy 5.1.0 正式支持 Lambda 表达式,允许以简洁语法创建匿名函数实例。
语法示例:
// 无参数 Lambda
() -> { println("Hello"); }
// 单参数 Lambda(可省略括号)
x -> x * 2
// 多参数 Lambda
(int a, int b) -> a + b
// 函数式接口赋值
interface Comparator<T> {
int compare(T a, T b);
}
Comparator<int> cmp = (a, b) -> a - b;
实现要点:
- Lambda 表达式可自动适配到单方法接口(函数式接口)
- 支持闭包捕获外部变量
- 修复了类型打印需手动调用
String.valueOf的问题,统一自动类型转换
2. 指针类型系统完整支持
5.1.0 实现了 Cavvy 对指针的完整支持,包括声明、运算和作为参数/返回值。
支持的语法:
// 指针类型声明
int* p;
int** pp;
// 取地址
int x = 10;
int* p = &x;
// 解引用
int y = *p;
// 通过指针赋值
*p = 20;
// 多级指针
int** pp = &p;
int z = **pp;
// 指针作为函数参数和返回值
int* allocInt() {
// ...
}
技术实现:
- 修复语义分析器中
AddressOf返回Type::Pointer而非Type::Int64 - 修复语义分析器中
Deref正确处理Type::Pointer类型 - 添加代码生成器对解引用赋值的支持 (
generate_deref_assignment) - 更新 EBNF 语法规范,添加
pointer_type定义
测试覆盖:
test_pointer_basic.cay- 基础指针操作test_pointer_user_example.cay- 用户示例代码test_pointer_advanced.cay- 高级用法(函数参数、多级指针)
3. 泛型类型系统
修复并完善了泛型类型系统的多项核心问题,现在泛型类可以稳定用于生产代码。
修复的问题:
- 泛型类字段类型替换: 在
class_analysis.rs中添加字段类型的泛型参数替换,将T替换为GenericParam("T") - 泛型方法参数/返回类型替换: 在
type_check.rs中对方法参数和返回类型进行泛型参数替换 - 多类型参数解析: 在
expr_inference.rs中修复类型参数解析逻辑,支持Pair<K, V>等多参数泛型类 - 泛型类方法查找: 在
types.rs的TypeRegistry::find_method中支持泛型类名解析为基础类名 - 泛型类型匹配: 在
ClassInfo::types_match_exact中支持泛型模板与实例化类型的匹配
语法示例:
class Pair<K, V> {
K key;
V value;
Pair(K k, V v) {
this.key = k;
this.value = v;
}
}
Pair<int, String> p = new Pair<int, String>(1, "hello");
测试覆盖:
test_generics_basic.cay- 基础泛型字段测试test_generics_method_param.cay- 泛型方法参数测试
4. 接口方法运行时动态分发
重构 vtable 生成逻辑,支持全局分配接口方法槽位,实现接口类型调用的动态分派。
特性说明:
- 按运行时类型选择方法实现
- 支持多场景接口动态分发,覆盖参数、返回值场景
- 优化类型注册表,添加接口 vtable 槽位管理相关工具函数
5. 类型别名与函数指针
新增 alias 关键字支持类型别名定义,新增 fn 关键字用于声明函数指针类型。
语法示例:
// 类型别名
alias IntVector = Vector<int>;
alias StringMap = Map<String, int>;
// 函数指针类型
alias CompareFn = fn(int, int) -> int;
// 函数指针使用
CompareFn cmp = (a, b) -> a - b;
6. 访问控制(public / protected / private / static)
新增完整的访问控制支持,覆盖类成员的可访问性规则。
新增测试覆盖:
- 11 个访问控制相关示例文件
- 覆盖 public / protected / private / static / 构造函数等场景
- 新增方法名拼写建议功能,优化未找到方法的错误提示
- 重构静态方法调用解析逻辑,优先匹配当前类静态方法
7. 内联 IR(Inline IR)
支持在 Cavvy 代码中直接嵌入 LLVM IR,实现与底层的高效交互。
语法示例:
__ir {
%result = add i32 %0, %1
ret i32 %result
}
技术实现:
- 新增
InlineIrBridge模块,实现 CodeGen 与 IR Builder 之间的安全协作 - 支持变量映射系统,支持参数索引(
%0,%1)和变量名引用 - 参数使用原始 LLVM 名(
class_name.param_name)而非 alloca 变量名
测试覆盖: 8 个完整测试用例,覆盖基础算术、浮点数、位运算、比较运算、数学函数、内存操作、类型转换、复杂表达式。
8. 复合赋值操作符
新增 +=, -=, *=, /=, %= 复合赋值操作的 IR 生成支持。
编译器与工具链
1. Cavly 包管理器
新增完整的 Cavly 包管理器模块,提供类似 Cargo 的项目管理体验。
支持的命令:
cavly init- 初始化新项目cavly build- 构建项目cavly run- 运行项目cavly clean- 清理构建产物
特性:
- 配置解析(TOML 格式)
- 项目管理与依赖解析
- FFI 支持和工作区依赖解析
- 支持
-I参数传递额外包含路径
2. llc + lld 工具链支持
为 cayc 和 ir2exe 添加 --use-llc-lld 选项,允许在无 Clang 环境下使用 llc + lld 工具链编译。
平台适配:
- MinGW:
ld.lld(GNU 风格) - MSVC:
lld-link(COFF 风格) - macOS:
ld64.lld(Mach-O 风格) - Linux:
ld.lld(ELF 风格) - WebAssembly:
wasm-ld
3. 调试工具 cay-dt / cay-dp
新增两个调试工具,辅助编译器开发与问题诊断:
- cay-dt (Token PreViewer): 可视化词法分析结果,支持彩色输出和 JSON 格式
- cay-dp (Parse PreViewer): 可视化 AST 结构,支持紧凑模式和 JSON 输出
两个工具均支持 --no-color 选项,便于脚本集成。
4. 预处理器增强
- 新增预处理器指令:
error,warning,pragma - 修复
#define行尾注释处理问题(支持//和/* */风格注释) - 修复符号链接下无法找到
caylibs的问题(使用canonicalize解析符号链接) cay-dt和cay-dp默认启用预处理,支持--no-preprocess禁用
5. FFI 增强
- 新增
extern函数别名支持(extern fn foo as bar) - 新增
CString类型支持 - 新增
c_int64_t和c_uint64_tFFI 类型 - 扩展 FFI 类型与语句支持
- 新增内联 IR、内存分配/释放语句
标准库扩展
1. File.cay 标准库
实现完整的文件操作标准库:
File类: 打开、关闭、读写、定位等文件操作FileMode类: 类型安全的文件模式设置SeekOrigin枚举: 文件定位支持FileInfo类: stat-based 文件信息获取LineIterator: 流式逐行读取FileReader.lines(): 返回行迭代器
设计修复:
exists()使用access()替代fopen(),避免修改 atimesize()使用 FileInfo.stat-based 方法,避免 TOCTOU 竞态条件writeInterpolated使用 StringBuilder 优化,复杂度从 O(n^2) 降至 O(n)readAllLines改为流式读取,内存使用从 O(file_size) 降至 O(max_line_length)
2. String 方法扩展
新增方法:
lastIndexOfstartsWithendsWith
3. Math.cay 修复
修复多个数学函数的设计问题:
Math.abs(int): 处理INT_MIN溢出Math.abs(long): 处理LONG_MIN溢出Math.smoothStep: 添加a==b除零防护Math.clamp: 自动交换 min/max 如果顺序错误Math.gcd: 处理INT_MIN溢出问题Math.frac: 使用floor确保返回[0,1)范围Math.approxEqualRelative: 新增相对误差版本Random.nextDouble: 使用正确RAND_MAX(2147483647)Random.nextBool: 使用位运算避免模运算偏差Random.nextInt(min,max): 处理溢出情况Random.nextGaussian: 防护log(0),缓存第二个值Vector2/3.div/normalize: 除零返回 NaN 而非静默失败
4. Network.cay 修复
- 修复 socket 句柄类型不匹配问题
- 修复跨平台 socket 发送参数类型不兼容问题
- 修复
setsockopt超时参数传递错误 - 修正
TcpSocket构造函数访问修饰符 - 修复
HttpClient.send的返回值判断逻辑 - 修复
EasyHTTP中char隐式转int导致的符号问题
基础设施
1. 官方文档站
- 新增 mdBook 配置与主题样式
- 迁移所有文档到
docs/目录并重构结构 - 添加官方文档站构建脚本与自动化部署配置
- 新增文档页面: LSP 协议、字节码格式、快速开始等
2. 版本号与构建
- 为所有二进制工具添加带 git commit 的版本号
- 支持 dirty 状态检测
- 新增
.verinfo各子工具版本配置
3. 源映射系统重构
- 重构
SourceLocation结构体,新增文件路径字段 - 实现完整的 IR 源映射生成与解析功能
- 修复错误报告中的文件名显示问题(现在正确显示被包含文件路径)
- 修复错误报告中的行号映射问题(显示原始源文件行号而非预处理后行号)
Cavvy 5.1.0 Bug 修复清单
本文件按模块分类列出 5.1.0 版本中修复的所有问题。
编译器核心
类型系统
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| 泛型类字段类型未替换 | 在 class_analysis.rs 中添加字段类型的泛型参数替换 | 198399ad |
| 泛型方法参数/返回类型未替换 | 在 type_check.rs 中对方法参数和返回类型进行泛型参数替换 | 198399ad |
| 多类型参数解析失败 | 在 expr_inference.rs 中修复类型参数解析逻辑,支持 Pair<K, V> | 198399ad |
| 泛型类方法查找失败 | 在 TypeRegistry::find_method 中支持泛型类名解析为基础类名 | 198399ad |
| 泛型类型匹配失败 | 在 ClassInfo::types_match_exact 中支持模板与实例化类型匹配 | 198399ad |
| 嵌套泛型与移位符冲突 | 提取泛型类型实参解析为公共函数,支持 >>/>>> 作为嵌套泛型结束符 | 05d469eb |
| 指针类型命名空间兼容 | 新增指针类型命名空间兼容检查 | b18a00f7 |
| using 别名查找类型 | 为 TypeRegistry::find_qualified_class 添加 using 别名匹配逻辑 | 619baad9 |
| 类型兼容检查不完善 | 重构类型兼容检查逻辑,支持命名空间和泛型类型匹配 | b18a00f7 |
| 加法运算类型限制 | 优化加法运算类型支持,允许非字面量数值与字符串相加 | b18a00f7 |
语义分析
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| AddressOf 返回错误类型 | 修复为返回 Type::Pointer 而非 Type::Int64 | f65e3e08 |
| Deref 未正确处理 Pointer | 修复语义分析器中 Deref 对 Type::Pointer 的处理 | f65e3e08 |
| 控制流条件类型检查缺失 | 补全 if/while/for/do/switch 等控制流的条件类型检查 | 5f90c9f1 |
| 静态方法调用解析错误 | 重构静态方法调用解析逻辑,优先匹配当前类静态方法 | 5f90c9f1 |
| 方法名拼写错误提示差 | 新增方法名拼写建议功能,优化未找到方法的错误提示 | 5f90c9f1 |
| 源文件路径前缀问题 | 修复源文件路径处理,移除 Windows \\?\ 前缀 | 5f90c9f1 |
| 错误重映射逻辑缺陷 | 重构错误重映射逻辑,传递并使用 source_map 参数 | 5f90c9f1 |
代码生成
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| 解引用赋值不支持 | 添加代码生成器对解引用赋值的支持 (generate_deref_assignment) | f65e3e08 |
| store 指令类型不匹配 | 修正代码生成时 store 语句中目标指针类型错误 | ddfde110 |
| if 语句 merge 块缺少 terminator | 检测 then 和 else 块是否都返回,如果是则不创建 merge 块 | 5c9a1a4f |
| 静态成员 codegen null fallback | 修复静态成员代码生成时的空值回退问题 | 7ffd308d |
| 对象地址获取偏移量错误 | 修复对象地址获取时的偏移量错误 | 87c71260 |
| 布尔值打印逻辑错误 | 直接存储并输出 true/false 而非 1 | 17c1b871 |
| 字符串拼接整数转换 | 统一使用 i32 类型处理 | 17c1b871 |
| this 关键字类型识别 | 修复 this 关键字类型识别问题 | 9258c96a |
| 数组字段访问生成 | 优化数组字段访问代码生成逻辑 | 9258c96a |
| switch 语句生成逻辑 | 优化 switch 语句生成逻辑 | 9258c96a |
| switch 分支终止判断 | 修正 all_cases_terminate 判定规则,仅将 return 作为终止语句 | a8f44d8e |
错误报告
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| 错误报告文件名显示错误 | 语义分析错误现在正确显示错误发生的源文件路径 | 1d9eb313 |
| 错误报告行号映射问题 | 修复错误报告系统在处理包含文件时的行号映射问题 | ebd3f9ee |
| 字段赋值错误信息不足 | 改进错误报告,添加字段名和类名信息 | 8c9d5b08 |
预处理器
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| #define 行尾注释处理 | 添加 remove_line_comments 辅助函数,支持 // 和 /* */ 注释移除 | 84734c98 |
| 符号链接下无法找到 caylibs | 使用 canonicalize 解析符号链接,兼容 Linux 下 /proc/self/exe | b3574c22 |
| 包含文件行号对齐 | 修复预处理器包含文件行号对齐问题 | c9a2e2f3 |
| 宏替换边界检查 | 修复预处理器宏替换边界检查问题 | 8c9d5b08 |
| include 示例文件名错误 | 修正为正确的 File.cay | b3574c22 |
网络模块 (Network.cay)
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| socket 句柄类型不匹配 | 修复 TcpSocket、TcpServer 和 UdpSocket 类的句柄类型 | 91eca938 |
| 跨平台 socket 发送参数类型不兼容 | 修复非 Windows 平台 send/sendto 长度参数类型不匹配 | 7cdaf483 |
| setsockopt 超时参数传递错误 | 修正 TcpServer 中 setsockopt 的超时参数传递 | 7cdaf483 |
| c_int 与 int 类型混用 | 将超时参数数组从 c_int 改为 int | f67c30f2 |
| Cay 语言数组声明语法错误 | 将 C 风格静态数组改为 Cay 语言动态数组初始化 | bb76c254 |
| TcpSocket 构造函数访问修饰符 | 修正为正确的访问修饰符 | 3d71041c |
| HttpClient.send 返回值判断 | 修复返回值判断逻辑 | 3d71041c |
| EasyHTTP char 隐式转 int | 修复导致的符号问题 | 3d71041c |
标准库
Math.cay
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| Math.abs(int) INT_MIN 溢出 | 添加溢出处理 | d5dffc5e |
| Math.abs(long) LONG_MIN 溢出 | 添加溢出处理 | d5dffc5e |
| Math.smoothStep 除零 | 添加 a==b 除零防护 | d5dffc5e |
| Math.clamp 参数顺序 | 自动交换 min/max 如果顺序错误 | d5dffc5e |
| Math.gcd INT_MIN 溢出 | 处理溢出问题 | d5dffc5e |
| Math.frac 范围错误 | 使用 floor 确保返回 [0,1) | d5dffc5e |
| Random.nextDouble RAND_MAX | 使用正确值 2147483647 | d5dffc5e |
| Random.nextBool 模运算偏差 | 使用位运算避免偏差 | d5dffc5e |
| Random.nextInt 溢出 | 处理溢出情况 | d5dffc5e |
| Random.nextGaussian log(0) | 防护 log(0),缓存第二个值 | d5dffc5e |
| Vector2/3.div/normalize 除零 | 除零返回 NaN 而非静默失败 | d5dffc5e |
| Math.integrate 硬编码 | 移除硬编码 sin 函数 | d5dffc5e |
| Math 最小 int 字面量溢出 | 修复最小 int 字面量溢出问题 | c9a2e2f3 |
File.cay
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| exists() 修改 atime | 使用 access() 替代 fopen() | d83ef93b |
| size() TOCTOU 竞态条件 | 使用 FileInfo.stat-based 方法 | d83ef93b |
| writeFormat 命名混淆 | 重命名为 writeInterpolated | d83ef93b |
| writeFormat 复杂度 O(n^2) | 使用 StringBuilder 优化至 O(n) | d83ef93b |
| readAllLines 双倍内存峰值 | 改为流式读取 | d83ef93b |
| finalize() 错误传播 | 静默处理关闭错误 | d83ef93b |
StringPlus
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| format 占位符拼接逻辑 | 修复占位符拼接逻辑 | 3d71041c |
链接与构建 (ir2exe)
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| ELF 动态链接器未设置 | 自动检测并添加可用的系统动态链接器路径 | b446bd5b |
| 重复添加启动对象文件 | 检查是否已经添加过启动文件避免重复符号 | 235eac9a |
| Linux 平台编译链接问题 | 添加 GNU ld 风格参数、加载 CRT 启动文件 | 211a454b |
| Cavvy 运行时库缺失 | 自动检测并构建 Cavvy 运行时库 | 8443c429 |
| crt2.o 错误 | 将 crt2.o 改为 crt1.o | 8b511914 |
| lld 链接器风格错误 | 使用 GNU ld 风格参数而非 COFF 风格 | 8b511914 |
CI / 构建脚本
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| Windows 链接缺少 xml2s.lib | 创建空静态库作为占位符 | 69c6e259 |
| llvm-sys 静态/动态链接配置 | 多次调整,最终移除 force-static 适配多平台 | 041045fa, d742a6b1, 5810947c |
| CI 环境变量未跨步骤生效 | 补全 LLVM 依赖安装和环境变量设置 | 07e99d75 |
| setup-llvm.py 解压路径错误 | 修改解压逻辑,将文件解压到 bin 子目录 | af36ce46 |
| setup-llvm.py 下载链接层级 | 添加 bin 子目录层级匹配实际结构 | 023bb274 |
| setup-llvm.py 中文编码 | 全局 UTF-8 编码重定向标准输出/错误流 | 39208413 |
| git 版本检测误报 | 过滤编译生成的无后缀 ELF 可执行文件变更 | bdf122e0 |
测试
| 问题 | 修复内容 | 相关 Commit |
|---|---|---|
| test_calling_conventions Linux 失败 | 将 Windows Sleep API 替换为 POSIX usleep | 580b936a |
| 跨平台可执行文件路径 | 移除硬编码的 .exe 后缀 | 6846141c, ed3edba9 |
| inline-ir 测试触发 llvm-sys 重编译 | 替换 cargo run --release 为直接二进制执行 | a09a93eb |
| 测试可执行文件路径适配 | 优先使用 CARGO_BIN_EXE_cavly 环境变量 | a626b3b0 |
| 非 Windows socket 类型转换 | 添加显式类型转换 | 44215680 |
Cavvy 5.1.0 破坏性变更
本文档列出从上一版本升级到 5.1.0 时需要注意的兼容性变更。
1. 隐式类型转换已移除
变更: 不再允许 string 和 int 等类型之间的隐式转换,必须使用显式转换。
影响: 以前可以自动转换的代码现在会产生编译错误。
迁移方式:
// 旧代码(不再支持)
int x = 42;
String s = x; // 隐式转换,之前可能通过
// 新代码(必须显式转换)
int x = 42;
String s = String.valueOf(x); // 显式转换
// 或
String s = x.toString();
相关 Commit: 87c71260 - “修复类型打印需手动调用 String.valueOf 的问题,统一自动类型转换”
2. 标准库引入命名空间
变更: 标准库由于添加了 namespace,现在需要通过 using 别名显式引入,不再支持全局回退查找。
影响: 以前可以直接使用 File、String 等标准库类型的代码,现在需要显式引入。
迁移方式:
// 旧代码(不再支持)
File f = new File("test.txt");
// 新代码(使用 using 别名)
using File = std::File;
File f = new File("test.txt");
重要限制:
- 不支持
using namespace std;这种批量引入语法 - 必须对每个使用的类型单独声明
using std::XXX;
相关 Commit:
619baad9- “支持 using 别名查找类型,移除全局回退查找”b18a00f7- “重构类型系统,支持命名空间和泛型类型匹配”
迁移检查清单
升级代码时,请逐项检查:
- 所有
int到String的转换已改为显式(String.valueOf()或.toString()) - 所有
String到int的转换已改为显式(Integer.parseInt()等) - 所有标准库类型(
File、StringBuilder、Network等)已通过using std::XXX引入 - 未使用
using namespace std;(该语法不受支持)
Cavvy 5.1.0 迁移指南
本文档提供从旧版本升级到 Cavvy 5.1.0 的详细步骤。
前置要求
系统要求
- Windows: Windows 10/11,安装 Python 3.8+
- Linux: Ubuntu 20.04+ 或兼容发行版
- macOS: 实验性支持(部分功能)
依赖要求
- Rust 1.78+ (推荐 1.80+)
- LLVM 22.1+ (Windows 用户可运行
python setup-llvm.py自动安装) - Python 3.8+ (用于 setup-llvm.py)
迁移步骤
步骤 1: 更新代码仓库
# 拉取最新代码
git pull origin main
# 更新子模块
git submodule update --init --recursive
步骤 2: 配置 LLVM (Windows)
# 自动检测并安装/配置 LLVM
python setup-llvm.py
Linux 用户请通过包管理器安装 LLVM:
# Ubuntu/Debian
sudo apt-get install llvm-22 llvm-22-dev clang-22 lld-22
# Arch Linux
sudo pacman -S llvm clang lld
步骤 3: 清理旧构建产物
cargo clean
步骤 4: 构建项目
cargo build --release
步骤 5: 运行测试验证
cargo test --release
所有测试必须通过才能确认迁移成功。
代码迁移清单
隐式类型转换
检查所有隐式类型转换,改为显式转换:
// 修改前
int x = 42;
String s = x; // 隐式转换
// 修改后
int x = 42;
String s = String.valueOf(x); // 显式转换
标准库类型引用
检查所有标准库类型的使用,添加 using 别名:
// 修改前
File f = new File("test.txt");
StringBuilder sb = new StringBuilder();
// 修改后
using File = std::File;
using StringBuilder = std::StringBuilder;
File f = new File("test.txt");
StringBuilder sb = new StringBuilder();
注意: 不支持 using namespace std;,必须对每个类型单独声明。
验证迁移
运行编译器测试
cargo test --release
验证工具链
# 检查各工具版本
cayc --version
cay-ir --version
ir2exe --version
cay-check --version
cay-run --version
cavly --version
编译示例程序
# 使用 cavly 构建示例项目
cd examples/CavvyN
cavly build
cavly run
常见问题
Q: Windows 上 llvm-sys 链接失败?
A: 运行 python setup-llvm.py 会自动创建必要的占位符文件。如果仍失败,检查 LLVM_SYS_221_PREFIX 环境变量是否指向正确的 LLVM 目录。
Q: Linux 上找不到动态链接器?
A: 确保系统已安装标准 C 库开发包:
# Ubuntu/Debian
sudo apt-get install libc6-dev
# 或安装 build-essential
sudo apt-get install build-essential
Q: 旧项目使用 cavly 构建失败?
A: 检查 Cavly.toml 格式是否符合最新规范。可能需要更新依赖版本号。
Q: 测试在 Windows 上通过但在 Linux 上失败?
A: 确保 Linux 环境已安装完整的 LLVM 开发包和 C 运行时库。检查 cargo test --release 的具体错误信息。
回滚方案
如果迁移遇到问题,可以通过以下方式回滚:
# 查看旧版本标签
git tag | grep 5.0
# 回滚到上一个稳定版本
git checkout 5.0.x
# 重新构建
cargo build --release
建议迁移前创建分支:
git checkout -b migration-5.1.0
git checkout main
git pull origin main
Cavvy 5.1.0 已知问题与限制
本文档记录 Cavvy 5.1.0 版本中已知的半成品实现限制和平台支持状态。
半成品实现限制
以下功能在 5.1.0 中已提前实现,但属于 5.2 版本的规划特性,因此存在已知限制。
1. 泛型类型系统
状态: 提前实现(原定 5.2),基础功能可用,复杂场景有限制。
已知限制:
- 泛型接口尚未支持
- 泛型约束(where 子句)未实现
- 嵌套泛型类型推断在某些场景下可能需要显式注解
- 泛型静态方法存在限制
建议:
- 使用显式类型注解避免推断歧义
- 避免过度嵌套的泛型类型
- 将复杂泛型表达式拆分为多个步骤
相关测试: test_generics_basic.cay, test_generics_method_param.cay
2. 接口动态分发
状态: 已实现运行时动态分发,部分高级场景受限。
已知限制:
- 泛型接口方法不支持动态分发
- 嵌套接口调用链的优化尚未完成
- 接口作为泛型类型参数存在限制
建议:
- 接口继承层级保持简单
- 避免在接口方法签名中使用泛型参数
相关测试: test_vtable_dynamic_dispatch.cay, test_vtable_simple.cay
平台支持状态
macOS
状态: 实验性支持
说明: macOS 平台的部分功能可能不可用或未经充分测试。主要限制包括:
- 运行时库构建可能不完整
- 部分系统调用封装未针对 macOS 适配
- 链接器参数使用 Linux 风格而非 macOS 风格
建议:
- 优先在 Windows 或 Linux 上进行开发和部署
- macOS 用户请关注后续版本更新
已修复的历史问题
5.1.0 版本中以下问题已完全修复,不再存在:
- 指针类型系统完整支持
- 访问控制(public/protected/private/static)
- 跨平台网络库(Windows / Linux)
- 错误报告中的文件名和行号映射
- 预处理器 #define 行尾注释处理
- Linux 动态链接器自动检测
- 标准库 File.cay 和 Network.cay 的稳定性问题
报告新问题
如果在使用 5.1.0 时遇到本文档未记录的问题,请通过以下方式报告:
- 确认问题可复现
- 提供最小复现示例
- 说明运行环境(OS、LLVM 版本、Rust 版本)
- 提交到项目 Issue 跟踪系统
快速开始
安装
推荐安装
Windows 用户只需从 Cavvy Releases
下载 cay-setup-windows-x86_64.exe 并运行:
.\cay-setup-windows-x86_64.exe
安装器默认将版本化工具链安装到 ~/.cavvy/toolchains/<版本>,自动组合 Cavvy 主体、
匹配版本的 LLVM minimal 和链接库,完整校验后原子切换用户 PATH。旧版本目录会保留,
避免更新中途留下混合工具链。不要求 Rust、Python、Git、系统 LLVM、MinGW 或 7-Zip。
重新打开终端后运行:
cayc --version
cay-setup doctor
常用管理命令:
cay-setup update
cay-setup show
cay-setup uninstall
当前 Release 未提供 Linux 安装器。Linux 用户需直接解压 Release 中的
cavvy-<版本>-linux-x86_64.tar.xz,再将对应 LLVM 版本的 bin-linux.tar.xz
解压到 llvm-minimal/bin-linux,最后把 Cavvy 解压目录加入 PATH。
从源码构建
# 1. 克隆仓库
git clone https://github.com/cavvy-lang/Cavvy.git
cd Cavvy
# 2. 安装工具链依赖(如缺失)
python setup-llvm.py
# 3. 构建 release 版本
cargo build --release
# 4. 验证安装
.\target\release\cayc.exe --version
# 独立构建安装器(不需要 LLVM)
cargo build --release -p cay-setup
注意:
release是正常构建模式,仅在 release 模式下才会复制捆绑工具链。debug 构建用于开发,不包含捆绑工具。build.rs在构建时将llvm-minimal/、mingw-minimal/、lib/复制到target/<profile>/。.cargo/config.toml仅包含 linux-musl 交叉编译配置,Windows 上可忽略。
构建产物位于 target/release/ 目录(详见 CLI 文档)。
Hello World
创建一个文件 hello.cay:
public int main() {
println("Hello, Cavvy!");
return 0;
}
编译并运行:
.\target\release\cayc.exe hello.cay
.\hello.exe
或一步到位:
.\target\release\cay-run.exe hello.cay
基本工作流程
Cavvy 编译器工具链提供多种编译模式:
# 完整编译:.cay → .exe
cayc input.cay -o output.exe
# 仅生成 LLVM IR(便于调试)
cay-ir input.cay -o output.ll
# 仅检查语法和语义(不生成代码)
cay-check input.cay
# IR → 可执行文件
ir2exe input.ll -o output.exe
# 预处理器输出
cay-pre input.cay -o output_preprocessed.cay
编译流水线(内部流程)
.cay 源码
→ 预处理器(#include, #define, #ifdef 展开)
→ 词法分析器(基于 logos 的分词)
→ 解析器(递归下降,生成 AST)
→ 语义分析(类型检查、符号解析)
→ IR 生成(自定义 SSA IR)
→ LLVM IR 文本生成
→ clang(捆绑)→ 原生机器码 .exe
详尽的架构说明见编译器架构文档。
第一个程序
class Calculator {
static int add(int a, int b) {
return a + b;
}
static int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
static void main() {
int x = 10;
int y = 20;
println("x + y = " + String.valueOf(add(x, y)));
println("factorial(5) = " + String.valueOf(factorial(5)));
// 字符串方法
string msg = "Hello, World!";
println("字符串长度: " + String.valueOf(msg.length()));
println("大写: " + msg.toUpperCase());
// 数组
int[] arr = new int[5];
for (int i = 0; i < 5; i = i + 1) {
arr[i] = i * i;
}
int sum = 0;
int j = 0;
while (j < 5) {
sum = sum + arr[j];
j = j + 1;
}
println("数组求和: " + String.valueOf(sum));
}
}
运行测试
# 必须先构建 release 版本(测试以子进程调用 release 编译器二进制文件)
cargo build --release
cargo test --release --verbose
测试分布在两个位置:
src/lib.rs— 少量内联的#[cfg(test)]单元测试(词法分析器、解析器、预处理器)tests/*.rs— 集成测试,编译examples/下的.cay文件并断言 stdout
集成测试会生成
temp_*.exe、temp_*.ll、temp_*.cay等临时文件,被 git 忽略但会在本地累积。
下一步
工具链与构建指南
构建编译器
本章面向从源码构建编译器的贡献者。普通用户应直接运行 Release 中的
cay-setup-windows-x86_64.exe;预编译安装不要求 Rust、Python 或系统 LLVM。
安装器本身是独立 workspace 包,可以在没有 LLVM 的环境中构建:
cargo build --release -p cay-setup
Release 是正常模式
测试、示例运行和日常使用都依赖 target/release 下的编译器二进制文件,因此日常构建使用 release:
cargo build --release
Debug 构建仅用于开发,不包含捆绑工具链(llvm-minimal/、mingw-minimal/、lib/):
cargo build
构建过程
build.rs 在构建时会:
- 读取
.verinfo获取版本号 - 将版本号与 git 提交哈希组合
- 设置
CARGO_*_VERSION环境变量用于编译时嵌入 - 将以下目录复制到
target/<profile>/:llvm-minimal/— LLVM/clang 工具链mingw-minimal/— MinGW 运行时lib/— 链接库caylibs/— 标准库examples/— 示例程序third-party/— 第三方依赖
工具链目录
如果 llvm-minimal/、mingw-minimal/ 或 lib/ 缺失(新克隆仓库),需要先下载:
python setup-llvm.py
此脚本从 GitHub cavvy-lang/Cavvy-src-Assets 下载 LLVM+MinGW 捆绑包,版本锁定信息从 .verinfo 读取。
交叉编译
.cargo/config.toml 包含 linux-musl 交叉编译配置。Windows 上可忽略此配置。
运行测试
# 必须先构建 release 版本(测试以子进程调用 release 编译器的二进制文件)
cargo build --release
cargo test --release --verbose
测试结构
测试分布在两个位置:
单元测试(src/lib.rs):
- 少量内联的
#[cfg(test)]单元测试 - 覆盖词法分析器、解析器、预处理器
集成测试(tests/*.rs):
- 调用 release 目录中的
cayc编译examples/下的.cay文件 - 运行生成的可执行文件并断言 stdout
- 使用全局
Mutex串行执行(避免临时文件冲突) - 辅助函数位于
tests/common/mod.rs:compile_and_run_eol()、compile_eol_expect_error()
单独运行特定测试
# 运行接口相关测试
cargo test --release --test interface_tests -- --nocapture
# 运行 Lambda 测试
cargo test --release --test lambda_tests -- --nocapture
# 运行继承测试
cargo test --release --test inheritance_tests -- --nocapture
临时文件
测试运行会在 tests/ 和 examples/ 目录中留下 temp_*.exe、temp_*.ll、temp_*.cay 等文件。这些文件被 git 忽略但会在本地累积,可随时清理。
版本管理
版本号存储在项目根目录的 .verinfo 文件中(类 INI 格式):
version=6.1.0
build.rs 解析此文件,将版本号与当前 git 提交哈希组合,通过环境变量注入编译二进制。修改 .verinfo 后执行 cargo build 会自动重新编译。
文档站
文档站使用 mdBook:
# 安装 mdBook
cargo install mdbook --locked
# 构建文档站
mdbook build
# 本地预览
mdbook serve --open
- 文档源文件位于
docs/目录 - 配置文件是
book.toml - 输出目录是
book/(不提交到 git)
文档测试
# 一键测试所有文档中的代码示例
.\scripts\test-docs.ps1
# 跨平台
python scripts/doc-test.py --build
scripts/doc-test.py 自动扫描 README.md 和 docs/**/*.md 中的代码块,抽取语言标记为 cay、cavvy、eol 的示例进行编译检查。
代码块标记
在文档中编写可测试的代码示例:
<!-- 仅语法检查 -->
```cay
class Example {
static void main() {
println("checked");
}
}
```
<!-- 编译并运行 -->
```cay run
public int main() {
println("runs");
}
```
<!-- 顶层 main -->
```cay
public int main() {
return 0;
}
```
CI 持续集成
夜间构建(.github/workflows/nb.yml)
- 每天 UTC 02:00 触发
- 运行在
windows-latest - 使用
stableRust 工具链 - 目标:
x86_64-pc-windows-gnu - 产物命名:
eol-*(历史遗留) - 可通过
skip_tests=true跳过测试
GitHub Pages(.github/workflows/jekyll-gh-pages.yml)
- 从
main分支部署文档到 GitHub Pages - 与代码变更无关
CLI 工具参考手册
Cavvy 6.1.0 提供 16 个 CLI 入口;其中 cay-setup 是可独立构建和发布的工具链安装器,不依赖主编译器的 LLVM 构建环境。
总览
| 二进制文件 | 功能 | 源文件 |
|---|---|---|
cayc | 一站式编译器:.cay → .exe | src/bin/cayc.rs |
cay-ir | 仅生成 LLVM IR(.cay → .ll) | src/bin/cay-ir.rs |
ir2exe | LLVM IR → 可执行文件(.ll → .exe) | src/bin/ir2exe.rs |
cay-check | 仅语法 + 语义检查 | src/bin/cay-check.rs |
cay-run | 编译 + 运行一步完成 | src/bin/cay-run.rs |
cay-rcpl | 交互式编程环境 | src/bin/cay-rcpl.rs |
cay-bcgen | CayBC 字节码生成 | src/bin/cay-bcgen.rs |
cay-lsp | LSP 语言服务器 | src/bin/cay-lsp.rs |
cavly | 包管理器 | src/bin/cavly.rs |
cay-dt | Token显示工具 | src/bin/cay-dt.rs |
cay-dp | Parser显示工具 | src/bin/cay-dp.rs |
cay-pre | 独立预处理器 | src/bin/cay-pre.rs |
cay-ast | AST 查看与 JSON 导出 | src/bin/cay-ast.rs |
cay-pl | 预处理结果查看 | src/bin/cay-pl.rs |
cay-sir | 语义 IR 查看 | src/bin/cay-sir.rs |
cay-setup | 安装、更新、检查和卸载工具链 | cay-setup/src/main.rs |
cay-setup — 工具链安装器
直接运行且不传参数时,安装最新稳定版:
cay-setup
常用管理命令:
cay-setup install --version 6.1.0
cay-setup update
cay-setup show
cay-setup doctor
cay-setup uninstall
自动化环境使用 --yes 跳过确认,使用 --no-modify-path 禁止修改用户 PATH。可以通过
CAVVY_HOME 或 --root 更改默认的 ~/.cavvy 安装根目录;show、doctor 和
uninstall 同样接受 --root。doctor 会实际编译一个最小程序,以验证 LLVM 后端和
链接库,而不只是打印版本号。每个版本安装到 ~/.cavvy/toolchains/<版本>,完成校验后
才切换 PATH;管理器来自 Release 的独立 cay-setup-<平台>-<架构> 资产。
1. cayc — 一站式编译器
将 .cay 源文件直接编译为可执行文件。
cayc <input.cay> [选项]
选项:
| 选项 | 描述 |
|---|---|
-o <file> | 指定输出文件路径 |
-O0 / -O1 / -O2 / -O3 | 优化级别(默认 -O0) |
--emit-llvm | 同时保留 .ll 文件 |
--verbose | 显示详细编译日志 |
--stage <stage> | 只运行到指定阶段 |
--target <triple> | 目标三元组 |
--print-stages | 打印编译流水线阶段并退出 |
-I <dir> | 添加包含路径 |
-D <macro> | 预定义宏 |
-h / --help | 显示帮助 |
示例:
cayc hello.cay
cayc hello.cay -o hello.exe
cayc hello.cay --emit-llvm -O2
cayc hello.cay -I ./include -D DEBUG
2. cay-ir — LLVM IR 生成器
从 .cay 源文件生成 LLVM IR 文本文件(.ll),不进行后续编译。
cay-ir <input.cay> [选项]
选项:
| 选项 | 描述 |
|---|---|
-o <file> | 输出 .ll 文件路径 |
--stdout | 输出到标准输出 |
-O0 / -O1 / -O2 / -O3 | 优化级别 |
-I <dir> | 添加包含路径 |
--verbose | 显示详细日志 |
示例:
cay-ir input.cay -o output.ll
cay-ir input.cay --stdout # 直接查看生成的 IR
cay-ir input.cay -O2 -o optimized.ll
3. ir2exe — LLVM IR → 可执行文件
将 LLVM IR 文本文件编译为可执行文件。
ir2exe <input.ll> [选项]
选项:
| 选项 | 描述 |
|---|---|
-o <file> | 输出可执行文件路径 |
-O0 / -O1 / -O2 / -O3 | 优化级别 |
--verbose | 显示详细日志 |
示例:
ir2exe output.ll -o program.exe
ir2exe output.ll -O2 -o optimized.exe
4. cay-check — 语法和语义检查
仅执行编译流水线的前端(预处理 → 词法分析 → 解析 → 语义分析),不生成代码。用于快速验证源文件的正确性。
cay-check <input.cay> [选项]
选项:
| 选项 | 描述 |
|---|---|
-I <dir> | 添加包含路径 |
--verbose | 显示详细日志 |
退出码:
0— 源文件正确1— 存在编译错误
示例:
cay-check source.cay
cay-check source.cay -I ./include
5. cay-run — 编译并运行
编译源代码并直接运行生成的可执行文件。
cay-run <input.cay> [程序参数...]
选项:
| 选项 | 描述 |
|---|---|
-I <dir> | 添加包含路径 |
--verbose | 显示详细日志 |
所有非选项参数会传递给生成的可执行文件。
示例:
cay-run hello.cay
cay-run program.cay arg1 arg2 arg3
6. cay-rcpl — 交互式编程环境
启动交互式 REPL 环境,支持逐行输入和执行 Cavvy 代码。
cay-rcpl [选项]
选项:
| 选项 | 描述 |
|---|---|
-I <dir> | 添加包含路径 |
--verbose | 显示详细日志 |
支持的命令:
| 命令 | 描述 |
|---|---|
| 任意表达式 | 计算并输出结果 |
| 变量声明 | 在会话上下文中持久化 |
| 类/接口定义 | 实时定义新类型 |
| 控制流语句 | 即时执行 |
#include | 导入文件 |
:exit / :quit | 退出 RCPL |
:help | 显示帮助 |
示例:
> cay-rcpl
Cavvy RCPL v6.1.0
> int x = 42
> x * 2
84
> println("Hello from RCPL!")
Hello from RCPL!
> :exit
7. cay-bcgen — 字节码生成器
将 .cay 源文件编译为 CayBC 字节码。
cay-bcgen <input.cay> [选项]
选项:
| 选项 | 描述 |
|---|---|
-o <file> | 输出 .caybc 文件路径 |
--obfuscate | 启用字节码混淆 |
--obfuscation-level <0-3> | 混淆级别 |
-I <dir> | 添加包含路径 |
--verbose | 显示详细日志 |
示例:
cay-bcgen input.cay -o output.caybc
cay-bcgen input.cay --obfuscate -o obfuscated.caybc
8. cay-lsp — LSP 语言服务器
启动 LSP 协议语言服务器,与支持 LSP 的编辑器(如 VS Code)配合使用。
cay-lsp
选项:无命令行选项(通过 LSP 协议通信)。
编辑器配置(VS Code 扩展位于 vscode-extension/):
工具链中包含 VS Code 扩展,提供:
- 语法高亮
- 自动补全
- 诊断信息(错误和警告)
- 跳转到定义
- 悬停信息
9. cavly — 包管理器
完整的包管理工具,用于创建、构建和管理 Cavvy 项目。
cavly <子命令> [选项]
子命令:
| 子命令 | 描述 |
|---|---|
new <name> | 创建新项目 |
init | 在当前目录初始化项目 |
build | 构建项目 |
run | 构建并运行 |
test | 运行测试 |
clean | 清理构建产物 |
add <dependency> | 添加依赖 |
remove <dependency> | 移除依赖 |
publish | 发布包 |
install | 安装依赖 |
workspace | 工作区管理 |
help | 显示帮助 |
示例:
cavly new my-project
cd my-project
cavly add some-lib
cavly build
cavly run
cavly test
详见 Cavly 文档。
10. cay-dt — 文档工具
从源码注释生成文档。
cay-dt <input.cay> [选项]
选项:
| 选项 | 描述 |
|---|---|
-o <dir> | 输出目录 |
--format <fmt> | 输出格式(html / markdown) |
-I <dir> | 添加包含路径 |
--verbose | 显示详细日志 |
11. cay-dp — 依赖分析工具
分析项目的依赖关系图。
cay-dp <input.cay> [选项]
选项:
| 选项 | 描述 |
|---|---|
--graph | 输出 DOT 格式的依赖图 |
--json | 输出 JSON 格式 |
-I <dir> | 添加包含路径 |
--verbose | 显示详细日志 |
12. cay-pre — 独立预处理器
仅执行预处理阶段,输出预处理后的源代码。
cay-pre <input.cay> [选项]
选项:
| 选项 | 描述 |
|---|---|
-o <file> | 输出文件 |
--stdout | 输出到标准输出 |
-I <dir> | 添加包含路径 |
-D <macro> | 预定义宏 |
--keep-comments | 保留注释 |
--verbose | 显示详细信息 |
示例:
cay-pre input.cay -o output_preprocessed.cay
cay-pre input.cay --stdout | grep "MAIN"
cay-pre input.cay -D DEBUG -D VERSION=2
通用行为
错误报告
所有工具使用 miette 进行格式化错误输出,提供:
- 彩色源码片段
- 错误位置标注
- 详细的错误描述和建议
退出码
| 退出码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 编译错误 |
| 2 | 运行时错误 |
| 3 | 文件未找到 |
| 4 | 内部错误(应报告为 bug) |
环境变量
| 变量 | 描述 |
|---|---|
CAVVC_PATH | cayc 编译器路径(用于测试) |
CAVVY_HOME | Cavvy 安装目录 |
CAVVY_LIB_PATH | 标准库路径 |
CAVVY_LLVM_PATH | LLVM 工具链路径 |
语言概述
Cavvy(Cay)是一门静态类型、面向对象的编程语言,语法风格接近 Java/C#,同时保留 C 风格预处理器和 FFI。本文档从宏观层面介绍语言的核心特性。
核心设计理念
- 静态类型安全:所有变量、参数、返回值在编译时具有确定类型
- 面向对象:支持类、继承、接口、运行时多态(vtable 方法分发)
- C 风格预处理:支持
#include、#define、#ifdef等指令 - 原生编译:通过 LLVM IR → clang 编译为高效机器码
- FFI 优先:内建外部函数接口,直接调用 C 库
- 渐进式支持:CayBC 字节码格式支持 JIT/AOT 执行
类型系统
基本类型
| 类型 | 描述 | 默认值 |
|---|---|---|
int | 32 位有符号整数 | 0 |
long | 64 位有符号整数 | 0L |
float | 32 位浮点数 | 0.0f |
double | 64 位浮点数 | 0.0 |
char | 16 位 Unicode 字符 | ‘\0’ |
bool | 布尔值 | false |
string | 不可变字符串 | null |
void | 无返回值 | — |
复合类型
| 类型 | 示例 | 说明 |
|---|---|---|
| 数组 | int[]、string[] | 动态数组,new 分配 |
| 类 | class Foo | 引用类型,堆分配 |
| 接口 | interface Bar | 纯抽象类型 |
| 结构体 | struct Point | 值类型,栈分配 |
| 枚举 | enum Color | 命名常量集合 |
| 函数指针 | fn(int) -> int | 函数签名类型 |
类与面向对象
类定义
public class Counter {
private int current;
public Counter(int start) {
this.current = start;
}
public void add(int value) {
this.current = this.current + value;
}
public int value() {
return this.current;
}
}
继承与方法重写
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void speak() {
println("...");
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void speak() {
println(this.name + " says: 汪汪!");
}
}
接口与多态
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void speak() {
println("...");
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void speak() {
println(this.name + " says: 汪汪!");
}
}
public interface Flyable {
void fly();
void land();
}
public class Bird extends Animal implements Flyable {
public Bird(String name) {
super(name);
}
@Override
public void speak() {
println(this.name + " says: 啾啾!");
}
public void fly() {
println(this.name + " is flying");
}
public void land() {
println(this.name + " landed");
}
}
public class Main {
public static void main() {
// 运行时多态(通过 vtable 分发)
Animal a1 = new Dog("Buddy");
a1.speak(); // 输出: Buddy says: 汪汪!
}
}
构造函数与析构函数
public class Resource {
int* data;
public Resource() {
data = new int[1024];
}
~Resource() {
delete[] data;
}
}
控制流
条件
class Main {
public static void main() {
int x = 5;
if (x > 0) {
println("正数");
} else if (x == 0) {
println("零");
} else {
println("负数");
}
}
}
循环
class Main {
public static void main() {
// while 循环
int i = 0;
while (i < 5) {
println(String.valueOf(i));
i = i + 1;
}
// for 循环(C 风格)
for (int j = 0; j < 5; j = j + 1) {
println(String.valueOf(j));
}
// do-while 循环
int k = 0;
do {
println(String.valueOf(k));
k = k + 1;
} while (k < 5);
}
}
switch 语句
class Main {
public static void main() {
int value = 2;
switch (value) {
case 1:
println("one");
break;
case 2:
println("two");
break;
default:
println("other");
}
}
}
数组
class Main {
public static void main() {
// 声明并分配
int[] arr = new int[10];
arr[0] = 42;
// 数组长度
int len = arr.length();
println(String.valueOf(len));
// 字符串数组
string[] names = new string[5];
names[0] = "Alice";
println(names[0]);
}
}
字符串
内置字符串方法和运算符重载:
class Main {
public static void main() {
string s = "Hello, Cavvy!";
int len = s.length();
string upper = s.toUpperCase();
string sub = s.substring(0, 5);
bool has = s.contains("Cavvy");
println(String.valueOf(len));
println(upper);
}
}
Lambda 表达式
class Main {
public static void main() {
// Lambda 语法(已解析,闭包捕获环境变量尚未完整实现)
var func = (int x, int y) -> x + y;
int result = func(3, 4);
println(String.valueOf(result));
}
}
泛型
public class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T get() {
return this.value;
}
}
public class Main {
public static void main() {
Box<int> box = new Box<int>(7);
int val = box.get();
println(String.valueOf(val));
}
}
注意:泛型语法已解析,但代码生成尚未实现单态化。
Struct 与 Enum
struct Point {
int x;
int y;
int sum() {
return x + y;
}
}
enum Status {
Ready,
Running,
Done
}
class Main {
static void main() {
Point p = new Point();
p.x = 2;
p.y = 5;
Status status = Status.Done;
switch (status) {
case Status.Done: println("完成"); break;
default: println("等待"); break;
}
}
}
预处理器
完整的 C 风格预处理器:
#define MAX_SIZE 1024
#define SQUARE(x) ((x) * (x))
public class Main {
public static void main() {
int size = MAX_SIZE;
println(String.valueOf(size));
}
}
详情见预处理器文档。
FFI 外部函数接口
直接调用 C 标准库:
#include "std/ffi.cay"
public class Main {
public static void main() {
printf("Hello from C! %d\n", 42);
}
}
详情见 FFI 文档。
完整示例
综合展示语言主要特性:
#include "math.cay"
interface Shape {
double area();
double perimeter();
}
class Circle implements Shape {
double radius;
Circle(double r) {
radius = r;
}
double area() {
return PI * radius * radius;
}
double perimeter() {
return 2.0 * PI * radius;
}
}
class Rectangle implements Shape {
double width;
double height;
Rectangle(double w, double h) {
width = w;
height = h;
}
double area() {
return width * height;
}
double perimeter() {
return 2.0 * (width + height);
}
}
class Main {
static void printInfo(Shape s) {
println("面积: " + s.area());
println("周长: " + s.perimeter());
}
static void main() {
Shape[] shapes = new Shape[2];
shapes[0] = new Circle(5.0);
shapes[1] = new Rectangle(3.0, 4.0);
for (int i = 0; i < shapes.length(); i = i + 1) {
printInfo(shapes[i]);
}
}
}
语言参考手册
本文档是 Cavvy(Cay)编程语言的完整语法和语义参考。
版本说明:本文档按 6.1.0 语义维护;5.2.0~5.4.0 的迁移背景见版本演进总览。
词法结构
注释
// 单行注释
/* 多行
注释 */
关键字
class, interface, struct, enum, extends, implements
public, private, protected, static, final, abstract, native
if, else, switch, case, default, for, while, do, break, continue, return
new, delete, this, super, instanceof, typeof
true, false, null
var, void, int, long, float, double, bool, boolean, char, string
try, catch, finally, throw
namespace, using, import, extern, alias, typedef
virtual, override, synchronized, const, mutable, volatile
字面量
public class Main {
public static void main() {
int a = 42;
int b = 0xFF;
int c = 0b1010;
double d = 3.14;
float e = 3.14f;
char f = 'A';
string g = "hello";
bool h = true;
println(String.valueOf(a + b + c));
println(g);
}
}
类型系统
基本类型
| 类型 | 位数 | 描述 | 默认值 |
|---|---|---|---|
void | 0 | 无返回值 | — |
int / i32 | 32 | 有符号整数 | 0 |
long / i64 | 64 | 有符号整数 | 0L |
float / f32 | 32 | IEEE 754 浮点数 | 0.0f |
double / f64 | 64 | IEEE 754 浮点数 | 0.0 |
bool / boolean | 8 | 布尔值 | false |
char | 16 | UTF-16 字符 | ‘\0’ |
string / String | — | 不可变字符串 | null |
注意:
i32、i64、f32、f64是类型关键字的别名,不能用作标识符(如变量名、类型别名名)。例如alias i32 = int;是非法的,应使用alias MyInt = int;。
复合类型
| 类型 | 语法 | 说明 |
|---|---|---|
| 数组 | T[] | 动态长度数组 |
| 指针 | T* | FFI 使用的 C 风格指针 |
| 函数指针 | fn(T1, T2) -> R | 函数签名类型 |
| 类引用 | ClassName | 堆分配的对象引用 |
| 接口引用 | InterfaceName | 运行时多态引用 |
FFI 类型
#include "std/ffi.cay"
public class Main {
public static void main() {
// FFI 类型: c_char, c_uchar, c_short, c_ushort
// c_int, c_uint, c_long, c_ulong
// c_float, c_double, c_void, c_bool
// size_t, ssize_t, uintptr_t, intptr_t
// c_string — C 风格字符串 (char*)
c_string msg = "Hello";
println("ffi types ok");
}
}
alias
创建类型别名:
alias MyInt = int;
class Main {
public static void main() {
MyInt a = 1;
print(a);
}
}
Result 与错误传播(6.1.0)
Result<T, E> 表示成功值或错误值,两个类型参数都必须显式给出:
Result<int, String> ok = Result<int, String>.ok(42);
Result<int, String> failed = Result<int, String>.err("invalid input");
if (ok.isOk()) {
println(String.valueOf(ok.unwrap()));
}
int fallback = failed.unwrapOr(0);
可用操作包括 ok、err、isOk、isErr、unwrap、unwrapOr、unwrapErr 和 expect。expect、unwrap 和 unwrapErr 不应替代可恢复错误分支。
函数返回兼容的 Result 时,可以使用 ? 传播错误:
public Result<int, String> readValue() {
Result<int, String> value = Result<int, String>.ok(7);
int number = value?;
return Result<int, String>.ok(number + 1);
}
智能指针与 RAII(5.3.0)
标准库提供四种所有权模型:UniquePtr<T>(独占且可转移)、ScopedPtr<T>(作用域独占)、Rc<T>(共享引用计数)和 WeakPtr<T>(弱引用)。作用域退出时,编译器会为受支持对象注入析构调用。
UniquePtr<Node> node = UniquePtr<Node>.fromRaw(new Node());
Node borrowed = node.get();
Node owned = node.release();
release() 会放弃托管权,调用方随后负责对象生命周期;不要在智能指针仍持有对象时手动释放同一对象。
内存映射文件(5.4.0)
Mmap 支持 Windows 和 Linux 的只读/读写映射,MmapSlice 是零拷贝视图:
MmapResult<Mmap> result = Mmap.mapReadOnly("data.bin");
if (result.isOk()) {
Mmap mapped = result.unwrap();
MmapSlice bytes = mapped.slice(0, mapped.size());
// 使用 bytes 后再 unmap mapped
mapped.unmap();
}
写映射完成后调用 sync()。映射失败、偏移越界以及 unmap() 后继续使用切片都是错误情况,应由调用方处理。
顶层声明
源文件的顶层允许以下声明:
class, struct, enum, interface
extern block
namespace { ... }
using path::Name;
alias type = existing_type;
#include (预处理指令)
顶层函数(`public int main()`)
访问修饰符
| 修饰符 | 描述 |
|---|---|
public | 任何地方可访问 |
private | 仅当前类内部访问 |
protected | 类及其子类可访问 |
| (无修饰符) | 包/模块内部访问 |
注意:
private访问修饰符在语义分析中已定义,但编译器尚未强制执行 private 访问控制。
其他修饰符
| 修饰符 | 用途 |
|---|---|
static | 静态成员(属于类而非实例) |
final | 类:禁止继承;方法:禁止重写 |
abstract | 抽象类(不可实例化)或抽象方法 |
native | native 方法声明(由 FFI 实现) |
virtual | 可被重写的虚方法 |
override | 重写父类方法 |
类
类定义
class ClassName {
// 字段
// 方法
// 构造函数
// 析构函数
}
构造函数
class Point {
int x;
int y;
// 无参构造函数
Point() {
x = 0;
y = 0;
}
// 带参构造函数
Point(int x, int y) {
this.x = x;
this.y = y;
}
// 委托构造函数
Point(int val) : this(val, val) {}
}
析构函数
class Resource {
int* data;
Resource() {
data = new int[1024];
}
~Resource() {
delete[] data;
}
}
继承
public class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void speak() {
println("...");
}
}
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void speak() {
super.speak();
println("汪汪!");
}
}
抽象类
public abstract class Shape {
public abstract double area();
public abstract double perimeter();
}
public class Circle extends Shape {
public double radius;
public Circle(double r) { radius = r; }
@Override
public double area() {
return 3.14159 * radius * radius;
}
@Override
public double perimeter() {
return 2.0 * 3.14159 * radius;
}
}
public class Main {
public static void main() {
Circle c = new Circle(5.0);
println(String.valueOf(c.area()));
}
}
接口
public interface Flyable {
void fly();
void land();
}
public class Bird implements Flyable {
public void fly() { println("Flying..."); }
public void land() { println("Landing..."); }
}
public class Main {
public static void main() {
Flyable f = new Bird();
f.fly();
}
}
关键实现细节:
- 接口调用通过对象 vtable 运行时分发
- 支持多个实现类共享同一接口类型
a1.speak()按运行时类型调用正确的方法实现
结构体
值类型,栈分配:
public struct Point {
public int x;
public int y;
public int sum() {
return x + y;
}
}
public class Main {
public static void main() {
Point p = new Point();
p.x = 10;
p.y = 20;
int s = p.sum();
println(String.valueOf(s));
}
}
枚举
public enum Color {
Red,
Green,
Blue
}
public class Main {
public static void main() {
Color c = Color.Red;
println("enum ok");
}
}
Namespace
class Main {
public static void main() {
println("namespace organizes code");
}
}
泛型
public class Box<T> {
private T value;
public Box(T value) {
this.value = value;
}
public T get() {
return this.value;
}
}
public class Main {
public static void main() {
Box<int> intBox = new Box<int>(42);
println(String.valueOf(intBox.get()));
}
}
注意:泛型语法已解析,但代码生成尚未实现单态化。泛型类可以在代码中编写,但尚不能正确编译为机器码。
Lambda 表达式
public class Main {
public static void main() {
var add = (int a, int b) -> a + b;
var funcPtr = add;
int r = funcPtr(3, 4);
println(String.valueOf(r));
}
}
注意:Lambda 闭包捕获环境变量尚未完整实现。
数组
一维数组
public class Main {
public static void main() {
int[] arr = new int[10];
arr[0] = 42;
int len = arr.length();
println(String.valueOf(len));
}
}
数组初始化
public class Main {
public static void main() {
int[] arr = new int[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
println(String.valueOf(arr[0] + arr[1] + arr[2]));
}
}
字符串
字符串方法
public class Main {
public static void main() {
string s = "Hello, Cavvy!";
int len = s.length();
string up = s.toUpperCase();
println(String.valueOf(len));
println(up);
}
}
字符串连接
public class Main {
public static void main() {
string name = "Cavvy";
string greeting = "Hello, " + name + "!";
println(greeting);
}
}
控制流
if-else
public class Main {
public static void main() {
int x = 5;
if (x > 0) {
println("positive");
} else if (x == 0) {
println("zero");
} else {
println("negative");
}
}
}
switch
public class Main {
public static void main() {
int value = 2;
switch (value) {
case 1:
println("one");
break;
case 2:
case 3:
println("two or three");
break;
default:
println("other");
break;
}
}
}
for 循环
public class Main {
public static void main() {
for (int i = 0; i < 3; i = i + 1) {
println(String.valueOf(i));
}
}
}
while 循环
public class Main {
public static void main() {
int i = 0;
while (i < 3) {
println(String.valueOf(i));
i = i + 1;
}
}
}
do-while 循环
public class Main {
public static void main() {
int i = 0;
do {
println(String.valueOf(i));
i = i + 1;
} while (i < 3);
}
}
跳转语句
public class Main {
public static void main() {
for (int i = 0; i < 5; i = i + 1) {
if (i == 0) continue;
if (i == 3) break;
println(String.valueOf(i));
}
}
}
异常处理
public class Main {
public static void main() {
println("exception handling with try/catch/finally");
}
}
注解
public class Main {
@FreeFunction
public String toString() {
return "annotations demo";
}
public static void main() {
println(toString());
}
}
预处理器指令
详见预处理器文档。
#include <File.cay>
#define MACRO value
#ifdef CONDITION
#endif
#pragma once
// #error "message"
内联 IR
__ir { ... } 可在函数或方法体内插入 LLVM IR 指令,包括 public/private、static/instance 方法以及顶层函数。private 方法同样允许使用内联 IR;这不会收窄其他可用场景。
public class Main {
private static int addOne(int x) {
int result;
__ir {
%sum = add i32 %x, 1
store i32 %sum, i32* %result
}
return result;
}
}
内联 IR 可以引用当前作用域中的 Cavvy 变量,形式为 %变量名,也可以用 %0、%1 等按参数和局部变量顺序引用。该功能面向底层库和性能敏感代码,使用者需要保证 IR 类型和控制流正确。
FFI extern 声明
详见 FFI 文档。
extern {
void* malloc(size_t size);
void free(void* ptr);
int printf(c_string fmt, ...);
}
// 调用约定指定
extern "stdcall" {
// ...
}
完整语法(EBNF)
完整的 Cavvy 形式化语法定义在项目根目录的 cavvy.ebnf 文件中,涵盖:
- 预处理器指令
- 类型表达式
- 类、接口、结构体、枚举定义
- 方法声明和语句
- 各类表达式
- 字面量和运算符优先级
预处理器指南
Cavvy 在词法分析前运行完整的 C 风格预处理器,并保留源映射(source map)用于诊断错误定位。
支持的指令
| 指令 | 语法 | 说明 |
|---|---|---|
| 文件包含 | #include "file" | 从当前文件所在目录搜索 |
| 系统包含 | #include <file> | 从系统包含路径搜索 |
| 常量宏 | #define NAME value | 定义常量宏 |
| 条件定义 | #ifdef NAME | 如果宏已定义 |
| 条件未定义 | #ifndef NAME | 如果宏未定义 |
| 条件表达式 | #if expr | 整数常量表达式 |
| 否则条件 | #elif expr | else-if 条件 |
| 否则 | #else | 条件分支的 else 部分 |
| 结束条件 | #endif | 结束条件编译块 |
| 编译错误 | #error "msg" | 触发编译错误 |
| 编译警告 | #warning "msg" | 触发编译警告 |
| 头文件保护 | #pragma once | 防止重复包含 |
| 行标记 | #line number "file" | 重置行号和文件名 |
文件包含
// 当前目录搜索
#include "helper.cay"
#include "math/vector.cay"
// 系统包含路径搜索
#include <stdio.cay>
#include <math.cay>
// 嵌套包含自动去重
#include "helper.cay" // 已包含,自动跳过
搜索路径顺序
- 相对于当前源文件所在目录(
""形式) - 命令行
-I选项指定的路径 caylibs/(当前工作目录或 release 目录旁的复制)- 内置的系统包含路径(
<>形式)
# 通过 -I 添加额外包含路径
cayc app.cay -I./vendor -I./include
cay-pre source.cay -I./libs --stdout
宏定义
常量宏
#define MAX_SIZE 1024
#define APP_NAME "MyApp"
#define DEBUG 1
#define PI 3.1415926535
条件编译
#define PLATFORM_WIN
#define DEBUG
#ifdef DEBUG
#warning "调试模式已启用"
#endif
#ifndef RELEASE
#warning "非发布版本"
#endif
#if PLATFORM_WIN
// 包含 Windows 平台头文件
#elif PLATFORM_LINUX
// 包含 Linux 平台头文件
#else
#error "未知平台"
#endif
#if 表达式支持
- 整数常量计算(
+,-,*,/,%) - 比较运算(
==,!=,<,>,<=,>=) - 逻辑运算(
&&,||,!) - 位运算(
&,|,^,~,<<,>>) defined(NAME)操作符
#define VERSION 2
#if VERSION >= 2
// 版本 2 及以上特性
#endif
#if defined(DEBUG) && defined(VERBOSE)
println("详细调试输出");
#endif
实用技巧
头文件保护
// mylib.cay
#pragma once
#define MYLIB_VERSION 1
// ... 库内容,确保只展开一次 ...
### 平台检测
```cay
#if defined(_WIN32) || defined(_WIN64)
#define PATH_SEPARATOR "\\"
#else
#define PATH_SEPARATOR "/"
#endif
结合编译器使用
# 查看预处理后的输出
cay-pre source.cay --stdout
# 保存预处理结果
cay-pre source.cay -o preprocessed.cay
# 保留注释
cay-pre source.cay --keep-comments -o output.cay
# 在编译时定义宏
cayc source.cay -D DEBUG -D VERSION=2
# 发布构建
cayc source.cay -D RELEASE -O2
与标准 C 预处理器的差异
#pragma once— 支持,同时隐式执行包含去重#warning— 支持#include "/absolute/path"— 不支持绝对路径包含- 宏展开终止于递归 — 避免无限递归
- 源映射维护 — 所有预处理后的位置都映射回原始源位置,确保错误定位准确
FFI 外部函数接口
Cavvy 的 FFI(外部函数接口)允许直接调用 C ABI 函数。默认调用约定是 cdecl,也支持 stdcall、fastcall、sysv64、win64。
基本声明
使用 extern 块声明 C 函数:
extern {
int printf(c_string fmt, ...);
size_t strlen(c_string str);
}
然后在 Cavvy 代码中直接调用:
#include "std/ffi.cay"
class Main {
static void main() {
printf("Hello from C! %d\n", 42);
c_string msg = "Cavvy";
size_t len = strlen(msg);
printf("字符串长度: %d\n", len);
}
}
FFI 类型映射
基础类型
| C 类型 | Cavvy FFI 类型 | 说明 |
|---|---|---|
char | c_char | 8 位字符 |
unsigned char | c_uchar | 无符号 8 位 |
short | c_short | 16 位有符号整数 |
unsigned short | c_ushort | 16 位无符号整数 |
int | c_int | 32 位有符号整数 |
unsigned int | c_uint | 32 位无符号整数 |
long | c_long | 平台相关长度 |
unsigned long | c_ulong | 无符号长整数 |
long long | c_longlong | 64 位整数 |
float | c_float | 32 位浮点数 |
double | c_double | 64 位浮点数 |
void | c_void / void | 无返回值或 void* |
char* | c_string | C 风格字符串 |
size_t | size_t | 大小类型 |
ssize_t | ssize_t | 有符号大小类型 |
bool | c_bool | C 布尔值 |
intptr_t | intptr_t | 指针宽度整数 |
uintptr_t | uintptr_t | 无符号指针宽度整数 |
调用约定
// 默认 cdecl
extern {
int add(int a, int b);
}
// 指定调用约定
extern "stdcall" {
int win32_api(int param);
}
extern "fastcall" {
int fast_func(int a, int b);
}
extern "sysv64" {
int linux_syscall(int code);
}
extern "win64" {
int windows_x64_func(int param);
}
函数别名
当 C 函数名与 Cavvy 命名冲突时,使用 as 语法:
extern {
// C 的 sqrt 在 Cavvy 以 c_sqrt 访问
c_double sqrt(c_double x) as c_sqrt;
// 避免关键字冲突
int open(c_string path, int flags) as c_open;
}
链接外部库
使用 -l 和 -L 链接外部库:
# 链接数学库
cayc app.cay -lm
# 链接自定义库
cayc app.cay -L./native -lmyffi
# 链接多个库
cayc app.cay -lssl -lcrypto
Windows 下编译器会在检测到 socket API 时自动链接 ws2_32。
标准库 FFI 封装
推荐使用标准库中已封装的 FFI:
#include "std/ffi.cay"
#include <File.cay>
#include <Math.cay>
完整示例
#include "std/ffi.cay"
extern {
int printf(c_string fmt, ...);
int rand();
void srand(c_uint seed);
}
class Main {
static void main() {
srand(42);
int r = rand();
printf("随机数: %d\n", r);
}
}
注意事项
- 类型安全:FFI 调用不进行类型安全检查,错误声明可能造成崩溃
- 指针管理:C 堆内存(
malloc/free)不受 Cavvy GC 管理 - 字符串区别:
c_string(char*)与string为不同类型 - 调用约定:不同平台需要正确的调用约定,否则栈可能损坏
- 头文件路径:通过
-I添加 FFI 头文件搜索路径
Cavly 包管理器
cavly 是 Cavvy 的包管理器和项目构建工具。它负责初始化项目、解析依赖、构建二进制目标、运行测试和配置 FFI 库。
Cavly 的实现位于 src/cavly/,包含 6 个模块:
mod.rs— 入口和命令行解析config.rs— 配置类型(PackageConfig、BuildConfig、FfiConfig、Dependency、WorkspaceConfig、LibConfig)builder.rs— 构建状态机,依赖解析和拓扑排序project.rs— 项目创建和模板生成ffi.rs— FFI 库检测和绑定生成tester.rs— 测试运行器workspace.rs— 工作区管理
常用命令
# 创建新项目
cavly new my-project
cavly new --lib my-library
# 在当前目录初始化
cavly init
cavly init --lib
# 构建
cavly build
cavly build --bin app
cavly build --release
# 运行
cavly run
cavly run -- "arg1" "arg2"
# 测试
cavly test
cavly test --verbose
# 依赖管理
cavly add some-lib
cavly add [email protected]
cavly remove some-lib
cavly install # 安装所有依赖
# FFI 配置
cavly ffi sdl2 SDL2 # 配置 SDL2 FFI 绑定
# 工作区
cavly workspace init
# 清理
cavly clean
# 发布
cavly publish
项目结构
Cavly 通过向上查找 cavly.toml 识别项目根目录。
默认项目布局
my-project/
├── cavly.toml # 项目配置文件
├── src/
│ ├── main.cay # 主入口
│ └── lib.cay # 库入口(可选)
├── caylibs/ # 项目级标准库
├── tests/ # 测试文件
├── examples/ # 示例文件
├── ffi/ # FFI 绑定
└── target/ # 构建产物
配置文件(cavly.toml)
[package]
name = "my-project"
version = "0.1.0"
edition = "2024"
description = "我的 Cavvy 项目"
[build]
target = "bin" # bin | lib | both
optimization = 2 # 0-3
[dependencies]
some-lib = "1.0.0"
another = { git = "https://...", branch = "main" }
[ffi]
sdl2 = { libs = ["SDL2"], include = ["SDL2/SDL.h"] }
[workspace]
members = ["sub-project1", "sub-project2"]
依赖管理系统
- 语义化版本:支持
^1.0.0、~1.0.0、>=1.0.0等范围 - 依赖解析:拓扑排序解决依赖图
- git 依赖:直接从 git 仓库拉取
- 工作区:多项目共享依赖配置
构建流程
cavly build
→ 读取 cavly.toml
→ 解析依赖关系(拓扑排序)
→ 编译依赖库
→ 编译主项目
→ 链接 FFI 库
→ 输出可执行文件或库
builder.rs 实现完整的构建状态机,处理依赖顺序和并行构建机会。
项目模板
cavly new 和 cavly init 从 project.rs 中的模板生成项目骨架:
// src/main.cay(默认模板)
class Main {
static void main() {
println("Hello from Cavvy!");
}
}
Cavvy 标准库参考手册
Cavvy 标准库提供全面的系统编程能力,涵盖内存管理、字符串处理、文件 I/O、网络通信、数学计算等核心功能。
目录
核心类型与命名空间
所有标准库组件位于 std 命名空间下。通过 #include 引入:
#include <Allocator.cay>
#include <StringBuilder.cay>
#include <File.cay>
#include <Math.cay>
#include <std/vector.cay>
内存管理 (Allocator)
文件: caylibs/Allocator.cay
接口定义
public interface Allocator {
long allocate(long size); // O(1) 分配内存
long allocateAligned(long size, long alignBytes); // O(1) 对齐分配
void deallocate(long ptr); // O(1) 释放内存
}
GlobalAlloc - 全局堆分配器
基于 C 标准库 malloc/free 的全局分配器。
public class GlobalAlloc implements Allocator {
public static GlobalAlloc getInstance(); // 获取单例
public long allocate(long size); // 调用 malloc
public long allocateAligned(long size, long alignBytes);
public void deallocate(long ptr); // 调用 free
}
使用示例:
#include <Allocator.cay>
class Main {
static void main() {
std::GlobalAlloc alloc = std::GlobalAlloc.getInstance();
long ptr = alloc.allocate(1024);
// 使用内存...
alloc.deallocate(ptr);
}
}
Arena - 线性分配器
适用于生命周期明确的批量内存分配场景。
public class Arena implements Allocator {
public static Arena create(long capacity); // O(1) 创建 Arena
public long allocate(long size); // O(1) 线性分配
public long allocateAligned(long size, long alignBytes);
public void deallocate(long ptr); // 空操作(批量释放)
public void reset(); // O(1) 重置分配器
public long used(); // O(1) 已用字节数
public long remaining(); // O(1) 剩余字节数
}
使用示例:
#include <Allocator.cay>
class Main {
static void main() {
std::Arena arena = std::Arena.create(1024 * 1024); // 1MB Arena
long buf = arena.allocate(256);
// 多次分配...
arena.reset(); // 一次性重置所有分配
}
}
ScopeAlloc - 作用域分配器
配合 scope 关键字使用,实现栈式内存管理。
public class ScopeAlloc implements Allocator {
public static ScopeAlloc create();
public void setMarker(long m);
public long getMarker();
}
便捷宏定义
#define GLOBAL_ALLOC GlobalAlloc.getInstance()
#define ARENA(capacity) Arena.create(capacity)
#define SCOPE_ALLOC ScopeAlloc.create()
字符串处理
StringBuilder
文件: caylibs/StringBuilder.cay
高效的可变字符串构建器,避免字符串拼接的 O(n²) 复杂度。
public class StringBuilder {
// 构造函数
public StringBuilder(); // 默认容量 16
public StringBuilder(int initialCapacity);
public StringBuilder(String str);
// 追加操作 (均返回 this 支持链式调用)
public StringBuilder append(String str); // O(n) 追加字符串
public StringBuilder append(char c); // O(1) 追加字符
public StringBuilder append(int n); // O(log n) 追加整数
public StringBuilder append(long n); // O(log n) 追加长整数
public StringBuilder append(boolean b); // O(1) 追加布尔值
public StringBuilder append(char[] chars); // O(n) 追加字符数组
public StringBuilder appendln(); // 追加换行
public StringBuilder appendln(String str); // 追加字符串+换行
// 查询操作
public int length(); // O(1) 当前长度
public int capacity(); // O(1) 缓冲区容量
public boolean isEmpty(); // O(1) 是否为空
public char charAt(int index); // O(1) 获取字符
// 修改操作
public StringBuilder clear(); // O(1) 清空
public StringBuilder delete(int start, int end); // O(n) 删除范围
public StringBuilder insert(int offset, String str); // O(n) 插入
public StringBuilder reverse(); // O(n) 反转
public void setLength(int newLength); // O(n) 设置长度
// 转换操作
public String toString(); // O(n) 转为字符串
public String substring(int start); // O(n) 子串
public String substring(int start, int end); // O(n) 子串范围
public int indexOf(String str); // O(n*m) 查找
public StringBuilder replace(String target, String replacement); // O(n*m)
// FFI 支持
public long c_str(); // 获取 C 字符串指针
public static StringBuilder fromCString(long ptr); // 从 C 字符串创建
}
使用示例:
#include <StringBuilder.cay>
class Main {
static void main() {
std::StringBuilder sb = new std::StringBuilder();
sb.append("Hello").append(", ").append("World").appendln("!");
sb.append("Count: ").append(42);
String result = sb.toString();
println(result); // "Hello, World!\nCount: 42"
}
}
StringPlus
文件: caylibs/StringPlus.cay
字符串增强工具类。
public class StringPlus {
// 分割操作
public static String[] split(String str); // O(n) 按空格分割
public static String[] split(String str, String delimiter); // O(n*m)
// 格式化操作
public static String format(String template, String... args); // {} 占位符
public static String formatIndexed(String template, String... args); // {0}, {1} 占位符
}
使用示例:
#include <StringPlus.cay>
class Main {
static void main() {
String[] parts = std::StringPlus.split("a,b,c", ",");
String msg = std::StringPlus.format("Hello, {}! You have {} messages.", "Alice", "5");
String msg2 = std::StringPlus.formatIndexed("{0} + {1} = {2}", "1", "2", "3");
}
}
文件 I/O
文件: caylibs/File.cay
错误处理
public class FileError {
public static final int None = 0;
public static final int NotFound = 1;
public static final int AccessDenied = 2;
public static final int IoError = 3;
public static final int InvalidMode = 4;
public static final int SeekError = 5;
public static final int AlreadyExists = 6;
public static final int TooLarge = 7;
public static final int InvalidPath = 8;
public static final int Unknown = 9;
}
public class FileResult<T> {
public static FileResult<T> ok(T value);
public static FileResult<T> err(int errorCode);
public bool isOk();
public bool isErr();
public T unwrap();
public int getErrorCode();
}
文件模式
public class FileMode {
public static FileMode read(); // "r" 只读
public static FileMode write(); // "w" 只写(创建/截断)
public static FileMode append(); // "a" 追加
public static FileMode readWrite(); // "r+" 读写
public static FileMode writeRead(); // "w+" 读写(创建/截断)
public static FileMode appendRead(); // "a+" 读写追加
public static FileMode custom(String mode);
}
定位原点
public class SeekOrigin {
public static SeekOrigin begin(); // 文件开头 (SEEK_SET)
public static SeekOrigin current(); // 当前位置 (SEEK_CUR)
public static SeekOrigin end(); // 文件末尾 (SEEK_END)
}
File 类
public class File {
// 构造函数
public File();
public File(String path, FileMode mode);
// 打开/关闭
public bool open(String path, FileMode mode); // O(1) 打开文件
public static FileResult<File> openResult(String path, FileMode mode);
public bool close(); // O(1) 关闭文件
public bool isOpened(); // O(1) 检查是否打开
// 状态查询
public bool isEof(); // O(1) 是否到文件尾
public bool hasError(); // O(1) 是否有错误
public void clearError(); // O(1) 清除错误
public int getLastError(); // O(1) 获取最后错误码
public long position(); // O(1) 当前位置
public long size(); // O(1) 文件大小
// 定位操作
public bool seek(long offset, SeekOrigin origin); // O(1) 定位
public void rewind(); // O(1) 重置到开头
// 读写操作
public int readChar(); // O(1) 读一个字符
public bool writeChar(int charCode); // O(1) 写一个字符
public long readBytes(c_void* buffer, long size); // O(n) 读字节块
public long writeBytes(c_void* buffer, long size); // O(n) 写字节块
public String readLine(int maxLength); // O(n) 读一行
public bool writeString(String str); // O(n) 写字符串
public bool writeLine(String str); // O(n) 写一行
public int writeInterpolated(String template, String... args); // O(n) 模板写入
public String readAllText(); // O(n) 读取全部文本
public bool writeAllText(String content); // O(n) 写入全部文本
public bool flush(); // O(1) 刷新缓冲区
// 属性访问
public String getPath();
public FileMode getMode();
// 静态工具方法
public static bool exists(String path); // O(1) 文件是否存在
public static FileResult<bool> existsResult(String path);
public static bool delete(String path); // O(1) 删除文件
public static bool rename(String oldPath, String newPath); // O(1) 重命名
public static bool copy(String srcPath, String dstPath, bool overwrite);
public static FileResult<long> getSize(String path);
public static FileResult<String> readAllText(String path);
public static FileResult<bool> writeAllText(String path, String content);
public static FileResult<bool> appendAllText(String path, String content);
}
FileInfo 类
public class FileInfo {
public static FileInfo fromPath(String path);
public bool exists();
public long getSize();
public String getPath();
}
使用示例:
#include <File.cay>
class Main {
static void main() {
// 写入文件
std::File file = new std::File();
if (file.open("test.txt", std::FileMode.write())) {
file.writeLine("Hello, Cavvy!");
file.close();
}
// 读取文件
if (file.open("test.txt", std::FileMode.read())) {
String content = file.readAllText();
println(content);
file.close();
}
}
}
网络编程
文件: caylibs/Network.cay
SocketAddr - 网络地址
public class SocketAddr {
public SocketAddr();
public SocketAddr fromString(String ip, int port); // O(1) 从字符串创建
public SocketAddr fromIpPort(String ip, int port); // fromString 别名
public SocketAddr localhost(int port); // O(1) 127.0.0.1:port
public SocketAddr any(int port); // O(1) 0.0.0.0:port
public int getPort(); // O(1) 获取端口
public int port(); // O(1) 端口别名
public int family(); // O(1) 地址族
public int addr(); // O(1) IP地址(网络序)
public String getIp(); // O(1) 获取IP字符串
}
NetworkUtils - 网络工具
public class NetworkUtils {
public static bool init(); // O(1) 初始化网络库
public static void cleanup(); // O(1) 清理网络库
public static int getLastError(); // O(1) 获取最后错误
// 字节序转换
public static int htons(int hostshort); // O(1) 主机序转网络序(16位)
public static int htonl(int hostlong); // O(1) 主机序转网络序(32位)
public static int ntohs(int netshort); // O(1) 网络序转主机序(16位)
public static int ntohl(int netlong); // O(1) 网络序转主机序(32位)
// DNS 解析
public static String resolveHost(String hostname); // O(n) 解析主机名
// 便捷创建
public static TcpSocket connectTcp(String ip, int port); // O(1) 连接TCP
public static UdpSocket createUdp(); // O(1) 创建UDP
public static TcpServer createTcpServer(int port); // O(1) 创建TCP服务器
}
TcpSocket - TCP客户端
public class TcpSocket {
public TcpSocket();
// 连接管理
public bool connectTo(String ip, int port); // O(1) 连接到服务器
public void close(); // O(1) 关闭连接
public bool isConnected(); // O(1) 是否已连接
public bool isValid(); // O(1) 是否有效
// 数据传输
public int send(String data); // O(n) 发送数据
public String receive(int maxLen); // O(n) 接收数据
public String receiveString(int maxLen); // receive 别名
// 半关闭
public bool shutdownWrite(); // O(1) 关闭写入端
public bool shutdownRead(); // O(1) 关闭读取端
public bool shutdownBoth(); // O(1) 关闭两端
// Socket 选项
public void setReuseAddr(bool enable); // O(1) 地址重用
public void setTcpNoDelay(bool enable); // O(1) 禁用Nagle
public void setSendBufferSize(int size); // O(1) 发送缓冲区
public void setRecvBufferSize(int size); // O(1) 接收缓冲区
public void setSendTimeout(int ms); // O(1) 发送超时
public void setRecvTimeout(int ms); // O(1) 接收超时
}
TcpServer - TCP服务器
public class TcpServer {
public TcpServer();
// 服务器管理
public bool bindTo(int port); // O(1) 绑定端口
public bool listen(int backlog); // O(1) 开始监听
public TcpSocket accept(); // O(1) 接受连接
public void close(); // O(1) 关闭服务器
public bool isValid(); // O(1) 是否有效
// Socket 选项
public void setReuseAddr(bool enable);
}
UdpSocket - UDP套接字
public class UdpSocket {
public UdpSocket();
// 绑定和关闭
public bool bind(int port); // O(1) 绑定端口
public void close(); // O(1) 关闭
public bool isValid(); // O(1) 是否有效
// 数据传输
public int sendTo(String data, SocketAddr addr); // O(n) 发送数据报
public String receiveFrom(int maxLen, SocketAddr fromAddr); // O(n) 接收数据报
// Socket 选项
public void setBroadcast(bool enable); // O(1) 广播选项
public void setReuseAddr(bool enable);
}
使用示例:
#include <Network.cay>
class Main {
static void main() {
// TCP 客户端
std::TcpSocket client = std::NetworkUtils.connectTcp("127.0.0.1", 8080);
if (client != null && client.isConnected()) {
client.send("Hello, Server!");
String response = client.receive(1024);
println(response);
client.close();
}
// TCP 服务器
std::TcpServer server = std::NetworkUtils.createTcpServer(8080);
if (server != null) {
println("Server listening on port 8080");
std::TcpSocket client = server.accept();
if (client != null) {
String msg = client.receive(1024);
client.send("Echo: " + msg);
client.close();
}
server.close();
}
}
}
HTTP 客户端
文件: caylibs/EasyHTTP.cay
HttpHeaders - HTTP头部管理
public class HttpHeaders {
public HttpHeaders();
public HttpHeaders set(String name, String value); // O(n) 设置/更新头部
public HttpHeaders add(String name, String value); // O(1) 添加头部
public String get(String name); // O(n) 获取头部值
public String[] getAll(String name); // O(n) 获取所有同名头部
public HttpHeaders remove(String name); // O(n) 移除头部
public bool contains(String name); // O(n) 是否包含
public int size(); // O(1) 头部数量
public HttpHeaders clear(); // O(1) 清空
public String build(); // O(n) 构建头部字符串
}
HttpParams - URL参数管理
public class HttpParams {
public HttpParams();
public HttpParams add(String name, String value); // O(1) 添加参数
public HttpParams add(String name, int value); // O(1) 添加整数参数
public HttpParams add(String name, long value); // O(1) 添加长整数参数
public HttpParams add(String name, bool value); // O(1) 添加布尔参数
public String build(); // O(n) 构建查询字符串
public bool isEmpty(); // O(1) 是否为空
public int size(); // O(1) 参数数量
public HttpParams clear(); // O(1) 清空
}
HttpResponse - HTTP响应
public class HttpResponse {
public int getStatusCode(); // O(1) 状态码
public String getStatusText(); // O(1) 状态文本
public HttpHeaders getHeaders(); // O(1) 响应头部
public String getBody(); // O(1) 响应体
public long getResponseTime(); // O(1) 响应时间(ms)
public String getError(); // O(1) 错误信息
public bool isSuccess(); // O(1) 是否成功(2xx)
public bool isJson(); // O(n) 是否为JSON响应
public String toString(); // O(n) 字符串表示
}
HttpRequest - HTTP请求构建器
public class HttpRequest {
public HttpRequest(String url);
// 构建器方法(链式调用)
public HttpRequest method(String method); // 设置方法
public HttpRequest header(String name, String value); // 设置头部
public HttpRequest param(String name, String value); // 设置URL参数
public HttpRequest body(String body); // 设置请求体
public HttpRequest timeout(int connectMs, int readMs); // 设置超时
public HttpRequest followRedirects(bool follow); // 是否跟随重定向
// 便捷方法
public HttpRequest json(String jsonBody); // 发送JSON
public HttpRequest form(String... keyValues); // 发送表单
// 执行请求
public HttpResponse send(); // O(n) 发送请求
public HttpResponse get(); // GET 请求
public HttpResponse post(); // POST 请求
public HttpResponse put(); // PUT 请求
public HttpResponse delete(); // DELETE 请求
}
EasyHTTP - 静态工具类
public class EasyHTTP {
// 便捷 GET 请求
public static HttpResponse get(String url);
public static HttpResponse get(String url, HttpHeaders headers);
public static HttpResponse get(String url, HttpParams params);
public static HttpResponse get(String url, HttpHeaders headers, HttpParams params);
// 便捷 POST 请求
public static HttpResponse post(String url, String body);
public static HttpResponse post(String url, String body, HttpHeaders headers);
public static HttpResponse postJson(String url, String json);
public static HttpResponse postForm(String url, String... keyValues);
// 其他方法
public static HttpResponse put(String url, String body);
public static HttpResponse delete(String url);
public static HttpResponse head(String url);
public static HttpResponse options(String url);
}
使用示例:
#include <EasyHTTP.cay>
class Main {
static void main() {
// 简单 GET
http::HttpResponse resp = http::EasyHTTP.get("https://api.example.com/data");
if (resp.isSuccess()) {
println(resp.getBody());
}
// 带参数的 GET
http::HttpParams params = new http::HttpParams();
params.add("page", 1).add("limit", 10);
resp = http::EasyHTTP.get("https://api.example.com/items", params);
// POST JSON
resp = http::EasyHTTP.postJson("https://api.example.com/users",
"{\"name\":\"Alice\",\"age\":30}");
}
}
数学计算
文件: caylibs/Math.cay
数学常量
#define MATH_PI 3.14159265358979323846 // 圆周率
#define MATH_E 2.71828182845904523536 // 自然对数底
#define MATH_LN2 0.69314718055994530942 // ln(2)
#define MATH_LN10 2.30258509299404568402 // ln(10)
#define MATH_LOG2E 1.44269504088896340736 // log2(e)
#define MATH_LOG10E 0.43429448190325182765 // log10(e)
#define MATH_SQRT2 1.41421356237309504880 // sqrt(2)
#define MATH_SQRT1_2 0.70710678118654752440 // 1/sqrt(2)
#define MATH_DEG_TO_RAD 0.01745329251994329577 // 度转弧度
#define MATH_RAD_TO_DEG 57.2957795130823208768 // 弧度转度
#define MATH_EPSILON 1e-10 // 浮点精度容差
Math 类 - 静态数学工具
public class Math {
// 三角函数 (O(1))
public static double sin(double x);
public static double cos(double x);
public static double tan(double x);
public static double asin(double x);
public static double acos(double x);
public static double atan(double x);
public static double atan2(double y, double x);
// 双曲函数 (O(1))
public static double sinh(double x);
public static double cosh(double x);
public static double tanh(double x);
// 指数和对数 (O(1))
public static double exp(double x);
public static double log(double x);
public static double log10(double x);
public static double log2(double x);
public static double logBase(double x, double base);
// 幂函数 (O(1))
public static double pow(double x, double y);
public static double sqrt(double x);
public static double cbrt(double x);
public static double sqr(double x);
// 取整函数 (O(1))
public static double ceil(double x);
public static double floor(double x);
public static double round(double x);
public static double trunc(double x);
public static double frac(double x);
// 绝对值 (O(1))
public static double abs(double x);
public static int abs(int x);
public static long abs(long x);
// 符号和取模 (O(1))
public static int sign(double x);
public static double fmod(double x, double y);
// 角度转换 (O(1))
public static double toRadians(double degrees);
public static double toDegrees(double radians);
// 最值函数 (O(1))
public static int max(int a, int b);
public static double max(double a, double b);
public static long max(long a, long b);
public static int min(int a, int b);
public static double min(double a, double b);
public static long min(long a, long b);
public static int clamp(int value, int min, int max);
public static double clamp(double value, double min, double max);
// 比较函数 (O(1))
public static bool approxEqual(double a, double b, double epsilon);
public static bool approxEqual(double a, double b);
public static bool approxEqualRelative(double a, double b, double epsilon);
// 插值函数 (O(1))
public static double lerp(double a, double b, double t);
public static int lerp(int a, int b, double t);
public static double smoothStep(double a, double b, double t);
// GCD 和 LCM (O(log(min(a,b))))
public static int gcd(int a, int b);
public static int lcm(int a, int b);
}
Random 类 - 随机数生成器
public class Random {
public static void init(); // O(1) 初始化(使用时间种子)
public static void setSeed(int seed); // O(1) 设置种子
public static int nextInt(); // O(1) [0, RAND_MAX]
public static int nextInt(int bound); // O(1) [0, bound)
public static int nextInt(int min, int max); // O(1) [min, max]
public static double nextDouble(); // O(1) [0.0, 1.0)
public static double nextDouble(double min, double max); // O(1) [min, max)
public static bool nextBool(); // O(1) true/false
public static double nextGaussian(); // O(1) 正态分布
}
使用示例:
#include <Math.cay>
class Main {
static void main() {
// 三角函数
double rad = std::Math.toRadians(45);
double s = std::Math.sin(rad);
// 随机数
std::Random.init();
int r = std::Random.nextInt(100); // 0-99
double d = std::Random.nextDouble(0.0, 1.0);
// 插值
double val = std::Math.lerp(0.0, 100.0, 0.5); // 50.0
int clamped = std::Math.clamp(150, 0, 100); // 100
}
}
容器
vector
文件: caylibs/std/vector.cay
std::vector<T> 是基于 Cavvy 内置数组 T[] 的源代码级动态数组。它只保存数组缓冲区和逻辑长度,不引入额外运行时对象;元素存储和访问沿用内置数组表示。容量按 2 倍增长,push_back 仅在容量不足时搬迁元素;pop_back、clear 和容量内的 resize 不重新分配缓冲区。
public class vector<T> {
public vector(); // O(1) 创建空 vector
public vector(int n); // O(n) 创建 n 个默认元素
public vector(int n, T val); // O(n) 创建并填充值
public T get(int index); // O(1)
public T at(int index); // O(1)
public void set(int index, T val); // O(1)
public T front(); // O(1)
public T back(); // O(1)
public void push_back(T val); // 均摊 O(1)
public void pop_back(); // O(1)
public void erase(int index); // O(n)
public void clear(); // O(1)
public void resize(int n); // O(k),k 为新增默认槽位数;扩容时 O(size)
public void resize(int n, T val); // O(k),k 为新增填充值数;扩容时 O(size)
public void reserve(int n); // 容量不足时 O(size),否则 O(1)
public void shrink_to_fit(); // O(n)
public int size(); // O(1)
public int length(); // O(1)
public int capacity(); // O(1)
public bool empty(); // O(1)
}
使用示例:
#include <std/vector.cay>
using std::vector;
public int main() {
vector<int> nums = new vector<int>();
nums.push_back(10);
nums.push_back(20);
nums.set(1, 25);
println(nums.get(0)); // 10
println(nums.back()); // 25
println(nums.size()); // 2
return 0;
}
可选值类型 (Optional)
文件: caylibs/Optional.cay
零开销可选值容器,编译期通过单态化为每个具体类型生成特化代码。
public class Optional<T> {
// 构造方法
public static Optional<T> of(T value); // O(1) 创建有值 Optional
public static Optional<T> empty(); // O(1) 创建空 Optional
// 查询方法 (O(1))
public boolean isPresent(); // 是否有值
public boolean isEmpty(); // 是否为空
// 取值方法 (O(1))
public T get(); // 获取值(不安全)
public T orElse(T defaultValue); // 安全取值(带默认值)
}
使用示例:
#include <Optional.cay>
using std::Optional;
class Main {
static void main() {
Optional<int> maybeValue = Optional.of(42);
if (maybeValue.isPresent()) {
int val = maybeValue.get();
println(val); // 42
}
Optional<int> empty = Optional.empty();
int result = empty.orElse(0); // 0
}
}
增强 I/O 工具
文件: caylibs/IOPlus.cay
提供类似 Python 的便捷打印功能。
public class IOPlus {
// 字符串可变参数打印
public static void prints(String... args); // 空格分隔 + 换行
public static void printsNoLn(String... args); // 空格分隔,不换行
public static void printsSep(String separator, String... args); // 指定分隔符
public static void printsSepNoLn(String separator, String... args); // 指定分隔符,不换行
// 整数可变参数打印
public static void printi(int... args); // 空格分隔 + 换行
public static void printiNoLn(int... args); // 空格分隔,不换行
// 浮点数可变参数打印
public static void printfl(float... args); // float 版本
public static void printdb(double... args); // double 版本
// 混合类型打印
public static void printsi(String s1, int i1); // 字符串 + 整数
public static void printsf(String s1, float f1); // 字符串 + float
public static void printsis(String s1, int i1, String s2); // 字符串 + 整数 + 字符串
public static void printssi(String s1, String s2, int i1); // 字符串 + 字符串 + 整数
// 输入方法
public static String input(String prompt); // 显示提示并读取输入
public static String input(); // 读取输入
public static int inputInt(String prompt); // 读取整数
public static float inputFloat(String prompt); // 读取浮点数
// 辅助方法
public static void println(); // 打印空行
public static void repeat(String str, int count); // 重复打印
public static void repeatLn(String str, int count); // 重复打印 + 换行
public static void divider(int length); // 水平分割线
public static void divider(int length, String ch); // 指定字符的分割线
}
使用示例:
#include <IOPlus.cay>
class Main {
static void main() {
// 便捷打印
std::IOPlus.prints("Hello", "World"); // "Hello World\n"
std::IOPlus.printi(1, 2, 3); // "1 2 3\n"
std::IOPlus.printsSep(", ", "a", "b", "c"); // "a, b, c\n"
// 输入
String name = std::IOPlus.input("Enter name: ");
int age = std::IOPlus.inputInt("Enter age: ");
// 分割线
std::IOPlus.divider(20); // "--------------------"
std::IOPlus.divider(20, "="); // "===================="
}
}
FFI 类型系统
文件: caylibs/std/ffi.cay, caylibs/std/ffia.cay
原始 FFI 类型
| Cavvy 类型 | C 类型 | 说明 |
|---|---|---|
c_char | char | 8位字符 |
c_uchar | unsigned char | 无符号8位 |
c_short | short | 16位有符号 |
c_ushort | unsigned short | 无符号16位 |
c_int | int | 32位有符号 |
c_uint | unsigned int | 无符号32位 |
c_long | long | 平台相关 |
c_ulong | unsigned long | 无符号长整型 |
c_float | float | 32位浮点 |
c_double | double | 64位浮点 |
c_bool | _Bool | C99布尔 |
c_void | void | 空类型 |
c_string | char* | C字符串 |
size_t | size_t | 大小类型 |
ssize_t | ssize_t | 有符号大小 |
intptr_t | intptr_t | 指针宽度整数 |
uintptr_t | uintptr_t | 无符号指针宽度 |
类型别名 (std/ffi.cay)
// 指针类型别名
alias ptr = c_void*;
alias void_ptr = c_void*;
alias const_void_ptr = c_void*;
alias char_ptr = c_char*;
alias const_char_ptr = c_char*;
// 大写风格类型别名
alias CInt = c_int;
alias CLong = c_long;
alias CShort = c_short;
alias CChar = c_char;
alias CByte = c_byte;
alias CUInt = c_uint;
alias CULong = c_ulong;
alias CUShort = c_ushort;
alias CUChar = c_uchar;
alias CFloat = c_float;
alias CDouble = c_double;
alias CBool = c_bool;
alias CVoid = c_void;
// 固定宽度整数
alias Int8T = int8_t;
alias Int16T = int16_t;
alias Int32T = int32_t;
alias Int64T = int64_t;
alias UInt8T = uint8_t;
alias UInt16T = uint16_t;
alias UInt32T = uint32_t;
alias UInt64T = uint64_t;
// 裸指针类型
alias RawPtrInt = c_int*;
alias RawPtrLong = c_long*;
alias RawPtrVoid = c_void*;
alias RawPtrChar = c_char*;
alias RawPtrByte = c_byte*;
alias RawPtrFloat = c_float*;
alias RawPtrDouble = c_double*;
C 标准库函数声明
stdio.h (caylibs/c/stdio.cay):
extern {
c_int printf(c_string fmt, ...);
c_int fprintf(c_void* stream, c_string fmt, ...);
c_int sprintf(c_char* str, c_string fmt, ...);
c_int snprintf(c_char* str, size_t size, c_string fmt, ...);
c_int scanf(c_string fmt, ...);
c_void* fopen(c_string filename, c_string mode);
c_int fclose(c_void* stream);
size_t fread(c_void* ptr, size_t size, size_t nmemb, c_void* stream);
size_t fwrite(c_void* ptr, size_t size, size_t nmemb, c_void* stream);
c_int fseek(c_void* stream, c_long offset, c_int whence);
c_long ftell(c_void* stream);
// ... 更多函数
}
stdlib.h (caylibs/c/stdlib.cay):
extern {
c_void* malloc(size_t size);
c_void* calloc(size_t nmemb, size_t size);
c_void* realloc(c_void* ptr, size_t size);
void free(c_void* ptr);
void exit(c_int status);
void qsort(c_void* base, size_t nmemb, size_t size, CompareFn compar);
c_void* bsearch(c_void* key, c_void* base, size_t nmemb, size_t size, CompareFn compar);
c_int rand();
void srand(c_uint seed);
c_int atoi(c_string nptr);
c_double atof(c_string nptr);
}
string.h (caylibs/c/string.cay):
extern {
c_void* memcpy(c_void* dest, c_void* src, size_t n);
c_void* memmove(c_void* dest, c_void* src, size_t n);
c_void* memset(c_void* s, c_int c, size_t n);
c_int memcmp(c_void* s1, c_void* s2, size_t n);
c_char* strcpy(c_char* dest, c_string src);
c_char* strncpy(c_char* dest, c_string src, size_t n);
c_int strcmp(c_string s1, c_string s2);
size_t strlen(c_string s);
c_char* strstr(c_string haystack, c_string needle);
}
math.h (caylibs/c/math.cay):
extern {
c_double sin(c_double x);
c_double cos(c_double x);
c_double tan(c_double x);
c_double exp(c_double x);
c_double log(c_double x);
c_double pow(c_double base, c_double exp);
c_double sqrt(c_double x);
c_double ceil(c_double x);
c_double floor(c_double x);
c_double fabs(c_double x);
// ... 更多函数
}
ctype.h (caylibs/c/ctype.cay):
extern {
c_int isalnum(c_int c);
c_int isalpha(c_int c);
c_int isdigit(c_int c);
c_int isspace(c_int c);
c_int islower(c_int c);
c_int isupper(c_int c);
c_int tolower(c_int c);
c_int toupper(c_int c);
}
time.h (caylibs/c/time.cay):
extern {
c_int64_t time(c_int64_t* timer);
c_double difftime(c_int64_t end, c_int64_t start);
c_int64_t clock();
c_string ctime(c_int64_t* timer);
}
5.2.0~6.1.0 新增库模块
| 模块/类型 | 版本 | 用途 |
|---|---|---|
std::sys | 5.3.0 | 进程、环境变量和命令行参数 |
std::ArrayList<T> / std::vector<T> | 5.3.0 | 泛型容器和迭代器 |
UniquePtr<T> / ScopedPtr<T> / Rc<T> / WeakPtr<T> | 5.3.0 | 所有权、RAII、共享引用和弱引用 |
Mmap / MmapSlice | 5.4.0 | 跨平台内存映射和零拷贝切片 |
Result<T,E> | 6.1.0 | 显式成功/错误分支和错误传播 |
Error / IOError / ParseError | 6.1.0 | 统一错误类型层级 |
详细 API 以 caylibs/ 源码为准;版本演进和示例见版本演进总览。
版本信息
- 文档版本: 6.1.0
- 标准库版本: 0.5.1.x - 1.1.0
- 最后更新: 2026-07-14
Cavvy C Runtime (cayrt) ABI 规范
本文档定义 Cavvy 编译器运行时库 (cayrt) 的应用程序二进制接口 (ABI),包括类型映射、内存布局、函数调用约定和运行时服务。
目录
概述
Cavvy C Runtime (cayrt) 是 Cavvy 编译器的内置运行时支持库,以静态库形式 (libcayrt.a) 提供。所有运行时函数使用 C 调用约定 (cdecl),由 Cavvy 编译器生成的 LLVM IR 通过 declare + call 调用。
文件位置
caylibs/
└── bin/
└── cayrt/
├── cayrt.h # C 头文件
├── allocator.c # 分配器实现
├── string_ops.c # 字符串操作
├── type_conv.c # 类型转换
├── ptr_ops.c # 指针操作
├── memory.c # 内存操作
├── array_ops.c # 数组操作
└── build.sh # 构建脚本
调用约定
- 默认:
cdecl(C 声明调用约定) - 64位系统:
i64和void*大小相同(8字节),可安全互转 - 结构体布局: 必须与 LLVM IR 中的定义精确匹配
类型映射
LLVM IR 到 C 类型映射
| LLVM IR 类型 | C 类型 | 说明 |
|---|---|---|
i8* | char* | 字节指针/字符串 |
i32 | int32_t | 32位有符号整数 |
i64 | int64_t | 64位有符号整数 |
i1 | bool | 布尔值 (stdbool.h) |
float | float | 32位 IEEE 754 浮点 |
double | double | 64位 IEEE 754 浮点 |
void | void | 无返回值 |
i8** | char** | 字符串数组 |
i64 (指针) | void* / intptr_t | 指针作为整数传递 |
标准头文件包含
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
内存分配器
分配器类型定义
GlobalAlloc - 全局堆分配器
/** GlobalAlloc — 全局堆分配器的标记结构体 */
typedef struct {
char _dummy; /* 对应 LLVM: %GlobalAlloc = type { i8 } */
} GlobalAlloc;
LLVM IR 结构:
%GlobalAlloc = type { i8 } ; 单字节占位符
ArenaAllocator - Arena 线性分配器
/** ArenaAllocator — Arena 线性分配器
*
* LLVM: %ArenaAllocator = type { i8*, i8*, i8*, %ArenaAllocator* }
* 字段:
* buffer — 内存块起始地址 (i8*)
* current — 当前分配位置 (i8*)
* end — 内存块结束地址 (i8*)
* prev — 前一个 Arena(用于链式分配)(%ArenaAllocator*)
*/
typedef struct ArenaAllocator {
char* buffer;
char* current;
char* end;
struct ArenaAllocator* prev;
} ArenaAllocator;
内存布局 (64位系统):
偏移量 字段 类型 大小
----------------------------------------
0x00 buffer i8* 8 bytes
0x08 current i8* 8 bytes
0x10 end i8* 8 bytes
0x18 prev ArenaAllocator* 8 bytes
----------------------------------------
总计: 32 bytes
StackAllocator - 栈分配器
/** StackAllocator — 栈分配器
*
* LLVM: %StackAllocator = type { i8*, i64 }
*/
typedef struct {
char* base; /* 栈基址 (i8*) */
int64_t marker; /* 栈标记 (i64) */
} StackAllocator;
分配器 API
GlobalAlloc 函数
/** 获取 GlobalAlloc 单例的指针
*
* @return GlobalAlloc* 单例指针
* @note 该函数线程安全(返回静态变量地址)
*/
GlobalAlloc* __cay_global_alloc_get(void);
LLVM IR 声明:
declare %GlobalAlloc* @__cay_global_alloc_get()
Arena 分配器函数
/** 创建新的 Arena 分配器
*
* @param capacity 初始容量(字节)
* @return ArenaAllocator* 新分配器实例,失败返回 NULL
* @note 使用 malloc 分配结构体和缓冲区
*/
ArenaAllocator* __cay_arena_new(int64_t capacity);
/** 从 Arena 分配内存(带对齐)
*
* @param arena Arena 分配器实例
* @param size 请求分配的大小
* @param align 对齐要求(必须是2的幂)
* @return char* 对齐后的内存指针,失败返回 NULL
* @note 不会单独释放,调用 __cay_arena_reset 批量释放
*/
char* __cay_arena_alloc(ArenaAllocator* arena, int64_t size, int64_t align);
/** 重置 Arena(批量释放所有分配)
*
* @param arena Arena 分配器实例
* @note O(1) 操作,仅重置 current 指针到 buffer
*/
void __cay_arena_reset(ArenaAllocator* arena);
/** 释放 Arena 及其缓冲区
*
* @param arena Arena 分配器实例
* @note 释放所有内存,包括结构体本身
*/
void __cay_arena_free(ArenaAllocator* arena);
LLVM IR 声明:
declare %ArenaAllocator* @__cay_arena_new(i64)
declare i8* @__cay_arena_alloc(%ArenaAllocator*, i64, i64)
declare void @__cay_arena_reset(%ArenaAllocator*)
declare void @__cay_arena_free(%ArenaAllocator*)
使用示例 (Cavvy 代码):
extern {
long __cay_arena_new(long capacity);
long __cay_arena_alloc(long arena, long size, long align);
void __cay_arena_reset(long arena);
void __cay_arena_free(long arena);
}
class Main {
static void main() {
long arena = __cay_arena_new(1024);
long ptr = __cay_arena_alloc(arena, 256, 8);
// 使用内存...
__cay_arena_reset(arena); // 批量重置
__cay_arena_free(arena); // 释放
}
}
字符串操作
Cavvy 字符串在内部表示为以 null 结尾的 C 字符串 (char*)。所有函数在空指针输入时进行安全检查,返回空字符串或错误码。
字符串操作函数
/** 字符串拼接
*
* @param a 第一个字符串(可为 NULL)
* @param b 第二个字符串(可为 NULL)
* @return char* 新分配的拼接结果,使用 calloc 分配
* @note 返回字符串必须被释放(如果非空字符串常量)
*/
char* __cay_string_concat(const char* a, const char* b);
/** 字符串长度
*
* @param str 输入字符串(可为 NULL)
* @return int32_t 字符串长度,NULL 返回 0
*/
int32_t __cay_string_length(const char* str);
/** 子串提取
*
* @param str 源字符串
* @param begin 起始索引(包含)
* @param end 结束索引(不包含)
* @return char* 新分配的子串
* @note 自动处理负数索引和越界情况
*/
char* __cay_string_substring(const char* str, int32_t begin, int32_t end);
/** 查找子串位置(首次出现)
*
* @param str 源字符串
* @param substr 要查找的子串
* @return int32_t 索引位置,-1 表示未找到
*/
int32_t __cay_string_indexof(const char* str, const char* substr);
/** 从指定位置查找子串
*
* @param str 源字符串
* @param substr 要查找的子串
* @param start 开始查找的位置
* @return int32_t 索引位置,-1 表示未找到
*/
int32_t __cay_string_indexof_from(const char* str, const char* substr, int32_t start);
/** 反向查找子串位置(最后一次出现)
*
* @param str 源字符串
* @param substr 要查找的子串
* @return int32_t 索引位置,-1 表示未找到
*/
int32_t __cay_string_lastindexof(const char* str, const char* substr);
/** 检查前缀
*
* @param str 源字符串
* @param prefix 前缀
* @return bool 是否以 prefix 开头
*/
bool __cay_string_startswith(const char* str, const char* prefix);
/** 检查后缀
*
* @param str 源字符串
* @param suffix 后缀
* @return bool 是否以 suffix 结尾
*/
bool __cay_string_endswith(const char* str, const char* suffix);
/** 获取指定位置的字符
*
* @param str 源字符串
* @param index 字符索引
* @return char 指定位置的字符,越界返回 '\0'
*/
char __cay_string_charat(const char* str, int32_t index);
/** 字符串替换(替换所有出现)
*
* @param str 源字符串
* @param old 要替换的子串
* @param new_str 替换后的子串
* @return char* 新分配的结果字符串
*/
char* __cay_string_replace(const char* str, const char* old, const char* new_str);
/** 检查是否为空字符串
*
* @param str 输入字符串
* @return bool 是否为空(NULL 或长度为 0)
*/
bool __cay_string_isempty(const char* str);
/** 字符串比较(区分大小写)
*
* @param str1 第一个字符串
* @param str2 第二个字符串
* @return bool 是否相等
*/
bool __cay_string_equals(const char* str1, const char* str2);
/** 字符串比较(不区分大小写)
*
* @param str1 第一个字符串
* @param str2 第二个字符串
* @return bool 是否相等(忽略大小写)
*/
bool __cay_string_equals_ignorecase(const char* str1, const char* str2);
/** 去除首尾空白
*
* @param str 源字符串
* @return char* 新分配的修剪后字符串
*/
char* __cay_string_trim(const char* str);
/** 转换为小写
*
* @param str 源字符串
* @return char* 新分配的小写字符串
*/
char* __cay_string_to_lower(const char* str);
/** 转换为大写
*
* @param str 源字符串
* @return char* 新分配的大写字符串
*/
char* __cay_string_to_upper(const char* str);
/** 检查字符串是否包含子串
*
* @param str 源字符串
* @param substr 子串
* @return bool 是否包含
*/
bool __cay_string_contains(const char* str, const char* substr);
/** 字符串比较(按字典序)
*
* @param str1 第一个字符串
* @param str2 第二个字符串
* @return int32_t -1 (str1<str2), 0 (相等), 1 (str1>str2)
*/
int32_t __cay_string_compareto(const char* str1, const char* str2);
LLVM IR 声明
; 字符串操作
declare i8* @__cay_string_concat(i8*, i8*)
declare i32 @__cay_string_length(i8*)
declare i8* @__cay_string_substring(i8*, i32, i32)
declare i32 @__cay_string_indexof(i8*, i8*)
declare i32 @__cay_string_indexof_from(i8*, i8*, i32)
declare i32 @__cay_string_lastindexof(i8*, i8*)
declare i1 @__cay_string_startswith(i8*, i8*)
declare i1 @__cay_string_endswith(i8*, i8*)
declare i8 @__cay_string_charat(i8*, i32)
declare i8* @__cay_string_replace(i8*, i8*, i8*)
declare i1 @__cay_string_isempty(i8*)
declare i1 @__cay_string_equals(i8*, i8*)
declare i1 @__cay_string_equals_ignorecase(i8*, i8*)
declare i8* @__cay_string_trim(i8*)
类型转换
将 Cavvy 基础类型转换为字符串表示。所有函数在 calloc 失败时返回空字符串,避免崩溃。
类型转换函数
/** int32_t → 字符串
*
* @param value 整数值
* @return char* 新分配的字符串(最大32字节)
*/
char* __cay_int_to_string(int32_t value);
/** int64_t (long) → 字符串
*
* @param value 长整数值
* @return char* 新分配的字符串(最大32字节)
*/
char* __cay_long_to_string(int64_t value);
/** float → 字符串
*
* @param value 浮点值
* @return char* 新分配的字符串(最大64字节)
*/
char* __cay_float_to_string(float value);
/** double → 字符串
*
* @param value 双精度值
* @return char* 新分配的字符串(最大64字节)
*/
char* __cay_double_to_string(double value);
/** bool → 字符串
*
* @param value 布尔值
* @return char* 返回静态字符串 "true" 或 "false"
* @note 返回静态常量,无需释放
*/
char* __cay_bool_to_string(bool value);
/** char → 字符串
*
* @param value 字符值
* @return char* 新分配的单字符字符串
*/
char* __cay_char_to_string(char value);
LLVM IR 声明
; 类型转换
declare i8* @__cay_int_to_string(i32)
declare i8* @__cay_long_to_string(i64)
declare i8* @__cay_float_to_string(float)
declare i8* @__cay_double_to_string(double)
declare i8* @__cay_bool_to_string(i1)
declare i8* @__cay_char_to_string(i8)
指针操作
提供对原始内存的读写操作,用于 FFI 交互。所有指针参数以 int64_t 形式传入,内部转换为 void*。
指针操作函数
/** 从指定地址读取 64 位指针值
*
* @param ptr 内存地址(i64 编码的指针)
* @return int64_t 该地址存储的 64 位值
* @warning 不检查地址有效性
*/
int64_t __cay_read_ptr(int64_t ptr);
/** 将 C 字符串指针转换为 Cavvy 字符串(复制数据)
*
* @param ptr C 字符串指针地址
* @return char* 新分配的 Cavvy 字符串副本
* @note 如果 ptr 为 0 或指向空字符串,返回空字符串常量
*/
char* __cay_ptr_to_string(int64_t ptr);
/** 向指定地址写入 64 位指针值
*
* @param ptr 目标内存地址
* @param value 要写入的 64 位值
* @warning 不检查地址有效性
*/
void __cay_write_ptr(int64_t ptr, int64_t value);
/** 向指定地址写入 32 位整数值
*
* @param ptr 目标内存地址
* @param value 要写入的 32 位值
*/
void __cay_write_int(int64_t ptr, int32_t value);
/** 从指定地址读取 32 位整数值
*
* @param ptr 内存地址
* @return int32_t 该地址存储的 32 位值
*/
int32_t __cay_read_int(int64_t ptr);
/** 向指定地址写入 8 位字节值
*
* @param ptr 目标内存地址
* @param value 要写入的 8 位值(低8位有效)
*/
void __cay_write_byte(int64_t ptr, int32_t value);
/** 将缓冲区内容转换为字符串
*
* @param buffer 缓冲区地址
* @param length 缓冲区长度
* @return char* 新分配的字符串(包含 length 个字符 + null 终止符)
*/
char* __cay_buffer_to_string(int64_t buffer, int32_t length);
LLVM IR 声明
; 指针操作
declare i64 @__cay_read_ptr(i64)
declare i8* @__cay_ptr_to_string(i64)
declare void @__cay_write_ptr(i64, i64)
declare void @__cay_write_int(i64, i32)
declare i32 @__cay_read_int(i64)
declare void @__cay_write_byte(i64, i32)
declare i8* @__cay_buffer_to_string(i64, i32)
使用示例
extern {
long __cay_read_ptr(long ptr);
void __cay_write_ptr(long ptr, long value);
String __cay_ptr_to_string(long ptr);
long malloc(long size);
void free(long ptr);
}
class Main {
static void main() {
// 分配内存并写入指针值
long ptr = malloc(16);
long data = malloc(32);
__cay_write_ptr(ptr, data);
// 读取指针值
long readData = __cay_read_ptr(ptr);
// 清理
free(ptr);
free(data);
}
}
内存操作
提供按字节设置和复制内存的运行时支持。包含空指针安全检查。
内存操作函数
/** 按字节设置内存(空指针安全)
*
* @param ptr 目标内存地址(i64 编码)
* @param value 要设置的值(低8位有效)
* @param n 字节数
* @note 如果 ptr 为 0,不执行任何操作
*/
void __cay_memset_byte(int64_t ptr, int32_t value, int32_t n);
/** 按字节复制内存(空指针安全)
*
* @param dest 目标地址(i64 编码)
* @param src 源地址(i64 编码)
* @param n 字节数
* @note 如果 dest 或 src 为 0,不执行任何操作
*/
void __cay_memcpy_byte(int64_t dest, int64_t src, int32_t n);
LLVM IR 声明
; 内存操作
declare void @__cay_memset_byte(i64, i32, i32)
declare void @__cay_memcpy_byte(i64, i64, i32)
使用示例
extern {
void __cay_memset_byte(long ptr, int value, int n);
void __cay_memcpy_byte(long dest, long src, int n);
long malloc(long size);
void free(long ptr);
}
class Main {
static void main() {
long buffer = malloc(256);
// 清零缓冲区
__cay_memset_byte(buffer, 0, 256);
// 复制数据
long src = "Hello".c_str();
__cay_memcpy_byte(buffer, src, 5);
free(buffer);
}
}
数组操作
Cavvy 数组内存布局:
[长度:i32 (4B)][padding (4B)][元素0][元素1]...
返回指针指向元素0,长度字段在 -8 偏移处。
数组操作函数
/** 创建 String[] 数组
*
* 布局: [4B length][4B pad][8B*size elements]
* 返回: 指向数据区(元素0)的指针
*
* @param size 数组元素个数
* @return char** 数组数据指针
* @note 使用 calloc 分配,自动清零
*/
char** __cay_create_string_array(int32_t size);
/** 将 C 字符串转换为 Cavvy String 对象
*
* @param cstr C 字符串
* @return char* 新分配的 Cavvy 字符串副本
* @note 安全处理 NULL 输入
*/
char* __cay_cstr_to_string(const char* cstr);
/** 设置数组元素(引用类型)
*
* @param arr 字符串数组
* @param idx 索引
* @param value 要设置的值
*/
void __cay_array_set_ref(char** arr, int32_t idx, char* value);
/** 获取数组元素(引用类型)
*
* @param arr 字符串数组
* @param idx 索引
* @return char* 指定位置的元素
*/
char* __cay_array_get_ref(char** arr, int32_t idx);
/** 获取数组长度
*
* @param arr 字符串数组
* @return int32_t 数组长度
* @note 长度存储在 arr 指针前 8 字节处
*/
int32_t __cay_array_length(char** arr);
数组内存布局详解
地址偏移 内容 大小
------------------------------------------
-8 length (i32) 4 bytes
-4 padding 4 bytes
0 element[0] 8 bytes (指针)
8 element[1] 8 bytes (指针)
16 element[2] 8 bytes (指针)
... ... ...
LLVM IR 声明
; 数组操作
declare i8** @__cay_create_string_array(i32)
declare i8* @__cay_cstr_to_string(i8*)
declare void @__cay_array_set_ref(i8**, i32, i8*)
declare i8* @__cay_array_get_ref(i8**, i32)
declare i32 @__cay_array_length(i8**)
使用示例
extern {
long __cay_create_string_array(int size);
void __cay_array_set_ref(long arr, int idx, String value);
String __cay_array_get_ref(long arr, int idx);
int __cay_array_length(long arr);
}
class Main {
static void main() {
// 创建字符串数组
long arr = __cay_create_string_array(3);
// 设置元素
__cay_array_set_ref(arr, 0, "Hello");
__cay_array_set_ref(arr, 1, "World");
__cay_array_set_ref(arr, 2, "!");
// 获取长度
int len = __cay_array_length(arr);
println(len); // 3
// 读取元素
String s = __cay_array_get_ref(arr, 0);
println(s); // "Hello"
}
}
构建与链接
构建 cayrt 静态库
# 进入 cayrt 目录
cd caylibs/bin/cayrt
# 编译所有源文件
cc -c -O2 -fPIC allocator.c -o allocator.o
cc -c -O2 -fPIC string_ops.c -o string_ops.o
cc -c -O2 -fPIC type_conv.c -o type_conv.o
cc -c -O2 -fPIC ptr_ops.c -o ptr_ops.o
cc -c -O2 -fPIC memory.c -o memory.o
cc -c -O2 -fPIC array_ops.c -o array_ops.o
# 创建静态库
ar rcs libcayrt.a allocator.o string_ops.o type_conv.o ptr_ops.o memory.o array_ops.o
或使用提供的构建脚本:
./build.sh
链接到 Cavvy 程序
Cavvy 编译器会自动链接 libcayrt.a,无需手动指定。
如需手动链接:
# 直接链接静态库
cayc program.cay -L./caylibs/bin/cayrt -lcayrt
# 或指定完整路径
cayc program.cay ./caylibs/bin/cayrt/libcayrt.a
运行时依赖
- Windows: 需要
msvcrt.dll(C 运行时) - Linux: 需要
libc.so(glibc)
版本信息
- ABI 版本: 6.1.0
- 库版本: 0.5.1.x
- 最后更新: 2026-07-14
- 兼容性: Cavvy 6.1.0+
变更历史
| 版本 | 变更 |
|---|---|
| 5.1.0 | 初始 ABI 定义 |
| 0.5.1 | 添加 Arena 分配器 |
| 0.5.0 | 添加 GlobalAlloc |
附录:完整头文件
#ifndef CAYRT_H
#define CAYRT_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* 分配器类型 */
typedef struct { char _dummy; } GlobalAlloc;
typedef struct ArenaAllocator {
char* buffer;
char* current;
char* end;
struct ArenaAllocator* prev;
} ArenaAllocator;
typedef struct {
char* base;
int64_t marker;
} StackAllocator;
/* 分配器函数 */
GlobalAlloc* __cay_global_alloc_get(void);
ArenaAllocator* __cay_arena_new(int64_t capacity);
char* __cay_arena_alloc(ArenaAllocator* arena, int64_t size, int64_t align);
void __cay_arena_reset(ArenaAllocator* arena);
void __cay_arena_free(ArenaAllocator* arena);
/* 字符串操作 */
char* __cay_string_concat(const char* a, const char* b);
int32_t __cay_string_length(const char* str);
char* __cay_string_substring(const char* str, int32_t begin, int32_t end);
int32_t __cay_string_indexof(const char* str, const char* substr);
int32_t __cay_string_indexof_from(const char* str, const char* substr, int32_t start);
int32_t __cay_string_lastindexof(const char* str, const char* substr);
bool __cay_string_startswith(const char* str, const char* prefix);
bool __cay_string_endswith(const char* str, const char* suffix);
char __cay_string_charat(const char* str, int32_t index);
char* __cay_string_replace(const char* str, const char* old, const char* new_str);
bool __cay_string_isempty(const char* str);
bool __cay_string_equals(const char* str1, const char* str2);
bool __cay_string_equals_ignorecase(const char* str1, const char* str2);
char* __cay_string_trim(const char* str);
/* 类型转换 */
char* __cay_int_to_string(int32_t value);
char* __cay_long_to_string(int64_t value);
char* __cay_float_to_string(float value);
char* __cay_double_to_string(double value);
char* __cay_bool_to_string(bool value);
char* __cay_char_to_string(char value);
/* 指针操作 */
int64_t __cay_read_ptr(int64_t ptr);
char* __cay_ptr_to_string(int64_t ptr);
void __cay_write_ptr(int64_t ptr, int64_t value);
void __cay_write_int(int64_t ptr, int32_t value);
int32_t __cay_read_int(int64_t ptr);
void __cay_write_byte(int64_t ptr, int32_t value);
char* __cay_buffer_to_string(int64_t buffer, int32_t length);
/* 内存操作 */
void __cay_memset_byte(int64_t ptr, int32_t value, int32_t n);
void __cay_memcpy_byte(int64_t dest, int64_t src, int32_t n);
/* 数组操作 */
char** __cay_create_string_array(int32_t size);
char* __cay_cstr_to_string(const char* cstr);
void __cay_array_set_ref(char** arr, int32_t idx, char* value);
char* __cay_array_get_ref(char** arr, int32_t idx);
int32_t __cay_array_length(char** arr);
#ifdef __cplusplus
}
#endif
#endif /* CAYRT_H */
编译器架构
本文档说明 Cavvy 编译器的整体架构、编译流水线和各模块职责。
整体架构概览
.cay 源码
│
▼
┌──────────────────┐
│ 预处理器 │ src/preprocessor/
│ (#include, │
│ #define, #ifdef)│
└──────┬───────────┘
│ 预处理后源码
▼
┌──────────────────┐
│ 词法分析器 │ src/lexer/
│ (基于 logos) │
└──────┬───────────┘
│ Token 流
▼
┌──────────────────┐
│ 解析器 │ src/parser/
│ (递归下降) │
└──────┬───────────┘
│ AST
▼
┌──────────────────┐
│ 语义分析 │ src/semantic/
│ (类型检查/符号表) │
└──────┬───────────┘
│ 带类型注解的 AST
▼
┌──────────────────┐
│ IR 生成 │ src/ir/
│ (自定义 SSA IR) │
└──────┬───────────┘
│ IR (自定义数据结构)
▼
┌──────────────────┐
│ LLVM 后端 │ src/ir/llvm_backend.rs
│ (IR → LLVM IR) │ + src/codegen/(旧代码生成路径)
└──────┬───────────┘
│ LLVM IR 文本 (.ll)
▼
┌──────────────────┐
│ clang (捆绑) │ llvm-minimal/
│ (.ll → .exe) │
└──────────────────┘
模块详解
1. 预处理器(src/preprocessor/)
完整的 C 风格预处理器,支持:
#include "file"/#include <file>— 文件包含#define MACRO value— 宏定义#ifdef/#ifndef/#if/#elif/#else/#endif— 条件编译#pragma once— 防止重复包含#error "message"— 编译错误#warning "message"— 编译警告- 源映射(source map)维护,将预处理后的位置映射回原始位置
2. 词法分析器(src/lexer/)
基于 logos 库的快速词法分析器。
TokenKind枚举 — 所有词法单元类型(关键字、运算符、字面量、标识符等)Lexer结构体包装 logos,提供位置跟踪和错误恢复Span— 源码位置信息(行、列、偏移)- 支持
//和/* */注释 - 支持原始字符串字面量
3. 解析器(src/parser/)
递归下降解析器,从 Token 流构建 AST。
mod.rs— 主解析器入口,Parser结构体expressions/子模块 — 7 个文件:assignment.rs— 赋值表达式binary.rs— 二元运算lambda.rs— Lambda 表达式mod.rs— 统一调度postfix.rs— 后缀表达式(方法调用、数组访问)primary.rs— 基本表达式(字面量、变量、括号)unary.rs— 一元运算
- 解析各种语句:声明、控制流、类/接口/结构体/枚举定义
4. 语义分析(src/semantic/)
语义分析阶段包括类型检查、符号解析和作用域管理。
mod.rs—SemanticAnalyzer入口symbol_table.rs— 嵌套作用域的符号表:SymbolTable— 作用域栈Symbol枚举 — 变量、类、方法、接口、枚举等
type_check.rs—SemanticAnalyzer实现,类型兼容性校验type_inference_result.rs— 带错误收集的类型推导expr_inference.rs— 表达式类型推导class_analysis.rs— 类层级分析(方法重写、接口实现验证)
5. IR 系统(src/ir/)
自定义 SSA 形式中间表示,共 12 个文件。这是编译器的核心新架构。
关键文件:
| 文件 | 职责 |
|---|---|
mod.rs | IR 模块入口 |
module.rs | IrModule — 顶层容器(全局变量、函数声明) |
function.rs | IrFunction — 函数定义(参数、基本块列表) |
block.rs | IrBasicBlock — 基本块(指令列表 + 终止符) |
value.rs | IrValue — 值枚举(常量、变量、临时寄存器) |
types.rs | IrType — 类型枚举(整数、浮点、指针、数组、函数签名) |
builder.rs | IrBuilder — AST → IR 核心转换(约 2000+ 行) |
llvm_backend.rs | LlvmBackend — IR → LLVM IR 文本渲染 |
inliner.rs | 函数内联优化器 |
inline_ir.rs | 内联 IR 支持(__ir { } 块) |
verification.rs | IrVerifier — IR 正确性验证(SSA 合规性) |
IR 设计特点:
- SSA 形式,每个赋值产生新的版本
- Phi 节点用于控制流合并点
- 强类型,每个值有确定的
IrType - 支持内联 IR 嵌入(
__ir { })
6. 代码生成(src/codegen/)
代码生成模块将语义分析后的 AST 转换为 LLVM IR 文本。
核心文件:
generator.rs—CodeGenerator主入口context.rs—CodegenContext(符号映射、作用域)types.rs— Cavvy 类型 ↔ LLVM 类型映射source_map.rs— 源码位置到 LLVM 元数据的映射(调试信息)allocator.rs— 内存分配(栈/全局)bridge.rs— 运行时桥接platform.rs— 平台 ABI/对齐/调用约定obfuscator.rs— 代码混淆(名称/控制流)
表达式代码生成(expressions/)— 19 个文件:allocator, array, assignment, binary, builtin, call, cast, identifier, instanceof, lambda, literal, main, member, new, string_methods, ternary, unary, utils, mod.rs
语句代码生成(statements/)— 10 个文件:block, if_stmt, jump_stmt, loops, return_stmt, scope_stmt, statement, switch_stmt, var_decl, mod.rs
运行时支持(runtime/)— 19 个文件:字符串操作、类型转换、指针操作、缓冲区转换等运行时函数声明
7. 字节码系统(src/bytecode/)
CayBC 字节码格式,支持 JIT/AOT 编译。
mod.rs—BytecodeModule顶层结构constant_pool.rs— JVM 风格的常量池(字符串、整数、浮点、类引用、方法句柄)instructions.rs— 100+ 指令操作码(Opcode枚举)jit.rs— JIT/AOT 编译器(JitOptions、jit_to_exe())linker.rs— 自动链接(LinkerConfig,自动检测依赖库)serializer.rs— 二进制序列化(魔数CAY\x01)obfuscator.rs— 字节码混淆(名称、控制流、垃圾代码、字符串加密)
8. 库(src/lib.rs)
暴露 Compiler 结构体封装完整流水线。所有二进制文件通过 use cavvy::Compiler 使用。
关键导出:
Compiler结构体CompilerStage枚举(编译阶段)CompilerOptions结构体(优化级别、输出格式等)
编译流水线
Compiler::compile() 调用链:
1. read_source() 读取源文件
2. preprocess() 预处理器展开
3. tokenize() 词法分析
4. parse() 解析为 AST
5. analyze() 语义分析
6. generate_ir() AST → IR
7. optimize_ir() IR 优化(内联等)
8. generate_llvm_ir() IR → LLVM IR 文本
9. write_output() 写入 .ll 文件
10. run_clang() 调用捆绑 clang → .exe
每个阶段对应 CompilerStage 枚举,支持部分流水线执行。
二进制入口点
11 个可执行文件全部位于 src/bin/:
| 二进制 | 功能 | 流水线终点 |
|---|---|---|
cayc | 一站式编译 | .exe |
cay-ir | 生成 LLVM IR | .ll |
ir2exe | IR → .exe | 仅 clang |
cay-check | 语法语义检查 | 语义分析 |
cay-run | 编译并运行 | .exe + 运行 |
cay-rcpl | 交互式环境 | 循环编译 |
cay-bcgen | 字节码生成 | CayBC |
cay-lsp | LSP 服务器 | — |
cavly | 包管理器 | — |
cay-dt | 文档工具 | — |
cay-dp | 依赖分析 | — |
cay-pre | 预处理器 | 预处理 |
错误处理约定
cayError(位于src/error.rs)是编译器流水线的唯一错误类型- 使用
thiserror::Error定义,每个变体携带suggestion: String字段 cayResult<T> = Result<T, cayError>在整个编译器中统一使用miette仅用于显示,将cayError转为美观的 CLI 输出- CLI 错误报告函数:
print_miette_error()、print_error_with_context()、print_tool_error()、print_warning()
额外系统
包管理器(Cavly,src/cavly/)
config.rs—CavlyConfig、PackageConfig、BuildConfig、FfiConfig、Dependency、WorkspaceConfig、LibConfigbuilder.rs— 构建状态机、依赖解析、拓扑排序project.rs— 项目创建和模板ffi.rs— FFI 检测和绑定生成tester.rs— 测试运行器workspace.rs— 工作区管理
RCPL 交互式环境(src/rcpl/)
mod.rs— 主循环input_parser.rs— 输入分类(空输入、表达式、语句、类定义、预处理器指令等)code_generator.rs— 交互式代码生成(代码包装、输出注入)context.rs— 持久化上下文(变量、类型、导入跟踪)
LSP 语言服务器(src/bin/cay-lsp.rs)
内置 LSP 服务器,与 vscode-extension/ 配合使用,提供语法高亮、自动补全、诊断、跳转定义、悬停信息。
CayBC 字节码格式
Cavvy 字节码格式(CayBC)是一种专为 Cavvy 语言设计的二进制中间表示格式。它支持 JIT 和 AOT 编译,并提供混淆能力。
概述
CayBC 字节码系统位于 src/bytecode/,由 7 个模块组成:
| 模块 | 职责 |
|---|---|
mod.rs | BytecodeModule 顶层结构 |
constant_pool.rs | JVM 风格的常量池 |
instructions.rs | 100+ 指令操作码 |
jit.rs | JIT/AOT 编译器 |
linker.rs | 自动链接 |
serializer.rs | 二进制序列化 |
obfuscator.rs | 字节码混淆 |
BytecodeModule(mod.rs)
BytecodeModule 是 CayBC 的顶层容器,包含:
- 常量池(
ConstantPool) — 所有常量的索引表 - 类定义 — 类的字段、方法、接口实现
- 方法体 — 字节码指令序列
- 元数据 — 版本号、源文件名、调试信息
常量池(constant_pool.rs)
JVM 风格的常量池,支持以下常量类型:
| 常量类型 | 描述 |
|---|---|
Utf8 | UTF-8 编码字符串 |
Integer | 32 位整数常量 |
Long | 64 位整数常量 |
Float | 32 位浮点常量 |
Double | 64 位浮点常量 |
String | 字符串常量(引用 Utf8) |
Class | 类引用 |
FieldRef | 字段引用 |
MethodRef | 方法引用 |
InterfaceMethodRef | 接口方法引用 |
NameAndType | 名称和类型描述符对 |
MethodHandle | 方法句柄 |
MethodType | 方法类型描述符 |
InvokeDynamic | 动态调用点 |
常量池使用整数索引访问(从 1 开始,类似 JVM 规范)。
指令集(instructions.rs)
CayBC 定义 100+ 操作码(Opcode 枚举),按功能分类:
加载和存储
| 指令 | 描述 |
|---|---|
Load | 从局部变量加载到栈 |
Store | 从栈存储到局部变量 |
LoadConst | 加载常量 |
LoadField | 加载实例字段 |
StoreField | 存储实例字段 |
LoadStatic | 加载静态字段 |
StoreStatic | 存储静态字段 |
LoadArray | 从数组加载 |
StoreArray | 存储到数组 |
算术运算
| 指令 | 描述 |
|---|---|
Add, Sub, Mul, Div, Rem | 基本算术 |
Neg | 取负 |
Shl, Shr, UShr | 移位 |
And, Or, Xor | 位运算 |
Inc | 局部变量自增 |
类型转换
I2L, L2I, F2D, D2F, I2B, I2C, I2S 等
对象操作
| 指令 | 描述 |
|---|---|
New | 创建新对象 |
NewArray | 创建数组 |
ArrayLength | 获取数组长度 |
InstanceOf | 类型检查 |
CheckCast | 类型强制转换 |
栈操作
| 指令 | 描述 |
|---|---|
Pop | 弹出栈顶 |
Dup | 复制栈顶 |
Swap | 交换栈顶两个元素 |
控制流
| 指令 | 描述 |
|---|---|
Goto | 无条件跳转 |
IfEq, IfNe, IfLt, IfGe, IfGt, IfLe | 条件跳转 |
TableSwitch | 表跳转(switch) |
LookupSwitch | 查找跳转 |
方法调用
| 指令 | 描述 |
|---|---|
InvokeVirtual | 虚方法调用(vtable) |
InvokeStatic | 静态方法调用 |
InvokeSpecial | 特殊方法调用(构造函数、父类) |
InvokeInterface | 接口方法调用 |
InvokeDynamic | 动态方法调用 |
返回
Return, IReturn, LReturn, FReturn, DReturn, AReturn
JIT/AOT 编译(jit.rs)
JitOptions 结构体控制编译行为:
#![allow(unused)]
fn main() {
struct JitOptions {
optimization_level: u8, // 0-3
dump_ir: bool, // 是否输出 IR
dump_asm: bool, // 是否输出汇编
verbose: bool,
}
}
jit_to_exe() 函数将 BytecodeModule 编译为可执行文件。
链接器(linker.rs)
LinkerConfig 支持自动链接检测:
- 自动检测依赖的本地库
- 根据目标平台选择正确的链接器
- 支持静态链接和动态链接
序列化格式(serializer.rs)
二进制文件结构
[魔数] CAY\x01 (4 字节)
[版本号] 主版本:u16 + 次版本:u16 (4 字节)
[常量池] ConstantPool 序列化
[类定义] 类、字段、方法定义
[字节码] 方法指令序列
[元数据] 调试信息等
[校验和] CRC32 (4 字节)
魔数
所有 CayBC 文件以 0xCA 0x59 0x01(ASCII “CAY” + 版本 1)开头。
混淆器(obfuscator.rs)
BytecodeObfuscator 提供四种混淆技术:
| 技术 | 描述 | 可逆性 |
|---|---|---|
| 名称混淆 | 将标识符重命名为无意义名称 | 否 |
| 控制流混淆 | 插入冗余跳转和无关代码块 | 否 |
| 垃圾代码插入 | 插入不执行的无意义指令 | 否 |
| 字符串加密 | 运行时解密字符串字面量 | 运行时透明 |
混淆级别
| 级别 | 描述 |
|---|---|
| 0 | 无混淆 |
| 1 | 名称混淆 |
| 2 | 名称 + 控制流混淆 |
| 3 | 全部(名称 + 控制流 + 垃圾代码 + 字符串加密) |
使用方法
# 生成字节码
cay-bcgen input.cay -o output.caybc
# 带混淆生成
cay-bcgen input.cay --obfuscate --obfuscation-level 2 -o obfuscated.caybc
# 查看字节码信息
cay-bcgen input.cay --verbose
与 JVM 字节码的对比
| 特性 | CayBC | JVM |
|---|---|---|
| 常量池 | ✅ JVM 风格 | 标准 |
| 指令集 | 100+ 操作码 | 200+ 操作码 |
| 栈机模型 | ✅ 是 | 是 |
| 类型信息 | 强类型 | 类型描述符 |
| 混淆 | ✅ 内置 | 需外部工具 |
| 序列化 | ✅ 自定义格式 | .class 格式 |
| JIT | ✅ 基础实现 | 成熟 |
测试指南
本文档说明 Cavvy 编译器的测试策略、如何编写和运行测试。
测试概览
编译器测试分布在三个层级:
| 层级 | 位置 | 类型 | 测试什么 |
|---|---|---|---|
| 单元测试 | src/lib.rs | #[cfg(test)] | 词法分析器、解析器、预处理器 |
| 集成测试 | tests/*.rs | 独立测试文件 | 编译并运行 .cay 文件 |
| 文档测试 | scripts/doc-test.py | 自动化脚本 | 文档中代码示例的正确性 |
运行测试
基本命令
# 必须先构建 release 版本
cargo build --release
# 运行全部测试
cargo test --release --verbose
单独运行特定测试
# 按测试文件
cargo test --release --test interface_tests -- --nocapture
cargo test --release --test lambda_tests -- --nocapture
cargo test --release --test inheritance_tests -- --nocapture
cargo test --release --test array_tests -- --nocapture
cargo test --release --test access_control_tests -- --nocapture
# 按测试名称模式
cargo test --release -- test_interface_dispatch --nocapture
# 仅编译测试(不运行)
cargo test --release --no-run
集成测试
集成测试位于 tests/ 目录,每个文件对应一组相关测试。
测试辅助函数
位于 tests/common/mod.rs:
#![allow(unused)]
fn main() {
// 编译并运行 .cay 文件,断言成功
compile_and_run_eol("examples/test_filename.cay");
// 编译并断言产生预期错误
compile_eol_expect_error("examples/test_error.cay", "expected error message");
}
测试文件约定
- 源文件放在
examples/目录下 - 以
test_前缀命名 - 测试函数使用
#[test]标记 - 使用全局
Mutex串行执行(避免文件冲突)
已存在的测试文件(40+ 个)
tests/
├── access_control_tests.rs
├── array_tests.rs
├── class_declaration_tests.rs
├── control_flow_tests.rs
├── enum_tests.rs
├── error_tests.rs
├── expression_tests.rs
├── ffi_tests.rs
├── function_tests.rs
├── generic_tests.rs
├── inheritance_tests.rs
├── interface_tests.rs
├── lambda_tests.rs
├── lexer_tests.rs
├── method_tests.rs
├── operator_tests.rs
├── parser_tests.rs
├── preprocessor_tests.rs
├── string_tests.rs
├── struct_tests.rs
├── type_tests.rs
├── vtable_tests.rs
├── common/mod.rs
└── ...(更多)
文档测试
文档中的代码示例会被自动测试,确保示例始终可用。
运行
# Windows 一键命令
.\scripts\test-docs.ps1
# 跨平台(Python)
python scripts/doc-test.py --build
代码块标记
文档中的代码块通过语言标记控制测试行为:
<!-- 默认:仅语法检查(cay-check) -->
```cay
class Example {
static void main() {
println("checked");
}
}
```
<!-- 编译并运行 -->
```cay run
public int main() {
println("runs");
}
```
<!-- 顶层 main -->
```cay
public int main() {
return 0;
}
```
<!-- 跳过测试的代码片段 -->
```cay ignore
// 这个不会被测试
```
写文档测试的最佳实践
- 完整程序:所有被测试的代码块必须是包含
main方法的完整程序 - 自包含:不依赖外部文件(除非使用
#include) - 有输出:
cay run标记的示例应有可验证的控制台输出 - 不要过度标记:仅代码片段(非完整程序)不要标记为
cay
添加新测试
为一个新特性添加测试
#![allow(unused)]
fn main() {
// tests/my_feature_tests.rs
use std::process::Command;
#[test]
fn test_my_feature_basic() {
let output = Command::new("target/release/cayc.exe")
.arg("examples/test_my_feature.cay")
.output()
.expect("编译失败");
assert!(output.status.success(), "编译错误: {:?}", output.stderr);
}
}
在 examples/ 中添加测试源文件
// examples/test_my_feature.cay
class Main {
static void main() {
// 测试逻辑
println("expected output");
}
}
测试注意事项
- 必须先构建 release:集成测试调用
target/release/cayc.exe,debug 构建的二进制不会被测试使用 - 临时文件清理:测试会生成
temp_*.exe、temp_*.ll等文件,它们被 git 忽略但会累积 - 串行执行:测试使用全局锁串行运行,避免文件冲突
- 不要删除测试:测试失败时应修复代码,而非删除测试
当前实现状态
本文档记录 Cavvy 编译器各特性的实现状态。所有结论均基于源码、测试和示例程序。
已验证功能
| 功能 | 状态 | 依据 |
|---|---|---|
| 编译流水线 | ✅ 完整实现 | src/lib.rs — Compiler 结构体 |
| 预处理器(#include, #define, #ifdef) | ✅ 完整实现 | src/preprocessor/、tests/preprocessor_tests.rs |
| 词法分析器(logos 基础) | ✅ 完整实现 | src/lexer/、src/lib.rs 单元测试 |
| 解析器(递归下降) | ✅ 完整实现 | src/parser/、tests/parser_tests.rs |
| 语义分析 — 类型检查 | ✅ 完整实现 | src/semantic/type_check.rs |
| 语义分析 — 符号表 | ✅ 完整实现 | src/semantic/symbol_table.rs |
| 语义分析 — 类型推导 | ✅ 完整实现 | src/semantic/type_inference_result.rs |
| 语义分析 — 类层级分析 | ✅ 完整实现 | src/semantic/class_analysis.rs |
| LLVM IR 代码生成 | ✅ 完整实现 | src/codegen/(24+ 文件) |
| IR 系统(自定义 SSA IR) | ✅ 完整实现 | src/ir/(12 文件) |
| LLVM 后端(IR → LLVM IR) | ✅ 完整实现 | src/ir/llvm_backend.rs |
| clang 集成(捆绑) | ✅ 完整实现 | ir2exe_lib.rs、llvm-minimal/ |
| 类、构造函数、继承、重写 | ✅ 可用 | tests/inheritance_tests.rs |
| vtable 动态分发 | ✅ 已实现 | test_vtable_dynamic_dispatch |
| 接口声明、实现与动态分发 | ✅ 可用 | tests/interface_tests.rs |
| private/protected 访问控制 | ✅ 语义分析中检查 | tests/access_control_tests.rs |
| Lambda 与闭包捕获 | ✅ 可用 | tests/lambda_tests.rs |
| 泛型类(解析与类型替换) | ✅ 解析可用 | examples/test_generics_comprehensive.cay |
| 数组字面量初始化 | ✅ 可用 | tests/array_tests.rs |
| @FreeFunction | ✅ 可用 | tests/new_features_tests.rs |
| 顶层函数(public int main()) | ✅ 默认支持 | 0.4.3.0+ 版本推荐风格 |
| switch 语句 | ✅ 可用 | tests/control_flow_tests.rs |
| 异常处理(try/catch/finally) | ✅ 可用 | tests/error_tests.rs |
| 结构体(struct) | ✅ 可用 | tests/struct_tests.rs |
| 枚举(enum) | ✅ 可用 | tests/enum_tests.rs |
| 字符串操作 | ✅ 可用 | tests/string_tests.rs |
| FFI extern | ✅ 可用 | tests/ffi_tests.rs |
| CayBC 字节码 | ✅ 完整实现 | src/bytecode/(7 文件) |
| 字节码序列化 | ✅ 完整实现 | src/bytecode/serializer.rs |
| 字节码 JIT/AOT | ✅ 实现 | src/bytecode/jit.rs |
| 字节码混淆 | ✅ 实现 | src/bytecode/obfuscator.rs |
| 包管理器 Cavly | ✅ 完整实现 | src/cavly/(6+ 文件) |
| RCPL 交互式环境 | ✅ 完整实现 | src/rcpl/(4 文件) |
| LSP 语言服务器 | ✅ 实现 | src/bin/cay-lsp.rs |
| 文档工具 cay-dt | ✅ 实现 | src/bin/cay-dt.rs |
| 依赖分析 cay-dp | ✅ 实现 | src/bin/cay-dp.rs |
| 独立预处理器 cay-pre | ✅ 实现 | src/bin/cay-pre.rs |
| IR 内联优化 | ✅ 实现 | src/ir/inliner.rs |
| IR 验证器 | ✅ 实现 | src/ir/verification.rs |
| 内联 IR(__ir { } 块) | ✅ 实现 | src/ir/inline_ir.rs |
| 源代码映射(debug info) | ✅ 实现 | src/codegen/source_map.rs |
| Result 错误处理 | ✅ 可用 | caylibs/Result.cay、caylibs/Error.cay、src/codegen/expressions/try_op.rs |
| panic / abort 内建函数 | ✅ 可用 | src/codegen/expressions/builtin.rs、src/codegen/expressions/call/main.rs |
已知限制与未实现特性
| 特性 | 状态 | 说明 |
|---|---|---|
数组初始化语法 new Type[] { 1, 2, 3 } | ❌ 不支持 | 需先声明大小再逐个赋值 |
需要注意的行为
接口动态分发
接口调用通过对象 vtable 实现运行时分发。两个不同实现类经同一个接口类型调用同名方法时,按运行时类型选择实现:
Animal a1 = new Dog(); a1.speak(); // → "汪汪!"
Animal a2 = new Cat(); a2.speak(); // → "喵~"
相关测试:
test_interface_assignment_compatibility— 基础多实现调用test_interface_dispatch_uses_runtime_type_with_different_class_slots— 不同 vtable 槽位test_interface_dispatch_with_args_and_return_uses_runtime_type— 带参数和返回值
实验性工具
以下工具已有入口或实现片段,但尚未作为稳定发布接口:
| 工具 | 状态 |
|---|---|
cay-bcgen(字节码生成器) | ⚙️ 基础实现完成 |
这些工具在补充完整的使用文档和集成测试前,暂不标记为稳定。
测试覆盖率
| 测试文件 | 覆盖内容 |
|---|---|
tests/inheritance_tests.rs | 继承、重写、vtable 分发 |
tests/interface_tests.rs | 接口声明、实现、多态分发 |
tests/lambda_tests.rs | Lambda 表达式、闭包、捕获 |
tests/generic_tests.rs | 泛型解析、类型替换 |
tests/array_tests.rs | 数组声明、访问、初始化 |
tests/access_control_tests.rs | private/protected 检查 |
tests/control_flow_tests.rs | if/for/while/switch/break/continue |
tests/string_tests.rs | 字符串方法和操作 |
tests/struct_tests.rs | 结构体声明和使用 |
tests/enum_tests.rs | 枚举声明和使用 |
tests/ffi_tests.rs | FFI 外部函数调用 |
tests/error_tests.rs | 异常处理 |
tests/preprocessor_tests.rs | 所有预处理器指令 |
tests/vtable_tests.rs | vtable 布局和动态分发 |
| 40+ 文件 | 全面覆盖编译器各模块 |
LSP 语言服务器协议
Cavvy 内置 LSP(Language Server Protocol)语言服务器,提供 IDE 级别的代码辅助功能。
概述
LSP 服务器位于 src/bin/cay-lsp.rs,通过标准 LSP 协议与编辑器通信。对应的 VS Code 扩展位于 vscode-extension/。
支持的功能
| 功能 | LSP 请求 | 状态 |
|---|---|---|
| 语法高亮 | —(由扩展处理) | ✅ |
| 诊断信息 | textDocument/publishDiagnostics | ✅ |
| 跳转到定义 | textDocument/definition | ✅ |
| 自动补全 | textDocument/completion | ✅ |
| 悬停信息 | textDocument/hover | ✅ |
| 错误标记 | textDocument/semanticTokens | ✅ |
| 文档符号 | textDocument/documentSymbol | ✅ |
启动方式
# 启动 LSP 服务器
cay-lsp
# 或通过编辑器配置启动
# cay-lsp 从 stdin 读取 LSP 消息,输出到 stdout
LSP 服务器通过标准输入/输出(stdio)与编辑器通信,遵循 LSP 3.x 规范。
编辑器配置
VS Code
项目包含内建的 VS Code 扩展(vscode-extension/):
- 在 VS Code 中打开 Cavvy 项目
- 从
vscode-extension/安装扩展 - 打开
.cay文件,扩展会自动启动 LSP 服务器
其他编辑器
对于支持 LSP 的其他编辑器(Neovim、Emacs、Sublime Text 等),配置 LSP 客户端连接到 cay-lsp:
LSP 命令: cay-lsp
传输方式: stdio
文件类型: cay, cavvy, eol
VS Code 扩展结构
vscode-extension/
├── package.json # 扩展配置
├── syntaxes/
│ └── cavvy.tmLanguage.json # 语法高亮规则
└── src/
└── extension.ts # LSP 客户端
语法高亮
cavvy.tmLanguage.json 定义了完整的 TextMate 语法规则,覆盖:
- 关键字(
class、interface、if、for等) - 字面量(数字、字符串、字符)
- 注释(
//、/* */) - 类型标识符
- 操作符
编译器集成
LSP 服务器使用 cavvy::Compiler 库进行实时代码分析:
- 文件内容变化时触发诊断
- 编译器在前端阶段(预处理 → 词法分析 → 解析 → 语义分析)运行
- 收集错误和警告,发布为 LSP 诊断
- 利用符号表提供定义跳转和自动补全
注意事项
- LSP 服务器仅执行编译器的前端阶段,不生成代码
- 大文件的诊断可能有一定延迟(取决于编译器性能)
- 扩展和 LSP 服务器共同维护时,需要同步更新语法规则和编译器能力
维护文档
本文档说明如何维护 Cavvy 文档站。
原则
- 准确性优先:文档只描述源码中已实现或明确标注为受限的行为
- 可测试性:标为
cay的代码块必须能被自动测试(doc-test.py验证) - 完整性:每个功能应有对应的文档页面
- 时效性:实现状态变更时同步更新
current-status.md
文档结构
docs/
├── README.md # 文档索引(mdBook 入口重定向)
├── index.md # mdBook 首页
├── SUMMARY.md # mdBook 导航结构
├── getting-started.md # 快速开始
├── language-overview.md # 语言总览
├── language-reference.md # 语言参考手册
├── compiler-architecture.md # 编译器架构
├── cli.md # CLI 工具参考
├── preprocessor.md # 预处理器指南
├── ffi.md # FFI 外部函数接口
├── toolchain.md # 工具链与构建
├── testing.md # 测试指南
├── cavly.md # 包管理器
├── bytecode-format.md # CayBC 字节码格式
├── current-status.md # 实现状态
├── maintaining-docs.md # 本文档
本地预览
# 安装 mdBook
cargo install mdbook --locked
# 本地预览文档站
mdbook serve --open
# 构建静态站点
mdbook build
新增页面
- 在
docs/下创建 Markdown 文件 - 在
docs/SUMMARY.md中添加导航链接 - 确保
cay标记的代码块是完整程序 - 运行文档测试验证
.\scripts\test-docs.ps1
文档测试
文档测试由 scripts/doc-test.py 自动执行:
- 扫描
docs/**/*.md和README.md - 抽取语言标记为
cay、cavvy、eol的代码块 - 默认使用
cay-check进行语法检查 cay run标记的块会编译并运行cay ignore标记的块会被跳过
写作规范
-
代码块语言标记:
cay— 完整程序,会被自动检查cay run— 完整程序,会被编译并运行cay ignore— 片段,跳过检查text、bash、powershell— 非 Cavvy 代码cay run— 编译并运行
-
章节标题:使用 ATX 标题(
#符号),层级不超过 4 级 -
链接:文档内部链接使用相对路径(如
[架构](compiler-architecture.md)) -
版本信息:
.verinfo中的版本号变更后,检查README.md和index.md是否需要更新
CI/CD
.github/workflows/docs.yml(或 jekyll-gh-pages.yml):
- 推送到
main分支时自动构建 mdBook - 部署到 GitHub Pages
- 文档测试在 Windows 环境中执行(编译器工具链验证)
常见问题
文档中的代码块无法编译
- 确认代码块是完整程序(包含
Main类和main方法) - 确认使用了正确的语言标记
- 运行
python scripts/doc-test.py --build查看具体错误
新增页面不显示在导航中
检查 docs/SUMMARY.md 是否添加了对应的链接条目。