Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:26
erlang
2561-stdlib-make-it-possible-to-list-local-func...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 2561-stdlib-make-it-possible-to-list-local-functions-and-.patch of Package erlang
From d6ceeb913f15f62ddc8e3ee6966f2b34fe38ae17 Mon Sep 17 00:00:00 2001 From: frazze-jobb <frazze@erlang.org> Date: Tue, 22 Aug 2023 11:50:06 +0200 Subject: [PATCH 1/2] stdlib: make it possible to list local functions and types introducing lf(), lt(), lr() to list local functions, types and records respectively. introducing fl(), ff(), tf() to remove locally defined types and functions. introducing save_module(FilePath) that saves local defined functions and types to given filename. updating fd to store the original function definition. updating rd, rf to accurately add and remove locally defined records to local table. --- lib/stdlib/doc/src/shell.xml | 49 ++++++++ lib/stdlib/src/shell.erl | 196 ++++++++++++++++++++++++++++--- lib/stdlib/src/shell_default.erl | 10 +- lib/stdlib/test/shell_SUITE.erl | 93 ++++++++++++++- 4 files changed, 323 insertions(+), 25 deletions(-) diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml index 3de6f34680..9b07d4c900 100644 --- a/lib/stdlib/doc/src/shell.xml +++ b/lib/stdlib/doc/src/shell.xml @@ -282,6 +282,55 @@ To read all record definitions, use <c>'_'</c> as value of <c>RecordNames</c>.</p> </item> + <tag><c>lf()</c></tag> + <item> + <p>Outputs locally defined function with function specs if they exist. + </p> + </item> + <tag><c>lt()</c></tag> + <item> + <p>Outputs locally defined types. + </p> + </item> + <tag><c>lr()</c></tag> + <item> + <p>Outputs locally defined records. + </p> + </item> + <tag><c>ff()</c></tag> + <item> + <p>Forget locally defined functions (including function specs if they exist). + </p> + </item> + <tag><c>ff({FunName,Arity})</c></tag> + <item> + <p>Forget a locally defined function (including function spec if it exist). Where + <c>FunName</c> is the name of the function as an atom and <c>Arity</c> is an integer. + </p> + </item> + <tag><c>tf()</c></tag> + <item> + <p>Forget locally defined types. + </p> + </item> + <tag><c>tf(Type)</c></tag> + <item> + <p>Forget locally defined type where <c>Type</c> is the name of the type represented as an atom. + </p> + </item> + <tag><c>fl()</c></tag> + <item> + <p>Forget locally defined functions, types and records. + </p> + </item> + <tag><c>save_module(FilePath)</c></tag> + <item> + <p>Saves all locally defined functions, types and records to a module file, where <c>FilePath</c> should include both the + path to the file and the name of the module with <c>.erl</c> suffix. + </p> + <p>Example: <c>src/my_module.erl</c> + </p> + </item> </taglist> </section> diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index 760889164c..146ee97bf8 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.erl @@ -164,7 +164,15 @@ server(StartSync) -> RT = ets:new(?RECORDS, [public,ordered_set]), _ = initiate_records(Bs, RT), process_flag(trap_exit, true), - %% Store function definitions and types in an ets table. + %% Store local definitions of functions, records and types in an ets table. + %% {function_def, {M,F,A}},string()} i.e. "func(X) -> X." + %% {function, {M,F,A}},fun()} i.e. rewrite of the function_def with the same MFA into a fun "fun(X)-> X end." + %% {function_type_def, {M,F,A}},string()} i.e. "-spec func(X :: integer()) -> X." + %% {{function_type, {M,F,A}}, attr_form()} i.e. The intermediate representation of the spec, used for type traversal in edlin_type_suggestion.erl + %% {type_def, {M,F,A}},string()} i.e. "-type foo() :: bar() | baz()" + %% {type, {M,F,A}}, attr_form()} i.e. The intermediate representation of the type, used for type traversal in edlin_type_suggestion.erl + %% {record_def, {M,F,A}},string()} i.e. "-record(foo, {bar :: baz()})." + %% The attr_form() of the record is saved in the RT table. FT = ets:new(user_functions, [public,ordered_set]), %% Check if we're in user restricted mode. @@ -310,9 +318,10 @@ get_command(Prompt, Eval, Bs, RT, FT, Ds) -> {ok, FunDef} -> case {edlin_expand:shell_default_or_bif(atom_to_list(FunName)), shell:local_func(FunName)} of {"user_defined", false} -> - FakeLine =reconstruct(FunDef, FunName), + FunDef1 = lists:flatten(escape_quotes(lists:flatten(erl_pp:form(FunDef)))), + FakeLine = reconstruct(FunDef, FunName), {done, {ok, FakeResult, _}, _} = erl_scan:tokens( - [], "fd("++ atom_to_list(FunName) ++ ", " ++ FakeLine ++ ").\n", + [], "fd("++ atom_to_list(FunName) ++ ", " ++ FakeLine ++ ", \"" ++ FunDef1 ++ "\").\n", {1,1}, [text,{reserved_word_fun,fun erl_scan:reserved_word/1}]), erl_eval:extended_parse_exprs(FakeResult); _ -> erl_eval:extended_parse_exprs(Toks) @@ -340,7 +349,26 @@ get_command(Prompt, Eval, Bs, RT, FT, Ds) -> end, Pid = spawn_link(Parse), get_command1(Pid, Eval, Bs, RT, FT, Ds). - +escape_quotes(String) -> escape_quotes(String, []). + +escape_quotes([], Acc) -> + % When we've processed all characters, reverse the accumulator + % because we've been prepending for efficiency reasons. + lists:reverse(Acc); + +escape_quotes([$\\, $\" | Rest], Acc) -> + % If we find an escaped quote (\"), + % we escape the backslash and the quote (\\\") and continue. + escape_quotes(Rest, [$\", $\\, $\\, $\\ | Acc]); + +escape_quotes([$\" | Rest], Acc) -> + % If we find a quote ("), + % we escape it (\\") and continue. + escape_quotes(Rest, [$\", $\\ | Acc]); + +escape_quotes([Char | Rest], Acc) -> + % In case of any other character, we keep it as is. + escape_quotes(Rest, [Char | Acc]). reconstruct(Fun, Name) -> lists:flatten(erl_pp:expr(reconstruct1(Fun, Name))). reconstruct1({function, Anno, Name, Arity, Clauses}, Name) -> @@ -1066,7 +1094,8 @@ init_dict([]) -> true. %% handled internally - it should return 'true' for all local functions %% handled in this module (i.e. those that are not eventually handled by %% non_builtin_local_func/3 (user_default/shell_default). -local_func() -> [v,h,b,f,fl,rd,rf,rl,rp,rr,history,results,catch_exception]. +%% fd, ft and td should not be exposed to the user +local_func() -> [v,h,b,f,ff,fl,lf,lr,lt,rd,rf,rl,rp,rr,tf,save_module,history,results,catch_exception]. local_func(Func) -> lists:member(Func, local_func()). local_func(v, [{integer,_,V}], Bs, Shell, _RT, _FT, _Lf, _Ef) -> @@ -1092,21 +1121,68 @@ local_func(f, [{var,_,Name}], Bs, _Shell, _RT, _FT, _Lf, _Ef) -> {value,ok,erl_eval:del_binding(Name, Bs)}; local_func(f, [_Other], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> erlang:raise(error, function_clause, [{shell,f,1}]); -local_func(fl, [], Bs, _Shell, _RT, FT, _Lf, _Ef) -> - {value, ets:tab2list(FT), Bs}; -local_func(fd, [{atom,_,FunName}, FunExpr], Bs, _Shell, _RT, FT, _Lf, _Ef) -> +%% Output local functions (with specs) +local_func(lf, [], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + Output = local_functions_and_specs(FT), + io:requests([{put_chars, unicode, Output++"\n"}, nl]), + {value, ok, Bs}; +%% Output local types +local_func(lt, [], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + Output = local_types(FT), + io:requests([{put_chars, unicode, Output}, nl]), + {value, ok, Bs}; +%% Output local records +local_func(lr, [], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + Output = local_records(FT), + io:requests([{put_chars, unicode, Output}, nl]), + {value, ok, Bs}; + +%% Undocumented function that outputs contents of FT to a module file +%% compiles it and loads it, its still in experimentation phase +%% In theory, you may want to be able to load a module in to local table +%% edit them, and then save it back to the file system. +%% You may also want to be able to save a test module. +local_func(save_module, [{string,_,PathToFile}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + [_Path, FileName] = string:split("/"++PathToFile, "/", trailing), + [Module, _] = string:split(FileName, ".", leading), + Output = ( + "-module("++Module++").\n\n" ++ + "-export(["++[atom_to_list(F)++"/"++integer_to_list(A)||{F,A}<-local_defined_functions(FT)]++"]).\n\n"++ + local_types(FT) ++ + local_records(FT) ++ + local_functions(FT) + ), + Ret = case filelib:is_file(PathToFile) of + false -> + write_and_compile_module(PathToFile, Output); + true -> + case io:get_line("File exists, do you want to overwrite it? (y/N)\n") of + "y\n" -> + write_and_compile_module(PathToFile, Output); + _ -> + io:format("Aborting save~n"), + ok + end + end, + {value, Ret, Bs}; +local_func(save_module, [_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> + erlang:raise(error, function_clause, [{shell,save_module,1}]); +local_func(fd, [{atom,_,FunName}, FunExpr, {string, _, FunDef}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> {value, Fun, []} = erl_eval:expr(FunExpr, []), {arity, Arity} = erlang:fun_info(Fun, arity), + %% unescape_quotes(FunDef) ets:insert(FT, [{{function, {shell_default, FunName, Arity}}, Fun}]), + ets:insert(FT, [{{function_def, {shell_default, FunName, Arity}}, FunDef}]), {value, ok, Bs}; -local_func(fd, [_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> - erlang:raise(error, function_clause, [{shell, fd, 1}]); +local_func(fd, [_,_,_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> + erlang:raise(error, function_clause, [{shell, fd, 3}]); local_func(ft, [{string, _, TypeDef}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> case erl_scan:tokens([], TypeDef, {1,1}, [text,{reserved_word_fun,fun erl_scan:reserved_word/1}]) of {done, {ok, Toks, _}, _} -> case erl_parse:parse_form(Toks) of {ok, {attribute,_,spec,{{FunName, Arity},_}}=AttrForm} -> ets:insert(FT, [{{function_type, {shell_default, FunName, Arity}}, AttrForm}]), + ets:insert(FT, [{{function_type_def, {shell_default, FunName, Arity}}, TypeDef}]), {value, ok, Bs}; {error,{_Location,M,ErrDesc}} -> ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), @@ -1118,12 +1194,33 @@ local_func(ft, [{string, _, TypeDef}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> end; local_func(ft, [_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> erlang:raise(error, function_clause, [{shell, ft, 1}]); +local_func(fl, [], Bs, _Shell, RT, FT, _Lf, _Ef) -> + LocalRecords = [R || {{record_def, R},_} <- ets:tab2list(FT)], + true = ets:delete_all_objects(FT), + lists:foreach(fun(Name) -> ets:delete(RT, Name) end, LocalRecords), + {value,ok,Bs}; +local_func(ff, [], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + ets:select_delete(FT, [{{{function_def, '_'},'_'}, [],[true]}]), + ets:select_delete(FT, [{{{function, '_'},'_'}, [],[true]}]), + ets:select_delete(FT, [{{{function_type_def, '_'},'_'}, [],[true]}]), + ets:select_delete(FT, [{{{function_type, '_'},'_'}, [],[true]}]), + {value,ok,Bs}; +local_func(ff, [{tuple, _, [{atom, _, F}, {integer,_,A}]}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + M = shell_default, + ets:select_delete(FT, [{{{function_def, {M, F, A}},'_'}, [],[true]}]), + ets:select_delete(FT, [{{{function, {M, F, A}},'_'}, [],[true]}]), + ets:select_delete(FT, [{{{function_type_def, {M, F, A}},'_'}, [],[true]}]), + ets:select_delete(FT, [{{{function_type, {M, F, A}},'_'}, [],[true]}]), + {value,ok,Bs}; +local_func(ff, [_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> + erlang:raise(error, function_clause, [{shell,ff,1}]); local_func(td, [{string, _, TypeDef}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> case erl_scan:tokens([], TypeDef, {1,1}, [text,{reserved_word_fun,fun erl_scan:reserved_word/1}]) of {done, {ok, Toks, _}, _} -> case erl_parse:parse_form(Toks) of {ok, {attribute,_,type,{TypeName, _, _}}=AttrForm} -> - ets:insert(FT, [{{type, TypeName}, AttrForm}]), + true = ets:insert(FT, [{{type, TypeName}, AttrForm}]), + true = ets:insert(FT, [{{type_def, TypeName}, TypeDef}]), {value, ok, Bs}; {error,{_Location,M,ErrDesc}} -> ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), @@ -1135,12 +1232,23 @@ local_func(td, [{string, _, TypeDef}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> end; local_func(td, [_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> erlang:raise(error, function_clause, [{shell, td, 1}]); -local_func(rd, [{string, _, TypeDef}], Bs, _Shell, RT, _FT, _Lf, _Ef) -> +local_func(tf, [], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + ets:select_delete(FT, [{{{type_def, '_'}, '_'}, [],[true]}]), + ets:select_delete(FT, [{{{type, '_'}, '_'}, [],[true]}]), + {value,ok,Bs}; +local_func(tf, [{atom,_,A}], Bs, _Shell, _RT, FT, _Lf, _Ef) -> + ets:select_delete(FT, [{{{type_def, A},'_'}, [],[true]}]), + ets:select_delete(FT, [{{{type, A},'_'}, [],[true]}]), + {value,ok,Bs}; +local_func(tf, [_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> + erlang:raise(error, function_clause, [{shell,tf,1}]); +local_func(rd, [{string, _, TypeDef}], Bs, _Shell, RT, FT, _Lf, _Ef) -> case erl_scan:tokens([], TypeDef, {1,1}, [text,{reserved_word_fun,fun erl_scan:reserved_word/1}]) of {done, {ok, Toks, _}, _} -> case erl_parse:parse_form(Toks) of - {ok,{attribute,_,_,_}=AttrForm} -> + {ok,{attribute,_,_,{TypeName,_}}=AttrForm} -> [_] = add_records([AttrForm], Bs, RT), + true = ets:insert(FT, [{{record_def, TypeName}, TypeDef}]), {value,ok,Bs}; {error,{_Location,M,ErrDesc}} -> ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), @@ -1152,15 +1260,16 @@ local_func(rd, [{string, _, TypeDef}], Bs, _Shell, RT, _FT, _Lf, _Ef) -> end; local_func(rd, [_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> erlang:raise(error, function_clause, [{shell, rd, 1}]); -local_func(rd, [{atom,_,RecName0},RecDef0], Bs, _Shell, RT, _FT, _Lf, _Ef) -> +local_func(rd, [{atom,_,RecName0},RecDef0], Bs, _Shell, RT, FT, _Lf, _Ef) -> RecDef = expand_value(RecDef0), RDs = lists:flatten(erl_pp:expr(RecDef)), RecName = io_lib:write_atom_as_latin1(RecName0), Attr = lists:concat(["-record(", RecName, ",", RDs, ")."]), {ok, Tokens, _} = erl_scan:string(Attr), case erl_parse:parse_form(Tokens) of - {ok,AttrForm} -> + {ok,{attribute,_,_,{TypeName,_}}=AttrForm} -> [RN] = add_records([AttrForm], Bs, RT), + true = ets:insert(FT, [{{record_def, TypeName}, RecDef}]), {value,RN,Bs}; {error,{_Location,M,ErrDesc}} -> ErrStr = io_lib:fwrite(<<"~ts">>, [M:format_error(ErrDesc)]), @@ -1168,15 +1277,19 @@ local_func(rd, [{atom,_,RecName0},RecDef0], Bs, _Shell, RT, _FT, _Lf, _Ef) -> end; local_func(rd, [_,_], _Bs, _Shell, _RT, _FT, _Lf, _Ef) -> erlang:raise(error, function_clause, [{shell,rd,2}]); -local_func(rf, [], Bs, _Shell, RT, _FT, _Lf, _Ef) -> +local_func(rf, [], Bs, _Shell, RT, FT, _Lf, _Ef) -> + ets:select_delete(FT, [{{{record_def, '_'},'_'}, [],[true]}]), true = ets:delete_all_objects(RT), {value,initiate_records(Bs, RT),Bs}; -local_func(rf, [A], Bs0, _Shell, RT, _FT, Lf, Ef) -> +local_func(rf, [A], Bs0, _Shell, RT, FT, Lf, Ef) -> {[Recs],Bs} = expr_list([A], Bs0, Lf, Ef), if '_' =:= Recs -> + ets:select_delete(FT, [{{{record_def, '_'},'_'}, [],[true]}]), true = ets:delete_all_objects(RT); true -> - lists:foreach(fun(Name) -> true = ets:delete(RT, Name) + lists:foreach(fun(Name) -> + true = ets:delete(RT, Name), + true = ets:delete(FT, {record_def, Name}) end, listify(Recs)) end, {value,ok,Bs}; @@ -1219,6 +1332,53 @@ local_func(F, As0, Bs0, _Shell, _RT, FT, Lf, Ef) when is_atom(F) -> {As,Bs} = expr_list(As0, Bs0, Lf, Ef), non_builtin_local_func(F,As,Bs, FT). +local_functions_and_specs(FT) -> + Output_functions = maps:from_list( + [{{FunNameAtom, Arity}, lists:flatten(FunDef)} ||{{function_def,{_, FunNameAtom, Arity}},FunDef} <- ets:tab2list(FT)]), + Output_function_specs = maps:from_list( + [{{FunNameAtom, Arity}, FunSpec}|| {{function_type_def, {_, FunNameAtom, Arity}}, FunSpec} <- ets:tab2list(FT)]), + Keys1 = maps:keys(Output_functions), + Keys2 = maps:keys(Output_function_specs), + Keys = lists:uniq(Keys1 ++ Keys2), + lists:join($\n,lists:map(fun(Key) -> + Spec = maps:get(Key, Output_function_specs, nospec), + Def = maps:get(Key, Output_functions, nodef), + case {Spec, Def} of + {nospec, _} -> Def; + {_, nodef} -> + {FunName, Arity} = Key, + Spec ++ "%% " ++ atom_to_list(FunName) ++ "/" ++ integer_to_list(Arity) ++ " not implemented"; + {_, _} -> Spec ++ Def + end + end, + Keys)). +local_defined_functions(FT) -> + [{F, A} ||{{function_def,{_, F, A}},_} <- ets:tab2list(FT)]. +local_functions(FT) -> + local_functions(local_defined_functions(FT), FT). +local_functions(Keys, FT) -> + lists:join($\n, + [begin + Spec = case ets:lookup(FT, {function_type_def, {shell_default, F, A}}) of + [{{function_type_def, {shell_default, F, A}}, Spec1}] -> Spec1; + [] -> "" + end, + [{{function_def, {shell_default, F, A}}, Def}] = ets:lookup(FT, {function_def, {shell_default, F, A}}), + Spec++Def + end || {F, A} <- Keys]). +%% Output local types +local_types(FT) -> + lists:join($\n, + [TypeDef||{{type_def, _},TypeDef} <- ets:tab2list(FT)]). +%% Output local records +local_records(FT) -> + lists:join($\n, + [RecDef||{{record_def, _},RecDef} <- ets:tab2list(FT)]). +write_and_compile_module(PathToFile, Output) -> + case file:write_file(PathToFile, Output) of + ok -> c:c(PathToFile); + Error -> Error + end. non_builtin_local_func(F,As,Bs, FT) -> Arity = length(As), case erlang:function_exported(user_default, F, Arity) of diff --git a/lib/stdlib/src/shell_default.erl b/lib/stdlib/src/shell_default.erl index 669266d255..fd11ecfae1 100644 --- a/lib/stdlib/src/shell_default.erl +++ b/lib/stdlib/src/shell_default.erl @@ -67,7 +67,15 @@ help() -> format("rr(File) -- read record information from File (wildcards allowed)\n"), format("rr(F,R) -- read selected record information from file(s)\n"), format("rr(F,R,O) -- read selected record information with options\n"), - format("** commands in module c **\n"), + format("lf() -- list locally defined functions\n"), + format("lt() -- list locally defined types\n"), + format("lr() -- list locally defined records\n"), + format("ff() -- forget all locally defined functions\n"), + format("ff({F,A}) -- forget locally defined function named as atom F and arity A\n"), + format("tf() -- forget all locally defined types\n"), + format("tf(T) -- forget locally defined type named as atom T\n"), + format("fl() -- forget all locally defined functions, types and records\n"), + format("save_module(FilePath) -- save all locally defined functions, types and records to a file\n"), c:help(), format("** commands in module i (interpreter interface) **\n"), format("ih() -- print help for the i module\n"), diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index d337077f13..6be501495a 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -25,7 +25,7 @@ bs_match_misc_SUITE/1, bs_match_int_SUITE/1, bs_match_tail_SUITE/1, bs_match_bin_SUITE/1, bs_construct_SUITE/1, - prompt_width/1, + prompt_width/1,local_definitions_save_to_module_and_forget/1, refman_bit_syntax/1, progex_bit_syntax/1, progex_records/1, progex_lc/1, progex_funs/1, @@ -78,7 +78,7 @@ suite() -> all() -> [forget, known_bugs, otp_5226, otp_5327, otp_5435, otp_5195, otp_5915, otp_5916, - prompt_width, + prompt_width,local_definitions_save_to_module_and_forget, start_interactive, whereis, {group, bits}, {group, refman}, {group, progex}, {group, tickets}, {group, restricted}, {group, records}, {group, definitions}]. @@ -189,13 +189,14 @@ comm_err(<<"ugly().">>), comm_err(<<"1 - 2.">>), %% Make sure we test all local shell functions in a restricted shell. LocalFuncs = shell:local_func(), -[] = lists:subtract(LocalFuncs, [v,h,b,f,fl,rd,rf,rl,rp,rr,history,results,catch_exception]), +[] = lists:subtract(LocalFuncs, [v,h,b,f,fl,ff,lf,lr,lt,rd,rf,rl,rp,rr,tf,save_module,history,results,catch_exception]), LocalFuncs2 = [ <<"A = 1.\nv(1).">>, <<"h().">>, <<"b().">>, <<"f().">>, <<"f(A).">>, - <<"fl()">>, <<"rd(foo,{bar}).">>, <<"rf().">>, <<"rf(foo).">>, <<"rl().">>, <<"rl(foo).">>, <<"rp([hej]).">>, - <<"rr(shell).">>, <<"rr(shell, shell_state).">>, <<"rr(shell,shell_state,[]).">>, - <<"history(20).">>, <<"results(20).">>, <<"catch_exception(0).">>], + <<"fl()">>, <<"ff()">>, <<"ff({my_func,1})">>, <<"lf()">>, <<"lr()">>, <<"lt()">>, + <<"rd(foo,{bar}).">>, <<"rf().">>, <<"rf(foo).">>, <<"rl().">>, <<"rl(foo).">>, <<"rp([hej]).">>, + <<"rr(shell).">>, <<"rr(shell, shell_state).">>, <<"rr(shell,shell_state,[]).">>, <<"tf()">>, <<"tf(hej)">>, + <<"save_module(\"src/my_module.erl\")">>, <<"history(20).">>, <<"results(20).">>, <<"catch_exception(0).">>], lists:foreach(fun(LocalFunc) -> try ("exception exit: restricted shell does not allow"++_Rest) = Error = local_func_error_t(LocalFunc), @@ -649,6 +650,86 @@ typed_records(Config) when is_list(Config) -> file:delete(Test), ok. +local_definitions_save_to_module_and_forget(Config) when is_list(Config) -> + %% extra dot on the empty line is a consequence of dotify + "ok.\n-type hej() :: integer().\n.\nok.\n" = t( + <<"-type hej() :: integer().\n" + "lt().">>), + "ok.\n-record(svej,{a}).\n.\nok.\n" = t( + <<"-record(svej, {a}).\n" + "lr().">>), + "ok.\nok.\n-spec my_func(X) -> X.\nmy_func(X) ->\n X.\n\n.\nok.\n" = t( + <<"-spec my_func(X) -> X.\n" + "my_func(X) -> X.\n" + "lf().">>), + %% Save local definitions to a module + "ok.\nok.\nok.\nok.\n{ok,my_module}.\n" = t( + <<"-type hej() :: integer().\n" + "-record(svej, {a :: hej()}).\n" + "-spec my_func(X) -> X.\n my_func(#svej{a=A}) -> A.\n" + "save_module(\"my_module.erl\").">>), + %% Read back the newly created module + {ok,<<"-module(my_module).\n\n" + "-export([my_func/1]).\n\n" + "-type hej() :: integer().\n" + "-record(svej,{a :: hej()}).\n" + "-spec my_func(X) -> X.\n" + "my_func(#svej{a = A}) ->\n" + " A.\n">>} = file:read_file("my_module.erl"), + file:delete("my_module.erl"), + + %% Forget one locally defined type + "ok.\nok.\nok.\n-type svej() :: integer().\n.\nok.\n" = t( + <<"-type hej() :: integer().\n" + "-type svej() :: integer().\n" + "tf(hej).\n" + "lt().">>), + %% Forget one locally defined record + "ok.\nok.\nok.\n-record(hej,{a}).\n.\nok.\n" = t( + <<"-record(hej, {a}).\n" + "-record(svej, {a}).\n" + "rf(svej).\n" + "lr().">>), + %% Forget one locally defined function + "ok.\nok.\nok.\nok.\nok.\n-spec my_func2(X) -> X.\nmy_func2(X) ->\n X.\n\n.\nok.\n" = t( + <<"-spec my_func(X) -> X.\n" + "my_func(X) -> X.\n" + "-spec my_func2(X) -> X.\n" + "my_func2(X) -> X.\n" + "ff({my_func,1}).\n" + "lf().">>), + %% Forget all locally defined types + "ok.\nok.\nok.\n.\nok.\n" = t( + <<"-type hej() :: integer().\n" + "-type svej() :: integer().\n" + "tf().\n" + "lt().">>), + %% Forget all locally defined records + "ok.\nok.\n[]\n.\nok.\n" = t( + <<"-record(hej, {a}).\n" + "-record(svej, {a}).\n" + "rf().\n" + "lr().">>), + %% Forget all locally defined functions + "ok.\nok.\nok.\nok.\nok.\n\n.\nok.\n" = t( + <<"-spec my_func(X) -> X.\n" + "my_func(X) -> X.\n" + "-spec my_func2(X) -> X.\n" + "my_func2(X) -> X.\n" + "ff().\n" + "lf().">>), + %% Forget all local definitions + "ok.\nok.\nok.\nok.\nok.\n\n.\nok.\n.\nok.\n.\nok.\n" = t( + <<"-type hej() :: integer().\n" + "-record(svej, {a}).\n " + "-spec my_func(X) -> X.\n" + "my_func(X) -> X.\n" + "fl().\n" + "lf().\n" + "lt().\n" + "lr().\n">>), + ok. + %% Known bugs. known_bugs(Config) when is_list(Config) -> %% erl_eval:merge_bindings/2 cannot handle _removal_ of bindings. -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor