Have you ever created a SAS macro variable and at resolution time received a warning that it did not exist? Many times this warning is because your program referenced the macro variable outside the scope it was created in.
Every macro variable created is stored in one of two symbol tables. The symbol table lists the macro variable name and its value and determines its scope. Global macro variables, or those stored in the global symbol table, exist for the duration of the SAS session and can be referenced anywhere except in the CARDS and DATALINES statements. Local macro variables, or those stored in a local symbol table, exist only during the execution of the macro in which the variable is created.
This post will help you determine which scope a macro variable will be defined in. I will also show a nice feature of CALL SYMPUTX for assigning the scope for macro variables. Lastly, I will discuss some SAS functions that help determine if a macro variable exists in one of the two scopes.
More about the two types of scope
Global macro variables include the following:
- all automatic macro variables except SYSPBUFF
- macro variables created outside of any macro definition
- macro variables created on a %GLOBAL statement
- most macro variables created by CALL SYMPUT/CALL SYMPUTX, except in special cases
Local macro variables include the following:
- macro parameters
- macro variables created on a %LOCAL statement
- macro statements that define macro variables within a macro definition, such as %LET and the iterative %DO statement (if the variable does not already exist globally or a %GLOBAL statement is not used)
The following is a nice diagram that illustrates what happens when creating a macro variable. This diagram does not apply when a %LOCAL statement is used to create the variable.
Difference in scope between CALL SYMPUT and %LET
CALL SYMPUT and %LET are the two most popular ways of creating a macro variable so we will focus on those two methods.
In the first example, notice that CALL SYMPUT placed the macro variable on the global symbol table because once it determines the macro variable does not exist, it places it in the first non-empty symbol table it finds, starting with the most local scope. In this case, this is the global table (global table is never empty as it contains the SAS automatic variables).
%macro test; data one; call symput('bbb',100); run; %put _user_; %mend test; %test
SAS Log results:
GLOBAL BBB 100
In this example, %LET placed the macro variable CCC in the local symbol table because once it determines the macro variable does not exist it creates the variable in the scope for the current macro. In this case, this is the local table since we are within the local scope of the macro test2. A macro's local symbol table is empty until the macro creates at least one macro variable.
%macro test2; %let ccc=200; %put _user_; %mend test2; %test2
SAS Log results:
TEST2 CCC 200
Problems with nesting local variables
Local symbol tables can also be nested within each other. In this example we have one global macro variable called OUTER. One local macro variable, AA, local to the macro TEST and one local macro variable, BB, local to the macro test2. Since the macro TEST2 is nested within the TEST macro, the local table for TEST2 is also nested within the local table for TEST. The diagram below shows the intended scope for local variables AA and BB:
Processing the following code reveals problems with referencing:
%let outer=500; %macro test2; %let bb=200; %put &aa; %mend test2; %macro test; %let aa=100; %test2 %put &bb; %mend test; %test
SAS Log results:
100 WARNING: Apparent symbolic reference BB not resolved. &bb
Why did this happen? The first %PUT encountered (%PUT &AA ) resolves to 100 even though we are within the TEST2 macro, but this is because TEST2 is nested within TEST. The second %PUT encountered (%PUT &BB) does not resolve because BB is local to the TEST2 macro and is not known to macro TEST.
Using CALL SYMPUTX to assign scope
To force macro variables created by CALL SYMPUT to be global when the local table is not empty, use a %GLOBAL statement, listing all the variables. This would be difficult if creating a list of macro variables. Now with the addition of CALL SYMPUTX this task is much easier. CALL SYMPUTX contains an argument that specifies the scope in which to place the macro variable. The following values are valid as the first non-blank character in symbol-table:
|G||specifies the global symbol table, even if the local symbol table exists.|
|L||specifies the most local symbol table that exists, which will be the global symbol table, if used outside a macro.|
|F||specifies that if the macro variable exists in any symbol table, CALL SYMPUTX uses the version in the most local symbol table in which it exists. If the macro variable does not exist, CALL SYMPUTX stores the variable in the most local symbol table.|
Suppose you are inside a macro and want to create a macro variable for each observation from a data set. The example below would fail because the macro variables val1-val3 are local to the macro TEST and only exist within the TEST macro. Once TEST has finished executing those macro variables are deleted.
data one; input name $; cards; abc def ghi ; %macro test(dsn); data _null_; set one; call symput('val'||strip(_n_),name); run; %mend test; %test(one); %put &val1 &val2 &val3;
In the past you would have to add something like the following before the CALL SYMPUT statement to make the macro variables global:
call execute('%global val'||strip(_n_)||';');
Now with CALL SYMPUTX this is easy. Just change the CALL SYMPUT above to the following. The ‘g’ argument makes all the macro variables created by this call routine global.
How to determine where macro variables are stored
Here are a few statements that are very helpful in determining which symbol table the macro variables are stored in:
- %PUT _USER_;
- %PUT _LOCAL_;
- %PUT _GLOBAL_;
There are times you may want to find out if a macro variable exists in a certain scope. There are three functions that might help in this situation.
- %SYMEXIST(mac_var) – returns 1 if macro variable exist, otherwise 0
- %SYMGLOBL(mac_var) – returns 1 if macro variable exist in global scope, otherwise 0
- %SYMLOCAL(mac_var) – returns 1 if macro variable exists in local scope, otherwise 0
I hope this blog post has been helpful. If you have other SAS macro questions you’d like me to cover in future blogs, please comment below.