Let's suppose I have a list of parameter names in a SAS data set, two of which are shown here
GainRange_Test Gain DCGN_L (None) 401
Image_Test UnifRMS (%rms) 114
Note that the second parameter name contains a percent sign, if I'm not careful SAS will think I am talking about the %rms macro, when in fact this is just text.
So, if I capture these parameter names in a SAS macro variable ¶meters as follows:
proc sql noprint;
select distinct parameter into :parameters separated by '~' from use_these_parameters1;
quit;
%put PARAMETERS %superq(parameters);
The %put statement gives the correct result:
PARAMETERS GainRange_Test Gain DCGN_L (None) 401~Image_Test UnifRMS (%rms) 114
Now, I want to run this macro variable through a macro %eqt1 that does some useful tasks ... please note that I have stripped most of the useful tasks out of the example next just to illustrate the problem
%macro eqt1(string=);
%do ii=1 %to %sysfunc(countw(%nrbquote(&string),~));
%let thisword=%qscan(%nrbquote(&string),&ii,~);
%put II &ii PARAMETER "%quote(%qupcase(%nrbquote(&thisword)))";
%end;
%mend;
%eqt1(string=%superq(parameters))
Works properly (as far as I can tell), however note that in the line of the macro which begins with %put II that I have a whole lot of quoting going on. If I leave out some of this quoting, for example, if I remove the %quote function, I get a WARNING in my SASLOG
WARNING: Apparent invocation of macro RMS not resolved.
even though everything seems to work properly. The WARNING makes me nervous, but so does this extreme amount of quoting within a single command. So, my questions are:
1) Should I worry about this WARNING?
2) Is there a simpler way to do this quoting that doesn't produce the WARNING?
Paige,
Much of the time, the warning won't hurt anything, but there are some pitfalls. What if there actually was a macro named %RMS? Can you guarantee that the "apparent invocations" will never actually find a macro with that name? The other pitfall involves SASAUTOS and the autocall facility. Once a search for a named macro fails, the automatic search process is turned off. Additional macros would not be found, when they exist in the autocall library but have not yet been defined in the program.
I would guess this works, but haven't tested it:
%let thisword=%superq(thisword);
The %PUT statement shouldn't attempt to unquote anything. While you have simplified your application, it is unlikely that quoting will produce any harm in subsequent code.
Good luck.
Paige,
Much of the time, the warning won't hurt anything, but there are some pitfalls. What if there actually was a macro named %RMS? Can you guarantee that the "apparent invocations" will never actually find a macro with that name? The other pitfall involves SASAUTOS and the autocall facility. Once a search for a named macro fails, the automatic search process is turned off. Additional macros would not be found, when they exist in the autocall library but have not yet been defined in the program.
I would guess this works, but haven't tested it:
%let thisword=%superq(thisword);
The %PUT statement shouldn't attempt to unquote anything. While you have simplified your application, it is unlikely that quoting will produce any harm in subsequent code.
Good luck.
Can you guarantee that the "apparent invocations" will never actually find a macro with that name? The other pitfall involves SASAUTOS and the autocall facility.
Today, I can state that there are no %rms macros available to the system. I cannot state that this will always be true in the future, nor can I anticipate possible future parameter names that might contain other text strings beginning with a percent sign. Thus, my desire to make sure the code always avoids trying to use %rms (or %whatever) in the parameter names as a macro invocation.
I would guess this works, but haven't tested it:
%let thisword=%superq(thisword);
Hmmm ... appears to work as you stated, I remove all the quoting from the %put statement except I still need %qupcase, and it works.
So, I don't know if there's a simple explanation why this works, I will attempt to figure this out, but if you can explain in one paragraph why this works, it would be greatly appreciated.
And why doesn't %NRBQUOTE work here? It seems to me that from my reading of the docs about %NRBQUOTE, it should do what I want as well, but it does not.
Best guess: %nrbquote needs to resolve &STRING. As part of that resolution, it keeps re-resolving until all macro triggers (& and %) are gone. Once that happens, %nrbquote works on the resulting string.
The attempted invocation of %rms occurs when resolving &string, before %nrbquote starts quoting.
Are the strings data or code. I you can keep them out of the code (macro world) then no problem. Maybe easier said than done.
I would put the call to %SUPERQ() right after the SQL code that creates the string.
data have ;
input parameter $50. ;
cards4;
GainRange_Test Gain DCGN_L (None) 401
What about, comma?
Image_Test UnifRMS (%rms) 114
;;;;
proc sql noprint;
select distinct parameter into :parameters separated by '~' from have;
%let parameters=%superq(parameters);
quit;
%macro eqt1(string=);
%do ii=1 %to %sysfunc(countw(&string,~));
%let thisword=%qscan(&string,&ii,~);
%let upword = %qupcase(&thisword);
%put II=&ii PARAMETER=|&upword|;
%end;
%mend;
%eqt1(string=¶meters);
Thanks, everyone.
I had a feeling there were much simpler ways to get this done, I just have to get to the point where these concepts are much clearer in my mind.
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.