14 个版本
0.6.5 | 2019 年 3 月 17 日 |
---|---|
0.6.3 | 2017 年 12 月 24 日 |
0.5.1 | 2017 年 11 月 23 日 |
#5 in #config-format
被 2 crates 使用
130KB
3K SLoC
结束
结束:最佳数据格式。
目录
关于
结束是一个类似于 XML 或 JSON 的一般用途数据格式,但要好得多。以下是它的一些关键特性
- 结束被设计成既直观又灵活,便于人类阅读和编写。
- 它功能强大,具有变量、文件包含和对象父类等概念。
- 它有一个优雅且通用的类型系统,可以安全地表示所有常见的数据。
- 它设计上具有容错性,没有像 YAML 或 TOML 那样的奇怪行为或语法。
示例
作为数据格式的基本用法可能看起来像这样
receipt: "Oz-Ware Purchase Invoice"
date: "2012-08-06"
customer: {
first_name: "Dorothy"
family_name: "Gale"
}
items: [
{
part_no: "A4786"
descrip: "Water Bucket (Filled)"
price: 01.47
quantity: 4
}
{
part_no: "E1628"
descrip: "High Heeled \"Ruby\" Slippers"
size: 8
price: 133.70
quantity: 1
}
]
bill_to: {
street:
# A multi-line string. Can also be written as "123 Tornado Alley\nSuite16"
"123 Tornado Alley
Suite 16"
city: "East Centerville"
state: "KS"
}
ship_to: bill_to
specialDelivery:
"Follow the Yellow Brick Road to the Emerald City. Pay no attention to the man behind the curtain."
这个基本示例已经展示了结束的许多优点
- 您可以立即了解每个字段包含的数据类型。
- 多行字符串不需要特殊语法;请参阅
bill_to.street
。 - 变量;请参阅
ship_to
。 - 注释(听起来很简单,但 JSON 没有注释)。
用法
将结束添加到您的 Cargo.toml
[dependencies]
over = "*"
Rust 代码读取上述示例数据
#[macro_use]
extern crate over;
use over::obj::Obj;
#[test]
fn example() {
let obj = Obj::from_file("tests/test_files/example.over").unwrap();
assert_eq!(obj.get("receipt").unwrap(), "Oz-Ware Purchase Invoice");
assert_eq!(obj.get("date").unwrap(), "2012-08-06");
assert_eq!(
obj.get("customer").unwrap(),
obj!{"first_name" => "Dorothy",
"family_name" => "Gale"}
);
assert_eq!(
obj.get("items").unwrap(),
arr![
obj!{"part_no" => "A4786",
"descrip" => "Water Bucket (Filled)",
"price" => frac!(147,100),
"quantity" => 4},
obj!{"part_no" => "E1628",
"descrip" => "High Heeled \"Ruby\" Slippers",
"size" => 8,
"price" => frac!(1337,10),
"quantity" => 1},
]
);
assert_eq!(
obj.get("bill_to").unwrap(),
obj!{"street" => "123 Tornado Alley\nSuite 16",
"city" => "East Centerville",
"state" => "KS",
}
);
assert_eq!(obj.get("ship_to").unwrap(), obj.get("bill_to").unwrap());
assert_eq!(
obj.get("specialDelivery").unwrap(),
"Follow the Yellow Brick Road to the Emerald City. \
Pay no attention to the man behind the curtain."
);
}
目前结束仅实现了 Rust;将来可能支持更多语言。
特性
容器
结束有三个容器类型
- 一个 [数组],其中所有元素必须具有相同的数据类型(由解析器强制执行)。
- 一个 (元组),可以包含不同类型的元素。
- {对象},包含字段到不同类型值的映射。
以下是一个有效的数组,因为每个子元组都是同一类型
[ ("Alex" 10) ("Alice" 12) ]
以下不是一个有效的数组。你能看出为什么吗?
[ ("Morgan" 13) ("Alan" 15 16) ]
变量
在对象中定义的字段可以在后面引用,但只能在对象的作用域内
var: 2
number: var
obj: {
number: var # Invalid!
}
您还可以定义全局变量。这些是私有的,不会出现在最终数据中的字段中
@var: 2
number: @var
obj: {
number: @var # Valid!
}
父类
一个对象可以继承另一个对象的字段。在以下示例中,我们定义了一个名为 @default
的模板对象,并将其定义为 foo
和 bar
的父对象,使用 ^
字段
@default: {
a: 1
b: 2
}
foo: {
^: @default
b: 5 # Override the value of "b" inherited from "@default".
# foo.a == 1
}
bar: {
^: @default
a: 5 # Override the value of "a" inherited from "@default".
# bar.b == 2
}
对象字段访问
可以使用点符号访问对象的字段。只要相关的对象在作用域内且字段存在,则此操作有效。示例
obj: {
sub_obj: {
secret_value: 0
}
}
value: obj.sub_obj.secret_value
这允许一些很好的命名空间可能性
@colors: {
red: "#FF0000"
green: "#00FF00"
blue: "#0000FF"
}
obj: {
name: "Red monster"
color: @colors.red
}
数组与元组也可以使用点符号进行索引。
tup: ("test" 0)
zero: tup.1
test: tup.zero
值和变量的算术运算
值和变量可以进行基本算术运算。可用的运算符有 +
、-
、*
、/
和 %
,但并非所有运算符都可以应用于所有类型。运算符 *
、/
和 %
的优先级高于 +
和 -
。
请注意,运算符和它们的操作数之间不能有空格。语言的语义是,值后的空格表示该值的结束。
以下是一个示例:
grid: 16
x: 18 y: 20
width: 4
height: 6
rectangle: (x-x%grid y-y%grid width*grid height*grid)
文件包含
为了体现模块化的精神,OVER 提供了一种分割文件的功能。通过示例可以最好地说明这一功能。
假设我们有两个对象,我们希望分别放在两个不同的文件中:
includes/obj1.over
:
a: 1
b: 2
c: 3
includes/obj2.over
:
a: 2
b: 3
c: 1
我们可以拥有一个类似于“主”文件的文件,其中包含两个子对象文件
main.over
:
obj1: <"includes/obj1.over">
obj2: <"includes/obj2.over">
includes 的主要好处是方便和组织。我们还可以将数组和元组放入单独的文件中,这使得自动生成的空白分隔的值很容易包含进来。最后,字符串也可以放在它们自己的文件中,在这种情况下,它们将被逐字解析;不进行字符转义。这对于长字符串来说是一个非常方便的选项。
以下是一个演示包含 Str
、Arr
和 Tup
的示例
main.over
:
str: <Str "includes/str.over">
arr: <Arr "includes/arr.over">
tup: <Tup "includes/tup.over">
includes/str.over
:
Multi-line string
which should be included verbatim
in another file. "Quotes" and $$$
don't need to be escaped.
includes/arr.over
:
1 2 3 4 5
includes/tup.over
:
1
'a'
3
'b'
5
关于文件包含的一些说明
- 全局变量在文件之间无效。
- 文件不能以循环方式包含;例如,如果文件
main
包含sub-obj
,那么sub-obj
不能包含main
。 - 您可以多次包含相同的文件。文件包含只会在第一次遇到时进行处理。
- 包含仅对
Obj
、Str
、Arr
和Tup
有效。当包含对象文件时,Obj
关键字是可选的。
字符串替换
即将推出!
类型
空值
一个简单的空值,表示为 null
。
布尔值
true
或 false
。任选其一。
整数
任意长度的有符号整数类型。任何以 -
、+
或数字开头的标记要么是 Int
,要么是 Frac
(见下文)。
示例: 1
、-2
、+4
分数
十进制值的合理表示。忘记浮点类型,改用分数。
示例: -1/3
、-5-1/4
、2+1/2
、42+6/1
分数也可以写作小数,它们将自动转换为分数表示。
示例: 2.5
,-.0
字符
表示单个Unicode字符的类型。
示例: 'q'
,' '
字符串
Unicode字符串类型。
示例: "smörgåsbord"
,"A string with \"quotes\""
多行字符串非常简单
"You don't need any
special syntax for multi-line strings;
newlines are captured automatically."
数组
一个可以容纳任意数量单个类型元素的数组容器。
示例: []
,[1 2 3]
,[(1 2) (3 4)]
元组
一个可以容纳不同类型元素的元组容器。
示例: ()
,(1 "John")
( ('x' 1/2) [1 2 3] )
对象
所有类型的“教父”,即对象。它是一个键到值的哈希表,我们称之为字段,其中值可以是任何类型,包括其他对象。
字段后必须跟一个冒号,且不能是保留关键字。
保留关键字
@
null
true
false
对象
字符串
数组
元组
示例
{a: 1b: 2列表: [a b b a] }
{id: 4字段: {字段: "对象可以嵌套,并且每个都有自己的作用域。" } }
待办事项
由于本项目是为我个人的需求开发的,因此有一些必要的步骤来使其准备好用于1.0
,而我对此没有太多的动力。以下任何一种方式都是为项目做出贡献的好方法
- [简单] 多行/块注释,例如
#[ ... ]#
。应该能够嵌套这些。 - [困难]
super
关键字?即super.var
(仅禁止super
吗?)不确定这是否值得努力,但我可以看到潜在的使用案例。 - 编写一个Emacs模式,以JSON模式为起点。
- [简单] 与等效的JSON文件进行基准测试。
- [中等] 实现字符串替换。
- [简单] 查看API指南:https://rust-lang-nursery.github.io/api-guidelines/checklist.html
- [中等] 将错误处理移动到Failure? https://www.reddit.com/r/rust/comments/7b88qp/failure_a_new_error_management_story/
- [中等] 性能:考虑在内部替换 HashMap。使用 Flame 进行基准测试:https://github.com/TyOverby/flame
JSON 的问题是什么?
我开始这个项目是因为我想找一个 JSON 的替代品。为什么?
- 列表中的最后一个元素不能有尾随的逗号。
- 关于开/闭花括号的用法是什么?
- 浮点数值类型。
- 允许不同类型的数组。
- 字段名必须用引号括起来,例如
"name": "Johnny"
而不是name: "Johnny"
。它很冗长,不够人性化。 - 不支持注释是一个致命缺陷。一些 JSON 实现允许它们,但这不是标准。
JSON 和其他选项也缺少许多我感兴趣的功能,例如变量、对象父级概念、文件包含等。
YAML/其他呢?
让我们比较这个 README 中的第一个例子(示例)与来自 维基百科 的相同数据的 YAML 版本。
---
receipt: Oz-Ware Purchase Invoice
date: 2012-08-06
customer:
first_name: Dorothy
family_name: Gale
items:
- part_no: A4786
descrip: Water Bucket (Filled)
price: 1.47
quantity: 4
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
size: 8
price: 133.7
quantity: 1
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Centerville
state: KS
ship-to: *id001
specialDelivery: >
Follow the Yellow Brick
Road to the Emerald City.
Pay no attention to the
man behind the curtain.
...
如你所见,这比 OVER 版本要不清楚得多。YAML 有奇怪的语法(例如 &id001
和 *id001
;还有 >
和 |
是什么意思?)以及在其他方面缺少有用的语法(每个值看起来都像字符串)。`date` 字段是数字还是字符串?YAML 确实在外观上更加令人愉悦,这可能是它被广泛使用的原因,但它在一些轻微的审查下无法站稳脚跟。它的一切都是关于外观好看而牺牲清晰度。
在 StackExchange 上看 这个答案,了解 YAML 是多么的不直观。这还不是最糟糕的;官方规范中有令人震惊的许多奇怪之处。这种设计灾难还使得编写高效的 YAML 解析器变得不可能。
最后,正如整个 README 中所看到的,OVER 在保持简单的同时,管理着比 YAML 更强大的功能!这可能看起来像是一个悖论,但这只是 YAML 和公司(不要以为我忘了 TOML)不负责任设计的结果。有像 StrictYAML 这样的选项,但据我观察,它们只是修补了一个有缺陷的解决方案。
变更日志
见 CHANGELOG.md。
版权
(c) 2017 Marcin Swieczkowski
依赖项
~620KB
~14K SLoC