#config-parser #configuration #string #config-file #file-format #table #hierarchical

slr_parser

一种简单的配置格式。此包包含解析器,有关更用户友好的API,请参阅slr_config包。

9个版本

0.0.22 2024年2月5日
0.0.21 2022年7月11日
0.0.20 2020年1月5日
0.0.19 2019年12月27日
0.0.14 2016年8月8日

#200 in 解析器实现

34 每月下载量
用于 slr_config

LGPL-3.0

35KB
1.5K SLoC

SLRConfig

Build Status

SLRConfig 是一种简单的配置格式,注重可读性和易于编辑。它支持表(字符串到元素的映射)和元素的数组,其中元素可以是字符串、数组或表。尽管它只支持字符串的排列,但它的宽松语法和标准实现,可以保留每个元素分配的位置,允许程序员以与内置类型相同的错误消息质量添加对任何其他类型的支持。更多信息请见下文。

文档

示例

以下是一个示例片段。语法细节将在下文进一步解释。

# This is a comment.
# The implicit outer structure is a table, a mapping of string keys to string
# values, as well as other collections.
key = value

statement = there's no need to quote the vast majority of characters

"sometimes, you" = "need
to"

"you can always escape ☺
" = you can always escape \u263a\n

raw string for when you're tired of escaping = {{"embedded quote -> " <-"}}

there is no builtin date format = 1970/01/01
there are no bulltin integers = 1_000_000
all values are strings = -1.5

on = a, single = line

table
{
	array = [a, { b = c }, [e]]
}

# Tagged variants of tables and arrays are particularly useful when serializing
# structs and tuple/struct variants.
tagged table = tag
{
   tagged array = tag [1, 2]
}

动机

SLRConfig 是我在之前尝试制作配置文件过程中的第三次迭代,继 SLConfigDConfig 之后。我一直想要一种简单、以人为本的配置格式,能够表达我类型的层次结构。在这次第三次尝试中,我砍掉了所有我认为不必要的东西

  • 除了集合和字符串外,没有内置类型。这是一个重要的观点,因为我感觉人们太急于添加对类型的原生支持,这造成了内置类型和其他类型之间的人为划分。例如,为什么支持一种特定的日期格式,而不是另一种?为什么只支持日期,而不是时间?为什么只支持64位整数,而不是更多?有些语言声称自己是最低限度的,但支持N种时间格式,这让我感到惊讶。

    在SLRConfig中,我简化了语法,使得您可以在不使用引号的情况下写入许多字符串。通过一些简单的账目记录,我可以在用户使用的同时,让他们跟踪字符串的来源,以便于错误报告。

  • 如上所述,我设置了这样的设置,使得字符串加引号变得很少。字符串支持空格,无需加引号。保留字符的选择使得写入浮点数、日期、单位和其他常见的类型编码也无需加引号。

  • 与SLConfig相比,我取消了文件包含、复杂的变量扩展和文档字符串的支持。这些都只是似乎使规范和实现变得更加复杂。

尽管如此,我确实增加了一些不太常见的功能。

  • 原始字符串。我认为这对于没有转义符的长时间文本输入非常有用。

  • 简单的变量扩展。这可能是SLRConfig最多余的特性,但它在有用时很难复制。我很乐意推迟字符串替换到包含文件,但变量扩展确实必须遵守语法以便于使用。

格式描述。

使用GOLD元语言描述语言语法。基本上,字符集使用集合表示法指定,终结符使用正则表达式指定,产生式使用BNF指定。

根元素由<TableElements>产生式定义。

词法语法

编码

格式的字符串表示使用UTF-8编码。

字符集

{Raw String Chars} = {Printable} + {Whitespace}
{Escaped String Chars} = {Printable} + {Whitespace} - ["]

{String Middle} = {Printable} - {Whitespace} - [#='['']'{}$",~] + [' ']
{String Border} = {String Middle} - [' ']

终结符

NakedEscapedString = ({String Border} | '\' {Raw String Chars})
                     ({String Middle} | '\' {Raw String Chars})*
                     ({String Border} | '\' {Raw String Chars})
                   | ({String Border} | '\' {Raw String Chars})
QuotedEscapedString = '"' {Escaped String Chars}* '"'

RawString0 = '{{"' {Raw String Chars}* '"}}'
RawString1 = '{{{"' {Raw String Chars}* '"}}}'
RawString2 = '{{{{"' {Raw String Chars}* '"}}}}'

注释

使用#字符引入行注释。行注释可以位于行上的任何位置,并在换行符(LF)处结束。

解析器语法和语义

字符串

<String> ::= NakedEscapedString
           | QuotedEscapedString
           | RawString0
           | RawString1
           | RawString2

字符串既可以用作表中的键,也可以作为表和数组中的一种元素。有两种类型的字符串:转义和原始。

转义字符串

转义字符串可以有字符转义,这些转义在解析过程中会被解析。支持以下转义:

  • \n - 换行符
  • \r - 回车符
  • \t - 水平制表符
  • 0 - NUL
  • \\ - 反斜杠
  • \uxxxx - Unicode字符xxxx,其中x是十六进制数字的小写字母
  • \Uxxxxxxxx - Unicode字符xxxxxxxx,其中x是十六进制数字的小写字母

无效的转义会被替换为(U+fffd)字符。有两种类型的转义字符串:裸露的和带引号的。裸露的字符串不需要引号包围,但对其可以包含的字符有限制。

原始字符串

原始字符串包含其内部的精确字符。首部大括号的数目必须与尾部大括号的数目相匹配,但如果有字符串内部的引号字符后面跟着一系列尾部大括号,则可以增加。

表达式

<Expansion> ::= '$' <String>

<Expr> ::= <String>
        |  <Expansion>
        |  <Expr> '~' <String>
        |  <Expr> '~' <Expansion>

表和数组元素被分配给表达式的结果。表达式可以产生一个值(字符串)、一个表或一个数组。可以使用~运算符连接多个字符串。

使用扩展语法可以获取之前已分配元素的值。元素通过键从表中查找,通过索引(基于0)从数组中查找。查找是通过在分配给元素的表/数组中查找元素来进行的。如果没有找到,则检查更高一级的表/数组,依此类推。正在查找的元素必须源字符串中在分配给元素的元素之前。分配给元素的元素永远不会被视为扩展元素。

<OptComma> ::= ','
            |
<Table> ::= '{' <TableContents> '}'

<TableElement> ::= <String> <Table>
                |  <String> '=' <Array>
                |  <String> '=' <Expr>
                |  <String> '=' <TaggedTable>
                |  <String> '=' <TaggedArray>

<TableElements> ::= <TableElement>
                 |  <TableElements> <OptComma> <TableElement>

<TableContents> ::= <TableElements> <OptComma>
                 |

数组

<Array> ::= '[' <ArrayContents> ']'

<ArrayElement> ::= <Expr>
                |  <Table>
                |  <Array>
                |  <TaggedTable>
                |  <TaggedArray>

<ArrayElements> ::= <ArrayElement>
                 |  <ArrayElements> ',' <ArrayElement>

<ArrayContents> ::= <ArrayElements> <OptComma>
                 |

标记表

<TaggedTable> ::= <String> <Table>

标记表是附加了字符串标记的表。这些对于表示命名类型非常有用。

标记数组

<TaggedArray> ::= <String> <Array>

标记数组是附加了字符串标记的数组。这些对于表示命名类型非常有用。

序列化和反序列化集成

以下是各种Rust结构在SLRConfig中的编码方式。

数值类型

这些使用它们的字符串表示形式作为裸字符串进行编码。

布尔值

true编码为truefalse编码为false

字符串和字符

字符串作为转义字符串进行编码,并且会自动进行转义。

字节切片

字节编码为整数数组。

切片和元组

这些编码为数组。

映射

这些编码为2元素数组的数组(键/值对)。这些不是编码为表,因为表必须将字符串作为它们的键,而Rust映射不需要。将来,我们可能会放宽这一点。

选项

Some编码为包含的值,None编码为空字符串。

单元

单元编码为空字符串。

单元结构和变体

这些通过它们的名称作为字符串进行编码。

新类型结构和变体

这些通过1元素标记数组进行编码。标记是结构/变体的名称,数组元素是值。

元组结构和变体

这些通过标记数组进行编码。标记是结构/变体的名称,数组元素是元组元素。在反序列化元组结构时,也接受非标记数组。

结构和结构变体

这些通过标记表进行编码。标记是结构/变体的名称,表元素是结构元素。在反序列化元组结构时,也接受非标记表。为了允许扩展,具有与结构/变体字段不对应的键的表元素将被忽略。

依赖项

~110–345KB