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。