From 0d15f8ab1293a611831840df75ea40eee41b86a2 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Tue, 1 Dec 2009 22:52:11 +0100 Subject: epp: fix bug in the function scan_undef The dict St#epp.uses that helps to find circular macros was not correctly updated. --- lib/stdlib/src/epp.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 8b702c005b..02cfd5a378 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -576,11 +576,11 @@ add_macro_uses([{Key, Def} | Rest], D0) -> %% scan_undef(Tokens, UndefToken, From, EppState) scan_undef([{'(',_Llp},{atom,_Lm,M},{')',_Lrp},{dot,_Ld}], _Undef, From, St) -> - scan_toks(From, St#epp{macs=dict:erase({atom,M}, St#epp.macs), - uses=all_macro_uses(St#epp.macs)}); + Macs = dict:erase({atom,M}, St#epp.macs) + scan_toks(From, St#epp{macs=Macs, uses=all_macro_uses(Macs)}); scan_undef([{'(',_Llp},{var,_Lm,M},{')',_Lrp},{dot,_Ld}], _Undef, From,St) -> - scan_toks(From, St#epp{macs=dict:erase({atom,M}, St#epp.macs), - uses=all_macro_uses(St#epp.macs)}); + Macs = dict:erase({atom,M}, St#epp.macs), + scan_toks(From, St#epp{macs=Macs, uses=all_macro_uses(Macs)}); scan_undef(_Toks, Undef, From, St) -> epp_reply(From, {error,{loc(Undef),epp,{bad,undef}}}), wait_req_scan(St). -- cgit v1.2.3 From 5023556e555fd99d6b5bbe77707066371e38abd7 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Fri, 4 Dec 2009 21:58:10 +0100 Subject: epp: Add support of macros overloading This feature simplifies the definition of macros by avoiding to have a different name for each version (with different arities) of the same macros. New rules: - can have multiple definitions of the same macro with different arities - cannot overload macro with the same arity - the overloading of predefined macros (?MODULE, ?LINE, ...) is forbidden - the directive '-undef' removes all definitions of a macro --- lib/stdlib/src/epp.erl | 193 ++++++++++++++++++++++++------------------ lib/stdlib/test/epp_SUITE.erl | 81 ++++++++++++++++-- 2 files changed, 183 insertions(+), 91 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index 02cfd5a378..b7b759b7c2 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -262,14 +262,15 @@ user_predef([{M,Val}|Pdm], Ms) when is_atom(M) -> {error,{redefine,M}}; error -> Exp = erl_parse:tokens(erl_parse:abstract(Val)), - user_predef(Pdm, dict:store({atom,M}, {none,Exp}, Ms)) + user_predef(Pdm, dict:store({atom,M}, [{none, {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)) + user_predef(Pdm, + dict:store({atom,M}, [{none, {none,[{atom,1,true}]}}], Ms)) end; user_predef([Md|_Pdm], _Ms) -> {error,{bad,Md}}; user_predef([], Ms) -> {ok,Ms}. @@ -476,57 +477,55 @@ scan_extends(_Ts, _As, Ms) -> Ms. %% scan_define(Tokens, DefineToken, From, EppState) -scan_define([{'(',_Lp},{atom,_Lm,M}=Mac,{',',_Lc}|Toks], _Def, From, St) -> +scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',_Lc}|Toks], _Def, From, St) + when Type =:= atom; Type =:= var -> case dict:find({atom,M}, St#epp.macs) of - {ok,_OldDef} -> - epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), - wait_req_scan(St); - error -> - scan_define_cont(From, St, - {atom, M}, - {none,macro_expansion(Toks)}) + {ok, Defs} when is_list(Defs) -> + %% User defined macros: can be overloaded + case proplists:is_defined(none, Defs) of + true -> + epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), + wait_req_scan(St); + false -> + scan_define_cont(From, St, + {atom, M}, + {none, {none,macro_expansion(Toks)}}) + end; + {ok, _PreDef} -> + %% Predefined macros: cannot be overloaded + epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), + wait_req_scan(St); + error -> + scan_define_cont(From, St, + {atom, M}, + {none, {none,macro_expansion(Toks)}}) end; -scan_define([{'(',_Lp},{atom,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St) -> - case dict:find({atom,M}, St#epp.macs) of - {ok,_Def} -> - epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), - wait_req_scan(St); - error -> - case catch macro_pars(Toks, []) of - {ok, {As, Me}} -> - scan_define_cont(From, St, - {atom, M}, - {As, Me}); - _ -> - epp_reply(From, {error,{loc(Def),epp,{bad,define}}}), - wait_req_scan(St) - end - end; -scan_define([{'(',_Lp},{var,_Lm,M}=Mac,{',',_Lc}|Toks], _Def, From, St) -> - case dict:find({atom,M}, St#epp.macs) of - {ok,_OldDef} -> - epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), - wait_req_scan(St); - error -> - scan_define_cont(From, St, - {atom, M}, - {none,macro_expansion(Toks)}) - end; -scan_define([{'(',_Lp},{var,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St) -> - case dict:find({atom,M}, St#epp.macs) of - {ok,_Def} -> - epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), - wait_req_scan(St); - error -> - case catch macro_pars(Toks, []) of - {ok, {As, Me}} -> - scan_define_cont(From, St, - {atom, M}, - {As, Me}); - _ -> - epp_reply(From, {error,{loc(Def),epp,{bad,define}}}), - wait_req_scan(St) - end +scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St) + when Type =:= atom; Type =:= var -> + case catch macro_pars(Toks, []) of + {ok, {As,Me}} -> + Len = length(As), + case dict:find({atom,M}, St#epp.macs) of + {ok, Defs} when is_list(Defs) -> + %% User defined macros: can be overloaded + case proplists:is_defined(Len, Defs) of + true -> + epp_reply(From,{error,{loc(Mac),epp,{redefine,M}}}), + wait_req_scan(St); + false -> + scan_define_cont(From, St, {atom, M}, + {Len, {As, Me}}) + end; + {ok, _PreDef} -> + %% Predefined macros: cannot be overloaded + epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), + wait_req_scan(St); + error -> + scan_define_cont(From, St, {atom, M}, {Len, {As, Me}}) + end; + _ -> + epp_reply(From, {error,{loc(Def),epp,{bad,define}}}), + wait_req_scan(St) end; scan_define(_Toks, Def, From, St) -> epp_reply(From, {error,{loc(Def),epp,{bad,define}}}), @@ -541,13 +540,11 @@ scan_define(_Toks, Def, From, St) -> %%% the information from St#epp.uses is traversed, and if a circularity %%% is detected, an error message is thrown. -scan_define_cont(F, St, M, Def) -> - Ms = dict:store(M, Def, St#epp.macs), - U = dict:store(M, macro_uses(Def), St#epp.uses), +scan_define_cont(F, St, M, {Arity, Def}) -> + Ms = dict:append_list(M, [{Arity, Def}], St#epp.macs), + U = dict:append_list(M, [{Arity, macro_uses(Def)}], St#epp.uses), scan_toks(F, St#epp{uses=U, macs=Ms}). -macro_uses(undefined) -> - undefined; macro_uses({_Args, Tokens}) -> Uses0 = macro_ref(Tokens), lists:usort(Uses0). @@ -556,31 +553,27 @@ macro_ref([]) -> []; macro_ref([{'?', _}, {'?', _} | Rest]) -> macro_ref(Rest); -macro_ref([{'?', _}, {atom, _, A} | Rest]) -> - [{atom, A} | macro_ref(Rest)]; -macro_ref([{'?', _}, {var, _, A} | Rest]) -> - [{atom, A} | macro_ref(Rest)]; +macro_ref([{'?', _}, {Type, Lm, A} | Rest]) when Type =:= atom; Type =:= var -> + try + Arity = count_args(Rest, Lm, A), + [{{atom, A}, Arity} | macro_ref(Rest)] + catch + _:_ -> + macro_ref(Rest) + end; macro_ref([_Token | Rest]) -> macro_ref(Rest). -all_macro_uses(D0) -> - L = dict:to_list(D0), - D = dict:new(), - add_macro_uses(L, D). - -add_macro_uses([], D) -> - D; -add_macro_uses([{Key, Def} | Rest], D0) -> - add_macro_uses(Rest, dict:store(Key, macro_uses(Def), D0)). - %% scan_undef(Tokens, UndefToken, From, EppState) scan_undef([{'(',_Llp},{atom,_Lm,M},{')',_Lrp},{dot,_Ld}], _Undef, From, St) -> - Macs = dict:erase({atom,M}, St#epp.macs) - scan_toks(From, St#epp{macs=Macs, uses=all_macro_uses(Macs)}); + Macs = dict:erase({atom,M}, St#epp.macs), + Uses = dict:erase({atom,M}, St#epp.uses), + scan_toks(From, St#epp{macs=Macs, uses=Uses}); scan_undef([{'(',_Llp},{var,_Lm,M},{')',_Lrp},{dot,_Ld}], _Undef, From,St) -> Macs = dict:erase({atom,M}, St#epp.macs), - scan_toks(From, St#epp{macs=Macs, uses=all_macro_uses(Macs)}); + Uses = dict:erase({atom,M}, St#epp.uses), + scan_toks(From, St#epp{macs=Macs, uses=Uses}); scan_undef(_Toks, Undef, From, St) -> epp_reply(From, {error,{loc(Undef),epp,{bad,undef}}}), wait_req_scan(St). @@ -819,42 +812,56 @@ expand_macros(Type, MacT, M, Toks, Ms0) -> %% (Type will always be 'atom') {Ms, U} = Ms0, Lm = loc(MacT), - check_uses([{Type,M}], [], U, Lm), Tinfo = element(2, MacT), - case dict:find({Type,M}, Ms) of + case expand_macro1(Type, Lm, M, Toks, Ms) of {ok,{none,Exp}} -> - expand_macros(expand_macro(Exp, Tinfo, Toks, dict:new()), Ms0); + 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}} -> + check_uses([{{Type,M}, length(As)}], [], U, Lm), {Bs,Toks1} = bind_args(Toks, Lm, M, As, dict:new()), - %%io:format("Bound arguments to macro ~w (~w)~n", [M,Bs]), expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs), Ms0); {ok,undefined} -> throw({error,Lm,{undefined,M}}); + {ok,mismatch} -> + throw({error,Lm,{mismatch,M}}); error -> throw({error,Lm,{undefined,M}}) end. -check_uses(undefined, _Anc, _U, _Lm) -> - ok; +expand_macro1(Type, Lm, M, Toks, Ms) -> + Arity = count_args(Toks, Lm, M), + case dict:find({Type,M}, Ms) of + {ok, Defs} when is_list(Defs) -> %% User defined macro + {ok, proplists:get_value(Arity, Defs, + proplists:get_value(none, Defs, + mismatch))}; + {ok, PreDef} -> %% Predefined macro + {ok, PreDef}; + error -> + error + end. + check_uses([], _Anc, _U, _Lm) -> ok; check_uses([M|Rest], Anc, U, Lm) -> case lists:member(M, Anc) of true -> - {_, Name} = M, + {{_, Name},_} = M, throw({error,Lm,{circular,Name}}); false -> L = get_macro_uses(M, U), check_uses(L, [M|Anc], U, Lm), check_uses(Rest, Anc, U, Lm) end. - -get_macro_uses(M, U) -> + +get_macro_uses({M,Arity}, U) -> case dict:find(M, U) of error -> []; {ok, L} -> - L + proplists:get_value(Arity, L, proplists:get_value(none, L, [])) end. %% Macro expansion @@ -907,6 +914,26 @@ store_arg(L, M, _A, [], _Bs) -> store_arg(_L, _M, A, Arg, Bs) -> dict:store(A, Arg, Bs). +%% count_args(Tokens, MacroLine, MacroName) +%% Count the number of arguments in a macro call. +count_args([{'(', _Llp},{')',_Lrp}|_Toks], _Lm, _M) -> + 0; +count_args([{'(',_Llp}|Toks0], Lm, M) -> + {_Arg,Toks1} = macro_arg(Toks0, [], []), + count_args(Toks1, Lm, M, 1); +count_args(_Toks, _Lm, _M) -> + none. + +count_args([{')',_Lrp}|_Toks], _Lm, _M, NbArgs) -> + NbArgs; +count_args([{',',_Lc}|Toks0], Lm, M, NbArgs) -> + {_Arg,Toks1} = macro_arg(Toks0, [], []), + count_args(Toks1, Lm, M, NbArgs+1); +count_args([], Lm, M, _NbArgs) -> + throw({error,Lm,{arg_error,M}}); +count_args(_Toks, Lm, M, _NbArgs) -> + throw({error,Lm,{mismatch,M}}). + %% macro_arg([Tok], [ClosePar], [ArgTok]) -> {[ArgTok],[RestTok]}. %% Collect argument tokens until we hit a ',' or a ')'. We know a %% enough about syntax to recognise "open parentheses" and keep diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index 67e20fd2e1..0055aedda9 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -23,7 +23,7 @@ upcase_mac/1, upcase_mac_1/1, upcase_mac_2/1, variable/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1, - otp_8130/1]). + otp_8130/1, overload_mac/1]). -export([epp_parse_erl_form/2]). @@ -61,8 +61,9 @@ fin_per_testcase(_, Config) -> all(doc) -> ["Test cases for epp."]; all(suite) -> - [rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362, - pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130]. + [rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362, + pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, + overload_mac]. rec_1(doc) -> ["Recursive macros hang or crash epp (OTP-1398)."]; @@ -718,22 +719,22 @@ otp_8130(Config) when is_list(Config) -> {otp_8130_c17, <<"\n-define(A(B), B).\n" "-define(A, 1).\n">>, - {errors,[{{3,9},epp,{redefine,'A'}}],[]}}, + []}, {otp_8130_c18, <<"\n-define(A, 1).\n" "-define(A(B), B).\n">>, - {errors,[{{3,9},epp,{redefine,'A'}}],[]}}, + []}, {otp_8130_c19, <<"\n-define(a(B), B).\n" "-define(a, 1).\n">>, - {errors,[{{3,9},epp,{redefine,a}}],[]}}, + []}, {otp_8130_c20, <<"\n-define(a, 1).\n" "-define(a(B), B).\n">>, - {errors,[{{3,9},epp,{redefine,a}}],[]}}, + []}, {otp_8130_c21, <<"\n-define(A(B, B), B).\n">>, @@ -821,7 +822,8 @@ macs(Epp) -> macro(Epp, N) -> case lists:keyfind({atom,N}, 1, epp:macro_defs(Epp)) of false -> false; - {{atom,N},{_,V}} -> V + {{atom,N},{_,V}} -> V; + {{atom,N},Defs} -> lists:append([V || {_,{_,V}} <- Defs]) end. ifdef(Config) -> @@ -1030,6 +1032,69 @@ ifdef(Config) -> ], ?line [] = run(Config, Ts). + + +overload_mac(doc) -> + ["Advanced test on overloading macros."]; +overload_mac(suite) -> + []; +overload_mac(Config) when is_list(Config) -> + Cs = [ + %% '-undef' removes all definitions of a macro + {overload_mac_c1, + <<"-define(A, a).\n" + "-define(A(X), X).\n" + "-undef(A).\n" + "t1() -> ?A.\n", + "t2() -> ?A(1).">>, + {errors,[{{4,9},epp,{undefined,'A'}}, + {{5,9},epp,{undefined,'A'}}],[]}}, + + %% cannot overload predefined macros + {overload_mac_c2, + <<"-define(MODULE(X), X).">>, + {errors,[{{1,9},epp,{redefine,'MODULE'}}],[]}}, + + %% cannot overload macros with same arity + {overload_mac_c3, + <<"-define(A(X), X).\n" + "-define(A(Y), Y).">>, + {errors,[{{2,9},epp,{redefine,'A'}}],[]}} + ], + ?line [] = compile(Config, Cs), + + Ts = [ + {overload_mac_r1, + <<"-define(A, 1).\n" + "-define(A(X), X).\n" + "-define(A(X, Y), {X, Y}).\n" + "t() -> {?A, ?A(2), ?A(3, 4)}.">>, + {1, 2, {3, 4}}}, + + {overload_mac_r2, + <<"-define(A, 1).\n" + "-define(A(X), X).\n" + "t() -> ?A(?A).">>, + 1}, + + {overload_mac_r3, + <<"-define(A, a).\n" + "-define(A(X,Y), {X,Y}).\n" + "a(X) -> ?A(X,X).\n" + "t() -> ?A(1).">>, + {1,1}}, + + {overload_mac_r4, + <<"-define(A, ?B).\n" + "-define(B, a).\n" + "-define(B(X), {b,X}).\n" + "a(X) -> X.\n" + "t() -> ?A(1).">>, + 1} + ], + ?line [] = run(Config, Ts). + + check(Config, Tests) -> eval_tests(Config, fun check_test/2, Tests). -- cgit v1.2.3 From 27d7ca04eb2933345b5be50870a197a3b19588c0 Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Mon, 7 Dec 2009 16:47:44 +0100 Subject: epp: change rules to choose the right version of a macro Now, when we have only the constant definition of a macro (without arguments), we always use it. In all other cases, we try to find the exact matching definition. We throw an error if we don't find it. --- lib/stdlib/src/epp.erl | 95 ++++++++++++++++++++++++++----------------- lib/stdlib/test/epp_SUITE.erl | 30 +++++++------- 2 files changed, 72 insertions(+), 53 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index b7b759b7c2..d91a4408d7 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -111,18 +111,24 @@ 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({undefined,M,none}) -> + io_lib:format("undefined macro '~s'", [M]); +format_error({undefined,M,A}) -> + io_lib:format("undefined macro '~s/~p'", [M,A]); format_error({depth,What}) -> io_lib:format("~s too deep",[What]); format_error({mismatch,M}) -> - io_lib:format("argument mismatch for macro '~w'", [M]); + io_lib:format("argument mismatch for macro '~s'", [M]); format_error({arg_error,M}) -> - io_lib:format("badly formed argument for macro '~w'", [M]); + io_lib:format("badly formed argument for macro '~s'", [M]); format_error({redefine,M}) -> - io_lib:format("redefining macro '~w'", [M]); -format_error({circular,M}) -> - io_lib:format("circular macro '~w'", [M]); + io_lib:format("redefining macro '~s'", [M]); +format_error({redefine_predef,M}) -> + io_lib:format("redefining predefined macro '~s'", [M]); +format_error({circular,M,none}) -> + io_lib:format("circular macro '~s'", [M]); +format_error({circular,M,A}) -> + io_lib:format("circular macro '~s/~p'", [M,A]); format_error({include,W,F}) -> io_lib:format("can't find include ~s \"~s\"", [W,F]); format_error({illegal,How,What}) -> @@ -258,16 +264,20 @@ user_predef([{M,Val,redefine}|Pdm], Ms) when is_atom(M) -> user_predef(Pdm, dict:store({atom,M}, {none,Exp}, Ms)); user_predef([{M,Val}|Pdm], Ms) when is_atom(M) -> case dict:find({atom,M}, Ms) of - {ok,_Def} -> + {ok,_Defs} when is_list(_Defs) -> %% User defined macros {error,{redefine,M}}; + {ok,_Def} -> %% Predefined macros + {error,{redefine_predef,M}}; error -> Exp = erl_parse:tokens(erl_parse:abstract(Val)), user_predef(Pdm, dict:store({atom,M}, [{none, {none,Exp}}], Ms)) end; user_predef([M|Pdm], Ms) when is_atom(M) -> case dict:find({atom,M}, Ms) of - {ok,_Def} -> + {ok,_Defs} when is_list(_Defs) -> %% User defined macros {error,{redefine,M}}; + {ok,_Def} -> %% Predefined macros + {error,{redefine_predef,M}}; error -> user_predef(Pdm, dict:store({atom,M}, [{none, {none,[{atom,1,true}]}}], Ms)) @@ -493,7 +503,7 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{',',_Lc}|Toks], _Def, From, St) end; {ok, _PreDef} -> %% Predefined macros: cannot be overloaded - epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), + epp_reply(From, {error,{loc(Mac),epp,{redefine_predef,M}}}), wait_req_scan(St); error -> scan_define_cont(From, St, @@ -518,7 +528,7 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St) end; {ok, _PreDef} -> %% Predefined macros: cannot be overloaded - epp_reply(From, {error,{loc(Mac),epp,{redefine,M}}}), + 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}}) @@ -541,9 +551,15 @@ scan_define(_Toks, Def, From, St) -> %%% is detected, an error message is thrown. scan_define_cont(F, St, M, {Arity, Def}) -> - Ms = dict:append_list(M, [{Arity, Def}], St#epp.macs), - U = dict:append_list(M, [{Arity, macro_uses(Def)}], St#epp.uses), - scan_toks(F, St#epp{uses=U, macs=Ms}). + try + Ms = dict:append_list(M, [{Arity, Def}], St#epp.macs), + U = dict:append_list(M, [{Arity, macro_uses(Def)}], St#epp.uses), + scan_toks(F, St#epp{uses=U, macs=Ms}) + catch + _:{error, Line, Reason} -> + epp_reply(F, {error,{Line,epp,Reason}}), + wait_req_scan(St) + end. macro_uses({_Args, Tokens}) -> Uses0 = macro_ref(Tokens), @@ -553,14 +569,12 @@ macro_ref([]) -> []; macro_ref([{'?', _}, {'?', _} | Rest]) -> macro_ref(Rest); -macro_ref([{'?', _}, {Type, Lm, A} | Rest]) when Type =:= atom; Type =:= var -> - try - Arity = count_args(Rest, Lm, A), - [{{atom, A}, Arity} | macro_ref(Rest)] - catch - _:_ -> - macro_ref(Rest) - end; +macro_ref([{'?', _}, {atom, Lm, A} | Rest]) -> + Arity = count_args(Rest, Lm, A), + [{{atom, A}, Arity} | macro_ref(Rest)]; +macro_ref([{'?', _}, {var, Lm, A} | Rest]) -> + Arity = count_args(Rest, Lm, A), + [{{atom, A}, Arity} | macro_ref(Rest)]; macro_ref([_Token | Rest]) -> macro_ref(Rest). @@ -821,26 +835,27 @@ expand_macros(Type, MacT, M, Toks, Ms0) -> {ok,{As,Exp}} -> 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); - {ok,undefined} -> - throw({error,Lm,{undefined,M}}); - {ok,mismatch} -> - throw({error,Lm,{mismatch,M}}); - error -> - throw({error,Lm,{undefined,M}}) + expand_macros(expand_macro(Exp, Tinfo, Toks1, Bs), Ms0) end. expand_macro1(Type, Lm, M, Toks, Ms) -> Arity = count_args(Toks, Lm, M), case dict:find({Type,M}, Ms) of - {ok, Defs} when is_list(Defs) -> %% User defined macro - {ok, proplists:get_value(Arity, Defs, - proplists:get_value(none, Defs, - mismatch))}; + error -> %% macro not found + throw({error,Lm,{undefined,M,Arity}}); + {ok, undefined} -> %% Predefined macro without definition + throw({error,Lm,{undefined,M,Arity}}); + {ok, [{none, Def}]} -> + {ok, Def}; + {ok, Defs} when is_list(Defs) -> + case proplists:get_value(Arity, Defs) of + undefined -> + throw({error,Lm,{mismatch,M}}); + Def -> + {ok, Def} + end; {ok, PreDef} -> %% Predefined macro - {ok, PreDef}; - error -> - error + {ok, PreDef} end. check_uses([], _Anc, _U, _Lm) -> @@ -848,8 +863,8 @@ check_uses([], _Anc, _U, _Lm) -> check_uses([M|Rest], Anc, U, Lm) -> case lists:member(M, Anc) of true -> - {{_, Name},_} = M, - throw({error,Lm,{circular,Name}}); + {{_, Name},Arity} = M, + throw({error,Lm,{circular,Name,Arity}}); false -> L = get_macro_uses(M, U), check_uses(L, [M|Anc], U, Lm), @@ -918,6 +933,8 @@ store_arg(_L, _M, A, Arg, Bs) -> %% Count the number of arguments in a macro call. count_args([{'(', _Llp},{')',_Lrp}|_Toks], _Lm, _M) -> 0; +count_args([{'(', _Llp},{',',_Lc}|_Toks], Lm, M) -> + throw({error,Lm,{arg_error,M}}); count_args([{'(',_Llp}|Toks0], Lm, M) -> {_Arg,Toks1} = macro_arg(Toks0, [], []), count_args(Toks1, Lm, M, 1); @@ -926,6 +943,8 @@ count_args(_Toks, _Lm, _M) -> count_args([{')',_Lrp}|_Toks], _Lm, _M, NbArgs) -> NbArgs; +count_args([{',',_Lc},{')',_Lrp}|_Toks], Lm, M, _NbArgs) -> + throw({error,Lm,{arg_error,M}}); count_args([{',',_Lc}|Toks0], Lm, M, NbArgs) -> {_Arg,Toks1} = macro_arg(Toks0, [], []), count_args(Toks1, Lm, M, NbArgs+1); diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index 0055aedda9..25dd69b6dd 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -467,7 +467,7 @@ otp_6277(Config) when is_list(Config) -> -define(ASSERT, ?MODULE). ?ASSERT().">>, - [{error,{{4,16},epp,{undefined,'MODULE'}}}]}], + [{error,{{4,16},epp,{undefined,'MODULE', none}}}]}], ?line [] = check(Config, Ts), ok. @@ -674,7 +674,7 @@ otp_8130(Config) when is_list(Config) -> {otp_8130_c7, <<"\nt() -> ?A.\n">>, - {errors,[{{2,9},epp,{undefined,'A'}}],[]}}, + {errors,[{{2,9},epp,{undefined,'A', none}}],[]}}, {otp_8130_c8, <<"\n-include_lib(\"$apa/foo.hrl\").\n">>, @@ -684,7 +684,7 @@ otp_8130(Config) when is_list(Config) -> {otp_8130_c9, <<"-define(S, ?S).\n" "t() -> ?S.\n">>, - {errors,[{{2,9},epp,{circular,'S'}}],[]}}, + {errors,[{{2,9},epp,{circular,'S', none}}],[]}}, {otp_8130_c10, <<"\n-file.">>, @@ -746,7 +746,7 @@ otp_8130(Config) when is_list(Config) -> {otp_8130_c23, <<"\n-file(?b, 3).\n">>, - {errors,[{{2,8},epp,{undefined,b}}],[]}}, + {errors,[{{2,8},epp,{undefined,b, none}}],[]}}, {otp_8130_c24, <<"\n-include(\"no such file.erl\").\n">>, @@ -1047,19 +1047,26 @@ overload_mac(Config) when is_list(Config) -> "-undef(A).\n" "t1() -> ?A.\n", "t2() -> ?A(1).">>, - {errors,[{{4,9},epp,{undefined,'A'}}, - {{5,9},epp,{undefined,'A'}}],[]}}, + {errors,[{{4,9},epp,{undefined,'A', none}}, + {{5,9},epp,{undefined,'A', 1}}],[]}}, %% cannot overload predefined macros {overload_mac_c2, <<"-define(MODULE(X), X).">>, - {errors,[{{1,9},epp,{redefine,'MODULE'}}],[]}}, + {errors,[{{1,9},epp,{redefine_predef,'MODULE'}}],[]}}, %% cannot overload macros with same arity {overload_mac_c3, <<"-define(A(X), X).\n" "-define(A(Y), Y).">>, - {errors,[{{2,9},epp,{redefine,'A'}}],[]}} + {errors,[{{2,9},epp,{redefine,'A'}}],[]}}, + + {overload_mac_c4, + <<"-define(A, a).\n" + "-define(A(X,Y), {X,Y}).\n" + "a(X) -> X.\n" + "t() -> ?A(1).">>, + {errors,[{{4,9},epp,{mismatch,'A'}}],[]}} ], ?line [] = compile(Config, Cs), @@ -1078,13 +1085,6 @@ overload_mac(Config) when is_list(Config) -> 1}, {overload_mac_r3, - <<"-define(A, a).\n" - "-define(A(X,Y), {X,Y}).\n" - "a(X) -> ?A(X,X).\n" - "t() -> ?A(1).">>, - {1,1}}, - - {overload_mac_r4, <<"-define(A, ?B).\n" "-define(B, a).\n" "-define(B(X), {b,X}).\n" -- cgit v1.2.3 From ec618ee396decf7afe9ef86b80d205649914badb Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 1 Feb 2010 16:28:30 +0100 Subject: Minor corrections and clean-ups --- lib/stdlib/src/epp.erl | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index d91a4408d7..424aed3d2e 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% -module(epp). @@ -528,6 +528,7 @@ scan_define([{'(',_Lp},{Type,_Lm,M}=Mac,{'(',_Lc}|Toks], Def, From, St) 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 -> @@ -551,12 +552,12 @@ scan_define(_Toks, Def, From, St) -> %%% is detected, an error message is thrown. scan_define_cont(F, St, M, {Arity, Def}) -> - try - Ms = dict:append_list(M, [{Arity, Def}], St#epp.macs), - U = dict:append_list(M, [{Arity, macro_uses(Def)}], St#epp.uses), - scan_toks(F, St#epp{uses=U, macs=Ms}) + Ms = dict:append_list(M, [{Arity, Def}], St#epp.macs), + try dict:append_list(M, [{Arity, macro_uses(Def)}], St#epp.uses) of + U -> + scan_toks(F, St#epp{uses=U, macs=Ms}) catch - _:{error, Line, Reason} -> + {error, Line, Reason} -> epp_reply(F, {error,{Line,epp,Reason}}), wait_req_scan(St) end. @@ -904,7 +905,7 @@ expand_macros([T|Ts], Ms) -> expand_macros([], _Ms) -> []. %% bind_args(Tokens, MacroLocation, MacroName, ArgumentVars, Bindings) -%% Collect the arguments to a macro call and check for correct number. +%% Collect the arguments to a macro call. bind_args([{'(',_Llp},{')',_Lrp}|Toks], _Lm, _M, [], Bs) -> {Bs,Toks}; @@ -912,7 +913,7 @@ 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}}). + throw({error,Lm,{mismatch,M}}). % Cannot happen. macro_args([{')',_Lrp}|Toks], _Lm, _M, [], Bs) -> {Bs,Toks}; @@ -920,9 +921,9 @@ 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)); macro_args([], Lm, M, _As, _Bs) -> - throw({error,Lm,{arg_error,M}}); + throw({error,Lm,{arg_error,M}}); % Cannot happen. macro_args(_Toks, Lm, M, _As, _Bs) -> - throw({error,Lm,{mismatch,M}}). + throw({error,Lm,{mismatch,M}}). % Cannot happen. store_arg(L, M, _A, [], _Bs) -> throw({error,L,{mismatch,M}}); @@ -951,7 +952,7 @@ count_args([{',',_Lc}|Toks0], Lm, M, NbArgs) -> count_args([], Lm, M, _NbArgs) -> throw({error,Lm,{arg_error,M}}); count_args(_Toks, Lm, M, _NbArgs) -> - throw({error,Lm,{mismatch,M}}). + throw({error,Lm,{mismatch,M}}). % Cannot happen. %% macro_arg([Tok], [ClosePar], [ArgTok]) -> {[ArgTok],[RestTok]}. %% Collect argument tokens until we hit a ',' or a ')'. We know a -- cgit v1.2.3 From 77a1272d90f93fba24d56b5121ad13cb4ab57f84 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 1 Feb 2010 16:20:16 +0100 Subject: epp_SUITE: Increase code coverage --- lib/stdlib/test/epp_SUITE.erl | 58 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/test/epp_SUITE.erl b/lib/stdlib/test/epp_SUITE.erl index 25dd69b6dd..9a3ae0baf5 100644 --- a/lib/stdlib/test/epp_SUITE.erl +++ b/lib/stdlib/test/epp_SUITE.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% %% 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 online 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. -%% +%% %% %CopyrightEnd% -module(epp_SUITE). @@ -23,7 +23,7 @@ upcase_mac/1, upcase_mac_1/1, upcase_mac_2/1, variable/1, variable_1/1, otp_4870/1, otp_4871/1, otp_5362/1, pmod/1, not_circular/1, skip_header/1, otp_6277/1, otp_7702/1, - otp_8130/1, overload_mac/1]). + otp_8130/1, overload_mac/1, otp_8388/1]). -export([epp_parse_erl_form/2]). @@ -63,7 +63,7 @@ all(doc) -> all(suite) -> [rec_1, upcase_mac, predef_mac, variable, otp_4870, otp_4871, otp_5362, pmod, not_circular, skip_header, otp_6277, otp_7702, otp_8130, - overload_mac]. + overload_mac, otp_8388]. rec_1(doc) -> ["Recursive macros hang or crash epp (OTP-1398)."]; @@ -1095,6 +1095,50 @@ overload_mac(Config) when is_list(Config) -> ?line [] = run(Config, Ts). +otp_8388(doc) -> + ["OTP-8388. More tests on overloaded macros."]; +otp_8388(suite) -> + []; +otp_8388(Config) when is_list(Config) -> + Dir = ?config(priv_dir, Config), + ?line File = filename:join(Dir, "otp_8388.erl"), + ?line ok = file:write_file(File, <<"-module(otp_8388)." + "-define(LINE, a).">>), + fun() -> + PreDefMacros = [{'LINE', a}], + ?line {error,{redefine_predef,'LINE'}} = + epp:open(File, [], PreDefMacros) + end(), + + fun() -> + PreDefMacros = ['LINE'], + ?line {error,{redefine_predef,'LINE'}} = + epp:open(File, [], PreDefMacros) + end(), + + Ts = [ + {macro_1, + <<"-define(m(A), A).\n" + "t() -> ?m(,).\n">>, + {errors,[{{2,11},epp,{arg_error,m}}],[]}}, + {macro_2, + <<"-define(m(A), A).\n" + "t() -> ?m(a,).\n">>, + {errors,[{{2,12},epp,{arg_error,m}}],[]}}, + {macro_3, + <<"-define(LINE, a).\n">>, + {errors,[{{1,9},epp,{redefine_predef,'LINE'}}],[]}}, + {macro_4, + <<"-define(A(B, C, D), {B,C,D}).\n" + "t() -> ?A(a,,3).\n">>, + {errors,[{{2,8},epp,{mismatch,'A'}}],[]}}, + {macro_5, + <<"-define(Q, {?F0(), ?F1(,,4)}).\n">>, + {errors,[{{1,24},epp,{arg_error,'F1'}}],[]}} + ], + ?line [] = compile(Config, Ts), + ok. + check(Config, Tests) -> eval_tests(Config, fun check_test/2, Tests). -- cgit v1.2.3 From 1ab56cb24a8a037d61076314230839fdcc325d7d Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Mon, 1 Feb 2010 16:34:22 +0100 Subject: yecc_SUITE: Adjustment for modified error tuple --- lib/parsetools/test/yecc_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/parsetools/test/yecc_SUITE.erl b/lib/parsetools/test/yecc_SUITE.erl index 212557194c..b5da414f7b 100644 --- a/lib/parsetools/test/yecc_SUITE.erl +++ b/lib/parsetools/test/yecc_SUITE.erl @@ -312,7 +312,7 @@ syntax(Config) when is_list(Config) -> ?line {ok, _, []} = yecc:file(Filename, ParserFile3 ++ Ret), %% Note: checking the line numbers. Changes when yeccpre.hrl changes. fun() -> - ?line {error,[{_,[{5,_,{undefined,'F'}}]}, + ?line {error,[{_,[{5,_,{undefined,'F',1}}]}, {_,[{L1,_,{undefined_function,{yeccpars2_2_,1}}}, {L2,_,{bad_inline,{yeccpars2_2_,1}}}]}], []} = compile:file(Parserfile1, [basic_validation,return]), -- cgit v1.2.3