%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1998-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(ic_codegen).
-include_lib("ic/src/ic.hrl").
-include_lib("ic/src/icforms.hrl").
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([emit/2, emit/3]).
-export([emit_c_enc_rpt/4, emit_c_dec_rpt/4]).
-export([comment/2, comment/3, comment/4, comment_inlined/5, comment_prefixed/4]).
-export([mcomment/2, mcomment/3, mcomment_inlined/5, mcomment_prefixed/3]).
-export([mcomment_light/2, mcomment_light/3, mcomment_light_inlined/5, mcomment_light_prefixed/3]).
-export([nl/1, export/2]).
-export([record/5]).
-export([emit_stub_head/4, emit_hrl_head/4, emit_hrl_foot/2]).
%%-----------------------------------------------------------------
%% Internal exports
%%-----------------------------------------------------------------
%%-----------------------------------------------------------------
%% External functions
%%-----------------------------------------------------------------
%%--------------------------------------------------------------------
%% Emit output as a formatted string, (old emit)
%%--------------------------------------------------------------------
emit(nil, _) -> ok;
emit(Fd, Str) ->
file:write(Fd, Str).
emit(nil, _, _) -> ok;
emit(Fd, Fmt, Args) ->
file:write(Fd, io_lib:format(Fmt, Args)).
emit_c_enc_rpt(Fd, Prefix, Fmt, Args) ->
emit(Fd, Prefix ++ "OE_RPT_ERR(\"Encode error: " ++ Fmt ++ "\");\n", Args).
emit_c_dec_rpt(Fd, Prefix, Fmt, Args) ->
emit(Fd, Prefix ++ "OE_RPT_ERR(\"Decode error: " ++ Fmt ++ "\");\n", Args).
%%--------------------------------------------------------------------
%% Emit comments
%%--------------------------------------------------------------------
comment(Fd, C) ->
comment_prefixed(Fd, C, [], "%%").
comment(Fd, C, A) ->
comment_prefixed(Fd, C, A, "%%").
comment(Fd, C, A, c) ->
comment_inlined(Fd, C, A, "/*", "*/");
comment(Fd, C, A, erl) ->
comment_prefixed(Fd, C, A, "%%");
comment(Fd, C, A, java) ->
comment_prefixed(Fd, C, A, "//");
%% Should be removed after a check if it's used !!!!! (LTH)
comment(Fd, C, A, CommentSequence) when is_list(CommentSequence) ->
comment_prefixed(Fd, C, A, CommentSequence).
comment_inlined(Fd, C, A, Start, End) ->
emit(Fd, Start ++ " " ++ C ++ " " ++ End ++"\n", A).
comment_prefixed(Fd, C, A, Prefix) ->
emit(Fd, Prefix ++ " " ++ C ++ "\n", A).
%%--------------------------------------------------------------------
%% Emit multiline comments with nice delimiters
%%--------------------------------------------------------------------
mcomment(Fd, List) ->
mcomment_prefixed(Fd, List, "%%").
mcomment(Fd, List, c) ->
mcomment_inlined(Fd, List, "/*", "*/", " *");
mcomment(Fd, List, erl) ->
mcomment_prefixed(Fd, List, "%%");
mcomment(Fd, List, java) ->
mcomment_prefixed(Fd, List, "//").
mcomment_inlined(Fd, List, Start, End, Intermediate) ->
emit(Fd, Start ++
"------------------------------------------------------------\n"),
emit(Fd, Intermediate ++ "\n"),
lists:foreach(fun(C) -> comment(Fd, C, [], Intermediate) end, List),
emit(Fd, Intermediate ++ "\n"),
emit(Fd, Intermediate ++
"------------------------------------------------------------" ++ End ++ "\n"),
ok.
mcomment_prefixed(Fd, List, Prefix) ->
emit(Fd, Prefix ++
"------------------------------------------------------------\n"),
emit(Fd, Prefix ++ "\n"),
lists:foreach(fun(C) -> comment(Fd, C, [], Prefix) end, List),
emit(Fd, Prefix ++ "\n"),
emit(Fd, Prefix ++
"------------------------------------------------------------\n"),
ok.
%%--------------------------------------------------------------------
%% Emit multiline comments with nice delimiters as above but a
%% little lighter
%%--------------------------------------------------------------------
mcomment_light(Fd, List) ->
mcomment_light_prefixed(Fd, List, "%%").
mcomment_light(Fd, List, c) ->
mcomment_light_inlined(Fd, List, "/*", " */", " *");
mcomment_light(Fd, List, erl) ->
mcomment_light_prefixed(Fd, List, "%%");
mcomment_light(Fd, List, java) ->
mcomment_light_prefixed(Fd, List, "//");
%% Should be removed after a check if it's used !!!!! (LTH)
mcomment_light(Fd, List, Prefix) when is_list(Prefix) ->
mcomment_light_prefixed(Fd, List, Prefix).
mcomment_light_inlined(Fd, List, Start, End, Intermediate) ->
emit(Fd, "\n" ++ Start ++ "\n"),
lists:foreach(fun(C) -> comment(Fd, C, [], Intermediate) end, List),
emit(Fd, End ++ "\n"),
ok.
mcomment_light_prefixed(Fd, List, Prefix) ->
emit(Fd, Prefix),
lists:foreach(fun(C) -> comment(Fd, C, [], Prefix) end, List),
emit(Fd, Prefix ++ "\n"),
ok.
%%--------------------------------------------------------------------
%% New line
%%--------------------------------------------------------------------
nl(Fd) ->
emit(Fd, "\n").
%%--------------------------------------------------------------------
-define(IFRIDFIELD(G), ic_util:mk_name(G, "ID")).
%%--------------------------------------------------------------------
%% Emit record definitions for erlang
%%--------------------------------------------------------------------
record(G, X, Name, _IFRID, Recs) when is_record(X, struct) ->
F = ic_genobj:hrlfiled(G),
emit(F, "-record(~p, {~p", [ic_util:to_atom(Name),hd(Recs)]),
lists:foreach(fun(Y) -> emit(F, ", ~p", [Y]) end, tl(Recs)),
emit(F, "}).\n");
record(G, X, Name, _IFRID, _Recs) when is_record(X, union) ->
F = ic_genobj:hrlfiled(G),
emit(F, "-record(~p, {label, value}).\n",[ic_util:to_atom(Name)]);
record(G, _X, Name, IFRID, Recs) when length(Recs) > 3 ->
F = ic_genobj:hrlfiled(G),
emit(F, "-record(~p,~n {~p=~p",
[ic_util:to_atom(Name), ic_util:to_atom(?IFRIDFIELD(G)), IFRID]),
rec2(F, "", ", ", Recs),
emit(F, "}).\n");
record(G, _X, Name, IFRID, Recs) ->
F = ic_genobj:hrlfiled(G),
emit(F, "-record(~p, {~p=~p", [ic_util:to_atom(Name),
ic_util:to_atom(?IFRIDFIELD(G)),
IFRID]),
lists:foreach(fun(Y) -> emit(F, ", ~p", [Y]) end, Recs),
emit(F, "}).\n").
rec2(F, Align, Delim, [M1 , M2, M3 | Ms]) ->
emit(F, "~s~s~p, ~p, ~p", [Delim, Align, M1, M2, M3]),
rec2(F, " ", ",\n", Ms);
rec2(F, Align, Delim, [M1 , M2]) ->
emit(F, "~s~s~p, ~p", [Delim, Align, M1, M2]);
rec2(F, Align, Delim, [M]) ->
emit(F, "~s~s~p", [Delim, Align, M]);
rec2(_F, _Align, _Delim, []) ->
ok.
%%--------------------------------------------------------------------
%% Emit export lists for erlang
%%--------------------------------------------------------------------
export(F, [E1, E2, E3 | Exports]) ->
emit(F, "-export([~s]).\n", [exp_list([E1, E2, E3])]),
export(F, Exports);
export(_F, []) -> ok;
export(F, Exports) ->
emit(F, "-export([~s]).\n", [exp_list(Exports)]).
exp_list([E1 | L]) ->
exp_to_string(E1) ++
lists:map(fun(E) -> ", " ++ exp_to_string(E) end, L).
exp_to_string({F,N}) -> io_lib:format("~p/~p", [ic_util:to_atom(F), N]).
%%--------------------------------------------------------------------
%% Emit Stub file header
%%--------------------------------------------------------------------
emit_stub_head(_G, ignore, _Name, _) -> ignore;
emit_stub_head(G, F1, Name, erlang) ->
comment(F1, " coding: latin-1", []),
mcomment(F1, stub_header(G, Name)),
nl(F1),
emit(F1, "-module(~p).\n", [list_to_atom(Name)]),
emit(F1, "-ic_compiled(~p).\n", [compiler_vsn(?COMPILERVSN)]),
emit(F1, "\n\n"), F1;
emit_stub_head(G, F1, Name, erlang_template) ->
comment(F1, " coding: latin-1", []),
ic_erl_template:emit_header(G, F1, Name),
F1;
emit_stub_head(_G, F1, _Name, erlang_template_no_gen) ->
F1;
emit_stub_head(G, F1, Name, c) ->
mcomment(F1, stub_header(G, Name), c),
emit(F1, "int ic_compiled_~s_~s;\n", [compiler_vsn(?COMPILERVSN), Name]),
emit(F1, "\n\n"), F1;
emit_stub_head(G, F1, Name, c_server) ->
CSName = [Name, "__s"],
mcomment(F1, stub_header(G, CSName), c),
emit(F1, "int ic_compiled_~s_~s;\n", [compiler_vsn(?COMPILERVSN), CSName]),
emit(F1, "\n\n"), F1;
emit_stub_head(G, F1, Name, java) ->
mcomment(F1, stub_header(G, Name), java),
emit(F1, "\n\n"), F1.
stub_header(G, Name) ->
["Implementation stub file",
"",
io_lib:format("Target: ~ts", [Name]),
io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]),
io_lib:format("IC vsn: ~s", [?COMPILERVSN]),
"",
"This file is automatically generated. DO NOT EDIT IT."].
compiler_vsn(Vsn) ->
lists:map(fun($.) -> $_;
(C) -> C
end, Vsn).
%%--------------------------------------------------------------------
%% Emit include file header
%%--------------------------------------------------------------------
%% Name is Fully scoped (undescore) name of interface or module
emit_hrl_head(_G, ignore, _Name, _) -> ignore;
emit_hrl_head(G, Fd, Name, erlang) ->
comment(Fd, " coding: latin-1", []),
mcomment(Fd, ["Erlang header file" |
hrl_header(G, Name)]),
nl(Fd),
nl(Fd),
IfdefName = ic_util:to_uppercase(Name++"_HRL"),
emit(Fd, "-ifndef(~s).~n", [IfdefName]),
emit(Fd, "-define(~s, true).~n", [IfdefName]),
nl(Fd),
nl(Fd),
Fd;
emit_hrl_head(G, Fd, Name, c) ->
mcomment(Fd, ["C header file" |
hrl_header(G, Name)], c),
nl(Fd),
nl(Fd),
IfdefName = ic_util:to_uppercase(Name++"_H"),
emit(Fd, "#ifndef ~s~n", [IfdefName]),
emit(Fd, "#define ~s ~n", [IfdefName]),
nl(Fd),
nl(Fd),
Fd;
emit_hrl_head(G, Fd, Name, c_server) ->
mcomment(Fd, ["C header file" |
hrl_header(G, [Name, "__s"])], c),
nl(Fd),
nl(Fd),
IfdefName = ic_util:to_uppercase(Name++"__S_H"),
emit(Fd, "#ifndef ~s~n", [IfdefName]),
emit(Fd, "#define ~s ~n", [IfdefName]),
nl(Fd),
nl(Fd),
Fd.
hrl_header(G, Name) ->
["",
io_lib:format("Target: ~ts", [Name]),
io_lib:format("Source: ~ts", [ic_genobj:idlfile(G)]),
io_lib:format("IC vsn: ~s", [?COMPILERVSN]),
"",
"This file is automatically generated. DO NOT EDIT IT."].
%%--------------------------------------------------------------------
%% Emit include file footer
%%--------------------------------------------------------------------
emit_hrl_foot(_G, erlang_template) ->
ok;
emit_hrl_foot(_G, erlang_template_no_gen) ->
ok;
emit_hrl_foot(G, erlang) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
Fd = ic_genobj:hrlfiled(G),
nl(Fd),
nl(Fd),
emit(Fd, "-endif.\n"),
nl(Fd),
nl(Fd),
Fd;
false ->
ok
end;
emit_hrl_foot(G, erlang_no_stub) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
Fd = ic_genobj:hrlfiled(G),
nl(Fd),
nl(Fd),
emit(Fd, "-endif.\n"),
nl(Fd),
nl(Fd),
Fd;
false ->
ok
end;
emit_hrl_foot(G, c) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
Fd = ic_genobj:hrlfiled(G),
nl(Fd),
nl(Fd),
emit(Fd, "#ifdef __cplusplus\n"),
emit(Fd, "}\n"),
emit(Fd, "#endif\n"),
nl(Fd),
emit(Fd, "#endif\n"),
nl(Fd),
nl(Fd),
Fd;
false ->
ok
end;
emit_hrl_foot(G, c_server) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
Fd = ic_genobj:hrlfiled(G),
nl(Fd),
nl(Fd),
emit(Fd, "#ifdef __cplusplus\n"),
emit(Fd, "}\n"),
emit(Fd, "#endif\n"),
nl(Fd),
emit(Fd, "#endif\n"),
nl(Fd),
nl(Fd),
Fd;
false ->
ok
end;
emit_hrl_foot(G, c_no_stub) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
Fd = ic_genobj:hrlfiled(G),
nl(Fd),
nl(Fd),
emit(Fd, "#ifdef __cplusplus\n"),
emit(Fd, "}\n"),
emit(Fd, "#endif\n"),
nl(Fd),
emit(Fd, "#endif\n"),
nl(Fd),
nl(Fd),
Fd;
false ->
ok
end;
emit_hrl_foot(G, c_server_no_stub) ->
case ic_genobj:is_hrlfile_open(G) of
true ->
Fd = ic_genobj:hrlfiled(G),
nl(Fd),
nl(Fd),
emit(Fd, "#ifdef __cplusplus\n"),
emit(Fd, "}\n"),
emit(Fd, "#endif\n"),
nl(Fd),
emit(Fd, "#endif\n"),
nl(Fd),
nl(Fd),
Fd;
false ->
ok
end.
%%-----------------------------------------------------------------
%% Internal functions
%%-----------------------------------------------------------------