如何将字符串传递给epp_dodger?

时间:2014-03-24 18:21:22

标签: erlang

我正在写一些东西来处理Erlang源代码。几乎该计划的第一行是:

{ok, Forms} = epp_dodger:parse_file(Filename)

但是,我想做一些简单的单元测试。那么:我如何说服epp_dodger从字符串而不是文件中获取输入?

或者,它有epp_dodger:parse_form/2,3,需要IODevice,那么如何在字符串上提供IODevice

1 个答案:

答案 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.