我正在写一些东西来处理Erlang源代码。几乎该计划的第一行是:
{ok, Forms} = epp_dodger:parse_file(Filename)
但是,我想做一些简单的单元测试。那么:我如何说服epp_dodger
从字符串而不是文件中获取输入?
或者,它有epp_dodger:parse_form/2,3
,需要IODevice
,那么如何在字符串上提供IODevice
?
答案 0 :(得分:1)
下面的代码(这无疑是有点hackish)启动一个gen_server
,它接受一个字符串作为参数,然后满足Erlang I/O Protocol足以满足epp_dodger:parse/2
能够读取并解析该进程中的字符串。
以下是使用它的示例:
1> {ok,P} = iostr:start("-module(x).\n-export([f/0]).\nf() -> ok.\n").
2> epp_dodger:parse(P,1).
{ok,[{tree,attribute,
{attr,1,[],none},
{attribute,
{tree,atom,{attr,1,[],none},module},
[{tree,atom,{attr,1,[],none},x}]}},
{tree,attribute,
{attr,2,[],none},
{attribute,
{tree,atom,{attr,2,[],none},export},
[{tree,list,
{attr,2,[],none},
{list,
[{tree,arity_qualifier,
{attr,2,[],none},
{arity_qualifier,
{tree,atom,{attr,...},f},
{tree,integer,{...},...}}}],
none}}]}},
{tree,function,
{attr,3,[],none},
{func,
{tree,atom,{attr,3,[],none},f},
[{tree,clause,
{attr,3,[],none},
{clause,[],none,[{atom,3,ok}]}}]}}]}
一旦字符串耗尽,该过程就会消失。
请注意,代码可能会遗漏一些事情 - 例如正确处理unicode - 但如果需要,它应该让您知道如何为此目的制作更强大的I / O服务器。
-module(iostr).
-behaviour(gen_server).
-export([start_link/1, start/1, stop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {
data,
line = 1,
lines
}).
start_link(Data) ->
gen_server:start_link(?MODULE, [Data], []).
start(Data) ->
gen_server:start(?MODULE, [Data], []).
stop(Pid) ->
gen_server:cast(Pid, stop).
init([Data0]) ->
Data = [Line++"\n" || Line <- string:tokens(Data0, "\n")],
{ok, #state{data=Data,lines=length(Data)}}.
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({io_request,From,ReplyAs,{get_until,_,_,_,_,_}},
#state{data=[],lines=L}=State) ->
From ! {io_reply, ReplyAs, {eof,L}},
{stop, normal, State};
handle_info({io_request,From,ReplyAs,{get_until,_,_,M,F,Args}},
#state{data=Data,line=L}=State) ->
case handler(Data,L,[],M,F,Args) of
eof ->
Lines = State#state.lines,
From ! {io_reply, ReplyAs, {eof,Lines}},
{stop, normal, State#state{data=[]}};
{ok,Result,Rest,NData,NL} ->
From ! {io_reply, ReplyAs, Result},
case Rest of
[] ->
{noreply, State#state{data=NData,line=NL}};
_ ->
{noreply, State#state{data=[Rest|NData],line=NL}}
end
end;
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handler([Input|Data],L,Cont,M,F,Extra) ->
case catch apply(M,F,[Cont,Input|Extra]) of
{done,eof,_} ->
eof;
{done,Result,Rest} ->
{ok,Result,Rest,Data,L+1};
{more,NCont} ->
case Data of
[] -> eof;
_ -> handler(Data,L+1,NCont,M,F,Extra)
end
end.