Skip to content

Instantly share code, notes, and snippets.

@vinoski
Last active December 11, 2015 18:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vinoski/4643721 to your computer and use it in GitHub Desktop.
Save vinoski/4643721 to your computer and use it in GitHub Desktop.
This parse transform looks for a plain call to get_all_bindings/1 within each function body within the module being processed and if found replaces its argument with a list of {variable name, value} tuples, one for each variable used in the function body up to the call point. The module must contain its own suitable definition for the get_all_bi…
-module(get_all_bindings).
-export([parse_transform/2]).
-author('Steve Vinoski <vinoski@ieee.org>').
%% This parse transform looks for a plain call to get_all_bindings/1 within
%% each function body within the module being processed and if found
%% replaces its argument with a list of {variable name, value} tuples, one
%% for each variable used in the function body up to the call point. The
%% module must contain its own suitable definition for the
%% get_all_bindings/1 function.
parse_transform(Forms, _Options) ->
handle_forms(Forms, []).
handle_forms([], Acc) ->
lists:reverse(Acc);
handle_forms([{function,L,Nm,Ar,[{clause,CL,Args,G,Body}]}|Forms], Acc) ->
NewBody = replace_get_all_bindings(Args, Body),
NewFun = {function,L,Nm,Ar,[{clause,CL,Args,G,NewBody}]},
handle_forms(Forms, [NewFun|Acc]);
handle_forms([Form|Forms], Acc) ->
handle_forms(Forms, [Form|Acc]).
replace_get_all_bindings(Args, Body) ->
get_all_bindings(Body, find_vars(Args, sets:new())).
get_all_bindings(Forms, Vars) ->
get_all_bindings(Forms, Vars, false, []).
get_all_bindings([], _, _, Acc) ->
lists:reverse(Acc);
get_all_bindings([{call,CL1,{atom,CL2,get_all_bindings},_}|Forms],
Vars, false, Acc) ->
Bindings = lists:foldl(fun(A,Acc1) ->
V={tuple,CL1,[{atom,CL1,A},
{var,CL1,A}]},
{cons,CL1,V,Acc1}
end, {nil,CL1}, sets:to_list(Vars)),
NewCall = {call,CL1,{atom,CL2,get_all_bindings},[Bindings]},
get_all_bindings(Forms, Vars, true, [NewCall|Acc]);
get_all_bindings([Form|Forms], Vars, false, Acc) ->
get_all_bindings(Forms, find_vars(Form, Vars), false, [Form|Acc]);
get_all_bindings([Form|Forms], Vars, true, Acc) ->
get_all_bindings(Forms, Vars, true, [Form|Acc]).
find_vars([{'fun',_,_}|Vals], Acc) ->
find_vars(Vals, Acc);
find_vars([{var,_,Val}|Vals], Acc) ->
find_vars(Vals, sets:add_element(Val, Acc));
find_vars([{_,_,_}|Vals], Acc) ->
find_vars(Vals, Acc);
find_vars([var,_,Val|Vals], Acc) ->
find_vars(Vals, sets:add_element(Val, Acc));
find_vars(['fun',_,_|Vals], Acc) ->
find_vars(Vals, Acc);
find_vars([Val|Vals], Acc) when is_tuple(Val) ->
find_vars(Vals, find_vars(tuple_to_list(Val), Acc));
find_vars([[Val0|Val1]|Vals], Acc) ->
find_vars([Val1,Vals], find_vars(Val0, Acc));
find_vars([_|Vals], Acc) ->
find_vars(Vals, Acc);
find_vars([], Acc) ->
Acc;
find_vars(Val, Acc) when is_tuple(Val) ->
find_vars(tuple_to_list(Val), Acc);
find_vars(_, Acc) ->
Acc.
@vinoski
Copy link
Author

vinoski commented Jan 29, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment