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.
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?
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.
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.
The masked value of &prodnm is resolved in the macro processor.
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.
This is where things go wrong. As tokenization continues, the word scanner encounters &STICK and sends it back to the macro processor for resolution.
At that point &STICK resolves to UnwantedText in the macro processor.
The final value of &uProd is stored in the global symbol table unmasked, with undesired results, leaving us heartbroken again.
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.
The masked value of &quprod is then stored in the global symbol table as desired.
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.
April 27 – 30 | Gaylord Texan | Grapevine, Texas
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!
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.