%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% -module(ictype). -include("ic.hrl"). -include("icforms.hrl"). %%----------------------------------------------------------------- %% External exports %%----------------------------------------------------------------- -export([type_check/2, scoped_lookup/4, maybe_array/5, to_uppercase/1]). -export([name2type/2, member2type/3, isBasicTypeOrEterm/3, isEterm/3]). -export([isBasicType/1, isBasicType/2, isBasicType/3, isString/3, isWString/3, isArray/3, isStruct/3, isUnion/3, isEnum/3, isSequence/3, isBoolean/3 ]). -export([fetchTk/3, fetchType/1, tk/4]). %%----------------------------------------------------------------- %% Internal exports %%----------------------------------------------------------------- -export([]). %%----------------------------------------------------------------- %% Macros %%----------------------------------------------------------------- %%-define(DBG(F,A), io:format(F,A)). -define(DBG(F,A), true). -define(STDDBG, ?DBG(" dbg: ~p: ~p~n", [element(1,X), ic_forms:get_id2(X)])). %%----------------------------------------------------------------- %% External functions %%----------------------------------------------------------------- type_check(G, Forms) -> S = ic_genobj:tktab(G), check_list(G, S, [], Forms). scoped_lookup(G, S, N, X) -> Id = ic_symtab:scoped_id_strip(X), case ic_symtab:scoped_id_is_global(X) of true -> lookup(G, S, [], X, Id); false -> lookup(G, S, N, X, Id) end. %%-------------------------------------------------------------------- %% maybe_array %% %% Array declarators are indicated on the declarator and not on %% the type, therefore the declarator decides if the array type %% kind is added or not. %% maybe_array(G, S, N, X, TK) when is_record(X, array) -> mk_array(G, S, N, X#array.size, TK); maybe_array(_G, _S, _N, _, TK) -> TK. name2type(G, Name) -> S = ic_genobj:tktab(G), ScopedName = lists:reverse(string:tokens(Name, "_")), InfoList = ets:lookup(S, ScopedName ), filter( InfoList ). %% This is en overloaded function, %% differs in input on unions member2type(_G, X, I) when is_record(X, union)-> Name = ic_forms:get_id2(I), case lists:keysearch(Name,2,element(6,X#union.tk)) of false -> error; {value,Rec} -> fetchType(element(3,Rec)) end; member2type( G, SName, MName ) -> S = ic_genobj:tktab( G ), SNList = lists:reverse(string:tokens(SName,"_")), ScopedName = [MName | SNList], InfoList = ets:lookup( S, ScopedName ), case filter( InfoList ) of error -> %% Try a little harder, seeking inside tktab case lookup_member_type_in_tktab(S, ScopedName, MName) of error -> %% Check if this is the "return to return1" case case MName of "return1" -> %% Do it all over again ! ScopedName2 = ["return" | SNList], InfoList2 = ets:lookup( S, ScopedName2 ), case filter( InfoList2 ) of error -> %% Last resort: seek in pragma table lookup_type_in_pragmatab(G, SName); Other -> Other end; _ -> %% Last resort: seek in pragma table lookup_type_in_pragmatab(G, SName) end; Other -> Other end; Other -> Other end. lookup_member_type_in_tktab(S, ScopedName, MName) -> case ets:match_object(S, {'_',member,{MName,'_'},nil}) of [] -> error; [{_FullScopedName,member,{MName,TKInfo},nil}]-> fetchType( TKInfo ); List -> lookup_member_type_in_tktab(List,ScopedName) end. lookup_member_type_in_tktab([], _ScopedName) -> error; lookup_member_type_in_tktab([{FullScopedName,_,{_,TKInfo},_}|Rest],ScopedName) -> case lists:reverse(string:tokens(ic_util:to_undersc(FullScopedName),"_")) of ScopedName -> fetchType(TKInfo); _ -> lookup_member_type_in_tktab(Rest,ScopedName) end. lookup_type_in_pragmatab(G, SName) -> S = ic_genobj:pragmatab(G), %% Look locally first case ets:match(S,{file_data_local,'_','_','$2','_','_',SName,'_','_'}) of [] -> %% No match, seek included case ets:match(S,{file_data_included,'_','_','$2','_','_',SName,'_','_'}) of [] -> error; [[Type]] -> io:format("1 Found(~p) : ~p~n",[SName,Type]), Type end; [[Type]] -> io:format("2 Found(~p) : ~p~n",[SName,Type]), Type end. isString(G, N, T) when element(1, T) == scoped_id -> case ic_symtab:get_full_scoped_name(G, N, T) of {_FullScopedName, _, {'tk_string',_}, _} -> true; _ -> false end; isString(_G, _N, T) when is_record(T, string) -> true; isString(_G, _N, _Other) -> false. isWString(G, N, T) when element(1, T) == scoped_id -> %% WSTRING case ic_symtab:get_full_scoped_name(G, N, T) of {_FullScopedName, _, {'tk_wstring',_}, _} -> true; _ -> false end; isWString(_G, _N, T) when is_record(T, wstring) -> true; isWString(_G, _N, _Other) -> false. isArray(G, N, T) when element(1, T) == scoped_id -> case ic_symtab:get_full_scoped_name(G, N, T) of {_FullScopedName, _, {'tk_array', _, _}, _} -> true; _ -> false end; isArray(_G, _N, T) when is_record(T, array) -> true; isArray(_G, _N, _Other) -> false. isSequence(G, N, T) when element(1, T) == scoped_id -> case ic_symtab:get_full_scoped_name(G, N, T) of {_FullScopedName, _, {'tk_sequence', _, _}, _} -> true; _ -> false end; isSequence(_G, _N, T) when is_record(T, sequence) -> true; isSequence(_G, _N, _Other) -> false. isStruct(G, N, T) when element(1, T) == scoped_id -> case ic_symtab:get_full_scoped_name(G, N, T) of {_FullScopedName, _, {'tk_struct', _, _, _}, _} -> true; _ -> false end; isStruct(_G, _N, T) when is_record(T, struct) -> true; isStruct(_G, _N, _Other) -> false. isUnion(G, N, T) when element(1, T) == scoped_id -> case ic_symtab:get_full_scoped_name(G, N, T) of {_FullScopedName, _, {'tk_union', _, _, _,_,_}, _} -> true; _Other -> false end; isUnion(_G, _N, T) when is_record(T, union) -> true; isUnion(_G, _N, _Other) -> false. isEnum(G, N, T) when element(1, T) == scoped_id -> case ic_symtab:get_full_scoped_name(G, N, T) of {_FullScopedName, _, {'tk_enum',_,_,_}, _} -> true; _Other -> false end; isEnum(_G, _N, T) when is_record(T, enum) -> true; isEnum(_G, _N, _Other) -> false. isBoolean(G, N, T) when element(1, T) == scoped_id -> {_, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, T), case fetchType(TK) of 'boolean' -> true; _ -> false end; isBoolean(_, _, {'tk_boolean',_}) -> true; isBoolean(_, _, {'boolean',_}) -> true; isBoolean(_, _, _) -> false. %%% Just used for C isBasicTypeOrEterm(G, N, S) -> case isBasicType(G, N, S) of true -> true; false -> isEterm(G, N, S) end. isEterm(G, N, S) when element(1, S) == scoped_id -> {FullScopedName, _, _TK, _} = ic_symtab:get_full_scoped_name(G, N, S), case ic_code:get_basetype(G, ic_util:to_undersc(FullScopedName)) of "erlang_term" -> true; "ETERM*" -> true; _X -> false end; isEterm(_G, _Ni, _X) -> false. isBasicType(_G, _N, {scoped_id,_,_,["term","erlang"]}) -> false; isBasicType(G, N, S) when element(1, S) == scoped_id -> {_, _, TK, _} = ic_symtab:get_full_scoped_name(G, N, S), isBasicType(fetchType(TK)); isBasicType(_G, _N, {string, _} ) -> false; isBasicType(_G, _N, {wstring, _} ) -> %% WSTRING false; isBasicType(_G, _N, {unsigned, {long, _}} ) -> true; isBasicType(_G, _N, {unsigned, {short, _}} ) -> true; isBasicType(_G, _N, {Type, _} ) -> isBasicType(Type); isBasicType(_G, _N, _X) -> false. isBasicType( G, Name ) -> isBasicType( name2type( G, Name ) ). isBasicType( Type ) -> lists:member(Type, [tk_short,short, tk_long,long, tk_longlong,longlong, %% LLONG tk_ushort,ushort, tk_ulong,ulong, tk_ulonglong,ulonglong, %% ULLONG tk_float,float, tk_double,double, tk_boolean,boolean, tk_char,char, tk_wchar,wchar, %% WCHAR tk_octet,octet, tk_any,any]). %% Fix for any %%----------------------------------------------------------------- %% Internal functions %%----------------------------------------------------------------- check(G, _S, N, X) when is_record(X, preproc) -> handle_preproc(G, N, X#preproc.cat, X), X; check(G, S, N, X) when is_record(X, op) -> ?STDDBG, TK = tk_base(G, S, N, ic_forms:get_type(X)), tktab_add(G, S, N, X), N2 = [ic_forms:get_id2(X) | N], Ps = lists:map(fun(P) -> tktab_add(G, S, N2, P), P#param{tk=tk_base(G, S, N, ic_forms:get_type(P))} end, X#op.params), %% Check for exception defs. Raises = lists:map(fun(E) -> name_lookup(G, S, N, E) end, X#op.raises), case ic_forms:is_oneway(X) of true -> if TK /= tk_void -> ic_error:error(G, {bad_oneway_type, X, TK}); true -> ok end, case ic:filter_params([inout, out], X#op.params) of [] -> ok; % No out parameters! _ -> ic_error:error(G, {oneway_outparams, X}) end, case X#op.raises of [] -> ok; _ -> ic_error:error(G, {oneway_raises, X}) end; false -> ok end, X#op{params=Ps, tk=TK, raises=Raises}; check(G, S, N, X) when is_record(X, interface) -> ?STDDBG, N2 = [ic_forms:get_id2(X) | N], TK = {tk_objref, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X)}, Inherit = inherit_resolve(G, S, N, X#interface.inherit, []), tktab_add(G, S, N, X, TK, Inherit), CheckedBody = check_list(G, S, N2, ic_forms:get_body(X)), InhBody = calc_inherit_body(G, N2, CheckedBody, Inherit, []), X2 = X#interface{inherit=Inherit, tk=TK, body=CheckedBody, inherit_body=InhBody}, ic_symtab:store(G, N, X2), X2; check(G, S, N, X) when is_record(X, forward) -> ?STDDBG, tktab_add(G, S, N, X, {tk_objref, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X)}), X; check(G, S, N, #constr_forward{tk = tk_struct} = X) -> ?STDDBG, ID = ic_forms:get_id2(X), Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")), tktab_add(G, S, N, X, {tk_struct, ictk:get_IR_ID(G, N, X), ID, Module}), X; check(G, S, N, #constr_forward{tk = tk_union} = X) -> ?STDDBG, ID = ic_forms:get_id2(X), Module = list_to_atom(string:join(lists:reverse([ID|N]), "_")), tktab_add(G, S, N, X, {tk_union, ictk:get_IR_ID(G, N, X), ID, [], [], Module}), X; check(G, S, N, X) when is_record(X, const) -> ?STDDBG, case tk_base(G, S, N, ic_forms:get_type(X)) of Err when element(1, Err) == error -> X; TK -> check_const_tk(G, S, N, X, TK), case iceval:eval_const(G, S, N, TK, X#const.val) of Err when element(1, Err) == error -> X; {ok, NewTK, Val} -> V = iceval:get_val(Val), tktab_add(G, S, N, X, NewTK, V), X#const{val=V, tk=NewTK}; Val -> V = iceval:get_val(Val), tktab_add(G, S, N, X, TK, V), X#const{val=V, tk=TK} end end; check(G, S, N, X) when is_record(X, except) -> ?STDDBG, TK = tk(G, S, N, X), X#except{tk=TK}; check(G, S, N, X) when is_record(X, struct) -> ?STDDBG, TK = tk(G, S, N, X), X#struct{tk=TK}; check(G, S, N, X) when is_record(X, enum) -> ?STDDBG, TK = tk(G, S, N, X), X#enum{tk=TK}; check(G, S, N, X) when is_record(X, union) -> ?STDDBG, TK = tk(G, S, N, X), X#union{tk=TK}; check(G, S, N, X) when is_record(X, attr) -> ?STDDBG, TK = tk_base(G, S, N, ic_forms:get_type(X)), XX = #id_of{type=X}, lists:foreach(fun(Id) -> tktab_add(G, S, N, XX#id_of{id=Id}) end, ic_forms:get_idlist(X)), X#attr{tk=TK}; check(G, S, N, X) when is_record(X, module) -> ?STDDBG, tktab_add(G, S, N, X), X#module{body=check_list(G, S, [ic_forms:get_id2(X) | N], ic_forms:get_body(X))}; check(G, S, N, X) when is_record(X, typedef) -> ?STDDBG, TKbase = tk(G, S, N, X), X#typedef{tk=TKbase}; check(_G, _S, _N, X) -> ?DBG(" dbg: ~p~n", [element(1,X)]), X. handle_preproc(G, _N, line_nr, X) -> ic_genobj:set_idlfile(G, ic_forms:get_id2(X)); handle_preproc(_G, _N, _C, _X) -> ok. %%-------------------------------------------------------------------- %% %% TK calculation %% %%-------------------------------------------------------------------- tk(G, S, N, X) when is_record(X, union) -> N2 = [ic_forms:get_id2(X) | N], DisrcTK = tk(G, S, N, ic_forms:get_type(X)), case check_switch_tk(G, S, N, X, DisrcTK) of true -> do_special_enum(G, S, N2, ic_forms:get_type(X)), BodyTK = lists:reverse( tk_caselist(G, S, N2, DisrcTK, ic_forms:get_body(X))), tktab_add(G, S, N, X, {tk_union, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), DisrcTK, default_count(ic_forms:get_body(X)), BodyTK}); false -> tk_void end; tk(G, S, N, X) when is_record(X, enum) -> N2 = [ic_forms:get_id2(X) | N], tktab_add(G, S, N, X, {tk_enum, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), enum_body(G, S, N2, ic_forms:get_body(X))}); %% Note that the TK returned from this function is the base TK. It %% must be modified for each of the identifiers in the idlist (for %% array reasons). tk(G, S, N, X) when is_record(X, typedef) -> case X of %% Special case only for term and java backend ! {typedef,{any,_},[{'',_,"term"}],undefined} -> case ic_options:get_opt(G, be) of java -> tktab_add(G, S, N, X, tk_term), tk_term; _ -> TK = tk(G, S, N, ic_forms:get_body(X)), lists:foreach(fun(Id) -> tktab_add(G, S, N, #id_of{id=Id, type=X}, maybe_array(G, S, N, Id, TK)) end, X#typedef.id), TK end; _ -> TK = tk(G, S, N, ic_forms:get_body(X)), lists:foreach(fun(Id) -> tktab_add(G, S, N, #id_of{id=Id, type=X}, maybe_array(G, S, N, Id, TK)) end, X#typedef.id), TK end; tk(G, S, N, X) when is_record(X, struct) -> N2 = [ic_forms:get_id2(X) | N], tktab_add(G, S, N, X, {tk_struct, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), tk_memberlist(G, S, N2, ic_forms:get_body(X))}); tk(G, S, N, X) when is_record(X, except) -> N2 = [ic_forms:get_id2(X) | N], tktab_add(G, S, N, X, {tk_except, ictk:get_IR_ID(G, N, X), ic_forms:get_id2(X), tk_memberlist(G, S, N2, ic_forms:get_body(X))}); tk(G, S, N, X) -> tk_base(G, S, N, X). tk_base(G, S, N, X) when is_record(X, sequence) -> {tk_sequence, tk(G, S, N, X#sequence.type), len_eval(G, S, N, X#sequence.length)}; tk_base(G, S, N, X) when is_record(X, string) -> {tk_string, len_eval(G, S, N, X#string.length)}; tk_base(G, S, N, X) when is_record(X, wstring) -> %% WSTRING {tk_wstring, len_eval(G, S, N, X#wstring.length)}; %% Fixed constants can be declared as: %% (1) const fixed pi = 3.14D; or %% (2) typedef fixed<3,2> f32; %% const f32 pi = 3.14D; tk_base(G, S, N, X) when is_record(X, fixed) -> %% Case 2 {tk_fixed, len_eval(G, S, N, X#fixed.digits), len_eval(G, S, N, X#fixed.scale)}; tk_base(_G, _S, _N, {fixed, _}) -> %% Case 1 tk_fixed; %% Special case, here CORBA::TypeCode is built in %% ONLY when erl_corba is the backend of choice tk_base(G, S, N, {scoped_id,V1,V2,["TypeCode","CORBA"]}) -> case ic_options:get_opt(G, be) of false -> tk_TypeCode; erl_corba -> tk_TypeCode; erl_template -> tk_TypeCode; _ -> case scoped_lookup(G, S, N, {scoped_id,V1,V2,["TypeCode","CORBA"]}) of T when element(1, T) == error -> T; T when is_tuple(T) -> element(3, T) end end; tk_base(G, S, N, X) when element(1, X) == scoped_id -> case scoped_lookup(G, S, N, X) of T when element(1, T) == error -> T; T when is_tuple(T) -> element(3, T) end; tk_base(_G, _S, _N, {long, _}) -> tk_long; tk_base(_G, _S, _N, {'long long', _}) -> tk_longlong; %% LLONG tk_base(_G, _S, _N, {short, _}) -> tk_short; tk_base(_G, _S, _N, {'unsigned', {short, _}}) -> tk_ushort; tk_base(_G, _S, _N, {'unsigned', {long, _}}) -> tk_ulong; tk_base(_G, _S, _N, {'unsigned', {'long long', _}})-> tk_ulonglong; %% ULLONG tk_base(_G, _S, _N, {float, _}) -> tk_float; tk_base(_G, _S, _N, {double, _}) -> tk_double; tk_base(_G, _S, _N, {boolean, _}) -> tk_boolean; tk_base(_G, _S, _N, {char, _}) -> tk_char; tk_base(_G, _S, _N, {wchar, _}) -> tk_wchar; %% WCHAR tk_base(_G, _S, _N, {octet, _}) -> tk_octet; tk_base(_G, _S, _N, {null, _}) -> tk_null; tk_base(_G, _S, _N, {void, _}) -> tk_void; tk_base(_G, _S, _N, {any, _}) -> tk_any; tk_base(_G, _S, _N, {'Object', _}) -> {tk_objref, "", "Object"}. %%-------------------------------------------------------------------- %% %% Special handling of idlists. Note that the recursion case is given %% as accumulator to foldr. Idlists are those lists of identifiers %% that share the same definition, i.e. multiple cases, multiple type %% declarations, multiple member names. %% tk_memberlist(G, S, N, [X | Xs]) -> BaseTK = tk(G, S, N, ic_forms:get_type(X)), XX = #id_of{type=X}, lists:foldr(fun(Id, Acc) -> [tk_member(G, S, N, XX#id_of{id=Id}, BaseTK) | Acc] end, tk_memberlist(G, S, N, Xs), ic_forms:get_idlist(X)); tk_memberlist(_G, _S, _N, []) -> []. %% same as above but for case dcls tk_caselist(G, S, N, DiscrTK, Xs) -> lists:foldl(fun(Case, Acc) -> BaseTK = tk(G, S, N, ic_forms:get_type(Case)), %% tktab_add for the uniqueness check of the declarator tktab_add(G, S, N, Case), lists:foldl(fun(Id, Acc2) -> case tk_case(G, S, N, Case, BaseTK, DiscrTK, Id) of Err when element(1, Err)==error -> Acc2; TK -> unique_add_case_label(G, S, N, Id, TK, Acc2) end end, Acc, ic_forms:get_idlist(Case)) end, [], Xs). %% Handling of the things that can be in an idlist or caselist tk_member(G, S, N, X, BaseTK) -> tktab_add(G, S, N, X, {ic_forms:get_id2(X), maybe_array(G, S, N, X#id_of.id, BaseTK)}). get_case_id_and_check(G, _S, _N, _X, ScopedId) -> case ic_symtab:scoped_id_is_global(ScopedId) of true -> ic_error:error(G, {bad_scope_enum_case, ScopedId}); false -> ok end, case ic_symtab:scoped_id_strip(ScopedId) of [Id] -> Id; _List -> ic_error:error(G, {bad_scope_enum_case, ScopedId}), "" end. tk_case(G, S, N, X, BaseTK, DiscrTK, Id) -> case case_eval(G, S, N, DiscrTK, Id) of Err when element(1, Err) == error -> Err; Val -> case iceval:check_tk(G, DiscrTK, Val) of true -> {iceval:get_val(Val), ic_forms:get_id2(X), maybe_array(G, S, N, X#case_dcl.id, BaseTK)}; false -> ic_error:error(G, {bad_case_type, DiscrTK, X, iceval:get_val(Val)}) end end. tktab_add(G, S, N, X) -> tktab_add_id(G, S, N, X, ic_forms:get_id2(X), nil, nil). tktab_add(G, S, N, X, TK) -> tktab_add_id(G, S, N, X, ic_forms:get_id2(X), TK, nil). tktab_add(G, S, N, X, TK, Aux) -> tktab_add_id(G, S, N, X, ic_forms:get_id2(X), TK, Aux). tktab_add_id(G, S, N, X, Id, TK, Aux) when is_record(X,enumerator) -> %% Check if the "scl" flag is set to true %% if so, allow old semantics ( errornous ) %% Warning, this is for compatibility reasons only. Name = case ic_options:get_opt(G, scl) of true -> [Id | N]; false -> [Id | tl(N)] end, UName = mk_uppercase(Name), case ets:lookup(S, Name) of [_] -> ic_error:error(G, {multiply_defined, X}); [] -> case ets:lookup(S, UName) of [] -> ok; [_] -> ic_error:error(G, {illegal_spelling, X}) end end, ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK; %% %% Fixes the multiple file module definition check %% but ONLY for Corba backend %% tktab_add_id(G, S, N, X, Id, TK, Aux) when is_record(X,module) -> case ic_options:get_opt(G, be) of erl_template -> Name = [Id | N], UName = mk_uppercase(Name), ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK; erl_corba -> Name = [Id | N], UName = mk_uppercase(Name), ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK; false -> %% default == erl_corba Name = [Id | N], UName = mk_uppercase(Name), ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK; java -> Name = [Id | N], UName = mk_uppercase(Name), ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK; erl_genserv -> Name = [Id | N], UName = mk_uppercase(Name), ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK; erl_plain -> Name = [Id | N], UName = mk_uppercase(Name), ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK; _Be -> Name = [Id | N], UName = mk_uppercase(Name), case ets:lookup(S, Name) of [_] -> ic_error:error(G, {multiply_defined, X}); [] -> case ets:lookup(S, UName) of [] -> ok; [_] -> ic_error:error(G, {illegal_spelling, X}) end end, ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK end; tktab_add_id(G, S, N, X, Id, TK, Aux) -> Name = [Id | N], UName = mk_uppercase(Name), case ets:lookup(S, Name) of [{_, forward, _, _}] when is_record(X, interface) -> ok; [{_, constr_forward, _, _}] when is_record(X, union) orelse is_record(X, struct) -> ok; [XX] when is_record(X, forward) andalso element(2, XX)==interface -> ok; [_] -> ic_error:error(G, {multiply_defined, X}); [] -> case ets:lookup(S, UName) of [] -> ok; [_] -> ic_error:error(G, {illegal_spelling, X}) end end, ets:insert(S, {Name, element(1, get_beef(X)), TK, Aux}), if UName =/= Name -> ets:insert(S, {UName, spellcheck}); true -> true end, TK. %%-------------------------------------------------------------------- %% enum_body %% %% Special because ids are treated different than usual. %% enum_body(G, S, N, [Enum | EnumList]) -> tktab_add(G, S, N, Enum), %%%, enum_val, Enum), %% tktab_add(G, S, N, X, TK, V), [ic_forms:get_id2(Enum) | enum_body(G, S, N, EnumList)]; enum_body(_G, _S, _N, []) -> []. %%-------------------------------------------------------------------- %% mk_array %% %% Multi dimensional arrays are written as nested tk_array %% mk_array(G, S, N, [Sz | Szs], TK) -> case iceval:eval_const(G, S, N, positive_int, Sz) of Err when element(1, Err) == error -> TK; Val -> {tk_array, mk_array(G, S, N, Szs, TK), iceval:get_val(Val)} end; mk_array(_G, _S, _N, [], TK) -> TK. %%-------------------------------------------------------------------- %% len_eval %% %% Evaluates the length, which in case it has been left out is a %% plain 0 (zero) %% len_eval(_G, _S, _N, 0) -> 0; len_eval(G, S, N, X) -> %%iceval:eval_const(G, S, N, positive_int, X). case iceval:eval_const(G, S, N, positive_int, X) of Err when element(1, Err) == error -> 0; Val -> iceval:get_val(Val) end. %%-------------------------------------------------------------------- %% case_eval %% %% Evaluates the case label. %% case_eval(G, S, N, DiscrTK, X) when element(1, DiscrTK) == tk_enum, element(1, X) == scoped_id -> {tk_enum, _, _, Cases} = DiscrTK, Id = get_case_id_and_check(G, S, N, X, X), %%io:format("Matching: ~p to ~p~n", [Id, Cases]), case lists:member(Id, Cases) of true -> {enum_id, Id}; false -> iceval:mk_val(scoped_lookup(G, S, N, X)) % Will generate error end; case_eval(G, S, N, DiscrTK, X) -> iceval:eval_e(G, S, N, DiscrTK, X). %% The enum declarator is in the union scope. do_special_enum(G, S, N, X) when is_record(X, enum) -> tktab_add(G, S, N, #id_of{id=X#enum.id, type=X}); do_special_enum(_G, _S, _N, _X) -> ok. unique_add_case_label(G, _S, _N, Id, TK, TKList) -> %%%io:format("check_case_labels: TK:~p TKLIST:~p ~n", [TK, TKList]), if element(1, TK) == error -> TKList; true -> case lists:keysearch(element(1, TK), 1, TKList) of {value, _} -> ic_error:error(G, {multiple_cases, Id}), TKList; false -> [TK | TKList] end end. %%-------------------------------------------------------------------- %% default_count %% %% Returns the position of the default case. %% %% Modified for OTP-2007 %% default_count(Xs) -> default_count2(Xs, 0). default_count2([X | Xs], N) -> default_count3(X#case_dcl.label, Xs, N); default_count2([], _) -> -1. default_count3([{default, _} | _Ys], _Xs, N) -> N; default_count3([_ | Ys], Xs, N) -> default_count3(Ys, Xs, N+1); default_count3([], Xs, N) -> default_count2(Xs, N). %% %% Type checks. %% %% Check constant type references (only for the scoped id case, others %% are caught by the BNF) %% check_const_tk(_G, _S, _N, _X, tk_long) -> true; check_const_tk(_G, _S, _N, _X, tk_longlong) -> true; %% LLONG check_const_tk(_G, _S, _N, _X, tk_short) -> true; check_const_tk(_G, _S, _N, _X, tk_ushort) -> true; check_const_tk(_G, _S, _N, _X, tk_ulong) -> true; check_const_tk(_G, _S, _N, _X, tk_ulonglong) -> true; %% ULLONG check_const_tk(_G, _S, _N, _X, tk_float) -> true; check_const_tk(_G, _S, _N, _X, tk_double) -> true; check_const_tk(_G, _S, _N, _X, tk_boolean) -> true; check_const_tk(_G, _S, _N, _X, tk_char) -> true; check_const_tk(_G, _S, _N, _X, tk_wchar) -> true; %% WCHAR check_const_tk(_G, _S, _N, _X, tk_octet) -> true; check_const_tk(_G, _S, _N, _X, {tk_string, _Len}) -> true; check_const_tk(_G, _S, _N, _X, {tk_wstring, _Len}) -> true; %% WSTRING check_const_tk(_G, _S, _N, _X, tk_fixed) -> true; check_const_tk(_G, _S, _N, _X, {tk_fixed, _Digits, _Scale}) -> true; check_const_tk(G, _S, _N, X, TK) -> ic_error:error(G, {illegal_const_t, X, TK}). check_switch_tk(_G, _S, _N, _X, tk_long) -> true; check_switch_tk(_G, _S, _N, _X, tk_longlong) -> true; %% LLONG check_switch_tk(_G, _S, _N, _X, tk_short) -> true; check_switch_tk(_G, _S, _N, _X, tk_ushort) -> true; check_switch_tk(_G, _S, _N, _X, tk_ulong) -> true; check_switch_tk(_G, _S, _N, _X, tk_ulonglong) -> true; %% ULLONG check_switch_tk(_G, _S, _N, _X, tk_boolean) -> true; check_switch_tk(_G, _S, _N, _X, tk_char) -> true; check_switch_tk(_G, _S, _N, _X, tk_wchar) -> true; %% WCHAR check_switch_tk(_G, _S, _N, _X, TK) when element(1, TK) == tk_enum -> true; check_switch_tk(G, _S, _N, X, TK) -> ic_error:error(G, {illegal_switch_t, X, TK}), false. %% Lookup a name name_lookup(G, S, N, X) -> case scoped_lookup(G, S, N, X) of T when is_tuple(T) -> element(1, T) end. lookup(G, S, N, X, Id) -> N2 = Id ++ N, ?DBG(" Trying ~p ...~n", [N2]), case ets:lookup(S, N2) of [] -> case look_for_interface(G, S, [hd(N2)], tl(N2)) of %% First attempt: filtering inherited members ! [{_, member, _, _}] -> case look_for_interface(G, S, [hd(N)], tl(N2)) of [T] -> ?DBG(" -- found ~p~n", [T]), T; _ -> lookup(G, S, tl(N), X, Id) end; %% [T] -> ?DBG(" -- found ~p~n", [T]), T; _ -> if N == [] -> ic_error:error(G, {tk_not_found, X}); true -> lookup(G, S, tl(N), X, Id) end end; %% Second attempt: filtering members ! [{_, member, _, _}] -> case look_for_interface(G, S, [hd(N2)], tl(N2)) of [T] -> ?DBG(" -- found ~p~n", [T]), T; _ -> if N == [] -> ic_error:error(G, {tk_not_found, X}); true -> lookup(G, S, tl(N), X, Id) end end; %% [T] -> ?DBG(" -- found ~p~n", [T]), T end. look_for_interface(_G, _S, _Hd, []) -> false; look_for_interface(G, S, Hd, Tl) -> case ets:lookup(S, Tl) of [{_, interface, _TK, Inh}] -> case look_in_inherit(G, S, Hd, Inh) of %% gather_inherit(G, S, Inh, [])) of [X] when is_tuple(X) -> [X]; _ -> look_for_interface(G, S, Hd ++ [hd(Tl)], tl(Tl)) end; _ -> look_for_interface(G, S, Hd ++ [hd(Tl)], tl(Tl)) end. look_in_inherit(G, S, Id, [I | Is]) -> case ets:lookup(S, Id ++ I) of [X] when is_tuple(X) -> [X]; [] -> look_in_inherit(G, S, Id, Is) end; look_in_inherit(_G, _S, _Id, []) -> false. %% L is a list of names mk_uppercase(L) -> lists:map(fun(Z) -> lists:map(fun(X) when X>=$a, X=<$z -> X-$a+$A; (X) -> X end, Z) end, L). %%-------------------------------------------------------------------- %% %% Inheritance stuff %% %% %%-------------------------------------------------------------------- %% InhBody is an accumulating parameter calc_inherit_body(G, N, OrigBody, [X|Xs], InhBody) -> case ic_symtab:retrieve(G, X) of Intf when is_record(Intf, interface) -> Body = filter_body(G, X, ic_forms:get_body(Intf), N, OrigBody, InhBody), calc_inherit_body(G, N, OrigBody, Xs, [{X, Body} | InhBody]); XXX -> io:format("Oops, not found ~p~n", [XXX]), calc_inherit_body(G, N, OrigBody, Xs, InhBody) end; calc_inherit_body(_G, _N, _OrigBody, [], InhBody) -> lists:reverse(InhBody). filter_body(G, XPath, [X | Xs], OrigPath, OrigBody, InhBody) -> case complex_body_member(G, XPath, X, OrigPath, OrigBody, InhBody) of true -> %%io:format("NOT adding ~p~n", [ic_forms:get_id2(X)]), filter_body(G, XPath, Xs, OrigPath, OrigBody, InhBody); {false, NewX} -> % For those with idlist %%io:format("Adding from idlist~n", []), [NewX | filter_body(G, XPath, Xs, OrigPath, OrigBody, InhBody)]; false -> %%io:format("Adding: ~p~n", [ic_forms:get_id2(X)]), [X | filter_body(G, XPath, Xs, OrigPath, OrigBody, InhBody)] end; filter_body(_G, _XPath, [], _OrigPath, _OrigBody, _InhBody) -> []. complex_body_member(G, XPath, X, OrigPath, OrigBody, InhBody) -> case has_idlist(X) of true -> idlist_member(G, XPath, X, OrigPath, OrigBody, InhBody); false -> straight_member(G, XPath, X, OrigPath, OrigBody, InhBody) end. idlist_member(G, XPath, X, OrigPath, OrigBody, InhBody) -> XX = #id_of{type=X}, F = fun(Id) -> not(straight_member(G, XPath, XX#id_of{id=Id}, OrigPath, OrigBody, InhBody)) end, case lists:filter(F, ic_forms:get_idlist(X)) of [] -> true; IdList -> %%% io:format("Idlist added: ~p~n",[IdList]), {false, replace_idlist(X, IdList)} end. straight_member(G, XPath, X, OrigPath, OrigBody, InhBody) -> %%io:format("straight member: ~p~n", [ic_forms:get_id2(X)]), case body_member(G, XPath, X, OrigPath, OrigBody) of true -> true; false -> inh_body_member(G, XPath, X, InhBody) end. inh_body_member(G, XPath, X, [{Name, Body} | InhBody]) -> case body_member(G, XPath, X, Name, Body) of true -> true; false -> inh_body_member(G, XPath, X, InhBody) end; inh_body_member(_G, _XPath, _X, []) -> false. body_member(G, XPath, X, YPath, [Y|Ys]) -> case has_idlist(Y) of true -> YY = #id_of{type=Y}, case list_and(fun(Y2) -> not(is_equal(G, XPath, X, YPath, YY#id_of{id=Y2})) end, ic_forms:get_idlist(Y)) of true -> body_member(G, XPath, X, YPath, Ys); false -> true end; false -> case is_equal(G, XPath, X, YPath, Y) of false -> body_member(G, XPath, X, YPath, Ys); true -> true end end; body_member(_G, _XPath, _X, _YPath, []) -> false. is_equal(G, XPath, X, YPath, Y) -> case {ic_forms:get_id2(X), ic_forms:get_id2(Y)} of {ID, ID} -> collision(G, XPath, X, YPath, Y), true; _ -> false end. %% X is the new item, Y is the old one. So it is X that collides with %% Y and Y shadows X. collision(G, XPath, X, YPath, Y) -> I1 = get_beef(X), % I2 = get_beef(Y), if is_record(I1, op) -> %%, record(I2, op) -> ic_error:error(G, {inherit_name_collision, {YPath, Y}, {XPath, X}}); is_record(I1, attr) -> %%, record(I2, attr) -> ic_error:error(G, {inherit_name_collision, {YPath, Y}, {XPath, X}}); true -> ?ifopt(G, warn_name_shadow, ic_error:warn(G, {inherit_name_shadow, {YPath, Y}, {XPath, X}})) end. has_idlist(X) when is_record(X, typedef) -> true; has_idlist(X) when is_record(X, member) -> true; has_idlist(X) when is_record(X, case_dcl) -> true; has_idlist(X) when is_record(X, attr) -> true; has_idlist(_) -> false. replace_idlist(X, IdList) when is_record(X, typedef) -> X#typedef{id=IdList}; replace_idlist(X, IdList) when is_record(X, attr) -> X#attr{id=IdList}. get_beef(X) when is_record(X, id_of) -> X#id_of.type; get_beef(X) -> X. %% And among all elements in list list_and(F, [X|Xs]) -> case F(X) of true -> list_and(F, Xs); false -> false end; list_and(_F, []) -> true. %%-------------------------------------------------------------------- %% %% resolve_inherit shall return a list of resolved inheritances, %% that is all names replaced with their global names. %% inherit_resolve(G, S, N, [X|Rest], Out) -> case scoped_lookup(G, S, N, X) of {Name, _T, _TK, Inh} -> case lists:member(Name, Out) of true -> inherit_resolve(G, S, N, Rest, Out); false -> case unique_append(Inh, [Name|Out]) of error -> ic_error:error(G, {inherit_resolve, X, Name}), inherit_resolve(G, S, N, Rest, []); UA -> inherit_resolve(G, S, N, Rest, UA) end end; _ -> inherit_resolve(G, S, N, Rest, Out) end; inherit_resolve(_G, _S, _N, [], Out) -> lists:reverse(Out). unique_append([X|Xs], L) -> case lists:member(X, L) of true -> unique_append(Xs, L); false -> unique_append(Xs, [X|L]) end; unique_append([], L) -> L; %% Error unique_append(_, _L) -> error. %%-------------------------------------------------------------------- %% %% Utilities %% %% Must preserve order, therefore had to write my own (instead of lists:map) check_list(G, S, N, [X|Xs]) -> X1 = check(G, S, N, X), [X1 | check_list(G, S, N, Xs)]; check_list(_G, _S, _N, []) -> []. filter( [] ) -> error; filter( [I | Is ] ) -> case I of { _, member, { _, TKINFO }, _ } -> fetchType( TKINFO ); { _, struct, _, _ } -> struct; { _, typedef, TKINFO, _ } -> fetchType( TKINFO ); { _, module, _, _ } -> module; { _, interface, _, _ } -> interface; { _, op, _, _ } -> op; { _,enum, _, _ } -> enum; { _, spellcheck } -> filter( Is ); _ -> error end. fetchType( { tk_sequence, _, _ } ) -> sequence; fetchType( { tk_array, _, _ } ) -> array; fetchType( { tk_struct, _, _, _} ) -> struct; fetchType( { tk_string, _} ) -> string; fetchType( { tk_wstring, _} ) -> %% WSTRING wstring; fetchType( { tk_fixed, _, _} ) -> fixed; fetchType( tk_short ) -> short; fetchType( tk_long ) -> long; fetchType( tk_longlong ) -> %% LLONG longlong; fetchType( tk_ushort ) -> ushort; fetchType( tk_ulong ) -> ulong; fetchType( tk_ulonglong ) -> %% ULLONG ulonglong; fetchType( tk_float ) -> float; fetchType( tk_double ) -> double; fetchType( tk_boolean ) -> boolean; fetchType( tk_char ) -> char; fetchType( tk_wchar ) -> %% WCHAR wchar; fetchType( tk_octet ) -> octet; fetchType( { tk_enum, _, _, _ } ) -> enum; fetchType( { tk_union, _, _, _, _, _ } ) -> union; fetchType( tk_any ) -> any; fetchType( _ ) -> error. %% Z is a single name to_uppercase(Z) -> lists:map(fun(X) when X>=$a, X=<$z -> X-$a+$A; (X) -> X end, Z). %%------------------------------------------------------------ %% %% Always fetchs TK of a record. %% %%------------------------------------------------------------ fetchTk(G,N,X) -> case ic_forms:get_tk(X) of undefined -> searchTk(G,ictk:get_IR_ID(G, N, X)); TK -> TK end. %%------------------------------------------------------------ %% %% seek type code when not accessible by get_tk/1 %% %%------------------------------------------------------------ searchTk(G,IR_ID) -> S = ic_genobj:tktab(G), case catch searchTk(S,IR_ID,typedef) of {value,TK} -> TK; _ -> %% false / exit case catch searchTk(S,IR_ID,struct) of {value,TK} -> TK; _ -> %% false / exit case catch searchTk(S,IR_ID,union) of {value,TK} -> TK; _ -> undefined end end end. searchTk(S,IR_ID,Type) -> L = lists:flatten(ets:match(S,{'_',Type,'$1','_'})), case lists:keysearch(IR_ID,2,L) of {value,TK} -> {value,TK}; false -> searchInsideTks(L,IR_ID) end. searchInsideTks([],_IR_ID) -> false; searchInsideTks([{tk_array,TK,_}|Xs],IR_ID) -> case searchIncludedTk(TK,IR_ID) of {value,TK} -> {value,TK}; false -> searchInsideTks(Xs,IR_ID) end. searchIncludedTk({tk_array,TK,_},IR_ID) -> searchIncludedTk(TK,IR_ID); searchIncludedTk({tk_sequence,TK,_},IR_ID) -> searchIncludedTk(TK,IR_ID); searchIncludedTk(TK, _IR_ID) when is_atom(TK) -> false; searchIncludedTk(TK,IR_ID) -> case element(2,TK) == IR_ID of true -> {value,TK}; false -> false end.