The macro package provides macro commands and a pre-processor for R programs. The package can be useful in several ways. Before illustrating the functionality, let’s first discuss the macro concept at a high level.
A macro language is like a meta-language for code. The meta-language runs first, generates the regular code, and then the regular code is executed to produce some sort of output.
The SAS® macro language is commonly used in SAS programs. The R macro package was inspired by SAS.
Clearly, a macro language for R will not be the same as the SAS macro language. The SAS macro language is built into the SAS execution environment. The SAS pre-processor is run automatically, and the macro syntax is understood by the SAS syntax checker.
With R, none of the above applies. R does not have a built-in macro language, does not have a built-in pre-processor, and the R syntax checker will not expect any sort of macro language syntax.
Therefore, to build macro-like functionality in R, we must get a little creative.
The first thing to note is that macro statements in the macro package are written as code comments. The comments ensure that the R syntax checker will not generate an error when parsing the macro functions.
In SAS, macro functions are prefixed with a percent sign (%). In R, macro statements are therefore prefixed with a comment symbol (#) and a percent sign (%). A simple macro assignment looks like this:
#%let x <- 1
A simple macro conditional looks like this:
Note that the above R macro syntax is not identical to the SAS syntax. The syntax is a blend of SAS and R syntax, all inside a macro comment.
In the macro package, pre-processing of the macro
code is accomplished by a special function: msource()
. The
msource()
function is similar to the source()
function in R. The msource()
function will execute your
code, just like the source()
function. First, however, it
will execute the macro pre-processor.
The steps can be summarized as follows:
Step 1: Pre-process the code and resolve all macro language
syntax.
Step 2: Generate a new code file with the resolved code.
Step 3: Execute the resolved code with the source()
function.
Notice that the above steps map directly to the way the SAS macro processor works.
In addition, the R macro language is a text-replacement language, just like SAS. Macro variables are swapped out with real values as text replacements. Macro conditions are evaluated, and only the code in the TRUE macro conditions is executed.
All of the above is handled by the msource()
function.
msource()
is in fact the only user-facing function in the
macro package.
To use the macro package, you will first write your
macro-enabled R code in a regular “.R” code file. You can use RStudio®
or another code editor. The macro statements are written as special
comments, as described above. To execute a macro-enabled program, simply
call the msource()
function in the console and pass the
program path. That’s it!
If you are working in RStudio, the function will use the path of the
currently active program by default. In that case, you don’t need to do
hardly anything! Just call msource()
in the console with no
parameters.
The macro package supports the most basic functionality of a macro system. That includes:
Above we saw a simple macro variable assignment. Macro variables are
defined with the keyword #%let
and then the variable name
and value.
We can document those macro assignments with macro comments. Macro
comments are prefixed with #%
. You can type anything after
the prefix.
Note that macro comments are a single line comment. There are no multi-line comments in R.
Here are a few macro variable assignments with comments for different data types:
#% Assign integer value
#%let v1 <- 1
#% Assign double value
#%let v2 <- 1.2
#% Assign character value
#%let v3 <- "Hello World!"
#% Assign date value
#%let v4 <- as.Date("2025-07-15")
#% Assign vector
#%let v5 <- c(1, 2, 3, 4, 5)
#% Assign calculated value
#%let v6 <- 2 + 2
#% Assign using another macro variable
#%let v7 <- v6.
#% Calculate using another macro variable
#%let v8 <- v7. + 1
Once a macro variable is assigned, it can be used as a text replacement token anywhere in your program.
Note that R macro variables have no ampersand (“&”) prefix. The reason is that R does not allow special characters in variable names. Therefore, R macro variables are identified simply by a dot (“.”) suffix.
For example, we can use some of the above macro assignments like this:
If we execute the above code using msource()
, we’ll get
the following result:
> msource()
[1] "Hello World!"
[1] "Hello World!"
[1] "Hello World!"
[1] "Hello World!"
And the generated code would look like this:
Notice in the above generated code that the macro variables
v3.
and v6.
were replaced exactly as they were
assigned. Macro variable expressions are not evaluated unless done so
explicitly using %sysfunc()
. Usage of this macro function
will be explained below.
Macro conditionals allows you to conditionally execute a chunk of code. If a macro condition is TRUE, the code inside that condition will be output to the generated code file and executed. Otherwise, the code will not be emitted. This feature makes the generated code file more compact.
Here are some examples of simple macro conditions:
#% Assign integer value
#%let x = 1
#%if (x. == 1)
# X is one
print("Inside the first condition")
#%elseif (x. == 2)
# X is two
print("Inside the second condition")
#%else
# X is something else
print("Inside the else.")
#%end
When executed with msource()
, the above code will
generate the result "Inside the first condition"
. If you
pass a file name to the file_out
parameter of the
msource()
function, a code file will be generated. That
code file will contain the following content:
# X is one
print("Inside the first condition")
Notice that all spacing and comments from inside the condition are preserved. Also notice that all macro code and code from inside other conditions has been removed. Only the code inside TRUE condition has been emitted.
Macro conditionals can also be nested. Here is an example that illustrates such conditionals:
#% Assign integers
#%let x = 1
#%let y = 2
#%if (x. == 1)
#%if (y. == 1)
print("X is one and Y is 1")
#%else
print("X is one and Y is y.")
#%end
#%elseif (x. == 2)
#%if (y. == 1)
print("X is two and Y is 1")
#%else
print("X is two and Y is y.")
#%end
#%else
#%if (y. == 1)
print("X is x. and Y is 1")
#%else
print("X is x. and Y is y.")
#%end
#%end
The result of the above code will be “X is one and Y is 2”, and the output file will look like this:
print("X is one and Y is 2")
In this way, many levels of macro logic can be boiled down to a few lines of code.
There is no equivalent in Base R for a SAS macro include. The R
source()
function executes an external code file. But it
does not “include” or “insert” the code into the calling program.
Therefore, the macro include from the macro package
brings a useful feature to R.
A macro include from the macro package looks like this:
#%include 'c:/myfolder/myprogram.R'
Upon execution by msource()
, the contents of
“myprogram.R” will be extracted from the file and copied into the
calling program as text. The calling program will then be scanned for
macro commands that may have existed in “myprogram.R”. The result will
be a fully integrated program with all macro commands resolved.
There are a couple macro functions that are considered necessary for
proper operation of the system. The essential functions are
%sysfunc()
and %symexist()
.
The %sysfunc()
macro function allows you to evaluate an
R expression as part of a macro command. The expression can contain any
valid R function or operator. Observe:
#% Assign integer vector
#%let a = c(2, 5, 8, 9)
#% Sum vector without %sysfunc
#%let b = sum(a.) / 1.3
#% Sum vector with %sysfunc
#%let c = %sysfunc(sum(a.) / 1.3)
#% Format expression with %sysfunc
#%let d = %sysfunc(sum(a.) / 1.3, %.2f)
# Text replacement
w <- a.
# Expression replacement
x <- b.
# Evaluated with %sysfunc()
y <- c.
# Formatted with %sysfunc()
z <- d.
Here is the above code after pre-processing:
# Text replacement
w <- c(2, 5, 8, 9)
# Expression replacement
x <- sum(c(2, 5, 8, 9)) / 1.3
# Evaluated with %sysfunc()
y <- 18.4615384615385
# Formatted with %sysfunc()
z <- 18.46
Notice how the macro values resolve differently depending on whether
and how %sysfunc()
is used.
The %symexist()
macro function is used to determine
whether or not a macro variable exists. You will pass the name of the
macro variable without quotes and without the “.” suffix.
Here is a very simple example:
#%let a = 1
#%if (%symexist(a))
print("Macro variable 'a' exists!")
print("Here is the value: a.")
#%else
print("Macro variable 'a' does not exist!")
#%end
#%if (%symexist(b))
print("Macro variable 'b' exists!")
print("Here is the value: b.")
#%else
print("Macro variable 'b' does not exist!")
#%end
And the resulting code looks like this: