BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.

I would like to understand how the macro processor looks up the global symbol table when resolving the second %put outside statement. If my understanding is correct, the first %let assigns a value of 1 in the global symbol table, however when forcing the same name as a local within a macro definition, what really happens? Does it create another macro variable with the same name in the local symbol table when a macro variable with the same name exists in the global symbol table? Any chance of overlap or ambiguity here? I would appreciate if somebody can explain the mechanism at length, please and thanks!!!!

 

Cheers,

Charlotte from England

 

%let variable=1;
%macro routine;
   %put ***** Beginning ROUTINE *****;
   %local variable;
   %let variable=2;
   %put The value of variable inside ROUTINE is &variable;
   %put ***** Ending ROUTINE *****;
%mend routine;
%routine
%put The value of variable outside ROUTINE is &variable;
Submitting these statements writes these lines to the SAS log:
***** Beginning ROUTINE *****
The value of variable inside ROUTINE is 2
***** Ending ROUTINE *****
The value of variable outside ROUTINE is 1
1 ACCEPTED SOLUTION

Accepted Solutions
Quentin
Super User

A request for an explanation of macro symbol tables at length, on a rainy Saturday morning??? Oh happy day.  : )

 

As you say, there is a global symbol table that holds macro variables.  The global symbol table is persistent for the entire SAS sesssion.  And a macro may create a local symbol table that also holds macro variables.  The local symbol table only exists while the macro is executing.  And yes, it is possible to have two different macro variables named VARIABLE, one in the global symbol table and one in the local symbol table.  Despite that, when the %LOCAL statement is used appropriately, there is no chance for ambiguity or collisions between the two symbol tables.

 

Consider this code:

%macro routine;
   %let variable=2;
   %put The value of variable inside ROUTINE is &variable;
%mend routine;

What happens when the macro is invoked?

 

 

When the %LET statement executes, it assigns a value of 2 to the a macro variable named VARIABLE.  The scoping rules for the %LET statement are it first looks to the local symbol table of the macro ROUTINE, to see if it already has a macro variable named VARIABLE.  If it does, it updates the value.  If not, it looks to the global symbol table to see if it has a macro variables named VARIABLE.  If it does, it updates the value.  If not, the %LET statement needs to create the macro variable, and it creates it in the local symbol table and assigns the value.

 

What happens when the macro variable reference &VARIABLE on the %PUT statement resolves?  The macro processor first looks to the local symbol table to look for a macro variable named VARIABLE, and it will provide the resolved value.  If there is no macro variable in the local symbol table VARIABLE, it will then look to the global symbol table for a macro variable named VARIABLE.

 

Consider this log exploring the results of a name collsion when the %LOCAL statement is not used:

 

41   %macro routine(dummy);
42      %put ***** Beginning ROUTINE *****;
43      %put The value of X at top of ROUTINE is &X;
44      %let X=2;
45      %put The value of X at bottom of ROUTINE is &X;
46      %put ***** Ending ROUTINE *****;
47   %mend routine;
48
49   %let x=1;
50   %put The global value of X before ROUTINE is &X;
The global value of X before ROUTINE is 1
51   %routine()
***** Beginning ROUTINE *****
The value of X at top of ROUTINE is 1
The value of X at bottom of ROUTINE is 2
***** Ending ROUTINE *****
52   %put The global value of X after ROUTINE is &X;
The global value of X after ROUTINE is 2

 

Here a global variable X is created.  When the macro executes, the first %PUT statement needs to resolve X.  It looks to see if a local macro variable named X exists, but it doesn't.  It looks to see if a global macro variable named X exist, and it does.  So it provides the resolved value 1.  When the %LET statement executes, it checks if there is a local vaiable named X, seeing none, it looks to the global symbol table variable X and updates the value in the global symbol table to be 2.  Then when the second %PUT statement executes, it also resolves the value from the global symbol table.  Tthe %PUT statement outside of the macro invocation resolves the value from the global symbol table.

 

Now, how can the %LOCAL statement help?  The %LOCAL statement does only one thing: it creates a macro variable in the local symbol table.  But given the scoping rules, this is all you need to avoid macro variable colllisions.  So consider:

53   %macro routine(dummy);
54      %put ***** Beginning ROUTINE *****;
55      %put The value of X at top of ROUTINE before local statement is &X;
56      %local X;
57      %put The value of X at top of ROUTINE after local statement is &X;
58      %let X=2;
59      %put The value of X at bottom of ROUTINE is &X;
60      %put ***** Ending ROUTINE *****;
61   %mend routine;
62
63   %let x=1;
64   %put The global value of X before ROUTINE is &X;
The global value of X before ROUTINE is 1
65   %routine()
***** Beginning ROUTINE *****
The value of X at top of ROUTINE before local statement is 1
The value of X at top of ROUTINE after local statement is
The value of X at bottom of ROUTINE is 2
***** Ending ROUTINE *****
66   %put The global value of X after ROUTINE is &X;
The global value of X after ROUTINE is 1

 

Here, the first  %PUT statement  in the macro resolves X to 1.  Because there is no local macro variable named X, the global macro variable is returned.

 

The %LOCAL statement then creates a local macro variable named X.

 

The %PUT statement after the local statement then resolves X to a null value.  This is because it is resolving the macro variable X from the local symbol table, and that macro variable has not been given a value.

 

When the %LET statement executes, it looks to see if a macro variable named X exists in the local symbol table, and it does.  So the %LET statement updates the value of the local variable X and sets the value to 2, it does NOT collide with the macro variable X in the global symbol table. 

 

When the %PUT statement at the bottom of the macro executes, it resolves the value of X from the local symbol table.

 

When the %PUT statement outside of the macro executes, it resolves the value of X from the global symbol table.

 

 

The simple rule to avoid collisions between local macro variables and global macro variables is to list all macro variables created (or referenced) in a macro on a %LOCAL statement at the top of a macro definition.  Because of the scoping rules for macro variable assignments and resolution, this will ensure that all macro variables are written to and read from the local symbol table, even if there happens to be a macro variable in the global symbol table with the same name.

 

Hope that helps!

BASUG is hosting free webinars Next up: Jane Eslinger presenting PROC REPORT and the ODS EXCEL destination on Mar 27 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.

View solution in original post

4 REPLIES 4
FreelanceReinh
Jade | Level 19

Hello Charlotte,

 

Your understanding is correct. The %LOCAL statement creates a new macro variable VARIABLE in the local symbol table of macro ROUTINE, regardless of whether a macro variable of the same name exists somewhere else (i.e. in another symbol table). When the subsequent %LET statement is executed, the local symbol table of macro ROUTINE is searched first (because the %LET statement resides in this macro). Thanks to the preceding %LOCAL statement the macro processor finds the name VARIABLE in this table and therefore changes the value of VARIABLE (currently a null value) to 2. Similarly, the macro variable reference &variable in the second %PUT statement is resolved to 2, because VARIABLE is found in the local symbol table of macro ROUTINE, which is searched first for a value.

 

After macro ROUTINE has finished executing, its local symbol table is destroyed. The macro variable reference &variable in the last %PUT statement is resolved to 1, based on the global symbol table, which has not been touched during macro execution. (It could have been touched and the value of the global macro variable VARIABLE would have changed, if you had omitted the %LOCAL statement.)

 

The explanations and the diagram in How Macro Variables Are Assigned and Resolved describe the process in general.

Quentin
Super User

A request for an explanation of macro symbol tables at length, on a rainy Saturday morning??? Oh happy day.  : )

 

As you say, there is a global symbol table that holds macro variables.  The global symbol table is persistent for the entire SAS sesssion.  And a macro may create a local symbol table that also holds macro variables.  The local symbol table only exists while the macro is executing.  And yes, it is possible to have two different macro variables named VARIABLE, one in the global symbol table and one in the local symbol table.  Despite that, when the %LOCAL statement is used appropriately, there is no chance for ambiguity or collisions between the two symbol tables.

 

Consider this code:

%macro routine;
   %let variable=2;
   %put The value of variable inside ROUTINE is &variable;
%mend routine;

What happens when the macro is invoked?

 

 

When the %LET statement executes, it assigns a value of 2 to the a macro variable named VARIABLE.  The scoping rules for the %LET statement are it first looks to the local symbol table of the macro ROUTINE, to see if it already has a macro variable named VARIABLE.  If it does, it updates the value.  If not, it looks to the global symbol table to see if it has a macro variables named VARIABLE.  If it does, it updates the value.  If not, the %LET statement needs to create the macro variable, and it creates it in the local symbol table and assigns the value.

 

What happens when the macro variable reference &VARIABLE on the %PUT statement resolves?  The macro processor first looks to the local symbol table to look for a macro variable named VARIABLE, and it will provide the resolved value.  If there is no macro variable in the local symbol table VARIABLE, it will then look to the global symbol table for a macro variable named VARIABLE.

 

Consider this log exploring the results of a name collsion when the %LOCAL statement is not used:

 

41   %macro routine(dummy);
42      %put ***** Beginning ROUTINE *****;
43      %put The value of X at top of ROUTINE is &X;
44      %let X=2;
45      %put The value of X at bottom of ROUTINE is &X;
46      %put ***** Ending ROUTINE *****;
47   %mend routine;
48
49   %let x=1;
50   %put The global value of X before ROUTINE is &X;
The global value of X before ROUTINE is 1
51   %routine()
***** Beginning ROUTINE *****
The value of X at top of ROUTINE is 1
The value of X at bottom of ROUTINE is 2
***** Ending ROUTINE *****
52   %put The global value of X after ROUTINE is &X;
The global value of X after ROUTINE is 2

 

Here a global variable X is created.  When the macro executes, the first %PUT statement needs to resolve X.  It looks to see if a local macro variable named X exists, but it doesn't.  It looks to see if a global macro variable named X exist, and it does.  So it provides the resolved value 1.  When the %LET statement executes, it checks if there is a local vaiable named X, seeing none, it looks to the global symbol table variable X and updates the value in the global symbol table to be 2.  Then when the second %PUT statement executes, it also resolves the value from the global symbol table.  Tthe %PUT statement outside of the macro invocation resolves the value from the global symbol table.

 

Now, how can the %LOCAL statement help?  The %LOCAL statement does only one thing: it creates a macro variable in the local symbol table.  But given the scoping rules, this is all you need to avoid macro variable colllisions.  So consider:

53   %macro routine(dummy);
54      %put ***** Beginning ROUTINE *****;
55      %put The value of X at top of ROUTINE before local statement is &X;
56      %local X;
57      %put The value of X at top of ROUTINE after local statement is &X;
58      %let X=2;
59      %put The value of X at bottom of ROUTINE is &X;
60      %put ***** Ending ROUTINE *****;
61   %mend routine;
62
63   %let x=1;
64   %put The global value of X before ROUTINE is &X;
The global value of X before ROUTINE is 1
65   %routine()
***** Beginning ROUTINE *****
The value of X at top of ROUTINE before local statement is 1
The value of X at top of ROUTINE after local statement is
The value of X at bottom of ROUTINE is 2
***** Ending ROUTINE *****
66   %put The global value of X after ROUTINE is &X;
The global value of X after ROUTINE is 1

 

Here, the first  %PUT statement  in the macro resolves X to 1.  Because there is no local macro variable named X, the global macro variable is returned.

 

The %LOCAL statement then creates a local macro variable named X.

 

The %PUT statement after the local statement then resolves X to a null value.  This is because it is resolving the macro variable X from the local symbol table, and that macro variable has not been given a value.

 

When the %LET statement executes, it looks to see if a macro variable named X exists in the local symbol table, and it does.  So the %LET statement updates the value of the local variable X and sets the value to 2, it does NOT collide with the macro variable X in the global symbol table. 

 

When the %PUT statement at the bottom of the macro executes, it resolves the value of X from the local symbol table.

 

When the %PUT statement outside of the macro executes, it resolves the value of X from the global symbol table.

 

 

The simple rule to avoid collisions between local macro variables and global macro variables is to list all macro variables created (or referenced) in a macro on a %LOCAL statement at the top of a macro definition.  Because of the scoping rules for macro variable assignments and resolution, this will ensure that all macro variables are written to and read from the local symbol table, even if there happens to be a macro variable in the global symbol table with the same name.

 

Hope that helps!

BASUG is hosting free webinars Next up: Jane Eslinger presenting PROC REPORT and the ODS EXCEL destination on Mar 27 at noon ET. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
CharlotteCain
Quartz | Level 8

Thank you @Quentin  @Astounding  @FreelanceReinh  for making me clearly understand the concept and sorry for the bother on a saturday(haha). Well, macro programming is very intriguing as it seems to me a layer on top the SAS codes that generates extra texts, resolution of macro references, and the passage of macro compilation/compilation to SAS compiler is a beauty tale. I am loving it. Thanks once again!

 

Regards,

Charlotte

PS There is another concept(indirect references) that makes my brain crazy. I will post that as a new question later Smiley Happy

 

Astounding
PROC Star

Charlotte,

 

The explanations you have so far are good, and complete.  I just wanted to add one more way of looking at this.

 

What madman invented such a scheme?  Why even have local symbol tables?

 

This sort of scheme is required to allow multiple programmers to write macros independently of one another.  If you write a macro and want to be able to (1) call other macros from your macro, or (2) allow other macros to call your macro, you need a way that a called macro won't change the values of macro variables in the calling macro.  Now macro programmers aren't clairvoyant enough to know which macros will be calling the macros that they write.  So they can't know which macro variable names to avoid because the same name might be in use by another macro.  Allowing the creation of local symbol tables (plus the assumption of good programming practices that were already mentioned) lets macro programmers write their macros without knowing how they are going to be called.

sas-innovate-2024.png

Join us for SAS Innovate April 16-19 at the Aria in Las Vegas. Bring the team and save big with our group pricing for a limited time only.

Pre-conference courses and tutorials are filling up fast and are always a sellout. Register today to reserve your seat.

 

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
  • 828 views
  • 3 likes
  • 4 in conversation