ms_transform.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 982 行 · 第 1/2 页

ERL
982
字号
%% ``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%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%-module(ms_transform).-export([format_error/1,transform_from_shell/3,parse_transform/2]).%% Error codes.-define(ERROR_BASE_GUARD,0).-define(ERROR_BASE_BODY,100).-define(ERR_NOFUN,1).-define(ERR_ETS_HEAD,2).-define(ERR_DBG_HEAD,3).-define(ERR_HEADMATCH,4).-define(ERR_SEMI_GUARD,5).-define(ERR_UNBOUND_VARIABLE,6).-define(ERR_HEADBADREC,7).-define(ERR_HEADBADFIELD,8).-define(ERR_HEADMULTIFIELD,9).-define(ERR_HEADDOLLARATOM,10).-define(ERR_HEADBINMATCH,11).-define(ERR_GENMATCH,16).-define(ERR_GENLOCALCALL,17).-define(ERR_GENELEMENT,18).-define(ERR_GENBADFIELD,19).-define(ERR_GENBADREC,20).-define(ERR_GENMULTIFIELD,21).-define(ERR_GENREMOTECALL,22).-define(ERR_GENBINCONSTRUCT,23).-define(ERR_GENDISALLOWEDOP,24).-define(ERR_GUARDMATCH,?ERR_GENMATCH+?ERROR_BASE_GUARD).-define(ERR_BODYMATCH,?ERR_GENMATCH+?ERROR_BASE_BODY).-define(ERR_GUARDLOCALCALL,?ERR_GENLOCALCALL+?ERROR_BASE_GUARD).-define(ERR_BODYLOCALCALL,?ERR_GENLOCALCALL+?ERROR_BASE_BODY).-define(ERR_GUARDELEMENT,?ERR_GENELEMENT+?ERROR_BASE_GUARD).-define(ERR_BODYELEMENT,?ERR_GENELEMENT+?ERROR_BASE_BODY).-define(ERR_GUARDBADFIELD,?ERR_GENBADFIELD+?ERROR_BASE_GUARD).-define(ERR_BODYBADFIELD,?ERR_GENBADFIELD+?ERROR_BASE_BODY).-define(ERR_GUARDBADREC,?ERR_GENBADREC+?ERROR_BASE_GUARD).-define(ERR_BODYBADREC,?ERR_GENBADREC+?ERROR_BASE_BODY).-define(ERR_GUARDMULTIFIELD,?ERR_GENMULTIFIELD+?ERROR_BASE_GUARD).-define(ERR_BODYMULTIFIELD,?ERR_GENMULTIFIELD+?ERROR_BASE_BODY).-define(ERR_GUARDREMOTECALL,?ERR_GENREMOTECALL+?ERROR_BASE_GUARD).-define(ERR_BODYREMOTECALL,?ERR_GENREMOTECALL+?ERROR_BASE_BODY).-define(ERR_GUARDBINCONSTRUCT,?ERR_GENBINCONSTRUCT+?ERROR_BASE_GUARD).-define(ERR_BODYBINCONSTRUCT,?ERR_GENBINCONSTRUCT+?ERROR_BASE_BODY).-define(ERR_GUARDDISALLOWEDOP,?ERR_GENDISALLOWEDOP+?ERROR_BASE_GUARD).-define(ERR_BODYDISALLOWEDOP,?ERR_GENDISALLOWEDOP+?ERROR_BASE_BODY).%%%% Called by compiler or ets/dbg:fun2ms when errors occur%%format_error(?ERR_NOFUN) ->	        "Parameter of ets/dbg:fun2ms/1 is not a literal fun";format_error(?ERR_ETS_HEAD) ->	        "ets:fun2ms requires fun with single variable or tuple parameter";format_error(?ERR_DBG_HEAD) ->	        "dbg:fun2ms requires fun with single variable or list parameter";format_error(?ERR_HEADMATCH) ->	        "in fun head, only matching (=) on toplevel can be translated into match_spec";format_error(?ERR_SEMI_GUARD) ->	        "fun with semicolon (;) in guard cannot be translated into match_spec";format_error(?ERR_GUARDMATCH) ->	        "fun with guard matching ('=' in guard) is illegal as match_spec as well";format_error({?ERR_GUARDLOCALCALL, Name, Arithy}) ->	        lists:flatten(io_lib:format("fun containing the local function call "				"'~w/~w' (called in guard) "				"cannot be translated into match_spec",				[Name, Arithy]));format_error({?ERR_GUARDREMOTECALL, Module, Name, Arithy}) ->	        lists:flatten(io_lib:format("fun containing the remote function call "				"'~w:~w/~w' (called in guard) "				"cannot be translated into match_spec",				[Module,Name,Arithy]));format_error({?ERR_GUARDELEMENT, Str}) ->    lists:flatten(      io_lib:format("the language element ~s (in guard) cannot be translated "		    "into match_spec", [Str]));format_error({?ERR_GUARDBINCONSTRUCT, Var}) ->    lists:flatten(      io_lib:format("bit syntax construction with variable ~w (in guard) "		    "cannot be translated "		    "into match_spec", [Var]));format_error({?ERR_GUARDDISALLOWEDOP, Operator}) ->    %% There is presently no operators that are allowed in bodies but    %% not in guards.    lists:flatten(      io_lib:format("the operator ~w is not allowed in guards", [Operator]));format_error(?ERR_BODYMATCH) ->	        "fun with body matching ('=' in body) is illegal as match_spec";format_error({?ERR_BODYLOCALCALL, Name, Arithy}) ->	        lists:flatten(io_lib:format("fun containing the local function "				"call '~w/~w' (called in body) "				"cannot be translated into match_spec",				[Name,Arithy]));format_error({?ERR_BODYREMOTECALL, Module, Name, Arithy}) ->	        lists:flatten(io_lib:format("fun containing the remote function call "				"'~w:~w/~w' (called in body) "				"cannot be translated into match_spec",				[Module,Name,Arithy]));format_error({?ERR_BODYELEMENT, Str}) ->    lists:flatten(      io_lib:format("the language element ~s (in body) cannot be translated "		    "into match_spec", [Str]));format_error({?ERR_BODYBINCONSTRUCT, Var}) ->    lists:flatten(      io_lib:format("bit syntax construction with variable ~w (in body) "		    "cannot be translated "		    "into match_spec", [Var]));format_error({?ERR_BODYDISALLOWEDOP, Operator}) ->     %% This will probably never happen, Are there op's that are allowed in     %% guards but not in bodies? Not at time of writing anyway...    lists:flatten(      io_lib:format("the operator ~w is not allowed in function bodies", 		    [Operator]));format_error({?ERR_UNBOUND_VARIABLE, Str}) ->    lists:flatten(      io_lib:format("the variable ~s is unbound, cannot translate "		    "into match_spec", [Str]));format_error({?ERR_HEADBADREC,Name}) ->	        lists:flatten(      io_lib:format("fun head contains unknown record type ~w",[Name]));format_error({?ERR_HEADBADFIELD,RName,FName}) ->	        lists:flatten(      io_lib:format("fun head contains reference to unknown field ~w in "		    "record type ~w",[FName, RName]));format_error({?ERR_HEADMULTIFIELD,RName,FName}) ->	        lists:flatten(      io_lib:format("fun head contains already defined field ~w in "		    "record type ~w",[FName, RName]));format_error({?ERR_HEADDOLLARATOM,Atom}) ->	        lists:flatten(      io_lib:format("fun head contains atom ~w, which conflics with reserved "		    "atoms in match_spec heads",[Atom]));format_error({?ERR_HEADBINMATCH,Atom}) ->	        lists:flatten(      io_lib:format("fun head contains bit syntax matching of variable ~w, "		    "which cannot be translated into match_spec", [Atom]));format_error({?ERR_GUARDBADREC,Name}) ->	        lists:flatten(      io_lib:format("fun guard contains unknown record type ~w",[Name]));format_error({?ERR_GUARDBADFIELD,RName,FName}) ->	        lists:flatten(      io_lib:format("fun guard contains reference to unknown field ~w in "		    "record type ~w",[FName, RName]));format_error({?ERR_GUARDMULTIFIELD,RName,FName}) ->	        lists:flatten(      io_lib:format("fun guard contains already defined field ~w in "		    "record type ~w",[FName, RName]));format_error({?ERR_BODYBADREC,Name}) ->	        lists:flatten(      io_lib:format("fun body contains unknown record type ~w",[Name]));format_error({?ERR_BODYBADFIELD,RName,FName}) ->	        lists:flatten(      io_lib:format("fun body contains reference to unknown field ~w in "		    "record type ~w",[FName, RName]));format_error({?ERR_BODYMULTIFIELD,RName,FName}) ->	        lists:flatten(      io_lib:format("fun body contains already defined field ~w in "		    "record type ~w",[FName, RName]));format_error(Else) ->    lists:flatten(io_lib:format("Unknown error code ~w",[Else])).%%%% Called when translating in shell%%transform_from_shell(Dialect, Clauses, BoundEnvironment) ->    SaveFilename = setup_filename(),    case catch ms_clause_list(1,Clauses,Dialect) of	{'EXIT',Reason} ->	    cleanup_filename(SaveFilename),	    exit(Reason);	{error,Line,R} ->	    {error, [{cleanup_filename(SaveFilename),		      [{Line, ?MODULE, R}]}], []};	Else ->            case (catch fixup_environment(Else,BoundEnvironment)) of                {error,Line1,R1} ->                    {error, [{cleanup_filename(SaveFilename),                             [{Line1, ?MODULE, R1}]}], []};                 Else1 ->		    Ret = normalise(Else1),                    cleanup_filename(SaveFilename),		    Ret            end    end.    %%%% Called when translating during compiling%%parse_transform(Forms, _Options) ->    SaveFilename = setup_filename(),    case catch forms(Forms) of	{'EXIT',Reason} ->	    cleanup_filename(SaveFilename),	    exit(Reason);	{error,Line,R} ->	    {error, [{cleanup_filename(SaveFilename),		      [{Line, ?MODULE, R}]}], []};	Else ->	    cleanup_filename(SaveFilename),	    Else    end.setup_filename() ->    {erase(filename),erase(records)}.put_filename(Name) ->    put(filename,Name).put_records(R) ->    put(records,R),    ok.get_records() ->    case get(records) of	undefined ->	    [];	Else ->	    Else    end.cleanup_filename({Old,OldRec}) ->    Ret = case erase(filename) of	      undefined ->		  "TOP_LEVEL";	      X ->		  X	  end,    case OldRec of	undefined ->	    erase(records);	Rec ->	    put(records,Rec)    end,    case Old of	undefined ->	    Ret;	Y ->	    put(filename,Y),	    Ret    end.add_record_definition({Name,FieldList}) ->    {KeyList,_} = lists:foldl(		    fun({record_field,_,{atom,Line0,FieldName}},{L,C}) ->			    {[{FieldName,C,{atom,Line0,undefined}}|L],C+1};		       ({record_field,_,{atom,_,FieldName},Def},{L,C}) ->			    {[{FieldName,C,Def}|L],C+1}		    end,		    {[],2},		    FieldList),    put_records([{Name,KeyList}|get_records()]).forms([F0|Fs0]) ->    F1 = form(F0),    Fs1 = forms(Fs0),    [F1|Fs1];forms([]) -> [].form({attribute,_,file,{Filename,_}}=Form) ->    put_filename(Filename),    Form;form({attribute,_,record,Definition}=Form) ->     add_record_definition(Definition),    Form;form({function,Line,Name0,Arity0,Clauses0}) ->    {Name,Arity,Clauses} = function(Name0, Arity0, Clauses0),    {function,Line,Name,Arity,Clauses};form(AnyOther) ->    AnyOther.function(Name, Arity, Clauses0) ->    Clauses1 = clauses(Clauses0),    {Name,Arity,Clauses1}.clauses([C0|Cs]) ->    C1 = clause(C0),    [C1|clauses(Cs)];clauses([]) -> [].clause({clause,Line,H0,G0,B0}) ->    B1 = copy(B0),    {clause,Line,H0,G0,B1}.copy({call,Line,{remote,_Line2,{atom,_Line3,ets},{atom,_Line4,fun2ms}},      As0}) ->    transform_call(ets,Line,As0);copy({call,Line,{remote,_Line2,{atom,_Line3,dbg},{atom,_Line4,fun2ms}},      As0}) ->    transform_call(dbg,Line,As0);copy(T) when is_tuple(T) ->    list_to_tuple(copy_list(tuple_to_list(T)));copy(L) when is_list(L) ->    copy_list(L);copy(AnyOther) ->    AnyOther.copy_list([H|T]) ->    [copy(H)|copy_list(T)];copy_list([]) ->    [].transform_call(Type,_Line,[{'fun',Line2,{clauses, ClauseList}}]) ->    ms_clause_list(Line2, ClauseList,Type);transform_call(_Type,Line,_NoAbstractFun) ->    throw({error,Line,?ERR_NOFUN}).% Fixup semicolons in guardsms_clause_expand({clause, Line, Parameters, Guard = [_,_|_], Body}) ->    [ {clause, Line, Parameters, [X], Body} || X <- Guard ];ms_clause_expand(_Other) ->    false.ms_clause_list(Line,[H|T],Type) ->    case ms_clause_expand(H) of	NewHead when is_list(NewHead) ->	    ms_clause_list(Line,NewHead ++ T, Type);	false ->	    {cons, Line, ms_clause(H,Type), ms_clause_list(Line, T,Type)}    end;ms_clause_list(Line,[],_) ->    {nil,Line}.ms_clause({clause, Line, Parameters, Guards, Body},Type) ->    check_type(Line,Parameters,Type),    {MSHead,Bindings} = transform_head(Parameters),    MSGuards = transform_guards(Line, Guards, Bindings),    MSBody = transform_body(Line,Body,Bindings),    {tuple, Line, [MSHead,MSGuards,MSBody]}.check_type(_,[{var,_,_}],_) ->    ok;check_type(_,[{tuple,_,_}],ets) ->    ok;check_type(_,[{record,_,_,_}],ets) ->    ok;check_type(_,[{cons,_,_,_}],dbg) ->    ok;check_type(Line0,[{match,_,{var,_,_},X}],Any) ->    check_type(Line0,[X],Any);check_type(Line0,[{match,_,X,{var,_,_}}],Any) ->    check_type(Line0,[X],Any);check_type(Line,_Type,ets) ->    throw({error,Line,?ERR_ETS_HEAD});check_type(Line,_,dbg) ->    throw({error,Line,?ERR_DBG_HEAD}).-record(tgd,{ b, %Bindings 	      p, %Part of spec	      eb %Error code base, 0 for guards, 100 for bodies	     }).transform_guards(Line,[],_Bindings) ->    {nil,Line};transform_guards(Line,[G],Bindings) ->    B = #tgd{b = Bindings, p = guard, eb = ?ERROR_BASE_GUARD},    tg0(Line,G,B);transform_guards(Line,_,_) ->    throw({error,Line,?ERR_SEMI_GUARD}).    transform_body(Line,Body,Bindings) ->    B = #tgd{b = Bindings, p = body, eb = ?ERROR_BASE_BODY},    tg0(Line,Body,B).    guard_top_trans({call,Line0,{atom,Line1,OldTest},Params}) ->    case old_bool_test(OldTest,length(Params)) of	undefined ->	    {call,Line0,{atom,Line1,OldTest},Params};	Trans ->	    {call,Line0,{atom,Line1,Trans},Params}    end;guard_top_trans(Else) ->    Else.tg0(Line,[],_) ->    {nil,Line};tg0(Line,[H0|T],B) when B#tgd.p =:= guard ->    H = guard_top_trans(H0),    {cons,Line, tg(H,B), tg0(Line,T,B)};tg0(Line,[H|T],B) ->    {cons,Line, tg(H,B), tg0(Line,T,B)}.    tg({match,Line,_,_},B) ->     throw({error,Line,?ERR_GENMATCH+B#tgd.eb});tg({op, Line, Operator, O1, O2}, B) ->    {tuple, Line, [{atom, Line, Operator}, tg(O1,B), tg(O2,B)]};tg({op, Line, Operator, O1}, B) ->    {tuple, Line, [{atom, Line, Operator}, tg(O1,B)]};tg({call, _Line, {atom, Line2, bindings},[]},_B) ->    	    {atom, Line2, '$*'};tg({call, _Line, {atom, Line2, object},[]},_B) ->    	    {atom, Line2, '$_'};tg({call, Line, {atom, _, is_record}=Call,[Object, {atom,Line3,RName}=R]},B) ->    MSObject = tg(Object,B),    RDefs = get_records(),    case lists:keysearch(RName,1,RDefs) of	{value, {RName, FieldList}} ->	    RSize = length(FieldList)+1,	    {tuple, Line, [Call, MSObject, R, {integer, Line3, RSize}]};	_ ->	    throw({error,Line3,{?ERR_GENBADREC+B#tgd.eb,RName}})    end;tg({call, Line, {atom, Line2, FunName},ParaList},B) ->    case is_ms_function(FunName,length(ParaList), B#tgd.p) of	true ->	    {tuple, Line, [{atom, Line2, FunName} | 			   lists:map(fun(X) -> tg(X,B) end, ParaList)]};	_ ->	    throw({error,Line,{?ERR_GENLOCALCALL+B#tgd.eb,			       FunName,length(ParaList)}})     end;tg({call, Line, {remote,_,{atom,_,erlang},{atom, Line2, FunName}},ParaList},   B) ->    L = length(ParaList),    case is_imported_from_erlang(FunName,L,B#tgd.p) of	true ->	    case is_operator(FunName,L,B#tgd.p) of		false ->		    tg({call, Line, {atom, Line2, FunName},ParaList},B);		true ->		    tg(list_to_tuple([op,Line2,FunName | ParaList]),B)		end;	_ ->	    throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,erlang,			       FunName,length(ParaList)}})     end;tg({call, Line, {remote,_,{atom,_,ModuleName},		 {atom, _, FunName}},_ParaList},B) ->    throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,ModuleName,FunName}});tg({cons,Line, H, T},B) ->     {cons, Line, tg(H,B), tg(T,B)};tg({nil, Line},_B) ->    {nil, Line};tg({tuple,Line,L},B) ->    {tuple,Line,[{tuple,Line,lists:map(fun(X) -> tg(X,B) end, L)}]};tg({integer,Line,I},_) ->    {integer,Line,I};tg({char,Line,C},_) ->    {char,Line,C};tg({float, Line,F},_) ->    {float,Line,F};tg({atom,Line,A},_) ->    case atom_to_list(A) of	[$$|_] ->	   {tuple, Line,[{atom, Line, 'const'},{atom,Line,A}]};	_ ->	    {atom,Line,A}    end;tg({string,Line,S},_) ->    {string,Line,S};tg({var,Line,VarName},B) ->    case lkup_bind(VarName, B#tgd.b) of	undefined ->	    {tuple, Line,[{atom, Line, 'const'},{var,Line,VarName}]};	AtomName ->	    {atom, Line, AtomName}    end;tg({record_field,Line,Object,RName,{atom,_Line1,KeyName}},B) ->    RDefs = get_records(),    case lists:keysearch(RName,1,RDefs) of	{value, {RName, FieldList}} ->	    case lists:keysearch(KeyName,1, FieldList) of		{value, {KeyName,Position,_}} ->		    NewObject = tg(Object,B),		    {tuple, Line, [{atom, Line, 'element'}, 				   {integer, Line, Position}, NewObject]};		_ ->		    throw({error,Line,{?ERR_GENBADFIELD+B#tgd.eb, RName, 				       KeyName}})	    end;	_ ->	    throw({error,Line,{?ERR_GENBADREC+B#tgd.eb,RName}})    end;tg({record,Line,RName,RFields},B) ->    RDefs = get_records(),    KeyList0 = lists:foldl(fun({record_field,_,{atom,_,Key},Value},

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?