中心:一个问题,三件资产
这是 0.1.4 的方向修正(docs/175)。真正只属于 Loment 的,是可校验的边界。AI 训练与系统开发是这个中心的消费者,不是两门要各自精通的手艺——Agent 生成的代码要能放心跑(训练侧)与要能进内核(系统侧),问的是同一个问题。
多语法:C / Rust / Go / Java / Python 的源,读得进来
一条链走两段:外源源码先由 potato_from 变成 Potato 形式对象,再由 lomt_from 发成 L1 的 .lomt 文本,然后进冻结的核心。判据 loment_multisyntax_test 55/55——五种语法每种十条,加五条跨语法。
| 语法 | 哪条腿 | 第十条判据是什么 |
|---|---|---|
| C · Rust · Go | 链接型 | 真跑:编目标文件 → 生成接口单元 → L1 里调用 → 链 → 跑出预期退出码 |
| Java · Python | 运行型 | 钉住它们一个都不会被发成 extern fn——JVM / 解释器的调用约定不是 C ABI(Java 的 native 走 JNI,头两个参数还是 JNIEnv* / jobject)。这是「要调它们得走进程桥」的机器可验形式。 |
只出接口,不出带体的实现
Potato 的 schema 里没有函数体——表示层记的是「有什么」(结构 / 能力 / 契约 / 布局),不是「怎么算」。所以这一版的前端产出的是声明单元:类型、常量、能力、函数签名,函数一律发 pub extern fn。
这不是权宜之计:一份「我把那个模块的接口抄过来了」的文件,语义上就是对外部符号的 FFI 声明。要做的是「用 Rust 语法写整个 Loment 程序」,得让表示层携带实现——那是 Potato 的一次结构性扩展,不是加一个字段。
认错语言比认不出更坏
判语言分两层:后缀优先(.c / .rs / .go / .java / .py),后缀不在表里时按内容特征判——而这条只用于 .lomt,也就是「一份装着别的语法的 Loment 源文件」。
认不出时它报「请用 --lang 指明」,不猜。理由很实在:一份被猜成 C 的 Loment 文件会被转出一堆错的 extern 声明,而它不会报错。
extern "C")。以及几处很具体的:任何收
char * 的 C 函数都用不了——char * 映成 str(指针加长度),而 FFI 那道闸门只收标量与 ptr;判据要求它必须被报出来,不是静默丢掉。跳过的函数每条都带原因写到 stderr,--out 模式下还在成功行里带条数——静默才是敌人,不是「拒绝」。
兼容层:十种语言的库能调到
extern fn 声明,调用点走平台 C ABI,构建时 loment build --link lib.o 链进来。判据 loment_ffi_test 18/18,没有一个 SKIP——下面每一个数都是这台机器上真跑出来的退出码。
| 语言 | 走哪条腿 | 实测 |
|---|---|---|
| C | C ABI:--link | 退出码 52;三个实参 123、多目标互引 22、归档只挑需要的成员 22 |
| C++ | C ABI(extern "C" 包一层) | 退出码 42 |
| Rust | C ABI(#[no_mangle] pub extern "C") | 退出码 42 |
| Zig | C ABI:下载 tarball 编出 .o | 退出码 42 |
| Go | 进程桥(不是 c-archive) | 退出码 5 |
| Python | 进程桥 | 退出码 9 |
| JavaScript | 进程桥 | 退出码 7 |
| Perl | 进程桥 | 退出码 7 |
| Lua | 进程桥(源码自建) | 退出码 5 |
| Java | 进程桥(下载 JDK) | 退出码 7 |
还有一段不存在:库里那句话中间的「这个库在哪、什么形态、怎么取、怎么装」——库系统里「外部库」这个概念还没有。所以「能调到」目前的意思是「手工把一个对象喂给
--link 能跑通」,不是「install 一个 zlib 就能用」。这句话写在这里,是因为仓库自己的审计(docs/183)点了名:README 的措辞比实情强。
真正的工作量不在「多加几个语言」
原来只能吃一个自包含的 .o:不许有重定位、不许有未定义符号。可真实的 C 库不长这样——它是多目标文件的归档,成员之间互相引用,每个成员都带重定位。所以正题是机制:重定位(64 / PC32 / PLT32 / 32 / 32S / PC64)、ar 归档解析与固定点成员选择、跨对象符号解析。
有一类重定位硬拒:指向非代码节的。但 .eh_frame / .debug* / .comment / .note* 这类元数据节跳过——不摆它、代码也引用不到。名单里不含 .data / .rodata:丢掉那些是静默错编。
自举侧曾经在静默错编
产品路径走的是自举链接器(lomelf.lomt),不是参考实现。在补上之前,它对重定位静默忽略——那是错编,不是「不支持」。现在它包含重定位,并与参考实现逐字节相同。
同一类病在别处也犯过:lomelf 曾经对超过缓冲上限的输入静默截断,产物在启动时直接段错误。现在它报错,上限也从 2 MiB 提到 4 MiB。
--link 配 --target pe 是明确拒绝。原计划打算归档做完再试 Go 的 c-archive,实测走不通(它的对象带自己的运行期),如实记在 docs/173。
choose:模式、开关、写法
一个项目要么 freestanding、要么 hosted,由一条 choose 定死。这条规矩的收益不只是防手滑——模式是 Potato 形式对象里的一个字段,于是独立校验器不读源码就能判「这个产物声明自己是 no_std,而它引用的原语全在 freestanding 子集里」。
| 写法 | 结果 |
|---|---|
| 不写 choose | std(默认) |
choose std / choose no_std(核心的两种硬写法) | 项目级模式;只能出现在根单元、恰好一次;库不许写 |
set choose <名字> { … } · choose <名字> · choose close <名字> | 用户可以自己定义的开关:打开时编进去,关闭时真的不编(词法与括号配对仍跑,解析 / 语义 / 代码生成跳过)。上限 500 条。 |
choose write grammar <名字>(文件头,module 之前,至多一次) | 声明这份源码用哪种写法读。六种规范名:loment / c / python / java / go / rust;16 条拼法归到 8 个规范名上。判据 loment_grammar_test 14/14(枚举对拍 469 条)。 |
choose write grammar python 之后,语义仍然全是 Loment 的——不再有「CPython 的向下取整和 Loment 不一样」这类两套语义的问题。原先那对为 CPython 语义加的辅助方向反了,已经拆掉。
std 是分层,不是替换
「不再 no_std」这个说法危险,要写准:不是「Loment 不再是 no_std」,而是「Loment 的核一直是 freestanding,hosted 层现在有了」。Loment 在系统侧的差异点就是 freestanding、确定性、无 GC;而内核线手上全是纯 Loment 产物,那是跨线契约。
这一轮往核里加了两个模块:mem(字节级 copy / move / fill / zero / eq / find,加小端 16 / 32 位读写)与 num(十进制 / 十六进制双向);sha256 改成 use mem。loment/lib/ 从三个文件长到五个。判据 loment_std_test 2/2,27 项与 Python 版逐字节一致——这是 std 第一次有「算得对不对」的判据,而不只是「编得过」。hosted 层还没做:它卡在一处跨线决定上。
Potato 从 v1 长到 v6
形式对象没有变胖,它只是把「读者需要知道的东西」逐版收进来:v2 加必填 mode(这个程序是 std 还是 no_std);v4 加 dialects(方言);v5 加 bodies(外来语块);v6 加 grammar(这份源码用哪种写法读)。校验器同时接受 v0 / v1 / v2,逐版收口。
mode 那条判据有一半在本仓验不了:独立校验器住在另一条线上,本机没有那个仓。所以本仓只能验到「校验器会红」这一半,另一半要等那边跑。记在这里,不假装验过。
进度:说清楚哪些没做完
100 个里程碑按依赖排序推进。下表由脚本从路线图文档生成,不是手写的摘要。
已经闭环的几件事
- 自举定点 —— 编译器编译自身;三阶段产物 IR 逐字节全等(种子 1 641 696 B——这个数随驱动单元变化,由 loment_seed_test 每次重算钉住,不是常量)。
- 原生后端 —— LLVM IR 路径与 Rust 转译路径双路径差分,逐值一致;自研后端 lomelf 把同一份 IR 链成 ELF 或 PE。
- 兼容层与多语法 —— 十种语言的库能调到(18/18);五种语法的源读得进来(55/55)。
- 门禁并行 —— 41 条静态判据彼此独立,串行跑纯是浪费。墙钟 25+ 分钟降到约 3 分钟(实测 188.6 秒墙钟 / 各条合计 1029.3 秒)。
- 工具链 —— lomfmt、LSP、包管理器、文档生成器、DWARF 语句级行表、增量构建(冷 82.6 ms → 热 9.0 ms)。命令面 38 条,本身就是用 Loment 写的。
- 内核内运行 —— Loment 用户态程序跑在 FujoOS 上;syscall 层由 .lom 生成,46/46 opcode 覆盖。
止损线写在前面
P2 若三个月内无法与 Rust 原版逐位对齐,就回退到“L0 + 代码生成”路线,L1 归档。这条线在立项文档里就写定了,而不是事后追认。
当时担心的两件事后来都过了:自举 parser 在全语料上与 Python 版逐字符一致(43/43),检查器的规则等价也到了 63/63。所以 L1 没有触发这条止损线。
开源
源代码已经公开:github.com/FujoJTOP/loment。Lompi 也公开了(github.com/FujoJTOP/lompi)——它随包一起安装,但是另一个命令,不是 Loment 官方工具。规范与实现都可复现,这是测量的前提。预编译工具链尚未发布:现在能拿到的是源码。
tools/loment_audit.py 跑完 24 条主张并落盘证据),缺的是第三方复核本身,而这件事作者无法自证。审计包在 docs/160,里面有主张、不主张、复核步骤与对抗性尝试。
波 C 测量:Loment 与 Potato 自己怎么被量
三个对照,都是确定性的、可复现的,不使用随机数:工具链严格档 vs 宽松档,以及宿主 LLM 臂。整页见文档站的《波 C 测量》。
当前主表的「两臂」是工具链的严格 / 宽松档,都确定性、可复现,不得在论文中当作 LLM 对照。语料是 12 个仓库真实文件(每语言 4 个),每个文件的 sha256 记入结果文件,测量与内容绑定。