昨天寫了個json的解析器。其實yecc早看過了,只是那時對自己要求太高,想一下子寫個小語言。然后大腦就陷入混亂... 后來注意力轉移了。就不那么急著去開發些難道大的。今天回來一看,覺得都理解了,實踐一下,發現沒人寫json的,太好了。于是就在紙上寫了一下。晚上沒事都敲掉計算機里試試。果然很好用。廢話就不多說了,不專業的我在readme里面已經寫了不少廢話了。主要也不知道git有沒規范約束readme不能寫廢話。其實被google騙了一下,有人寫過json的erlang解析,每次我搜yrl,它就主動搜url文件。還要點一下堅持搜索才行??纯磆ttps://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,就不寫這么簡單的了。哈哈。不要期待在下一篇里出現哦。
一種更好的態度,更好的學習、思維方式。它會是網絡極佳的生存方式,你喜歡就對。