---
title: "Macro Language Reference"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Macro Language Reference}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

Below is the documentation for all macro commands in the **macro** package.
The documentation contains the syntax, a detailed explanation, and examples for
each of the available commands. 

## Index{#top}

* [Macro Comments](#comments)
* [Macro Variables](#variables)
* [Macro Conditionals](#conditionals)
* [Macro Include](#include)
* [Built-in Macro Functions](#builtin)
* [Macro Do Loops](#doloop)
* [User-Defined Macro Functions](#functions)
* [Macro Line Continuation](#line)

## Macro Comments {#comments}

### Syntax

```
#% <comment>
```
Where "&lt;comment&gt;" is any text string. The macro comment is a single 
line comment.  If you need multiple lines, use multiple macro comments.

### Details

A macro comment is like a regular R comment, but with a "#%" prefix. The 
macro comment can be used to document your macro statements. The macro
comment will be removed during pre-processing.

### Examples

Here is a macro comment and a regular R comment:
```{r eval=FALSE, echo=TRUE}

#% Here is a macro comment

# Here is a regular comment

```
During pre-processing, the macro comment is removed, while the regular comment
is retained:

```{r eval=FALSE, echo=TRUE}

# Here is a regular comment

```

[top](#top) 

## Macro Variables {#variables}

### Syntax
```
#%let <variable> <- <value>
#%let <variable> = <value>
#%let <variable>
```
Where "&lt;variable&gt;" is the name of the variable, and "&lt;value&gt;" is some 
value assigned to the variable.    

Note that macro variable names must
begin with an alphabetic character or dot (".").  They can contain only alphabetic 
characters, numbers, dots, and underscores ("_"). 

Macro variables should be named without a leading ampersend ("&").  The leading 
ampersand will be appended automatically when the variable is added to 
the symbol table.

Either the left arrow ("&lt;-") or equals sign ("=") is a valid assignment
operator for macro variables.

To clear a macro variable, leave it unassigned. This action
will remove the macro variable from the symbol table.

### Details

Macro variables are variables that have a temporary existence during pre-processing.
The variables are stored in the pre-processor symbol table, and used as 
text replacement tokens during pre-processing.  

The assigned value is treated
as a string, and replaced exactly as assigned.  That means if the value is
assigned without quotes, it will be replaced without quotes.  If the value
is quoted, the quotes will be retained during replacement.  

Macro variable resolution can be delayed using multiple ampersands.  See 
example below.

### Examples

#### Basic Assignment

Here is a basic macro assignment of numeric values:
```{r eval=FALSE, echo=TRUE}
#% Assignment using left arrow
#%let a <- 1
x <- `&a`

#% Assignment using equals sign
#%let b = 2
y <- `&b`

```
Here is the resolved output:
```{r eval=FALSE, echo=TRUE}
x <- 1

y <- 2

```
Both variables `&a` and `&b` have been replaced as assigned. 

Note the use of backticks ("`") on the macro variables.  The backticks are necessary
because R does not normally permit a special character to begin the name
of a variable.  The backticks essentially "escape" the variable name, and allow
it to contain any character.

#### Quoted Assignment

```{r eval=FALSE, echo=TRUE}
#% Quoted Assignment
#%let a <- "One"
x <- `&a`

#% Unquoted Assignment
#%let b <- Two
y <- "&b"
z <- '&b'

```
The above code will resolve as follows:
```{r eval=FALSE, echo=TRUE}
x <- "One"

y <- "Two"
z <- 'Two'

```
Observe that when macro variables appear inside text strings, comments, 
or macro commands, they do not need to be escaped with backticks.  

Also observe with the macro variable `&b` that single or double quotes 
have no effect on macro variable resolution. Both are resolved normally.
This behavior is different from SAS.

#### Unquoted Assignment Errors

If you do not quote the macro variable value, and do not quote the macro variable
to be replaced, it will result in an error:
```{r eval=FALSE, echo=TRUE}
#% Unquoted Assignment
#%let c <- Three

# Unquoted resolution
z <- `&c`

```
The error is:
```
Error in eval(ei, envir) : object 'Three' not found
```
The error occurs because the value "Three" is now left unquoted in open code.
The R parser thinks it is the name of a variable or object, and will try to
look it up in the environment.  If it can't find the name in the environment,
the parser generates an error.

#### Variable Removal 

If you want to remove an existing macro variable from the symbol table, 
leave it unassigned.  Like this:
```{r eval=FALSE, echo=TRUE}
#% Macro assignment
#%let b <- 2
x <- `&b`

#% Clear assignment
#%let b
y <- `&b`

```
The above code will generate an error, as the `y` assignment runs
after the `&b` macro variable has been cleared.  Here is the 
error that is generated:
```
Error in mreplace(ln) : 
  Macro variable '&b' not valid for text replacement.
```
#### Global Macro Assignment 

Macro variables can also be assigned in regular R code.  In the below code, 
macro variable `&c` is assigned the value of 3, and then the 
template code file "temp1.R" is executed via `msource()`:

```{r eval=FALSE, echo=TRUE}
# Assignment before msource()
`&c` <- 3

# Pre-process
msource("./temp1.R", "./temp1_mod.R")

```

Here is the file "temp1.R":
```{r eval=FALSE, echo=TRUE}
# Print macro variable
z <- `&c`
```
When the "temp1.R" file is resolved to "temp1_mod.R", it will look like this:
```{r eval=FALSE, echo=TRUE}
# Print macro variable
z <- 3
```
You can see that the macro variable `&c` is replaced normally. The reason is because 
any variable with a leading ampersand ("&") in the parent environment will
be copied to the macro symbol table when the pre-processor begins.

Also note that parent macro assignments do not need to take place in the 
same code file as `msource()`.  You can assign them in a setup file if desired.
As long as they exist in the parent environment, the macro variables will be
copied to the symbol table.

#### Nested Assignment

Macro variables can be nested inside one another. Nesting means that 
a macro variable name can be constructed from other macro variables.
This feature allows for more dynamic assignments.  Here is a simple demonstration:

```{r eval=FALSE, echo=TRUE}
#%let x <- 1
#%let mvar1 <- Hello!

print("&mvar&x")
```
The above code outputs the string "Hello!".  Why?  The reason is because the
macro processor first found the variable "&x" and replaced it with the value
"1". The macro processor continued to scan for variables, found that
"&mvar1" was also a macro variable, and made the replacement. The system
allows up to 10 levels of nesting.  This feature can be useful in some situations.


#### Delayed Resolution

If needed, macro variable assignment can be delayed using additional ampersands.
For example, let's say there was another macro variable `mvar` and we don't want
to conflict with it.  In that case, you can add another ampersand and delay
resolution to get the proper assignment to `mvar1`:

```{r eval=FALSE, echo=TRUE}
#%let x <- 1
#%let mvar <- Goodbye!
#%let mvar1 <- Hello!

print("&mvar&x")
print("&&mvar&x")
```
The above code resolved as follows:
```
print("Goodbye!1")
print("Hello!")
```
Notice the difference in the resolution between `&mvar` and `&&mvar`.  Since `&mvar`
was a valid macro variable, it performed the replacement when encountered.  The 
same with `&x`.  But for `&&mvar`, the double ampersand delayed resolution until after
`&x` had been replaced.  At that point, `&&mvar` becomes `&mvar1`, and the replacement
is made for `&mvar1`.  In this way,
you have fine-grained control over how and when macro variables resolve.

#### Advanced Assignment

The examples above use R macro variables much like regular variables. However,
it is important to realize that macro variables can be used anywhere, to 
manipulate any piece of code.  Here is a taste of some advanced assignments:
```{r eval=FALSE, echo=TRUE}
#%let a <- # Subset mtcars
#%let b <- mtcars
#%let c <- , mpg > 25
#%let d <- dat <- subset(&b&c)
#%let e <- (dat)
`&a`
`&d`
print&e
```
Here is the generated code:
```{r eval=FALSE, echo=TRUE}
# Subset mtcars
dat <- subset(mtcars, mpg > 25)
print(dat)
```
And here is the console output:
```
>msource()
                mpg cyl  disp  hp drat    wt  qsec vs am gear carb
Fiat 128       32.4   4  78.7  66 4.08 2.200 19.47  1  1    4    1
Honda Civic    30.4   4  75.7  52 4.93 1.615 18.52  1  1    4    2
Toyota Corolla 33.9   4  71.1  65 4.22 1.835 19.90  1  1    4    1
Fiat X1-9      27.3   4  79.0  66 4.08 1.935 18.90  1  1    4    1
Porsche 914-2  26.0   4 120.3  91 4.43 2.140 16.70  0  1    5    2
Lotus Europa   30.4   4  95.1 113 3.77 1.513 16.90  1  1    5    2

```
Obviously, the above demonstration makes no sense. The point is to illustrate
that the R macro language views all code as text.  You can construct
comments, lines of code, variable names, function calls, parameters, or 
anything else using the macro language. 

#### Dynamic Variable Creation

You can create the names of regular R variables dynamically using macro variables.
There are different ways to do it.
Here are some of the different ways:
```{r eval=FALSE, echo=TRUE}
#% Assign variable name to macro
#%let x <- cyl

# Leading macro replacement
`&x._mean` <- mean(mtcars[["&x"]])

# Trailing macro replacement
mean_&x <- mean(mtcars[["&x"]])

# Middle macro replacement
my_&x_mean <- mean(mtcars[["&x"]])

# Intermediate macro replacement
#%let myvar <- &x_mean
`&myvar` <- mean(mtcars[["&x"]])
                
```
Here is the resolved code:
```{r eval=FALSE, echo=TRUE}

# Leading macro replacement
`cyl_mean` <- mean(mtcars[["cyl"]])

# Trailing macro replacement
mean_cyl <- mean(mtcars[["cyl"]])

# Middle macro replacement
my_cyl_mean <- mean(mtcars[["cyl"]])

# Intermediate macro replacement
cyl_mean <- mean(mtcars[["cyl"]])
```
Notice that you only need backtick escapes when you have a leading macro
variable.

[top](#top) 

## Macro Conditionals {#conditionals}

### Syntax
```
#%if <condition>
#%if (<condition>)

#%ifelse <condition>
#%ifelse (<condition>)

#%else

#%end
```
Where "&lt;condition&gt;" is an expression that evaluates to TRUE or FALSE. 

A macro conditional block must begin with an `#%if` condition and be finalized 
with a `#%end`.  The `#%ifelse` and `#%else` blocks are optional. 
Failure to finalize the block with an `#%end` will result in an error or other
unexpected behavior.

Parenthesis around the conditional expressions are optional but recommended.

The syntax of R conditionals is somewhat different from SAS. One difference is
that the `%then %do` directives in SAS have been entirely eliminated from
the **macro** package syntax.
Another difference is that the `#%ifelse` command is one word instead of two.  
A third difference
is that in SAS, each conditional section must be concluded with an `%end`.
In R, you only need one `#%end` at the conclusion of the entire conditional.
In general, the R syntax is simpler.

### Details

Conditional expressions should be standard R expressions,
using standard R syntax. For comparisons, the expression should use the double-equals
("==") comparison operator, as in a normal R "if" statement.
Likewise, all other comparison operators should follow Base R syntax.

A conditional expression may contain macro variables.  These macro variables
will be resolved before the expression is evaluated.  Reminder that the macro
variables are text replacements, and will be resolved accordingly.

The conditional expression may also contain R functions like `any()` and
`all()`.  In most cases, these function do not need to be wrapped in 
`%sysfunc()`.  They will evaluate properly as part of the expression.

If an expression in a macro "if" block is TRUE, the code inside that block
will be emitted during pre-processing.  If the expression evaluates to FALSE,
code inside that block will be ignored.

Macro conditions may be nested inside one another. There is no limit on the 
number of nested levels.

### Examples

#### Simple Conditional

Here is a simple condition to construct a path. The source directory can 
change depending on the environment.  
```{r eval=FALSE, echo=TRUE}
#%let dev_path <- ./dev/data
#%let prod_path <- ./prod/data
#%let env <- dev

# Path to data
#%if ("&env" == "prod")
pth <- "&prod_path/dm.sas7bdat"
#%else 
pth <- "&dev_path/dm.sas7bdat"
#%end
```
When run through the macro processor, the above code will generate the following:
```{r eval=FALSE, echo=TRUE}

# Path to data
pth <- "./dev/data/dm.sas7bdat"
```
The pre-processor selected the "dev" path as indicated, constructed the path 
appropriately, and removed all macro statements.

#### Nested Conditionals

Macro conditionals may also be nested inside one another.  Nesting allows
you to construct more complicated logic.  Here is an example:
```{r eval=FALSE, echo=TRUE}
#% Data source SAS or RDS
#%let src <- SAS

#% Select analysis variables
#%let anal_vars <- c("AGE", "AGEG", "SEX", "RACE", "PULSE", "TEMP")

###################
# Get data
###################
#%if ("&src" == "SAS")
  library(haven)

  # Get adsl dataset
  adsl <- haven("./data/ADSL.sas7bdat")
  
  #%if (any(c("PULSE", "TEMP", "BP") %in% &anal_vars))
  # Get advs dataset
  advs <- haven("./data/ADVS.sas7bdat")
  #%end

#%else 
  
  # Get adsl dataset
  adsl <- readRDS("./data/ADSL.rds")
  
  #%if (any(c("PULSE", "TEMP", "BP") %in% &anal_vars))
  # Get advs dataset
  advs <- readRDS("./data/ADVS.rds")
  #%end

#%end
```
When the nested conditionals resolve, they will look like this:
```{r eval=FALSE, echo=TRUE}
######################
# Get data
######################
  library(haven)

  # Get adsl dataset
  adsl <- haven("./data/ADSL.sas7bdat")

  # Get advs dataset
  advs <- haven("./data/ADVS.sas7bdat")

```
In this way, you can perform complex logic in the macro pre-processor, and 
still produce a concise output.

[top](#top) 

## Macro Include {#include}

### Syntax
```
#%include <path>
#%include '<path>'
#%include "<path>"
```
Where "&lt;path&gt;" is a path to the file to include. The file path may be 
quoted or unquoted. If the path is quoted, single or double quotes may be used.

### Details

The `#%include()` macro command inserts text from an external file into
the generated output file. This behavior is different from the Base R
`source()` function, which only executes the code in the external file.
A macro include actually copies the code into the generated file.  The 
included code does not have to be fully functional. It can be a code snippet
that only works when combined with other snippets.

### Examples

The below example includes code from the file "dat01.R".  This file 
contains a snippet of sample data:
```{r eval=FALSE, echo=TRUE}
# Create sample data
#%include "./templates/dat01.R"

# Print sample data
print(dat)
```
The resolved macro will look like this:
```{r eval=FALSE, echo=TRUE}
# Create sample data
dm <- read.table(header = TRUE, text = '
       SUBJID  ARM    SEX  RACE    AGE
       "001"   "ARM A" "F"  "ASIAN" 19
       "002"   "ARM B" "F"  "WHITE" 21
       "003"   "ARM C" "F"  "WHITE" 23
       "004"   "ARM D" "F"  "BLACK OR AFRICAN AMERICAN" 28
       "005"   "ARM A" "M"  "WHITE" 37
       "006"   "ARM B" "M"  "WHITE" 34
       "007"   "ARM C" "M"  "ASIAN" 36
       "008"   "ARM D" "M"  "WHITE" 30
       "009"   "ARM A" "F"  "WHITE" 39
       "010"   "ARM B" "F"  "WHITE" 31
       "011"   "ARM C" "F"  "BLACK OR AFRICAN AMERICAN" 33
       "012"   "ARM D" "F"  "WHITE" 38
       "013"   "ARM A" "M"  "BLACK OR AFRICAN AMERICAN" 37
       "014"   "ARM B" "M"  "WHITE" 34
       "015"   "ARM C" "M"  "WHITE" 36
       "016"   "ARM A" "M"  "WHITE" 40')

# Print sample data
print(dm)
```
Notice that the included code is integrated with the surrounding code
from the macro-enabled program.  In this way, you can collate a program
from multiple code snippets into a unified result.

[top](#top) 

## Built-in Macro Functions {#builtin}

The **macro** package currently supports four built-in macro functions: 
`%sysfunc()`, `%symexist()`, `%symput()`, and `%nrstr()`.  

### Syntax
```
%sysfunc(<expression>, [<format>])
```
Where "&lt;expression&gt;" is an R expression,
and "&lt;format&gt;" is an optional format code.  The expression may
contain R functions, operators, hard-coded values, and macro variables.

If "&lt;format&gt;" is supplied, the function will apply the format after 
the expression is resolved, and return the formatted value.  The format code does 
not need to be quoted.  If the format is unquoted, the output of the 
`%sysfunc()` function will be unquoted. If the format is quoted, the output of the 
`%sysfunc()` function will be quoted.

Formats specified on the `%sysfunc()` function may be any format code supported
by the `fapply()` function from the **fmtr** package.  See the 
[fapply](https://fmtr.r-sassy.org/reference/fapply.html) reference page and the 
[FormattingStrings](https://fmtr.r-sassy.org/reference/FormattingStrings.html) 
page for further information.

Note that the R `%sysfunc()` is a general evaluation function.  It can evaluate
any R expression, even those that do not resolve to a numeric value. Likewise,
the optional format can be any type of format supported by `fapply()`, even
those that take text input.

```
%symexist(<name>)
```
Where "&lt;name&gt;" is an unquoted name of a macro variable. If the macro
variable exists, the function will return TRUE.  Otherwise, the function
will return FALSE.

```
%symput(<expression>)
```
Where "&lt;expression&gt;" is an R expression that references the execution
environment. The expression can contain macro variables. The return value
from `%symput()` is assigned to a macro variable using a `#%let`
command.  The `%symput()` function must exist as part of a `#%let` statement.
It cannot be used with any other R macro command.

```
%nrstr(<expression>)
```
Where "&lt;expression&gt;" is any R expression, specifically one that contains 
a reference to a macro variable that you do not want to resolve. The `%nrstr()`
masks the expression to prevent normal resolution.


### Details

The `%sysfunc()`, `%symexist()`, `%symput()`, and `%nrstr()` functions are helper functions 
that are usually contained in other macro statements.  Most commonly, they will exist
as part of "let" or "if" statement.  For this reason, the syntax does not include
a leading comment symbol ("#").

The purpose of `%sysfunc()` is to evaluate regular R expressions during macro
processing. The evaluated result can then be assigned to a macro variable, 
or used in a macro condition.

The purpose of `%symexist()` is to check for the existence of a macro variable.
This check is likewise most often performed as part of a macro conditional
expression.

The purpose of `%symput()` is to evaluate an expression in the execution
environment and assign it to a macro variable in the macro environment.

Normally, the macro commands in a program are resolved first, and then
the remaining R code is executed.  Sometimes, however, you want to make 
decisions in the macro code depending on what is going on in the regular 
R code.  For example, if your input dataset has no rows, you may decide do 
something differently in the macro logic. It is for this type of 
situation that `%symput()` was designed for.

When the macro pre-processor encounters a `%symput()` function, it will 
first evaluate all emitted code. Then it will evaluate and return the expression
inside the `%symput()`.  

SAS programmers will notice that the R `%symput()` function is quite different
from the SAS version. But they have the same goal.  

Similar to the SAS version, `%nrstr()` is a macro quoting function, and can 
be used to prevent resolution of a macro variable or other macro expression.
This function can be used if you are trying to output a macro variable
name instead of the resolved value.

### Examples

#### %sysfunc()

Here are some examples illustrating the basic operation of `%sysfunc()`:
```{r eval=FALSE, echo=TRUE}
#% Unevaluated assignment
#%let a <- 2 + 2
w <- `&a`

#% Evaluated assignment
#%let b <- %sysfunc(2 + 2)
x <- `&b`

#% Unevaluated replacement 
#%let c <- &a + &b
y <- `&c`

#% Evaluated replacement
#%let d <- %sysfunc(&a + &b)
z <- `&d`
  
```
The above code resolves as follows:
```{r eval=FALSE, echo=TRUE}
w <- 2 + 2
x <- 4
y <- 2 + 2 + 4
z <- 8
```
Observe the different ways each expression resolves depending on the usage
`%sysfunc()`.

Here is another example:
```{r eval=FALSE, echo=TRUE}
#%let a <- c(1.205, 4.683, 3.812, 6.281, 9.467)

#%if (%sysfunc(mean(&a.)) > 5)
x <- "> 5"
#%else 
x <- "<= 5"
#%end
print(x)
print("The mean of 'a' is %sysfunc(mean(&a.), %.2f)")
```
The above macro evaluates the mean of `&a.`, compares to the hard-coded
value 5, and then sets the value of `x`.  The `%sysfunc()` macro function
allows for immediate evaluation of the Base R `mean()` function. 

Here is the execution of the above example:
```{r eval=FALSE, echo=TRUE}
> msource()
# ---------
# x <- "> 5"
#  print(x)
#  print("The mean of 'a' is 5.09")
# ---------
# [1] "> 5"
# [1] "The mean of 'a' is 5.09"
```
In the above example, `%sysfunc()` is used in the conditional, and inside a
text string.  You can see that it is resolved in both cases.

Note that you do not need multiple calls to `%sysfunc()` if you have multiple
Base R functions in your expression. Just put your entire expression
inside one call to `%sysfunc()`, and it will evaluate as desired.

#### %symexist()

Below is a simple example checking for the existence of some macro variables:
```{r eval=FALSE, echo=TRUE}
#%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 here is the resolved version:
```{r eval=FALSE, echo=TRUE}

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

  print("Macro variable 'b' does not exist!")
```
Notice that the variable name inside the `%symexist()` function is not 
quoted. For this function, quoting is not necessary, and will in fact prevent
the function from finding the specified variable name.

#### %symput()

Examine the following code.  In this code, we are trying to get the length
of a vector into a macro variable:
```{r eval=FALSE, echo=TRUE}
# Assign vector
v1 <- c("one", "two", "three")

#% Get length of vector
#%let x <- %symput(length(v1))

#% Get vector values
#%let y <- %symput(v1)

# Use macro variables
a <- `&x`
b <- `&y`

# Print to console
print(a)
print(b)

```
If you run the above code in `msource()`, you will get the following output:
```
> msource()
---------
# Assign vector
 v1 <- c("one", "two", "three")
 
 # Use macro variables
 a <- 3
 b <- c('one', 'two', 'three')
 
 print(a)
 print(b)
---------
[1] 3
[1] "one"   "two"   "three"
```
You can see the length and value of vector 'v1' has been pulled from normal
R code into macro variables.  These macro variables can then be used with
other macro commands to control what happens during pre-processing.

#### %nrstr()

The following example shows how `%nrstr()` can prevent resolution of a 
macro variable:
```{r eval=FALSE, echo=TRUE}
#% Assign variables
#%let x <- 1
print("The value of %nrstr(&x) is &x")
```
The above code sends the following to the console:
```
> msource()
[1] "The value of &x is 1"
```
Why didn't the first `&x` resolve?  Because `%nrstr()` prevented it.  This 
function is useful if you need to reference macro variable names or generate
macro code.

[top](#top) 

## Macro Do Loops {#doloop}

### Syntax
```
#%do <variable> = <start> %to <end>
#%end
```
Where "&lt;variable&gt;" is a macro variable name to use for the loop counter,
and "&lt;start&gt;" and "&lt;end&gt;" signify the beginning and ending of the 
loop sequence.  The "&lt;start&gt;" and "&lt;end&gt;" values can be numbers, 
macro variables that resolve to numbers, or expressions that resolve to numbers.

The "#%end" command signifies the end of the "do" block.  It is required.

### Details

A macro "do" loop causes the pre-processor to repeat a section of code some
number of times. The do variable will be incremented for each pass.  This 
variable can be used to alter the section of code each time around.  In the
generated output file, the do block code will actually be copied for each
iteration of the loop.

### Examples
Here is a simple example of a macro do loop:
```{r eval=FALSE, echo=TRUE}
#%do x = 1 %to 3
print("Hello #&x.")
#%end
```
The console output from the above macro do loop is as follows:
```{r eval=FALSE, echo=TRUE}
[1] "Hello #1"
[1] "Hello #2"
[1] "Hello #3"
```
And the generated code is as follows:
```{r eval=FALSE, echo=TRUE}
print("Hello #1")
print("Hello #2")
print("Hello #3")
```
Observe that the code inside the macro do loop was copied for each iteration
of the loop.

Here is a more interesting example:
```{r eval=FALSE, echo=TRUE}
#%let vars <- c("mpg", "disp", "hp", "drat")
# Get data
dat <- mtcars

#% Print mean for each variable
#%do v = 1 %to %sysfunc(length(&vars))
#%let var <- %sysfunc(&vars[&v])

# Calculate mean for &var.
mn&v. <- sprintf("%.2f", mean(dat$`&var.`))
print(paste0("Mean of '&var.' is ", mn&v.))

#%end

```
This example calculates the mean for several variables in the "mtcars" sample
data frame.  The macro loops through a vector of variables and constructs
a code block for each variable. 

Here is the console output:
```
[1] "Mean of 'mpg' is 20.09"
[1] "Mean of 'disp' is 230.72"
[1] "Mean of 'hp' is 146.69"
[1] "Mean of 'drat' is 3.60"
```
And here is the generated code:
```{r eval=FALSE, echo=TRUE}
# Get data
dat <- mtcars

# Calculate mean for mpg
mn1 <- sprintf("%.2f", mean(dat$mpg))
print(paste0("Mean of 'mpg' is ", mn1))

# Calculate mean for disp
mn2 <- sprintf("%.2f", mean(dat$disp))
print(paste0("Mean of 'disp' is ", mn2))

# Calculate mean for hp
mn3 <- sprintf("%.2f", mean(dat$hp))
print(paste0("Mean of 'hp' is ", mn3))

# Calculate mean for drat
mn4 <- sprintf("%.2f", mean(dat$drat))
print(paste0("Mean of 'drat' is ", mn4))

```
Notice that the macro functionality can be used to create variable names dynamically.
The macro system can also produce comments and other statements dynamically.
Because the macro language is a text replacement language, any part of the code
is available for manipulation.

[top](#top) 

## User-Defined Macro Functions {#functions}

### Syntax
```
#%macro <name>([<param1>, <param2> = <default>, ...])
#%mend
```
Where "&lt;name&gt;" is the name of the user-defined function,
"&lt;param1&gt;", "&lt;param2&gt;", etc. are zero or more parameters,
and "&lt;default&gt;" is an optional default value. Between the "#%macro" and 
"#%mend" commands are one or more statements that comprise the function.

### Details

A user-defined macro function is essentially a code insertion function. Once
the macro function is defined, you can call it as many times as needed. For
each call, the pre-processor will replace the call with the code inside 
the macro function definition. This behavior is different from a normal
R function, which only returns the result of the function. Keep this difference
in mind, as it can influence which kind of function you choose for different
contexts.

When the macro pre-processor encounters a macro function definition, it
performs three steps.  First, the pre-processor will parse the `#%macro`
definition and the parameters. Then the pre-processor will scan the code 
until the `#%mend` is reached.  Lastly, both the parameters and the code
are stored in the symbol table for later use. 

When the function is called, the pre-processor first looks up the function
in the symbol table. The pre-processor then matches the called parameters
to the defined parameters, and ensures basic compliance.  A `#%let` statement
is generated for each parameter to assign the macro variables internal to the 
function.  Then the contents of the function is inserted into the output 
code file at the point the call was made.

Note that macro functions have a scope. Macro variables defined inside the 
macro function will not be available outside the function.
Macro variables assigned outside the function will be available inside the function.
If a macro variable inside the function is defined with the same name 
as a macro variable defined outside the function, a new variable is created
within the scope of the macro function.  

Macro functions can be created inside other macro functions.  The scope of the 
inner function will be nested inside the outer function.  Any macro variables
not passed into the inner function will be looked up first in the outer function,
and then in the global environment.


### Examples 

First, let's declare a simple function, just to see how the **macro** function
definitions work:
```{r eval=FALSE, echo=TRUE}
#%macro printit(txt)
print("Print the text: &txt.")
#%mend
#%printit(Hello!)
```
The console output for the above code is this:
```
> msource()
---------
print("Print the text: Hello!")
---------
[1] "Print the text: Hello!"
```
Note that the macro definition and call have both been removed.  Only the 
code inside the macro function has been emitted.

Here is a more interesting example:
```{r eval=FALSE, echo=TRUE}
library(dplyr)

#%macro print_analysis(dat, byvar, var)
cat("Analysis for &var.\n")
`&dat` |>
  select(`&byvar`, `&var`) |>
  group_by(`&byvar`) |>
  summarize(Mean = mean(`&var`, rm.na = TRUE),
            SD = sd(`&var`),
            Quantile = quantile(`&var`, probs = .25, rm.na = TRUE)) |>
  as.data.frame() |> print()
cat("\n")

#%mend

#%let vars <- c("Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width")
#%do idx = 1 %to %sysfunc(length(&vars))
#%print_analysis(iris, Species, %sysfunc(&vars[&idx]))
#%end
```
The above example loops through some variables on the `iris` dataset and 
prints an analysis for each to the console.  The macro function `print_analysis()`
uses the **dplyr** package, and accepts parameters for the dataset name, by variable, and analysis variable. 

Here is the console output:
```
Analysis for Sepal.Length
     Species  Mean        SD Quantile
1     setosa 5.006 0.3524897    4.800
2 versicolor 5.936 0.5161711    5.600
3  virginica 6.588 0.6358796    6.225

Analysis for Sepal.Width
     Species  Mean        SD Quantile
1     setosa 3.428 0.3790644    3.200
2 versicolor 2.770 0.3137983    2.525
3  virginica 2.974 0.3224966    2.800

Analysis for Petal.Length
     Species  Mean        SD Quantile
1     setosa 1.462 0.1736640      1.4
2 versicolor 4.260 0.4699110      4.0
3  virginica 5.552 0.5518947      5.1

Analysis for Petal.Width
     Species  Mean        SD Quantile
1     setosa 0.246 0.1053856      0.2
2 versicolor 1.326 0.1977527      1.2
3  virginica 2.026 0.2746501      1.8

```
And here is the generated code:
```{r eval=FALSE, echo=TRUE}
library(dplyr)

cat("Analysis for Sepal.Length\n")
iris |>
  select(Species, Sepal.Length) |>
  group_by(Species) |>
  summarize(Mean = mean(Sepal.Length, rm.na = TRUE),
            SD = sd(Sepal.Length),
            Quantile = quantile(Sepal.Length, probs = .25, rm.na = TRUE)) |>
  as.data.frame() |> print()
cat("\n")

cat("Analysis for Sepal.Width\n")
iris |>
  select(Species, Sepal.Width) |>
  group_by(Species) |>
  summarize(Mean = mean(Sepal.Width, rm.na = TRUE),
            SD = sd(Sepal.Width),
            Quantile = quantile(Sepal.Width, probs = .25, rm.na = TRUE)) |>
  as.data.frame() |> print()
cat("\n")

cat("Analysis for Petal.Length\n")
iris |>
  select(Species, Petal.Length) |>
  group_by(Species) |>
  summarize(Mean = mean(Petal.Length, rm.na = TRUE),
            SD = sd(Petal.Length),
            Quantile = quantile(Petal.Length, probs = .25, rm.na = TRUE)) |>
  as.data.frame() |> print()
cat("\n")

cat("Analysis for Petal.Width\n")
iris |>
  select(Species, Petal.Width) |>
  group_by(Species) |>
  summarize(Mean = mean(Petal.Width, rm.na = TRUE),
            SD = sd(Petal.Width),
            Quantile = quantile(Petal.Width, probs = .25, rm.na = TRUE)) |>
  as.data.frame() |> print()
cat("\n")

```

A macro like this is highly re-useable. You can apply it to any dataset, and
any set of variables.  If desired, you could put macro conditions inside
the macro function to handle different scenarios, such as the absence of 
a grouping variable.  

Note that you can use the macro capabilities to construct **tidyverse** 
statements dynamically.  This feature is convenient because **tidyverse** 
statements make heavy use of non-standard evaluation, which sometimes
make them difficult to use inside functions, loops, or other dynamic situations.

[top](#top) 

## Macro Line Continuation {#line}

### Syntax
```
#%>  <code>
```
Where "&lt;code&gt;" is a segment of macro code continued from the previous line.
The line continuation is used in situations where the macro command cannot
fit on a single line, or is inconvenient to do so.  

### Details

Sometimes it is necessary to wrap a macro call over several lines. You may
have a macro variable with a vector of several values, or a user-defined macro 
function with several parameters. The macro line continuation allows you 
to spread that call over several lines in the editor, and still execute 
the call as expected.

### Examples

The below example shows a macro assignment with a vector of name/value pairs.
In such a scenario, the macro line continuation operator makes the code much
more readable:
```{r eval=FALSE, echo=TRUE}
#%let trt_grps <- c("ARM A" = "Placebo",
#%>                 "ARM B" = "Drug 10mg",
#%>                 "ARM C" = "Drug 20mg",
#%>                 "ARM D" = "Competitor")

a <- `&trt_grps`
print(a)

```
The generated code for the above macro is as follows:
```{r eval=FALSE, echo=TRUE}
a <- c("ARM A" = "Placebo",
                 "ARM B" = "Drug 10mg",
                 "ARM C" = "Drug 20mg",
                 "ARM D" = "Competitor")
print(a)

```
And the printed result is this:
```
       ARM A        ARM B        ARM C        ARM D 
   "Placebo"  "Drug 10mg"  "Drug 20mg" "Competitor" 

```
As you can see, the `trt_grps` vector was assigned normally, replaced
as assigned, and can be used as you would expect.  

[top](#top) 


Next: [Symbol Table Functions](macro-symtable.html)
