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.