Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
nox committed Dec 3, 2012
1 parent bdf8870 commit 2c8038c
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 205 deletions.
155 changes: 96 additions & 59 deletions lib/stdlib/src/epp.erl
Expand Up @@ -20,9 +20,9 @@

%% An Erlang code preprocessor.

-export([open/2,open/3,open/4, open/5,close/1,format_error/1]).
-export([open/2,open/3,open/4, open/5, open/6,close/1,format_error/1]).
-export([scan_erl_form/1,parse_erl_form/1,macro_defs/1]).
-export([parse_file/1, parse_file/3, parse_file/4]).
-export([parse_file/1, parse_file/3, parse_file/4, parse_file/5]).
-export([interpret_file_attribute/1]).
-export([normalize_typed_record_fields/1,restore_typed_record_fields/1]).

Expand All @@ -42,7 +42,8 @@
path=[], %Include-path
macs = dict:new() :: dict(), %Macros (don't care locations)
uses = dict:new() :: dict(), %Macro use structure
pre_opened = false :: boolean()
pre_opened = false :: boolean(),
range = false :: boolean()
}).

%%% Note on representation: as tokens, both {var, Location, Name} and
Expand Down Expand Up @@ -95,13 +96,18 @@ open(Name, Path, Pdm) ->
ErrorDescriptor :: term().

open(Name, StartLocation, Path, Pdm) ->
open(Name, StartLocation, Path, Pdm, []).

open(Name, StartLocation, Path, Pdm, Opts) ->
Self = self(),
Epp = spawn(fun() -> server(Self, Name, StartLocation, Path, Pdm) end),
Epp = spawn(fun() -> server(Self, Name,
StartLocation, Path, Pdm, Opts) end),
epp_request(Epp).

open(Name, File, StartLocation, Path, Pdm) ->
open(Name, File, StartLocation, Path, Pdm, Opts) ->
Self = self(),
Epp = spawn(fun() -> server(Self, Name, File, StartLocation,Path,Pdm) end),
Epp = spawn(fun() -> server(Self, Name, File,
StartLocation,Path,Pdm,Opts) end),
epp_request(Epp).

-spec close(Epp) -> 'ok' when
Expand Down Expand Up @@ -206,7 +212,23 @@ parse_file(Ifile, Path, Predefs) ->
OpenError :: file:posix() | badarg | system_limit.

parse_file(Ifile, StartLocation, Path, Predefs) ->
case open(Ifile, StartLocation, Path, Predefs) of
parse_file(Ifile, StartLocation, Path, Predefs, []).

-spec parse_file(FileName, StartLocation, IncludePath, PredefMacros,
ScanOptions) ->
{'ok', [Form]} | {error, OpenError} when
FileName :: file:name(),
StartLocation :: erl_scan:location(),
IncludePath :: [DirectoryName :: file:name()],
Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line},
PredefMacros :: macros(),
ScanOptions :: erl_scan:options(),
Line :: erl_scan:line(),
ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(),
OpenError :: file:posix() | badarg | system_limit.

parse_file(Ifile, StartLocation, Path, Predefs, ScanOptions) ->
case open(Ifile, StartLocation, Path, Predefs, ScanOptions) of
{ok,Epp} ->
Forms = parse_file(Epp),
close(Epp),
Expand Down Expand Up @@ -273,30 +295,31 @@ restore_typed_record_fields([{attribute,La,type,{{record,Record},Fields,[]}}|
restore_typed_record_fields([Form|Forms]) ->
[Form|restore_typed_record_fields(Forms)].

%% server(StarterPid, FileName, Location, Path, PreDefMacros)
server(Pid, Name, AtLocation, Path, Pdm) ->
%% server(StarterPid, FileName, Location, Path, PreDefMacros, Opts)
server(Pid, Name, AtLocation, Path, Pdm, Opts) ->
process_flag(trap_exit, true),
case file:open(Name, [read]) of
{ok,File} ->
init_server(Pid, Name, File, AtLocation, Path, Pdm, false);
init_server(Pid, Name, File, AtLocation, Path, Pdm, false, Opts);
{error,E} ->
epp_reply(Pid, {error,E})
end.

%% server(StarterPid, FileName, IoDevice, Location, Path, PreDefMacros)
server(Pid, Name, File, AtLocation, Path, Pdm) ->
%% server(StarterPid, FileName, IoDevice, Location, Path, PreDefMacros, Opts)
server(Pid, Name, File, AtLocation, Path, Pdm, Opts) ->
process_flag(trap_exit, true),
init_server(Pid, Name, File, AtLocation, Path, Pdm, true).
init_server(Pid, Name, File, AtLocation, Path, Pdm, true, Opts).

init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre) ->
init_server(Pid, Name, File, AtLocation, Path, Pdm, Pre, Opts) ->
Ms0 = predef_macros(Name),
case user_predef(Pdm, Ms0) of
{ok,Ms1} ->
epp_reply(Pid, {ok,self()}),
%% ensure directory of current source file is first in path
Path1 = [filename:dirname(Name) | Path],
St = #epp{file=File, location=AtLocation, delta=0, name=Name,
name2=Name, path=Path1, macs=Ms1, pre_opened = Pre},
name2=Name, path=Path1, macs=Ms1, pre_opened = Pre,
range = lists:member(range, Opts)},
From = wait_request(St),
enter_file_reply(From, Name, AtLocation, AtLocation),
wait_req_scan(St);
Expand Down Expand Up @@ -352,6 +375,10 @@ user_predef([M|Pdm], Ms) when is_atom(M) ->
user_predef([Md|_Pdm], _Ms) -> {error,{bad,Md}};
user_predef([], Ms) -> {ok,Ms}.

-spec scan_options(#epp{}) -> ['end'].
scan_options(#epp{range = Range}) ->
[ 'end' || Range ].

%% wait_request(EppState) -> RequestFrom
%% wait_req_scan(EppState)
%% wait_req_skip(EppState, SkipIstack)
Expand All @@ -362,7 +389,9 @@ 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)),
Macs = dict:to_list(St#epp.macs),
Macs2 = [ {Arity, Def} || {Arity, Def, _File} <- Macs ],
epp_reply(From, Macs2),
wait_request(St);
{epp_request,From,close} ->
close_file(St),
Expand Down Expand Up @@ -414,7 +443,8 @@ enter_file(NewName, Inc, From, St) ->
enter_file2(NewF, Pname, From, St, AtLocation) ->
Loc = start_loc(AtLocation),
enter_file_reply(From, Pname, Loc, AtLocation),
Ms = dict:store({atom,'FILE'}, {none,[{string,Loc,Pname}]}, St#epp.macs),
Ms = dict:store({atom,'FILE'},
{none,[{string,Loc,Pname}],nofile}, St#epp.macs),
%% update the head of the include path to be the directory of the new
%% source file, so that an included file can always include other files
%% relative to its current location (this is also how C does it); note
Expand Down Expand Up @@ -460,7 +490,7 @@ leave_file(From, St) ->
name2=OldName2} = OldSt,
CurrLoc = add_line(OldLoc, Delta),
Ms = dict:store({atom,'FILE'},
{none,[{string,CurrLoc,OldName2}]},
{none,[{string,CurrLoc,OldName2}],nofile},
St#epp.macs),
NextSt = OldSt#epp{sstk=Sts,macs=Ms},
enter_file_reply(From, OldName, CurrLoc, CurrLoc),
Expand All @@ -483,7 +513,7 @@ leave_file(From, St) ->
%% scan_toks(Tokens, From, EppState)

scan_toks(From, St) ->
case io:scan_erl_form(St#epp.file, '', St#epp.location) of
case io:scan_erl_form(St#epp.file, '', St#epp.location, scan_options(St)) of
{ok,Toks,Cl} ->
scan_toks(Toks, From, St#epp{location=Cl});
{error,E,Cl} ->
Expand Down Expand Up @@ -581,7 +611,8 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',Lc}|Toks], _Def, From, St)
false ->
scan_define_cont(From, St,
{atom, M},
{none, {none,Expansion}})
{none,
{none,Expansion,St#epp.name2}})
end;
{ok, _PreDef} ->
%% Predefined macros: cannot be overloaded
Expand All @@ -590,7 +621,7 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',Lc}|Toks], _Def, From, St)
error ->
scan_define_cont(From, St,
{atom, M},
{none, {none,Expansion}})
{none, {none,Expansion,St#epp.name2}})
end;
{error,ErrL,What} ->
epp_reply(From, {error,{ErrL,epp,What}}),
Expand All @@ -610,15 +641,16 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St)
wait_req_scan(St);
false ->
scan_define_cont(From, St, {atom, M},
{Len, {As, Me}})
{Len, {As, Me, St#epp.name2}})
end;
{ok, _PreDef} ->
%% Predefined macros: cannot be overloaded
%% (There are currently no predefined F(...) macros.)
epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}),
wait_req_scan(St);
error ->
scan_define_cont(From, St, {atom, M}, {Len, {As, Me}})
scan_define_cont(From, St, {atom, M},
{Len, {As, Me, St#epp.name2}})
end;
{error,ErrL,What} ->
epp_reply(From, {error,{ErrL,epp,What}}),
Expand Down Expand Up @@ -651,7 +683,7 @@ scan_define_cont(F, St, M, {Arity, Def}) ->
wait_req_scan(St)
end.

macro_uses({_Args, Tokens}) ->
macro_uses({_Args, Tokens, _File}) ->
Uses0 = macro_ref(Tokens),
lists:usort(Uses0).

Expand Down Expand Up @@ -838,7 +870,8 @@ scan_endif(_Toks, Endif, From, St) ->
scan_file([{'(',_Llp},{string,_Ls,Name},{',',_Lc},{integer,_Li,Ln},{')',_Lrp},
{dot,_Ld}], Tf, From, St) ->
enter_file_reply(From, Name, Ln, neg_line(abs_loc(Tf))),
Ms = dict:store({atom,'FILE'}, {none,[{string,1,Name}]}, St#epp.macs),
Ms = dict:store({atom,'FILE'},
{none,[{string,1,Name}],nofile}, St#epp.macs),
Locf = loc(Tf),
NewLoc = new_location(Ln, St#epp.location, Locf),
{line, Line} = erl_scan:token_info(Tf, line),
Expand Down Expand Up @@ -918,16 +951,20 @@ expand_macros(Type, MacT, M, Toks, Ms0) ->
%% (Type will always be 'atom')
{Ms, U} = Ms0,
Lm = loc(MacT),
Tinfo = element(2, MacT),
case expand_macro1(Type, Lm, M, Toks, Ms) of
{ok,{none,Exp}} ->
{ok,{none,Exp,File}} ->
check_uses([{{Type,M}, none}], [], U, Lm),
Toks1 = expand_macros(expand_macro(Exp, Tinfo, [], dict:new()), Ms0),
expand_macros(Toks1++Toks, Ms0);
{ok,{As,Exp}} ->
Tinfo = element(2, MacT),
Toks1 = expand_macro(Exp, Tinfo, [], dict:new(), File),
io:format("~p~n", [Toks1]),
Toks2 = expand_macros(Toks1, Ms0),
expand_macros(Toks2++Toks, Ms0);
{ok,{As,Exp,File}} ->
check_uses([{{Type,M}, length(As)}], [], U, Lm),
{Bs,Toks1} = bind_args(Toks, Lm, M, As, dict:new()),
expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs), Ms0)
{Bs,Lrp,Toks1} = bind_args(Toks, Lm, M, As, dict:new()),
Tinfo = element(2, erl_parse:range_end(Lrp, MacT)),
io:format("~p~n", [expand_macro(Exp, Tinfo, Toks1, Bs, File)]),
expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs, File), Ms0)
end.

expand_macro1(Type, Lm, M, Toks, Ms) ->
Expand Down Expand Up @@ -973,14 +1010,14 @@ get_macro_uses({M,Arity}, U) ->

%% Macro expansion
%% Note: io:scan_erl_form() does not return comments or white spaces.
expand_macros([{'?',_Lq},{atom,_Lm,M}=MacT|Toks], Ms) ->
expand_macros(atom, MacT, M, Toks, Ms);
expand_macros([{'?',Lq},{atom,Lm,M}=MacT|Toks], Ms) ->
expand_macros(atom, erl_parse:range(Lq, Lm, MacT), M, Toks, Ms);
%% Special macros
expand_macros([{'?',_Lq},{var,Lm,'LINE'}=Tok|Toks], Ms) ->
expand_macros([{'?',Lq},{var,Lm,'LINE'}=Tok|Toks], Ms) ->
{line,Line} = erl_scan:token_info(Tok, line),
[{integer,Lm,Line}|expand_macros(Toks, Ms)];
expand_macros([{'?',_Lq},{var,_Lm,M}=MacT|Toks], Ms) ->
expand_macros(atom, MacT, M, Toks, Ms);
[erl_parse:range(Lq, Lm, {integer,Lm,Line})|expand_macros(Toks, Ms)];
expand_macros([{'?',Lq},{var,Lm,M}=MacT|Toks], Ms) ->
expand_macros(atom, erl_parse:range(Lq, Lm, MacT), M, Toks, Ms);
%% Illegal macros
expand_macros([{'?',_Lq},Token|_Toks], _Ms) ->
T = case erl_scan:token_info(Token, text) of
Expand All @@ -998,16 +1035,16 @@ expand_macros([], _Ms) -> [].
%% bind_args(Tokens, MacroLocation, MacroName, ArgumentVars, Bindings)
%% Collect the arguments to a macro call.

bind_args([{'(',_Llp},{')',_Lrp}|Toks], _Lm, _M, [], Bs) ->
{Bs,Toks};
bind_args([{'(',_Llp},{')',Lrp}|Toks], _Lm, _M, [], Bs) ->
{Bs,Lrp,Toks};
bind_args([{'(',_Llp}|Toks0], Lm, M, [A|As], Bs) ->
{Arg,Toks1} = macro_arg(Toks0, [], []),
macro_args(Toks1, Lm, M, As, store_arg(Lm, M, A, Arg, Bs));
bind_args(_Toks, Lm, M, _As, _Bs) ->
throw({error,Lm,{mismatch,M}}). % Cannot happen.

macro_args([{')',_Lrp}|Toks], _Lm, _M, [], Bs) ->
{Bs,Toks};
macro_args([{')',Lrp}|Toks], _Lm, _M, [], Bs) ->
{Bs,Lrp,Toks};
macro_args([{',',_Lc}|Toks0], Lm, M, [A|As], Bs) ->
{Arg,Toks1} = macro_arg(Toks0, [], []),
macro_args(Toks1, Lm, M, As, store_arg(Lm, M, A, Arg, Bs));
Expand Down Expand Up @@ -1091,32 +1128,30 @@ macro_arg([], _E, Arg) ->
%% argument values, inserting the location of first the macro call
%% and then the macro arguments, i.e. simulate textual expansion.

expand_macro([{var,_Lv,V}|Ts], L, Rest, Bs) ->
expand_macro([{var,_Lv,V}|Ts], L, Rest, Bs, File) ->
case dict:find(V, Bs) of
{ok,Val} ->
%% lists:append(Val, expand_macro(Ts, L, Rest, Bs));
expand_arg(Val, Ts, L, Rest, Bs);
expand_arg(Val, Ts, L, Rest, Bs, File);
error ->
[{var,L,V}|expand_macro(Ts, L, Rest, Bs)]
[{var,L,V}|expand_macro(Ts, L, Rest, Bs, File)]
end;
expand_macro([{'?', _}, {'?', _}, {var,_Lv,V}|Ts], L, Rest, Bs) ->
expand_macro([{'?', _}, {'?', _}, {var,_Lv,V}|Ts], L, Rest, Bs, File) ->
case dict:find(V, Bs) of
{ok,Val} ->
%% lists:append(Val, expand_macro(Ts, L, Rest, Bs));
expand_arg(stringify(Val, L), Ts, L, Rest, Bs);
expand_arg(stringify(Val, L), Ts, L, Rest, Bs, File);
error ->
[{var,L,V}|expand_macro(Ts, L, Rest, Bs)]
[{var,L,V}|expand_macro(Ts, L, Rest, Bs, File)]
end;
expand_macro([T|Ts], L, Rest, Bs) ->
[setelement(2, T, L)|expand_macro(Ts, L, Rest, Bs)];
expand_macro([], _L, Rest, _Bs) -> Rest.
expand_macro([T|Ts], L, Rest, Bs, File) ->
[setelement(2, T, L)|expand_macro(Ts, L, Rest, Bs, File)];
expand_macro([], _L, Rest, _Bs, _File) -> Rest.

expand_arg([A|As], Ts, _L, Rest, Bs) ->
%% It is not obvious that the location of arguments should replace L.
NextL = element(2, A),
[A|expand_arg(As, Ts, NextL, Rest, Bs)];
expand_arg([], Ts, L, Rest, Bs) ->
expand_macro(Ts, L, Rest, Bs).
expand_arg([A|As], Ts, L, Rest, Bs, File) ->
[A|expand_arg(As, Ts, L, Rest, Bs, File)];
expand_arg([], Ts, L, Rest, Bs, File) ->
expand_macro(Ts, L, Rest, Bs, File).

%%% stringify(Ts, L) returns a list of one token: a string which when
%%% tokenized would yield the token list Ts.
Expand All @@ -1140,9 +1175,11 @@ stringify1([]) ->
stringify1([T | Tokens]) ->
[io_lib:format(" ~s", [token_src(T)]) | stringify1(Tokens)].

stringify(Ts, L) ->
stringify([T|_]=Ts, _L) ->
[$\s | S] = lists:flatten(stringify1(Ts)),
[{string, L, S}].
Lb = element(2, T),
Le = element(2, lists:last(Ts)),
[erl_parse:range_end(Le, {string, Lb, S})].

%% epp_request(Epp)
%% epp_request(Epp, Request)
Expand Down

0 comments on commit 2c8038c

Please sign in to comment.