How to call macro from  one SAS program in another SAS program?

labradorchik
labradorchik used Ask the Experts™
on
How can I call macro from one SAS program in another SAS program?

This is my SAS code with calling macro, which gets a warning "WARNING: Apparent symbolic reference MYMACRO not resolved."  
I thought this is a correct way to call macro. Anyone see a problem with the way I am calling my macro?

DATA testnbrs;
  INFILE 'phonenum.dat' TRUNCOVER;
    INPUT numbs $ 1-10 rcode $ 14-16;
RUN;

PROC PRINT DATA = testnbrs NOOBS;
  WHERE numbs = "&mymacro";
   TITLE "Testing ALL Phone Numbers";
RUN;

Open in new window



Below is the macro SAS code that I am trying to run:
%macro mymacro(numbs,rcode);
  if verify(trim(&numbs),"0123456789") > 0 then
    do;
      &numbs =" ";
      &rcode="1";
    end;

  else if length(trim(&numbs)) ^=10           then
    do;
      &numbs =" ";
      &rcode="1";
    end;

  else if length(substr(&numbs,1,10)) =10  then
    do;
      if verify(trim(&numbs),"0123456789") = 0 then
        do;
          &rcode="0";
        end;

if substr(&numbs,1,1) in ("0","1")        then
        do;
          &numbs =" ";
          &rcode="1";
        end;

      else if substr(&numbs,4,1) in ("0","1")   then
        do;
          &numbs =" ";
          &rcode="1";
        end;

      else if substr(&numbs,2,1)="9"            then
        do;
          &numbs =" ";
          &rcode="1";
        end;

   isame = 1;
      do i = 5 to 10;
        if substr(&numbs,4,1) not = substr(&numbs,i,1) then 
           isame = 0;
      end;

      if isame = 1 then
        do;
          &numbs =" ";
          &rcode="1";
        end;
    end;
%mend mymacro;

Open in new window


Input file phonenum.dat has the following numbers (for testing purposes):
012345678h
0123456789
9876543210
0987654321
0112345678
8876543210
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
IanStatistician
Commented:
Hi there labradorchik,

As someone new to SAS you have a little confusion which is understandable.

First to call a macro the code is

----

"%mymacro(repl1, repl2)"

not

"&mymacro"

& introduces macro variables which give you a straight substitution of current value of the macro variable into your code

% introduces some sort of macro processing, in this case definition of a macro (%macro) or calling up of a macro (%mymacro).  It also introduces macro processing of macro functions - %substr()

----

Inside your macro you have used nums and rcode (as formal parameters) which are the same names you will need to use as actual parameters.  There is nothing wrong with doing that, except it can make it easier for you to make a mistake in the calling sequence, especially if you use the macro in more than one place.

In the macro definition I would tend to use a name like NN and RC (instead of nums and rcode) and then when calling the macro I would show the actual replacements by
%mymacro(nums, rcode)

------------

Note there is no need to have a semicolon after the macro call.  If you put one there then SAS will insert a semicolon in the code stream after inserting the macro code, which is almost always OK, but it is not hard to construct macro sequences where you do not want a semicolon.  Beware!

------------------

It is perfectily legit to call a macro up inside the proc (proc print in this case), but the resolved macro code must be appropriate for proc print.  In this case the code inside your macro is not appropriate for proc print.

---------------

Your macro definition is OK using "positional parameters", but here you should also consider using "keyword parameters", just to help you not make mistakes.  For example use NN and RC as formal parameters and default values with a bit of meaning or (perhaps in the case of a constant a useful value like ..., limit=100, ...)

Example of such a sequence -

%macro numbersTest(NN=numberField, RC=returnCode);
...     
      if substr(&NN, 7, 2) = "62" then &RC = "1";
....

%mend numbersTest;

Open in new window

and call up the macro using the actual variable names you are using in this instance

%numbersTest(NN=nums, RC=rcode)

Open in new window

----------

Your test list of numbers needs to be extended to include more tests, especially a test case like

01234444444
21237666666
06666666666
44444444444
00000000000

Open in new window


to test the later part of your macro.

---------

Your input data step is trying to read rcode, which is set to missing because of the TRUNCOVER.  This works OK, but is not the best way of doing things.  You dont need to read in rcode.  Just using it (you could say  rcode = "0";) is enough to get it defined.

---------

I assume that you want to use  rcode  as the return code to test.  Your "where statement"  uses nums

----------

Something like the following would be necessary to get this to run.

/* first define the macro  */

%macro numbersTest(numbs,rcode);
  if verify(trim(&numbs),"0123456789") > 0 then
    do;
      &numbs =" ";
      &rcode="1";
    end;

  else if length(trim(&numbs)) ^=10           then
    do;
      &numbs =" ";
      &rcode="1";
    end;

  else if length(substr(&numbs,1,10)) =10  then
    do;
      if verify(trim(&numbs),"0123456789") = 0 then
        do;
          &rcode="0";
        end;

if substr(&numbs,1,1) in ("0","1")        then
        do;
          &numbs =" ";
          &rcode="1";
        end;

      else if substr(&numbs,4,1) in ("0","1")   then
        do;
          &numbs =" ";
          &rcode="1";
        end;

      else if substr(&numbs,2,1)="9"            then
        do;
          &numbs =" ";
          &rcode="1";
        end;

   isame = 1;
      do i = 5 to 10;
        if substr(&numbs,4,1) not = substr(&numbs,i,1) then 
           isame = 0;
      end;

      if isame = 1 then
        do;
          &numbs =" ";
          &rcode="1";
        end;
    end;
%mend numbersTest; 


/*  Now run the testing code */

DATA testnbrs;
  INFILE 'phonenum.dat' TRUNCOVER;
    INPUT numbs $ 1-10;
 
  /* now check the validity of the phone numbers */
  /* variable rcode is set to reflect the validity of numbs */
  %numbersTest(numbs, rcode)

RUN;

/* summary print */
PROC PRINT DATA = testnbrs NOOBS;
  WHERE  (rcode = "0");

   TITLE "Testing ALL Phone Numbers, numbers with rcode of zero";

RUN; 


PROC PRINT DATA = testnbrs NOOBS;
  WHERE  (rcode = "1");

   TITLE "Testing ALL Phone Numbers, numbers with rcode of one";

RUN; 

Open in new window


Hope this helps,

We all have been through these problems and there is nothing worse than making mistakes because some concepts are a bit mysterious. I have fallen into many traps that way.

Ian

Author

Commented:
ShannonEE, thank you very much for your in-depth explanation!!
I can see where my problem was, but I am still getting the same error:

WARNING: Apparent invocation of macro NUMBERSTEST not resolved.

What I did I just run the SAS code that invokes the macro. I noticed that you run both (macro and the code which invokes macro in the same SAS program), but I need to keep them separated so this macro code can be invoked from any other programs as well. These have to be two different SAS programs. I assume that macro "numbersTest" would be a global variable and need to be available at any time I need to invoke it.  
How can I do that?
Statistician
Commented:
Hi there labradorchik,

You can use

%include " ....  localMacros.sas";

where you specify the location of your sas file with the macros in it.  This way it is possible to have a whole locally developed library of macros defined there.

There is another way - called "AUTOCALL" macros.  The name of the file that is automatically loaded is the macro name (with  ".sas"   extrension); The contents of the file is usually the macro definition, though you can add other stuff as well.  For example

%put NOTE:  Macro exampleTest(NN, RC)  has been included;

or you could define and initialise global macro variables and define "private" macros that your main macro will call.

Question is Where is the sas auto call library?

Run this code -

proc options option=sasautos long; run; quit;

which will print out (in the log) the definition that is used.  The definition will list
at least 1, maybe more file references.  One of those file references will
most likely be  SASAUTOS.

If you run the following code

filename SASAUTOS list;

it will tell you (in the log) where the autocall library is (if it has been defined).  If there are other
filerefs in the list you can also check them by the same technique.

If you have write permissions to any of those places you can put your files there.

However is is probably best not to put your macros in the folder that SASAUTOS point to as that is for SAS instute supplied macros.

You can include your own folder into the sas autocall library list by
/* specify FOLDER where you want to store all the macros */
/* *******    you fill in the dots *******      */
filename dateauto " ..... ";

/* make sure facility is turned on */
options mautosource;

/* access your date macros as well as SAS supplied macros */
sasautos=(dateauto sasautos); 

Open in new window

However you dont want to have to include that every time, so on your sas installation search for sas files  autoexec.sas and autoexec_usermods.sas.

Depending your SAS version there may or may not be a  "usermods" file.  If there is, use that file, otherwise use the autoexec.sas file.

Put those statements about sasautos in the autoexec.sas file. Then every time SAS starts the autoexec file is executed and the autocall macro library will be defined.  So you can execute macros just by

%numbersTest(numbs, rcode)

or whatever.  Provided that you have a file numbersTest.sas  in your defined  autocall library.  ((Note the macro will only be established to sas when it if fetched by saying %numbersTest  not when the autoexec.sas is executed.))

Note that the autocall facility only loads the file once, so that if you edit the macro, the new definition WILL NOT be used until you restart SAS.  To debug a macro you can either have it before the code you where you want to use it, or use the
%include
statement to always include it (while debugging) before it is used.

Hope this helps.
Ian

See:  
Building and Using Macro Libraries

Creating an autocall library

and    Selected Autocall Macros
IanStatistician

Commented:
Slight typing error  (on last line)  "options" missing.

* specify FOLDER where you want to store all the macros */
/* *******    you fill in the dots *******      */
filename dateauto " ..... ";

/* make sure facility is turned on */
options mautosource;

/* access your date macros as well as SAS supplied macros */
options  sasautos=(dateauto sasautos);  

Open in new window

Author

Commented:
Worked as a charm! :) I used %include statement. Thank you very much for all your comments!!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial