aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2013-12-02 09:42:34 +0100
committerAnders Svensson <[email protected]>2013-12-02 09:42:34 +0100
commit2b8c7d30e6c24ebc4c8d78c16d8033ff0d6c106e (patch)
tree8cb99cd58347852794fcc495fcc6548af4a80c01 /lib/diameter/src
parent2bc1b1c962adf7849abb5a8399706aad708c5969 (diff)
parent2eb76ba556b8775cffc94eac26b448eac70af267 (diff)
downloadotp-2b8c7d30e6c24ebc4c8d78c16d8033ff0d6c106e.tar.gz
otp-2b8c7d30e6c24ebc4c8d78c16d8033ff0d6c106e.tar.bz2
otp-2b8c7d30e6c24ebc4c8d78c16d8033ff0d6c106e.zip
Merge branch 'anders/diameter/dictionary_make/OTP-11348' into maint
* anders/diameter/dictionary_make/OTP-11348: Return compilable forms instead of beam Document diameter_make:format/1 and diameter_make:flatten/1 Fix diameter_make:flatten/1 Modify type that currently causes dialyzer woe Simplify and extend diameter_make interface Add diameter_make:flatten/1, remove reformat/1 Adapt compiler suite to diameter_make Remove dead code from codec suite Extend diameter_make:codec/2 Don't pollute process dictionary in diameter_codegen:from_dict/4 Make forms a separate output from diameter_codegen Remove last remnants of "spec" Write as last step in code generation Change extensions for debug output: .spec/forms -> .D/F
Diffstat (limited to 'lib/diameter/src')
-rw-r--r--lib/diameter/src/compiler/diameter_codegen.erl548
-rw-r--r--lib/diameter/src/compiler/diameter_dict_util.erl7
-rw-r--r--lib/diameter/src/compiler/diameter_make.erl245
3 files changed, 488 insertions, 312 deletions
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl
index e687145263..22422f2ef2 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").
@@ -48,18 +43,61 @@
%% ===========================================================================
--spec from_dict(File, Spec, Opts, Mode)
+-spec from_dict(File, ParseD, Opts, Mode)
-> ok
+ | term()
when File :: string(),
- Spec :: orddict:orddict(),
+ ParseD :: orddict:orddict(),
Opts :: list(),
- Mode :: spec | erl | hrl.
+ Mode :: parse | forms | erl | hrl.
-from_dict(File, Spec, Opts, Mode) ->
+from_dict(File, ParseD, Opts, Mode) ->
Outdir = proplists:get_value(outdir, Opts, "."),
+ Return = proplists:get_value(return, Opts, false),
+ Mod = mod(File, orddict:find(name, ParseD)),
putr(verbose, lists:member(verbose, Opts)),
- putr(debug, lists:member(debug, Opts)),
- codegen(File, Spec, Outdir, Mode).
+ try
+ maybe_write(Return, Mode, Outdir, Mod, gen(Mode, ParseD, ?A(Mod)))
+ after
+ eraser(verbose)
+ end.
+
+mod(File, error) ->
+ filename:rootname(filename:basename(File));
+mod(_, {ok, Mod}) ->
+ 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(parse) ->
+ "D";
+ext(forms) ->
+ "F";
+ext(T) ->
+ ?S(T).
+
+do_write(M, Path, T)
+ when M == parse;
+ M == forms ->
+ write_term(Path, T);
+do_write(_, Path, T) ->
+ write(Path, T).
+
+write(Path, T) ->
+ write(Path, "~s", T).
+
+write_term(Path, T) ->
+ write(Path, "~p.~n", T).
+
+write(Path, Fmt, T) ->
+ {ok, Fd} = file:open(Path, [write]),
+ io:fwrite(Fd, Fmt, [T]),
+ ok = file:close(Fd).
%% Optional reports when running verbosely.
report(What, Data) ->
@@ -77,20 +115,8 @@ putr(Key, Value) ->
getr(Key) ->
get({?MODULE, Key}).
-%% ===========================================================================
-%% ===========================================================================
-
-%% Generate from parsed dictionary in a file.
-
-file(F) ->
- file(F, spec).
-
-file(F, Mode) ->
- file(F, ".", Mode).
-
-file(F, Outdir, Mode) ->
- {ok, [Spec]} = file:consult(F),
- from_dict(F, Spec, Outdir, Mode).
+eraser(Key) ->
+ erase({?MODULE, Key}).
%% ===========================================================================
%% ===========================================================================
@@ -98,97 +124,68 @@ file(F, Outdir, Mode) ->
get_value(Key, Plist) ->
proplists:get_value(Key, Plist, []).
-write(Path, Str) ->
- w(Path, Str, "~s").
-
-write_term(Path, T) ->
- w(Path, T, "~p.").
-
-w(Path, T, Fmt) ->
- {ok, Fd} = file:open(Path, [write]),
- io:fwrite(Fd, Fmt ++ "~n", [T]),
- file:close(Fd).
-
-codegen(File, Spec, Outdir, Mode) ->
- Mod = mod(File, orddict:find(name, Spec)),
- Path = filename:join(Outdir, Mod), %% minus extension
- gen(Mode, Spec, ?A(Mod), Path),
- ok.
-
-mod(File, error) ->
- filename:rootname(filename:basename(File));
-mod(_, {ok, Mod}) ->
- Mod.
-
-gen(spec, Spec, _Mod, Path) ->
- write_term(Path ++ ".spec", [?VERSION | Spec]);
-
-gen(hrl, Spec, Mod, Path) ->
- gen_hrl(Path ++ ".hrl", Mod, Spec);
-
-gen(erl, Spec, Mod, Path) ->
- Forms = [{?attribute, module, Mod},
- {?attribute, compile, {parse_transform, diameter_exprecs}},
- {?attribute, compile, nowarn_unused_function},
- {?attribute, export, [{name, 0},
- {id, 0},
- {vendor_id, 0},
- {vendor_name, 0},
- {decode_avps, 2}, %% in diameter_gen.hrl
- {encode_avps, 2}, %%
- {msg_name, 2},
- {msg_header, 1},
- {rec2msg, 1},
- {msg2rec, 1},
- {name2rec, 1},
- {avp_name, 2},
- {avp_arity, 2},
- {avp_header, 1},
- {avp, 3},
- {grouped_avp, 3},
- {enumerated_avp, 3},
- {empty_value, 1},
- {dict, 0}]},
- %% diameter.hrl is included for #diameter_avp
- {?attribute, include_lib, "diameter/include/diameter.hrl"},
- {?attribute, include_lib, "diameter/include/diameter_gen.hrl"},
- f_name(Mod),
- f_id(Spec),
- f_vendor_id(Spec),
- f_vendor_name(Spec),
- f_msg_name(Spec),
- f_msg_header(Spec),
- f_rec2msg(Spec),
- f_msg2rec(Spec),
- f_name2rec(Spec),
- f_avp_name(Spec),
- f_avp_arity(Spec),
- f_avp_header(Spec),
- f_avp(Spec),
- f_enumerated_avp(Spec),
- f_empty_value(Spec),
- f_dict(Spec),
- {eof, ?LINE}],
-
- gen_erl(Path, insert_hrl_forms(Spec, Forms)).
-
-gen_erl(Path, Forms) ->
- getr(debug) andalso write_term(Path ++ ".forms", Forms),
- write(Path ++ ".erl",
- header() ++ erl_prettypr:format(erl_syntax:form_list(Forms))).
-
-insert_hrl_forms(Spec, Forms) ->
- {H,T} = lists:splitwith(fun is_header/1, Forms),
- H ++ make_hrl_forms(Spec) ++ T.
-
-is_header({attribute, _, export, _}) ->
- false;
-is_header(_) ->
- true.
-
-make_hrl_forms(Spec) ->
+gen(parse, ParseD, _Mod) ->
+ [?VERSION | ParseD];
+
+gen(forms, ParseD, Mod) ->
+ pp(erl_forms(Mod, ParseD));
+
+gen(hrl, ParseD, Mod) ->
+ gen_hrl(Mod, ParseD);
+
+gen(erl, ParseD, Mod) ->
+ [header(), prettypr(erl_forms(Mod, ParseD)), $\n].
+
+erl_forms(Mod, ParseD) ->
+ Forms = [[{?attribute, module, Mod},
+ {?attribute, compile, {parse_transform, diameter_exprecs}},
+ {?attribute, compile, nowarn_unused_function}],
+ make_hrl_forms(ParseD),
+ [{?attribute, export, [{name, 0},
+ {id, 0},
+ {vendor_id, 0},
+ {vendor_name, 0},
+ {decode_avps, 2}, %% in diameter_gen.hrl
+ {encode_avps, 2}, %%
+ {msg_name, 2},
+ {msg_header, 1},
+ {rec2msg, 1},
+ {msg2rec, 1},
+ {name2rec, 1},
+ {avp_name, 2},
+ {avp_arity, 2},
+ {avp_header, 1},
+ {avp, 3},
+ {grouped_avp, 3},
+ {enumerated_avp, 3},
+ {empty_value, 1},
+ {dict, 0}]},
+ %% diameter.hrl is included for #diameter_avp
+ {?attribute, include_lib, "diameter/include/diameter.hrl"},
+ {?attribute, include_lib, "diameter/include/diameter_gen.hrl"},
+ f_name(Mod),
+ f_id(ParseD),
+ f_vendor_id(ParseD),
+ f_vendor_name(ParseD),
+ f_msg_name(ParseD),
+ f_msg_header(ParseD),
+ f_rec2msg(ParseD),
+ f_msg2rec(ParseD),
+ f_name2rec(ParseD),
+ f_avp_name(ParseD),
+ f_avp_arity(ParseD),
+ f_avp_header(ParseD),
+ f_avp(ParseD),
+ f_enumerated_avp(ParseD),
+ f_empty_value(ParseD),
+ f_dict(ParseD),
+ {eof, ?LINE}]],
+
+ lists:append(Forms).
+
+make_hrl_forms(ParseD) ->
{_Prefix, MsgRecs, GrpRecs, ImportedGrpRecs}
- = make_record_forms(Spec),
+ = make_record_forms(ParseD),
RecordForms = MsgRecs ++ GrpRecs ++ lists:flatmap(fun({_,Fs}) -> Fs end,
ImportedGrpRecs),
@@ -199,16 +196,16 @@ make_hrl_forms(Spec) ->
%% export_records is used by the diameter_exprecs parse transform.
[{?attribute, export_records, RecNames} | RecordForms].
-make_record_forms(Spec) ->
- Prefix = prefix(Spec),
+make_record_forms(ParseD) ->
+ Prefix = prefix(ParseD),
- MsgRecs = a_record(Prefix, fun msg_proj/1, get_value(messages, Spec)),
- GrpRecs = a_record(Prefix, fun grp_proj/1, get_value(grouped, Spec)),
+ MsgRecs = a_record(Prefix, fun msg_proj/1, get_value(messages, ParseD)),
+ GrpRecs = a_record(Prefix, fun grp_proj/1, get_value(grouped, ParseD)),
ImportedGrpRecs = [{M, a_record(Prefix, fun grp_proj/1, Gs)}
- || {M,Gs} <- get_value(import_groups, Spec)],
+ || {M,Gs} <- get_value(import_groups, ParseD)],
- {Prefix, MsgRecs, GrpRecs, ImportedGrpRecs}.
+ {to_upper(Prefix), MsgRecs, GrpRecs, ImportedGrpRecs}.
msg_proj({Name, _, _, _, Avps}) ->
{Name, Avps}.
@@ -246,9 +243,9 @@ f_name(Name) ->
%%% # id/0
%%% ------------------------------------------------------------------------
-f_id(Spec) ->
+f_id(ParseD) ->
{?function, id, 0,
- [c_id(orddict:find(id, Spec))]}.
+ [c_id(orddict:find(id, ParseD))]}.
c_id({ok, Id}) ->
{?clause, [], [], [?INTEGER(Id)]};
@@ -260,9 +257,9 @@ c_id(error) ->
%%% # vendor_id/0
%%% ------------------------------------------------------------------------
-f_vendor_id(Spec) ->
+f_vendor_id(ParseD) ->
{?function, vendor_id, 0,
- [{?clause, [], [], [b_vendor_id(orddict:find(vendor, Spec))]}]}.
+ [{?clause, [], [], [b_vendor_id(orddict:find(vendor, ParseD))]}]}.
b_vendor_id({ok, {Id, _}}) ->
?INTEGER(Id);
@@ -273,9 +270,9 @@ b_vendor_id(error) ->
%%% # vendor_name/0
%%% ------------------------------------------------------------------------
-f_vendor_name(Spec) ->
+f_vendor_name(ParseD) ->
{?function, vendor_name, 0,
- [{?clause, [], [], [b_vendor_name(orddict:find(vendor, Spec))]}]}.
+ [{?clause, [], [], [b_vendor_name(orddict:find(vendor, ParseD))]}]}.
b_vendor_name({ok, {_, Name}}) ->
?Atom(Name);
@@ -286,15 +283,15 @@ b_vendor_name(error) ->
%%% # msg_name/1
%%% ------------------------------------------------------------------------
-f_msg_name(Spec) ->
- {?function, msg_name, 2, msg_name(Spec)}.
+f_msg_name(ParseD) ->
+ {?function, msg_name, 2, msg_name(ParseD)}.
%% Return the empty name for any unknown command to which
%% DIAMETER_COMMAND_UNSUPPORTED should be replied.
-msg_name(Spec) ->
+msg_name(ParseD) ->
lists:flatmap(fun c_msg_name/1, proplists:get_value(command_codes,
- Spec,
+ ParseD,
[]))
++ [{?clause, [?VAR('_'), ?VAR('_')], [], [?ATOM('')]}].
@@ -310,12 +307,12 @@ c_msg_name({Code, Req, Ans}) ->
%%% # msg2rec/1
%%% ------------------------------------------------------------------------
-f_msg2rec(Spec) ->
- {?function, msg2rec, 1, msg2rec(Spec)}.
+f_msg2rec(ParseD) ->
+ {?function, msg2rec, 1, msg2rec(ParseD)}.
-msg2rec(Spec) ->
- Pre = prefix(Spec),
- lists:map(fun(T) -> c_msg2rec(T, Pre) end, get_value(messages, Spec))
+msg2rec(ParseD) ->
+ Pre = prefix(ParseD),
+ lists:map(fun(T) -> c_msg2rec(T, Pre) end, get_value(messages, ParseD))
++ [?BADARG(1)].
c_msg2rec({N,_,_,_,_}, Pre) ->
@@ -325,12 +322,12 @@ c_msg2rec({N,_,_,_,_}, Pre) ->
%%% # rec2msg/1
%%% ------------------------------------------------------------------------
-f_rec2msg(Spec) ->
- {?function, rec2msg, 1, rec2msg(Spec)}.
+f_rec2msg(ParseD) ->
+ {?function, rec2msg, 1, rec2msg(ParseD)}.
-rec2msg(Spec) ->
- Pre = prefix(Spec),
- lists:map(fun(T) -> c_rec2msg(T, Pre) end, get_value(messages, Spec))
+rec2msg(ParseD) ->
+ Pre = prefix(ParseD),
+ lists:map(fun(T) -> c_rec2msg(T, Pre) end, get_value(messages, ParseD))
++ [?BADARG(1)].
c_rec2msg({N,_,_,_,_}, Pre) ->
@@ -340,13 +337,13 @@ c_rec2msg({N,_,_,_,_}, Pre) ->
%%% # name2rec/1
%%% ------------------------------------------------------------------------
-f_name2rec(Spec) ->
- {?function, name2rec, 1, name2rec(Spec)}.
+f_name2rec(ParseD) ->
+ {?function, name2rec, 1, name2rec(ParseD)}.
-name2rec(Spec) ->
- Pre = prefix(Spec),
- Groups = get_value(grouped, Spec)
- ++ lists:flatmap(fun avps/1, get_value(import_groups, Spec)),
+name2rec(ParseD) ->
+ Pre = prefix(ParseD),
+ Groups = get_value(grouped, ParseD)
+ ++ lists:flatmap(fun avps/1, get_value(import_groups, ParseD)),
lists:map(fun({N,_,_,_}) -> c_name2rec(N, Pre) end, Groups)
++ [{?clause, [?VAR('T')], [], [?CALL(msg2rec, [?VAR('T')])]}].
@@ -360,8 +357,8 @@ avps({_Mod, Avps}) ->
%%% # avp_name/1
%%% ------------------------------------------------------------------------
-f_avp_name(Spec) ->
- {?function, avp_name, 2, avp_name(Spec)}.
+f_avp_name(ParseD) ->
+ {?function, avp_name, 2, avp_name(ParseD)}.
%% 3588, 4.1:
%%
@@ -372,11 +369,11 @@ f_avp_name(Spec) ->
%% field. AVP numbers 256 and above are used for Diameter, which are
%% allocated by IANA (see Section 11.1).
-avp_name(Spec) ->
- Avps = get_value(avp_types, Spec),
- Imported = get_value(import_avps, Spec),
- Vid = orddict:find(vendor, Spec),
- Vs = vendor_id_map(Spec),
+avp_name(ParseD) ->
+ Avps = get_value(avp_types, ParseD),
+ Imported = get_value(import_avps, ParseD),
+ Vid = orddict:find(vendor, ParseD),
+ Vs = vendor_id_map(ParseD),
lists:map(fun(T) -> c_avp_name(T, Vs, Vid) end, Avps)
++ lists:flatmap(fun(T) -> c_imported_avp_name(T, Vs) end, Imported)
@@ -407,25 +404,25 @@ c_avp_name_(T, Code, Vid) ->
[],
[T]}.
-vendor_id_map(Spec) ->
+vendor_id_map(ParseD) ->
lists:flatmap(fun({V,Ns}) -> [{N,V} || N <- Ns] end,
- get_value(avp_vendor_id, Spec))
+ get_value(avp_vendor_id, ParseD))
++ lists:flatmap(fun({_,_,[],_}) -> [];
({N,_,[V],_}) -> [{N,V}]
end,
- get_value(grouped, Spec)).
+ get_value(grouped, ParseD)).
%%% ------------------------------------------------------------------------
%%% # avp_arity/2
%%% ------------------------------------------------------------------------
-f_avp_arity(Spec) ->
- {?function, avp_arity, 2, avp_arity(Spec)}.
+f_avp_arity(ParseD) ->
+ {?function, avp_arity, 2, avp_arity(ParseD)}.
-avp_arity(Spec) ->
- Msgs = get_value(messages, Spec),
- Groups = get_value(grouped, Spec)
- ++ lists:flatmap(fun avps/1, get_value(import_groups, Spec)),
+avp_arity(ParseD) ->
+ Msgs = get_value(messages, ParseD),
+ Groups = get_value(grouped, ParseD)
+ ++ lists:flatmap(fun avps/1, get_value(import_groups, ParseD)),
c_avp_arity(Msgs ++ Groups)
++ [{?clause, [?VAR('_'), ?VAR('_')], [], [?INTEGER(0)]}].
@@ -449,15 +446,15 @@ c_arity(Name, Avp) ->
%%% # avp/3
%%% ------------------------------------------------------------------------
-f_avp(Spec) ->
- {?function, avp, 3, avp(Spec) ++ [?BADARG(3)]}.
+f_avp(ParseD) ->
+ {?function, avp, 3, avp(ParseD) ++ [?BADARG(3)]}.
-avp(Spec) ->
- Native = get_value(avp_types, Spec),
- CustomMods = get_value(custom_types, Spec),
- TypeMods = get_value(codecs, Spec),
- Imported = get_value(import_avps, Spec),
- Enums = get_value(enum, Spec),
+avp(ParseD) ->
+ Native = get_value(avp_types, ParseD),
+ CustomMods = get_value(custom_types, ParseD),
+ TypeMods = get_value(codecs, ParseD),
+ Imported = get_value(import_avps, ParseD),
+ Enums = get_value(enum, ParseD),
Custom = lists:map(fun({M,As}) -> {M, custom_types, As} end,
CustomMods)
@@ -548,14 +545,14 @@ custom(codecs, AvpName, Type) ->
%%% # enumerated_avp/3
%%% ------------------------------------------------------------------------
-f_enumerated_avp(Spec) ->
- {?function, enumerated_avp, 3, enumerated_avp(Spec) ++ [?BADARG(3)]}.
+f_enumerated_avp(ParseD) ->
+ {?function, enumerated_avp, 3, enumerated_avp(ParseD) ++ [?BADARG(3)]}.
-enumerated_avp(Spec) ->
- Enums = get_value(enum, Spec),
+enumerated_avp(ParseD) ->
+ Enums = get_value(enum, ParseD),
lists:flatmap(fun cs_enumerated_avp/1, Enums)
++ lists:flatmap(fun({M,Es}) -> enumerated_avp(M, Es, Enums) end,
- get_value(import_enums, Spec)).
+ get_value(import_enums, ParseD)).
enumerated_avp(Mod, Es, Enums) ->
lists:flatmap(fun({N,_}) ->
@@ -585,16 +582,16 @@ c_enumerated_avp(AvpName, {_,I}) ->
%%% msg_header/1
%%% ------------------------------------------------------------------------
-f_msg_header(Spec) ->
- {?function, msg_header, 1, msg_header(Spec) ++ [?BADARG(1)]}.
+f_msg_header(ParseD) ->
+ {?function, msg_header, 1, msg_header(ParseD) ++ [?BADARG(1)]}.
-msg_header(Spec) ->
- msg_header(get_value(messages, Spec), Spec).
+msg_header(ParseD) ->
+ msg_header(get_value(messages, ParseD), ParseD).
msg_header([], _) ->
[];
-msg_header(Msgs, Spec) ->
- ApplId = orddict:fetch(id, Spec),
+msg_header(Msgs, ParseD) ->
+ ApplId = orddict:fetch(id, ParseD),
lists:map(fun({M,C,F,_,_}) -> c_msg_header(M, C, F, ApplId) end, Msgs).
@@ -616,14 +613,14 @@ emf('ERR', N) -> N bor 2#00100000.
%%% # avp_header/1
%%% ------------------------------------------------------------------------
-f_avp_header(Spec) ->
- {?function, avp_header, 1, avp_header(Spec) ++ [?BADARG(1)]}.
+f_avp_header(ParseD) ->
+ {?function, avp_header, 1, avp_header(ParseD) ++ [?BADARG(1)]}.
-avp_header(Spec) ->
- Native = get_value(avp_types, Spec),
- Imported = get_value(import_avps, Spec),
- Vid = orddict:find(vendor, Spec),
- Vs = vendor_id_map(Spec),
+avp_header(ParseD) ->
+ Native = get_value(avp_types, ParseD),
+ Imported = get_value(import_avps, ParseD),
+ Vid = orddict:find(vendor, ParseD),
+ Vs = vendor_id_map(ParseD),
lists:flatmap(fun(A) -> c_avp_header(A, Vs, Vid) end,
Native ++ Imported).
@@ -679,14 +676,14 @@ v(false, _, _, _) ->
%%% # empty_value/0
%%% ------------------------------------------------------------------------
-f_empty_value(Spec) ->
- {?function, empty_value, 1, empty_value(Spec)}.
+f_empty_value(ParseD) ->
+ {?function, empty_value, 1, empty_value(ParseD)}.
-empty_value(Spec) ->
- Imported = lists:flatmap(fun avps/1, get_value(import_enums, Spec)),
- Groups = get_value(grouped, Spec)
- ++ lists:flatmap(fun avps/1, get_value(import_groups, Spec)),
- Enums = [T || {N,_} = T <- get_value(enum, Spec),
+empty_value(ParseD) ->
+ Imported = lists:flatmap(fun avps/1, get_value(import_enums, ParseD)),
+ Groups = get_value(grouped, ParseD)
+ ++ lists:flatmap(fun avps/1, get_value(import_groups, ParseD)),
+ Enums = [T || {N,_} = T <- get_value(enum, ParseD),
not lists:keymember(N, 1, Imported)]
++ Imported,
lists:map(fun c_empty_value/1, Groups ++ Enums)
@@ -706,72 +703,52 @@ c_empty_value({Name, _}) ->
%%% # dict/0
%%% ------------------------------------------------------------------------
-f_dict(Spec) ->
+f_dict(ParseD) ->
{?function, dict, 0,
- [{?clause, [], [], [?TERM([?VERSION | Spec])]}]}.
+ [{?clause, [], [], [?TERM([?VERSION | ParseD])]}]}.
%%% ------------------------------------------------------------------------
-%%% # gen_hrl/3
+%%% # gen_hrl/2
%%% ------------------------------------------------------------------------
-gen_hrl(Path, Mod, Spec) ->
- {ok, Fd} = file:open(Path, [write]),
-
+gen_hrl(Mod, ParseD) ->
{Prefix, MsgRecs, GrpRecs, ImportedGrpRecs}
- = make_record_forms(Spec),
-
- file:write(Fd, hrl_header(Mod)),
-
- forms("Message records", Fd, MsgRecs),
- forms("Grouped AVP records", Fd, GrpRecs),
-
- lists:foreach(fun({M,Fs}) ->
- forms("Grouped AVP records from " ++ atom_to_list(M),
- Fd,
- Fs)
- end,
- ImportedGrpRecs),
-
- PREFIX = to_upper(Prefix),
-
- write("ENUM Macros",
- Fd,
- m_enums(PREFIX, false, get_value(enum, Spec))),
- write("DEFINE Macros",
- Fd,
- m_enums(PREFIX, false, get_value(define, Spec))),
-
- lists:foreach(fun({M,Es}) ->
- write("ENUM Macros from " ++ atom_to_list(M),
- Fd,
- m_enums(PREFIX, true, Es))
- end,
- get_value(import_enums, Spec)),
-
- file:close(Fd).
-
-forms(_, _, []) ->
- ok;
-forms(Banner, Fd, Forms) ->
- write(Banner, Fd, prettypr(Forms)).
-
-write(_, _, []) ->
- ok;
-write(Banner, Fd, Str) ->
- banner(Fd, Banner),
- io:fwrite(Fd, "~s~n", [Str]).
+ = make_record_forms(ParseD),
+
+ [hrl_header(Mod),
+ forms("Message records", MsgRecs),
+ forms("Grouped AVP records", GrpRecs),
+ lists:map(fun({M,Fs}) ->
+ forms("Grouped AVP records from " ++ atom_to_list(M),
+ Fs)
+ end,
+ ImportedGrpRecs),
+ format("ENUM Macros", m_enums(Prefix, false, get_value(enum, ParseD))),
+ format("DEFINE Macros", m_enums(Prefix, false, get_value(define, ParseD))),
+ lists:map(fun({M,Es}) ->
+ format("ENUM Macros from " ++ atom_to_list(M),
+ m_enums(Prefix, true, Es))
+ end,
+ get_value(import_enums, ParseD))].
+
+forms(_, [] = No) ->
+ No;
+forms(Banner, Forms) ->
+ format(Banner, prettypr(Forms)).
+
+format(_, [] = No) ->
+ No;
+format(Banner, Str) ->
+ [banner(Banner), Str, $\n].
prettypr(Forms) ->
erl_prettypr:format(erl_syntax:form_list(Forms)).
-banner(Fd, Heading) ->
- file:write(Fd, banner(Heading)).
-
banner(Heading) ->
- ("\n\n"
+ ["\n\n"
"%%% -------------------------------------------------------\n"
- "%%% " ++ Heading ++ ":\n"
- "%%% -------------------------------------------------------\n\n").
+ "%%% ", Heading, ":\n"
+ "%%% -------------------------------------------------------\n\n"].
z(S) ->
string:join(string:tokens(S, "\s\t"), "\s").
@@ -845,8 +822,8 @@ arity([_], '*' = Inf) -> {0, Inf};
arity({_}, '*' = Inf) -> {1, Inf};
arity(_, {_,_} = Q) -> Q.
-prefix(Spec) ->
- case orddict:find(prefix, Spec) of
+prefix(ParseD) ->
+ case orddict:find(prefix, ParseD) of
{ok, P} ->
P ++ "_";
error ->
@@ -855,3 +832,70 @@ prefix(Spec) ->
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, []),
+ lists:flatmap(fun(T) -> include(T, Files) end, Forms);
+
+pp(Forms, {error, Reason}) ->
+ erlang:error({forms, Reason, Forms}).
+
+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(_) ->
+ [].
diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl
index 36a6efa294..3941f30e03 100644
--- a/lib/diameter/src/compiler/diameter_dict_util.erl
+++ b/lib/diameter/src/compiler/diameter_dict_util.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -46,7 +46,7 @@
-spec parse(File, Opts)
-> {ok, orddict:orddict()}
| {error, term()}
- when File :: {path, string()}
+ when File :: {path, file:name_all()}
| iolist()
| binary(),
Opts :: list().
@@ -265,6 +265,9 @@ io(K, Id)
io(vendor = K, {Id, Name}) ->
[?NL, section(K) | [[?SP, tok(X)] || X <- [Id, Name]]];
+io(_, []) ->
+ [];
+
io(avp_types = K, Body) ->
[?NL, ?NL, section(K), ?NL, [body(K,A) || A <- Body]];
diff --git a/lib/diameter/src/compiler/diameter_make.erl b/lib/diameter/src/compiler/diameter_make.erl
index 16e30c1ffb..2f314b7e57 100644
--- a/lib/diameter/src/compiler/diameter_make.erl
+++ b/lib/diameter/src/compiler/diameter_make.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2010-2013. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -30,102 +30,231 @@
-module(diameter_make).
--export([codec/1,
- codec/2,
- dict/1,
- dict/2,
+-export([codec/2,
+ codec/1,
format/1,
- reformat/1]).
+ flatten/1]).
-export_type([opt/0]).
+-include("diameter_vsn.hrl").
+
+%% Options passed to codec/2.
-type opt() :: {include|outdir|name|prefix|inherits, string()}
+ | return
| verbose
- | debug.
+ | parse %% internal parsed form
+ | forms %% abstract format for compile:forms/1,2
+ | erl
+ | hrl.
+
+%% Internal parsed format with a version tag.
+-type parsed() :: list().
+
+%% Literal dictionary or path. A NL of CR identifies the former.
+-type dict() :: iolist()
+ | binary()
+ | parsed(). %% as returned by codec/2
+
+%% Name of a literal dictionary if otherwise unspecified.
+-define(DEFAULT_DICT_FILE, "dictionary.dia").
%% ===========================================================================
%% codec/1-2
%%
-%% Parse a dictionary file and generate a codec module.
+%% Parse a dictionary file and generate a codec module. Input
+%% dictionary can be either a path or the dictionary itself: the
+%% occurrence of \n or \r in the argument is used to distinguish the
+%% two.
--spec codec(Path, [opt()])
+-spec codec(File, [opt()])
-> ok
+ | {ok, list()} %% with option 'return', one element for each output
| {error, Reason}
- when Path :: string(),
+ when File :: dict()
+ | {path, file:name_all()},
Reason :: string().
codec(File, Opts) ->
- case dict(File, Opts) of
- {ok, Dict} ->
- make(File,
- Opts,
- Dict,
- [spec || _ <- [1], lists:member(debug, Opts)] ++ [erl, hrl]);
- {error, _} = E ->
- E
+ {Dict, Path} = identify(File),
+ case parse(Dict, Opts) of
+ {ok, ParseD} ->
+ make(Path, default(Opts), ParseD);
+ {error = E, Reason} ->
+ {E, diameter_dict_util:format_error(Reason)}
end.
codec(File) ->
codec(File, []).
-%% dict/2
+%% format/1
%%
-%% Parse a dictionary file and return the orddict that a codec module
-%% returns from dict/0.
-
--spec dict(string(), [opt()])
- -> {ok, orddict:orddict()}
- | {error, string()}.
+%% Turn an orddict returned by dict/1-2 back into a dictionary.
-dict(Path, Opts) ->
- case diameter_dict_util:parse({path, Path}, Opts) of
- {ok, _} = Ok ->
- Ok;
- {error = E, Reason} ->
- {E, diameter_dict_util:format_error(Reason)}
- end.
+-spec format(parsed())
+ -> iolist().
-dict(File) ->
- dict(File, []).
+format([?VERSION | Dict]) ->
+ diameter_dict_util:format(Dict).
-%% format/1
+%% flatten/1
%%
-%% Turn an orddict returned by dict/1-2 back into a dictionary file
-%% in the form of an iolist().
+%% Reconstitute a dictionary without @inherits.
--spec format(orddict:orddict())
- -> iolist().
+-spec flatten(parsed())
+ -> parsed().
-format(Dict) ->
- diameter_dict_util:format(Dict).
+flatten([?VERSION = V | Dict]) ->
+ [V | lists:foldl(fun flatten/2,
+ Dict,
+ [avp_vendor_id,
+ custom_types,
+ codecs,
+ [avp_types, import_avps],
+ [grouped, import_groups],
+ [enum, import_enums]])].
+
+%% ===========================================================================
+
+%% flatten/2
+
+flatten([_,_] = Keys, Dict) ->
+ [Values, Imports] = [orddict:fetch(K, Dict) || K <- Keys],
+ Vs = lists:append([Values | [V || {_Mod, V} <- Imports]]),
+ lists:foldl(fun({K,V},D) -> orddict:store(K,V,D) end,
+ Dict,
+ lists:zip([inherits | Keys], [[], Vs, []]));
+
+%% Inherited avp's setting the 'V' flag get their value either from
+%% @avp_vendor_id in the inheriting dictionary or from @vendor in the
+%% *inherited* (not inheriting) dictionary: add the latter to
+%% @avp_vendor_id as required.
+flatten(avp_vendor_id = Key, Dict) ->
+ Def = orddict:find(vendor, Dict),
+ ModD = imports(Dict),
+ Vids = orddict:fetch(Key, Dict),
+ Avps = lists:append([As || {_,As} <- Vids]),
+ orddict:store(Key,
+ dict:fold(fun(M, As, A) -> vid(M, As -- Avps, Def, A) end,
+ Vids,
+ ModD),
+ Dict);
+
+%% Import @codecs and @custom_types from inherited dictionaries as
+%% required.
+flatten(Key, Dict) ->
+ ImportAvps = orddict:fetch(import_avps, Dict),
+ ImportItems = [{M, As}
+ || {Mod, Avps} <- ImportAvps,
+ [_|D] <- [Mod:dict()],
+ {M,As0} <- orddict:fetch(Key, D),
+ F <- [fun(A) -> lists:keymember(A, 1, Avps) end],
+ [_|_] = As <- [lists:filter(F, As0)]],
+ orddict:store(Key,
+ lists:foldl(fun merge/2,
+ orddict:fetch(Key, Dict),
+ ImportItems),
+ Dict).
-%% reformat/1
+%% merge/2
+
+merge({Mod, _Avps} = T, Acc) ->
+ merge(lists:keyfind(Mod, 1, Acc), T, Acc).
+
+merge({Mod, Avps}, {Mod, As}, Acc) ->
+ lists:keyreplace(Mod, 1, Acc, {Mod, Avps ++ As});
+merge(false, T, Acc) ->
+ [T | Acc].
+
+%% imports/1
%%
-%% Parse a dictionary file and return its formatted equivalent.
+%% Return a module() -> [AVP] dict of inherited AVP's setting the V flag.
--spec reformat(File)
- -> {ok, iolist()}
- | {error, Reason}
- when File :: string(),
- Reason :: string().
+imports(Dict) ->
+ lists:foldl(fun imports/2,
+ dict:new(),
+ orddict:fetch(import_avps, Dict)).
+
+imports({Mod, Avps}, Dict) ->
+ dict:store(Mod,
+ [A || {A,_,_,Fs} <- Avps, lists:member($V, Fs)],
+ Dict).
-reformat(File) ->
- case dict(File) of
- {ok, Dict} ->
- {ok, format(Dict)};
- {error, _} = No ->
- No
+%% vid/4
+
+vid(_, [], _, Acc) ->
+ Acc;
+vid(Mod, Avps, Def, Acc) ->
+ v(Mod:vendor_id(), Avps, Def, Acc).
+
+v(Vid, _, {ok, {Vid, _}}, Acc) -> %% same id as inheriting dictionary's
+ Acc;
+v(Vid, Avps, _, Acc) ->
+ case lists:keyfind(Vid, 1, Acc) of
+ {Vid, As} ->
+ lists:keyreplace(Vid, 1, Acc, {Vid, As ++ Avps});
+ false ->
+ [{Vid, Avps} | Acc]
end.
%% ===========================================================================
-make(_, _, _, []) ->
+parse({dict, ParseD}, _) ->
+ {ok, ParseD};
+parse(File, Opts) ->
+ diameter_dict_util:parse(File, Opts).
+
+default(Opts) ->
+ def(modes(Opts), Opts).
+
+def([], Opts) ->
+ [erl, hrl | Opts];
+def(_, Opts) ->
+ Opts.
+
+modes(Opts) ->
+ lists:filter(fun is_mode/1, Opts).
+
+is_mode(T) ->
+ lists:member(T, [erl, hrl, parse, forms]).
+
+identify([Vsn | [T|_] = ParseD])
+ when is_tuple(T) ->
+ ?VERSION == Vsn orelse erlang:error({version, {Vsn, ?VERSION}}),
+ {{dict, ParseD}, ?DEFAULT_DICT_FILE};
+identify({path, File} = T) ->
+ {T, File};
+identify(File) ->
+ Bin = iolist_to_binary([File]),
+ case is_path(Bin) of
+ true -> {{path, File}, File};
+ false -> {Bin, ?DEFAULT_DICT_FILE}
+ end.
+
+%% Interpret anything containing \n or \r as a literal dictionary,
+%% otherwise a path. (Which might be the wrong guess in the worst case.)
+is_path(Bin) ->
+ try
+ [throw(C) || <<C>> <= Bin, $\n == C orelse $\r == C],
+ true
+ catch
+ throw:_ -> false
+ end.
+
+make(File, Opts, Dict) ->
+ ok(lists:foldl(fun(M,A) -> [make(File, Opts, Dict, M) | A] end,
+ [],
+ modes(Opts))).
+
+ok([ok|_]) ->
ok;
-make(File, Opts, Dict, [Mode | Rest]) ->
+ok([_|_] = L) ->
+ {ok, lists:reverse(L)}.
+
+make(File, Opts, Dict, Mode) ->
try
- ok = diameter_codegen:from_dict(File, Dict, Opts, Mode),
- make(File, Opts, Dict, Rest)
+ diameter_codegen:from_dict(File, Dict, Opts, Mode)
catch
error: Reason ->
erlang:error({Reason, Mode, erlang:get_stacktrace()})