1 个不稳定版本
0.1.0 | 2021 年 5 月 5 日 |
---|
#2817 在 解析器实现
130KB
3.5K SLoC
Verilization
Verilization 是一种序列化描述语言,旨在定义二进制文件格式。与 Protocol Buffers 等其他序列化工具不同,序列化的 Verilization 数据不具备向前或向后兼容性。相反,从旧格式版本转换变得容易,允许数据更加紧凑,并能够更好地控制数据的底层结构。
目标
Verilization 具有以下主要目标。
- 给予用户对文件格式的最大控制
- 以语言无关的方式定义格式
- 支持从旧格式版本轻松转换
其他更低层次的目标。
- 支持大整型类型
- 无需使用本地二进制即可嵌入到其他语言中
类型
以下类型得到支持。
类型 | 编码 |
---|---|
struct 类型 |
按照顺序编码每个字段 |
enum 类型 |
一个标签(与 nat 的编码格式相同)后面跟标签表示的字段的编码 |
extern 类型 |
由目标语言编写的代码定义 |
结构体
使用多个 版本 定义 struct
类型。每个版本定义一个字段列表。
struct Rectangle {
version 1 {
width: u32;
height: u32;
}
}
枚举
使用多个 版本 定义 enum
类型。每个版本定义一个用作情况的字段列表。枚举值正好由这些字段中的一个组成。
struct StringOrInt {
version 1 {
str: string;
num: int;
}
}
外部
在用户代码中定义 extern
类型。类型定义、转换和编解码器必须在目标语言中实现。
extern
类型可以声明可用于该类型的字面量。
extern MyString {
literal {
string;
}
}
以下字面量规范得到支持。
名称 | 示例 | 语法 | 注意 |
---|---|---|---|
整数 | integer [0, 256) | 'integer'open_bracket integer_literal? ','integer_literal?关闭括号 其中 `open_bracket : '[' |
'('and close_bracket : ']' |
字符串 | 字符串 |
'字符串' |
字符串的内容不能被限制。 |
序列 | 序列 T |
'序列'类型表达式 |
定义一个指定类型的序列。 |
情况 | 情况 正数() |
'情况'标识符'(' [类型表达式{ ','类型表达式} ] ')' |
定义一个情况。如果名称不同,则可以指定多个情况字面量。 |
记录 | 记录{a:A;b:B; } |
'记录' '{' {标识符':'类型表达式';' } '}' |
定义一个记录。 |
运行时库类型
运行时库提供了一些 extern
类型。
类型 | 字面量 | 编码 |
---|---|---|
{i,u}{8,16,32,64} |
类型范围内的整数 | 小端顺序的字节固定宽度序列 |
整型 |
整数 | 可变长度的格式 |
nat |
非负整数 | 与 int 类似的格式,但没有符号位 |
字符串 |
字符串 | 一个长度 nat ,后跟指定长度的 UTF-8 字节序列 |
list T |
类型 T 的序列 |
一个长度 nat ,后跟一个 T 序列 |
option T |
两种情况 some(x) 和 none() |
一个字节 b 。如果 b 非零,则其后跟一个 T |
int
和 nat
的编码定义了一个小端顺序的位序列。每个字节的最高位被设置,如果数字中有更多的字节。
此编码是一个字节序列 [B0, ..., Bn],其中当 i < n 时 Bi,7 = 1,且 Bn,7 = 0。这个字节序列等效于一个位序列 [B0,0, ... B0,6, ..., Bn-1,0, ..., Bn-1,6] = [b0, ..., bm-1],其中 m = 6n。本质上,位序列去除了用于确定序列何时结束的标志位,并将每个字节中的剩余位从最低位到最高位进行排序。位序列映射如下
- 对于
int
类型,如果 bm-1 = 0,则 k = b0 * 20 + ... + bm - 2 * 2m-2 - 对于
int
类型,如果 bm-1 = 1,则 k = -(b0 * 20 + ... + bm - 2 * 2m-2) - 1 - 对于
nat
类型,k = b0 * 20 + ... + bm - 1 * 2m-1
版本控制
在下面的示例中,用户有一个用户名和出生日期。
struct Person {
version 1 {
name: Name;
dob: Date;
}
}
struct Name {
version 1 {
firstName: string;
middleName: option string;
lastName: string;
}
}
但是,并不是每个人都有 2 或 3 个名字。为了适应这种情况,我们可以创建一个新的版本,允许任意数量的名字。
struct Name {
version 1 {
...
}
version 2 {
names: list string;
}
}
对 Name
的此更改意味着在格式的第 2 版中,Person
的 name
字段现在将使用 Name
的第 2 版。然而,由于 Person
没有直接更改,版本 2 是自动创建的。在生成的代码中,预期用户将提供可以将 Name
从版本 1 升级到版本 2 的代码。然而,无需提供升级 Person
的此类代码。 Person
可以通过其字段的升级代码自动升级。
最终
版本化的类型可以被声明为 final
来表示不会添加该类型的新版本。这限制类型为最后一个显式声明的版本,防止自动生成新版本。最终类型可能只包含最终类型或非版本化类型的字段。
final struct FormatVersion {
version 1 {
major: nat;
}
}
泛型
泛型类型允许一个类型被参数化。
final struct Pair(A, B) {
version 1 {
left: A;
right: B;
}
}
常量
常量允许定义在生成的任何语言之间共享的值。
文字 | 示例 | 用法 |
---|---|---|
整数 | 88 |
extern 类型与 integer 文字 |
字符串 | "你好,世界" |
extern 类型与 string 文字 |
序列 | [a,b,c] |
extern 类型与 sequence 文字 |
记录 | {x= 1;y= 2; } |
struct 类型与 extern 类型与 record 文字 |
情况 | 名称(a) |
enum 类型与 extern 类型与 case Name 文字 |
命令行
Verilization 有命令行界面。以下选项被支持。
语言生成器
以下语言被支持。
编译器绑定
verilization 编译器是用 Rust 编写的。它可以编译成 WebAssembly 以在其它语言中使用。这有优点,即工具可以分发(例如,作为 NPM 包、独立 JAR 等),而不需要任何原生二进制文件。这些绑定暴露了可以直接从运行时使用的接口,以及仅依赖于相关运行时的命令行界面。
目前,以下运行时存在绑定。
- Node
依赖
~2.5MB
~56K SLoC