📄 igor.erl
字号:
case sets:is_element(M, Forbid) of true -> false; false -> not sets:is_element(A, Names) end.%% In this second stage, we resolve any conflicts that may remain%% because of distinct source modules still containing distinct alias%% definitions of the same name - in that case we remove *all* of them%% (mainly for symmetry).alias_expansions_2(Modules, Table) -> %% Get the set of all alias definitions in all modules (collapsing %% duplicated but equivalent definitions). Aliases = lists:foldl( fun (M, A) -> ordsets:union(A, M#module.aliases) end, ordsets:new(), Modules), %% Get the set of names with multiple (distinct) definitions. Names = duplicates([F || {F, _} <- Aliases]), %% Go through all aliases in all source modules and add necessary %% entries to the expansion-table. We expect that there is an entry %% in the table here for each module. lists:foldl( fun (M, T) -> N = M#module.name, lists:foldl( fun ({A, F}, T1) -> case ordsets:is_element(A, Names) of true -> T2 = dict:fetch(N, T1), dict:store(N, dict:store(A, F, T2), T1); false -> T1 end end, T, M#module.aliases) end, Table, Modules).%% ---------------------------------------------------------------------%% Merging the source code.%% Data structure for code transformation environment.-record(code, {module, % = atom() target, % = atom() sources, % = ordset(atom()) static, % = ordset(atom()) safe, % = ordset(atom()) preserved, % = bool() no_headers, % = bool() notes, % = bool() map, % = ({atom(), int()}) -> {atom(), int()} renaming, % = (atom()) -> ({atom(), int()}) -> % {atom(), int()} expand, % = dict({atom(), int()}, % {atom(), {atom(), int()}}) redirect % = dict(atom(), atom()) }).%% `Trees' must be a list of syntax trees of type `form_list'. The%% result is a pair `{Result, Header}' where `Result' is a `form_list'%% tree representing the merged code, and if the `preserved' flag is%% set, `Header' is the list of forms up to and including the first%% `-module(...)' declaration, but stripped of any `-file(...)'%% attributes - otherwise `Header' is an empty list.merge_code(Trees, Modules, Expansions, Renaming, Env, St) -> Env1 = #code{target = Env#merge.target, sources = sets:from_list(Env#merge.sources), static = sets:from_list(Env#merge.static), safe = sets:from_list(Env#merge.safe), preserved = Env#merge.preserved, no_headers = Env#merge.no_headers, notes = Env#merge.notes, redirect = Env#merge.redirect, renaming = Renaming}, Code = order_code(Modules, Trees, Env1), {Code1, Header} = case Env1#code.preserved of true -> take_header(Code); false -> {Code, erl_syntax:form_list([])} end, {Forms, St1} = merge_code_1(Code1, Expansions, Env1, St), Tree = erl_syntax:form_list(Forms), {Tree, Header, St1}.merge_code_1(Code, Expansions, Env, St) -> lists:foldr( fun ({Module, T}, {Acc, St0}) -> M = Module#module.name, Expand = case dict:find(M, Expansions) of {ok, Dict} -> Dict; error -> dict:new() end, Env1 = Env#code{module = M, map = (Env#code.renaming)(M), expand = Expand}, {T1, St1} = transform(T, Env1, St0), {[section_header(M, T1, Env1) | Acc], St1} end, {[], St}, Code).%% Pair module info and source code, in the order we want, and flatten%% the form lists. If the name of the target is the same as one of the%% source modules, and the result should preserve the original module,%% the code for that module should be first in the output.order_code(Modules, Trees, Env) -> order_code(Modules, Trees, {}, [], Env).order_code([M | Ms], [T | Ts], First, Rest, Env) -> T1 = erl_syntax:flatten_form_list(T), case (M#module.name == Env#code.target) and Env#code.preserved of true -> order_code(Ms, Ts, {M, T1}, Rest, Env); false -> order_code(Ms, Ts, First, [{M, T1} | Rest], Env) end;order_code([], [], First, Rest, _Env) -> Rest1 = lists:reverse(Rest), case First of {} -> Rest1; M -> [M | Rest1] end.%% Extracting the "original" header (the `-module(...)' declaration is%% sure to exist).take_header([{M, T} | Ms]) -> Fs = erl_syntax:form_list_elements(T), {Header, Fs1} = take_header_1(Fs, []), T1 = erl_syntax:form_list(Fs1), {[{M, T1} | Ms], Header}.take_header_1([F | Fs], As) -> case erl_syntax_lib:analyze_form(F) of {'attribute', {'module', _}} -> {lists:reverse([F | As]), Fs}; % done {'attribute', {'file', _}} -> take_header_1(Fs, As); % discard _ -> take_header_1(Fs, [F | As]) % keep end.section_header(Name, Tree, Env) -> N = sets:size(Env#code.sources), if N > 1, Name /= Env#code.target, Env#code.notes /= no, Env#code.no_headers /= true -> Text = io_lib:fwrite("The following code stems " "from module `~w'.", [Name]), Header = comment([?COMMENT_BAR, "", lists:flatten(Text), ""]), erl_syntax:form_list([Header, Tree]); true -> Tree end.transform(Tree, Env, St) -> case erl_syntax:type(Tree) of application -> transform_application(Tree, Env, St); attribute -> transform_attribute(Tree, Env, St); function -> transform_function(Tree, Env, St); implicit_fun -> transform_implicit_fun(Tree, Env, St); rule -> transform_rule(Tree, Env, St); record_expr -> transform_record(Tree, Env, St); record_index_expr -> transform_record(Tree, Env, St); record_access -> transform_record(Tree, Env, St); _ -> default_transform(Tree, Env, St) end.default_transform(Tree, Env, St) -> case erl_syntax:subtrees(Tree) of [] -> {Tree, St}; Gs -> {Gs1, St1} = transform_1(Gs, Env, St), Tree1 = rewrite(Tree, erl_syntax:make_tree( erl_syntax:type(Tree), Gs1)), {Tree1, St1} end.transform_1([G | Gs], Env, St) -> {G1, St1} = transform_list(G, Env, St), {Gs1, St2} = transform_1(Gs, Env, St1), {[G1 | Gs1], St2};transform_1([], _Env, St) -> {[], St}.transform_list([T | Ts], Env, St) -> {T1, St1} = transform(T, Env, St), {Ts1, St2} = transform_list(Ts, Env, St1), {[T1 | Ts1], St2};transform_list([], _Env, St) -> {[], St}.%% Renaming function definitionstransform_function(T, Env, St) -> {T1, St1} = default_transform(T, Env, St), F = erl_syntax_lib:analyze_function(T1), {V, Text} = case (Env#code.map)(F) of F -> %% Not renamed {none, []}; {Atom, _Arity} -> %% Renamed Cs = erl_syntax:function_clauses(T1), N = rename_atom( erl_syntax:function_name(T1), Atom), T2 = erl_syntax:function(N, Cs), {{value, T2}, renaming_note(Atom)} end, {maybe_modified(V, T1, 2, Text, Env), St1}.renaming_note(Name) -> [lists:flatten(io_lib:fwrite("renamed function to `~w'", [Name]))].rename_atom(Node, Atom) -> rewrite(Node, erl_syntax:atom(Atom)).%% Renaming Mnemosyne rules (just like function definitions)transform_rule(T, Env, St) -> {T1, St1} = default_transform(T, Env, St), F = erl_syntax_lib:analyze_rule(T1), {V, Text} = case (Env#code.map)(F) of F -> %% Not renamed {none, []}; {Atom, _Arity} -> %% Renamed Cs = erl_syntax:rule_clauses(T1), N = rename_atom( erl_syntax:rule_name(T1), Atom), T2 = rewrite(T1, erl_syntax:rule(N, Cs)), {{value, T2}, renaming_note(Atom)} end, {maybe_modified(V, T1, 2, Text, Env), St1}.%% Renaming "implicit fun" expressions (done quietly).transform_implicit_fun(T, Env, St) -> {T1, St1} = default_transform(T, Env, St), F = erl_syntax_lib:analyze_implicit_fun(T1), {V, Text} = case (Env#code.map)(F) of F -> %% Not renamed {none, []}; {Atom, Arity} -> %% Renamed N = rewrite( erl_syntax:implicit_fun_name(T1), erl_syntax:arity_qualifier( erl_syntax:atom(Atom), erl_syntax:integer(Arity))), T2 = erl_syntax:implicit_fun(N), {{value, T2}, ["function was renamed"]} end, {maybe_modified_quiet(V, T1, 2, Text, Env), St1}.%% Transforming function applicationstransform_application(T, Env, St) -> %% We transform the arguments first, so we can concentrate on the %% application itself after that point. {As, St1} = transform_list( erl_syntax:application_arguments(T), Env, St), F = erl_syntax:application_operator(T), %% See if the operator is an explicit function name. %% (Usually, this will be the case.) case catch {ok, erl_syntax_lib:analyze_function_name(F)} of {ok, Name} -> transform_application_1(Name, F, As, T, Env, St1); syntax_error -> %% Oper is not a function name, but might be any other %% expression - we just visit it and reassemble the %% application. %% We do not handle applications of tuples `{M, F}'. {F1, St2} = transform(F, Env, St1), {rewrite(T, erl_syntax:application(F1, As)), St2}; {'EXIT', R} -> exit(R); R -> throw(R) end.%% At this point we should have an explicit function name, which might%% or might not be qualified by a module name.transform_application_1(Name, F, As, T, Env, St) -> %% Before doing anything else, we must unfold any uses of aliases %% whose definitions have been killed. Arity = length(As), {Name1, F1} = expand_operator(Name, Arity, F, Env), F2 = maybe_modified_quiet(F1, F, 7, ["unfolded alias"], Env), {V, St1} = transform_application_2(Name1, Arity, F2, As, Env, St), T1 = rewrite(T, erl_syntax:application(F2, As)), T3 = case V of none -> T1; {value, {T2, Depth, Message}} -> maybe_modified_quiet({value, T2}, T1, Depth, Message, Env) end, {T3, St1}.%% Here, Name has been expanded if necessary (if so, this is also%% reflected by F), and As have been transformed. We should return%% `{none, State}' if no further rewriting is necessary, and otherwise%% `{{value, {Tree, Depth, Message}}, State}', where `Depth' and%% `Message' are to be passed to `maybe_modified'.transform_application_2(Name, Arity, F, As, Env, St) when is_atom(Name) -> transform_atom_application(Name, Arity, F, As, Env, St);transform_application_2({M, N}, Arity, F, As, Env, St) when is_atom(M), is_atom(N) -> transform_qualified_application(M, N, Arity, F, As, Env, St);transform_application_2(_Name, _Arity, _F, _As, _Env, St) -> {none, St}. % strange name - do nothing.expand_operator(Name, Arity, _F, Env) when is_atom(Name) -> %% An unqualified function name - must be looked up. However, we %% must first check that it is not an auto-imported name - these %% have precedence over normal imports. We do a sanity check on the %% found arity. case is_auto_import({Name, Arity}) of true -> {Name, none}; % auto-import - never expand. false -> case dict:find({Name, Arity}, Env#code.expand) of {ok, {M, {N, A}}} when A =:= Arity -> %% Expand to a qualified name. F1 = erl_syntax:module_qualifier( erl_syntax:atom(M), erl_syntax:atom(N)), {{M, N}, {value, F1}}; error -> %% Not in the table - leave it unchanged {Name, none} end
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -