在苹果生态中,Shortcuts(原 Workflow)已经成为自动化工作流程的核心工具。然而,Shortcuts 的可视化编辑方式在处理复杂逻辑时显得笨拙 —— 重复循环、条件分支和变量管理随着项目规模增长而变得难以维护。Cherri 作为一个新兴的编程语言,试图通过提供一套完整的编译工具链,将开发者熟悉的语法直接转换为可运行的 Shortcut,从而解决这一痛点。
编译目标的特殊性
Apple Shortcut 本质上是一个包含动作序列的 XML/Plist 结构,每个动作携带特定的参数和连接关系。与传统编译器目标(如 LLVM IR、WebAssembly 或机器码)不同,Shortcut 的运行时环境是苹果设备上的 Shortcuts 应用,它对内存使用和执行流程有严格的约束。Cherri 编译器需要将高级语言构造逐一映射为 Shortcut 动作图谱,这一过程涉及语法转换、语义分析和运行时验证三个核心环节。
从技术实现角度看,Cherri 采用 Go 语言编写,提供了命令行工具、VSCode 扩展和 macOS 原生应用三种开发入口。项目采用半自举(Half-bootstrapped)架构 —— 大多数内置动作和类型定义本身也是用 Cherri 编写的,这种设计既证明了语言的表达能力,也为编译器本身的测试提供了天然的真实用例。
语法到动作的转换策略
Cherri 编译过程的核心挑战在于建立语言语法与 Shortcut 动作之间的一一对应关系。与其将多条语言语句编译为单一的复杂动作,Cherri 选择了尽可能保持一对一的翻译关系,这种策略显著降低了调试难度 —— 开发者在 Cherri 代码中的一个表达式,理论上对应 Shortcut 中的一个动作及其参数。
这种设计要求编译器具备完善的语法分析能力。Cherri 的语法参考了 Go 和 Ruby 的设计风格,提供了变量声明、函数定义、条件分支和循环结构等常规语言要素。编译器在前端完成词法和语法分析后,会生成中间表示(IR),其中每个表达式节点都附带类型信息和目标动作标识。随后,后端遍历 IR 树,根据节点类型查询内置的动作映射表,生成对应的 Plist 结构。
以变量操作为例,Cherri 中的常量声明 myVar = "hello" 会被翻译为 Shortcut 的「设置变量」动作,变量名和值作为参数嵌入。而复杂的表达式如 if condition { doSomething() } 则被拆解为条件动作块,包含条件判断和对应的动作序列。这种拆分策略虽然可能导致生成的 Shortcut 动作数量增加,但保证了语义的可预测性和调试的直观性。
类型系统与类型推断
为了在受限的 Shortcut 环境中提供可靠的编程体验,Cherri 实现了完整的类型系统,包括基本类型(字符串、数字、布尔值)、容器类型(数组、字典)以及可选值(Optional)处理。这一设计不仅是语言特性的需要,更是为了在编译期捕获潜在错误,避免运行时因类型不匹配导致的 Shortcut 执行失败。
类型推断是 Cherri 类型系统的另一个重要特性。由于 Shortcut 动作的参数类型往往由动作本身决定,编译器可以基于上下文自动推断变量类型。例如,当开发者编写 result = GetItemFromList(list, 1) 时,编译器能够根据「从列表获取项目」动作的返回类型推断 result 为字符串或任意类型,从而减少类型标注的负担。
此外,Cherri 支持自定义类型和枚举定义,配合类型检查机制,开发者可以在编译阶段发现参数错误、类型不兼容等问题。编译器前端生成的类型信息还会传递到后端,用于生成符合 Shortcut 动作签名的参数,进一步提高了编译产物的正确性。
包管理与签名机制
大型 Shortcut 项目的维护不仅涉及代码组织,还依赖于模块复用和依赖分发。Cherri 内置了基于远程 Git 仓库的包管理器,开发者可以通过声明式的方式引入第三方库,编译器会自动拉取并整合到最终产物中。这种设计借鉴了现代编程语言的依赖管理实践,使 Shortcut 项目的代码复用成为可能。
编译完成后的 Shortcut 需要签名才能在苹果设备上运行。Cherri 支持三种签名方式:优先使用本地 macOS 密钥库进行签名,若不可用则回退到 HubSign 服务或自建签名服务器。这一灵活的签名策略降低了跨平台开发和部署的门槛 —— 开发者可以在 Linux 或 Windows 环境下编写 Cherri 代码,最终通过远程签名服务生成可执行的 Shortcut。
内存优化与产物精简
Shortcuts 应用在运行时对内存占用有明确限制,尤其是重复循环等结构容易导致内存溢出。Cherri 编译器在生成最终产物时会进行优化,移除冗余动作、合并相似操作,并尽可能生成体积更小的 Shortcut 文件。文档中特别提到了针对重复循环的内存优化策略 —— 通过调整循环内部的变量作用域和执行顺序,降低单次迭代的内存峰值。
这种优化在编译器后端实现,涉及数据流分析和死代码消除等经典编译技术。虽然 Shortcut 的执行模型与传统程序不同(基于动作图的拓扑排序执行),但编译器仍可识别可并行的动作序列和可合并的变量操作,从而在保持语义等价的前提下生成更高效的产物。
工程实践的启示
Cherri 的设计为领域特定语言(DSL)编译器开发提供了多个值得借鉴的工程实践。首先,选择一对一语法映射策略虽然牺牲了某些编译优化空间,但大幅提升了开发体验和调试效率,这对面向终端用户的 DSL 尤为重要。其次,半自举架构既是语言能力的证明,也为编译器本身的迭代提供了真实的测试场景。最后,针对特定运行时环境(Shortcuts 应用)的约束进行专项优化,体现了务实而非教条的编译器设计思路。
对于希望在苹果生态中构建自动化工作流的开发者而言,Cherri 提供了一种介于可视化编辑和底层 API 调用之间的中间方案 —— 它保留了高级语言的表达能力和工程化优势,同时生成的产物能够直接运行在 Shortcuts 环境中。这种编译到特定运行时而非通用平台的策略,正是领域特定语言区别于通用编程语言的核心价值所在。
资料来源:Cherri 官方 GitHub 仓库(https://github.com/electrikmilk/cherri)