Size: 7927
Comment:
|
Size: 12928
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 13: | Line 13: |
Macro variables expand to text values that can be substituted into a program, in any location ''other than data input''. They are identified by a label up to 32 characters, beginning with a letter or underscore and continuing with letters, numbers, and underscores. Note that, similar to Unix shells, the macro processor will not substitute variables into single-quoted strings. |
Macro variables expand to text values that can be substituted into a program, in any location ''other than data input''. They are identified by a label up to 32 characters, beginning with a letter or underscore and continuing with letters, numbers, and underscores. A macro label is prepended with an ampersand (`&`) to trigger macro processing. Note that, similar to Unix shells, variables are not substituted in single-quoted strings. |
Line 21: | Line 21: |
=== Built-in Macros === | === Built-in Macro Variables === |
Line 35: | Line 35: |
These macros are referenced using `&LABEL`. | Macros are referenced using `&LABEL`. A macro definition can reference another macro. For a macro definition to reference a SAS function, the `%sysfunc` macro function must be used. |
Line 44: | Line 44: |
Note: `%sysfunc` cannot take nested functions, but can take nested `%sysfunc` calls. | For considerations on using macro functions including `%sysfunc`, see [[#Macro-Functions|Macro Functions]]. |
Line 83: | Line 83: |
== Macro Programs == === Built-in Macros === SAS comes with many functional macros for processing. |
== Macro 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. === Masking Special Characters === Because macros fundamentally are creating syntax, they often need to contain characters that affect processing. In order to defer the interpretation of these characters, special masking functions are available. Special characters include (but are not limited to): * semicolons (`;`) * commas (`,`) * operators, including... * arithmetic (`+`, `-`, etc.) * expression (`<`, `|`, etc.) * mnemonic expression (`LE`, `OR`, etc.) * macro triggers (`&`, `%`) `%str` and `%nrstr` mask text until compilation, then are interpreted into the syntax. `%quote` and `%nrquote` mask text until execution. The `nr` versions mask all special characters, while the non-`nr` versions do not mask macro triggers. Many macro functions have two versions: one that is interpreted immediately, and one that automatically 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' */ }}} If special characters are at all possible within a macro function, consider using the masked version. === Built-in Macro Functions === There are also a number of computational macro functions. |
Line 93: | Line 125: |
%let a=foo; %let b=%nrstr(&a); %let c=%nrstr(&a bar); }}} ||'''Name''' ||'''Action''' ||'''Example Input''' ||'''Example Result''' || ||`%nrstr(value)` ||Masks special characters inside value || || || |
%let a=Foo; %let b=%nrstr(&a bar); %let c=%nrstr(&a); }}} The following functions can be used as: ||'''Name''' ||'''Action''' ||'''Example Input''' ||'''Example Result''' || |
Line 101: | Line 134: |
||`%qsysfunc(function call)` ||Same, but masks function call || || || ||`%upcase(value)` ||Translates lowercase into uppercase ||`%upcase(&a)` ||'FOO' || || || ||`%upcase(&b)` ||'foo' || ||`%qupcase(value)` ||Same, but masks value ||`%qupcase(&b)` ||'&A' || ||`%lowcase(value)` ||Translates uppercase into lowercase || || || ||`%qlowcase(value)` ||Same, but masks value || || || |
||`%qsysfunc(function call)` ||Same, but masks value || || || ||`%substr(value,position,length)` ||Copies a substring from a value by literal position ||`%substr(&c,1,2)` ||'Foo' || ||`%qsubstr(value,position,length)` ||Same, but masks value ||`%qsubstr(&c,1,2)` ||'&a' || ||`%scan(value,index,delimiter)` ||Copies a substring from a value by parsed position ||`%scan(&c,1,' ')` ||'Foo' || ||`%qscan(value,index,delimiter)` ||Same, but masks value ||`%qscan(&c,1,' ')` ||'&a' || ||`%upcase(value)` ||Translates lowercase into uppercase ||`%upcase(&c)` ||'Foo' || ||`%qupcase(value)` ||Same, but masks value ||`%qupcase(&c)` ||'&A' || ||`%lowcase(value)` position ||Translates uppercase into lowercase ||`%lowcase(&c)` ||'Foo' || ||`%qlowcase(value)` ||Same, but masks value ||`%qlowcase(&c)` ||'&a' || |
Line 111: | Line 147: |
||`%substr(value,position,length)` ||Copies a substring from a value ||`%substr(&c,4,3)` ||'bar' || || || ||`%substr(&a,1,2)` ||'foo' || ||`%qsubstr(value,position,length)` ||Same, but masks value ||`%qsubstr(&a,1,2)` ||'&a' || |
|
Line 117: | Line 150: |
The difference between `sysfunc` and `qsysfunc` is very fine. | === sysfunc === The macro functions `%sysfunc` and `qsysfunc` cannot take nested functions, but macros can be nested without issue. {{{ %let mon_title = %sysfunc(left(%qsysfunc(mdy(&mon,1,&curr_yr), monname9.))); }}} The difference between `sysfunc` and `qsysfunc` is masking of special characters. In this context, the most contentious special character is the comma, which can be confused for an argument delimiter. |
Line 142: | Line 186: |
== Macro Variables as Macro Programs == Disclosure: this is a terrible idea. {{{ %let mymacro=%str(proc means data=LIBREF.TABLE; var foo bar; run;); &mymacro }}} ---- == Macro Programs == Macro programs expand to syntax that is immediately executed in any location ''other than data input''. They are identified by a label up to 32 characters, beginning with a letter or underscore and continuing with letters, numbers, and underscores. A macro label is prepended with an ampersand (`%`) to trigger macro processing. 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; }}} |
|
Line 148: | Line 230: |
proc means data=LIBREF.TABLE &stats &opts; | proc means data=LIBREF.TABLE &stats &opts; /* default stats are "N SUM MIN MAX", default opts are "" */ |
Line 153: | Line 235: |
%mymacro(01JAN2020,01JAN2021) }}} ---- |
%mymacro(01JAN2020, 01JAN2021) }}} |
Line 161: | Line 242: |
It is possible to iterate over ranges using `%do LABEL=START %to END`. The loop value is accessed by `&LABEL`. | 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 ...`. |
Line 165: | Line 246: |
%do year=2000 %to 2020; proc means data=LIBREF.data_from_&year; title "Data from &year"; run; |
%* Note: '&year.TABLE' becomes '2000TABLE'-the period is indicating the end of the macro label; %do year=2000 %to 2020 %by 1; /* do something */ |
Line 175: | Line 255: |
---- | 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 like "%let month = %scan(&syspbuff,&index);" */ %let index=%eval(&index + 1); /* increment the index */ %end; %mend mymacro; %mymacro(1, 2, 3) }}} |
Line 181: | Line 284: |
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. |
|
Line 183: | Line 298: |
/* `open()` returns a positive integer identifying the opened dataset, or 0 on error */ %let dsid = %sysfunc(open(&dsname)); |
%let dsid = %sysfunc(open(&dsname)); /* open file, and do something different if the id=0 (indicates an error) */ |
Line 187: | Line 301: |
%let nobs = %sysfunc(attrn(&dsid, nobs)); %let rc = %sysfunc(close(&dsid)); title "Report on &dsname"; title2 "Number of Observations: &nobs"; proc means data=&dsname; var VAR; run; |
/* 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 */ |
Line 197: | Line 305: |
%else %if (&dsname = "") %then %put You forgot to set the dataset name!; %else %put Could not open the dataset &dsname..; |
|
Line 202: | Line 312: |
---- == Dispatching Macros == |
=== Branching === Lastly, SAS macros can use `%goto` to branch out. {{{ %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("") }}} ---- == Resolution Order of Macros == |
SAS Macros
SAS supports a macro feature. For more options for extending and programming in SAS (or related software), see this crosswalk of programming features.
Contents
Macro Variables
Macro variables expand to text values that can be substituted into a program, in any location other than data input. They are identified by a label up to 32 characters, beginning with a letter or underscore and continuing with letters, numbers, and underscores. A macro label is prepended with an ampersand (&) to trigger macro processing.
Note that, similar to Unix shells, variables are not substituted in single-quoted strings.
To delimit a macro variable label from immediately-appended text, use a period. That is, &prefix1 expects a macro under the name prefix1, while &prefix.1 expects a macro under the name prefix. (To 'escape' the period, use two.)
Built-in Macro Variables
There are several built-in macro variables including %sysdate (system date as DATE7), or %systime (system time as TIME5).
Custom Macros
A macro variable can be defined as:
%let LABEL = VALUE;
Macros are referenced using &LABEL. A macro definition can reference another macro. For a macro definition to reference a SAS function, the %sysfunc macro function must be used.
%let mon = 1; %let curr_yr = %sysfunc(year("&sysdate"d)); %let mon_title = %sysfunc(mdy(&mon,1,&curr_yr), monname9.); %let mon_title = %sysfunc(left(%qsysfunc(mdy(&mon,1,&curr_yr), monname9.)));
For considerations on using macro functions including %sysfunc, see Macro Functions.
Program-Set Macros
A macro variable can also be set by symputx calls. This enables programmatic creation of macros, for example to store aggregated values from a data step.
data _NULL_; set LIBREF.TABLE end=lastobs; retain sum_my_var 0; sum_my_var = sum_my_var + my_var; if lastobs then call symputx('sum_of_my_var', put(sum_my_var, dollar10.2)); run; proc means data=LIBREF.TABLE; title "Allocation of &sum_of_my_var" run;
Note: symputx and symput are very similar. The former is silent (i.e., does not log) and trims leading spaces when converting a value into character data (which all macros must be reduced into.
Scope
A macro variable defined by open code is stored in the global table. (And therefore: a macro variable defined in a separate program is local.) By the nature of SAS, most custom macros will be global.
If necessary, it is possible to force local resolution of a label.
%local A; %let A=B;
Macro 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.
Masking Special Characters
Because macros fundamentally are creating syntax, they often need to contain characters that affect processing. In order to defer the interpretation of these characters, special masking functions are available.
Special characters include (but are not limited to):
semicolons (;)
commas (,)
- operators, including...
arithmetic (+, -, etc.)
expression (<, |, etc.)
mnemonic expression (LE, OR, etc.)
macro triggers (&, %)
%str and %nrstr mask text until compilation, then are interpreted into the syntax. %quote and %nrquote mask text until execution. The nr versions mask all special characters, while the non-nr versions do not mask macro triggers.
Many macro functions have two versions: one that is interpreted immediately, and one that automatically 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' */
If special characters are at all possible within a macro function, consider using the masked version.
Built-in Macro Functions
There are also a number of computational macro functions.
Given:
%let a=Foo; %let b=%nrstr(&a bar); %let c=%nrstr(&a);
The following functions can be used as:
Name |
Action |
Example Input |
Example Result |
%sysfunc(function call) |
Evaluates a function call |
|
|
%qsysfunc(function call) |
Same, but masks value |
|
|
%substr(value,position,length) |
Copies a substring from a value by literal position |
%substr(&c,1,2) |
'Foo' |
%qsubstr(value,position,length) |
Same, but masks value |
%qsubstr(&c,1,2) |
'&a' |
%scan(value,index,delimiter) |
Copies a substring from a value by parsed position |
%scan(&c,1,' ') |
'Foo' |
%qscan(value,index,delimiter) |
Same, but masks value |
%qscan(&c,1,' ') |
'&a' |
%upcase(value) |
Translates lowercase into uppercase |
%upcase(&c) |
'Foo' |
%qupcase(value) |
Same, but masks value |
%qupcase(&c) |
'&A' |
%lowcase(value) position |
Translates uppercase into lowercase |
%lowcase(&c) |
'Foo' |
%qlowcase(value) |
Same, but masks value |
%qlowcase(&c) |
'&a' |
%cmpres(value) |
Strips leading/trailing and multiple whitespace |
|
|
%qcmpres(value) |
Same, but masks value |
|
|
%left(value) |
Strips leading whitespace |
|
|
%qleft(value) |
Same, but masks value |
|
|
%eval(expression) |
Evaluates expression using integer arithmetic |
%eval(10/3) |
'3' |
%sysevalf(expression) |
Evaluates expression using floating point arithmetic |
%sysevalf(10/3) |
'3.333333' |
sysfunc
The macro functions %sysfunc and qsysfunc cannot take nested functions, but macros can be nested without issue.
%let mon_title = %sysfunc(left(%qsysfunc(mdy(&mon,1,&curr_yr), monname9.)));
The difference between sysfunc and qsysfunc is masking of special characters. In this context, the most contentious special character is the comma, which can be confused for an argument delimiter.
This results in an error:
%let date=%sysfunc(left(%sysfunc(today(),worddate.))); /* ^ June 7, 2002 ^ left(June 7, 2002) ^ Error: `left` takes only one argument */
This will not:
%let date=%sysfunc(left(%qsysfunc(today(),worddate.)));
Macro Variables as Macro Programs
Disclosure: this is a terrible idea.
%let mymacro=%str(proc means data=LIBREF.TABLE; var foo bar; run;); &mymacro
Macro Programs
Macro programs expand to syntax that is immediately executed in any location other than data input. They are identified by a label up to 32 characters, beginning with a letter or underscore and continuing with letters, numbers, and underscores. A macro label is prepended with an ampersand (%) to trigger macro processing.
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 */ %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 like "%let month = %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 (&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.
%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("")
Resolution Order of Macros
The macro processor scans from right to left in each token. It looks up every valid label (i.e., an ampersand followed by a character or underscore) it encounters, and also substitutes double ampersands with single ampersands. 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;
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;