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