cli 设计
设计目标
符合性运行由 Python 工具驱动,这些工具会并行启动许多 native 进程。使用一个带 --backend 开关的可执行文件可以让构建保持简单(一个 MoonBit 包、一次链接),同时每个后端保留自己的选项语法。分发器的职责只是选择一个运行器,并将其返回值转换为进程退出状态。
数学背景
除运行器本身的性质外没有其他性质。唯一的契约是功能性的:退出状态即运行器的返回值,运行器的输出只取决于其参数和它读取的文件(参见 internal/runner_cli 设计)。
设计决策
三层结构
语料的解析和执行位于纯的 frontend/* 包中;参数处理、文件访问和输出位于 cli/*_expr_cli 运行器中,每个运行器都是一个带有 run(arguments) -> Int 的库;进程边界(@env.args()、@sys.exit)只存在于 cli 中。由于运行器是库,其用法路径无需启动进程即可进行单元测试,而前端在每个目标上都保持可用。
转发程序名
分发器移除 --backend 及其值,并转发 [program, rest…]。每个运行器都跳过第 0 个元素,因此无论是从分发器调用、从测试调用,还是(原则上)作为独立的可执行文件,运行器的行为都相同。
一个二进制文件,按后端复制
tools/conformance_cli.py 将 src/cli 构建到特定于后端的目标目录中,并将其复制为 <backend>-conformance.exe。因此,不同后端的并行构建永远不会共享 _build 目录或锁,工具调用的可执行文件名也总能说明它运行的是什么。
分发器的帮助优先
--help 在扫描参数时处理,此时后端尚未确定,因此它总是打印分发器的用法并以 0 退出。这使得 --help 在任何组合下都可安全调用;运行器选项则在各自的页面上说明。
正确性 / 不变式
- 每次调用恰好调用一个运行器;当参数无效时(退出码
2)或给出--help时(退出码0)则一个也不调用。 - 退出状态等于运行器的返回值:
0、1或2。 - 除
--backend、其值以及--help/-h之外的参数会原样按顺序传给运行器。
被否决的替代方案
- 四个可执行文件。 四个
main函数完全相同的包和四次链接,却没有任何行为上的收益。 - 子命令(
floating-conformance gda …)。 位置参数形式的后端会与运行器的路径冲突;显式选项则没有歧义。 - 由运行器给出退出码。 在运行器内部调用
exit会使其无法作为库进行测试。
边界
- 仅限 native 目标(通过
moonbitlang/x/fs访问文件,通过moonbitlang/x/sys退出)。 - 不负责语料下载、规划、并行或汇总:这些位于
tools/conformance.py和tools/run_*_interpreter.py脚本中。 - 没有公共 MoonBit API。