aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src/compiler/diameter_codegen.erl
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2013-09-25 11:28:19 +0200
committerAnders Svensson <[email protected]>2013-12-01 16:38:51 +0100
commit8e819a7960a256b6c3b7bf5856c3f81b8df9ca7e (patch)
tree4992f7815af4e08e48351c8d4ee4293f03ba8197 /lib/diameter/src/compiler/diameter_codegen.erl
parentec91114dc4f14d787eeb07d7332b8803298b575c (diff)
downloadotp-8e819a7960a256b6c3b7bf5856c3f81b8df9ca7e.tar.gz
otp-8e819a7960a256b6c3b7bf5856c3f81b8df9ca7e.tar.bz2
otp-8e819a7960a256b6c3b7bf5856c3f81b8df9ca7e.zip
Simplify and extend diameter_make interface
In particular, make codec/2 flexible as to what's generated, the formats (erl, hrl, parse, forms and beam) being passed in the options list and defaulting to [erl, hrl]. The 'parse' format is the internal format to which dictionaries are parsed, which can be manipulated by flatten/1 before being passed back to codec/2 or format/1. Remove the (undocumented) dict/1,2 since codec/2 now subsumes it with the 'parse' option.
Diffstat (limited to 'lib/diameter/src/compiler/diameter_codegen.erl')
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl120
1 files changed, 92 insertions, 28 deletions
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index 91bab2205c..47da193457 100644
--- a/lib/diameter/src/compiler/diameter_codegen.erl
+++ b/lib/diameter/src/compiler/diameter_codegen.erl
@@ -33,11 +33,6 @@
-export([from_dict/4]).
-%% Internal exports (for test).
--export([file/1,
- file/2,
- file/3]).
-
-include("diameter_forms.hrl").
-include("diameter_vsn.hrl").
@@ -50,11 +45,11 @@
-spec from_dict(File, ParseD, Opts, Mode)
-> ok
- | list()
+ | term()
when File :: string(),
ParseD :: orddict:orddict(),
Opts :: list(),
- Mode :: dict | forms | erl | hrl.
+ Mode :: parse | forms | erl | hrl | beam.
from_dict(File, ParseD, Opts, Mode) ->
Outdir = proplists:get_value(outdir, Opts, "."),
@@ -74,11 +69,12 @@ mod(_, {ok, Mod}) ->
maybe_write(true, _, _, _, T) ->
T;
+
maybe_write(_, Mode, Outdir, Mod, T) ->
Path = filename:join(Outdir, Mod), %% minus extension
do_write(Mode, [Path, $., ext(Mode)], T).
-ext(dict) ->
+ext(parse) ->
"D";
ext(forms) ->
"F";
@@ -86,9 +82,11 @@ ext(T) ->
?S(T).
do_write(M, Path, T)
- when M == dict;
+ when M == parse;
M == forms ->
write_term(Path, T);
+do_write(beam, Path, {_Mod, Beam}) ->
+ write(Path, Beam);
do_write(_, Path, T) ->
write(Path, T).
@@ -125,36 +123,35 @@ eraser(Key) ->
%% ===========================================================================
%% ===========================================================================
-%% Generate from parsed dictionary in a file.
-
-file(F) ->
- file(F, dict).
-
-file(F, Mode) ->
- file(F, ".", Mode).
-
-file(F, Outdir, Mode) ->
- {ok, [ParseD]} = file:consult(F),
- from_dict(F, ParseD, Outdir, Mode).
-
-%% ===========================================================================
-%% ===========================================================================
-
get_value(Key, Plist) ->
proplists:get_value(Key, Plist, []).
-gen(dict, ParseD, _Mod) ->
+gen(parse, ParseD, _Mod) ->
[?VERSION | ParseD];
-gen(hrl, ParseD, Mod) ->
- gen_hrl(Mod, ParseD);
-
gen(forms, ParseD, Mod) ->
erl_forms(Mod, ParseD);
+gen(beam, ParseD, Mod) ->
+ compile(pp(erl_forms(Mod, ParseD)));
+
+gen(hrl, ParseD, Mod) ->
+ gen_hrl(Mod, ParseD);
+
gen(erl, ParseD, Mod) ->
[header(), prettypr(erl_forms(Mod, ParseD)), $\n].
+compile({ok, Forms}) ->
+ case compile:forms(Forms, [debug_info, return]) of
+ {ok, Mod, Beam, _Warnings} ->
+ {Mod, Beam};
+ {error, Errors, _Warnings} ->
+ erlang:error({compile, Errors})
+ end;
+
+compile({error, Reason}) ->
+ erlang:error(Reason).
+
erl_forms(Mod, ParseD) ->
Forms = [[{?attribute, module, Mod},
{?attribute, compile, {parse_transform, diameter_exprecs}},
@@ -851,3 +848,70 @@ prefix(ParseD) ->
rec_name(Name, Prefix) ->
Prefix ++ Name.
+
+%% ===========================================================================
+%% pp/1
+%%
+%% Preprocess forms as generated by 'forms' option. In particular,
+%% replace the include_lib attributes in generated forms by the
+%% corresponding forms, extracting the latter from an existing
+%% dictionary (diameter_gen_relay). The resulting forms can be
+%% compiled to beam using compile:forms/2 (which does no preprocessing
+%% or it's own; DiY currently appears to be the only way to preprocess
+%% a forms list).
+
+pp(Forms) ->
+ {_, Beam, _} = code:get_object_code(diameter_gen_relay),
+ pp(Forms, abstract_code(Beam)).
+
+pp(Forms, {ok, Code}) ->
+ Files = files(Code, []),
+ {ok, lists:flatmap(fun(T) -> include(T, Files) end, Forms)};
+
+pp(_, {error, _} = No) ->
+ No.
+
+include({attribute, _, include_lib, Path}, Files) ->
+ Inc = filename:basename(Path),
+ [{Inc, Forms}] = [T || {F, _} = T <- Files, F == Inc], %% expect one
+ lists:flatmap(fun filter/1, Forms);
+
+include(T, _) ->
+ [T].
+
+abstract_code(Beam) ->
+ case beam_lib:chunks(Beam, [abstract_code]) of
+ {ok, {_Mod, [{abstract_code, {_Vsn, Code}}]}} ->
+ {ok, Code};
+ {ok, {_Mod, [{abstract_code, no_abstract_code = No}]}} ->
+ {error, No};
+ {error = E, beam_lib, Reason} ->
+ {E, Reason}
+ end.
+
+files([{attribute, _, file, {Path, _}} | T], Acc) ->
+ {Body, Rest} = lists:splitwith(fun({attribute, _, file, _}) -> false;
+ (_) -> true
+ end,
+ T),
+ files(Rest, [{filename:basename(Path), Body} | Acc]);
+
+files([], Acc) ->
+ Acc.
+
+%% Only retain record diameter_avp and functions not generated by
+%% diameter_exprecs.
+
+filter({attribute, _, record, {diameter_avp, _}} = T) ->
+ [T];
+
+filter({function, _, Name, _, _} = T) ->
+ case ?S(Name) of
+ [$#|_] -> %% generated by diameter_exprecs
+ [];
+ _ ->
+ [T]
+ end;
+
+filter(_) ->
+ [].