From 265c88664b93f9069c86bf6c25e5d07b7f41d2dc Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Mon, 23 Sep 2013 10:31:21 +0200 Subject: Extend diameter_make:codec/2 Function can now take a literal dictionary as input, instead of a path, and can return results instead of writing them to the filesystem. --- lib/diameter/doc/src/diameter_make.xml | 19 +++- lib/diameter/src/compiler/diameter_codegen.erl | 79 ++++++++++------- lib/diameter/src/compiler/diameter_dict_util.erl | 2 +- lib/diameter/src/compiler/diameter_make.erl | 105 +++++++++++++++++------ 4 files changed, 147 insertions(+), 58 deletions(-) (limited to 'lib/diameter') diff --git a/lib/diameter/doc/src/diameter_make.xml b/lib/diameter/doc/src/diameter_make.xml index ec71251be1..2e69fca1ae 100644 --- a/lib/diameter/doc/src/diameter_make.xml +++ b/lib/diameter/doc/src/diameter_make.xml @@ -64,12 +64,15 @@ interface.

-codec(Path::string(), [Opt]) -> ok | {error, Reason} +codec(File :: iolist() | binary(), [Opt]) -> ok | {ok, Ret} | {error, Reason} Compile a dictionary file into Erlang source.

Compile a single dictionary file to Erlang source. +The input File can be either a path or a literal dictionary, +the occurrence of newline (ascii NL) or carriage return (ascii CR) +identifying the latter. Opt can have the following types.

@@ -93,6 +96,13 @@ Write generated source to the specified directory. Defaults to the current working directory.

+return + +

+Return erl and hrl source as two iolists rather than writing them to +the filesystem.

+
+ {name|prefix, string()}

@@ -127,6 +137,13 @@ Multiple inherits options can be specified.

+

+Note that a dictionary's &dict_name;, together with the +outdir option, determine the output paths when the +return option is not specified. +The &dict_name; of a literal input dictionary defaults to +dictionary.

+
diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index f2c10f1748..91bab2205c 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -50,6 +50,7 @@ -spec from_dict(File, ParseD, Opts, Mode) -> ok + | list() when File :: string(), ParseD :: orddict:orddict(), Opts :: list(), @@ -57,13 +58,51 @@ 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)), try - codegen(File, ParseD, Outdir, Mode) + 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(dict) -> + "D"; +ext(forms) -> + "F"; +ext(T) -> + ?S(T). + +do_write(M, Path, T) + when M == dict; + 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) -> report(getr(verbose), What, Data), @@ -104,39 +143,17 @@ file(F, Outdir, Mode) -> get_value(Key, Plist) -> proplists:get_value(Key, Plist, []). -codegen(File, ParseD, Outdir, Mode) -> - Mod = mod(File, orddict:find(name, ParseD)), - Path = filename:join(Outdir, Mod), %% minus extension - gen(Mode, ParseD, ?A(Mod), Path), - ok. - -mod(File, error) -> - filename:rootname(filename:basename(File)); -mod(_, {ok, Mod}) -> - Mod. - -gen(dict, ParseD, _Mod, Path) -> - write_term(Path ++ ".D", [?VERSION | ParseD]); - -gen(hrl, ParseD, Mod, Path) -> - write(Path ++ ".hrl", gen_hrl(Mod, ParseD)); +gen(dict, ParseD, _Mod) -> + [?VERSION | ParseD]; -gen(forms, ParseD, Mod, Path) -> - write_term(Path ++ ".F", [erl_forms(Mod, ParseD)]); +gen(hrl, ParseD, Mod) -> + gen_hrl(Mod, ParseD); -gen(erl, ParseD, Mod, Path) -> - write(Path ++ ".erl", [header(), prettypr(erl_forms(Mod, ParseD)), $\n]). +gen(forms, ParseD, Mod) -> + erl_forms(Mod, ParseD); -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]), - file:close(Fd). +gen(erl, ParseD, Mod) -> + [header(), prettypr(erl_forms(Mod, ParseD)), $\n]. erl_forms(Mod, ParseD) -> Forms = [[{?attribute, module, Mod}, diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl index 36a6efa294..eef0cb26d4 100644 --- a/lib/diameter/src/compiler/diameter_dict_util.erl +++ b/lib/diameter/src/compiler/diameter_dict_util.erl @@ -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(). diff --git a/lib/diameter/src/compiler/diameter_make.erl b/lib/diameter/src/compiler/diameter_make.erl index e4486973dd..c883eb91a9 100644 --- a/lib/diameter/src/compiler/diameter_make.erl +++ b/lib/diameter/src/compiler/diameter_make.erl @@ -39,30 +39,40 @@ -export_type([opt/0]). +%% Options passed to codec/2. -type opt() :: {include|outdir|name|prefix|inherits, string()} + | return | verbose | debug. +%% Literal dictionary or path. A NL of CR identifies the former. +-type dict() :: iolist() + | binary(). + +%% Name of a literal dictionary if otherwise unspecified. +-define(DEFAULT_DICT_NAME, "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, Ret} | {error, Reason} - when Path :: string(), + when File :: dict(), + Ret :: list(), %% [Erl, Hrl | Debug], Debug = [] | [ParseD, Forms] Reason :: string(). codec(File, Opts) -> - case dict(File, Opts) of - {ok, Dict} -> - make(File, - Opts, - Dict, - lists:append([[dict, forms] || lists:member(debug, Opts)]) - ++ [erl, hrl]); + case to_dict(File, Opts) of + {ok, {Dict, Dictish}} -> + make(file(Dictish), Opts, Dict); {error, _} = E -> E end. @@ -70,30 +80,63 @@ codec(File, Opts) -> codec(File) -> codec(File, []). +file({path, Path}) -> + Path; +file(_) -> + ?DEFAULT_DICT_NAME. + %% dict/2 %% %% Parse a dictionary file and return the orddict that a codec module %% returns from dict/0. --spec dict(string(), [opt()]) +-spec dict(File, [opt()]) -> {ok, orddict:orddict()} - | {error, string()}. + | {error, string()} + when File :: dict(). -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)} +dict(File, Opts) -> + case to_dict(File, Opts) of + {ok, {Dict, _}} -> + {ok, Dict}; + {error, _} = E -> + E end. dict(File) -> dict(File, []). +%% to_dict/2 + +to_dict(File, Opts) -> + Dictish = maybe_path(File), + case diameter_dict_util:parse(Dictish, Opts) of + {ok, Dict} -> + {ok, {Dict, Dictish}}; + {error = E, Reason} -> + {E, diameter_dict_util:format_error(Reason)} + end. + +maybe_path(File) -> + Bin = iolist_to_binary([File]), + case is_path(Bin) of + true -> {path, File}; + false -> Bin + 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) || <> <= Bin, $\n == C orelse $\r == C], + true + catch + throw:_ -> false + end. + %% format/1 %% -%% Turn an orddict returned by dict/1-2 back into a dictionary file -%% in the form of an iolist(). +%% Turn an orddict returned by dict/1-2 back into a dictionary. -spec format(orddict:orddict()) -> iolist(). @@ -108,7 +151,7 @@ format(Dict) -> -spec reformat(File) -> {ok, iolist()} | {error, Reason} - when File :: string(), + when File :: dict(), Reason :: string(). reformat(File) -> @@ -121,12 +164,24 @@ reformat(File) -> %% =========================================================================== -make(_, _, _, []) -> +make(File, Opts, Dict) -> + ok(lists:foldl(fun(M,A) -> [make(File, Opts, Dict, M) | A] end, + [], + lists:append([[dict, forms] || lists:member(debug, Opts)]) + ++ [erl, hrl])). +%% The order in which results are generated (dict/forms/erl/hrl) is +%% intentional, in order of more processing (except for hrl, which +%% isn't needed by diameter itself), since an error raises an +%% exception. The order of return results is the reverse. + +ok([ok,_|_]) -> ok; -make(File, Opts, Dict, [Mode | Rest]) -> +ok([_,_|_] = L) -> + {ok, 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()}) -- cgit v1.2.3