Epp Bugfix and macros overloading

View: New views
3 Messages — Rating Filter:   Alert me  

Epp Bugfix and macros overloading

by Christopher Faulet :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi all,

Here are two patches for the module epp in R13B02-1.

* The first one fixes a bug in the function scan_undef. The dict that
helps to find circular macros is not correctly updated. Here is an example:

=============================
-module(test).
-export([test/0]).

-define(FOO(X), ?BAR(X)).
-define(BAR(X), ?FOO(X)).
-undef(FOO).

test() ->
    ?BAR(1).
=============================

On the last line, the preprocessor should found that 'FOO' is undefined.
But, with the current version, it finds a cycle between FOO and BAR.

* The second patch includes the first one and adds support of multiple
definitions for macros in the module epp (i.e. with the same name but
with different arities). This feature wouldn't break any code (I hope so
:) and might be usefull.

Here is an simple example that uses it:

=============================
-module(test).

-export([test/0]).

-define(MY_MACRO(),    foo).
-define(MY_MACRO,      bar).
-define(MY_MACRO(X,Y), {X, Y}).


test() ->  %% return [bar, foo, foo, {foo, bar}]
    [
     ?MY_MACRO,          %% use the 2nd def, replaced by bar
     ?MY_MACRO(),        %% use the 1st def, replaced by foo
     ?MY_MACRO(foo),     %% use the 2nd def, replaced by bar(foo)
     ?MY_MACRO(foo, bar) %% use the 3rd def, replaced by {foo,bar}
    ].

bar(X) ->
    X.
=============================

In this patch, we follow these rules:

- the overloading of predefined macros (?MODULE, ?LINE, ...) is forbidden
- the directive '-undef' removes all definitions of a macro
- when a macro is called, the preprocessor tries to match the definition
with the right number of arguments. If it fails, it uses the constant
definition, if there is one, else returns an error.
- the circular definitions are checked following the previous rule.

It would be interesting to add this feature. What do you think about it?

Best regards,
--
Christopher Faulet <christopher.faulet@...>

--- lib/stdlib/src/epp.erl.orig 2009-09-29 15:44:10.000000000 +0200
+++ lib/stdlib/src/epp.erl 2009-10-05 15:05:45.000000000 +0200
@@ -558,11 +558,11 @@
 %% 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).

--- lib/stdlib/src/epp.erl.orig 2009-10-07 21:32:58.000000000 +0200
+++ lib/stdlib/src/epp.erl 2009-10-07 21:47:22.000000000 +0200
@@ -244,14 +244,15 @@
     {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}.
@@ -458,57 +459,55 @@
 
 %% 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)})
-    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
+    {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},{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}}}),
@@ -523,13 +522,11 @@
 %%% 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).
@@ -538,31 +535,27 @@
     [];
 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) ->
-    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).
@@ -801,14 +794,15 @@
     %% (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}});
@@ -816,27 +810,37 @@
     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))};
+        {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
@@ -889,6 +893,26 @@
 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


signature.asc (268 bytes) Download Attachment

Re: Epp Bugfix and macros overloading

by Robert Virding :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

2009/10/9 christopher faulet <christopher.faulet@...>

> Hi all,
>
> Here are two patches for the module epp in R13B02-1.
>
> * The second patch includes the first one and adds support of multiple
> definitions for macros in the module epp (i.e. with the same name but
> with different arities). This feature wouldn't break any code (I hope so
> :) and might be usefull.
>
> Here is an simple example that uses it:
>
> =============================
> -module(test).
>
> -export([test/0]).
>
> -define(MY_MACRO(),    foo).
> -define(MY_MACRO,      bar).
> -define(MY_MACRO(X,Y), {X, Y}).
>
>
> test() ->  %% return [bar, foo, foo, {foo, bar}]
>    [
>     ?MY_MACRO,          %% use the 2nd def, replaced by bar
>     ?MY_MACRO(),        %% use the 1st def, replaced by foo
>     ?MY_MACRO(foo),     %% use the 2nd def, replaced by bar(foo)
>     ?MY_MACRO(foo, bar) %% use the 3rd def, replaced by {foo,bar}
>    ].
>
> bar(X) ->
>    X.
> =============================
>

I rather like this extension, it is something I always meant to do but never
got around to doing.

I am not sure, though, that I completely agree with how you choose which
definition to use, especially the third case here which I think should give
an undefined macro error. My reasoning is that if you define a macro to have
arguments, one or many definitions, and call it with arguments, even with an
empty argument list then you should only try for the matching definition and
not take the one without arguments. So if:

-define(M, a).
-define(M(), b).
-define(M(X,Y), {X,Y}).

then

?M        - should use 1st def
?M()     - should use 2nd def
?M(a,b) - should use 3rd def
?M(a)   - should generate an error

However if only:

-define(M, a).

then all calls will use this definition.

Robert

Re: Epp Bugfix and macros overloading

by Christopher Faulet :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Robert Virding a écrit :

> I rather like this extension, it is something I always meant to do but never
> got around to doing.
>
> I am not sure, though, that I completely agree with how you choose which
> definition to use, especially the third case here which I think should give
> an undefined macro error. My reasoning is that if you define a macro to have
> arguments, one or many definitions, and call it with arguments, even with an
> empty argument list then you should only try for the matching definition and
> not take the one without arguments. So if:
>
> -define(M, a).
> -define(M(), b).
> -define(M(X,Y), {X,Y}).
>
> then
>
> ?M        - should use 1st def
> ?M()     - should use 2nd def
> ?M(a,b) - should use 3rd def
> ?M(a)   - should generate an error
>
> However if only:
>
> -define(M, a).
>
> then all calls will use this definition.
>
Hi Robert,

Thanks for your comments. You are probably right. Your solution is less
error prone and avoids ambiguities.

I made a new patch that takes your suggestion into account. I also
modified some errors to be more precise in diagnostics.

--
Christopher Faulet <christopher.faulet@...>

--- lib/stdlib/src/epp.erl.orig 2009-10-16 14:11:07.000000000 +0200
+++ lib/stdlib/src/epp.erl 2009-10-16 16:28:14.000000000 +0200
@@ -99,18 +99,24 @@
     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}) ->
@@ -240,18 +246,23 @@
     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}.
@@ -458,57 +469,55 @@
 
 %% 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_predef,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_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}}}),
@@ -523,13 +532,17 @@
 %%% 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}) ->
+    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(undefined) ->
-    undefined;
 macro_uses({_Args, Tokens}) ->
     Uses0 = macro_ref(Tokens),
     lists:usort(Uses0).
@@ -538,31 +551,25 @@
     [];
 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).
@@ -801,42 +808,55 @@
     %% (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
- {ok,{none,Exp}} ->
-    expand_macros(expand_macro(Exp, Tinfo, Toks, dict:new()), Ms0);
- {ok,{As,Exp}} ->
+    case expand_macro1(Type, Lm, M, Toks, Ms) of
+ {ok, {none,Exp}} ->
+    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
+        {ok, [{none, Def}]} ->
+            {ok, Def};
+        {ok, Defs} when is_list(Defs) ->
+            case proplists:get_value(Arity, Defs) of
+                undefined ->
+                    throw({error,Lm,{undefined,M,Arity}});
+                Def ->
+                    {ok, Def}
+            end;
+        {ok, PreDef} -> %% Predefined macro
+            {ok, PreDef};
+        error ->
+            throw({error,Lm,{undefined,M,Arity}})
     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
@@ -889,6 +909,30 @@
 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}}).
+
 %% 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


signature.asc (268 bytes) Download Attachment