epp.erl

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

ERL
991
字号
%% ``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(epp).%% An Erlang code preprocessor.-export([open/2,open/3,close/1,format_error/1]).-export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]).-export([parse_file/3,parse_file/4]).-export([interpret_file_attribute/1]).%% Epp state record.-record(epp, {file,				%Current file	      line=1,				%Current line number	      name="",				%Current file name	      istk=[],				%Ifdef stack	      sstk=[],				%State stack	      path=[],				%Include-path	      macs=dict:new(),			%Macros	      uses=dict:new()			%Macro use structure	     }).%%% Note on representation: as tokens, both {var, Line, Name} and%%% {atom, Line, Name} can occur as macro identifiers. However, keeping%%% this distinction here is done for historical reasons only: previously,%%% ?FOO and ?'FOO' were not the same, but now they are. Removing the%%% distinction in the internal representation would simplify the code%%% a little.%% open(FileName, IncludePath)%% open(FileName, IncludePath, PreDefMacros)%% close(Epp)%% scan_erl_form(Epp)%% parse_erl_form(Epp)%% macro_defs(Epp)open(File, Path) ->    open(File, Path, []).open(File, Path, Pdm) ->    Self = self(),    Epp = spawn(fun() -> server(Self, File, Path, Pdm) end),    epp_request(Epp).close(Epp) ->    %% Make sure that close is synchronous as a courtesy to test    %% cases that test for resource leaks.    Ref = erlang:monitor(process, Epp),    R = epp_request(Epp, close),    receive {'DOWN',Ref,_,_,_} -> ok end,    R.scan_erl_form(Epp) ->    epp_request(Epp, scan_erl_form).parse_erl_form(Epp) ->    case epp_request(Epp, scan_erl_form) of	{ok,Toks} ->	    erl_parse:parse_form(Toks);	Other ->	    Other    end.macro_defs(Epp) ->    epp_request(Epp, macro_defs).%% format_error(ErrorDescriptor) -> String%%  Return a string describing the error.format_error(cannot_parse) ->    io_lib:format("cannot parse file, giving up", []);format_error({bad,W}) ->    io_lib:format("badly formed '~s'", [W]);format_error({call,What}) ->    io_lib:format("illegal macro call '~s'",[What]);format_error({undefined,M}) ->    io_lib:format("undefined macro '~w'", [M]);format_error({depth,What}) ->    io_lib:format("~s too deep",[What]);format_error({mismatch,M}) ->    io_lib:format("argument mismatch for macro '~w'", [M]);format_error({arg_error,M}) ->    io_lib:format("badly formed argument for macro '~w'", [M]);format_error({redefine,M}) ->    io_lib:format("redefining macro '~w'", [M]);format_error({circular,M}) ->    io_lib:format("circular macro '~w'", [M]);format_error({include,W,F}) ->    io_lib:format("can't find include ~s \"~s\"", [W,F]);format_error({illegal,How,What}) ->    io_lib:format("~s '-~s'", [How,What]);format_error({'NYI',What}) ->    io_lib:format("not yet implemented '~s'", [What]);format_error(E) -> file:format_error(E).%% parse_file(FileName, IncludePath, [PreDefMacro]) ->%%	{ok,[Form]} | {error,OpenError}parse_file(Ifile, Path, Predefs) ->    parse_file(Ifile, Path, Predefs, []).parse_file(Ifile, Path, Predefs, Options) ->    case open(Ifile, Path, Predefs) of	{ok,Epp} ->	    Forms = parse_file(Epp, Options),	    close(Epp),	    {ok,Forms};	{error,E} ->	    {error,E}    end.%% parse_file(Epp) ->%%	[Form]parse_file(Epp, Options) ->    case parse_erl_form(Epp) of	{ok,Form} ->	    case Form of		{attribute,La,record,{Record, Val}} ->		    case lists:member(typed_record,Options) of			false ->			    [{attribute, La, record,			      {Record, normalize_typed_record_fields(Val)}}			     |parse_file(Epp, Options)];			true ->			    [Form|parse_file(Epp, Options)]		    end;		_ ->		    [Form|parse_file(Epp, Options)]	    end;	{error,E} ->	    [{error,E}|parse_file(Epp, Options)];	{eof,Line} ->	    [{eof,Line}]    end.normalize_typed_record_fields([]) ->[];normalize_typed_record_fields([{typed_record_field,Field,_}|Rest]) ->    [Field|normalize_typed_record_fields(Rest)];normalize_typed_record_fields([Field|Rest]) ->    [Field|normalize_typed_record_fields(Rest)].%% server(StarterPid, FileName, Path, PreDefMacros)server(Pid, Name, Path, Pdm) ->    process_flag(trap_exit, true),    case file:open(Name, [read]) of	{ok,File} ->	    Ms0 = predef_macros(Name),	    case user_predef(Pdm, Ms0) of		{ok,Ms1} ->		    epp_reply(Pid, {ok,self()}),		    St = #epp{file=File,name=Name,path=Path,macs=Ms1},		    From = wait_request(St),		    enter_file_reply(From, Name, 1, 1),		    wait_req_scan(St);		{error,E} ->		    epp_reply(Pid, {error,E})	    end;	{error,E} ->	    epp_reply(Pid, {error,E})    end.%% predef_macros(FileName) -> Macrodict%%  Initialise the macro dictionary with the default predefined macros,%%  FILE, LINE, MODULE as undefined, MACHINE and MACHINE value.predef_macros(File) ->    Ms0 = dict:new(),    Ms1 = dict:store({atom,'FILE'}, {none,[{string,1,File}]}, Ms0),    Ms2 = dict:store({atom,'LINE'}, {none,[{integer,1,1}]}, Ms1),    Ms3 = dict:store({atom,'MODULE'}, undefined, Ms2),    Ms31 = dict:store({atom,'MODULE_STRING'}, undefined, Ms3),    Machine = list_to_atom(erlang:system_info(machine)),    Ms4 = dict:store({atom,'MACHINE'}, {none,[{atom,1,Machine}]}, Ms31),    dict:store({atom,Machine}, {none,[{atom,1,true}]}, Ms4).%% user_predef(PreDefMacros, Macros) ->%%	{ok,MacroDict} | {error,E}%%  Add the predefined macros to the macros dictionary. A macro without a%%  value gets the value 'true'.user_predef([{M,Val}|Pdm], Ms) when is_atom(M) ->    case dict:find({atom,M}, Ms) of	{ok,_Def} ->	    {error,{redefine,M}};	error ->	    Exp = erl_parse:tokens(erl_parse:abstract(Val)),	    user_predef(Pdm, dict:store({atom,M}, {none,Exp}, Ms))    end;user_predef([M|Pdm], Ms) when is_atom(M) ->    case dict:find({atom,M}, Ms) of	{ok,_Def} ->	    {error,{redefine,M}};	error ->	    user_predef(Pdm, dict:store({atom,M}, {none,[{atom,1,true}]}, Ms))    end;user_predef([Md|_Pdm], _Ms) -> {error,{bad,Md}};user_predef([], Ms) -> {ok,Ms}.%% wait_request(EppState) -> RequestFrom%% wait_req_scan(EppState)%% wait_req_skip(EppState, SkipIstack)%%  Handle requests, processing trivial requests directly. Either return%%  requestor or scan/skip tokens.wait_request(St) ->    receive	{epp_request,From,scan_erl_form} -> From;	{epp_request,From,macro_defs} ->	    epp_reply(From, dict:to_list(St#epp.macs)),	    wait_request(St);	{epp_request,From,close} ->	    file:close(St#epp.file),	    epp_reply(From, ok),	    exit(normal);	{'EXIT',_,R} ->	    exit(R);	Other ->	    io:fwrite("Epp: unknown '~w'\n", [Other]),	    wait_request(St)    end.wait_req_scan(St) ->    From = wait_request(St),    scan_toks(From, St).wait_req_skip(St, Sis) ->    From = wait_request(St),    skip_toks(From, St, Sis).	%% enter_file(Path, FileName, IncludeLine, From, EppState)%% leave_file(From, EppState)%%  Handle entering and leaving included files. Notify caller when the%%  current file is changed. Note it is an error to exit a file if we are%%  in a conditional. These functions never return.enter_file(_Path, _NewName, Li, From, St)  when length(St#epp.sstk) >= 8 ->    epp_reply(From, {error,{Li,epp,{depth,"include"}}}),    wait_req_scan(St);enter_file(Path, NewName, Li, From, St) ->    case file:path_open(Path, NewName, read) of	{ok,NewF,Pname} ->	    wait_req_scan(enter_file2(NewF, Pname, From, St, 1));	{error,_E} ->	    epp_reply(From, {error,{Li,epp,{include,file,NewName}}}),	    wait_req_scan(St)    end.%% enter_file2(File, FullName, From, EppState, AtLine) -> EppState.%%  Set epp to use this file and "enter" it.enter_file2(NewF, Pname, From, St, AtLine) ->    enter_file2(NewF, Pname, From, St, AtLine, []).enter_file2(NewF, Pname, From, St, AtLine, ExtraPath) ->    enter_file_reply(From, Pname, 1, AtLine),    Ms = dict:store({atom,'FILE'}, {none,[{string,1,Pname}]}, St#epp.macs),    Path = St#epp.path ++ ExtraPath,    #epp{file=NewF,name=Pname,sstk=[St|St#epp.sstk],path=Path,macs=Ms}.enter_file_reply(From, Name, Line, AtLine) ->    Rep = {ok, [{'-',AtLine},{atom,AtLine,file},{'(',AtLine},		{string,AtLine,file_name(Name)},{',',AtLine},		{integer,AtLine,Line},{')',Line},{dot,AtLine}]},    epp_reply(From, Rep).%% Flatten filename to a string. Must be a valid filename.file_name([C | T]) when is_integer(C), C > 0, C =< 255 ->    [C | file_name(T)];file_name([H|T]) ->    file_name(H) ++ file_name(T);file_name([]) ->    [];file_name(N) when is_atom(N) ->    atom_to_list(N).leave_file(From, St) ->    case St#epp.istk of	[I|Cis] -> 	    epp_reply(From,		      {error,{St#epp.line,epp, {illegal,"unterminated",I}}}),	    leave_file(wait_request(St),St#epp{istk=Cis});	[] ->	    case St#epp.sstk of		[OldSt|Sts] ->		    file:close(St#epp.file),		    enter_file_reply(From, OldSt#epp.name,                                      OldSt#epp.line, OldSt#epp.line),		    Ms = dict:store({atom,'FILE'},				    {none,				     [{string,OldSt#epp.line,OldSt#epp.name}]},				    St#epp.macs),		    wait_req_scan(OldSt#epp{sstk=Sts,macs=Ms});		[] ->		    epp_reply(From, {eof,St#epp.line}),		    wait_req_scan(St)	    end    end.%% scan_toks(From, EppState)%% scan_toks(Tokens, From, EppState)scan_toks(From, St) ->    case io:scan_erl_form(St#epp.file, '', St#epp.line) of	{ok,Toks,Cl} ->	    scan_toks(Toks, From, St#epp{line=Cl});	{error,E,Cl} ->	    epp_reply(From, {error,E}),	    wait_req_scan(St#epp{line=Cl});	{eof,Cl} ->	    leave_file(From, St#epp{line=Cl});	{error,_E} ->            epp_reply(From, {error,{St#epp.line,epp,cannot_parse}}),

⌨️ 快捷键说明

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