Is this a macro bug???

  • Follow


Hi,

I have a "loop" macro as follows:

/*---------------------------------------------------------------------
* PROGRAM:     loop
* DESCRIPTION: A "wrapper" macro to execute code over a list of items
* SAS VERSION: SAS 8.2
* DATE:        24 Apr 2006
* AUTHOR:      Scott Bass
*
* MODIFICATIONS:
* =============
* Date         UID Description
* -----------  --- ----------------------------------------------------
* 24 Apr 2006  SLB Initial revision
* 17 Jul 2006  SLB Added DLM parameter
---------------------------------------------------------------------*/

%macro loop
/*---------------------------------------------------------------------
Invoke the nested macro "%code" over a list of space separated
list of items.
---------------------------------------------------------------------*/
(__LIST__      /* Space or character separated list of items (REQ)   */
,DLM=%str( )   /* Delimiter character (REQ).  Default is a space.    */
,MNAME=code    /* Macro name (Optional).  Default is "%code"         */
);

/*---------------------------------------------------------------------
Usage:

%macro code;
   %put &word;
%mend;
%loop(Hello World);

%let str = Hello,World;
%loop(%bquote(&str),dlm=%bquote(,));

%macro mymacro;
   proc print data=&word;
   run;
%mend;

proc datasets kill nowarn nolist;
quit;

data one;x=1;run; data two;y=2;run;

proc sql noprint;
   select memname into :list separated by '|'
      from dictionary.tables
      where libname = "WORK" and memtype = "DATA"
   ;
quit;
%loop(&list,dlm=|,mname=mymacro);

proc sql noprint;
   select trim(value) into :list separated by '^'
      from your_dataset
   ;
quit;
%loop(&list,dlm=^);


-----------------------------------------------------------------------
Notes:

The nested macro "%code" must be created at run time before calling
this macro.

Use the macro variable "&word" within your %code macro for each token
(word) in the input list.

If your input list has embedded blanks, specify a different dlm value.

---------------------------------------------------------------------*/

%local macro parmerr iter;

/*
%* check input parameters ;
%let macro = &sysmacroname;
%parmv(__LIST__,     _req=1,_words=1,_case=N)

%if (&parmerr) %then %goto quit;
*/

%* make sure the iterator is unique ;
%* if %code resets the iterator problems occur ;
%let iter = _%substr(&sysprocessid,1,6)_;
%local &iter;

%let &iter = 1;
%let word = %scan(%superq(__list__),&&&iter,%str(&dlm));
%do %while (%superq(word) ne %str());
%&mname  /* do not indent macro call */
   %let &iter = %eval(&&&iter+1);
   %let word = %scan(%superq(__list__),&&&iter,%str(&dlm));
%end;

%quit:
%mend;

Now, compare the invocation of %loop in the following scenarios:

options mlogic symbolgen;

%macro test;
   %put *** &word ***;
%mend;
%loop(Hello World,mname=test);

%macro test(parm=);
   %put *** &word ***;
%mend;
%loop(Hello World,mname=test);

%macro test(parm1,parm2,parm3);
   %put *** &word ***;
%mend;
%loop(Hello World,mname=test);

The first version works, the 2nd and 3rd versions don't.  Any ideas why
macro parameters (even if they aren't used) cause the outer %loop macro
variable parsing logic to fail?  Could this be a bug in macro?

Thanks,
Scott
0
Reply sas_l_739 (82) 12/21/2009 6:03:24 AM

Summary:  How does the macro facility know when macro invocation
           is finished?
#iw-value=1

Scott,

The answer is no, it is not a bug.  The question you should ask
is:

    How does the macro facility know when macro invocation is finished?

Answer:

    1) When no parameters, when name is finished invoke named macro.

    2) When parameters (possibly empty), when something indicates
       the parameter list has been passed.

Your last two calls should have been

    %loop(Hello World,mname=test())

since the user, not the LOOP author, should know how his macro is to be
called.

Data_null_'s work around %UNQUOTE works because the closing parenthis
of the %UNQUOTE indates the argument is finished and thus the invocation
of TEST is finished.  He could just as well have used %UPCASE or for
more mystery %FIX where the macro is

    %macro fix ( dummy ) ; %mend ;

Incidentally your indirection with

    %* make sure the iterator is unique ;
    %* if %code resets the iterator problems occur ;
    %let iter = _%substr(&sysprocessid,1,6)_;
    %local &iter;

doesn't work.  Consider

    %macro break ;
       %let iter = nogood ;
    %mend  break ;

    %loop (Hello World, mname = break)

There is no good fix for the problem since macros can see variables
in the outer environment.  I prefer an announced prefix that the user
should avoid.  For a fixed prefix I commonly use __.   Otherwise,
make it a parameter say, PREF=XYZ, and then the reference is
&&&PREF._vname  where VNAME is your choice.

Further more your %LOCAL should precede the assignment as shown
by having an outer variable ITER.

    %global iter ;

    %macro oops ;
        %let iter = nogood ;
        %local iter ;
        %put iter = >>>&iter<<< ;
    %mend  oops ;

    %oops

Ian Whitlock

Date:         Mon, 21 Dec 2009 01:03:24 -0500
From:         Scott Bass <sas_l_739@YAHOO.COM.AU>
Subject:      Is this a macro bug???
Hi,

I have a "loop" macro as follows:

/*---------------------------------------------------------------------
* PROGRAM:     loop
* DESCRIPTION: A "wrapper" macro to execute code over a list of items
* SAS VERSION: SAS 8.2
* DATE:        24 Apr 2006
* AUTHOR:      Scott Bass
*
* MODIFICATIONS:
* =============
* Date         UID Description
* -----------  --- ----------------------------------------------------
* 24 Apr 2006  SLB Initial revision
* 17 Jul 2006  SLB Added DLM parameter
---------------------------------------------------------------------*/

%macro loop
/*---------------------------------------------------------------------
Invoke the nested macro "%code" over a list of space separated
list of items.
---------------------------------------------------------------------*/
(__LIST__      /* Space or character separated list of items (REQ)   */
,DLM=%str( )   /* Delimiter character (REQ).  Default is a space.    */
,MNAME=code    /* Macro name (Optional).  Default is "%code"         */
);

/*---------------------------------------------------------------------
Usage:

%macro code;
    %put &word;
%mend;
%loop(Hello World);

%let str = Hello,World;
%loop(%bquote(&str),dlm=%bquote(,));

%macro mymacro;
    proc print data=&word;
    run;
%mend;

proc datasets kill nowarn nolist;
quit;

data one;x=1;run; data two;y=2;run;

proc sql noprint;
    select memname into :list separated by '|'
       from dictionary.tables
       where libname = "WORK" and memtype = "DATA"
    ;
quit;
%loop(&list,dlm=|,mname=mymacro);

proc sql noprint;
    select trim(value) into :list separated by '^'
       from your_dataset
    ;
quit;
%loop(&list,dlm=^);


-----------------------------------------------------------------------
Notes:

The nested macro "%code" must be created at run time before calling
this macro.

Use the macro variable "&word" within your %code macro for each token
(word) in the input list.

If your input list has embedded blanks, specify a different dlm value.

---------------------------------------------------------------------*/

%local macro parmerr iter;

/*
%* check input parameters ;
%let macro = &sysmacroname;
%parmv(__LIST__,     _req=1,_words=1,_case=N)

%if (&parmerr) %then %goto quit;
*/

%* make sure the iterator is unique ;
%* if %code resets the iterator problems occur ;
%let iter = _%substr(&sysprocessid,1,6)_;
%local &iter;

%let &iter = 1;
%let word = %scan(%superq(__list__),&&&iter,%str(&dlm));
%do %while (%superq(word) ne %str());
%&mname  /* do not indent macro call */
    %let &iter = %eval(&&&iter+1);
    %let word = %scan(%superq(__list__),&&&iter,%str(&dlm));
%end;

%quit:
%mend;

Now, compare the invocation of %loop in the following scenarios:

options mlogic symbolgen;

%macro test;
    %put *** &word ***;
%mend;
%loop(Hello World,mname=test);

%macro test(parm=);
    %put *** &word ***;
%mend;
%loop(Hello World,mname=test);

%macro test(parm1,parm2,parm3);
    %put *** &word ***;
%mend;
%loop(Hello World,mname=test);

The first version works, the 2nd and 3rd versions don't.  Any ideas why
macro parameters (even if they aren't used) cause the outer %loop macro
variable parsing logic to fail?  Could this be a bug in macro?

Thanks,
Scott
0
Reply iw1sas 12/23/2009 6:47:12 PM


1 Replies
206 Views

(page loaded in 0.051 seconds)

Similiar Articles:













7/18/2012 1:31:46 AM


Reply: