文档章节

有关 tom-toml 的一些事儿

喻恒春
 喻恒春
发布于 2014/01/31 13:54
字数 1560
阅读 1607
收藏 3

为什么要再写一个TOML解析器

  • 学习写解析器
  • 支持注释
  • 支持格式化输出并保持次序

学习写解析器

一直认为编写解析器是非常有挑战性的任务. TOML 本身已经很简洁. 为 TOML 写个解析器很有吸引力. 我们知道已经有了 YACC 这样工具可以完成此类工作. 事实上 Go 提供了这样的工具, TOML 上也有关于 EBNF 的讨论, 已经出现出几个版本. 但是要让这些 EBNF 定义转换成特定语言的代码, 还有很多辅助工作. 作为学习目的, 我采取先手工写一个解析器, 可以对完整使用 EBNF 有更深刻的理解.

鉴于 TOML 的简洁. 手工写出所有的 First 集和 Follow 集是可行的. parser.go 中 stateEmpty/tokensEmpty 就是 First 集, 按照编译原理所阐述的, 解析完整结束也会回到 First 集. 解析开始的时候至少要匹配到 First 集合中的一项(TOML 没有二义性, 只匹配一个). 解析结束的时候会回到 First 集合, 由于我写的 First 集合中没有 EOF 匹配, 所以当匹配不了 First 集合时, 解析结束, 相反如果在 First 集合中写下 stateEof/tokensEof 那最终会以匹配 EOF 而结束. 其他的 Follow 集合也是必须要有匹配, 如果没有被匹配, 那表示输入无效, 实现中我在每个 stateXX 中增加了一个没有匹配到要执行的动作, 用来给出一点提示信息.

扫描器

解析器是有明确的阶段, 其中词法分析(也可以称为扫描器)是第一阶段. 对于手工写的解析器, 这些阶段的代码可以混合在一起. tom-toml 的扫描器 Scanner 是一个纯粹的 UTF-8 字符扫描器, 每次只扫描一个 UTF-8 字符, 别致的地方在于 Scanner 消除了 token 匹配中常见的 peek 操作. First 集和 Follow 集具体的匹配代码写法和这种 Scanner 是配合的, 所以在 itsString 这样的 token 匹配代码中可以看到 flag 这个状态标志, peek 被消除了. 当然这种方法只是一种尝试, 我并不确定是否可以普遍适用. 采用这种写法有个原因维护 peek 总让我晕头转向.

支持注释

TOML 的实现有很多, 在 tom-toml 之前, 很多实现都是不支持注释操作的, 我认为注释是必要被支持的. 曾经 fork 了 pelletier/go-toml 并增加了注释支持, 好像 pelletier 不理解支持注释是必要. 鉴于改造的比较大, 不如重新写一个解析器.

兼容性

先写下解释用的 TOML 文本

[nameOftable] # Kind() 为 TableName, String() 同此行
key1 = "v1" # Kind() 为 String, String() 是 "v1"
key2 = "v2" # Kind() 为 String, String() 是 "v2"
[[arrayOftables]] # Kind() 为 ArrayOfTables, String() 是此行及以下行
key3 = "v3" # Kind() 为 String, String() 是 "v3"

因为采用 map 和支持注释的原因, 使用上有些特别. Toml 对象中存储的

  • TableName 仅是 TOML 规范中的 [nameOftable] 的字面值.
  • Table 仅是 TOML 规范中的 [[arrayOftables]] 的一个 Table.

因此用 tm 表示上述 Toml 对象的话

tm["nameOftable"] 仅仅是 `[nameOftable]`, 不包含 Key/Value 部分
tm["arrayOftables"] 是全部的 `arrayOftables`, 因为它是数组
tm.Fetch("nameOftable") 是`[nameOftable]`的 Key/Value 部分, 类型是 Toml
tm["arrayOftables"].Table(0) 是第一个 Table, 类型也是 Table
tm["nameOftable.key1"] 直接访问到了值为 "v1" 的数据

可以看出

  • 只有通过 Fetch() 方法才能得到一个 TOML 规范中定义的 Table 的主体.
  • 只有通过 Table() 方法才能得到 Table 类型.
  • arrayOftables.key3 这种写法是错误的, 不满足 TOML 规范的定义

看上去很古怪, 但是如果要用 map 进行存储的话只能是这样, 就算不支持注释, 也逃不过 ArrayOfTables 的古怪.

map 带来 "nameOftable.key1" 这种点字符串方便的同时也产生了一些副作用.

map 更多的是表现平板式的数据结构, 没有太深的嵌套. 你可以用

<!-- lang: cpp -->
tm["a.b.c.d.foo"]  // 一下就访问到最终的目标
// 而不用像这样
tm.Get("a").Get("b").Get("c").Get("d").Get("foo")

TOML v0.2.0 定义中是可以深层嵌套的. 用 map 完全实现 TOML 的标准, 访问的时候必然产生一些语义上的差异.

Value 和 Item

由于上述的特别原因, tom-toml 在实现中, 把 TOML 定义中的段(Table/ArrayOfTables)和值(String, Integer ...)分开进行定义. 事实上 Table 的存储也被 Value 负责, 在 tom-toml 中 TableName 实际上就是个空的 Value. 因此会有这样的判断代码

<!-- lang: cpp -->
func (p *Value) IsValid() bool {
    return p.kind != InvalidKind && (p.v != nil || p.kind == TableName )
}

保留这个空的 Table 对 Toml 对象格式化输出TOML文本是有意义的.

Value 的方法 Int/String/Float/Boolean/Datetime 是仿照 reflect.Value 的方法设计的. 也就是说使用者要自己确定 Value 的 Kind 并调用相应的方法获取数据的值, 如果错误的调用(String方法特殊, 其他类型可以转换到 string), 方法不会产生错误, 会返回一个缺省值.

Item 扩展自 Value, 目前是为了支持 ArrayOfTables 的, 可以看出 Value 主要负责存储值的维护, Item 维护了复杂的类型定义.

支持格式化输出并保持次序

经过解析得到 Toml 对象后, 可以进行增删改所有 TOML 所支持的元素, 包括注释. 操作完后可以用 TomlString/String 方法得到带缩进的格式化输出. Toml 使用 map 保存数据, go 语言中 map 是无序的, tom-toml 内部使用一个计数器保证输出次序.

ArrayOfTables

这个名字很不好, 因为事实上经过分析, 这个定义就是允许以数组的形式进行 TOML 嵌套. 下面转贴官方在 讨论 中给出的例子, 这明明就是嵌套的 TOML.

[[fruit]]
name = "apple"

[fruit.physical]
color = "red"
shape = "round"

[[fruit.variety]]
name = "red delicious"

[[fruit.variety]]
name = "granny smith"

[[fruit]]
name = "banana"

[[fruit.variety]]
name = "plantain"

贡献

如果您有任何问题, 建议请 issues 反馈.

© 著作权归作者所有

喻恒春

喻恒春

粉丝 110
博文 31
码字总数 22644
作品 5
郑州
程序员
私信 提问
加载中

评论(4)

麦田听雨声

引用来自“喻恒春”的评论

引用来自“麦田听雨声”的评论

您好,请问您知道如何使用python编写toml文件吗?谢谢

回复@麦田听雨声 : 不会 py 啊
谢谢哈😃
喻恒春
喻恒春 博主

引用来自“麦田听雨声”的评论

您好,请问您知道如何使用python编写toml文件吗?谢谢

回复@麦田听雨声 : 不会 py 啊
麦田听雨声
您好,请问您知道如何使用python编写toml文件吗?谢谢
你打球像那谁
你打球像那谁
嗯,我们总是在造轮子中不断地进步的。79, 写的非常好
Go 语言 TOML 解析器--tom-toml

TOML 是简洁配置语言. tom-toml 为 go 语言提供了对 TOML 操作支持. 解析 从文本或者文件对TOML进行解析生成基于map的 Toml 对象 注释 支持 TOML 中的注释操作 格式化 Toml 对象可以输出带缩...

喻恒春
2014/01/30
1K
0
Go语言打造以太坊智能合约测试框架(level2)

传送门: 柏链项目学院 第二课 智能合约自动化编译 前期内容回顾 之前我们的介绍的是如何通过solc编译智能合约,并且调用智能合约,本节我们继续实践,将智能合约的代码自动化编译以及abi文件...

D256
04/09
0
0
小型配置脚本语言--TOML

TOML -> Tom's Obvious, Minimal Language. TOML 类似 INI 配置的语法,但更好!TOML 的目标是成为一个极简的配置文件格式。TOML 被设计成可以无歧义地被映射为哈希表,从而被多种语言解析。...

匿名
2013/02/25
4.7K
2
nsq使用的TOML配置文件规范文档中文版

在阅读 nsq 源码的时候,发现nsq使用 TOML 配置文件规范。顺带翻译了大部分。采用英中文混排的方式,这样比较容易理解。 toml-lang/toml TOML Tom's Obvious, Minimal Language. 直观的,最小...

智深
2014/07/31
0
2
使用cpptoml 读取 TOML 格式配置文件

获取cpptoml 只需要 cpptoml.h 。 $ git clone https://github.com/skystrife/cpptoml.git 2. TOM配置 mysql.toml配置文件如下: [Title]Author="lowkey2046" [MySQL]Host="127.0.0.1"Port=3......

for_
2015/12/28
167
1

没有更多内容

加载失败,请刷新页面

加载更多

抽象同步队列AQS——AbstractQueuedSynchronizer锁详解

AQS——锁的底层支持 谈到并发,不得不谈ReentrantLock;而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQS定义了一套多线程访问共享资...

须臾之余
今天
3
0
springboot配置百度UEditor 富文本详解

富文本简介 UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码... 准备工作 ueditor需要单独文...

wotrd
昨天
4
0
mysql 5.7之my.cnf配置大全

[client]port = 3306socket = /tmp/mysql.sock[mysqld]###############################基础设置######################################Mysql服务的唯一编号 每个mysql服务...

Online_Reus
昨天
3
0
MAVEN打包时引入外部链接的包

1.项目引入了ORACLE的jar包,MAVEN配置如下 2.打jar包的时候需要指定下main入口函数mainClass <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> ......

Cobbage
昨天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部