SAS Macros
SAS is extensible with macros.
Contents
Labels
Macro labels are 1 to 32 characters, beginning with a letter or underscore and optionally followed by letters, numbers, and underscores.
Macro values are accessed by prepending the label with a macro trigger; either an ampersand (&) or a percent sign (%).
Note that, similar to Unix shells, the macro processor does not alter single-quoted strings.
Expansion
A macro expands to literal text, which is then interpreted either at compilation or execution. This literal text does not need to follow any syntax rules until it is inserted and interpreted.
Macros often need to contain syntax keywords or operators. In order to defer the interpretation of these grammars, masking functions are employed.
%str and %nrstr mask text until compilation
%quote and %nrquote mask text until execution
%bquote and %nrbquote mask text until execution AND resolve mismatched quote marks
That is, the macro processor believes O'Hara is a syntax error because the string Hara is never finished. Beyond these functions, the resolution would be to escape quote marks with percent signs (%').
%superq prevents interpretation of text entirely
Special characters include (but are not limited to):
semicolons (;)
commas (,)
- operators, including...
arithmetic (+, -, etc.)
expression (<, |, etc.)
mnemonic expression (LE, OR, etc.)
macro triggers (&, %)
The nr versions of the above functions mask all special characters, while the non-nr versions do not mask macro triggers.
Resolution Order
The macro processor scans from right to left in each token. It processes every macro label, macro function, macro statement (i.e. %if), or escaped character (i.e. %') it encounters, except for those masked by a macro function. However, it will re-scan until all ampersands have been cleared. Therefore, double ampersands can be used to 'defer' processing.
%let section1=Math; %let section2=Science; %let section3=Literature; %let n = 2; proc means data=LIBREF.TABLE; where section=&§ion&n; run;
The rabbit hole does not end here.
%let wherevar=section; proc means data=LIBREF.TABLE; where &wherevar=&&&wherevar&n; run;
Statements
Macro statements are exceptional macro programming features.
Include
The %INCLUDE statement takes a filename and inserts its contents into the current program.
%include path/to/syntax.sas
Include and Run
To take user input, try %INCLUDE *;. SAS then waits for user input and will not resume until the user inputs %RUN;.
Variables
Macro variables can be used in any location other than data input. A macro variable's value is accessed by prepending the label with an ampersand (&).
To delimit a macro label from immediately-appended text, use a period. That is, &prefix1 looks-up prefix1, while &prefix.1 looks-up prefix. (To escape the period, use two.)
For more details, see Macro Variables.
Built-in Variables
There are several built-in macro variables including %sysdate (system date as DATE7), or %systime (system time as TIME5).
Functions
Macro functions are identified by a leading percent sign (%). They take one or more positional arguments within parentheses. They otherwise are used like macro variables.
For more details, see Macro Functions.
Quoted Functions
Many macro functions have two versions: one that is interpreted immediately, and one that is masked until execution.
%let a=foo; %let b=%nrstr(&a bar); %let substring1 = %substr(&b,1,2); /* => 'foo', **even though a substring of length 2 was requested** */ %let substring2 = %qsubstr(&b,1,2); /* => '&a' */
Variables as Programs
Disclosure: this is a terrible idea.
%let mymacro=%str(proc means data=LIBREF.TABLE; var foo bar; run;); &mymacro
Programs
Macro programs can be used in any location other than data input. A macro program is executed by prepended the label with an ampersand (%).
Macro programs can be called alone, or can be called with arguments within parentheses.
Do not add a semicolon (;) after a macro program call to terminate the line-the macro expands out to syntax that is correctly terminated.
Comments
Generally, use the /* comment */ style. The alternate options have tricky limitations.
%macro mymacro; /* This is a comment that will be treated literally by the macro processor */ %* This is a comment that the macro processor will interpret, so don t use any special characters like quotes in me; * This is a comment that the macro processor will tokenize, so don't use any macro triggers in me; %mend mymacro;
Arguments and Options
A macro can take both positional arguments and keyword options. Keyword options can also take default values, or be left null.
%macro mymacro(start, stop, opts=, stats=N SUM MIN MAX); proc means data=LIBREF.TABLE &stats &opts; /* default stats are "N SUM MIN MAX", default opts are "" */ where "&start"d <= date <= "&stop"d; run; %mend mymacro; %mymacro(01JAN2020, 01JAN2021)
Looping
Looping a SAS macro is accomplished through one of three structures. The most common structure is iterating over ranges. This is done %do ... %to ... %by ....
%macro mymacro; %* Note: '&year.TABLE' becomes '2000TABLE'-the period is indicating the end of the macro label; %do year=2000 %to 2020 %by 1; /* do something with "&year" */ %end; %mend mymacro; %mymacro
Note that %eval is automatically called on the values supplied to loops.
Other structures are %do %until ... and %do %while ....
Variable Number of Arguments
It is also possible to use a variable number of arguments. This is most feasibly used with a %do %until ... loop.
Enable the parmbuff option and access the arguments from the &syspbuff array.
%macro mymacro / parmbuff; %let index=1; /* initialize the index */ %do %until (%scan(&syspbuff,&index) = ''); /* do something with "%scan(&syspbuff,&index)" */ %let index=%eval(&index + 1); /* increment the index */ %end; %mend mymacro; %mymacro(1, 2, 3)
Conditional Processing
To process a macro statement conditionally, use the %if statement.
if EXPR then ASSIGNMENT; else if EXPR then ASSIGNMENT; else ASSIGNMENT;
Similar to conditional processing in data steps, an %if statement can be followed by %else (or %else %if) statements. And a %do block can be used to conditionally process multiple macro statements.
Similar to looping statements, %eval is automatically called on the expression.
%macro mymacro(dsname); %let dsid = %sysfunc(open(&dsname)); /* open file, and do something different if the id=0 (indicates an error) */ %if (&dsid > 0) %then %do /* do something like "%let nobs = %sysfunc(attrn(&dsid, nobs));" */ %let rc = %sysfunc(close(&dsid)); /* close file, though we can't do anything if the return code is an error */ %end; %else %if %superq(&dsname = "") %then %put You forgot to set the dataset name!; %else %put Could not open the dataset &dsname..; %mend; %mymacro(myds)
Branching
Lastly, SAS macros can use %goto to branch out. This is useful if an error could be reached at multiple points of a program.
%macro mymacro(dsname); %if (&dsname = "") %then %goto error; /* do something */ %goto success; %error: %put You forgot to set the dataset name!; quit; %success: %mend mymacro; %mymacro("")
Macro Programs in the Data Step
A macro program can be called from within a data step using call execute(). execute takes a single string argument-the name of the macro program to run.
While the macro is immediately interpreted, any contained processes or data steps are added to the stack and will execute after the parent data step ends.
Debugging Macros
Set the mcompilenote option to see compilation errors in macro programs logged.
options mcompilenote=all; /* do something */ options mcompilenote=none;
Enable the symbolgen option to see macro variable substitutions logged as they are processed. Enable the mprint option to see macro program commands as they are submitted.
options symbolgen mprint; /* do something */ options nosymbolgen nomprint;
The %put macro command can log arbitrary text.
%if (&dsid=0) %then %put Could not open file &dsname;