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 使用

MIT 许可证

130KB
3K SLoC

结束

Build Status crates.io Downloads Documentation Issues LoC License: MIT

结束:最佳数据格式。

目录

关于

结束是一个类似于 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 的模板对象,并将其定义为 foobar 的父对象,使用 ^ 字段

@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 的主要好处是方便和组织。我们还可以将数组和元组放入单独的文件中,这使得自动生成的空白分隔的值很容易包含进来。最后,字符串也可以放在它们自己的文件中,在这种情况下,它们将被逐字解析;不进行字符转义。这对于长字符串来说是一个非常方便的选项。

以下是一个演示包含 StrArrTup 的示例

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
  • 您可以多次包含相同的文件。文件包含只会在第一次遇到时进行处理。
  • 包含仅对 ObjStrArrTup 有效。当包含对象文件时,Obj 关键字是可选的。

字符串替换

即将推出!

类型

空值

一个简单的空值,表示为 null

布尔值

truefalse。任选其一。

整数

任意长度的有符号整数类型。任何以 -+ 或数字开头的标记要么是 Int,要么是 Frac(见下文)。

示例: 1-2+4

分数

十进制值的合理表示。忘记浮点类型,改用分数。

示例: -1/3-5-1/42+1/242+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,而我对此没有太多的动力。以下任何一种方式都是为项目做出贡献的好方法

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