aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/parsetools/test/yecc_SUITE.erl2
-rw-r--r--lib/stdlib/src/epp.erl265
-rw-r--r--lib/stdlib/test/epp_SUITE.erl143
-rw-r--r--system/doc/reference_manual/macros.xml38
4 files changed, 318 insertions, 130 deletions
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]),
diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl
index 8b702c005b..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).
@@ -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,18 +264,23 @@ 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,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} ->
+ {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,[{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 +487,56 @@ 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)})
- 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)})
+ {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_predef,M}}}),
+ wait_req_scan(St);
+ error ->
+ scan_define_cont(From, St,
+ {atom, M},
+ {none, {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
+ %% (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}})
+ 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 +551,17 @@ 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_toks(F, St#epp{uses=U, macs=Ms}).
+scan_define_cont(F, St, M, {Arity, Def}) ->
+ 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} ->
+ epp_reply(F, {error,{Line,epp,Reason}}),
+ wait_req_scan(St)
+ end.
-macro_uses(undefined) ->
- undefined;
macro_uses({_Args, Tokens}) ->
Uses0 = macro_ref(Tokens),
lists:usort(Uses0).
@@ -556,31 +570,25 @@ 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([{'?', _}, {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).
-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) ->
- 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),
+ 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) ->
- 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),
+ 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 +827,57 @@ 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}});
- 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
+ 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}
end.
-check_uses(undefined, _Anc, _U, _Lm) ->
- ok;
check_uses([], _Anc, _U, _Lm) ->
ok;
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),
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
@@ -882,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};
@@ -890,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};
@@ -898,15 +921,39 @@ 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}});
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},{',',_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);
+count_args(_Toks, _Lm, _M) ->
+ none.
+
+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);
+count_args([], Lm, M, _NbArgs) ->
+ throw({error,Lm,{arg_error,M}});
+count_args(_Toks, Lm, M, _NbArgs) ->
+ 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
%% 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..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]).
+ otp_8130/1, overload_mac/1, otp_8388/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, otp_8388].
rec_1(doc) ->
["Recursive macros hang or crash epp (OTP-1398)."];
@@ -466,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.
@@ -673,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">>,
@@ -683,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.">>,
@@ -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">>,
@@ -745,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">>,
@@ -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,113 @@ 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', none}},
+ {{5,9},epp,{undefined,'A', 1}}],[]}},
+
+ %% cannot overload predefined macros
+ {overload_mac_c2,
+ <<"-define(MODULE(X), X).">>,
+ {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'}}],[]}},
+
+ {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),
+
+ 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, ?B).\n"
+ "-define(B, a).\n"
+ "-define(B(X), {b,X}).\n"
+ "a(X) -> X.\n"
+ "t() -> ?A(1).">>,
+ 1}
+ ],
+ ?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).
diff --git a/system/doc/reference_manual/macros.xml b/system/doc/reference_manual/macros.xml
index a1ba182eff..9dd5fc79bd 100644
--- a/system/doc/reference_manual/macros.xml
+++ b/system/doc/reference_manual/macros.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2009</year>
+ <year>2003</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
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.
-
+
</legalnotice>
<title>The Preprocessor</title>
@@ -140,6 +140,38 @@ bar(X) ->
</section>
<section>
+ <title>Macros Overloading</title>
+ <p>It is possible to overload macros, except for predefined
+ macros. An overloaded macro has more than one definition,
+ each with a different number of arguments.</p>
+ <p>The feature was added in Erlang 5.7.5/OTP R13B04.</p>
+ <p>A macro <c>?Func(Arg1,...,ArgN)</c> with a (possibly empty)
+ list of arguments results in an error message if there is at
+ least one definition of <c>Func</c> with arguments, but none
+ with N arguments.</p>
+ <p>Assuming these definitions:</p>
+ <code type="none">
+-define(F0(), c).
+-define(F1(A), A).
+-define(C, m:f).</code>
+ <p>the following will not work:</p>
+ <code type="none">
+f0() ->
+ ?F0. % No, an empty list of arguments expected.
+
+f1(A) ->
+ ?F1(A, A). % No, exactly one argument expected.</code>
+ <p>On the other hand,</p>
+ <code>
+f() ->
+ ?C().</code>
+ <p>will expand to</p>
+ <code>
+f() ->
+ m:f().</code>
+ </section>
+
+ <section>
<title>Flow Control in Macros</title>
<p>The following macro directives are supplied:</p>
<taglist>