BookmarkSubscribeRSS Feed

Fixing Broken Hearts and Macros: Q Functions

Started 3 weeks ago by
Modified 3 weeks ago by
Views 380

Welcome back to Fixing Broken Hearts and Macros. In previous posts, we talked about macro timing, quoting functions, and two macros that helped us reduce character variable lengths. This week, our relationship trouble isn’t with data- it’s with macro functions, and more specifically, what happens when masked text suddenly decides to betray us.

 

In the first post of the series, we learned that macro timing matters. A lot. Two solutions were introduced to mask troublesome characters: %STR and %NRSTR. These functions mask special characters during macro compilation. However, even when we use these quoting functions, we can still run into problems once macro functions are involved.

 

In this post, we focus on what to do when masked macro variables behave unexpectedly during resolution and discover a new heartbreak remedy- macro Q functions.

 

 

Picking Up Where We Left Off

 

Let’s start with the same code from the first post.

 

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

%put &=prodnm;

 

Log:

 

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

 

Two macro variables are created: &stick and &prodnm. The %NRSTR macro function masks the ampersand in Peel&Stick so that &Stick is not resolved at compilation. As a result, the product name is stored correctly in the global symbol table.

 

Now suppose we want to store this value in all capital letters. We create a new macro variable named &uProd and use the %UPCASE macro function.

 

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

%put &=uProd;

 

Log:

 

UPROD=C-LINE PEELUnwantedText ADD-ON FILING POCKETS, 8-3/4 X 5-1/8, 10/PACK

 

Despite using %NRSTR when creating &prodnm, &stick is still resolved. Why does this happen?

 

 

Understanding What Went Wrong

 

The answer comes down to timing… again.

 

In comparison to normal SAS function, macro functions manipulate text, not data values. They are executed by the macro processor before the SAS code is compiled and executed.

 

While &prodnm is stored in the global symbol table with masked special characters, those masks do not automatically survive the use of standard macro functions.

 

01_CJC_Picture4-1.png

 

Select any image to see a larger version.
Mobile users: To view the images, select the "Full" version at the bottom of the page.

 

The following screenshots represent a simplified visual of what happens behind the scenes.

 

When the %LET statement containing %UPCASE is encountered, it is sent to the macro processor.

 

02_CJC_Picture5-1.png

 

The masked value of &prodnm is resolved in the macro processor.

 

03_CJC_Picture6-1.png

 

04_CJC_Picture7-1.png

 

Then, the macro processor executes %UPCASE. The masked value is manipulated, and the resolved value is sent back to the input stack. In the input stack, the resolved value is no longer masked.

 

05_CJC_Picture8-1.png

 

This is where things go wrong. As tokenization continues, the word scanner encounters &STICK and sends it back to the macro processor for resolution.

 

06_CJC_Picture9-1.png

 

At that point &STICK resolves to UnwantedText in the macro processor.

 

07_CJCPicture10-2.png

 

The final value of &uProd is stored in the global symbol table unmasked, with undesired results, leaving us heartbroken again.

 

08_CJC_Picture11-2.png

 

 

Healing With Q Functions

 

To remedy this, we can use the “Q” equivalent of the %UPCASE function: %QUPCASE. Macro Q functions mask the returned result after the function executes in the macro processor.

 

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

%put &=quprod;

 

Log:

 

QUPROD=C-LINE PEEL&STICK ADD-ON FILING POCKETS, 8-3/4 X 5-1/8, 10/PACK

 

Why does this work? Let’s look at the visual again.

 

When %QUPCASE is sent to the macro processor, the masked value of &prodnm is resolved just as before. However, because a Q function is used, %QUPCASE is used, the returned value is re-masked before it is sent back to the input stack. As tokenization continues, special characters remain masked and &STICK is seen as just text.

 

09_CJC_Picture12-1.png

 

The masked value of &quprod is then stored in the global symbol table as desired.

 

10_CJC_Picture13-1.png

 

 

Final Thoughts

 

In conclusion, Q functions are essential when you need to apply macro functions to values that contain masked special characters. They allow you to manipulate text while preserving the masking that prevents unintended macro resolution.

 

There are several other Q equivalent macro functions available. For a complete list, see the SAS Help Center documentation on macro functions.  

 

All code examples from this post can be found on my github here.

 

 

Find more articles from SAS Global Enablement and Learning here.

Contributors
Version history
Last update:
3 weeks ago
Updated by:

sas-innovate-2026-white.png



April 27 – 30 | Gaylord Texan | Grapevine, Texas

Registration is open

Walk in ready to learn. Walk out ready to deliver. This is the data and AI conference you can't afford to miss.
Register now and save with the early bird rate—just $795!

Register now

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