BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.

Hi All,

 

Over on Stack Overflow there was a routine question about how to macro quote a value like OR, and one of the comments surprised me. https://stackoverflow.com/questions/50395483/how-to-mask-or-with-variable-list-passed-through-using-...

 

A simple macro like:

 

%macro ck(state) ;
  %if &state=RI %then %put Rhode Island! ;
  %else %put Not Rhode Island ;
%mend ck ;

Will choke given a value like OR or 1+.2, because they are seen by the implied %EVAL as operators:

 

100 %ck(OR)
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric
operand is required. The condition was: &state=RI
ERROR: The macro CK will stop executing.

101 %ck(1+.2)
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric
operand is required. The condition was: &state=RI
ERROR: The macro CK will stop executing.

 

My answer would be to use a macro execution time quoting function, e.g. %superq(state) or %bquote(&state) to quote the resolved value of STATE.

 

The surprise to me is that apparently %STR(&state) will work (?) for the value of OR:

 

110 %macro ck(state) ;
111 %if %str(&state)=RI %then %put Rhode Island! ;
112 %else %put Not Rhode Island ;
113 %mend ck ;
114
115 %ck(OR)
Not Rhode Island

 

I would have expected that to fail, since %str() works at macro compile time, I would think the OR would still be seen during macro execution time, and would cause problems for %EVAL.

 

Happily, %STR() is not enough to mask 1+.2 , so it still errors:

 

116 %ck(1+.2)
ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric
operand is required. The condition was: &state=RI
ERROR: The macro CK will stop executing.


And happily, %SUPERQ can handle all those scenarios, so I'll definitely keep using %superq and %bquote for execution-time quoting. But I'm curious why %STR(&state) seems to be enough to mask OR.

 

Kind Regards,
--Q.

BASUG is hosting free webinars Next up: Mike Sale presenting Data Warehousing with SAS April 10 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
1 ACCEPTED SOLUTION

Accepted Solutions
Quentin
Super User

Given a macro that uses %STR() to attempt execution-time macro quoting:

 

%macro ck(state) ;
  %if %str(&state)=RI %then %put Rhode Island! ;
  %else %put Not Rhode Island ;
%mend ck ;

 

1. Why does %ck(OR) work? That is, why does %STR() suffice to mask the resolved OR operator?
2. Given that %ck(OR) works, why does %ck(|) not work? Why doesn't %STR() mask the resolved | operator?

 

I think I understand it after re-reading what I consider to be the trinity of macro quoting papers:
https://www.lexjansen.com/nesug/nesug99/bt/bt185.pdf
https://www.lexjansen.com/nesug/nesug03/at/at012.pdf
https://analytics.ncsu.edu/sesug/2008/CS-049.pdf

 

%STR() works at macro compile time. %STR(SOME ARGUMENT) adds an unprintable delta character before SOME ARGUMENT, an unprintable delta character after SOME ARGUMENT, and looks in the argument for a list of symbols that need to be masked by converting them to delta characters. The list includes ; | + - = etc. Since %STR() works at compile time, it cannot mask any symbols that result from resolving a macro reference. because it won't see them.

 

So why does %STR(&state) work when state resolves to OR? It's because even though %STR never sees the OR token, it still adds the delta characters before and after the OR. And it turns out that when %STR() macro quotes an OR, it doesn't actually convert OR into delta characters.  Adding the delta characters to the beginning and end is enough.

 

If you run:

%macro test(dummy) ;
  %let OR=%str(0 OR 1) ;
  %let pipe=%str(0|1) ; 
  %let plus=%str(0+1) ;
  %put _local_ ;
%mend ck ;
%test()


The %PUT _local_ allows you to see some of the delta characters added. This shows that in order to mask OR, %str() does not actually convert the OR token to masked characters. The delta characters added at the beginning and end of the string are enough to mask the OR. But for the pipe operator and + operator, when %STR masks them it converts them to delta characters.

 

I think that explains why %ck(OR) works but %ck(|) and %ck(1+.1) still error.  %STR() doesn't actually have to see the OR in order to mask it, because adding a delta character to the front and end is enough. But for | and + to be masked, %STR() needs to see them, so that they can be converted to a delta character. Since %STR can't see them (when they result from resolving a macro variable), it can't mask them.

 

Sounds reasonable? 

BASUG is hosting free webinars Next up: Mike Sale presenting Data Warehousing with SAS April 10 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.

View solution in original post

7 REPLIES 7
novinosrin
Tourmaline | Level 20

Odd use of %STR() at execution time beats all odds of SAS documenatation. Thank you @Quentin Sir for sharing and helping folks like me learn something new. 

Ksharp
Super User

I am not sure I correctly understood it.

 

Compile stage is before execution stage, then mask a macro variable would not be a problem .

i.e. the mask character(delta) is still there. when in execution stage the macro variable is still be masked .

Quentin
Super User

@Ksharp wrote:

I am not sure I correctly understood it.

 

Compile stage is before execution stage, then mask a macro variable would not be a problem .


 

Since %STR() works at macro compile time, it cannot quote a value that results from resolving a macro variable. Because it works before the macro variable has resolved.  

 

So below, the first comparison succeeds because %STR() masks the + operator.  The second comparison errors because %STR() can't mask the + operator that results from resolving MVAR because it doesn't see it.

 

%macro test(dummy) ;
  %if %str(1+A)=%str(1+A) %then %put 1+A=1+A ;

  %local mvar ;
  %let mvar=1+B ;
  %if %str(&mvar)=%str(1+B) %then %put 1+B=1+B ;
%mend ;

%test()

The surprise to me was that %STR() can effectively mask OR  which results from resolving MVAR. 

 

%macro test(dummy) ;
  %if %str(1 OR A)=%str(1 OR A) %then %put 1+A=1+A ;

  %local mvar ;
  %let mvar=1 OR B ;
  %if %str(&mvar)=%str(1 OR B) %then %put 1 OR B=1 OR B ;
%mend ;

%test()

 

I think I understand it now, will add a separate post with my explanation.

 

 

BASUG is hosting free webinars Next up: Mike Sale presenting Data Warehousing with SAS April 10 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Ksharp
Super User

@Quentin

 

Interesting thing is if there is not =-/* ,the code would work.

 

 

%macro test(dummy) ;
  %if %str(1+A)=%str(1+A) %then %put 1+A=1+A ;

  %local mvar ;
  %let mvar=1BB ;
  %if %str(&mvar)=%str(1+B) %then %put 1+B=1+B ;
%mend ;

%test()

 

It looks like SAS add %eval() where string has '+' .

 

if there is a '+' ,then code would look like  %if %str(%eval(1+b))  -->  %if  delta %eval() delta 

if there is not a '+' then code would look like  %if %str(1bb)  -->  %if  delta 1bb delta 

so this should work .

Quentin
Super User

The %IF statement always uses %EVAL to evaluate the expression.  I added a separate answer.

 

I think the short answer is that %str(&mvar) can "work" to mask OR because of the way %STR() quotes OR, which is different than the way %STR() quotes + or |. 

 

%STR() quotes OR by adding a delta character at the beginning and end of the quoted string.

%STR() quotes + and | by adding a delta character at the beginning and end of the quoted string, and converting the quoted symbols into delta characters.

 

Since %STR(OR) doesn't convert OR into delta characters, I think its a side effect that %STR(&mvar) works when MVAR resolves to OR.

BASUG is hosting free webinars Next up: Mike Sale presenting Data Warehousing with SAS April 10 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Quentin
Super User

Given a macro that uses %STR() to attempt execution-time macro quoting:

 

%macro ck(state) ;
  %if %str(&state)=RI %then %put Rhode Island! ;
  %else %put Not Rhode Island ;
%mend ck ;

 

1. Why does %ck(OR) work? That is, why does %STR() suffice to mask the resolved OR operator?
2. Given that %ck(OR) works, why does %ck(|) not work? Why doesn't %STR() mask the resolved | operator?

 

I think I understand it after re-reading what I consider to be the trinity of macro quoting papers:
https://www.lexjansen.com/nesug/nesug99/bt/bt185.pdf
https://www.lexjansen.com/nesug/nesug03/at/at012.pdf
https://analytics.ncsu.edu/sesug/2008/CS-049.pdf

 

%STR() works at macro compile time. %STR(SOME ARGUMENT) adds an unprintable delta character before SOME ARGUMENT, an unprintable delta character after SOME ARGUMENT, and looks in the argument for a list of symbols that need to be masked by converting them to delta characters. The list includes ; | + - = etc. Since %STR() works at compile time, it cannot mask any symbols that result from resolving a macro reference. because it won't see them.

 

So why does %STR(&state) work when state resolves to OR? It's because even though %STR never sees the OR token, it still adds the delta characters before and after the OR. And it turns out that when %STR() macro quotes an OR, it doesn't actually convert OR into delta characters.  Adding the delta characters to the beginning and end is enough.

 

If you run:

%macro test(dummy) ;
  %let OR=%str(0 OR 1) ;
  %let pipe=%str(0|1) ; 
  %let plus=%str(0+1) ;
  %put _local_ ;
%mend ck ;
%test()


The %PUT _local_ allows you to see some of the delta characters added. This shows that in order to mask OR, %str() does not actually convert the OR token to masked characters. The delta characters added at the beginning and end of the string are enough to mask the OR. But for the pipe operator and + operator, when %STR masks them it converts them to delta characters.

 

I think that explains why %ck(OR) works but %ck(|) and %ck(1+.1) still error.  %STR() doesn't actually have to see the OR in order to mask it, because adding a delta character to the front and end is enough. But for | and + to be masked, %STR() needs to see them, so that they can be converted to a delta character. Since %STR can't see them (when they result from resolving a macro variable), it can't mask them.

 

Sounds reasonable? 

BASUG is hosting free webinars Next up: Mike Sale presenting Data Warehousing with SAS April 10 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Ksharp
Super User

That is just what I want to say .

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

How to Concatenate Values

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 7 replies
  • 1230 views
  • 1 like
  • 3 in conversation