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.

The Boston Area SAS Users Group (BASUG) is hosting our in person SAS Blowout on Oct 18!
This full-day event in Cambridge, Mass features four presenters from SAS, presenting on a range of SAS 9 programming topics. Pre-registration by Oct 15 is required.
Full details and registration info at 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? 

The Boston Area SAS Users Group (BASUG) is hosting our in person SAS Blowout on Oct 18!
This full-day event in Cambridge, Mass features four presenters from SAS, presenting on a range of SAS 9 programming topics. Pre-registration by Oct 15 is required.
Full details and registration info at 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.

 

 

The Boston Area SAS Users Group (BASUG) is hosting our in person SAS Blowout on Oct 18!
This full-day event in Cambridge, Mass features four presenters from SAS, presenting on a range of SAS 9 programming topics. Pre-registration by Oct 15 is required.
Full details and registration info at 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.

The Boston Area SAS Users Group (BASUG) is hosting our in person SAS Blowout on Oct 18!
This full-day event in Cambridge, Mass features four presenters from SAS, presenting on a range of SAS 9 programming topics. Pre-registration by Oct 15 is required.
Full details and registration info at 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? 

The Boston Area SAS Users Group (BASUG) is hosting our in person SAS Blowout on Oct 18!
This full-day event in Cambridge, Mass features four presenters from SAS, presenting on a range of SAS 9 programming topics. Pre-registration by Oct 15 is required.
Full details and registration info at https://www.basug.org/events.
Ksharp
Super User

That is just what I want to say .

SAS Innovate 2025: Call for Content

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!

Submit your idea!

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
  • 1581 views
  • 1 like
  • 3 in conversation