Hi all,
I try to understand why in below sample macro variable MyMvar doesn't resolve when using call execute().
I understand that this is a timing issue but as simple as the example is, I can't understand the why. Could someone please shed some light on it for me?
%macro testit();
data _null_;
call symputx('MyMvar','Test String','L');
stop;
run;
data _null_;
put "MyMvar: &MyMvar";
stop;
run;
%mend;
data _null_;
call execute('%testit()');
stop;
run;
data _null_;
_rc=dosubl('%testit()');
stop;
run;
40 data _null_; 41 call execute('%testit()'); 42 stop; 43 run; WARNING: Apparent symbolic reference MYMVAR not resolved. NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds NOTE: CALL EXECUTE generated line. 1 + data _null_; call symputx('MyMvar','Test String','L'); stop; run; NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds 1 + data _null_; put "MyMvar: &MyMvar"; stop; run; MyMvar: Test String NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.01 seconds
Thanks,
Patrick
Hi @Patrick,
The late Roland Rashleigh-Berry recommended: Use %nrstr() when you "call execute" a macro. And indeed, if you follow this advice the warning does not occur. What he wrote seems to explain what you have observed: "the macro call will be resolved internally" already within the data step containing the CALL EXECUTE statement. So, when the macro processor parses the code of macro TESTIT it stumbles across macro variable reference &MyMvar, hence the warning, because the data step defining MyMvar has not been executed yet (as macro resolution has not yet finished plus we are still in the "outer" data step).
With DOSUBL it's a different story because of the "parallel processing" even of data steps which it enables.
Hi @Patrick,
The late Roland Rashleigh-Berry recommended: Use %nrstr() when you "call execute" a macro. And indeed, if you follow this advice the warning does not occur. What he wrote seems to explain what you have observed: "the macro call will be resolved internally" already within the data step containing the CALL EXECUTE statement. So, when the macro processor parses the code of macro TESTIT it stumbles across macro variable reference &MyMvar, hence the warning, because the data step defining MyMvar has not been executed yet (as macro resolution has not yet finished plus we are still in the "outer" data step).
With DOSUBL it's a different story because of the "parallel processing" even of data steps which it enables.
CALL EXECUTE is generating two DATA steps. However, it can't actually run either of them immediately. Both of them have to wait until the current DATA step (the one that contains CALL EXECUTE) has finished. Naturally, you just can't run another DATA step until the current DATA step has finished.
Instead, SAS has to stack up the statements that CALL EXECUTE is generating, until the current DATA step has finished. There's a problem stacking up the PUT statement, because &MYVAR doesn't exist yet. It only exists after the current DATA step finishes, and the first generated DATA step completes.
You might see it like that: call execute() pushes the code contained onto the same execution pipeline that the current data step is running in, while dosubl() creates a new, independent execution pipeline that only exists for the duration of the dosubl() call.
If I read it correctly, dosubl() has been added with SAS 9.4 and opens up a whole new way of thinking about timing. At least I can't find a reference in the docs for 9.3 and earlier.
You've already received some great answers. Just to add a bit, I think the key idea is that when you use CALL EXECUTE to invoke a macro, it will execute the macro. When the macro executes, it will generate any SAS code (data step code or whatever), resolve all the macro references, and put it on the input stack to be compiled and executed by the SAS data step compiler. When the macro executes, it will also execute any macro statements immediately. This causes the timing problem, because if the macro generates macro variables from data step code, the attempt to resolve the macro variables will be made before the macro variables have been generated.
If you use CALL EXECUTE to invoke a macro and hide the macro call in %NRSTR(), CALL EXECUTE will not actually execute the macro. Instead, it just generates the call to the macro and puts the macro call on on the input stack. The macro is executed after the data step with the call execute has completed. This lets the timing work out.
Here is slightly revised sample code, with a %PUT statement added
%macro testit();
data _null_;
call symputx('MyMvar','Test String','L');
stop;
run;
%put Macro %nrstr(%put) MyMVar: &MyMVar ;
data _null_;
put "Data step put MyMvar: &MyMvar";
stop;
run;
%mend;
If you run it like:
%symdel MyMvar ;
data _null_;
call execute('%testit()');
run;
The log shows that call execute generated the SAS code generated by %testit. So it executed %testit:
208 data _null_; 209 call execute('%testit()'); 210 run; WARNING: Apparent symbolic reference MYMVAR not resolved. Macro %put MyMVar: &MyMVar WARNING: Apparent symbolic reference MYMVAR not resolved. NOTE: CALL EXECUTE generated line. 1 + data _null_; call symputx('MyMvar','Test String','L'); stop; run; 1 + data _null_; put "Data step put MyMvar: &MyMvar"; stop; run; Data step put MyMvar: Test String
Warnings are thrown indicating that &MYMVAR could not resolve on the %PUT statement, or the first time it is seen on the PUT statement.
Also, note that even though you asked CALL SYMPUTX to make a local macro variable, it still made a global macro variable. Because when CALL SYMPUTX executed, there was no local scope.
If you run like:
%symdel MyMvar ;
data _null_;
call execute('%nrstr(%testit())');
run;
Call execute will generate the macro call, but won't actually execute the macro. The log shows:
292 data _null_; 293 call execute('%nrstr(%testit())'); 294 run; NOTE: CALL EXECUTE generated line. 1 + %testit() Macro %put MyMVar: Test String Data step put MyMvar: Test String
Note that call executed generated the call to %testit, but did not actually executed %testit. %Testit is executed after the data step with the call execute. So all the timing issues work out.
I always use %NRSTR() when invoking a macro via call execute. Not only does it make the macro timing work out, it also makes a cleaner log, where you can see the macro calls that were generated.
Patrick,
I think @FreelanceReinhar get the point ,add %nrstr() to suppress the warning .
Thank you all for your great answers.
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.