diff --git a/erlang/apps/as/src/scan_state.erl b/erlang/apps/as/src/scan_state.erl
index 88c1be8..c55e805 100644
--- a/erlang/apps/as/src/scan_state.erl
+++ b/erlang/apps/as/src/scan_state.erl
@@ -19,7 +19,6 @@
%%% along with pdp10-tools. If not, see .
-module(scan_state).
--behaviour(gen_server).
%% API
-export([ % file I/O wrappers
@@ -33,15 +32,6 @@
, format_error/1
]).
-%% gen_server callbacks
--export([ init/1
- , handle_call/3
- , handle_cast/2
- , handle_info/2
- , terminate/2
- , code_change/3
- ]).
-
%% The scanner state records the I/O handle, implements a one-character
%% pushback buffer, and maintains the current line number.
%% TODO: maintain column number too?
@@ -52,135 +42,84 @@
, linenr :: pos_integer()
}).
--type scan_state() :: pid().
--type location() :: {FileName :: string(), LineNr :: pos_integer()}.
+-type scan_state() :: {scan_state, reference()}.
+-type location() :: {Filename :: string(), LineNr :: pos_integer()}.
-export_type([scan_state/0, location/0]).
%% API -------------------------------------------------------------------------
--spec fclose(scan_state()) -> ok | {error, {module(), term()}}.
-fclose(Pid) ->
- gen_server:call(Pid, fclose, infinity).
-
--spec fgetc(scan_state()) -> {ok, byte()} | eof | {error, {module(), term()}}.
-fgetc(Pid) ->
- gen_server:call(Pid, fgetc, infinity).
-
--spec fopen(string()) -> {ok, scan_state()} | {error, {module(), term()}}.
-fopen(File) ->
- do_fopen(File).
-
--spec stdin() -> {ok, scan_state()}.
-stdin() ->
- do_fopen(stdin).
-
-do_fopen(File) ->
- case gen_server:start(?MODULE, File, []) of
- {error, {shutdown, Reason}} -> {error, Reason};
- Result -> Result
- end.
-
--spec ungetc(byte(), scan_state()) -> ok | {error, {module(), term()}}.
-ungetc(Ch, Pid) ->
- gen_server:call(Pid, {ungetc, Ch}, infinity).
-
--spec location(scan_state()) -> {ok, location()}.
-location(Pid) ->
- gen_server:call(Pid, location, infinity).
-
--spec format_error(term()) -> io_lib:chars().
-format_error(Reason) ->
- case Reason of
- {bad_request, Req} ->
- io_lib:format("internal error: bad request: ~p", [Req]);
- ungetc ->
- "internal error: invalid ungetc";
- _ ->
- io_lib:format("~p", [Reason])
- end.
-
-%% gen_server callbacks --------------------------------------------------------
-
-init(stdin) ->
- do_init("", standard_io);
-init(File) ->
- case file:open(File, [raw, read, read_ahead]) of
- {ok, IoDev} -> do_init(File, IoDev);
- {error, Reason} ->
- %% The {shutdown, ...} wrapper prevents an unwanted crash report.
- {stop, {shutdown, {file, Reason}}}
- end.
-
-do_init(FileName, IoDev) ->
- {ok, #state{ filename = FileName
- , iodev = IoDev
- , ungetc = []
- , linenr = 1
- }}.
-
-handle_call(Req, _From, State) ->
- case Req of
- fclose ->
- handle_fclose(State);
- fgetc ->
- handle_fgetc(State);
- {ungetc, Ch} ->
- handle_ungetc(State, Ch);
- location ->
- {reply, {ok, {State#state.filename, State#state.linenr}}, State};
- _ ->
- {reply, {error, {?MODULE, {bad_request, Req}}}, State}
- end.
-
-handle_cast(_Req, State) ->
- {noreply, State}.
-
-handle_info(_Info, State) ->
- {noreply, State}.
-
-terminate(_Reason, State) ->
- do_fclose(State).
-
-code_change(_OldVsn, State, _Extra) ->
- {ok, State}.
-
-%% fclose ----------------------------------------------------------------------
-
-handle_fclose(State) ->
- {stop, normal, ok, State}.
-
-do_fclose(State) ->
+-spec fclose(scan_state()) -> ok.
+fclose(Handle) ->
+ State = #state{} = get(Handle),
case State#state.iodev of
standard_io -> ok;
IoDev -> file:close(IoDev)
- end.
+ end,
+ erase(Handle),
+ ok.
-%% fgetc -----------------------------------------------------------------------
-
-handle_fgetc(State) ->
+-spec fgetc(scan_state()) -> {ok, byte()} | eof | {error, {module(), term()}}.
+fgetc(Handle) ->
+ State = #state{} = get(Handle),
case State#state.ungetc of
[] ->
- {Result, NewState} =
- case file:read(State#state.iodev, 1) of
- {ok, [Byte]} ->
- {{ok, Byte},
- case Byte of
- $\n -> State#state{linenr = State#state.linenr + 1};
- _ -> State
- end};
- eof -> {eof, State};
- {error, Reason} -> {{error, {file, Reason}}, State}
- end,
- {reply, Result, NewState};
+ case file:read(State#state.iodev, 1) of
+ {ok, [Byte]} ->
+ case Byte of
+ $\n ->
+ put(Handle, State#state{linenr = State#state.linenr + 1}),
+ {ok, $\n};
+ _ ->
+ {ok, Byte}
+ end;
+ eof ->
+ eof;
+ {error, Reason} ->
+ {error, {file, Reason}}
+ end;
Ch ->
- {reply, {ok, Ch}, State#state{ungetc = []}}
+ put(Handle, State#state{ungetc = []}),
+ {ok, Ch}
end.
-%% ungetc ----------------------------------------------------------------------
+-spec fopen(string()) -> {ok, scan_state()} | {error, {module(), term()}}.
+fopen(Filename) ->
+ case file:open(Filename, [raw, read, read_ahead]) of
+ {ok, IoDev} -> do_fopen(Filename, IoDev);
+ {error, Reason} -> {error, {file, Reason}}
+ end.
-handle_ungetc(State, Ch) ->
+-spec stdin() -> {ok, scan_state()}.
+stdin() ->
+ do_fopen(_Filename = "", _IoDev = standard_io).
+
+do_fopen(Filename, IoDev) ->
+ State = #state{ filename = Filename
+ , iodev = IoDev
+ , ungetc = []
+ , linenr = 1
+ },
+ Handle = {scan_state, make_ref()},
+ put(Handle, State),
+ {ok, Handle}.
+
+-spec ungetc(byte(), scan_state()) -> ok | {error, {module(), term()}}.
+ungetc(Ch, Handle) ->
+ State = #state{} = get(Handle),
case State#state.ungetc of
- [] -> {reply, ok, State#state{ungetc = Ch}};
- _ -> {reply, {error, {?MODULE, ungetc}}, State}
+ [] ->
+ put(Handle, State#state{ungetc = Ch}),
+ ok;
+ _ ->
+ {error, {?MODULE, ungetc}}
end.
+
+-spec location(scan_state()) -> {ok, location()}.
+location(Handle) ->
+ State = #state{} = get(Handle),
+ Location = {State#state.filename, State#state.linenr},
+ {ok, Location}.
+
+-spec format_error(term()) -> io_lib:chars().
+format_error(ungetc) -> "internal error: invalid ungetc".