一起来实现一个基于元编程的 Io-JSON 库吧!
首先,元编程是什么
Metaprogramming is the process of writing computer programs that treat programs as data, enabling them to analyze or transform existing programs or generate new ones.
按照中文社区比较流行的说法就是……「代码即数据」!
即是通过代码来处理或生成代码,可以实现一些类似「生成重复繁琐的代码」、「自定义语法」的功能。这里我们就考虑利用 Io 的元编程功能自定义语法,让 Io “以为” JSON 是一段合法的 Io 代码。
先来看看 Io 眼中的 JSON
这是一段 JSON 代码:
{
"Bob Smith": "111-111-111",
"Sally Johnson":
{
"phone": "222-222-222",
"email": "abc@efg.com"
},
"number": 10,
"list": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
}
因为 Io 允许我们自定义 {}
和 []
的行为,并且会把它们翻译成 curlyBrackets
和 squareBrackets
,这段代码就会被解释成这样:
curlyBrackets(
"Bob Smith": "111-111-111",
"Sally Johnson": curlyBrackets(
"phone": "222-222-222", "email": "abc@efg.com"
),
"number": 10,
"list": squareBrackets(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
)
再通过 OperatorTable addAssignOperator(":", "assign")
把 :
设置为一个 assign operator,这段 JSON 就会被识别为:
curlyBrackets(
assign("\"Bob Smith\"", "111-111-111"),
assign("\"Sally Johnson\"", curlyBrackets(
assign("\"phone\"", "222-222-222"), assign("\"email\"", "abc@efg.com")
)),
assign("\"number\"", 10),
assign("\"list\"", squareBrackets(1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
)
注意,由于 Io 会把 assign operator 左侧作为变量名并转为字符串作为 assign 的参数,所以我们后续处理时还要把 JSON 原有的双引号去掉。
做到这里事情就比较简单了,我们只要实现 curlyBrackets
、 squareBrackets
和 assign
就可以了。
具体实现
为了便于实现我们可以把 JSON 作为 Map 的一个子类。
JSON = Map clone
curlyBrackets
根据 JSON 的结构,每个 culyBrackets
的参数都是若干个 assign
语句。那么通过 Io 原生的消息操作[1] 把每一条 assign
提取出来,并在当前的 JSON 环境下执行即可。
JSON curlyBrackets := method(
call message arguments foreach(arg,
doMessage(arg)
)
)
squareBrackets
对于一个 squareBrackets(x1, x2, ...)
语句,只要对 xi
计算后,重新组成一个 List
。
JSON squareBrackets := method(
call message arguments map(x, doMessage(x))
)
assign
由于 JSON
直接继承于 Map
,所以 assign
只要把第一个参数的双引号去掉后用 atPut
插入一个 key-value 对。
因为 assign
的 key
参数是 immutable 的状态,还需要先做一个 asMutable
操作。
JSON assign := method(key, value,
atPut(
key asMutable removePrefix("\"") removeSuffix("\""),
value
)
)
完整实现
整个库的关键部分就是上文这些,完整的代码放在我的 GitHub 仓库里。
- 如果有机会的话会考虑再写一篇关于 Io 消息操作的博文 ↩