Packages

Open Telecom Platform Command Language

Current section

Files

Jump to
otpcl src otpcl_shell.erl
Raw

src/otpcl_shell.erl

-module(otpcl_shell).
-if(?OTP_RELEASE >= 21).
-define(CATCH(Type, Reason), Type:Reason:Stacktrace).
-define(HANDLE(Type, Reason),
show_error(Type, Reason, Stacktrace),
read(State, ps1(State), "")).
-else.
-define(CATCH(Type, Reason), Type:Reason).
-define(HANDLE(Type, Reason),
show_error(Type, Reason, erlang:get_stacktrace()),
read(State, ps1(State), "")).
-endif.
-export([start/0, start/1, simple_shell/1]).
start() ->
start(shell_init_state(otpcl_env:default_state())).
start(State) ->
spawn(?MODULE, simple_shell, [State]).
simple_shell(State) ->
process_flag(trap_exit, true),
show_banner(State),
read(State, ps1(State), "").
read(State, Prompt, Acc) ->
parse(State, Acc ++ io:get_line(Prompt)).
parse(State, Input) ->
try otpcl_parse:parse(Input) of
{ok, Tree, []} ->
eval(State, Tree);
{error, {expected, _}, _} ->
read(State, ps2(State), Input)
catch
?CATCH(Anything, Else) ->
?HANDLE(Anything, Else)
end.
eval(State, Tree) ->
try otpcl_eval:interpret(Tree, State) of
{RetVal, NewState} ->
show_retval(RetVal),
read(NewState, ps1(NewState), "")
catch
?CATCH(error, {badmatch,{error,{Rsn,Val},State}}) ->
?HANDLE(Rsn, Val);
?CATCH(Anything, Else) ->
?HANDLE(Anything, Else)
end.
%% Prompts. TODO/FIXME: actually read the PS1 and PS2 variables from
%% the provided state (which is currently ignored).
ps1(State) ->
case otpcl_stdlib:get(['PS1'], State) of
{Prompt, State} ->
Prompt;
_ ->
"otpcl> "
end.
ps2(State) ->
case otpcl_stdlib:get(['PS2'], State) of
{Prompt, State} ->
Prompt;
_ ->
" ...> "
end.
%% Show stuff
show_banner(_) ->
io:format("OTPCL Shell (WIP!)~n~n").
show_retval(RetVal) ->
io:format("~p~n", [RetVal]).
show_error(Step, Reason, Stacktrace) ->
io:format("~p: ~p~nStacktrace:~n", [Step, Reason]),
show_trace(Stacktrace).
show_trace([{Mod,Fun,Arity,Locs}|Rem]) ->
io:format(" ~p:~p/~p~n", [Mod,Fun,Arity]),
show_trace_locs(Locs),
show_trace(Rem);
show_trace([]) ->
ok.
show_trace_locs([{Type,Val}|Rem]) ->
io:format(" ~p: ~p~n", [Type,Val]),
show_trace_locs(Rem);
show_trace_locs([]) ->
ok.
shell_init_state(State0) ->
{_, State1} = otpcl_stdmeta:import([otpcl_stdshell], State0),
{_, State2} = otpcl_stdlib:set(['PS1', "otpcl> "], State1),
{_, State3} = otpcl_stdlib:set(['PS2', " ...> "], State2),
State3.