[erlang-questions] Programmatic interface to the shell

Fred Hebert mononcqc@REDACTED
Mon Aug 10 18:23:09 CEST 2015


On 08/10, Joe Armstrong wrote:
>Hello,
>
>Is there a *simple* programmatic interface to the Erlang shell?
>
>I'd like a module "shell_interface" that works like this:
>
>      Pid = shell_interface:new_shell(),
>
>Returns a new process that behaves like the Erlang shell
>
>      OutStr = shell_interface:eval(Pid, InStr)
>

Short answer is no. The Erlang shell in the `shell' module asks for 
information via the IO protocol and pulls it in, rather than you pushing 
it out.

There's ways to inject yourself in there, but it's not simple.

>This behaves like the Erlang shell. InStr should be what I typed into the
>shell. OutStr should be what the shell replied.
>
>For this purpose we can assume that InStr represents a complete
>sequence of expressions.
>

This sounds more like an evaluator/interpreter:

    1> {ok, Tokens, _} = erl_scan:string("X + 4 * lists:sum([1,2,3,4]).").
    ...
    2> {ok, [Form]} = erl_parse:parse_exprs(Tokens).
    ...
    3> Bindings = erl_eval:add_binding('X', 17, erl_eval:new_bindings()).
    [{'X',17}]
    4> {value, Value, _} = erl_eval:expr(Form, Bindings).
    {value,57,[{'X',17}]}
    5> Value.
    57

With these basic forms it becomes doable to write a mini-shell the way 
you'd like it.

    Eval = fun EvalLoop(Bindings) ->
        receive
            {cmd, Caller, Ref, String} ->
                try
                    {ok, Tokens, _} = erl_scan:string(String),
                    %% many forms can be comma-separated
                    {ok, Forms} = erl_parse:parse_exprs(Tokens),
                    %% eval individually
                    {value, Val, NewBindings} = erl_eval:exprs(Forms, Bindings),
                    Caller ! {ok, Ref, Val},
                    EvalLoop(NewBindings)
                catch
                    T:R ->
                        Caller ! {raise, Ref, T, R},
                        EvalLoop(Bindings)
                end
        end end.

    Send = fun(Pid, String) ->
        Ref = erlang:monitor(process, Pid),
        Pid ! {cmd, self(), Ref, String},
        receive
            {ok, Ref, Value} -> Value;
            {raise, Ref, T, R} -> erlang:T(R)
        end end.

    18> P = spawn(fun() -> Eval([]) end).
    <0.62.0>
    19> Send(P, "X=2+2.").
    4
    20> Send(P, "X*X.").
    16
    21> Send(P, "X/0.").
    ** exception error: an error occurred when evaluating an arithmetic expression
    22> Send(P, "X.").
    4

And there  you have an evaluator. It doesn't support all the stuff like 
'h().' and whatnot, but is close enough otherwise.




More information about the erlang-questions mailing list