views:

90

answers:

2

Hi,

I am getting a resolution error with some SAS v9.1.3 code.

Here is some code I want to store in a .txt file (called problem2.txt) and bring into SAS with a %INC

%macro email020;                  
   %if &email = 1 %then %do;       
     %put THIS RESOLVED AT 1;      
   %end;                           
   %else %if &email = 2 %then %do; 
     %put THIS RESOVLED AT 2;      
   %end;                           
   %put _user_;                    
%mend email020;                   

%email020; 

Then this is the main code:

filename problem2 'C:\Documents and Settings\Mark\My Documents\problem2.txt';

%macro report1;                            
  %let email = 1;
  %inc problem2;
%mend report1;                             

%macro report2 (inc);                            
  %let email = 2;                          
  %inc problem2;
%mend report2;                             

data test;                                 
  run = 'YES';                             
run;                                       

data _null_;                               
  set test; 
  call execute("%report1");  
  call execute("%report2");  
run;

The log shows:

NOTE: CALL EXECUTE generated line.
1   +  %inc problem2;
MLOGIC(EMAIL020):  Beginning execution.

WARNING: Apparent symbolic reference EMAIL not resolved.

ERROR: A character operand was found in the %EVAL function or %IF condition where a numeric operand is required. The condition was: &email = 1

ERROR: The macro EMAIL020 will stop executing.

MLOGIC(EMAIL020):  Ending execution.

So the question is why does CALL EXECUTE generate %inc problem2 rather than %report1, causing SAS to miss the assignment and what can I do about it?

+2  A: 

It seems to be a macro varible scope issue. Try:

%macro report1;   
  %global email; 
  %let email = 1;
  %inc problem2;
%mend report1;                             

%macro report2;            
%global email; 
  %let email = 2;                          
  %inc problem2;
%mend report2;                             

However, I think it would be better to pass email as a parameter to %email020 rather than using global macro variables. Also, I'd avoid using nested macro definitions.

To get more data on macro variable scope, you could query the dictionary.macros view during macro execution. You can get the description of dictionary.macros with

proc sql;
    describe table dictionary.macros;
quit;
Ville Koskinen
Hi Ville - thanks for that response. You are definitely right about the scope issue. What I guess I can't understand is why the "normal" method of variable resolution doesn't appear to be happening. By "normal" I mean if an inner macro can find no reference to a macro variable on its local symbol table it automatically searches the outer symbol tables to see if it is assigned somewhere else, moving up to the global symbol table (as I'm sure you know). As EMAIL is assigned in the outer macro I'd think when it isn't found as the inner macro executes it should be found when it steps up a level
Mark
If the code in the include file is copied into %report1 and %report2 there doesn't seem to be a scope issue as both work. Scope only seems to become an issue when when using %inc and I think, in true techie fashion, we'd like to know why becauase we can't figure it out!
Mark
Mark, I updated the answer. Some of the macro stuff in SAS is scary and personally I've given up trying to fully understand everything :)
Ville Koskinen
+1  A: 

%include is not a macro call but sort of a compiler directive to include code from an outside file. When the macro %report1 is compiled, there is no macro variable email (because the macro never ran before), thus the reference remains as it is, &email. Then the implicit %eval() sees &email = 1 and complains because it looks like you are comparing a text(&email) against a number (1).

Introducing the %global is to be avoided if possible. I would do away with %include completely. much simpler, below is. :-)

%macro doSomething(email=);                  
  %if &email = [email protected] %then %do;       
    %put THIS RESOLVED AT 1;      
  %end; %else %if &email = [email protected] %then %do; 
    %put THIS RESOVLED AT 2;      
  %end;                           
  %put _user_;                    
%mend doSomething;                   


data emails;
  email="[email protected]"; output;
  email="[email protected]"; output;
run;

data _null_;
  set emails;
  call execute(catx(email, '%doSomething(email=', ')'));
run;

/* on log
THIS RESOLVED AT 1
DOSOMETHING EMAIL [email protected]
THIS RESOVLED AT 2
DOSOMETHING EMAIL [email protected]
*/
Chang Chung
Chang Chung, thanks so much for your response. My apologies for delaying in responding: I have been in bed with swine flu for the last 2.5 weeks. 'Preciate your feedback and will add your thoughts to the code here. Cheers, Mark
Mark