1%% ``Licensed under the Apache License, Version 2.0 (the "License"); 2%% you may not use this file except in compliance with the License. 3%% You may obtain a copy of the License at 4%% 5%% http://www.apache.org/licenses/LICENSE-2.0 6%% 7%% Unless required by applicable law or agreed to in writing, software 8%% distributed under the License is distributed on an "AS IS" BASIS, 9%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10%% See the License for the specific language governing permissions and 11%% limitations under the License. 12%% 13%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. 14%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings 15%% AB. All Rights Reserved.'' 16%% 17%% $Id: sys_pre_attributes.erl,v 1.1 2008/12/17 09:53:42 mikpe Exp $ 18%% 19%% Purpose : Transform Erlang compiler attributes 20 21-module(sys_pre_attributes). 22 23-export([parse_transform/2]). 24 25-define(OPTION_TAG, attributes). 26 27-record(state, {forms, 28 pre_ops = [], 29 post_ops = [], 30 options}). 31 32%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 33%% Inserts, deletes and replaces Erlang compiler attributes. 34%% 35%% Valid options are: 36%% 37%% {attribute, insert, AttrName, NewAttrVal} 38%% {attribute, replace, AttrName, NewAttrVal} % replace first occurrence 39%% {attribute, delete, AttrName} 40%% 41%% The transformation is performed in two passes: 42%% 43%% pre_transform 44%% ------------- 45%% Searches for attributes in the list of Forms in order to 46%% delete or replace them. 'delete' will delete all occurrences 47%% of attributes with the given name. 'replace' will replace the 48%% first occurrence of the attribute. This pass is will only be 49%% performed if there are replace or delete operations stated 50%% as options. 51%% 52%% post_transform 53%% ------------- 54%% Looks up the module attribute and inserts the new attributes 55%% directly after. This pass will only be performed if there are 56%% any attributes left to be inserted after pre_transform. The left 57%% overs will be those replace operations that not has been performed 58%% due to that the pre_transform pass did not find the attribute plus 59%% all insert operations. 60 61parse_transform(Forms, Options) -> 62 S = #state{forms = Forms, options = Options}, 63 S2 = init_transform(S), 64 report_verbose("Pre options: ~p~n", [S2#state.pre_ops], S2), 65 report_verbose("Post options: ~p~n", [S2#state.post_ops], S2), 66 S3 = pre_transform(S2), 67 S4 = post_transform(S3), 68 S4#state.forms. 69 70%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 71%% Computes the lists of pre_ops and post_ops that are 72%% used in the real transformation. 73init_transform(S) -> 74 case S#state.options of 75 Options when list(Options) -> 76 init_transform(Options, S); 77 Option -> 78 init_transform([Option], S) 79 end. 80 81init_transform([{attribute, insert, Name, Val} | Tail], S) -> 82 Op = {insert, Name, Val}, 83 PostOps = [Op | S#state.post_ops], 84 init_transform(Tail, S#state{post_ops = PostOps}); 85init_transform([{attribute, replace, Name, Val} | Tail], S) -> 86 Op = {replace, Name, Val}, 87 PreOps = [Op | S#state.pre_ops], 88 PostOps = [Op | S#state.post_ops], 89 init_transform(Tail, S#state{pre_ops = PreOps, post_ops = PostOps}); 90init_transform([{attribute, delete, Name} | Tail], S) -> 91 Op = {delete, Name}, 92 PreOps = [Op | S#state.pre_ops], 93 init_transform(Tail, S#state{pre_ops = PreOps}); 94init_transform([], S) -> 95 S; 96init_transform([_ | T], S) -> 97 init_transform(T, S); 98init_transform(BadOpt, S) -> 99 report_error("Illegal option (ignored): ~p~n", [BadOpt], S), 100 S. 101 102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 103%% Handle delete and perhaps replace 104 105pre_transform(S) when S#state.pre_ops == [] -> 106 S; 107pre_transform(S) -> 108 pre_transform(S#state.forms, [], S). 109 110pre_transform([H | T], Acc, S) -> 111 case H of 112 {attribute, Line, Name, Val} -> 113 case lists:keysearch(Name, 2, S#state.pre_ops) of 114 false -> 115 pre_transform(T, [H | Acc], S); 116 117 {value, {replace, Name, NewVal}} -> 118 report_warning("Replace attribute ~p: ~p -> ~p~n", 119 [Name, Val, NewVal], 120 S), 121 New = {attribute, Line, Name, NewVal}, 122 Pre = lists:keydelete(Name, 2, S#state.pre_ops), 123 Post = lists:keydelete(Name, 2, S#state.post_ops), 124 S2 = S#state{pre_ops = Pre, post_ops = Post}, 125 if 126 Pre == [] -> 127 %% No need to search the rest of the Forms 128 Forms = lists:reverse(Acc, [New | T]), 129 S2#state{forms = Forms}; 130 true -> 131 pre_transform(T, [New | Acc], S2) 132 end; 133 134 {value, {delete, Name}} -> 135 report_warning("Delete attribute ~p: ~p~n", 136 [Name, Val], 137 S), 138 pre_transform(T, Acc, S) 139 end; 140 _Any -> 141 pre_transform(T, [H | Acc], S) 142 end; 143pre_transform([], Acc, S) -> 144 S#state{forms = lists:reverse(Acc)}. 145 146%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 147%% Handle insert and perhaps replace 148 149post_transform(S) when S#state.post_ops == [] -> 150 S; 151post_transform(S) -> 152 post_transform(S#state.forms, [], S). 153 154post_transform([H | T], Acc, S) -> 155 case H of 156 {attribute, Line, module, Val} -> 157 Acc2 = lists:reverse([{attribute, Line, module, Val} | Acc]), 158 Forms = Acc2 ++ attrs(S#state.post_ops, Line, S) ++ T, 159 S#state{forms = Forms, post_ops = []}; 160 _Any -> 161 post_transform(T, [H | Acc], S) 162 end; 163post_transform([], Acc, S) -> 164 S#state{forms = lists:reverse(Acc)}. 165 166attrs([{replace, Name, NewVal} | T], Line, S) -> 167 report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S), 168 [{attribute, Line, Name, NewVal} | attrs(T, Line, S)]; 169attrs([{insert, Name, NewVal} | T], Line, S) -> 170 report_verbose("Insert attribute ~p: ~p~n", [Name, NewVal], S), 171 [{attribute, Line, Name, NewVal} | attrs(T, Line, S)]; 172attrs([], _, _) -> 173 []. 174 175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 176%% Report functions. 177%% 178%% Errors messages are controlled with the 'report_errors' compiler option 179%% Warning messages are controlled with the 'report_warnings' compiler option 180%% Verbose messages are controlled with the 'verbose' compiler option 181 182report_error(Format, Args, S) -> 183 case is_error(S) of 184 true -> 185 io:format("~p: * ERROR * " ++ Format, [?MODULE | Args]); 186 false -> 187 ok 188 end. 189 190report_warning(Format, Args, S) -> 191 case is_warning(S) of 192 true -> 193 io:format("~p: * WARNING * " ++ Format, [?MODULE | Args]); 194 false -> 195 ok 196 end. 197 198report_verbose(Format, Args, S) -> 199 case is_verbose(S) of 200 true -> 201 io:format("~p: " ++ Format, [?MODULE | Args]); 202 false -> 203 ok 204 end. 205 206is_error(S) -> 207 lists:member(report_errors, S#state.options) or is_verbose(S). 208 209is_warning(S) -> 210 lists:member(report_warnings, S#state.options) or is_verbose(S). 211 212is_verbose(S) -> 213 lists:member(verbose, S#state.options). 214