shell.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 448 行

ERL
448
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(shell).-export([start/0,start/1,server/0,server/1,evaluator/3,local_func/4,	 history/1, results/1]).-define(LINEMAX, 30).-define(DEF_HISTORY, 20).-define(DEF_RESULTS, 20).start()->    start(false).start(NoCtrlG) ->    %% code:ensure_loaded(user_default),    spawn(shell, server, [NoCtrlG]).default_packages() ->    [].default_modules() ->    [].server(NoCtrlG)->    put(no_control_g, NoCtrlG),    server().    server() ->    %% Our spawner has fixed the process groups.    Bs0 = erl_eval:new_bindings(),    Bs = lists:foldl(fun ({K, V}, D) ->			     erl_eval:add_binding({module,K}, V, D)		     end,		     lists:foldl(fun (P, D) ->					 import_all(P, D)				 end,				 Bs0, default_packages()),		     default_modules()),    %% io:fwrite("Imported modules: ~p.\n", [erl_eval:bindings(Bs)]),    process_flag(trap_exit, true),    case get(no_control_g) of	true ->	    io:fwrite("Eshell V~s~n", [erlang:system_info(version)]);	_undefined_or_false ->	    io:fwrite("Eshell V~s  (abort with ^G)~n",		      [erlang:system_info(version)])    end,    erase(no_control_g),    check_env(shell_history_length, "shell history length"),    check_env(shell_saved_results, "max number of saved results"),    History = get_env(shell_history_length, ?DEF_HISTORY),    Results = get_env(shell_saved_results, ?DEF_RESULTS),    server_loop(0, start_eval(Bs, []), Bs, [], History, Results).server_loop(N0, Eval_0, Bs0, Ds0, History0, Results0) ->    N = N0 + 1,    {Res, Eval0} = get_command(prompt(N), Eval_0, Bs0, Ds0),    case Res of 	{ok,Es0,EndLine} ->			%Commands	    case expand_hist(Es0, N) of		{ok,Es} ->		    {V,Eval,Bs,Ds} = shell_cmd(Es, Eval0, Bs0, Ds0, N),		    History = get_env(shell_history_length, ?DEF_HISTORY),		    Results = min(History,				  get_env(shell_saved_results, ?DEF_RESULTS)),		    add_cmd(N, Es, V),		    del_cmd(command, N - History, N - History0),		    del_cmd(result, N - Results, N - Results0),		    server_loop(N, Eval, Bs, Ds, History, Results);		{error,E} ->		    io:fwrite("** ~s **\n", [E]),		    server_loop(N0, Eval0, Bs0, Ds0, History0, Results0)	    end;	{error,{Line,Mod,What},EndLine} ->	    io:fwrite("** ~w: ~s **\n", [Line,apply(Mod,format_error,[What])]),	    server_loop(N0, Eval0, Bs0, Ds0, History0, Results0);	{error,terminated} ->			%Io process terminated	    exit(Eval0, kill),	    terminated;	{error,interrupted} ->			%Io process interrupted us	    exit(Eval0, kill),	    {_,Eval,_,_} = shell_rep(Eval0, Bs0, Ds0),	    server_loop(N0, Eval, Bs0, Ds0, History0, Results0);	{eof,EndLine} ->	    io:fwrite("** Terminating erlang (~w) **\n", [node()]),	    halt();	eof ->	    io:fwrite("** Terminating erlang (~w) **\n", [node()]),	    halt()    end.get_command(Prompt, Eval, Bs, Ds) ->    Parse = fun() -> exit(io:parse_erl_exprs(Prompt)) end,    Pid = spawn_link(erlang, apply, [Parse, []]),    get_command1(Pid, Eval, Bs, Ds).get_command1(Pid, Eval, Bs, Ds) ->    receive	{'EXIT', Pid, Res} ->	    {Res, Eval};	{'EXIT', Eval, Reason} ->	    io:fwrite("** exited: ~P **\n", [Reason, ?LINEMAX]),	    get_command1(Pid, start_eval(Bs, Ds), Bs, Ds)    end.prompt(N) ->    case is_alive() of	true -> {format,"(~s)~w> ",[node(),N]};	false -> {format,"~w> ",[N]}    end.%% expand_hist(Expressions, CommandNumber)%%  Preprocess the expression list replacing all history list commands%%  with their expansions.expand_hist(Es, C) ->    catch {ok,expand_exprs(Es, C)}.expand_exprs([E|Es], C) ->    [expand_expr(E, C)|expand_exprs(Es, C)];expand_exprs([], C) ->    [].expand_expr({cons,L,H,T}, C) ->    {cons,L,expand_expr(H, C),expand_expr(T, C)};expand_expr({lc,L,E,Qs}, C) ->    {lc,L,expand_expr(E, C),expand_quals(Qs, C)};expand_expr({tuple,L,Elts}, C) ->    {tuple,L,expand_exprs(Elts, C)};expand_expr({record_index,L,Name,F}, C) ->    {record_index,L,Name,expand_expr(F, C)};expand_expr({record,L,Name,Is}, C) ->    {record,L,Name,expand_fields(Is, C)};expand_expr({record_field,L,R,Name,F}, C) ->    {record_field,L,expand_expr(R, C),Name,expand_expr(F, C)};expand_expr({record,L,R,Name,Ups}, C) ->    {record,L,expand_expr(R, C),Name,expand_fields(Ups, C)};expand_expr({record_field,L,R,F}, C) ->		%This is really illegal!    {record_field,L,expand_expr(R, C),expand_expr(F, C)};expand_expr({block,L,Es}, C) ->    {block,L,expand_exprs(Es, C)};expand_expr({'if',L,Cs}, C) ->    {'if',L,expand_cs(Cs, C)};expand_expr({'case',L,E,Cs}, C) ->    {'case',L,expand_expr(E, C),expand_cs(Cs, C)};expand_expr({'receive',L,Cs}, C) ->    {'receive',L,expand_cs(Cs, C)};expand_expr({'receive',L,Cs,To,ToEs}, C) ->    {'receive',L,expand_cs(Cs, C), expand_expr(To, C), expand_exprs(ToEs, C)};expand_expr({call,L,{atom,_,e},[N]}, C) ->    case get_cmd(N, C) of	{[Ce],V} ->	    Ce;	{Ces,V} ->	    {block,L,Ces};	undefined ->	    no_command(N)    end;expand_expr({call,L,{atom,_,v},[N]}, C) ->    case get_cmd(N, C) of	{Ces,V} ->	    {value,L,V};	undefined ->	    no_command(N)    end;expand_expr({call,L,F,Args}, C) ->    {call,L,expand_expr(F, C),expand_exprs(Args, C)};expand_expr({'catch',L,E}, C) ->    {'catch',L,expand_expr(E, C)};expand_expr({match,L,Lhs,Rhs}, C) ->    {match,L,Lhs,expand_expr(Rhs, C)};expand_expr({op,L,Op,Arg}, C) ->    {op,L,Op,expand_expr(Arg, C)};expand_expr({op,L,Op,Larg,Rarg}, C) ->    {op,L,Op,expand_expr(Larg, C),expand_expr(Rarg, C)};expand_expr({remote,L,M,F}, C) ->    {remote,L,expand_expr(M, C),expand_expr(F, C)};expand_expr(E, C) ->				%All constants    E.expand_cs([{clause,L,P,G,B}|Cs], C) ->    [{clause,L,P,G,expand_exprs(B, C)}|expand_cs(Cs, C)];expand_cs([], C) ->    [].expand_fields([{record_field,L,F,V}|Fs], C) ->    [{record_field,L,expand_expr(F, C),expand_expr(V, C)}|     expand_fields(Fs, C)];expand_fields([], C) -> [].expand_quals([{generate,L,P,E}|Qs], C) ->    [{generate,L,P,expand_expr(E, C)}|expand_quals(Qs, C)];expand_quals([E|Qs], C) ->    [expand_expr(E, C)|expand_quals(Qs, C)];expand_quals([], C) -> [].no_command(N) ->    throw({error,io_lib:fwrite("~s: command not found", [erl_pp:expr(N)])}).%% add_cmd(Number, Expressions, Value)%% get_cmd(Number, CurrentCommand)%% del_cmd(Number)add_cmd(N, Es, V) ->    put({command,N}, Es),    put({result,N}, V).getc(N) ->    {get({command,N}),     get({result,N})}.get_cmd(Num, C) ->    case catch erl_eval:expr(Num, []) of	{value,N,_} when N < 0 -> getc(C+N);	{value,N,_} -> getc(N);	Other -> undefined    end.del_cmd(Type, N, N0) when N < N0 ->    ok;del_cmd(Type, N, N0) ->    erase({Type,N}),    del_cmd(Type, N-1, N0).%% shell_cmd(Sequence, Evaluator, Bindings, Dictionary, CommandNumber)%% shell_rep(Evaluator) ->%%	{Value,Evaluator,Bindings,Dictionary}%%  Send a command to the evaluator and wait for the reply. Start a new%%  evaluator if necessary.shell_cmd(Es, Eval, Bs, Ds, N) ->    Eval ! {shell_cmd,self(),{eval,Es}},    shell_rep(Eval, Bs, Ds).shell_rep(Ev, Bs0, Ds0) ->    receive	{shell_rep,Ev,{value,V,Bs,Ds}} ->	    io:fwrite("~P~n", [V,?LINEMAX]),	    {V,Ev,Bs,Ds};	{shell_req,Ev,get_cmd} ->	    Ev ! {shell_rep,self(),get()},	    shell_rep(Ev, Bs0, Ds0);	{shell_req,Ev,exit} ->	    Ev ! {shell_rep,self(),exit},	    io:fwrite("** Terminating shell **\n", []),	    exit(normal);	{'EXIT',Ev,Reason} ->			%It has exited unnaturally	    io:fwrite("** exited: ~P **\n", [Reason,?LINEMAX]),	    {{'EXIT',Reason},start_eval(Bs0, Ds0), Bs0, Ds0};	{'EXIT',Id,interrupt} ->		%Someone interrupted us	    exit(Ev, kill),	    shell_rep(Ev, Bs0, Ds0);	{'EXIT',Id,R} ->	    exit(Ev, R),	    exit(R);	Other ->				%Ignore everything else	    shell_rep(Ev, Bs0, Ds0)    end.start_eval(Bs, Ds) ->    spawn_link(shell, evaluator, [self(),Bs,Ds]).%% evaluator(Shell, Bindings, ProcessDictionary)%%  Evaluate expressions from the shell. Use the "old" variable bindings%%  and ductionary.evaluator(Shell, Bs, Ds) ->    init_dict(Ds),    eval_loop(Shell, Bs).eval_loop(Shell, Bs0) ->    receive	{shell_cmd,Shell,{eval,Es}} ->	    {value,V,Bs} = erl_eval:exprs(Es, Bs0,					  {eval,{shell,local_func},[Shell]}),	    Shell ! {shell_rep,self(),{value,V,Bs,get()}},	    eval_loop(Shell, Bs)    end.init_dict([{K,V}|Ds]) ->    put(K, V),    init_dict(Ds);init_dict([]) -> true.%% local_func(Function, Args, Bindings, Shell) ->%%	{value,Val,Bs}%%  Evaluate local functions, including shell commands.local_func(h, [], Bs, Shell) ->    Cs = shell_req(Shell, get_cmd),    Cs1 = lists:filter(fun({{command, _},_}) -> true;			  ({{result, _},_}) -> true;			  (_) -> false		       end,		       Cs),    Cs2 = lists:map(fun({{T, N}, V}) -> {{N, T}, V} end,		    Cs1),    Cs3 = lists:keysort(1, Cs2),    {value,list_commands(Cs3),Bs};local_func(b, [], Bs, Shell) ->    {value,list_bindings(erl_eval:bindings(Bs)),Bs};local_func(f, [], Bs, Shell) ->    {value,ok,[]};local_func(f, [{var,_,Name}], Bs, Shell) ->    {value,ok,erl_eval:del_binding(Name, Bs)};local_func(f, [Other], Bs, Shell) ->    exit({function_clause,{shell,f,1}});local_func(which, [{atom,_,M}], Bs, Shell) ->    case erl_eval:binding({module,M}, Bs) of	{value, M1} ->	    {value,M1,Bs};	unbound ->	    {value,M,Bs}    end;local_func(which, [Other], Bs, Shell) ->    exit({function_clause,{shell,which,1}});local_func(import, [M], Bs, Shell) ->    case erl_parse:package_segments(M) of	error -> exit({badarg,[{shell,import,1}]});	M1 ->	    Mod = package_to_string(M1),	    case packages:is_valid(Mod) of		true ->		    Key = list_to_atom(packages:last(Mod)),		    Mod1 = list_to_atom(Mod),		    {value,ok,erl_eval:add_binding({module,Key}, Mod1, Bs)};		false ->		    exit({{bad_module_name, Mod}, [{shell,import,1}]})	    end    end;local_func(import, [Other], Bs, Shell) ->    exit({function_clause,[{shell,import,1}]});local_func(import_all, [P], Bs0, Shell) ->    case erl_parse:package_segments(P) of	error -> exit({badarg,[{shell,import_all,1}]});	P1 ->	    Name = package_to_string(P1),	    case packages:is_valid(Name) of		true ->		    Bs1 = import_all(Name, Bs0),		    {value,ok,Bs1};		false ->		    exit({{bad_package_name, Name},			  [{shell,import_all,1}]})	    end    end;local_func(import_all, [Other], Bs, Shell) ->    exit({function_clause,[{shell,import_all,1}]});local_func(use, [M], Bs, Shell) ->    local_func(import, [M], Bs, Shell);local_func(use_all, [M], Bs, Shell) ->    local_func(import_all, [M], Bs, Shell);local_func(history, [{integer,_,N}], Bs, Shell) ->    {value,history(N),Bs};local_func(history, [Other], Bs, Shell) ->    exit({function_clause,{shell,history,1}});local_func(results, [{integer,_,N}], Bs, Shell) ->    {value,results(N),Bs};local_func(results, [Other], Bs, Shell) ->    exit({function_clause,{shell,results,1}});local_func(exit, [], Bs, Shell) ->    shell_req(Shell, exit),			%This terminates us    exit(normal);local_func(F, As0, Bs0, Shell) when atom(F) ->    {As,Bs} = erl_eval:expr_list(As0, Bs0, {eval,{shell,local_func},[Shell]}),    case erlang:function_exported(user_default, F, length(As)) of	true ->	    {value,apply(user_default, F, As),Bs};	false ->	    {value,apply(shell_default, F, As),Bs}    end;local_func(F, As0, Bs0, Shell) ->    {As,Bs} = erl_eval:expr_list(As0, Bs0, {eval,{shell,local_func},[Shell]}),    {value,apply(F, As),Bs}.import_all(P, Bs0) ->    Ms = packages:find_modules(P),    lists:foldl(fun (M, Bs) ->			Key = list_to_atom(M),			M1 = list_to_atom(packages:concat(P, M)),			erl_eval:add_binding({module,Key}, M1, Bs)		end,		Bs0, Ms).shell_req(Shell, Req) ->    Shell ! {shell_req,self(),Req},    receive	{shell_rep,Shell,Rep} -> Rep    end.list_commands([{{N,command},Es}, {{N,result}, V} |Ds]) ->    io:requests([{format,"~w: ~s~n",[N,erl_pp:exprs(Es)]},		 {format,"-> ~P~n",[V,?LINEMAX]}]),    list_commands(Ds);list_commands([{{N,command},Es} |Ds]) ->    io:requests([{format,"~w: ~s~n",[N,erl_pp:exprs(Es)]}]),    list_commands(Ds);list_commands([D|Ds]) ->    list_commands(Ds);list_commands([]) -> ok.list_bindings([{Name,Val}|Bs]) ->    io:fwrite("~s = ~P~n", [Name,Val,?LINEMAX]),    list_bindings(Bs);list_bindings([]) ->    ok.min(X, Y) when X < Y ->    X;min(X, Y) ->    Y.get_env(V, Def) ->    Def.	    check_env(V, Name) ->    ok.	    set_env(App, Name, Val, Default) ->    Default.history(L) when integer(L), L >= 0 ->    set_env(stdlib, shell_history_length, L, ?DEF_HISTORY).results(L) when integer(L), L >= 0 ->    set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS).%% In syntax trees, module/package names are atoms or lists of atoms.package_to_string(A) when atom(A) -> atom_to_list(A);package_to_string(L) when list(L) -> packages:concat(L).

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?