Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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>?panicabort

文档导航

章节文档
入门快速开始工具链CLI
语言总览参考预处理器FFI
项目与库Cavly
编译器架构字节码格式测试实现状态

文档约定

本文档中的代码块带有特殊标记:

  • 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-astcay-plcay-sir,扩展 --use-embedded-llc
5.3.0调用语法、泛型推断、资源管理支持 Type::method()、省略 new、智能指针和自动 RAII、-g
5.4.0内存映射文件新增跨平台 Mmap/MmapSlice 零拷贝访问
6.1.0显式错误传播新增 Result<T,E>Error 层级、?panicabort

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 支持 atomicrmwcmpxchg

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::Errorstd::IOErrorstd::ParseError 为错误分类、系统错误码、位置和描述提供统一接口。
  • panic 用于带消息终止,abort 用于直接终止;它们不是可恢复错误处理机制。

升级顺序

  1. 从 5.2.0 开始先按错误码更新诊断和测试断言。
  2. 从 5.3.0 开始检查资源所有权,选择智能指针或显式所有权转移。
  3. 从 5.4.0 开始检查 mmap 的失败、越界和生命周期分支。
  4. 升级到 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>,提供 okerrisOkisErrunwrapunwrapOrunwrapErrexpect
  • 新增 std::Error 类型层级及 std::IOErrorstd::ParseError,可携带错误分类、原始系统错误码、位置和描述。
  • 新增 ? 运算符,在函数中自动传播错误并保持返回类型检查。
  • 新增 panicabort 内建函数。

Result 的核心操作是 okerrisOkisErrunwrapunwrapOrunwrapErrexpect。错误类型层级还提供 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、?panicabort 的示例及集成测试。

Cavvy 6.1.0 破坏性变更

Result API

错误处理 API 以 Result<T, E> 为规范形式。旧版专用结果容器应逐步迁移到泛型 Result,并显式声明错误类型。

? 运算符返回类型

使用 ? 的函数必须返回能够承载被传播错误的 Result 类型;不能在普通值返回函数中直接使用。

终止函数

unwrapexpectpanic 在错误分支会终止当前执行路径。需要可恢复流程时应使用 mapandThen 或显式分支。

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 检查。
  • MmapResultFileResult 等专用结果逐步统一为 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::Mmapstd::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.unmapINVALID_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) 及命名空间限定的静态调用。
  • 支持省略 newClassName(args)ClassName<T>(args) 实例化。
  • 增强泛型静态工厂、实例方法返回类型和链式调用的类型推断。
  • 增加代码风格警告,并改善无源码位置错误的调试信息。

标准库与资源管理

  • 新增 std::sys,封装进程、环境变量和命令行参数。
  • 新增泛型 std::ArrayListstd::vector 及迭代器支持。
  • 新增 UniquePtrScopedPtrRcWeakPtr,并支持作用域退出时自动析构。
  • 新增 eprinteprintlnexit 和数组分配内建能力。

工具链与测试

  • 编译器支持 -g,生成可供 GDB 等调试器使用的调试信息。
  • 内联 IR 解析支持 atomicrmwcmpxchg
  • 新增智能指针、容器、静态调用和系统库示例及集成测试。
  • 更新 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-rcplcay-run,ir2exe 嵌入式链接全面修复(系统库、入口点、动态链接器)
  • 泛型系统巩固: 全局泛型类型替换函数、接口 vtable 泛型后缀支持、递归嵌套泛型替换
  • REPL 与输入解析增强: pub/priv 修饰符支持、fn 风格方法定义
  • 新子命令工具: cay-astcay-plcay-sir 三个调试分析工具
  • 运行时库自动构建: Linux 平台自动检测并编译 libcayrt-linux.a

文档导航

Cavvy 5.2.0 新特性详解

目录


诊断系统重构

1. CayError 直接实现 miette::Diagnostic

核心改动(76 个文件,+1336/-2566 行):

5.2.0 对错误诊断系统进行了彻底的扁平化重构。此前,一个编译错误需要经过四层包装转换:CompilerErrorDisplayDiagnostic → 旧 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. 模块路径统一

将所有错误、诊断相关的导入从 errordiagnostic 模块统一迁移到 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-llcIR 生成后直接编译(已有)
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 输入解析器新增对 pubpriv 修饰符的解析支持,允许在交互式环境中定义带访问控制的类和成员:

>> 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-rcplcay-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 / ir2exe5.2.047
cay-check5.2.039
cay-run5.2.023
cavly5.2.013
cay-pre / cay-bcgen / cay-dt / cay-dp / cay-rcpl5.2.012
cay-setup5.2.02
cay-ast / cay-pl / cay-sir5.2.02

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.adefebed

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 命名规范保持一致。

影响: 所有引用 cayErrorcayResult 的代码需要更新。

迁移方式:

- 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. 错误/诊断模块导入路径变更

变更: 所有错误和诊断相关的导入从 errordiagnostic 模块迁移到 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/diagnosticmiette_diagnostic
  • 更新 CLI 参数:--use-llc-lld--use-embedded-llc(如果使用过)
  • 启用 cargo build --release 完整重编译(因增量编译已关闭)

逐步迁移

第一步:更新类型名称

全局替换:

旧名称新名称
cayErrorCayError
cayResultCayResult
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 加四位数字(如 E0001E0002)。

可根据错误类型按以下规则选择 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 参考

文档导航

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. 泛型类型系统

修复并完善了泛型类型系统的多项核心问题,现在泛型类可以稳定用于生产代码。

修复的问题:

  1. 泛型类字段类型替换: 在 class_analysis.rs 中添加字段类型的泛型参数替换,将 T 替换为 GenericParam("T")
  2. 泛型方法参数/返回类型替换: 在 type_check.rs 中对方法参数和返回类型进行泛型参数替换
  3. 多类型参数解析: 在 expr_inference.rs 中修复类型参数解析逻辑,支持 Pair<K, V> 等多参数泛型类
  4. 泛型类方法查找: 在 types.rsTypeRegistry::find_method 中支持泛型类名解析为基础类名
  5. 泛型类型匹配: 在 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 工具链支持

caycir2exe 添加 --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-dtcay-dp 默认启用预处理,支持 --no-preprocess 禁用

5. FFI 增强

  • 新增 extern 函数别名支持(extern fn foo as bar
  • 新增 CString 类型支持
  • 新增 c_int64_tc_uint64_t FFI 类型
  • 扩展 FFI 类型与语句支持
  • 新增内联 IR、内存分配/释放语句

标准库扩展

1. File.cay 标准库

实现完整的文件操作标准库:

  • File 类: 打开、关闭、读写、定位等文件操作
  • FileMode 类: 类型安全的文件模式设置
  • SeekOrigin 枚举: 文件定位支持
  • FileInfo 类: stat-based 文件信息获取
  • LineIterator: 流式逐行读取
  • FileReader.lines(): 返回行迭代器

设计修复:

  • exists() 使用 access() 替代 fopen(),避免修改 atime
  • size() 使用 FileInfo.stat-based 方法,避免 TOCTOU 竞态条件
  • writeInterpolated 使用 StringBuilder 优化,复杂度从 O(n^2) 降至 O(n)
  • readAllLines 改为流式读取,内存使用从 O(file_size) 降至 O(max_line_length)

2. String 方法扩展

新增方法:

  • lastIndexOf
  • startsWith
  • endsWith

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 的返回值判断逻辑
  • 修复 EasyHTTPchar 隐式转 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::Int64f65e3e08
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 而非 117c1b871
字符串拼接整数转换统一使用 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/exeb3574c22
包含文件行号对齐修复预处理器包含文件行号对齐问题c9a2e2f3
宏替换边界检查修复预处理器宏替换边界检查问题8c9d5b08
include 示例文件名错误修正为正确的 File.cayb3574c22

网络模块 (Network.cay)

问题修复内容相关 Commit
socket 句柄类型不匹配修复 TcpSocketTcpServerUdpSocket 类的句柄类型91eca938
跨平台 socket 发送参数类型不兼容修复非 Windows 平台 send/sendto 长度参数类型不匹配7cdaf483
setsockopt 超时参数传递错误修正 TcpServersetsockopt 的超时参数传递7cdaf483
c_int 与 int 类型混用将超时参数数组从 c_int 改为 intf67c30f2
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使用正确值 2147483647d5dffc5e
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 命名混淆重命名为 writeInterpolatedd83ef93b
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.o8b511914
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 usleep580b936a
跨平台可执行文件路径移除硬编码的 .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. 隐式类型转换已移除

变更: 不再允许 stringint 等类型之间的隐式转换,必须使用显式转换。

影响: 以前可以自动转换的代码现在会产生编译错误。

迁移方式:

// 旧代码(不再支持)
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 别名显式引入,不再支持全局回退查找。

影响: 以前可以直接使用 FileString 等标准库类型的代码,现在需要显式引入。

迁移方式:

// 旧代码(不再支持)
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 - “重构类型系统,支持命名空间和泛型类型匹配”

迁移检查清单

升级代码时,请逐项检查:

  • 所有 intString 的转换已改为显式(String.valueOf().toString()
  • 所有 Stringint 的转换已改为显式(Integer.parseInt() 等)
  • 所有标准库类型(FileStringBuilderNetwork 等)已通过 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 时遇到本文档未记录的问题,请通过以下方式报告:

  1. 确认问题可复现
  2. 提供最小复现示例
  3. 说明运行环境(OS、LLVM 版本、Rust 版本)
  4. 提交到项目 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_*.exetemp_*.lltemp_*.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 在构建时会:

  1. 读取 .verinfo 获取版本号
  2. 将版本号与 git 提交哈希组合
  3. 设置 CARGO_*_VERSION 环境变量用于编译时嵌入
  4. 将以下目录复制到 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.rscompile_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_*.exetemp_*.lltemp_*.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.mddocs/**/*.md 中的代码块,抽取语言标记为 caycavvyeol 的示例进行编译检查。

代码块标记

在文档中编写可测试的代码示例:

<!-- 仅语法检查 -->
```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
  • 使用 stable Rust 工具链
  • 目标: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.exesrc/bin/cayc.rs
cay-ir仅生成 LLVM IR(.cay.llsrc/bin/cay-ir.rs
ir2exeLLVM IR → 可执行文件(.ll.exesrc/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-bcgenCayBC 字节码生成src/bin/cay-bcgen.rs
cay-lspLSP 语言服务器src/bin/cay-lsp.rs
cavly包管理器src/bin/cavly.rs
cay-dtToken显示工具src/bin/cay-dt.rs
cay-dpParser显示工具src/bin/cay-dp.rs
cay-pre独立预处理器src/bin/cay-pre.rs
cay-astAST 查看与 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 安装根目录;showdoctoruninstall 同样接受 --rootdoctor 会实际编译一个最小程序,以验证 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_PATHcayc 编译器路径(用于测试)
CAVVY_HOMECavvy 安装目录
CAVVY_LIB_PATH标准库路径
CAVVY_LLVM_PATHLLVM 工具链路径

语言概述

Cavvy(Cay)是一门静态类型、面向对象的编程语言,语法风格接近 Java/C#,同时保留 C 风格预处理器和 FFI。本文档从宏观层面介绍语言的核心特性。


核心设计理念

  1. 静态类型安全:所有变量、参数、返回值在编译时具有确定类型
  2. 面向对象:支持类、继承、接口、运行时多态(vtable 方法分发)
  3. C 风格预处理:支持 #include#define#ifdef 等指令
  4. 原生编译:通过 LLVM IR → clang 编译为高效机器码
  5. FFI 优先:内建外部函数接口,直接调用 C 库
  6. 渐进式支持:CayBC 字节码格式支持 JIT/AOT 执行

类型系统

基本类型

类型描述默认值
int32 位有符号整数0
long64 位有符号整数0L
float32 位浮点数0.0f
double64 位浮点数0.0
char16 位 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);
    }
}

类型系统

基本类型

类型位数描述默认值
void0无返回值
int / i3232有符号整数0
long / i6464有符号整数0L
float / f3232IEEE 754 浮点数0.0f
double / f6464IEEE 754 浮点数0.0
bool / boolean8布尔值false
char16UTF-16 字符‘\0’
string / String不可变字符串null

注意i32i64f32f64 是类型关键字的别名,不能用作标识符(如变量名、类型别名名)。例如 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);

可用操作包括 okerrisOkisErrunwrapunwrapOrunwrapErrexpectexpectunwrapunwrapErr 不应替代可恢复错误分支。

函数返回兼容的 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抽象类(不可实例化)或抽象方法
nativenative 方法声明(由 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 exprelse-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"    // 已包含,自动跳过

搜索路径顺序

  1. 相对于当前源文件所在目录("" 形式)
  2. 命令行 -I 选项指定的路径
  3. caylibs/(当前工作目录或 release 目录旁的复制)
  4. 内置的系统包含路径(<> 形式)
# 通过 -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,也支持 stdcallfastcallsysv64win64


基本声明

使用 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 类型说明
charc_char8 位字符
unsigned charc_uchar无符号 8 位
shortc_short16 位有符号整数
unsigned shortc_ushort16 位无符号整数
intc_int32 位有符号整数
unsigned intc_uint32 位无符号整数
longc_long平台相关长度
unsigned longc_ulong无符号长整数
long longc_longlong64 位整数
floatc_float32 位浮点数
doublec_double64 位浮点数
voidc_void / void无返回值或 void*
char*c_stringC 风格字符串
size_tsize_t大小类型
ssize_tssize_t有符号大小类型
boolc_boolC 布尔值
intptr_tintptr_t指针宽度整数
uintptr_tuintptr_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);
    }
}

注意事项

  1. 类型安全:FFI 调用不进行类型安全检查,错误声明可能造成崩溃
  2. 指针管理:C 堆内存(malloc/free)不受 Cavvy GC 管理
  3. 字符串区别c_stringchar*)与 string 为不同类型
  4. 调用约定:不同平台需要正确的调用约定,否则栈可能损坏
  5. 头文件路径:通过 -I 添加 FFI 头文件搜索路径

Cavly 包管理器

cavly 是 Cavvy 的包管理器和项目构建工具。它负责初始化项目、解析依赖、构建二进制目标、运行测试和配置 FFI 库。

Cavly 的实现位于 src/cavly/,包含 6 个模块:

  • mod.rs — 入口和命令行解析
  • config.rs — 配置类型(PackageConfigBuildConfigFfiConfigDependencyWorkspaceConfigLibConfig
  • 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 newcavly initproject.rs 中的模板生成项目骨架:

// src/main.cay(默认模板)
class Main {
    static void main() {
        println("Hello from Cavvy!");
    }
}

Cavvy 标准库参考手册

Cavvy 标准库提供全面的系统编程能力,涵盖内存管理、字符串处理、文件 I/O、网络通信、数学计算等核心功能。


目录

  1. 核心类型与命名空间
  2. 内存管理 (Allocator)
  3. 字符串处理
  4. 文件 I/O
  5. 网络编程
  6. HTTP 客户端
  7. 数学计算
  8. 容器
  9. 可选值类型 (Optional)
  10. 增强 I/O 工具
  11. FFI 类型系统

核心类型与命名空间

所有标准库组件位于 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_backclear 和容量内的 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_charchar8位字符
c_ucharunsigned char无符号8位
c_shortshort16位有符号
c_ushortunsigned short无符号16位
c_intint32位有符号
c_uintunsigned int无符号32位
c_longlong平台相关
c_ulongunsigned long无符号长整型
c_floatfloat32位浮点
c_doubledouble64位浮点
c_bool_BoolC99布尔
c_voidvoid空类型
c_stringchar*C字符串
size_tsize_t大小类型
ssize_tssize_t有符号大小
intptr_tintptr_t指针宽度整数
uintptr_tuintptr_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::sys5.3.0进程、环境变量和命令行参数
std::ArrayList<T> / std::vector<T>5.3.0泛型容器和迭代器
UniquePtr<T> / ScopedPtr<T> / Rc<T> / WeakPtr<T>5.3.0所有权、RAII、共享引用和弱引用
Mmap / MmapSlice5.4.0跨平台内存映射和零拷贝切片
Result<T,E>6.1.0显式成功/错误分支和错误传播
Error / IOError / ParseError6.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),包括类型映射、内存布局、函数调用约定和运行时服务。


目录

  1. 概述
  2. 类型映射
  3. 内存分配器
  4. 字符串操作
  5. 类型转换
  6. 指针操作
  7. 内存操作
  8. 数组操作
  9. 构建与链接

概述

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位系统: i64void* 大小相同(8字节),可安全互转
  • 结构体布局: 必须与 LLVM IR 中的定义精确匹配

类型映射

LLVM IR 到 C 类型映射

LLVM IR 类型C 类型说明
i8*char*字节指针/字符串
i32int32_t32位有符号整数
i64int64_t64位有符号整数
i1bool布尔值 (stdbool.h)
floatfloat32位 IEEE 754 浮点
doubledouble64位 IEEE 754 浮点
voidvoid无返回值
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.rsSemanticAnalyzer 入口
  • symbol_table.rs — 嵌套作用域的符号表:
    • SymbolTable — 作用域栈
    • Symbol 枚举 — 变量、类、方法、接口、枚举等
  • type_check.rsSemanticAnalyzer 实现,类型兼容性校验
  • type_inference_result.rs — 带错误收集的类型推导
  • expr_inference.rs — 表达式类型推导
  • class_analysis.rs — 类层级分析(方法重写、接口实现验证)

5. IR 系统(src/ir/

自定义 SSA 形式中间表示,共 12 个文件。这是编译器的核心新架构。

关键文件

文件职责
mod.rsIR 模块入口
module.rsIrModule — 顶层容器(全局变量、函数声明)
function.rsIrFunction — 函数定义(参数、基本块列表)
block.rsIrBasicBlock — 基本块(指令列表 + 终止符)
value.rsIrValue — 值枚举(常量、变量、临时寄存器)
types.rsIrType — 类型枚举(整数、浮点、指针、数组、函数签名)
builder.rsIrBuilder — AST → IR 核心转换(约 2000+ 行)
llvm_backend.rsLlvmBackend — IR → LLVM IR 文本渲染
inliner.rs函数内联优化器
inline_ir.rs内联 IR 支持(__ir { } 块)
verification.rsIrVerifier — IR 正确性验证(SSA 合规性)

IR 设计特点

  • SSA 形式,每个赋值产生新的版本
  • Phi 节点用于控制流合并点
  • 强类型,每个值有确定的 IrType
  • 支持内联 IR 嵌入(__ir { }

6. 代码生成(src/codegen/

代码生成模块将语义分析后的 AST 转换为 LLVM IR 文本。

核心文件

  • generator.rsCodeGenerator 主入口
  • context.rsCodegenContext(符号映射、作用域)
  • 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.rsBytecodeModule 顶层结构
  • constant_pool.rs — JVM 风格的常量池(字符串、整数、浮点、类引用、方法句柄)
  • instructions.rs — 100+ 指令操作码(Opcode 枚举)
  • jit.rs — JIT/AOT 编译器(JitOptionsjit_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
ir2exeIR → .exe仅 clang
cay-check语法语义检查语义分析
cay-run编译并运行.exe + 运行
cay-rcpl交互式环境循环编译
cay-bcgen字节码生成CayBC
cay-lspLSP 服务器
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.rsCavlyConfigPackageConfigBuildConfigFfiConfigDependencyWorkspaceConfigLibConfig
  • builder.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.rsBytecodeModule 顶层结构
constant_pool.rsJVM 风格的常量池
instructions.rs100+ 指令操作码
jit.rsJIT/AOT 编译器
linker.rs自动链接
serializer.rs二进制序列化
obfuscator.rs字节码混淆

BytecodeModule(mod.rs

BytecodeModule 是 CayBC 的顶层容器,包含:

  • 常量池ConstantPool) — 所有常量的索引表
  • 类定义 — 类的字段、方法、接口实现
  • 方法体 — 字节码指令序列
  • 元数据 — 版本号、源文件名、调试信息

常量池(constant_pool.rs

JVM 风格的常量池,支持以下常量类型:

常量类型描述
Utf8UTF-8 编码字符串
Integer32 位整数常量
Long64 位整数常量
Float32 位浮点常量
Double64 位浮点常量
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 字节码的对比

特性CayBCJVM
常量池✅ 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
// 这个不会被测试
```

写文档测试的最佳实践

  1. 完整程序:所有被测试的代码块必须是包含 main 方法的完整程序
  2. 自包含:不依赖外部文件(除非使用 #include
  3. 有输出cay run 标记的示例应有可验证的控制台输出
  4. 不要过度标记:仅代码片段(非完整程序)不要标记为 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");
    }
}

测试注意事项

  1. 必须先构建 release:集成测试调用 target/release/cayc.exe,debug 构建的二进制不会被测试使用
  2. 临时文件清理:测试会生成 temp_*.exetemp_*.ll 等文件,它们被 git 忽略但会累积
  3. 串行执行:测试使用全局锁串行运行,避免文件冲突
  4. 不要删除测试:测试失败时应修复代码,而非删除测试

当前实现状态

本文档记录 Cavvy 编译器各特性的实现状态。所有结论均基于源码、测试和示例程序。


已验证功能

功能状态依据
编译流水线✅ 完整实现src/lib.rsCompiler 结构体
预处理器(#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.rsllvm-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.caycaylibs/Error.caysrc/codegen/expressions/try_op.rs
panic / abort 内建函数✅ 可用src/codegen/expressions/builtin.rssrc/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.rsLambda 表达式、闭包、捕获
tests/generic_tests.rs泛型解析、类型替换
tests/array_tests.rs数组声明、访问、初始化
tests/access_control_tests.rsprivate/protected 检查
tests/control_flow_tests.rsif/for/while/switch/break/continue
tests/string_tests.rs字符串方法和操作
tests/struct_tests.rs结构体声明和使用
tests/enum_tests.rs枚举声明和使用
tests/ffi_tests.rsFFI 外部函数调用
tests/error_tests.rs异常处理
tests/preprocessor_tests.rs所有预处理器指令
tests/vtable_tests.rsvtable 布局和动态分发
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/):

  1. 在 VS Code 中打开 Cavvy 项目
  2. vscode-extension/ 安装扩展
  3. 打开 .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 语法规则,覆盖:

  • 关键字(classinterfaceiffor 等)
  • 字面量(数字、字符串、字符)
  • 注释(///* */
  • 类型标识符
  • 操作符

编译器集成

LSP 服务器使用 cavvy::Compiler 库进行实时代码分析:

  1. 文件内容变化时触发诊断
  2. 编译器在前端阶段(预处理 → 词法分析 → 解析 → 语义分析)运行
  3. 收集错误和警告,发布为 LSP 诊断
  4. 利用符号表提供定义跳转和自动补全

注意事项

  • LSP 服务器仅执行编译器的前端阶段,不生成代码
  • 大文件的诊断可能有一定延迟(取决于编译器性能)
  • 扩展和 LSP 服务器共同维护时,需要同步更新语法规则和编译器能力

维护文档

本文档说明如何维护 Cavvy 文档站。


原则

  1. 准确性优先:文档只描述源码中已实现或明确标注为受限的行为
  2. 可测试性:标为 cay 的代码块必须能被自动测试(doc-test.py 验证)
  3. 完整性:每个功能应有对应的文档页面
  4. 时效性:实现状态变更时同步更新 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

新增页面

  1. docs/ 下创建 Markdown 文件
  2. docs/SUMMARY.md 中添加导航链接
  3. 确保 cay 标记的代码块是完整程序
  4. 运行文档测试验证
.\scripts\test-docs.ps1

文档测试

文档测试由 scripts/doc-test.py 自动执行:

  • 扫描 docs/**/*.mdREADME.md
  • 抽取语言标记为 caycavvyeol 的代码块
  • 默认使用 cay-check 进行语法检查
  • cay run 标记的块会编译并运行
  • cay ignore 标记的块会被跳过

写作规范

  1. 代码块语言标记

    • cay — 完整程序,会被自动检查
    • cay run — 完整程序,会被编译并运行
    • cay ignore — 片段,跳过检查
    • textbashpowershell — 非 Cavvy 代码
    • cay run — 编译并运行
  2. 章节标题:使用 ATX 标题(# 符号),层级不超过 4 级

  3. 链接:文档内部链接使用相对路径(如 [架构](compiler-architecture.md)

  4. 版本信息.verinfo 中的版本号变更后,检查 README.mdindex.md 是否需要更新


CI/CD

.github/workflows/docs.yml(或 jekyll-gh-pages.yml):

  • 推送到 main 分支时自动构建 mdBook
  • 部署到 GitHub Pages
  • 文档测试在 Windows 环境中执行(编译器工具链验证)

常见问题

文档中的代码块无法编译

  1. 确认代码块是完整程序(包含 Main 类和 main 方法)
  2. 确认使用了正确的语言标记
  3. 运行 python scripts/doc-test.py --build 查看具体错误

新增页面不显示在导航中

检查 docs/SUMMARY.md 是否添加了对应的链接条目。