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.

Macro Concept

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.

An R Macro Language

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.

Macro Statements

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:

#%if (x. == 1)
  print("x is one!")
#%else
  print("x is something else!")
#%end

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.

The msource() function

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.

How to Use

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.

Available Functionality

The macro package supports the most basic functionality of a macro system. That includes:

  1. Macro Comments: Like regular R comments, but are not emitted to the output file.
  2. Macro Variables: Allow you to assign a value to a macro variable, and use the macro variable as a text replacement token.
  3. Macro Conditionals: If-Then-Else logic that allows you to conditionally execute a chunk of code.
  4. Macro Include: Inserts the contents of an external file into your program.
  5. Macro Functions: A small number of macro functions to make the system more practical.

Macro Comments and Variables

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:

# Print inside loop
for (idx in seq(1, v6.)) {
  
  print(v3.)
}

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:

# Print inside loop
for (idx in seq(1, 2 + 2)) {

  print("Hello World!")
}

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.

Simple Macro Conditionals

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.

Nested Macro Conditionals

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.

Macro Include

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.

Macro Functions

There are a couple macro functions that are considered necessary for proper operation of the system. The essential functions are %sysfunc() and %symexist().

Evaluating and Formatting Expressions: %sysfunc()

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.

Checking For Macro Variables: %symexist()

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:


  print("Macro variable 'a' exists!")
  print("Here is the value: 1")

  print("Macro variable 'b' does not exist!")

Next Steps

Here are some more articles for further exploration of the macro package: