= SAS Macros = SAS is extensible with macros. <> ---- == 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 [[SAS/MacroVariables|Macro Variables]]. === Built-in Variables === There are several built-in macro variables including `%sysdate` (system date [[SASFormats#Datetime|as DATE7]]), or `%systime` (system time [[SASFormats#Datetime|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 [[SAS/MacroFunctions|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; }}} ---- CategoryRicottone