1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21-module(ic_plainbe).
22
23
24-export([do_gen/3]).
25%%------------------------------------------------------------
26%%
27%% Internal stuff
28%%
29%%------------------------------------------------------------
30
31-import(ic_util, [mk_var/1, mk_oe_name/2, to_atom/1, to_list/1]).
32-import(ic_forms, [get_id/1, get_id2/1, get_body/1]).
33-import(ic_codegen, [emit/3, nl/1]).
34
35-import(lists, [foreach/2, map/2]).
36
37-include("icforms.hrl").
38-include("ic.hrl").
39
40%%------------------------------------------------------------
41%%
42%% Generate the client side Erlang stubs.
43%%
44%% Each module is generated to a separate file.
45%%
46%% Export declarations for all interface functions must be
47%% generated. Each function then needs to generate a function head and
48%% a body. IDL parameters must be converted into Erlang parameters
49%% (variables, capitalised) and a type signature list must be
50%% generated (for later encode/decode).
51%%
52%%------------------------------------------------------------
53
54
55do_gen(G, File, Form) ->
56    G2 = ic_file:filename_push(G, [], mk_oe_name(G,
57					       ic_file:remove_ext(to_list(File))),
58			     erlang),
59    gen_head(G2, [], Form),
60    exportDependency(G2),
61    gen(G2, [], Form),
62    genDependency(G2),
63    ic_file:filename_pop(G2, erlang),
64    ok.
65
66
67gen(G, N, [X|Xs]) when is_record(X, preproc) ->
68    NewG = ic:handle_preproc(G, N, X#preproc.cat, X),
69    gen(NewG, N, Xs);
70
71gen(G, N, [X|Xs]) when is_record(X, module) ->
72    CD = ic_code:codeDirective(G,X),
73    G2 = ic_file:filename_push(G, N, X, CD),
74    N2 = [get_id2(X) | N],
75    gen_head(G2, N2, X),
76    gen(G2, N2, get_body(X)),
77    G3 = ic_file:filename_pop(G2, CD),
78    gen(G3, N, Xs);
79
80gen(G, N, [X|Xs]) when is_record(X, interface) ->
81    %% Add inheritence data to pragmatab
82    ic_pragma:add_inh_data(G,N,X),
83    G2 = ic_file:filename_push(G, N, X, erlang),
84    N2 = [get_id2(X) | N],
85    gen_head(G2, N2, X),
86    gen(G2, N2, get_body(X)),
87    foreach(fun({_Name, Body}) -> gen(G2, N2, Body) end,
88	    X#interface.inherit_body),
89    G3 = ic_file:filename_pop(G2, erlang),
90    gen(G3, N, Xs);
91
92gen(G, N, [X|Xs]) when is_record(X, const) ->
93%    N2 = [get_id2(X) | N],
94    emit_constant_func(G, X#const.id, X#const.val),
95    gen(G, N, Xs); %% N or N2?
96
97gen(G, N, [X|Xs]) when is_record(X, op) ->
98    {Name, ArgNames, TypeList, OutArgs} = extract_info(G, N, X),
99    emit_func(G, N, X, Name, ArgNames, TypeList, OutArgs),
100    gen(G, N, Xs);
101
102
103gen(G, N, [X|Xs]) when is_record(X, attr) ->
104    emit_attr(G, N, X, fun emit_func/7),
105    gen(G, N, Xs);
106
107gen(G, N, [X|Xs]) when is_record(X, except) ->
108    icstruct:except_gen(G, N, X, erlang),
109    gen(G, N, Xs);
110
111gen(G, N, [X|Xs]) ->
112    case may_contain_structs(X) of
113	true -> icstruct:struct_gen(G, N, X, erlang);
114	false -> ok
115    end,
116    gen(G, N, Xs);
117
118gen(_G, _N, []) -> ok.
119
120
121may_contain_structs(X) when is_record(X, typedef) -> true;
122may_contain_structs(X) when is_record(X, struct) -> true;
123may_contain_structs(X) when is_record(X, union) -> true;
124may_contain_structs(_X) -> false.
125
126
127%%------------------------------------------------------------
128%%
129%% Export stuff
130%%
131%%	Gathering of all names that should be exported from a stub
132%%	file.
133%%
134
135
136gen_head_special(G, N, X) when is_record(X, interface) ->
137    Fd = ic_genobj:stubfiled(G),
138
139    foreach(fun({Name, Body}) ->
140		    ic_codegen:comment(Fd, "Exports from ~p",
141				  [ic_util:to_colon(Name)]),
142		    ic_codegen:export(Fd, exp_top(G, N, Body, [])),
143		    nl(Fd)
144	    end, X#interface.inherit_body),
145    Fd;
146gen_head_special(_G, _N, _X) -> ok.
147
148
149
150%% Shall generate all export declarations
151gen_head(G, N, X) ->
152    case ic_genobj:is_stubfile_open(G) of
153	true ->
154	    F = ic_genobj:stubfiled(G),
155	    ic_codegen:comment(F, "Interface functions"),
156	    ic_codegen:export(F, exp_top(G, N, X, [])),
157	    nl(F),
158	    gen_head_special(G, N, X);
159	false -> ok
160    end.
161
162exp_top(_G, _N, X, Acc)  when element(1, X) == preproc ->
163    Acc;
164exp_top(G, N, L, Acc)  when is_list(L) ->
165    exp_list(G, N, L, Acc);
166exp_top(G, N, M, Acc)  when is_record(M, module) ->
167    exp_list(G, N, get_body(M), Acc);
168exp_top(G, N, I, Acc)  when is_record(I, interface) ->
169    exp_list(G, N, get_body(I), Acc);
170exp_top(G, N, X, Acc) ->
171    exp3(G, N, X, Acc).
172
173exp3(_G, _N, C, Acc)  when is_record(C, const) ->
174    [{get_id(C#const.id), 0} | Acc];
175
176exp3(_G, _N, Op, Acc)  when is_record(Op, op) ->
177    FuncName = get_id(Op#op.id),
178    Arity = length(ic:filter_params([in, inout], Op#op.params)),
179    [{FuncName, Arity} | Acc];
180
181exp3(_G, _N, A, Acc)  when is_record(A, attr) ->
182    lists:foldr(fun(Id, Acc2) ->
183			{Get, Set} = mk_attr_func_names([], get_id(Id)),
184			case A#attr.readonly of
185			    {readonly, _} -> [{Get, 1} | Acc2];
186			    _ ->             [{Get, 1}, {Set, 2} | Acc2]
187			end end, Acc, ic_forms:get_idlist(A));
188
189exp3(_G, _N, _X, Acc) -> Acc.
190
191exp_list(G, N, L, OrigAcc) ->
192    lists:foldr(fun(X, Acc) -> exp3(G, N, X, Acc) end, OrigAcc, L).
193
194
195
196
197%%------------------------------------------------------------
198%%
199%% Emit stuff
200%%
201%%	Low level generation primitives
202%%
203
204
205emit_func(G, _N, X, Name, ArgNames, _TypeList, OutArgs) ->
206    case ic_genobj:is_stubfile_open(G) of
207	false -> ok;
208	true ->
209	    Fd = ic_genobj:stubfiled(G),
210	    OpName = list_to_atom(Name),
211	    ArgList = mk_list(ArgNames),
212	    emit_op_comment(G, Fd, X, OpName, ArgNames, OutArgs),
213	    emit(Fd, "~p(~s) ->\n", [OpName,ArgList]),
214	    emit(Fd, "    ~p:~p(~s).\n\n", [to_atom(ic_genobj:impl(G)), OpName, ArgList])
215    end.
216
217emit_attr(G, N, X, F) ->
218    XX = #id_of{type=X},
219    {GetType, SetType} = mk_attr_func_types(N, X),
220    lists:foreach(fun(Id) ->
221			  X2 = XX#id_of{id=Id},
222			  {Get, Set} = mk_attr_func_names(N, get_id(Id)),
223			  F(G, N, X2, Get, [], GetType, []),
224			  case X#attr.readonly of
225			      {readonly, _} -> ok;
226			      _ ->
227				  F(G, N, X2, Set, [ic_util:mk_name(G, "Value")],
228				    SetType, [])
229			  end end, ic_forms:get_idlist(X)).
230
231emit_constant_func(G, Id, Val) ->
232    case ic_genobj:is_stubfile_open(G) of
233	false -> ok;
234	true ->
235	    Fd = ic_genobj:stubfiled(G),
236	    N = list_to_atom(get_id(Id)),
237	    emit_const_comment(G, Fd, Id, N),
238	    emit(Fd, "~p() -> ~p.\n\n", [N, Val])
239    end.
240
241
242emit_const_comment(_G, F, _X, Name) ->
243    ic_codegen:mcomment_light(F,
244			 [io_lib:format("Constant: ~p", [Name])]).
245
246
247emit_op_comment(G, F, X, Name, InP, OutP) ->
248    ic_codegen:mcomment_light(F,
249			 [io_lib:format("~s: ~p", [get_title(X), Name]),
250			  "",
251			  get_returns(G, X, InP, OutP) |
252			  get_raises(X)]).
253
254get_title(X) when is_record(X, attr) -> "Attribute Operation";
255get_title(_X) -> "Operation".
256
257get_raises(X) when is_record(X, op) ->
258    if  X#op.raises == [] -> [];
259	true ->
260	    ["  Raises:  " ++
261	     mk_list(lists:map(fun(E) -> ic_util:to_colon(E) end, X#op.raises))]
262    end;
263get_raises(_X) -> [].
264
265get_returns(_G, _X, _InP, []) ->
266    "  Returns: RetVal";
267get_returns(G, _X, _InP, OutP) ->
268    "  Returns: "++mk_list(["RetVal" | mk_erl_vars(G, OutP)]).
269
270
271
272
273%%------------------------------------------------------------
274%%
275%% Utilities
276%%
277%% Convenient little go-get functions
278%%
279%%------------------------------------------------------------
280
281%% The automaticly generated get and set operation names for an
282%% attribute.
283mk_attr_func_names(_Scope, Name) ->
284    {"_get_" ++ Name, "_set_" ++ Name}.
285
286%% Returns TK of the Get and Set attribute functions.
287mk_attr_func_types(_N, X) ->
288    TK = ic_forms:get_tk(X),
289    {{TK, [], []}, {tk_void, [TK], []}}.
290
291
292
293%%------------------------------------------------------------
294%%
295%% Generation utilities and common stuff
296%%
297%% Convenient stuff for generation
298%%
299%%------------------------------------------------------------
300
301
302%% Input is a list of parameters (in parse form) and output is a list
303%% of capitalised variable names. mk_var is in icgen
304mk_erl_vars(_G, Params) ->
305    map(fun(P) -> mk_var(get_id(P#param.id)) end, Params).
306
307
308%% mk_list produces a nice comma separated string of variable names
309mk_list([]) -> [];
310mk_list([Arg | Args]) ->
311    Arg ++ mk_list2(Args).
312mk_list2([Arg | Args]) ->
313    ", " ++ Arg ++ mk_list2(Args);
314mk_list2([]) -> [].
315
316
317%%------------------------------------------------------------
318%%
319%% Parser utilities
320%%
321%% Called from the yecc parser. Expands the identifier list of an
322%% attribute so that the attribute generator never has to handle
323%% lists.
324%%
325%%------------------------------------------------------------
326
327
328
329
330%% Export code produce for dependency function
331exportDependency(G) ->
332    Fd = ic_genobj:stubfiled(G),
333    ic_codegen:export(Fd, [{oe_dependency, 0}]),
334    nl(Fd).
335
336%% Code produce for dependency function
337genDependency(G) ->
338    Fd = ic_genobj:stubfiled(G),
339    nl(Fd),nl(Fd),
340    ic_codegen:comment(Fd, "Idl file dependency list function"),
341    emit(Fd, "oe_dependency() ->\n", []),
342    emit(Fd, "    ~p.\n\n", [ic_pragma:get_dependencies(G)]).
343
344
345
346
347extract_info(G, _N, X) when is_record(X, op) ->
348    Name	= get_id2(X),
349    InArgs	= ic:filter_params([in,inout], X#op.params),
350    OutArgs	= ic:filter_params([out,inout], X#op.params),
351    ArgNames	= mk_erl_vars(G, InArgs),
352    TypeList	= {ic_forms:get_tk(X),
353		   map(fun(Y) -> ic_forms:get_tk(Y) end, InArgs),
354		   map(fun(Y) -> ic_forms:get_tk(Y) end, OutArgs)
355		  },
356    {Name, ArgNames, TypeList, OutArgs}.
357