跳至内容

Erlang 编程/用 yecc 制作解析器

来自 Wikibooks,自由世界的自由图书

用 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.
华夏公益教科书