BookmarkSubscribeRSS Feed
Quentin
Super User

Hi All,

Below is a simple PROC REPORT step where it seems clear that in a COMPUTE block, macro variables are becoming unquoted.

%let mvar=If You Can Read Me, Something Went Wrong;  
%let text=%nrstr(This macro var should not resolve:&mvar);
proc report data=sashelp.class nowd;
 cols name height text;
 define text /computed "&text";
 compute text /character length=90;
   text="&text";
 endcomp;
run;

I would expect &text to return the following, because the & in front of MVAR has been masked by %nrstr().

This macro var should not resolve:&mvar

And in the column label, all is as I expect.  But in the compute block, the macro variable is resolved, and the value of text is:

This macro var should not resolve:If You Can Read Me, Something Went Wrong

A kind correspondent on SAS-L came up with a solution (forgive the cross-post, but my question is more focussed now):

proc report data=sashelp.class nowd;
 cols name height text;
 define text /computed "&text";
 compute text /character length=90;
   text="%nrstr(&text)";
 endcomp;
run;

Which is interesting.  Basically in this solution, %nrstr() is doing the work that should be done by %bquote or %superq, i.e. hiding the value of a macro trigger which results from resolution of a macro variable.

I know that there is a LOT going on inside PROC REPORT.  And given that compute blocks can create temporary variables, and arrays etc, I guess it may be that in some sense it may have its own compile time and execution time. But I can't get my head around *how* this could be working, such that in the above fix, %nrstr does not prevent &text from resolving, but it does prevent &mvar from resolving.

Any thoughts?

Thanks,

-Q.

BASUG is hosting free webinars Next up: Don Henderson presenting on using hash functions (not hash tables!) to segment data on June 12. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
8 REPLIES 8
ArtC
Rhodochrosite | Level 12

I am not totally sure on this, but this is how I have always thought of this process.  Relative to the macro language the timing is not as we would expect from our experience in the DATA step.  First we know that the REPORT step is a three phase process and the compute block is not executed until the third phase, however it seems that during the first phase the compute blocks have to have been scanned once.  Then during the final phase the compute block is scanned again prior to execution.  I have come to this conclusion based on the behavior of the LINE statement and certain oddities with quotes within quotes.  

In your first REPORT step the macro quoting is done outside of the compute block and I suspect that the macro quoting is removed and the macro variable is resolved within the compute block after the first scan.  When %NRSTR appears within the compute block, I think that it is executed after that initial scan and thus the macro quoting is preserved.

This seems to me to be similar behavior (but not exactly the same) to when we use macro language elements in compiled programs like SCL, views, and FCMP steps.

my 2 cents and that is probably about what it is worth.

Quentin
Super User

Thanks very much Art,

So much of the macro language is about timing.  It's fairly straight forward in data step, and most procs.  But timing gets more complex even with things like call execute().

Your description is a useful heuristic, and consistent with my vague sense that compute blocks might have their own compile time.  For most PROCs, I think there is no compile time (cuz they are just interpreted?) and the rules for macro resolution are clear (well, as clear as the macro language is.  : )   But seems like with the complexity of what you can do with a PROC REPORT compute block, I guess I shouldn't be surprised that the rules for how the code in a COMPUTE block is word scanned/macro resolved/compiled/etc might be different than the rules for the data step language.

Appreciate your response (almost started my question with "Dear Art", but didn't want to be presumptuous).  This will be motivation for me to take your PROC REPORT book home for the weekend to re-read especially Chapter 11, Details of the PROC Report Process.

--Q.

BASUG is hosting free webinars Next up: Don Henderson presenting on using hash functions (not hash tables!) to segment data on June 12. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Tim_SAS
Barite | Level 11

You nailed it.

Just like in all SAS programs, the macro processor runs first, before PROC REPORT even starts. PROC REPORT thinks the program looks like this:

proc report data=sashelp.class nowd;

cols name height text;

define text /computed "&text";

compute text /character length=90;

   text="This macro var should not resolve:&mvar";

endcomp;

run;

Initially PROC REPORT just saves up the content of the COMPUTE block (in this case, just the one statement). Later, it hands off the content to the data step. The &mvar variable gets resolved then.

Quentin
Super User

Thanks Tim,

Now I'm a bit confused.

Take my first example:

%let mvar=If You Can Read Me, Something Went Wrong;  
%let text=%nrstr(This macro var should not resolve:&mvar);
proc report data=sashelp.class nowd;
 cols name height text;
 define text /computed "&text";
 compute text /character length=90;
   text="&text";
 endcomp;
run;

Since macro resolution happens first, I would think to a "normal" proc the program would look like below.  So &text has resolved (on both the column label and in the compute block), and some of the symbols are still masked (indicated below by * instead of boxes for the non-prinable masking characters).

proc report data=sashelp.class nowd;
 cols name height text;
 define text /computed "*This macro var should not resolve:*mvar*";
 compute text /character length=90;
   text="*This macro var should not resolve:*mvar*";
 endcomp;
run;

The column label works as I expect.  It's only the compute block that unquotes/unmasks the values, and then resolves &mvar.  You say that the COMPUTE block code is saved and handed off to the data step.  So if my code block above is right, would that mean that the COMPUTE block code is unquoted when the data step compiles it?

Thanks,

--Q.

BASUG is hosting free webinars Next up: Don Henderson presenting on using hash functions (not hash tables!) to segment data on June 12. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Tim_SAS
Barite | Level 11

You're right. I wasn't paying any attention to text's column header, just the COMPUTE block. PROC REPORT gets

text="This macro var should not resolve:&mvar";


PROC REPORT hands this statement to the data step. The point I was trying to make is that PROC REPORT itself doesn't do any macro processing itself. It runs after the macro pass like any other proc. Whatever statements it finds in COMPUTE blocks* go to the data step. I'm not an authority on macro or data step processing, but I suppose you're right: &mvar gets resolved when the data step compiles the statement.


*Except for LINE statements. PROC REPORT executes LINE statements itself.

Quentin
Super User

Thanks again Tim.

I hadn't realized PROC REPORT was passing off the COMPUTE block code to the data step.  That's helpful.  And fits with Art's description of there being multiple passes of COMPUTE block code during which macro variables might be scanned, unquoted, and resolved.

--Q.

BASUG is hosting free webinars Next up: Don Henderson presenting on using hash functions (not hash tables!) to segment data on June 12. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
ArtC
Rhodochrosite | Level 12

It might be helpful to add that while the compute block is handled by much of the same software as the DATA step, it is not a DATA step exactly.  The processing is different - there is no PDV and the order of the execution of the various compute blocks (if there is more than one) is dependent on factors other than the data (such as, order of variables on the COLUMN statement and presence of BEFORE and AFTER location specifications).

Tom
Super User Tom
Super User

Perhaps the compute block doing something similar to the RESOLVE() function?

%let subvar=Nest variable;

%let var=Outer variable %nrstr(&subvar) . ;

data _null_;

  x=symget('var');

  put x=;

  z=resolve(x);

  put z=;

run;

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
  • 8 replies
  • 3067 views
  • 7 likes
  • 4 in conversation