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