Packages

Erlang RPC stub generator 'erpcgen'

Current section

Files

Jump to
erpcgen src erpcgen.erl
Raw

src/erpcgen.erl

%% Copyright (c) 2000 Sendmail, Inc. All rights reserved.
%%
%% Compile xdr files into erlang modules
%%
%% Input: proto.x
%%
%% Output: [ proto_clnt.erl ]
%% [ proto_svc.erl ]
%% [ proto_xdr.erl ]
%% [ proto.hrl ]
%% [ proto_stub.erl ]
%%
%% stubb_server.erl.gen
%% stubb_client.erl.gen
%%
%% options: hrl -- generate proto.hrl
%% clnt -- generate proto_clnt.erl
%% svc -- generate proto_svc.erl, user impl. gen_server
%% svc_callback -- generate proto_svc.erl, user impl. rpc_server
%% xdr -- generate proto_xdr.erl
%% trace -- make stubs profilable via 'trace' option
%% xdr_inc -- generate proto_xdr.hrl
%% svc_stub -- generate server server.erl.stub
%% client -- [hrl,clnt,xdr]
%% server -- [hrl,svc,xdr,svc_stub]
%% xdrlib -- [hrl,xdr]
%% all -- [hrl,clnt,svc,xdr,svc_stub]
%%
%%
-module(erpcgen).
-behavior(application).
-export([start/0, start/2]).
-export([file/1, file/2, file/3]).
-import(lists, [map/2, filter/2, foreach/2, reverse/1]).
-import(lists, [member/2, keysearch/3, concat/1, flatten/1]).
-import(xdrgen, [genname/2]).
-import(xdrgen, [mkfun/1, mkcall/2, mkcall/3, mkcase/2, mkif/1, mkvar/1]).
-import(xdrgen, [mkatom/1, mkatom/2, mkint/1, mkfloat/1, mkop/3, mkop/2]).
-import(xdrgen, [mkclause/3, mktuple/1, mkcons/2, mknil/0, mklist/1]).
-import(xdrgen, [mkmatch/2, mkblock/1, mkmodule/2, mkexport/2]).
-import(xdrgen, [mkfunction/3]).
%% Called by application startup.
start() -> start(normal, []).
start(normal, []) ->
{ok, [[InFile]]} = init:get_argument(infile),
{ok, [[OutFile]]} = init:get_argument(outfile),
{ok, [[OptionsArg]]} = init:get_argument(options),
{ok, Scan, _} = erl_scan:string(OptionsArg),
{ok, OptList} = erl_parse:parse_term(Scan),
case file(list_to_atom(InFile), list_to_atom(OutFile), OptList) of
[ok,ok,ok,ok,ok] ->
%% io:format("Compilation of ~s successful\n", [InFile]),
{error, 'Compilation successful'}; % For application behavior.
Error ->
%% Error is too complex to figure out. Let user figure it out.
{error, 'Compilation failed'} % For application behavior.
end.
file(File) ->
file(File, [all]).
file(File, Opts) ->
file(File, File, Opts).
file(In, Out, Opts) ->
file1(In, Out, trans_opts(Opts)).
file1(In, Out, {error,Reason}) -> {error, Reason};
file1(In, Out, {ok, Opts}) when is_atom(In), is_atom(Out) ->
File = atom_to_list(In) ++ ".x",
Base = atom_to_list(Out),
case xdr_scan:file(File) of
{error, {Line,Where,Reason}} ->
Message = xdr_scan:format_error(Reason),
io:format("~s:~w : ~s~n", [File, Line, Message]),
{error, {Line,Where,Reason}};
{error, Reason} ->
{error, Reason};
Tokens ->
case xdr_parse:parse(Tokens) of
{error, {Line,Where,Reason}} ->
Message = xdr_parse:format_error(Reason),
io:format("~s:~w : ~s~n", [File, Line,Message]),
{error, {Line,Where,Reason}};
{ok, Spec} ->
case catch transform(Spec,File) of
{ok,Spec1, Env} ->
generate(Base, Spec1, Env, Opts);
error ->
{error, File}
end
end
end.
generate(Base, Spec, Env, Opts) ->
map(
fun(hrl) -> gen_hrl(Base, Spec, Env, Opts);
(clnt) -> gen_clnt(Base, Spec, Env, Opts);
(svc) -> gen_svc(Base, Spec, Env, Opts, gen_server);
(svc_callback) -> gen_svc(Base, Spec, Env, Opts, rpc_server);
(xdr) -> gen_xdr(Base, Spec, Env, Opts);
(xdr_inc) -> gen_xdr_inc(Base, Spec, Env, Opts);
(svc_stub) -> gen_stub(Base, Spec, Env, Opts);
(_) -> ok
end, Opts).
trans_opts(Opts) ->
tr_opts(Opts, []).
%% check and expand options
tr_opts([Opt | Opts], L) ->
case Opt of
all -> tr_opts(Opts, add_opts([hrl,clnt,svc,xdr,svc_stub],L));
client -> tr_opts(Opts, add_opts([hrl,clnt,xdr],L));
server -> tr_opts(Opts, add_opts([hrl,svc,xdr,svc_stub],L));
xdrlib -> tr_opts(Opts, add_opts([hrl,xdr],L));
hrl -> tr_opts(Opts, add_opt(hrl,L));
clnt -> tr_opts(Opts, add_opt(clnt,L));
svc -> tr_opts(Opts, add_opt(svc,L));
svc_callback -> tr_opts(Opts, add_opt(svc_callback,L));
xdr -> tr_opts(Opts, add_opt(xdr,L));
xdr_inc -> tr_opts(Opts, add_opt(xdr_inc,L));
trace -> tr_opts(Opts, add_opt(trace,L));
svc_stub -> tr_opts(Opts, add_opt(svc_stub,L));
_ -> {error, {option, Opts}}
end;
tr_opts([], L) ->
case lists:member(svc, L) and lists:member(svc_callback, L) of
true -> {error, {option, [svc, svc_callback]}};
false -> {ok,L}
end.
add_opt(Opt, L) when is_atom(Opt) ->
case member(Opt, L) of
true -> L;
false -> [Opt | L]
end.
add_opts([Opt|Opts], L) ->
add_opts(Opts, add_opt(Opt, L));
add_opts([], L) -> L.
emit_list(L) ->
io:format("------------------------------------\n"),
foreach(fun(E) ->
io:format("~p~n", [E])
end, L).
%%
%% Month conversion
%%
month(1) -> "Jan";
month(2) -> "Feb";
month(3) -> "Mar";
month(4) -> "Apr";
month(5) -> "May";
month(6) -> "Jun";
month(7) -> "Jul";
month(8) -> "Aug";
month(9) -> "Sep";
month(10) -> "Oct";
month(11) -> "Nov";
month(12) -> "Dec".
%%
%% emit header
%%
gen_header(Fd, Module) ->
io:format(Fd, "%%\n%% ~s was generated by erpcgen (do not edit)\n",
[Module]),
{YY,MM,DD} = date(),
{H,M,S} = time(),
io:format(Fd, "%% date: ~s ~w ~2.2.0w:~2.2.0w:~2.2.0w ~w~n",
[month(MM), DD, H, M, S, YY]),
io:format(Fd, "%%\n", []).
%%
%% Generate module for encode/decoder
%%
emit_fun(Fd, Func) ->
io:put_chars(Fd, "\n"),
io:put_chars(Fd, erl_pp:form(Func)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%
%% Generate XDR File
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
gen_xdr(Base, Spec, Env, Opts) ->
gen_xdr_base(Base, Spec, Env, Opts, mod).
gen_xdr_inc(Base, Spec, Env, Opts) ->
gen_xdr_base(Base, Spec, Env, Opts, inc).
gen_xdr_base(Base, Spec, Env, Opts, Type) ->
Module = Base ++ "_xdr",
File = if Type == mod -> Module ++ ".erl";
Type == inc -> Module ++ ".hrl"
end,
case file:open(File, write) of
{ok, Fd} ->
if Type == mod ->
gen_header(Fd, Module),
io:format(Fd, "-module(~s).~n", [Module]),
case lists:member(trace, Opts) of
true -> io:format(Fd, "-compile([verbose, "
"report_errors, report_warnings, "
"trace]).~n", []);
_ -> skip_it
end;
Type == inc ->
ok
end,
gen_xdr_base(Fd, Base, Spec, Type),
file:close(Fd);
{error, Reason} ->
io:format("WARNING: could not open ~s for write~n", [File]),
{error, Reason}
end.
gen_xdr_base(Fd, Base, Spec, Type) ->
if Type == mod ->
foreach(
fun({type,Id,_}) ->
io:format(Fd, "-export([enc_~s/1, dec_~s/2]).~n",[Id,Id]);
(_) ->
true
end, Spec);
Type == inc ->
ok
end,
put(type_module, []),
foreach(
fun({type,Id,Type}) ->
[Enc] = xdrgen:encode({type,Id,Type}, []),
emit_fun(Fd, Enc),
foreach(fun(Dec) -> emit_fun(Fd, Dec) end,
xdrgen:decode({type,Id,Type}, []));
(_) ->
true
end, Spec),
gen_map_elem(get(map_elem), Fd),
gen_io_list_len(get(io_list_len), Fd),
gen_enc_align(get(enc_align), Fd),
gen_align(get(align), Fd).
gen_map_elem(true, Fd) ->
io:format(Fd,
"\nmap_elem(Fun, Bin, Off, infinity, N) ->\n"
" map_elem0(Fun, Bin, Off, N, []);\n"
"map_elem(Fun, Bin, Off, Max, N) when N =< Max ->\n"
" map_elem0(Fun, Bin, Off, N, []).\n"
"\n"
"map_elem0(Fun, Bin, Off, 0, L) ->\n"
" {lists:reverse(L,[]), Off};\n"
"map_elem0(Fun, Bin, Off, N, L) ->\n"
" {E,Off1} = Fun(Bin, Off),\n"
"map_elem0(Fun, Bin, Off1, N-1, [E|L]).\n", []);
gen_map_elem(_, _Fd) ->
ok.
gen_io_list_len(true, Fd) ->
io:format(Fd,
"\nio_list_len(L) -> io_list_len(L, 0).\n"
"io_list_len([H|T], N) ->\n"
" if\n"
" H >= 0, H =< 255 -> io_list_len(T, N+1);\n"
" is_list(H) -> io_list_len(T, io_list_len(H,N));\n"
" is_binary(H) -> io_list_len(T, size(H) + N);\n"
" true -> exit({xdr, opaque})\n"
" end;\n"
"io_list_len(H, N) when is_binary(H) ->\n"
" size(H) + N;\n"
"io_list_len([], N) ->\n"
"N.\n", []);
gen_io_list_len(_, _Fd) ->
ok.
gen_enc_align(true, Fd) ->
io:format(Fd,
"\nenc_align(Len) ->\n"
" case Len rem 4 of\n"
" 0 -> <<>>;\n"
" 1 -> <<0,0,0>>;\n"
" 2 -> <<0,0>>;\n"
" 3 -> <<0>>\n"
" end.\n", []);
gen_enc_align(_, _Fd) ->
ok.
gen_align(true, Fd) ->
io:format(Fd,
"\nalign(Len) ->\n"
" case Len rem 4 of\n"
" 0 -> Len;\n"
" 1 -> Len+3;\n"
" 2 -> Len+2;\n"
" 3 -> Len+1\n"
" end.\n", []);
gen_align(_, _Fd) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%
%% Generate CLNT File
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
gen_clnt(Base, Spec, Env, Opts) ->
Module = Base ++ "_clnt",
File = Module ++ ".erl",
case file:open(File, write) of
{ok, Fd} ->
gen_header(Fd, Module),
io:format(Fd, "-module(~s).~n", [Module]),
case lists:member(trace, Opts) of
true -> io:format(Fd, "-compile([verbose, report_errors, report_warnings, trace]).~n", []);
_ -> skip_it
end,
io:format(Fd, "-include(\"~s\").~n", [Base ++ ".hrl"]),
gen_clnt(Fd, Base, Spec),
file:close(Fd);
{error, Reason} ->
io:format("WARNING: could not open ~s for write~n", [File]),
{error, Reason}
end.
gen_clnt(Fd, Base, Spec) ->
foreach(
fun ({program,_,Prog,Vs}) ->
foreach(
fun({version,_,Ver,Ps}) ->
foreach(
fun({procedure,Id,Proc,_,As}) ->
Call = genname(Id,Ver),
io:format(Fd, "-export([~s/~w,~s/~w]).\n",
[Call,length(As)+1,
Call,length(As)+2])
end, Ps)
end, Vs);
(_) ->
true
end, Spec),
put(type_module, list_to_atom(concat([Base,"_xdr"]))),
foreach(
fun ({program,Id,Prog,Vers}) ->
Es = xdrgen:clnt({program,Id,Prog,Vers}, []),
foreach(fun(F) -> emit_fun(Fd, F) end, Es);
(_) ->
true
end, Spec).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%
%% Generate SVC
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
gen_svc(Base, Spec, Env, Opts, Type) ->
Module = Base ++ "_svc",
File = Module ++ ".erl",
case file:open(File, write) of
{ok, Fd} ->
gen_header(Fd, Module),
io:format(Fd, "-module(~s).~n", [Module]),
case lists:member(trace, Opts) of
true -> io:format(Fd, "-compile([verbose, report_errors, report_warnings, trace]).~n", []);
_ -> skip_it
end,
io:format(Fd, "-include(\"~s\").~n", [Base ++ ".hrl"]),
gen_svc(Fd, Base, Spec, Type),
file:close(Fd);
{error, Reason} ->
io:format("WARNING: could not open ~s for write~n", [File]),
{error, Reason}
end.
%%
%% Generate:
%% prog_ver1(Args) ->
%% call prog_ver1:init(Args) -- {reply, Reply, State0}
%% prog_ver1(Reason,StateN) ->
%% call prog_ver1:terminate(Reason, StateN) -- ignore reply
%%
%% prog_ver1(Proc,Params,Client) -> ...
%%
%% prog_ver2(Proc,Params,Client) -> ...
%% ...
%% prog_verN(Proc,Params,Client) -> ...
%% ...
%%
%% If Type == gen_server:
%% implementation gen_server must be locally registred as:
%% prog_server
%%
%% calls are made like:
%% gen_server:call(prog_server, {proc_1}, infinity)
%% gen_server:call(prog_server, {proc_2, Args}, infinity)
%% gen_server:call(prog_server, {proc_2, Args}, infinity)
%%
%% If Type == rpc_server:
%%
%% implementation module must be called:
%% prog_server
%% calls are made like:
%% prog_server(proc_1, Clnt, S)
%% prog_server(proc_2, Args..., Clnt, S)
%% prog_server(proc_3, Args..., Clnt, S)
%%
gen_svc(Fd, Base, Spec, Type) ->
foreach(
fun ({program,Id,Prog,Vs}) ->
foreach(
fun({version,_,Ver,Ps}) ->
ProgN = genname(Id,Ver),
io:format(Fd, "-export([~s/5]).\n", [ProgN])
end, Vs);
(_) ->
true
end, Spec),
io:format(Fd, "-export([init/1, handle_call/3, handle_cast/2, \n"
" handle_info/2, terminate/2]).\n", []),
put(type_module, list_to_atom(concat([Base,"_xdr"]))),
Fs = xdrgen:svc_gen_funcs(Type, Base, []),
foreach(fun(F) -> emit_fun(Fd, F) end, Fs),
foreach(
fun ({program,Id,Pn,Prog}) ->
Es = xdrgen:svc_prog({program,Id,Pn,Prog}, Type, Base, []),
foreach(fun(F) -> emit_fun(Fd, F) end, Es);
(_) ->
true
end, Spec).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% x_xdr.hrl
%% INCLUDES:
%% defines for RPC procedure numbers.
%% data type records?
%%
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
gen_hrl(Base, Spec, Env, Opts) ->
File = Base ++ ".hrl",
case file:open(File, write) of
{ok, Fd} ->
gen_header(Fd, File),
gen_hrl(Fd, Base, Spec, Env, Opts),
file:close(Fd);
{error, Reason} ->
io:format("WARNING: could not open ~s for write~n", [File]),
{error, Reason}
end.
%%
%% emit all constant as -define(CONSTANT, Value).
%%
gen_hrl(Fd, Base, Spec, Env, Opts) ->
foreach(
fun ({program,Pid,Prog,Vs}) ->
io:format(Fd, "-define(~s, ~w).~n",[Pid,Prog]),
foreach(
fun({version,Vid,Ver,Ps}) ->
io:format(Fd, "-define(~s, ~w).~n",[Vid,Ver])
end, Vs);
(_) ->
true
end, Spec),
%% emit define and record. (unions?)
foreach(
fun({Id,const,Value}) ->
io:format(Fd, "-define(~s, ~w).\n",[Id,Value]);
%% FIXME: think this over - these are not used, the xdr routines can't handle
%% these records.
% ({Id,type,{struct,[{Eid1,T1} | Es]}}) ->
% io:format(Fd, "\n-record(~s,\n\t{\n\t ~s", [Id, Eid1]),
% foreach(
% fun({Eid,T}) ->
% io:format(Fd, ",\n\t ~s", [Eid])
% end, Es),
% io:format(Fd, "\n\t}).\n\n", []);
(_) -> ok
end, Env).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%%
%% Generate SVC Stubb file(s)
%% one for each program version.
%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
gen_stub(Base, Spec, Env, Opts) ->
nyi.
%%
%% Transform + Verify
%%
error(Line,Fmt,As) ->
Mess = flatten(io_lib:format(Fmt, As)),
io:format("~s:~w: error : ~s~n", [get(infile), Line, Mess]),
put(errors, get(errors)+1).
%% format a type
fmt_type(int) -> "int";
fmt_type(unsigned_int) -> "unsigned int";
fmt_type(hyper) -> "hyper";
fmt_type(unsigned_hyper) -> "unsigned hyper";
fmt_type(bool) -> "bool";
fmt_type(void) -> "void";
fmt_type(float) -> "float";
fmt_type(double) -> "double";
fmt_type(string) -> "string";
fmt_type(opaque) -> "opaque";
fmt_type({struct,Elems}) -> "struct ...";
fmt_type({union,{DT,Elems}}) -> "union ...";
fmt_type({optional,T}) -> fmt_flatt("*~s", [fmt_type(T)]);
fmt_type({varray,infinity,T}) -> fmt_flatt("~s<>", [fmt_type(T)]);
fmt_type({varray,N,T}) -> fmt_flatt("~s<~w>", [fmt_type(T),N]);
fmt_type({array,N,T}) -> fmt_flatt("~s[~w]", [fmt_type(T),N]);
fmt_type({type,Id}) -> Id.
fmt_flatt(Fmt, Args) ->
flatten(io_lib:format(Fmt, Args)).
%%
%% 1 remove line numbers.
%% 2 check errors.
%%
transform(Spec, File) ->
put(infile, File),
put(errors, 0),
{Spec1,Env} = trans(Spec),
case get(errors) of
0 -> {ok, Spec1, Env};
N -> error
end.
%%
%% Verify some aspects of specs
%%
trans(Spec) ->
trans(Spec, [], []).
trans([{const,Line,Id,Value} | Fs], S0, Env) ->
case lookup(Id, Env) of
false ->
trans(Fs, S0, insert(Id,const,Value,Env));
_ ->
error(Line, "identifier ~s multiply defined", [Id]),
trans(Fs, S0, Env)
end;
trans([{typedef,Line,Id,Type} | Fs], S0, Env) ->
case lookup(Id, Env) of
false ->
Env1 = insert(Id,type,current,Env),
Type1 = trans_type(Type, Env1),
trans(Fs, [{type,Id,Type1}|S0], insert(Id,type,Type1,Env));
{Id,type,T} ->
error(Line, "type ~s multiply defined", [Id]),
trans(Fs, S0, Env);
_ ->
error(Line, "identifier ~s multiply defined", [Id]),
trans(Fs, S0, Env)
end;
trans([{program,Line,Id,ProgNo,Vers} | Fs], S0, Env) ->
case lookup(Id, Env) of
false ->
Prog1 = trans_vers(Vers, Env),
trans(Fs, [{program,Id,ProgNo,Prog1}|S0],
insert(Id,program,{ProgNo,Prog1},Env));
{_,_,_} ->
error(Line, "identifier ~s multiply defined", [Id]),
trans(Fs, S0, Env)
end;
trans([], S0, Env) ->
{ reverse(S0), Env }.
trans_vers([{version,Line,Id,VersNo,Procs} | Vs], Env) ->
Procs1 = trans_procs(Procs, Env),
[{version,Id,VersNo,Procs1} | trans_vers(Vs,Env)];
trans_vers([], Env) -> [].
trans_procs([{procedure,Line,Id,ProcNo,Ret,Args} | Ps], Env) ->
Ret1 = trans_type(Ret, Env),
Args1 = trans_type_list(Args, Env),
[{procedure,Id,ProcNo,Ret1,Args1} | trans_procs(Ps, Env)];
trans_procs([], Env) -> [].
trans_type_list([T | Ts], Env) ->
[ trans_type(T, Env) | trans_type_list(Ts,Env)];
trans_type_list([], Env) -> [].
trans_type({struct, _, Elems}, Env) ->
{struct, trans_struct_elems(Elems, [], Env)};
trans_type({union, Line, {{Did,DLine,Disc}, Elems}}, Env) ->
Disc1 = trans_type(Disc,Env),
DT = trans_disc_type(Disc1, Line, Env),
Elems1 = trans_union_elems(Elems, [], [], [], Env, DT),
{union, {{Did,DT},Elems1}};
trans_type({enum, _, Enums}, Env) ->
{enum, trans_enum(Enums, Env)};
trans_type({array, Line, N, Type}, Env) ->
N1 = trans_value(N, Env),
if N1 < 0 -> error(Line, "bad array size ~w", [N]);
true -> true
end,
Type1 =
if Type == opaque -> Type;
true -> trans_type(Type, Env)
end,
{array, N1, Type1};
trans_type({varray,Line,Max,Type}, Env) ->
Max1 = if
Max == infinity ->
infinity;
true ->
Max2 = trans_value(Max, Env),
if Max2 < 0 ->
error(Line, "bad dynamic array max ~w", [Max]);
true -> true
end,
Max2
end,
Type1 = if
Type == opaque -> opaque;
Type == string -> string;
true -> trans_type(Type, Env)
end,
{varray, Max1, Type1};
trans_type({int,_}, Env) -> int;
trans_type({unsigned_int,_},Env) -> unsigned_int;
trans_type({hyper,_},Env) -> hyper;
trans_type({unsigned_hyper,_},Env) -> unsigned_hyper;
trans_type({float,_},Env) -> float;
trans_type({double,_},Env) -> double;
trans_type({bool,_},Env) -> bool;
trans_type({void,_},Env) -> void;
trans_type({type,Line,Id}, Env) when is_list(Id) ->
case lookup(Id, Env) of
{_,type,Type1} -> true;
_ -> error(Line, "type ~s undefined", [Id])
end,
{type, Id};
trans_type({optional,Line,Type}, Env) ->
{optional, trans_type(Type, Env)}.
%% trans_type({optional,Line,Type}, Env) ->
%% Type1 = trans_type(Type, Env),
%% {union, {{"$opted", bool},
%% [{ {true, 1}, {"$element", Type1}},
%% { {false,0}, {"", void} }]}}.
%%
%% Check struct elements
%%
trans_struct_elems([{Id, Line, Type} | Elems], Ids, Env) ->
Type1 = trans_type(Type, Env),
case member(Id, Ids) of
true ->
error(Line, "struct member ~s multiply defined", [Id]),
[{Id,Type1} | trans_struct_elems(Elems, Ids, Env)];
false ->
[{Id,Type1} | trans_struct_elems(Elems, [Id|Ids], Env)]
end;
trans_struct_elems([], _, _) -> [].
%%
%% Check union elements
%% The Tag is translated as Tag -> {Tag,Value}
%%
trans_union_elems([{Tag,Line,{Id,_,Type}} | Elems],Tags,Ids,Vals,Env,Disc) ->
Tag1 = trans_tag(Tag),
case member(Tag1, Tags) of
true -> error(Line, "union tag ~w multiply defined", [Tag1]);
false -> true
end,
Ids1 =
if Id == [] -> Ids;
true ->
case member(Id, Ids) of
true -> error(Line, "union id ~s multiply defined", [Id]),
Ids;
false -> [Id | Ids]
end
end,
Type1 = trans_type(Type, Env),
Tag2 = {_,Val} = trans_tag_type(Tag1, Line, Env, Disc),
Vals1 = case member(Val, Vals) of
true -> error(Line, "case ~w multiply defined", [Val]), Vals;
false -> [Val | Vals]
end,
[{ Tag2, {Id, Type1}} |
trans_union_elems(Elems, [Tag|Tags], Ids1, Vals1, Env, Disc)];
trans_union_elems([], _, _, _, _,_) -> [].
trans_tag({identifier,_,Id}) -> Id;
trans_tag({integer,_,Value}) -> Value;
trans_tag(default) -> default.
%%
%% Translate Tag into -> {Tag,Value}
%%
trans_tag_type("TRUE", _, _, bool) -> {true,1};
trans_tag_type("FALSE", _, _, bool) -> {false,0};
trans_tag_type(N, _, _, int) when is_integer(N) -> {N,N};
trans_tag_type(N, Line, _, unsigned_int) when is_integer(N) ->
if N < 0 -> error(Line, "bad tag ~w for unsigned type", [N]);
true -> true
end,
{N,N};
trans_tag_type(default, _, _, _) -> {default,default};
trans_tag_type(Tag, Line,_,{enum,Nums}) ->
case keysearch(Tag, 1, Nums) of
{value,{_,Value}} ->
{Tag,Value};
false ->
error(Line, "tag ~p is not an enumerated value", [Tag]),
{Tag,0}
end;
trans_tag_type(Tag,Line,Env,{type,Id}) ->
{_, type, T} = lookup(Id, Env),
trans_tag_type(Tag,Line,Env,T);
trans_tag_type(_, _, _, _) -> {0,0}.
trans_disc_type(int, _, Env) -> int;
trans_disc_type(unsigned_int, _, Env) -> unsigned_int;
trans_disc_type(bool, _, Env) -> bool;
trans_disc_type({enum,Nums}, _, Env) -> {enum,Nums};
trans_disc_type({type,Id}, Line, Env) ->
case lookup(Id, Env) of
{_,type,T} ->
trans_disc_type(T, Line, Env),
{type,Id};
false ->
error(Line, "type ~s undefined", [Id]),
int
end;
trans_disc_type(T, Line, Env) ->
error(Line, "type ~s is not a valid discriminator type", [fmt_type(T)]).
%%
%% Check uniqness if enums (1-1)
%% i.e exactly one of each identifier
%% exactly one of each value
%%
trans_enum(Enums, Env) ->
trans_enums(Enums, Env, []).
trans_enums([{Tag,Line,Value} | Es], Env, Ids) ->
V = trans_value(Value, Env),
case member(Tag, Ids) of
true -> error(Line, "enumeration ~s multiply defined", [Tag]);
false -> true
end,
[{Tag,V} | trans_enums(Es, Env, [Tag|Ids])];
trans_enums([], _, _) -> [].
trans_value({integer,_,Value}, Env) -> Value;
trans_value({identifier,Line,Id}, Env) ->
case lookup(Id, Env) of
{_,const,Value} -> Value;
false -> error(Line, "constant ~s undefined", [Id]), 0
end.
insert(Id,Type,Value,Env) ->
[{Id,Type,Value} | Env].
lookup(Id, [{Id,Type,Value}|_]) -> {Id,Type,Value};
lookup(Id, [_|Env]) -> lookup(Id, Env);
lookup(Id, []) -> false.