[erlang-questions] : compile module from string, containing macro definitions
Raimo Niskanen
raimo+erlang-questions@REDACTED
Thu Mar 15 09:31:44 CET 2007
Have you compared these with the undocumented ram_file.
It seems to me at a first glance they do approximately
the same job.
On Wed, Mar 14, 2007 at 10:16:53PM +0100, Richard Carlsson wrote:
> Bengt Kleberg wrote:
> > inspired by string ports i have written a string_io module that allows
> > you to
> >
> > {ok, IO} = string_io:open( "a number: 10", [read, write]),
> > "a" = io:get_chars( IO, '', 1),
> > io:fwrite(IO, "-" ),
> > 10 = io:fread( IO, '', "number: ~d" ),
> > string_io:close( IO ).
>
> I wrote this (attached) module a few years back, and then more or less
> forgot about it again. It can work as a pipe, or just read from a
> string. It would be nice if something like it (or your own version)
> would make its way into the standard library. The code is not terribly
> well tested but I think it worked. Feel free to reuse the best parts.
>
> /Richard
> %% =====================================================================
> %% I/O streams/pipes in Erlang.
> %%
> %% Copyright (C) 2002 Richard Carlsson
> %%
> %% This library is free software; you can redistribute it and/or modify
> %% it under the terms of the GNU Lesser General Public License as
> %% published by the Free Software Foundation; either version 2 of the
> %% License, or (at your option) any later version.
> %%
> %% This library is distributed in the hope that it will be useful, but
> %% WITHOUT ANY WARRANTY; without even the implied warranty of
> %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> %% Lesser General Public License for more details.
> %%
> %% You should have received a copy of the GNU Lesser General Public
> %% License along with this library; if not, write to the Free Software
> %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
> %% USA
> %%
> %% Author contact: richardc@REDACTED
> %%
> %% ---------------------------------------------------------------------
>
> -module(iostream).
>
> -export([open/0, open/1, open_link/0, open_link/1, string/1, string/2,
> close/1]).
>
> -compile(export_all).
>
> %% The default timeout is 'infinity', i.e., someone has to be
> %% responsible for making the I/O stream process terminate when it is
> %% not needed anymore. Typically, the reader does this.
>
> -define(DEF_TIMEOUT, 'infinity').
>
> %% Creates a new I/O stream. Uses default timeout.
>
> open() ->
> open(?DEF_TIMEOUT).
>
> %% Uses given timeout. T is milliseconds or 'infinity'.
>
> open(T) ->
> {ok, spawn(fun () -> loop({buffer, buf__new()}, T) end)}.
>
> %% Like open/0, but also links stream process to creator process.
>
> open_link() ->
> open_link(?DEF_TIMEOUT).
>
> %% Like open/1, but also links stream process to creator process.
>
> open_link(T) ->
> {ok, spawn_link(fun () -> loop({buffer, buf__new()}, T) end)}.
>
> %% Creates a closed stream containing Chars. Uses default timeout.
>
> string(Chars) ->
> string(Chars, ?DEF_TIMEOUT).
>
> %% Like string/1, but uses the given timeout.
>
> string(Chars, T) ->
> spawn(fun () -> loop({buffer, buf__close(buf__new(Chars))}, T) end).
>
> %% Closes the stream. The stream process stays alive until the timeout.
>
> close(Pid) ->
> Pid ! close,
> ok.
>
> %% Terminates the stream process.
>
> kill(Pid) ->
> Pid ! exit,
> ok.
>
> %% ------------------------------------------------------------------
> %% This is the main server loop for the I/O stream process.
> %%
> %% The server has two states: buffering and transferring. In the
> %% buffering state, written characters are simply queued up. If a read
> %% request can be satisfied, the server returns to the buffering state
> %% afterwards. The transfer state is entered if a read request needs
> %% more input. Any writes will be passed directly to the reader, until
> %% it is satisfied. Meanwhile, any additional read requests are queued.
> %% When the last read request is satisfied, the process returns to the
> %% buffering state.
> %%
> %% If there is no activity for the given 'timeout limit' period, the I/O
> %% process terminates. By default the timeout is 'infinity'.
>
> loop(State, Timeout) ->
> receive
> {io_request, From, ReplyAs, Request} when pid(From) ->
> loop(io_request(Request, From, ReplyAs, State), Timeout);
> close ->
> loop(close_request(State), Timeout);
> exit ->
> exit(normal);
> _Other ->
> loop(State, Timeout)
> after Timeout ->
> exit(timed_out)
> end.
>
> close_request({buffer, Buf}) ->
> {buffer, buf__close(Buf)};
> close_request({transfer, Reqs, Buf}) ->
> Buf1 = buf__close(Buf),
> {R, Reqs1} = buf__get(Reqs),
> transfer(R, eof, Buf1, Reqs1),
> {buffer, Buf1}.
>
> io_request(Req, From, ReplyAs, State) ->
> case handle_request(Req, From, ReplyAs, State) of
> {ok, Reply, State1} ->
> io_reply(From, ReplyAs, Reply),
> State1;
> {later, State1} ->
> State1;
> {error, _Reason, State1} ->
> State1
> end.
>
> io_reply(From, ReplyAs, Reply) ->
> From ! {io_reply, ReplyAs, Reply}.
>
> handle_request({put_chars, Chars}, _From, _ReplyAs,
> {buffer, Buf} = State) ->
> case buf__is_open(Buf) of
> true ->
> Buf1 = buf__write(Chars, Buf),
> {ok, ok, {buffer, Buf1}};
> false ->
> {ok, {error, closed}, State}
> end;
> handle_request({put_chars, Chars}, _From, _ReplyAs,
> {transfer, Reqs, Buf} = State) ->
> case buf__is_open(Buf) of
> true ->
> {R, Reqs1} = buf__get(Reqs),
> transfer(R, Chars, Buf, Reqs1);
> false ->
> {ok, {error, closed}, State}
> end;
> handle_request({put_chars, Mod, Func, Args}, From, ReplyAs, State) ->
> handle_request({put_chars, catch apply(Mod, Func, Args)},
> From, ReplyAs, State);
> handle_request({get_until, _Prompt, M, F, As}, From, ReplyAs,
> {buffer, Buf}) ->
> get_until(From, ReplyAs, M, F, As, [], Buf);
> handle_request({get_until, _Prompt, M, F, As}, From, ReplyAs,
> {transfer, Reqs, Buf}) ->
> {later, {transfer, buf__put({From, ReplyAs, M, F, As, []}, Reqs), Buf}};
> handle_request({requests, Reqs}, From, ReplyAs, State) ->
> io_requests(Reqs, From, ReplyAs, {ok, ok, State});
> handle_request(R, _From, _ReplyAs, State) ->
> {ok, {error, {request, R}}, State}.
>
> %% Process a list of output requests as long as the status is 'ok'.
>
> io_requests([R | Rs], From, ReplyAs, {ok, _, State}) ->
> io_requests(Rs, From, ReplyAs, io_request(R, From, ReplyAs, State));
> io_requests(_, _, _, Final) ->
> Final.
>
> %% Beginning a 'get_until' request received in "buffer" state.
>
> get_until(From, ReplyAs, Mod, Func, Args, Cont, Buf) ->
> io:fwrite("Get until. ~p\n", [Buf]),
> {Chars, Buf1} = buf__read(Buf),
> case catch apply(Mod, Func, [Cont, Chars | Args]) of
> {done, Result, Chars1} ->
> io:fwrite("Done: ~p, ~p.\n", [Result, Chars1]),
> {ok, Result, {buffer, buf__push(Chars1, Buf1)}};
> {more, Cont1} ->
> io:fwrite("More: ~p.\n", [Cont1]),
> case buf__is_open(Buf1) of
> true ->
> {later,
> {transfer,
> buf__new([{From, ReplyAs, Mod, Func, Args, Cont1}]),
> Buf1}};
> false ->
> get_until(From, ReplyAs, Mod, Func, Args, Cont1, Buf1)
> end;
> _Other ->
> {error, {error, {Mod, Func, Args}},
> {buffer, buf__push(Chars, Buf1)}}
> end.
>
> %% Resuming an active 'get_until' request when new chars (or EOF) have
> %% arrived. Note that the buffer is empty here. If there are chars over
> %% when we have processed all queued read requests, these are pushed
> %% back onto the buffer before returning.
>
> transfer(R, [], Buf, Reqs) ->
> %% Don't send empty strings to the consumer function;
> %% instead return directly and wait for more characters.
> {ok, ok, {transfer, buf__push([R], Reqs), Buf}};
> transfer({From, ReplyAs, Mod, Func, Args, Cont}, Chars, Buf, Reqs) ->
> case catch apply(Mod, Func, [Cont, Chars | Args]) of
> {done, Result, Chars1} ->
> io_reply(From, ReplyAs, Result), %% reply to read-request
> case buf__get(Reqs) of
> {empty, _} ->
> {ok, ok, {buffer, buf__push(Chars1, Buf)}};
> {R, Reqs1} ->
> transfer(R, Chars1, Buf, Reqs1)
> end;
> {more, Cont1} ->
> {ok, ok,
> {transfer,
> buf__push([{From, ReplyAs, Mod, Func, Args, Cont1}], Reqs),
> Buf}};
> _Other ->
> {error, {error, {Mod, Func, Args}},
> {buffer, buf__push(Chars, Buf)}}
> end.
>
>
> %% An efficient functional buffer with 1-level constant time push-back.
>
> buf__new() ->
> {[], [], [], true}.
>
> buf__new(Ps) ->
> {[], [], Ps, true}.
>
> buf__close({In, Out, Ps, _}) ->
> {In, Out, Ps, false}.
>
> buf__is_open({_, _, _, Open}) ->
> Open.
>
> buf__put(X, {In, Out, Ps, true}) ->
> {[X | In], Out, Ps, true}.
>
> buf__write(List, {In, Out, Ps, true}) ->
> {flatrev(List, In), Out, Ps, true}.
>
> buf__get({In, Out, [X | Ps], Open}) ->
> {X, {In, Out, Ps, Open}};
> buf__get({In, [X | Out], [], Open}) ->
> {X, {In, Out, [], Open}};
> buf__get({[], [], [], true} = Buf) ->
> {empty, Buf};
> buf__get({[], [], [], false} = Buf) ->
> {eof, Buf};
> buf__get({In, [], [], Open}) ->
> [X | Out] = lists:reverse(In),
> {X, {[], Out, [], Open}}.
>
> buf__read({[], [], [], false} = Buf) ->
> {eof, Buf};
> buf__read({[], [], [], true} = Buf) ->
> {[], Buf};
> buf__read({[], [], Ps, Open}) ->
> {Ps, {[], [], [], Open}};
> buf__read({In, Out, Ps, Open}) ->
> {Ps ++ lists:reverse(In, Out), {[], [], [], Open}}.
>
> buf__push(Ps, {In, Out, [], Open}) ->
> {In, Out, Ps, Open};
> buf__push(Ps, {In, Out, Ps1, Open}) ->
> {In, Out, Ps ++ Ps1, Open}.
>
> flatrev(Xs, Tail) ->
> flatrev(Xs, [], Tail).
>
> flatrev([L | Xs], Ls, Tail) when list(L) ->
> flatrev(L, [Xs | Ls], Tail);
> flatrev([X | Xs], Ls, Tail) ->
> flatrev(Xs, Ls, [X | Tail]);
> flatrev([], [Xs | Ls], Tail) ->
> flatrev(Xs, Ls, Tail);
> flatrev([], [], Tail) ->
> Tail.
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://www.erlang.org/mailman/listinfo/erlang-questions
--
/ Raimo Niskanen, Erlang/OTP, Ericsson AB
More information about the erlang-questions
mailing list