Aiki:Alpha 里程碑 26 更新
摘要
本文介绍了 Aiki 项目的 Alpha 里程碑 26 更新,详细说明了该语言或工具在语法权威性、解析器工具以及失败语义方面的改进。
<p><a href="https://lobste.rs/s/qlvnlu/aiki_alpha_milestone_26_update">Comments</a></p>
查看缓存全文
缓存时间: 2026/08/16 03:50
# Aiki:Alpha 里程碑 26 更新
来源:https://decuser.github.io/posts/aiki-alpha-mileston26-update/
最近我一直在忙于 Aiki 的开发,过去几天尤其活跃。下周我将回归本职工作。我在 Tarleton State University 担任计算机信息系统教授。因此,我一直在利用暑假剩余的时间来完成几项相关工作。
当前状态标记为 `v0.4.0-alpha-26` (https://github.com/decuser/aiki/tree/v0.4.0-alpha-26)。我不打算为每一个这样的点都创建一个 GitHub Release。随着开发的持续,`master` 分支会不断前进;而标签则用来标记值得记录的状态。
首个公开的 alpha 版本已经包含了语言的大部分核心特性:精确有理数运算、从左到右的求值、一等公民函数、递归与迭代、管道、结构化数据、模块、可恢复错误、隔离并发、图形、标准库、语义分析、可执行文档、行为黄金标准、语法覆盖以及一个实质性的不变式框架。此后大部分工作并未显著扩大这一表层,而是使其底层关系变得更为严格。
## 权威性与语法
首个重要的 alpha 后工作是处理换行符。`grammar.ebnfx` 描述了产生式,但实际的表层规则部分仍存在于解析器代码中:物理换行符是否终止语句,取决于一个私有的完成集和分隔符抑制逻辑。该策略现在已移入语法中。解析器消费它,帮助文档暴露它,结构化分析推导其后果。
这揭示了现有语言的一些有用事实。一些最初看似可能造成问题的延续性符号,如 `(`、`[` 和 `-`,实际上在语句结束后被解析为新表达式的开头。而其他延续性则被当前规则明确阻止。以 `}` 结尾的函数字面量揭示了另一个边界情况,因为 `}` 可以结束一个表达式,但其自身并不属于换行完成集。我并未仅仅为了使规则更对称而更改这些语言选择。现在,它们是清晰可见的设计问题,而非隐藏在解析器代码中的行为。
同样的工作收紧了几种对语法敏感的关系。求值器的覆盖范围根据语法实际能产生的结构进行检查。格式化器的覆盖范围在语法层面是显式的,而非依赖于隐式递归。Linter 对语法节点的知识被检查。二元运算符的成员关系来自语法,而非单独的求值器列表。换行帮助及相关诊断则源自声明的策略。
随后的一轮检查发现,几个消费者各自独立地遍历语法以推导出相同的结构事实。这些推导现在集中在缓存的语法分析中。产生式名称、token 引用、产生 AST 的节点类型、终结符候选项和换行分析只推导一次,并在需要的地方使用。
工作原则很简单:一个事实应只有一个权威来源。消费者可以有各自的策略和表示,但不应独立重建相同的事实。
## 工具与失败语义
换行工作引入了故意不可解析的测试样本,暴露了周围工具的一些弱点。否定解析器样本现在需要显式声明:
```
# @negative parse
```
该声明仅限于有意义的测试夹具中使用。它不能用于普通源代码中,以豁免格式错误的代码不受格式化或 lint 检查。
跟踪这个问题揭示了几个历史遗留问题。递归格式化器可能遇到格式错误的源代码,报告错误,但仍无法正确传播错误。Linter 的格式化预检可能在第一个格式错误的文件后就停止。一些命令内部返回状态码,但顶层可执行文件会丢弃它们,允许一个命令打印失败信息却仍然成功退出。
这些路径现在会将状态传播给 shell。
更完整的 lint 遍历也暴露了模块解析的偏差。运行时通过模块注册表解析公共包名,而 lint 维护了该规则的一个文件系统近似表示。一旦 lint 开始访问之前跳过的源代码,使用 `use("list")` 的合法代码就足以暴露这种分歧。lint 现在使用相同的注册表模型来解析公共包,同时对路径导入保留正常的相对路径处理。
在语法项目之后,我进行了一次更全面的审计,并开始用稳定的标识符和明确的处置方式记录发现。有些被修复,有些被接受,有些被推迟。例如,换行分隔符抑制现在跟踪的是预期的闭合分隔符,而不是一个可能被未匹配的闭合符破坏的聚合深度。记录的要点不是将每个观察都变成工作;而是确保可信的发现在项目结束时不会消失。
## 分发与位置无关性
Aiki 还隐含了一个假设:源代码树就是安装目录。
这之所以可行,是因为开发可执行文件通常位于 `lib/` 目录旁边,但这意味着运行时正确性部分取决于进程启动的位置。现在运行的可执行文件会标识其分发版本。发布的模块相对于该可执行文件查找,用户模块有明确的主目录,命名包发现不会递归扫描当前工作目录下的任意目录树。
因此,正常的安装模型很简单:
```
解压 Aiki
将目录添加到 PATH
运行 aiki
```
`make dist` 构建用户分发包。`make distcheck` 更进一步:它在临时前缀下解压该分发包,从一个不相关的目录运行,在该目录下放置误导性的包树,并验证安装的 Aiki 仍然能正确解析并运行自己的模块。
还有一个单独的 `make baseline` 目标用于开发快照。基线保留了仓库,包括 `.git`,旨在作为一个完整的、可重新开始的开发状态。它不是用户分发包。
这种区分后来在自举过程中再次变得重要。许多从仓库根目录工作正常的特性,结果却依赖于在那里运行。
## 独立的 Aiki 前端
最大规模的 alpha 后工作始于一个关于自我描述的问题:Aiki 有多少部分可以用 Aiki 本身独立实现?
现在在 `selfhost/` 目录下有一个用 Aiki 编写的词法分析器、换行符规范化器和递归下降解析器。它们不调用 Go 实现的词法分析器或解析器。
独立性确实需要重复某些事实。词法分析器必须知道它识别的关键字和运算符。换行规范化器必须知道完成和抑制策略。这些重复的事实是根据从 `grammar.ebnfx` 派生的值进行检查的;算法本身保持独立。
词法和换行的实现是通过与审查过的一致性投影进行比较,与 Go 实现进行对照。对于解析,我重用了现有的、符合语法结构的解析黄金标准,而不是发明另一种期望树格式。结果是在语法和审查过的证据、Go 实现以及 Aiki 实现之间形成了一种有用的三方关系。
这不同于简单地围绕同一个实现添加更多测试。第二个前端有自己的代码和出错的独立机会。
## 语言服务
编辑器工作源自对权威性的同样关注。
解析器、格式化器、linter、编辑器扩展和 LSP 服务器很容易成为语言的几个不完整副本。为了让 LSP 服务器不成为 Aiki 语义的另一个所有者,我将可重用的语言知识移入了一个编辑器无关的服务层。
该层现在提供文档分析、结构化诊断、符号、定义查找、规范格式化、补全和悬停信息。`aiki lsp` 是该服务之上的适配器,而非服务本身。
这已经通过三个相当不同的编辑器进行了演练。Xed 使用其常规的 GtkSourceView 机制进行词法呈现,并使用一个轻量级插件实现实时诊断。VS Code 使用常规的轻量级语言客户端,并且已经测试了诊断、补全、悬停、转到定义和格式化文档。对于 nvi,Aiki 生成普通的标签文件:
```
aiki tags -o tags source.ai
```
nvi 以通常的方式使用它们。
实时编辑器测试很有用。Xed 暴露了“另存为”文档的身份问题以及零宽度 EOF 诊断的呈现问题。VS Code 暴露了无效的 JSON-RPC null 响应以及一个假设:定义请求应定位在标识符的第一个字节处。桌面启动的编辑器还表明,不能从交互式 shell 的 `PATH` 推断其进程环境。
这些都是适配器问题,而不是将 Aiki 语义移入适配器的邀请。
## 从解析器到解释器
独立前端随后发展成了一个解释器。
Aiki 现在有一个用 Aiki 编写的运行时环境和求值器,支持词法查找、遮蔽、对外部作用域的赋值、闭包、递归、剩余参数、列表、结构体、索引、字段访问、语句、控制流、模式匹配、管道和返回。
这项工作暴露了语言表层的两个空白。
解析器可以读取任意符号字面量,但普通的 Aiki 没有从字符串构造任意原生符号的操作。因此,一个自举的解释器要么需要维护一个有限的私有符号表,要么需要引入自己的替代表示,才能将源词法单元忠实地转换为相应的值。这两种情况都是不可接受的。
结果是:
```
to_symbol("foo")
```
它产生 `:foo`。
结构体值暴露了相关的边界问题。底层已经知道如何构造它们,但普通 Aiki 不知道。这变成了:
```
shaped(:point, [1, 2])
```
这些是语言上的小补充,但它们源自一个有用的标准:用 Aiki 编写的 Aiki 实现,不应需要为普通 Aiki 源代码可以表达的值使用私有表示。
## 模块与 HAL 边界
模块需要类似的决策。
自举求值器很容易将 `import()` 委托回宿主解释器,但这也会将源模块语义委托给正在被检查的实现。因此,自举路径自行执行 Aiki 源代码模块的加载:
```
解析
读取
词法分析
规范化
解析
求值
收集导出
缓存
```
平台设施仍然是宿主能力。一个特权引导程序捕获受信任库模块所需的 HAL 函数,并将它们安装到解释库环境中,而不将这些原始绑定暴露给普通调用者。
这种区分是有意的。Aiki 源代码语义可以在 Aiki 中实现。文件访问、时钟、图形、原生正则表达式和其他平台效果仍然必须来自底层。
## 行为符合性
通过独立求值器运行现有的行为样本,发现了一个更专注的求值器测试所遗漏的语义错误。
Aiki 的可恢复错误,如:
```
[@error, :math, "division by zero"]
```
是普通值。它们可以被绑定、返回、检查和匹配。
自举求值器最初使用 `is_error()` 来识别其自身的内部失败,从而将可恢复的 Aiki 值与求值器停止混为一谈。解释器现在使用单独的私有故障表示来处理其自身的控制路径,使得 `[@error, ...]` 值保持可恢复性。
行为符合性工作现在涵盖了代表性的算术、闭包与递归、匹配、管道、相对导入、字符串、文件效果、纯模块与 HAL 支持的模块、字节、哈希、正则行为以及其他现有的语言样本。并发、图形、仅调试器场景和交互式 I/O 仍然不在当前的自举证明范围内,而非被静默地计入覆盖范围。
## 递归自我解释
最终目标是运行一个用 Aiki 编写的解释器,该解释器通过自身执行。
最初的尝试似乎将大部分时间花在解析内部解析器上,这表明在树遍历解释器下进行递归下降解析是主要问题。语义分析显示并非如此。很大一部分成本来自独立词法分析器的字符处理:在大型源字符串上,重复的字符串索引导致重复的 rune 物化。
`string.chars` 现在有一个线性的底层实现,并且自举词法分析器对源代码只快照一次,并避免了一些重复的表扫描和位置计算工作。这减少了测量到的工作量,足以暴露下一个问题。
这些问题属于模块问题,而非解析器问题。独立加载器中的路径回退与宿主加载器不完全匹配。涉及 `.` 和 `..` 的不同路径可能指向同一个物理模块,但成为单独的缓存条目。从仓库外部递归运行后,暴露了一个进一步的假设:自举加载器需要访问与宿主加载器相同的有序分发模块根目录。
这些问题在没有将内部解析器或求值器交还给 Go 的情况下得到了纠正。
现在产生的路径是可行的:
```
Go 托管的 Aiki
-> 用 Aiki 编写的解释器
-> 自举加载的 Aiki 解释器
-> 1 + 2 * 3
-> 9
```
结果是 `9`,因为普通的 Aiki 二元表达式是从左到右求值的:`(1 + 2) * 3`。相同的可见语义规则在第三级得以保留。
Go 仍然是生产运行时和引导底层。自举实现是第二个实现和一致性边界,而不是试图假装底层机器或宿主运行时已经消失。
## 分析分析器
语义分析已经存在于首个 alpha 版本中。在自举工作中,它变得足够有用,以至于分析本身值得审查。
递归分析文件很大。我没有因为它们看起来合理就接受它们,而是构建了一个校准实验,从可以从几行 Aiki 代码直接读取语义计数的工作开始。
最初的程序将:
```
1 + 1
```
放在一层、两层、三层和四层的十次嵌套循环中。因此,叶节点计算分别执行 10、100、1,000 和 10,000 次。完成的循环体迭代总数为 10、110、1,110 和 11,110。分析器准确地报告了这些计数。
算术同样直接。每个叶节点贡献一次加法,每次循环体迭代递增一个循环变量,因此预期的算术计数为 20、210、2,110 和 2,110。这些计数也是精确的。观察到的比较序列:11、121、1,221 和 12,221,也完全符合对每个循环条件比其体执行多一次测试的结果。
一个更简单的表达式:
```
1 + 2 + 3 + 4
```
包含三个算术运算;分析器报告了三个。
一个传统循环:
```
while i < N {
x = x + 1
i = i + 1
}
```
具有直接可预测的关系:
```
arithmetic = 2N
comparison = N + 1
iteration = N
```
当 `N` 等于 10、100 和 1,000 时,观察到的计数与所有这三个关系完全吻合。
实验随后转向自我解释。加载一个用 Aiki 编写的解释器后,一次额外的求值贡献了:
```
arithmetic 958
comparison 1,082
call 3,255
iteration 832
index 935
```
两次求值正好增加两倍的工作量,四次求值正好增加四倍的工作量。
在下一个解释器级别,固定的基线是
相似文章
Aiki Alpha 2 发布
Aiki Alpha 2 已发布,其特点包括语法驱动的架构、明确的权限边界、大量自托管功能以及系统开发库。此外,还在验证、FFI集成和多平台分发方面进行了改进。
Aiki Alpha 3 发布 - 性能提升
Aiki Alpha 3 发布,带来显著的性能改进,包括更低成本的运行时实现、自适应数表示、增强的性能分析和覆盖率,以及更严格的库约束。
AIPass 更新 #20 - v2.8.2 + v2.8.3:制造测试的检查器与返回0的红叉
AIPass 发布 v2.8.2 和 v2.8.3,引入基于 AST 的测试质量检查器以取代有缺陷的子串评分,并修复退出码和测试文件的问题。
Artificial Analysis Capability Indices v1.1 (2分钟阅读)
Artificial Analysis 发布了 Intelligence Index v4.2,这是一个临时更新,包含新评估如 AA-Briefcase 和 GDP.pdf,增加了私人测试集以防止作弊,关键结果显示 Anthropic 和 OpenAI 领先。
agent-shell 0.55 更新
agent-shell 0.55 新增对 CodeBuddy、Hermes 和 Kimi Code 等新 AI 代理的支持,强调供应商中立的 ACP。该更新还讨论了生态系统变化,并呼吁社区支持。