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 + -
显示快捷键?