Erlang 程式設計/用 yecc 製作解析器
外觀
用 yecc 製作解析器
Yecc 是 erlang 版本的 yacc/bison。
我們在副檔名為 .yrl(yrl 表示 yecc 規則列表)的原始檔中有一個 BNF (Backus-Naur_form) 語法。我們可以使用 yecc 解析簡單的 xhtml 檔案。實際上,我們將應用 yecc 於 html.yrl 以建立一個稱作 html_parser.erl 的解析器。接著,我們使用 html_parser 來解析一些 xhtml,瞧。
yecc:yecc("html.yrl","html_parser.erl").
c(html_parser).
f(B), {_,B,_} =
erl_scan:string(
"<html><head></head><body>hello_world</body></html>").
html_parser:parse(B).
xhtml 程式碼中的所有標記都必須具有匹配的開啟標記和關閉標記。(當然,使用 xmerl 是解析 erlang 中 xml 檔案的一種更強大的方法)。
html.yrl 源
Nonterminals tag elements element start_tag end_tag .
Terminals 'atom' '<' '>' '/'.
Rootsymbol tag.
tag ->
start_tag tag end_tag :
['$1', '$2', '$3'].
tag ->
start_tag tag tag end_tag :
['$1', '$2', '$3', '$4'].
tag ->
start_tag elements end_tag :
['$1', {'contents','$2'}, '$3'].
tag ->
start_tag end_tag :
['$1','$2'].
start_tag -> '<' 'atom' '>' : {'open','$2'}.
end_tag -> '<' '/' 'atom' '>' : {'close','$3'}.
elements -> element : ['$1'].
elements -> element elements : ['$1', '$2'].
element -> atom : '$1'.
% yecc:yecc("html.yrl","html_parser.erl").
% c(html_parser).
% f(B), {_,B,_} =
% erl_scan:string(
% "<html><head></head><body>hello_world</body></html>").
% html_parser:parse(B).
每次編輯源 yrl 檔案時,生成並執行解析器都會很麻煩。為了加快速度,我們可使用一個程式自動為我們生成並執行解析器。我們編譯並執行測試程式,它為我們生成解析器,並在某些文件上對解析器進行測試。
-module(html_test).
-compile(export_all).
start() ->
yecc:yecc("html.yrl","html_parser.erl"),
cover:compile(html_parser),
{_,List_of_symbols,_}=erl_scan:string(
"<html><head><title>greeting</title></head>
<body>
hello there world what is up
</body>
</html>"),
{ok,L} = html_parser:parse(List_of_symbols),
register(do_event, spawn(html_test,event_loop,[])),
Events = lists:flatten(L),
send_events(Events),
Events.
send_events([]) -> do_event ! {exit};
send_events([H|T]) ->
do_event ! H,
%io:format(" ~w ~n",[H]),
send_events(T).
event_loop() ->
receive
{open,{atom,_Line_Number,html}} ->
io:format("~n start scan ~n", []),
event_loop();
{contents,List} ->
Contents = get_contents(List,[]),
io:format("~n contents: ~w ~n", [Contents]);
{exit} -> exit(normal)
end,
event_loop().
get_contents([],Items) -> Items;
get_contents([H|T],Items)->
if
length(T) > 0 ->
NT = hd(T);
true ->
NT = T
end,
{atom,_N,Item} = H,
NItems = Items++[Item],
% io:format(" ~w ",[Item]),
get_contents(NT,NItems).
% 6> c(html_test).
% {ok,html_test}
% 7> html_test:start().
% [greeting]
% [hello,there,world,what,is,up]
% and events.