2014-03-24 150 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.