作者: 一了 <1liao3@funlang.org>
日期: 2026-09-28
一句话总结: 开源四周,上游 134 次提交、代码净增约 1.3 万行。Linux 移植(lffi 与平台拆分)、可运维化(stderr、退出码、traceback)、小而准的语义内补(input、eq、@has、json:2),以及用 Fun 自写的 gen-version.fun。
开源四周:Fun 的第一个月
八月三十一日,Fun 正式对外开源。到今天,整整四周。
开源后的四周里,上游有 134 次提交,代码净增约一万三千行。但数字不是重点,曲线的形状才是。这四周的主线不是「又多了几个功能」,而是它在三个方向上各完成了一次转折:从能在 Linux 上跑,到能被当基础设施依赖,再到开始用自己写自己的工具。
一、把家搬到 Linux
四周前,Fun 是实打实的 Delphi/Windows 血统:FFI 依赖 Windows 的 LoadLibrary/GetProcAddress,网络、数据库、HTTP、XML、编码全线压在 Win32 上。
现在它是 Windows 与 Linux 双平台的一等公民。
最硬的一块是 FFI。新增的 src/lib/lffi.pas(461 行)把 libffi 包成 Fun 的本地调用后端:同一份紧凑的 dll.getapi(name, 签名) 写法,Windows 上走 stdcall、Linux 上走 SysV ABI,脚本一个字都不用改。
原来 Windows 独占的模块,按「平台无关 / 平台相关」拆成了两三份。以网络为例:
lib-winsock.fun (Windows 原版)
lib-winsock-base.fun (542 行,平台无关的协议与状态机)
lib-winsock-lnx.fun (629 行,Linux 侧,直接对着 poll(2) 写)
lib-ado(数据库)、lib-ajax(换 libcurl)、lib-xml、lib-unicode、lib-proc、lib-os、lib-crypt、lib-jsonrpc、lib-orm-rpc 都做了同样的手术。四周里标准库新增了 15 个模块,绝大多数就是这类 -lnx / -base 拆分。
二、从「能跑」到「敢让它跑」
一门语言能不能被放进 CI、cron 和监控里,取决于它出错时怎么表现。四周前 Fun 的答案很危险:脚本失败,退出码是 0。
这一版彻底改了:
- 诊断信息改走 stderr,解析/运行出错返回非零退出码;
a.fun && b.fun这种链式调用不再把失败当成功。 - 错误前缀统一成
文件:行:,一眼定位。 - 加
-dFunTraceback构建选项会附上 10 帧函数回溯;默认构建零开销,不背这个成本。 - 文件末尾漏写
;的最后一条语句,不再被静默丢弃(此前它会 exit 0 且什么都不输出)。
顺手把两类「沉默的雪崩」也堵了:字符串与列表的越界访问(越界写抛异常,越界读返回安全默认值),以及 JSON / FD 解析的嵌套深度上限 512 层——扫描本身是迭代的,但生成的树在释放、克隆、序列化时是递归的,几千层 [ 就足以让进程栈溢出。
三、小而准的语义内补
功能不是这四周的重点,但每一条补丁都对准一个真实缺口:
# 1) 控制台输入:三种读法;ok 出参区分「空行」与「EOF」
var ok;
var line = 'line'.input('> ', ok: ok); # 读整行
?. 'char'.input(); # 单键(终端原始模式)
?. 'all'.input(); # 读到 EOF
# 2) 严格比较与可移植类型号:类型和值都要对上
?. '1'.eq(1); # False
?. 1.eq(1.0); # False
?. 0.type(); # 3 (整型,对标 COM VARENUM)
# 3) 键存在探测:m[key] 分不清「键不存在」与「值为 nil」
var mp = new [];
mp['a'] = nil;
?. mp.@has('a'); # True
再加上几个藏得比较深的修复:链式调用 f(..)(..) 过去整条链静默求值为 Null(内层调用根本没执行),现在内层会执行、其结果再被当作被调方继续派发;Free Pascal 下布尔与数字的比较不对称(true = 1 与 1 = true 结果相反)也修平了。
其中工程含量最高的是 getJson(json: 2) 严格模式。它在原来的流式扫描器上补了一台 RFC 8259 分隔符状态机,开始拒绝尾逗号、缺逗号、缺冒号、单引号串、十六进制数、非字符串键等十几类「脏 JSON」;同时有意保留 .5、03 这类现实世界常见的宽松数字。取舍写进了提交信息:严格是为了挡坏数据,不是为了教条。
四、开始自己写自己
四周里最具分水岭意义的一件事,很小:版本号生成器改成了用 Fun 自己写。
fun src/prj/fun/gen-version.fun [YYYYMMDD] ["release"]
不依赖 shell,不依赖 git,Windows 与 Linux 是同一条命令,产出 src/core/version.inc。脚本自己也有两条路径探到当前解释器版本,且读的是同一个常量:
?. 2010.time(); # 20260926
?. 'host'.arg().getJson(fd:true).version; # 20260926
工具链开始吃自己的狗粮,是「移植完成」转入「自维护」的分界线。
五、文档与回归
四周里,公开仓库补上了 7 份 Markdown、合计 1200 多行:英文优先的双语 README、CONTRIBUTING、双语的 CHANGELOG,以及 169 行的 SAFETY.zh.md(越界与安全的边界说明)。标准库头注释里一直漏列的内建方法说明也一并补齐。
本地回归套件也一路长到 90 项,全绿。
结语:三条线的性质不同
回头看不难发现,这四周的三条新线性质各异:
语义内补(input / eq / @has / json:2)全是「小而准」的补丁,每个都写清了根因、修法、边界与验证;可运维化(退出码 / stderr / traceback)说明它开始被当成基础设施用,而不只是玩具;自举起步(gen-version.fun)说明它开始用自己造自己需要的工具。
一门语言成熟与否,从来不看它有多少特性,而看它敢不敢被放在会失败的地方。
Fun 的源码是开放的。欢迎来看看,四周之后的它跑得怎么样。