BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
whymath
Lapis Lazuli | Level 10

Recently, I happend to find cat*() function will be parsing unexpected, when they using in %sysfunc().

I want to use catx() in %sysfunc(), test code like this:

%let newtext=%sysfunc(catx(%str(, ),%str(grade>80),test text));
%put &=newtext;

%let newtext=%sysfunc(catx(%str(, ),%str(grade<80),test text));
%put &=newtext;

However, SAS takes %str(grade>80) as an expression and just resolve it as 1. The result of above code is:

NEWTEXT=1, test text
NEWTEXT=0, test text

The raw link is here.

 

I have do more test after that, just find the secret of cat*(), which has been pointed by @Tom:

%let text=%sysfunc(catx(%str(,),grade>80,test text));
%let text=%sysfunc(cats(grade>80,test text));
%let text=%sysfunc(coalescec(grade>80,test text));

Results:

1,test text
1test text
grade>80

I have tried more than ten different function, only cat*() function will parse its parameter as expression, not plain text.

I was going to give up the pursuit because of working things, however, my friends inspired me to go further.

 

One of my friend, who doesn't have her account in this site, tested something new:

%let text=%sysfunc(catx(%str(,),%nrstr(%str(grade>80)),test text));

The result was:

grade>80,test text

The answer was more accurate now.

 

Four days ago, my friend @Richard write to me and he tested more further:

%let p=lt;
%put %superq(&p);
%put %sysfunc(cats(%superq(&p),q));

Which logs:

53   %let p=lt;
54
55   %put %superq(&p);
WARNING: Apparent symbolic reference LT not resolved.

56   %put %sysfunc(cats(%superq(&p),q));
WARNING: Apparent symbolic reference LT not resolved.
ERROR: %SYSEVALF function has no expression to evaluate.

He thought this CATS function was attempting a macro evaluation prior to passing result as a parameter.

 

Thank you, my friends. You remind me the old school days, my teachers and my classmates.

Now, how do you think of this oddity of %SYSFUNC(CAT*())?

1 ACCEPTED SOLUTION

Accepted Solutions
Quentin
Super User

Hi,

 

I agree, this is an oddity of CAT* when called by Sysfunc.  I believe the core issue is that the CAT* family of functions does handles both character and numeric arguments, and when called by %SYFUNC it needs to look at the arguments to decide whether they are character expressions or numeric expressions.  As @Tom helpfully pointed out in this thread: https://communities.sas.com/t5/SAS-Programming/Macro-quoting-for-a-url/m-p/643208#M191928, this is not an issue in the DATA step because the data step compiler will know if an argument is character or numeric, and whether an argument is a text value or an expression.

 

I also originally hoped that by adding macro quoting you could force an expression to be seen as a character string, but it doesn't work well because macro quoting is (should be) automatically removed/unquoted before the arguments are passed to the CAT function.  I'm surprised that:

 

%let text=%sysfunc(catx(%str(,),%nrstr(%str(grade>80)),test text));
%put &text;

works. It might be that SAS is not unquoting appropriately.

 

 

One hack I had suggested in an earlier thread is to add actual quote marks around the value, which will then be passed to CAT as part of the argument, and will force the argument to be seen as a character string rather than logical expression.  The cost is you need to add a call to compress to remove the quotes, e.g.:

 

%let text=%sysfunc(catx(%str(,),'grade>80',test text));
%put &text;

%let text=%sysfunc(compress(%qsysfunc(catx(%str(,),'grade>80',test text)),%str(%')));
%put &text;

 

 

Returns:

1    %let text=%sysfunc(catx(%str(,),'grade>80',test text));
2    %put &text;
'grade>80',test text
3
4    %let text=%sysfunc(compress(%qsysfunc(catx(%str(,),'grade>80',test text)),%str(%')));
5    %put &text;
grade>80,test text

In the SAS language, it is (almost?) always explicitly communicated whether an argument is numeric or character.  In the macro language there are settings where the language has to look at a value and "decide", such as this case with %SYFUNC(CAT*()) , and also with %EVAL.  And sometimes the macro language decides wrong.  I had submitted a ballot item to propose the macro language add %EvalC and %EvalN to avoid some of this guessing.

 

 

 

 

 

BASUG is hosting free webinars Next up: Mark Keintz presenting History Carried Forward, Future Carried Back: Mixing Time Series of Differing Frequencies on May 8. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.

View solution in original post

4 REPLIES 4
Tom
Super User Tom
Super User

It looks like the %NRSTR() around already macro quoted text helps convince %SYSFUNC() to treat the field as character.

But it does not appear to help with preventing %SYSFUNC() from trying to evaluate % and & triggers in the text.

Using two nested calls to %NRSTR() seems to help.  And of course using %QSYSFUNC() to quote the result if it contains macro triggers or other characters you might need to use macro quoting on.

100  %put %sysfunc(catx(SEP,%nrstr(%nrstr(grade>80)),test text));
grade>80SEPtest text
101  %put %qsysfunc(catx(SEP,%nrstr(%nrstr(%%grade)),test text));
%gradeSEPtest text

But in macro code you can just expand the values where you need them to concatenate text.  So there is no need to use these data step functions for concatenating text in macro code.  So I would just never use them with %SYSFUNC().

Quentin
Super User

Hi,

 

I agree, this is an oddity of CAT* when called by Sysfunc.  I believe the core issue is that the CAT* family of functions does handles both character and numeric arguments, and when called by %SYFUNC it needs to look at the arguments to decide whether they are character expressions or numeric expressions.  As @Tom helpfully pointed out in this thread: https://communities.sas.com/t5/SAS-Programming/Macro-quoting-for-a-url/m-p/643208#M191928, this is not an issue in the DATA step because the data step compiler will know if an argument is character or numeric, and whether an argument is a text value or an expression.

 

I also originally hoped that by adding macro quoting you could force an expression to be seen as a character string, but it doesn't work well because macro quoting is (should be) automatically removed/unquoted before the arguments are passed to the CAT function.  I'm surprised that:

 

%let text=%sysfunc(catx(%str(,),%nrstr(%str(grade>80)),test text));
%put &text;

works. It might be that SAS is not unquoting appropriately.

 

 

One hack I had suggested in an earlier thread is to add actual quote marks around the value, which will then be passed to CAT as part of the argument, and will force the argument to be seen as a character string rather than logical expression.  The cost is you need to add a call to compress to remove the quotes, e.g.:

 

%let text=%sysfunc(catx(%str(,),'grade>80',test text));
%put &text;

%let text=%sysfunc(compress(%qsysfunc(catx(%str(,),'grade>80',test text)),%str(%')));
%put &text;

 

 

Returns:

1    %let text=%sysfunc(catx(%str(,),'grade>80',test text));
2    %put &text;
'grade>80',test text
3
4    %let text=%sysfunc(compress(%qsysfunc(catx(%str(,),'grade>80',test text)),%str(%')));
5    %put &text;
grade>80,test text

In the SAS language, it is (almost?) always explicitly communicated whether an argument is numeric or character.  In the macro language there are settings where the language has to look at a value and "decide", such as this case with %SYFUNC(CAT*()) , and also with %EVAL.  And sometimes the macro language decides wrong.  I had submitted a ballot item to propose the macro language add %EvalC and %EvalN to avoid some of this guessing.

 

 

 

 

 

BASUG is hosting free webinars Next up: Mark Keintz presenting History Carried Forward, Future Carried Back: Mixing Time Series of Differing Frequencies on May 8. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
SASKiwi
PROC Star

I'm struggling to understand why you need CATX here when normal macro text concatenation works just fine:

%let newtext=%str(, )%str(grade>80) test text;
%put &=newtext;

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

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
  • 4 replies
  • 567 views
  • 4 likes
  • 5 in conversation