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