Skip to content

Instantly share code, notes, and snippets.

@Kaylebor
Last active January 30, 2020 08:58
Show Gist options
  • Save Kaylebor/7be37160e5cafceebf4ed5115c560717 to your computer and use it in GitHub Desktop.
Save Kaylebor/7be37160e5cafceebf4ed5115c560717 to your computer and use it in GitHub Desktop.
%% @doc
%% USAGE:
%% In the $HOME folder, create a file called .erlang
%% On that file, write the following line:
%%
%% code:load_abs("/path/to/user_default").
%%
%% Note that user_default must have been compiled
%% (there must be a user_default.beam file in the folder)
%%
%% Also, the call to code:load_abs/1 must be the first line
%% in .erlang
%%
%% print_function/3 and print_record/2 have been copied from
%% https://gist.github.com/seriyps/ada87b9896e745f57ddce87672e3728c
%% @end
-module(user_default).
%% ------------------------------------------------------------------
%% API Function Exports
%% ------------------------------------------------------------------
-export([
format/1, format/2,
test_sequence/4,
test_sequence_parallel/5,
print_function/3,
print_record/2
]).
%% Record definitions MUST be a module
-define(DEFAULT_RECORD_DEFINITIONS_MODULE, module_atom).
format(Value) -> format(Value, ?DEFAULT_RECORD_DEFINITIONS_MODULE).
format(Value, RecordDefinitionsModule) when is_list(Value) -> lists:flatten([$[, string:join(lists:map(fun(Record) -> format(Record, RecordDefinitionsModule) end, Value), ",\n"), $]]);
%% The second argument on print_record/2 tells it where to find the record definitions for printing;
%% it can be an atom or a list
format(Value, RecordDefinitionsModule) when is_tuple(Value) -> print_record(Value, RecordDefinitionsModule);
format(Value, _RecordDefinitionsModule) -> lists:flatten(io_lib:format("~p", [Value])).
append_to_file(FileName, Data) ->
case file:read_file_info(FileName) of
{ok, _FileInfo} -> file:write_file(FileName, "\n" ++ format(Data), [append]);
{error, enoent} -> file:write_file(FileName, format(Data))
end.
test_sequence(FileName, Start, End, FBackend) ->
lists:map(fun(Counter) ->
Result = FBackend(Counter),
append_to_file(FileName, Result)
end, lists:seq(Start, End)).
test_sequence_parallel(FileName, Start, End, Interval, FBackend) ->
lists:map(fun(Counter) ->
spawn(fun() ->
CounterEnd = case Counter + Interval of
Val when Val > End -> End;
Val -> Val
end,
test_sequence(FileName, Counter, CounterEnd, FBackend)
end)
end, lists:seq(Start, End, Interval)).
print_function(Mod, Fun, Arity) when is_atom(Fun), is_integer(Arity) ->
try do_print_function(Mod, Fun, Arity)
catch E:R ->
error_logger:warning_msg("Error ~p:~p~n~p", [E, R, erlang:get_stacktrace()]),
io_lib:format("fun ~p:~p/~p", [Mod, Fun, Arity])
end.
do_print_function(Mod, Fun, Arity) when is_atom(Mod) ->
case code:which(Mod) of
preloaded -> error(preloaded);
Filename -> do_print_function(Filename, Fun, Arity)
end;
do_print_function(Filename, Fun, Arity) when is_list(Filename) ->
{ok, {_Mod, [{abstract_code, {_Version, Forms}}]}} = beam_lib:chunks(Filename, [abstract_code]),
[FunDef] = [D || {function, _, Fun0, Arity0, _} = D <- Forms, Fun0 == Fun, Arity0 == Arity],
erl_prettypr:format(erl_syntax:form_list([FunDef])).
print_record(Record, Module) ->
try do_print_record(Record, Module)
catch E:R ->
error_logger:warning_msg("Error: ~p:~p~n~p", [E, R, erlang:get_stacktrace()]),
io_lib:format("~p", [Record])
end.
do_print_record(Record, Module) when is_tuple(Record), is_atom(Module) ->
case code:which(Module) of
preloaded -> Record;
Filename -> do_print_record(Record, Filename)
end;
do_print_record(Record, Filename) when is_tuple(Record), is_list(Filename) ->
{ok, {_Mod, [{abstract_code, {_Version, Forms}}]}} = beam_lib:chunks(Filename, [abstract_code]),
RecordName = element(1, Record),
[RecordDef] = [D || {attribute, _, record, {RecordName1, _} = D} <- Forms, RecordName1 == RecordName],
try do_print_record_pretty(Record, RecordDef)
catch E:R ->
error_logger:warning_msg("prettyprint error ~p:~p", [E, R]),
do_print_record_raw(Record, RecordDef)
end.
%% Handcrafted simple record printer
do_print_record_raw(Record, {Name, FieldDefs}) ->
%% NFields = tuple_size(Record) - 1,
Name = element(1, Record),
FieldVals = tl(tuple_to_list(Record)),
FieldKV = print_pairs(FieldVals, FieldDefs),
["#", atom_to_list(Name), "{", FieldKV, "}"].
print_pairs([Val], [Def]) ->
%% trailing comma
[pair(Val, Def)];
print_pairs([Val | VRest], [Def | DRest]) ->
[pair(Val, Def), ", " | print_pairs(VRest, DRest)];
print_pairs([], []) -> [].
pair(Val, Def) -> io_lib:format("~p = ~500p", [field_to_name(Def), Val]).
%% Pretty printer with erl_syntax and erl_prettypr
do_print_record_pretty(Record, {Name, FieldDefs}) when element(1, Record) == Name ->
Values = tl(tuple_to_list(Record)),
Names = lists:map(fun field_to_name/1, FieldDefs),
Fields = lists:zip(Names, Values),
FieldsExpr = [erl_syntax:record_field(erl_syntax:atom(FieldName), erl_syntax:abstract(FieldValue)) || {FieldName, FieldValue} <- Fields],
RecordExpr = erl_syntax:record_expr(erl_syntax:atom(Name), FieldsExpr),
erl_prettypr:format(erl_syntax:revert(RecordExpr), [{paper, 120}, {ribbon, 110}]).
field_to_name({record_field, _, {atom, _, Name}, _Default}) -> Name;
field_to_name({record_field, _, {atom, _, Name}}) -> Name.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment