erl_syntax_lib.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,970 行 · 第 1/5 页
ERL
1,970 行
%% =====================================================================%% This library is free software; you can redistribute it and/or modify%% it under the terms of the GNU Lesser General Public License as%% published by the Free Software Foundation; either version 2 of the%% License, or (at your option) any later version.%%%% This library is distributed in the hope that it will be useful, but%% WITHOUT ANY WARRANTY; without even the implied warranty of%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU%% Lesser General Public License for more details.%%%% You should have received a copy of the GNU Lesser General Public%% License along with this library; if not, write to the Free Software%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307%% USA%%%% $Id$%%%% @copyright 1997-2006 Richard Carlsson%% @author Richard Carlsson <richardc@it.uu.se>%% @end%% =====================================================================%% @doc Support library for abstract Erlang syntax trees.%%%% This module contains utility functions for working with the%% abstract data type defined in the module {@link erl_syntax}.%%%% @type syntaxTree() = erl_syntax:syntaxTree(). An abstract syntax%% tree. See the {@link erl_syntax} module for details.-module(erl_syntax_lib).-export([analyze_application/1, analyze_attribute/1, analyze_export_attribute/1, analyze_file_attribute/1, analyze_form/1, analyze_forms/1, analyze_function/1, analyze_function_name/1, analyze_implicit_fun/1, analyze_import_attribute/1, analyze_module_attribute/1, analyze_record_attribute/1, analyze_record_expr/1, analyze_record_field/1, analyze_rule/1, analyze_wild_attribute/1, annotate_bindings/1, annotate_bindings/2, fold/3, fold_subtrees/3, foldl_listlist/3, function_name_expansions/1, is_fail_expr/1, limit/2, limit/3, map/2, map_subtrees/2, mapfold/3, mapfold_subtrees/3, mapfoldl_listlist/3, new_variable_name/1, new_variable_name/2, new_variable_names/2, new_variable_names/3, strip_comments/1, to_comment/1, to_comment/2, to_comment/3, variables/1]).%% =====================================================================%% @spec map(Function, Tree::syntaxTree()) -> syntaxTree()%%%% Function = (syntaxTree()) -> syntaxTree()%% %% @doc Applies a function to each node of a syntax tree. The result of%% each application replaces the corresponding original node. The order%% of traversal is bottom-up.%%%% @see map_subtrees/2map(F, Tree) -> case erl_syntax:subtrees(Tree) of [] -> F(Tree); Gs -> Tree1 = erl_syntax:make_tree(erl_syntax:type(Tree), [[map(F, T) || T <- G] || G <- Gs]), F(erl_syntax:copy_attrs(Tree, Tree1)) end.%% =====================================================================%% @spec map_subtrees(Function, syntaxTree()) -> syntaxTree()%%%% Function = (Tree) -> Tree1%% %% @doc Applies a function to each immediate subtree of a syntax tree.%% The result of each application replaces the corresponding original%% node.%%%% @see map/2map_subtrees(F, Tree) -> case erl_syntax:subtrees(Tree) of [] -> Tree; Gs -> Tree1 = erl_syntax:make_tree(erl_syntax:type(Tree), [[F(T) || T <- G] || G <- Gs]), erl_syntax:copy_attrs(Tree, Tree1) end.%% =====================================================================%% @spec fold(Function, Start::term(), Tree::syntaxTree()) -> term()%%%% Function = (syntaxTree(), term()) -> term()%%%% @doc Folds a function over all nodes of a syntax tree. The result is%% the value of `Function(X1, Function(X2, ... Function(Xn, Start)%% ... ))', where `[X1, X2, ..., Xn]' are the nodes of%% `Tree' in a post-order traversal.%%%% @see fold_subtrees/3%% @see foldl_listlist/3fold(F, S, Tree) -> case erl_syntax:subtrees(Tree) of [] -> F(Tree, S); Gs -> F(Tree, fold_1(F, S, Gs)) end.fold_1(F, S, [L | Ls]) -> fold_1(F, fold_2(F, S, L), Ls);fold_1(_, S, []) -> S.fold_2(F, S, [T | Ts]) -> fold_2(F, fold(F, S, T), Ts);fold_2(_, S, []) -> S.%% =====================================================================%% @spec fold_subtrees(Function, Start::term(), Tree::syntaxTree()) ->%% term()%%%% Function = (syntaxTree(), term()) -> term()%%%% @doc Folds a function over the immediate subtrees of a syntax tree.%% This is similar to `fold/3', but only on the immediate%% subtrees of `Tree', in left-to-right order; it does not%% include the root node of `Tree'.%%%% @see fold/3fold_subtrees(F, S, Tree) -> foldl_listlist(F, S, erl_syntax:subtrees(Tree)).%% =====================================================================%% @spec foldl_listlist(Function, Start::term(), [[term()]]) -> term()%%%% Function = (term(), term()) -> term()%%%% @doc Like `lists:foldl/3', but over a list of lists.%%%% @see fold/3%% @see //stdlib/lists:foldl/3foldl_listlist(F, S, [L | Ls]) -> foldl_listlist(F, foldl(F, S, L), Ls);foldl_listlist(_, S, []) -> S.foldl(F, S, [T | Ts]) -> foldl(F, F(T, S), Ts);foldl(_, S, []) -> S.%% =====================================================================%% @spec mapfold(Function, Start::term(), Tree::syntaxTree()) ->%% {syntaxTree(), term()}%%%% Function = (syntaxTree(), term()) -> {syntaxTree(), term()}%%%% @doc Combines map and fold in a single operation. This is similar to%% `map/2', but also propagates an extra value from each%% application of the `Function' to the next, while doing a%% post-order traversal of the tree like `fold/3'. The value%% `Start' is passed to the first function application, and%% the final result is the result of the last application.%%%% @see map/2%% @see fold/3mapfold(F, S, Tree) -> case erl_syntax:subtrees(Tree) of [] -> F(Tree, S); Gs -> {Gs1, S1} = mapfold_1(F, S, Gs), Tree1 = erl_syntax:make_tree(erl_syntax:type(Tree), Gs1), F(erl_syntax:copy_attrs(Tree, Tree1), S1) end.mapfold_1(F, S, [L | Ls]) -> {L1, S1} = mapfold_2(F, S, L), {Ls1, S2} = mapfold_1(F, S1, Ls), {[L1 | Ls1], S2};mapfold_1(_, S, []) -> {[], S}.mapfold_2(F, S, [T | Ts]) -> {T1, S1} = mapfold(F, S, T), {Ts1, S2} = mapfold_2(F, S1, Ts), {[T1 | Ts1], S2};mapfold_2(_, S, []) -> {[], S}.%% =====================================================================%% @spec mapfold_subtrees(Function, Start::term(),%% Tree::syntaxTree()) -> {syntaxTree(), term()}%%%% Function = (syntaxTree(), term()) -> {syntaxTree(), term()}%%%% @doc Does a mapfold operation over the immediate subtrees of a syntax%% tree. This is similar to `mapfold/3', but only on the%% immediate subtrees of `Tree', in left-to-right order; it%% does not include the root node of `Tree'.%%%% @see mapfold/3mapfold_subtrees(F, S, Tree) -> case erl_syntax:subtrees(Tree) of [] -> {Tree, S}; Gs -> {Gs1, S1} = mapfoldl_listlist(F, S, Gs), Tree1 = erl_syntax:make_tree(erl_syntax:type(Tree), Gs1), {erl_syntax:copy_attrs(Tree, Tree1), S1} end.%% =====================================================================%% @spec mapfoldl_listlist(Function, State, [[term()]]) ->%% {[[term()]], term()}%%%% Function = (term(), term()) -> {term(), term()}%%%% @doc Like `lists:mapfoldl/3', but over a list of lists.%% The list of lists in the result has the same structure as the given%% list of lists.mapfoldl_listlist(F, S, [L | Ls]) -> {L1, S1} = mapfoldl(F, S, L), {Ls1, S2} = mapfoldl_listlist(F, S1, Ls), {[L1 | Ls1], S2};mapfoldl_listlist(_, S, []) -> {[], S}.mapfoldl(F, S, [L | Ls]) -> {L1, S1} = F(L, S), {Ls1, S2} = mapfoldl(F, S1, Ls), {[L1 | Ls1], S2};mapfoldl(_, S, []) -> {[], S}.%% =====================================================================%% @spec variables(syntaxTree()) -> set(atom())%%%% set(T) = //stdlib/sets:set(T)%%%% @doc Returns the names of variables occurring in a syntax tree, The%% result is a set of variable names represented by atoms. Macro names%% are not included.%%%% @see //stdlib/setsvariables(Tree) -> variables(Tree, sets:new()).variables(T, S) -> case erl_syntax:type(T) of variable -> sets:add_element(erl_syntax:variable_name(T), S); macro -> %% macro names are ignored, even if represented by variables case erl_syntax:macro_arguments(T) of none -> S; As -> variables_2(As, S) end; _ -> case erl_syntax:subtrees(T) of [] -> S; Gs -> variables_1(Gs, S) end end.variables_1([L | Ls], S) -> variables_1(Ls, variables_2(L, S));variables_1([], S) -> S.variables_2([T | Ts], S) -> variables_2(Ts, variables(T, S));variables_2([], S) -> S.-define(MINIMUM_RANGE, 100).-define(START_RANGE_FACTOR, 100).-define(MAX_RETRIES, 3). % retries before enlarging range-define(ENLARGE_ENUM, 8). % range enlargment enumerator-define(ENLARGE_DENOM, 1). % range enlargment denominatordefault_variable_name(N) -> list_to_atom("V" ++ integer_to_list(N)).%% =====================================================================%% @spec new_variable_name(Used::set(atom())) -> atom()%%%% @doc Returns an atom which is not already in the set `Used'. This is%% equivalent to `new_variable_name(Function, Used)', where `Function'%% maps a given integer `N' to the atom whose name consists of "`V'"%% followed by the numeral for `N'.%%%% @see new_variable_name/2new_variable_name(S) -> new_variable_name(fun default_variable_name/1, S).%% =====================================================================%% @spec new_variable_name(Function, Used::set(atom())) -> atom()%%%% Function = (integer()) -> atom()%%%% @doc Returns a user-named atom which is not already in the set%% `Used'. The atom is generated by applying the given%% `Function' to a generated integer. Integers are generated%% using an algorithm which tries to keep the names randomly distributed%% within a reasonably small range relative to the number of elements in%% the set.%%%% This function uses the module `random' to generate new%% keys. The seed it uses may be initialized by calling%% `random:seed/0' or `random:seed/3' before this%% function is first called.%%%% @see new_variable_name/1%% @see //stdlib/sets%% @see //stdlib/randomnew_variable_name(F, S) -> R = start_range(S), new_variable_name(R, F, S).new_variable_name(R, F, S) -> new_variable_name(generate(R, R), R, 0, F, S).new_variable_name(N, R, T, F, S) when T < ?MAX_RETRIES -> A = F(N), case sets:is_element(A, S) of true -> new_variable_name(generate(N, R), R, T + 1, F, S); false -> A end;new_variable_name(N, R, _T, F, S) -> %% Too many retries - enlarge the range and start over. R1 = (R * ?ENLARGE_ENUM) div ?ENLARGE_DENOM, new_variable_name(generate(N, R1), R1, 0, F, S).%% Note that we assume that it is very cheap to take the size of%% the given set. This should be valid for the stdlib%% implementation of `sets'.start_range(S) -> max(sets:size(S) * ?START_RANGE_FACTOR, ?MINIMUM_RANGE).max(X, Y) when X > Y -> X;max(_, Y) -> Y.%% The previous number might or might not be used to compute the%% next number to be tried. It is currently not used.%%%% It is important that this function does not generate values in%% order, but (pseudo-)randomly distributed over the range.generate(_Key, Range) -> random:uniform(Range). % works well%% =====================================================================%% @spec new_variable_names(N::integer(), Used::set(atom())) -> [atom()]%%%% @doc Like `new_variable_name/1', but generates a list of%% `N' new names.%% %% @see new_variable_name/1new_variable_names(N, S) -> new_variable_names(N, fun default_variable_name/1, S).%% =====================================================================
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?