1%% Copyright (c) 2008-2016 Robert Virding
2%%
3%% Licensed under the Apache License, Version 2.0 (the "License");
4%% you may not use this file except in compliance with the License.
5%% You may obtain a copy of the License at
6%%
7%%     http://www.apache.org/licenses/LICENSE-2.0
8%%
9%% Unless required by applicable law or agreed to in writing, software
10%% distributed under the License is distributed on an "AS IS" BASIS,
11%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12%% See the License for the specific language governing permissions and
13%% limitations under the License.
14
15%%% File    : lfe_gen.erl
16%%% Author  : Robert Virding
17%%% Purpose : Lisp Flavoured Erlang dynamic code generator.
18
19%% We have kept the old tuple based formats for exports and imports
20%% for backwards compatibility but they are not documented.
21
22-module(lfe_gen).
23
24-export([new_module/1,add_exports/2,add_imports/2,add_attribute/2,add_form/2,
25         build_mod/1,compile_mod/1]).
26
27-import(lists, [map/2,foldl/3,mapfoldl/3]).
28
29-record(gen, {name,exps=[],imps=[],attrs=[],forms=[]}).
30
31%% new_module(Name) -> Module.
32%% add_exports([{Name,Arity}], Module) -> Module.
33%% add_imports({from,Mod,[{Name,Arity}]}, Module) -> Module.
34%% add_attribute(Attr, Module) -> Module.
35%% add_form(Form, Module) -> Module.
36%% build_mod(Module) -> iolist().
37%% compile_mod(Mod) -> {ok,Name,Bin,Warns} | {error,Errors,Warns}.
38%%  The incremental interface to compiling a module.
39
40new_module(Name) ->
41    #gen{name=Name,forms=[]}.
42
43add_exports(Exps, Mod) ->
44    Es0 = Mod#gen.exps,
45    Es1 = foldl(fun ([N,Ar], Es) when is_atom(N), is_integer(Ar) ->
46                        ordsets:add_element([N,Ar], Es);
47                    ({N,Ar}, Es) when is_atom(N), is_integer(Ar) ->
48                        ordsets:add_element([N,Ar], Es)
49                end, Es0, Exps),
50    Mod#gen{exps=Es1}.
51
52add_imports([from,M|Is], Mod) ->
53    collect_from_imports(M, Is, Mod);
54add_imports([rename,M|Is], Mod) ->
55    collect_rename_imports(M, Is, Mod);
56%% The older now deprecated forms.
57add_imports({from,M,Is}, Mod) ->
58    collect_from_imports(M, Is, Mod);
59add_imports({rename,M,Is}, Mod) ->
60    collect_rename_imports(M, Is, Mod).
61
62collect_from_imports(M, Is, #gen{imps=Imps0}=Mod) ->
63    From = fun ([F,A], Imps) -> store_import(F, A, F, Imps);
64               ({F,A}, Imps) -> store_import(F, A, F, Imps)
65           end,
66    Imps1 = collect_imp(From, M, Imps0, Is),
67    Mod#gen{imps=Imps1}.
68
69collect_rename_imports(M, Is, #gen{imps=Imps0}=Mod) ->
70    Rename = fun ([[F,A],R], Imps) -> store_import(F, A, R, Imps);
71                 ({{F,A},R}, Imps) -> store_import(F, A, R, Imps)
72             end,
73    Imps1 = collect_imp(Rename, M, Imps0, Is),
74    Mod#gen{imps=Imps1}.
75
76store_import(F, A, R, Imps) ->
77    orddict:store([F,A], R, Imps).
78
79collect_imp(Fun, Mod, Imps, Is) ->
80    Mimps0 = safe_fetch(Mod, Imps, []),
81    Mimps1 = foldl(Fun, Mimps0, Is),
82    orddict:store(Mod, Mimps1, Imps).
83
84add_attribute(Attr, #gen{attrs=As}=Mod) ->
85    Mod#gen{attrs=As ++ [Attr]}.
86
87add_form(Form, #gen{forms=Fs}=Mod) ->
88    Mod#gen{forms=Fs ++ [Form]}.
89
90compile_mod(Mod) ->
91    Fs = build_mod(Mod),
92    case lfe_comp:forms(Fs, [return]) of
93        {ok,[{ok,Name,Bin,Mws}],Ws} -> {ok,Name,Bin,Ws ++ Mws};
94        {error,[{error,Mes,Mws}],Es,Ws} -> {error,Es ++ Mes,Ws ++ Mws}
95    end.
96
97build_mod(Mod) ->
98    [build_def(Mod)|Mod#gen.forms].
99
100%% build_def(ModDef) -> form().
101
102build_def(Mod) ->
103    Exps = Mod#gen.exps,                        %This is a set of [F,A]
104    %% We know these are orddicts.
105    ImpFun = fun ({M,Is}) ->
106                     [rename,M|map(fun ({F,R}) -> [F,R] end, Is)]
107             end,
108    Imps = map(ImpFun, Mod#gen.imps),
109    [defmodule,Mod#gen.name,
110     [export|Exps],
111     [import|Imps]|
112     Mod#gen.attrs].
113
114%% safe_fetch(Key, Dict, Default) -> Value.
115
116safe_fetch(Key, D, Def) ->
117    case orddict:find(Key, D) of
118        {ok,Val} -> Val;
119        error -> Def
120    end.
121