Macro Variables - Part 04: Double Ampersand Indirect Referencing and %SYSFUNC
Overview
Parts 01 to 03 covered %LET, CALL SYMPUTX, %GLOBAL/%LOCAL scope, and text manipulation functions %LENGTH, %SCAN, %INDEX.
This lesson covers two advanced macro variable techniques: indirect referencing using double ampersands (&&) to construct variable names dynamically, and %SYSFUNC which bridges the gap between macro code and DATA step functions.
Both techniques are essential for writing flexible, data-driven macro programs that adapt to varying inputs without hardcoded lists.
How SAS Resolves Ampersands
A single ampersand (&name) tells SAS to resolve the macro variable named name immediately.
A double ampersand (&&name) tells SAS to resolve the two ampersands first into a single ampersand, then resolve the resulting &name in a second pass.
This two-pass resolution allows you to build a macro variable name dynamically using another macro variable's value.
The most common use of this pattern is &&var&i — where i is a loop counter. On each iteration, &i resolves to 1, 2, 3, etc., producing &var1, &var2, &var3, each of which is then resolved to its value.
SAS Log
When i = 1: &&var&i resolves in two passes. Pass 1: && becomes & and &i becomes 1, giving &var1. Pass 2: &var1 resolves to USUBJID.
When i = 2: &&var&i becomes &var2, which resolves to TRTA. And so on.
Check the SAS log after running — you should see four lines, one per variable, showing the resolved name.
This technique allows a macro to iterate through a dynamically defined list of variables without knowing the list contents at write time.
Building Dataset Names Dynamically with Indirect Referencing
The same double-ampersand pattern can be used to construct dataset names, libnames, or any string that should vary across iterations.
This is commonly used in QC macros, reporting macros, or dataset-processing loops where the number of inputs varies by study.
SAS Log
&&ds&i resolves to the value of ds1, ds2, or ds3 on each iteration, giving the dataset name (adsl, adlb, adae).
PROC SQL with SELECT ... INTO :nobs captures the observation count into the macro variable nobs for each dataset in turn.
The PUT statements write the dataset name and count to the log — verify that all three datasets are counted correctly.
To add more datasets to process, simply define ds4, ds5, etc., and update dscount — no other code changes are needed.
%SYSFUNC - Calling DATA Step Functions in Macro Context
Macro code executes before the DATA step compiles. This means you cannot directly call DATA step functions (like TODAY(), STRIP(), CATS(), or EXIST()) inside macro code — they are not available at macro resolution time.
%SYSFUNC(function(arguments)) bridges this gap by executing a DATA step function during macro compilation and returning the result as a text string.
%SYSFUNC is used in macro %IF conditions, %LET assignments, and anywhere you need a DATA step function result available at macro execution time.
An optional second argument to %SYSFUNC specifies a SAS format to apply to the result: %SYSFUNC(today(), date9.) returns the current date formatted as a date string.
SAS Log
%SYSFUNC(today(), date9.) calls the DATA step TODAY() function and formats the result with DATE9 — producing a string like 05APR2026.
Nested %SYSFUNC calls are allowed: %SYSFUNC(year(%SYSFUNC(today()))) extracts the year from the result of today().
%SYSFUNC(exist(ds)) returns 1 if the dataset exists, and 0 if it does not. This is one of the most commonly used %SYSFUNC patterns for defensive macro programming.
Check the SAS log for the NOTE lines showing today's date and year. Verify that the %safe_print macro prints the first 5 rows of WORK.ADSL and writes a WARNING for the nonexistent dataset.
%SYSFUNC with String Functions
%SYSFUNC also gives access to character DATA step functions like STRIP, COMPRESS, UPCASE, LOWCASE, SUBSTR, and CATS — useful when you need string manipulation beyond what %SCAN and %INDEX provide.
Note that character function arguments in %SYSFUNC do not use quotes around string literals — the macro preprocessor handles the text before %SYSFUNC sees it.
SAS Log
STRIP removes leading and trailing spaces — useful when macro variable values may have been generated with surrounding whitespace.
UPCASE converts to uppercase — useful for standardising macro variable values before using them in comparisons or dataset names.
CATS concatenates values without adding any separator, using the DATA step CATS function via %SYSFUNC — a reliable way to build compound names.
Check the log output to confirm clean_name has no surrounding spaces, and upper_name is fully uppercase.
Key Points
Double ampersand (&&) triggers a two-pass resolution: the first pass reduces && to & and resolves any trailing macro variables; the second pass resolves the resulting macro variable reference.
The &&var&i pattern is the standard way to iterate through a list of indexed macro variables in a %DO loop — each iteration resolves a different variable name.
%SYSFUNC(function(args), format) executes a DATA step function during macro resolution and returns the formatted result as a text string.
%SYSFUNC(exist(ds)) is the standard guard for checking dataset existence before processing — returns 1 if the dataset exists, 0 otherwise.
%SYSFUNC also provides access to character functions like STRIP, UPCASE, and CATS for string manipulation that goes beyond the native macro string functions.