2014-03-24 190 views
0

我在寫東西來處理Erlang源代碼。該程序的第一行幾乎是:如何將字符串傳遞給epp_dodger?

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

但是,我想做一些簡單的單元測試。所以:我如何說服epp_dodger從字符串而不是文件輸入?

另外,它有epp_dodger:parse_form/2,3,這需要IODevice,所以如何提供一個字符串上的IODevice

+1

理論上你可以做一個'文件因爲當它傳遞到'io'模塊時它會中斷。我相信這是一個錯誤。 –

+0

我已經報告錯誤處理使用'ram'選項產生的'IODevice'作爲錯誤。 –

+0

不是一個錯誤。我混淆了兩個獨立的'IODevice'類型,一個來自'file'模塊,另一個來自'io'模塊。 –

回答

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服務器用於此目的的一個好主意。通過將字符串作爲文件和包括'ram`選項,但生成的`IODevice`不`epp_dodger`工作開/ 2`的字符串:

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