20261105 MoonBit v0.10.19
对应 moonc 版本:v0.10.19
语言更新
derive 语法迁移
把写在类型定义末尾的 derive(...) 移到了定义上方,统一为属性的写法。已有代码可以交给 moon fmt 转换。属性移除了 #foo(qual.id) 的语法,增加 #foo(@pkg.id) 和 #foo([elem1, elem2, elem3]) 的语法支持。
#derive(Debug, Eq, Hash)
pub(all) struct Point {
x : Int32
y : Int32
}
Int/UInt 类型改名为 Int32/UInt32
Int32 和 UInt32 分别表示 32 位有符号和无符号整数。在需要同时读写多种位宽的场合,例如二进制协议或 FFI 签名,显式位宽能让代码更容易读。原来的 Int 和 UInt 仍可作为别名使用,两种拼法可以在同一个项目中混用。格式化工具可以将旧拼法迁移成显式位宽的写法。
浮点数语义调整
运算和比较运算符(==、!=、<=、<、>=、>)依旧保持 IEEE 语义:-0.0 == 0.0,NaN 与任何值(包括自身)都不相等。主要改动在于 compare 函数对 NaN 的计算结果,目前 compare 不提供关于 NaN 的计算结果的保证,这主要影响排序,二分查找,并且哈希表之类的结构不应使用 NaN 作键值,如果需要对 NaN 有语义保证的 compare 版本,可以使用新提供的 Float::total_compare 和 Double::total_compare,这两者按如下顺序进行比较:
NaN-Infinity- 负数
-0.0和0.0(这两者相等)- 正数
+Infinity
extenum 的声明形式调整
旧写法将类型声明和构造器放在同一个定义中:
extenum E {
A
}
现在应先用 pub extenum E 声明类型,再用 extenum E += { ... } 扩展构造器:
pub extenum E
extenum E += {
A
}
类型声明部分只支持 pub 可见性:私有的可扩展枚举无法由下游扩展,可以用普通 enum 替代;pub(all) 与 pub 对下游的效果相同,因此统一为 pub。扩展构造器时,依然可以使用 pub(all)、pub 或 priv 控制构造器的可见性。旧写法目前会给出弃用警告,moon fmt 会自动迁移到新写法。
新增二进制正则支持
reb"..." 用于在 =~ 和 lexmatch 中匹配 Bytes / BytesView,用法与匹配 String / StringView 的 re"..." 一致。reb"..." 的匹配单位是字节而不是 Unicode 字符,不做任何编码解码,可以匹配任意字节序列。它的正则语法与 re"..." 基本一致,主要区别是:捕获的类型由正则静态决定:确定只匹配一个字节时为 Byte(如 reb"." as b),否则为 BytesView;before / after 的类型为 BytesView。在普通表达式中,reb"..." 的类型为 BytesRegex,可以像 Regex 一 样定义为常量并用 +、| 组合,在模式中引用:
fn parse_request(line : Bytes) -> Unit {
if line =~ (reb"^(GET|POST|PUT) " + (reb"[^ ]+" as path), after=rest) {
...
}
}
fn content_length(header : BytesView) -> BytesView? {
lexmatch header {
(reb"^(?i:content-length): " + (reb"[0-9]+" as n), after=_) => Some(n)
_ => None
}
}
- 字面量中只能写 ASCII 字符,非 ASCII 字节使用
\xHH表示(也可用于字符类和范围),例如reb"\xef\xbb\xbf"、reb"[\x80-\xff]+";不支持\u{...}和直接写非 ASCII 字符(如reb"é"需写成reb"\xc3\xa9")。 .和[^...]匹配任意单个字节(包括 0x80–0xFF),因此.只会匹配多字节 UTF-8 序列中的第一个字节。(?i:...)、\b、[[:word:]]只针对 ASCII,0x7F 以上的字节不做大小写折叠,也不视为单词字节。
const HIGH : BytesRegex = reb"[\x80-\xff]"
const HIGH_THEN_A : BytesRegex = HIGH + reb"a"
lexscan 目前不支持 bytes 正则。
破坏性变更预告:JavaScript 后端的 Array 布局
下个版本我们将调整 JavaScript 后端下 Array 类型的运行时布局,使其与其他后端一致。Array 将不再编译成 JavaScript 原生 Array。
顶层 declare let
过去,declare 主要用于函数、类型等接口。现在也可以先用 declare let 写出一个顶层值的名称、类型和可见性,再在同一个包内提供与之匹配的定义。这样可以先审阅包对外承诺的接口,再决定值放在哪个文件里、如何初始化。
///|
declare pub let default_limit : Int32
///|
pub let default_limit : Int32 = 100
工具链更新
原生后端支持构建静态链接库
可以编写如下的 MoonBit 程序,这里用 #export_name 指定对外符号。在 moon.pkg 中用 pkgtype(kind: "foreign_library") 声明包类型,之后运行 moon build --target native 后,会生成可供宿主链接的 .a 文件,在 Windows 平台是 .lib 文件:
let offset : Int32 = 2
#export_name("add")
pub fn add(a : Int32, b : Int32) -> Int32 {
a + b + offset
}
$ moon build --target native
Finished. moon: ran 28 tasks, now up to date
Artifacts:
username/foreign/lib native path/to/_build/native/debug/build/lib/lib.a
在宿主侧,则需要调用 moonbit_init() 函数来初始化 MoonBit 程序的运行时,比如:
#include "moonbit.h"
#include <stdio.h>
extern int32_t add(int32_t a, int32_t b);
int main(void) {
moonbit_init();
printf("add(40, 2) = %d\n", add(40, 2));
return 0;
}
这之后我们可以将它和 MoonBit 生成的静态链接库一起编译,并运行:
$ cc -I ~/.moon/include host.c path/to/_build/native/debug/build/lib/lib.a -lm -o host
$ ./host
add(40, 2) = 44
mbtx 脚本格式改进
脚本可以在文件开头的 --- 之间放置配置,把依赖和运行时权限集中写在一起。文件头沿用 moon.pkg 和 moon.mod 的配置语法。对于需要从宿主环境读取变量的脚本,也可以在此声明要请求的权限。导入配置的 import 语法保持不变,仍然支持包别名和完全导入。同一个导入还可以同时写包别名和 *。下例中,Queue 可以直接调用,也可以写成 @q.Queue。如果只写 *,包的默认别名仍然可用。
---
import {
"moonbitlang/core/queue" @q *,
"moonbitlang/x/bcrypt",
}
---
fn main {
println(Queue([1, 2]).length())
println(@q.Queue([3, 4, 5]).length())
}