简易语言

数字沟通

 

用yecc(erlang)写一个json解析器

  昨天写了个json的解析器。其实yecc早看过了,只是那时对自己要求太高,想一下子写个小语言。然后大脑就陷入混乱... 后来注意力转移了。就不那么急着去开发些难道大的。今天回来一看,觉得都理解了,实践一下,发现没人写json的,太好了。于是就在纸上写了一下。晚上没事都敲掉计算机里试试。果然很好用。废话就不多说了,不专业的我在readme里面已经写了不少废话了。主要也不知道git有没规范约束readme不能写废话。其实被google骗了一下,有人写过json的erlang解析,每次我搜yrl,它就主动搜url文件。还要点一下坚持搜索才行。看看https://github.com/jchris/erlang-json-eep-parser/downloads这上面就是一个解析器,还好我们写的不是太像,他写的更精细一点。我写的更容易使用上手。我写的就到这下载吧https://github.com/yangyusong/erlang_json_parser。

  接着就是讲讲内容了,大学学过编译原理就很容易理解这其中的内容。yrl文件就是erlang中的满足LALR-1规范的解析生成器,相似于yacc。会有很多文章做解释,这里不详述。yrl文件或yacc中的.y这类文件就是给我们写编译规则用的,我们写好一个推理机制,按照规范分解成4部分,放到这一个文件中,那么yecc就可以给我们生成一个符合这个推理规则的解析器,当然这里就是生成.erl的源文件给我们使用,其中会有parse作为默认方法提供给我们解析我们的字符串。

  yrl文件中一共有四部分,其实三部分分别用Nonterminals Terminals Rootsymbol关键字来标识,意义很明显,非终结符,终结符,起始符(这个忘了怎么翻译)。要解释一下也行,一个更好的理解方式就是,非终结符可以在推理符号(->)的左边和右边,相当于函数作用,最终分析为终结符的组合。终结符只能在推理符号(->)的右边。意义就是一个符号系统的基本集合。 Rootsymbol是其中一个非终结符,作为推理的起始点。用一棵解析树来表示的话,Rootsymbol就是根节点。Nonterminals就是树枝。Terminals就是树叶。任何一个符合此推理规则的字符串都可以用这样一棵解析树表示出来(我就不画了)。

  除了上面说的三部分就剩下最重要的部分了:推理规则。其实这四部分都是列表,只不过Rootsymbol这个表只有一个元素。规则列表有多条,通常每行一条规则,和erlang一样用.结束一条规则。
终结符用单引号引起,冒号后面是我们解析后的erlang表达式。$1,$2,$3这种相似正则表达式规则,也说一下吧,就是对冒号左边的元素作为列表并从1计数。再搞不懂就发邮局问吧,呵呵。

  那么这样的一个规则列表就很好建立了,其实这个过程还是有很多规则可以遵循的,其中这里遵循了左递归,终结字符先出现的规则优先表达这两条规则。更多,你还可以画个有限状态机,做一下分析,化解,做成闭包,某些运算还要考虑优先级之类。当然这里这样小的结构基本是最优了,没什么化解的必要。

代码附上
Nonterminals list object kv_list v_list kv k v. % 7

Terminals ',' ':' 'element' '[' ']' '{' '}'. % 7

Rootsymbol object.

object -> '{' '}' : {}.
object -> '{' kv_list '}' : { '$2' }.


kv_list -> kv ',' kv_list : '$1' , '$3'.
kv_list -> kv : '$1'.

kv -> k ':' v : {'$1', '$3'}.

k -> 'element' : '$1'.

v -> 'element' : '$1'.
v -> list : '$1'.
v -> object : '$1'.

list -> '[' ']' : [].
list -> '[' v_list ']' : [ '$2' ].

v_list -> v ',' v_list : '$1' , '$3'.
v_list -> v.

  其中object,list就是json中最基本的结构。kv_list就是剥离大括号后的键值对列表。v_list是剥离中括号的列表。

  再讲讲这个解析器的使用吧,json_parser就是yrl文件生成的解析器了,我们就用这个解析器来做解析。文件use_json_parser对json_parser的使用做了一个包装,那就是parser/1函数了,我们给它传入json字符串就返回解析后的erlang列表。例如我们输入use_json_parser:parser("{a, b, c}").就会返回[{'{',1},
                                        {atom,1,a},
                                        {',',1},
                                        {atom,1,b},
                                        {',',1},
                                        {atom,1,c},
                                        {'}',1},
                                        {'$end',999}]。
  这个文件还提供一个测试函数了unit_test_()。为了方便大家,我还是讲讲测试方法吧。在命令行输入
cd erlang_json_parser
erl -pa ./ebin/ -eval "make:all([{d, 'EUNIT'},{outdir, \"./ebin/\"}, debug_info]) ,eunit:test(\"./ebin\",[]),init:stop()"

  参考更多的解析器制作,可以参考erlang官网提供的计算表达式解析,list解析。也可以从其他网站搜到html,xml等的解析,当然如果你看得多一点还会看到aleppo,erlydtl这类的工程。希望更多的人们投入到这些更有意思的开发中。下次再写yecc,就不写这么简单的了。哈哈。不要期待在下一篇里出现哦。


一种更好的态度,更好的学习、思维方式。它会是网络极佳的生存方式,你喜欢就对。

posted on 2011-12-30 23:28 yangyusong 阅读(3440) 评论(0)  编辑  收藏


只有注册用户登录后才能发表评论。


网站导航:
 

导航

统计

常用链接

留言簿(3)

随笔分类

随笔档案

文章分类

搜索

最新评论

阅读排行榜

评论排行榜