FFI · 调用别的语言
extern fn 是 0.1.4 Pre2 加进语言的唯一东西。形状很简单——只有签名、末尾一个分号,没有函数体:
extern fn c_add(a: i32, b: i32) -> i32;
fn _start() {
syscall4(60, c_add(20, 32) as u64, 0, 0); // 退出码 = C 那边的返回值
}
两条腿,按「对方是什么」分
| 对方 | 怎么接 | 为什么 |
|---|---|---|
| C / C++ / Rust / Zig / Go / Swift / C# / Fortran | extern fn 声明 + loment build --link lib.o |
它们导出的是同一种符号与同一套传参约定,所以加一个语言就是加一条构建配方加一条判据 |
| Python / Java / JS / Lua / Ruby | 进程桥 loment/lib/proc.lomt(起一个解释器进程,交给它,读回 stdout) |
它们不是导出 C ABI 的库,是解释器。代价是多一条进程边界 |
一个容易误读的区分:
「支持 7 个语言」是保守说法。机制上两条腿各自覆盖一整族——C ABI 那一族是「任何导出 C 符号的东西」,不是七种语言。真正说的是:配方与判据写出来的有七个,其中本机实测跑通五个,余下因为这台机器没装那些工具链而 SKIP。缺的是工具链,不是通路。
这台机器上真跑出来的
| 语言 | 走哪条腿 | 结果 |
|---|---|---|
| C | --link | 退出码 52 |
| C++ | --link(extern "C" 包装) | 退出码 42 |
| Rust | --link(#[no_mangle] extern "C") | 退出码 42 |
| Python | 进程桥(python3 + import json) | 退出码 9 |
| JavaScript | 进程桥(node + JSON.stringify) | 退出码 7 |
| Java / Zig / Go / Swift / C# / Fortran | 配方在判据与设计文档里 | SKIP |
一条判据差点没抓住的东西
判据里有一条是证伪:把 C ABI 的前两个寄存器对调,判据必须红。第一版没红——因为 C 夹具只做了 a+b 与 a*b,而这两个运算可交换,寄存器对调之后结果一模一样。换成不可交换的 a-b 与 a*100+b*10+c,对调立刻打红。
这条值得单独写出来:一条不会红的判据不是判据,是装饰。它在第一版里是绿的吗?是。它测到了任何东西吗?没有。
边界(这一版不做的事)
- 签名只收标量与 ptr ——
i8..i64/u8..u64/bool/ptr。str与按值传的聚合类型都在外面,报 E021。宁可不支持,也不静默错编。 - 不支持变参与回调 ——
printf那类变参函数不支持;把 Loment 函数当函数指针传出去也不支持(lomelf至今没有间接调用)。 - 只做静态链接 —— 动态库(.so / .dll)不支持,外部目标文件由使用方自己准备。
- 进程桥多一条边界 —— 运行期那一族多了进程边界,数据要过 stdout/退出码。它是「能调到」,不是「零成本调到」。
纯 Loment 程序不受影响。
它仍然是 freestanding 的——无运行时、无 libc、不需要 Python。只有声明了
extern 的程序才带外部依赖,而且它自己负责把目标文件准备好。既有程序零改动:extern 是新语法,不用它的程序一个字节都不变(语料 54/54 仍逐字节一致)。