The msource function is used to pre-process and source macro-enabled programs. The function first runs the macro pre-processor to evaluate any macro commands. During macro evaluation, an output file is created that contains generated code. After pre-processing, this generated code is sourced normally.

msource(
  pth = Sys.path(),
  file_out = NULL,
  envir = globalenv(),
  exec = TRUE,
  debug = FALSE,
  debug_out = NULL,
  ...
)

Arguments

pth

The path to the R program to process. This parameter is required. It will default to the currently activated program in the development environment.

file_out

If you want to save or view the generated code from the msource function, supply a full path and file name on this parameter. Default is NULL. When NULL, the function will create a temp file for the generated code. The temp file will be deleted when processing is complete.

envir

The environment to be used for program execution. Default is the global environment.

exec

Whether or not to execute the output file after pre-processing. Default is TRUE. When FALSE, only the pre-processing step is performed. If a file_out parameter is supplied, the generated code file will still be created.

debug

If TRUE, prints lines to the console as they are processed. This information case be useful for debugging macro code. Default is FALSE.

debug_out

A path to a file to be used for debugging. If a path is supplied, debug output will be written to the file instead of the console. Default is NULL.

...

Follow-on parameters to the source function. See the source function for additional information.

Value

The results of the source() function, invisibly. The path of the resolved output file is also included under the "$output" list item.

Details

R does not have a native macro language. The macro package attempts to make up for that deficiency. The package devised a set of macro commands inspired by SAS syntax, which can be added to any R script. The macro commands are placed in R comments, prefixed by the characters "#%".

The macro commands function as pre-processor directives, and the msource function is the pre-processor. These commands operate as text replacement and branching functions. They allow you to perform high-level manipulation of your program before the code is executed.

How to Use

The msource function works very much like the Base R source function. You pass the path to the code file as the first parameter, and msource will run it. The difference is that msource will first pre-process the file and resolve any macro commands. The resolved code is placed by default into a temp file and then executed. If you wish to save the generated code, supply a local path on the file_out parameter.

The msource function can be run on the command line or from an R script. When run from the command line, the function will take the currently active program in RStudio as the default input. That means if you are working interactively in RStudio, you can easily execute your macro code just by running msource() on the command line with no parameters.

Macro Commands

Here is a summary of the available macro commands:

  • #%<comment>: A macro comment.

  • #%include '<path>': Inserts code from included file as text into current program.

  • #%if (<condition>): Begins a macro conditional block.

  • #%elseif (<condition>): Defines a subsequent conditional block.

  • #%else: Identifies the default behavior in a condition.

  • #%end: Ends a macro condition.

  • %sysfunc(<expression>): Evaluates an R expression as part of a macro command.

  • %symexist(<name>): Determines if a macro variable name exists in the macro symbol table.

Note that there are no "do" loop commands in the R macro language. This functionality would be redundant to existing R looping structures, and is therefore unnecessary.

You can find extensive documentation for the above macro functions in the the Macro Language vignette. To access the vignette, run vignette("macro-language") on the R command line.

Pre-Processor

There are three main steps to processing a macro-enabled program: pre-process the input file, generate an output file, and then execute the output file.

The pre-processor works by inputting each line of the program line by line. For each line in the input script, the function will assign and replace any macro variables. The pre-processor also evaluates any macro conditions. For any macro conditions that are TRUE, the pre-processor will output that line to the generated code file. If a condition evaluates as FALSE, the lines inside that block will be ignored.

In short, the pre-processor scans the input program from top to bottom, spitting out lines or not depending on the macro conditions. This logic makes the macro package perfect for code generation.

Code Generation

Code generation in R is most often performed using string concatenation, and writing out strings to a file. The macro package gives you a much easier way to do it. Using pre-processor directives, you can write your code as normal code. These code lines will be subject to the syntax checker, and any errors in syntax will be highlighted immediately.

The macro package also makes it easy to construct code from code snippets. You can store your snippets in separate files, and then pull them together using #%include and macro logic.

The collation process is further enhanced by the macro debugger. The debugger allows you to solve issues in the macro code much faster and easier than doing string concatenation.

Debugger

The msource function has a built-in debugger. The debugger can be very useful when identifying problems in your macro-enabled program. The debugger can be activated by setting debug = TRUE on your call to msource. When activated, the debugger will by default send debug information to the R console. The debug information can show you which lines made it into the output file, and how those lines resolved. It will also echo the source call for the generated code. If an error occurs at either of these stages, the debug information will help you pinpoint which line produced the error.

For a full explanation of the debugger capabilities and several examples, see the debugging vignette at vignette('macro-debug').

Output File Execution

Once the output file has been generated successfully, the msource function will execute it normally using the Base R source function. At this point the generated code runs like a normal R program, and any errors or warnings will be sent to the console by default.

If you do not wish to execute the generated code, use the exec parameter to turn off execution.

Examples


# Create temp file names
tmp <- file.path(tempdir(), "test.R")
out <- file.path(tempdir(), "test_mod.R")

# Write macro code to temp file
cat("#%let a <- 1\n", file = tmp)
cat("#%if (a. == 1)\n", file = tmp, append = TRUE)
cat("print('a is one!')\n", file =tmp, append = TRUE)
cat("#%else\n", file = tmp, append = TRUE)
cat("print('a is something else!')\n", file = tmp, append = TRUE)
cat("#%end", file = tmp, append = TRUE)
cat("\n", file = tmp, append = TRUE)

# Process temp file
res <- msource(tmp, out)
# [1] "a is one!"

# Read output file
readLines(res$output)
# [1] "print('a is one!')"

# Rerun in debugger
msource(tmp, out, debug = TRUE)
# ********************************************************************************
# **  Pre-Processing
# ********************************************************************************
# -    File In: C:\Users\dbosa\AppData\Local\Temp\RtmpyM9Kep/test.R
# -   File Out: C:\Users\dbosa\AppData\Local\Temp\RtmpyM9Kep/test_mod.R
# ********************************************************************************
# [ In#][Out#]:
# [   1][    ]: #%let a <- 1
# [   2][    ]: #%if (a. == 1)
# [   3][   1]: print('a is one!')
# [   4][    ]: #%else
# [   5][    ]: print('a is something else!')
# [   6][    ]: #%end
# ********************************************************************************
# **  Execution
# ********************************************************************************
#
# > print('a is one!')
# [1] "a is one!"
#
# ********************************************************************************
# **  End
# ********************************************************************************