BookmarkSubscribeRSS Feed

Fixing Broken Hearts and Macros: NRSTR

Started ‎08-01-2025 by
Modified ‎08-01-2025 by
Views 847

Have you ever had a macro variable that doesn’t resolve to the value you expect? You’re left with a broken heart, wondering where things went wrong. Typical relationship advice would say, it’s just bad timing—and in macro programming, that might actually be true.

 

In this post, we’ll take a look at the timing of macro processing and explore one possible remedy for your broken macro relationships: the %NRSTR function. This is the beginning of a series on macro quoting functions to help you better understand where things are going wrong… or why what you're doing is working just fine.

 

 

It's Complicated: Understanding Macro Timing

 

When a SAS program is submitted, it’s sent to an area of memory called the input stack. The input stack holds the program while the word scanner analyzes characters and breaks them into tokens. Tokens are the smallest pieces of meaningful information to SAS.

 

The four token types are:

 

  • Literal: One or more characters enclosed in single or double quotation marks.
  • Number: A numeric value including integers and real (floating-point) numbers.
  • Name: One or more characters beginning with a letter or an underscore. Other characters can be letters, underscores, and digits.
  • Special: Any character that is not a letter, number, or underscore.

 

A token ends when the word scanner encounters either a blank space or when a new token begins.

 

The following code would be tokenized as shown by the color coding:

 

Data01_Screenshot 2025-07-31 151416.png

 

As the word scanner reads each token, it determines where to send it. Everything in the above program is sent from the input stack to the word scanner for tokenization. After tokenization, it’s sent to the compiler and executed.

 

The ampersand (&) and percent sign (%) are macro triggers. When the word scanner encounters a macro trigger followed by an underscore or character, it sends the information to the macro processor instead of the compiler. The macro processor interprets the macro language and sends the result back to the top of the input stack, where it is re-tokenized by the word scanner.

 

SAS also creates a global symbol table at the beginning of each session to store automatic and global macro variable values. When a macro definition is called, a local symbol table is also created.

 

The diagram below shows the processing flow when a program is submitted:

 

01_CJC_Picture14.png

 

 

A Happy Macro Relationship

 

The following code uses product names from a sample data set with orders placed by superstore customers. The data set and code can be downloaded from my GitHub.

 

Let’s walk through an example of an unproblematic macro.

 

%let prodnm=Bush Somerset Collection Bookcase;

title "Orders placed for: &prodnm";
proc print data=blog.superstore_small;
     where product_name="&prodnm";
run;

 

Here’s what happens:

 

  1. The program is held in the input stack while the word scanner processes one token at a time.
  2. The % macro trigger activates the macro processor.
  3. The macro variable prodnm is created with the value Bush Somerset Collection Bookcase in the global symbol table.
  4. Tokenization continues until the & macro trigger is found in the TITLE statement.
  5. The macro processor resolves &prodnm with the value found in the global symbol table.
  6. The resolved text is sent back to the input stack, tokenized again, and sent to the compiler.
  7. The same thing happens when the & is encountered in the WHERE statement.
  8. When the RUN statement is encountered, it’s a boundary that causes SAS to execute the code.

 

Results:

02_CJC_Picture11.png

 

 

When Your Macro Seems Fine, but Isn't Fine at All

 

In the following program, the macro variable value contains an &, which is a macro trigger. When the program is run, three warnings are generated in the log- but the correct results are returned. Why?

 

%let prodnm=C-Line Peel&Stick Add-On Filing Pockets, 8-3/4 x 5-1/8, 10/Pack;

title "Orders placed for: &prodnm";
proc print data=blog.superstore_small;
     where product_name="&prodnm";
run;

 

Log:

03_CJC_Picture3.png

 

Results:

04_CJC_Picture12-1024x94.png

 

  • The %LET statement includes &Stick, so the macro processor tries to resolve a macro variable named stick.
  • Because no such macro variable exists, SAS issues a warning and includes the & as regular text in the macro variable's stored value.
  • In the TITLE and WHERE statements, the same thing happens: &Stick is scanned, not found as a macro variable, and left as is.

 

The warnings are harmless because there's no macro variable named stick, so nothing is substituted. But what if there is one?

 

 

There's Someone Else... in the Symbol Table

 

%let stick=UnwantedText;
%let prodnm=C-Line Peel&Stick Add-On Filing Pockets, 8-3/4 x 5-1/8, 10/Pack;

title "Orders placed for: &prodnm";
proc print data=blog.superstore_small;
     where product_name="&prodnm";
run;

 

This time, a macro variable named stick exists. Now the macro processor encounters &Stick, finds the value UnwantedText in the global symbol table, and substitutes it. The resulting value of prodnm becomes: C-Line PeelUnwantedText Add-On Filing Pockets.

 

The TITLE and WHERE statements are now incorrect:

 

title "Orders placed for: C-Line PeelUnwantedText Add-On Filing Pockets";

where product_name="C-Line PeelUnwantedText Add-On Filing Pockets";

 

This is not a valid value in the data, therefore no rows are returned, and no report is generated.

 

Log:

05_CJC_Picture5.png

 

 

How to Save the Relationship: %NRSTR

 

To protect your macro values from unintended substitutions, use the %NRSTR quoting function. %NRSTR masks special characters, including & and %, and mnemonic operators in constant text during macro compilation. This prevents the macro processor from resolving macro variables during compilation.

 

%let stick=UnwantedText;
%let prodnm=%nrstr(C-Line Peel&Stick Add-On Filing Pockets, 8-3/4 x 5-1/8, 10/Pack);

title "Orders placed for: &prodnm";
proc print data=blog.superstore_small;
     where product_name="&prodnm";
run;

 

Log:

06_CJC_Picture6.png

 

Results:

07_CJC_Picture13-1024x100.png

 

 

Adding %NRSTR around the value for &prodnm causes the macro processor to treat the & as regular text. Even though a macro variable named stick exists, %NRSTR masks the & so it’s treated as text, not as a macro trigger. You get the expected result, no warnings, and accurate output.

 

 

Additional (Macro) Relationship Advice

 

A similar function to %NRSTR is %STR. Both are used to mask special characters and mnemonic operators in constant text at macro compilation, however, %STR does not mask % and &.

 

The special characters and mnemonics they both mask are:

 

08_CJC_Picture8.png

 

They also mask the following characters when they occur in pairs. If only one of these characters appears in the text, add a preceding % to mask it.

 

09_CJC_Picture9.png

 

For example, the following would cause unexpected log notes due to the unclosed quotation mark:

 

%let prodnm=Eldon Fold 'N Roll Cart System;

 

To mask it, use %STR with a % in front of the quotation mark:

 

 

%let prodnm= %str(Eldon Fold %'N Roll Cart System);
%put &=prodnm;

 

Log:

10_CJC_Picture10.png

 

 

In Conclusion: It's Not You, It's the Macro Processor

 

Here’s what we’ve covered:

 

  • How SAS processes programs with and without macro code
  • How macro timing can affect your results—and your sanity
  • Why warnings occur when macro variables aren’t found
  • How %NRSTR can protect your values from being hijacked by other macros

 

I hope this explanation saves you from future macro heartbreak. Stay tuned for more advice on how to build strong, functional macro relationships.

 

 

Looking for Deeper Healing?

 

If this post feels like the macro programming version of a self-help book, that’s because it is—an encouraging first step toward understanding where things might be going wrong. But sometimes, you need more than just good advice… you need macro therapy.

 

For a deeper dive into macro logic, quoting functions, and how to avoid messy breakups with your code, I highly recommend our SAS e-learning courses. Start with SAS Macro Language 1: Essentials and follow up with SAS Macro Language 2: Advanced Techniques. They’re like a trusted therapist—ready to walk you through the hard parts, step by step.

 

 

Find more articles from SAS Global Enablement and Learning here.

Comments

If I may suggest, the best "macro quoting" reading ever: 

 

"Secrets of Macro Quoting Functions - How and Why" by Susan O’Connor

https://www.lexjansen.com/nesug/nesug99/bt/bt185.pdf

 

@yabwon Wonderful, thank you for sharing!

Contributors
Version history
Last update:
‎08-01-2025 01:28 PM
Updated by:

hackathon24-white-horiz.png

The 2025 SAS Hackathon has begun!

It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.

Latest Updates

SAS AI and Machine Learning Courses

The rapid growth of AI technologies is driving an AI skills gap and demand for AI talent. Ready to grow your AI literacy? SAS offers free ways to get started for beginners, business leaders, and analytics professionals of all skill levels. Your future self will thank you.

Get started