1
votes

Good day,

I ran into following situation where macro variables are defined as global depending if they are from include or not.... Variables defined in macros should not be visible outside of the macro, right. This fails:

%macro if_env;
    %let a=100;
%mend if_env;
%if_env;
%put &a.;

WARNING: Apparent symbolic reference A not resolved.

This is as expected. However, I ran into an issue, where the variable bleeds to global space:

I have an include file: C:\TEMP\test.sas, which contains only setting for variables a b c d: (For testing purposes)

%let a=100;
%let B=200;
%let C=300;
%let D=400;

This works also as intended:

%macro if_env;
    %include"C:\TEMP\test.sas";
%mend if_env;
%if_env;
%put &B.;

WARNING: Apparent symbolic reference B not resolved.

All good so far. Now, I added if clause 'include if file exists'-condition:

%macro if_env;
   %if %sysfunc(fileexist(C:\TEMP\test.sas)) %then
   %let C=100
%mend if_env;
%if_env;
%put &C.;

WARNING: Apparent symbolic reference C not resolved.

Last step: Add the actual include to the code:

%macro if_env;
   %if %sysfunc(fileexist(C:\TEMP\test.sas)) %then
   %include"C:\TEMP\test.sas";
%mend if_env;
%if_env;
%put &A.;
%put &B.;
%put &C.;
%put &D.;

100 200 300 400

Umm, not computing. This should not happen. How could %let-command have different namespace depending if it is in an include or not?

Any idea why this happens as it does? Bug or really fancy feature?

Edit: Very interesting. Based on SAS documentation one should use semicolon at the end of include. Glad to have clarification on the matter. Thanks for the answers.

2
I knew that %include stores the macro variables to the global environment. Have you tried out @Robert suggestion?samkart
@samkart Umm? Example B seems to contradict this. I'll try to get Robert's solution with more sleep under the belt.pinegulf

2 Answers

4
votes

The %include statement is terminated by a semi-colon.

The %if statement is terminated by a semi-colon.

The semi-colon in the macro terminates the %if in the macro.

  • When the %if evaluates true the %include statement is begun but is terminated by the semi-colon just past the invocation. Thus the %include statement execution scope is the same as the %put statements.
  • When the %if` evaluates false the semi-colon after the invocation is simply a null statement, or stand-alone semi-colon.

This kind of macro programming subtlety is often overlooked.

To force the %include to occur in the macro scope use

... %then %do; %include ...; %end;

or

... %then %include ... %str(;) ;
3
votes

I wouldn't call it a bug in SAS, but rather a bug in your program. Since your macro is not supplying a semi-colon to terminated the %INCLUDE statement the macro ends before the %include statement runs. So the local symbol space no longer exists and the %let statements create global macros.

If you format the code so that SAS code and macro code are on different lines it is clearer.

%if 1=1 %then 
  %include test
;

Adding %do/%end will allow you to provide the semi-colon for the %include.

%if 1=1 %then %do;
  %include test ;
%end;

If you call this macro inside of another macro then the macro variables will be created as local to that calling macro. So this is a trick that can be used to push macro variables into the parent environment.

%macro if_env;
  %include test
%mend if_env;
%macro outer ;
 %if_env;
 %put _local_;
%mend outer;

Turn on MLOGIC option to see what is happening.

235  options mlogic source2;
236  %outer ;
MLOGIC(OUTER):  Beginning execution.
MLOGIC(IF_ENV):  Beginning execution.
MLOGIC(IF_ENV):  Ending execution.
NOTE: %INCLUDE (level 1) file TEST is file /.../#LN00048.
237 +%let a=1;
238 +%let b=2;
239 +%let c=3;
NOTE: %INCLUDE (level 1) ending.
MLOGIC(OUTER):  %PUT _local_
OUTER A 1
OUTER B 2
OUTER C 3
MLOGIC(OUTER):  Ending execution.