#string #configuration #config-format #table #config-file #lexer #file-format

slr_lexer

一种简单的配置格式。此包仅包含词法分析器,有关解析器和其他工具,请参阅slr_config包。

13个版本

使用旧Rust 2015

0.0.13 2016年5月20日
0.0.12 2015年8月1日
0.0.11 2015年7月28日

#6#config-format

LGPL-3.0

16KB
718

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种时间格式令人惊讶。

    如上所述,我将语法简化到可以写许多不带引号的字符串。通过一点记录,我可以将这些字符串的解释推迟到用户那里,同时让他们仍然能够追踪字符串的来源,以便于错误报告。

  • 如前所述,我设置了这样的事物,以至于引用字符串是罕见的。字符串支持其中的空格,无需引用。保留字符的选择使得写下浮点数、日期、单位和其他常见类型编码也无需引用。

  • 与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字符xxxxxxx,其中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 被编码为 true,而 false 被编码为 false

字符串和字符

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

字节数组

字节被编码为一个整数数组。

切片和元组

这些被编码为数组。

映射

这些被编码为包含2个元素数组的数组(键/值对)。这些不是以表格形式编码,因为表格必须使用字符串作为键,而Rust映射不需要。未来,我们可能会放宽这个限制。

可选类型

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

单元类型

单元类型被编码为空字符串。

单元结构和变体

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

新类型结构和变体

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

元组结构和变体

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

结构和结构变体

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

没有运行时依赖项