-module(jalib_records). %% Reference files: %% rec_test1.erl %% rec_test2.erl %% error_handler.erl %% changes to ~/.erlang %% to install compile %% erlc error_handler.erl jalib_records.erl %% add the following to ${HOME}/.erlang %% (catch jalib_records:start()). %% code:unstick_mod(error_handler). %% code:load_abs("/home/joe/code/jalib/error_handler"). %% io:format("new error handler loaded~n"). %% add %% -compile({parse_transform, jalib_records}). %% to your files -export([start/0, all/0, defn/1, parse_transform/2, print_record/1]). -import(lists, [foreach/2,zip/2]). -import(erl_parse, [abstract/1, normalise/1]). %%---------------------------------------------------------------------- %% server to keep track of records start() -> register(jalib_records, spawn(fun() -> loop([]) end)). all() -> rpc(all). defn(Name) -> rpc({defn, Name}). %% end of interface rpc(Q) -> jalib_records ! {self(), Q}, receive {jalib_records, Reply} -> Reply end. %% L = [{Record,Mod,Defn}] loop(L) -> receive {loaded, Mod, Recs} -> L1 = add_records(Recs, Mod, L), loop(L1); {From, all} -> From ! {jalib_records, L}, loop(L); {From, {defn, Name}} -> From ! {jalib_records, lookup(Name, L)}, loop(L); Any -> io:format(":jalib_records:~p~n",[Any]), loop(L) end. add_records([{RecName,Def}|T], Mod, L) -> case lookup(RecName, L) of {_,Def} -> %% we have a consistent definition add_records(T, Mod, L); error -> %% no definition - just add the new definition add_records(T, Mod, [{RecName,Mod,Def}|L]); {Old, Val1} -> io:format("*** Record definition error:~n" "Record:~p~n" "Old Mod:~p definition:~p~n" "New Mod:~p definition:~p~n", [RecName,Old,Val1,Mod,Def]), add_records(T, Mod, L) end; add_records([], _, L) -> L. lookup(Name, [{Name,Mod,Val}|_]) -> {Mod,Val}; lookup(Name, [_|T]) -> lookup(Name, T); lookup(_, []) -> error. %%---------------------------------------------------------------------- %% a parse transform that adds record definitions to the %% module %% load() -> %% code:unstick_mod(error_handler), %% code:load_abs("./error_handler"). parse_transform(X, _) -> %% Mod = find_module_name(X), Records = [ xform(R) || {attribute,_,record,R} <- X], Extra = make_constant_function(module_records, Records), X1 = X ++ [Extra], %% lib_misc:dump("records", {records, Records, extra, Extra,abs,X}), X2 = add_exports(X1, [{module_records, 0}]), %% pp(Mod, X2), X2. xform({Tag,L}) -> {Tag, [{normalise(X), normalise(Y)} || {record_field,_,X,Y} <- L]}. make_constant_function(Name, Data) -> {function,999999,Name, 0, [{clause,0,[],[],[abstract(Data)]}]}. add_exports([{attribute,Ln,module,_} = M | T], Exports) -> [M,{attribute,Ln,export,Exports}|T]; add_exports([H|T], Exports) -> [H|add_exports(T,Exports)]. %%---------------------------------------------------------------------- %% print a record print_record(X) when is_tuple(X) -> Name = element(1, X), case defn(Name) of error -> exit({eNoDefn,Name}); {_Mod, Fields} -> Names = [K || {K, _} <- Fields], L = tl(tuple_to_list(X)), L1 = zip(Names, L), L2 = [io_lib:format("~w = ~p",[K,V]) || {K,V} <- L1], L3 = ["#",atom_to_list(Name),"{", interleive($,, L2),"}"], lists:flatten(L3) end. interleive(_, [H]) -> [H]; interleive(_, []) -> []; interleive(X, [H|T]) -> [H,X|interleive(X, T)].