Date: Fri, 12 Mar 2004 18:46:18 -0500
Reply-To: Quentin McMullen <quentin_mcmullen@BROWN.EDU>
Sender: "SAS(r) Discussion" <SAS-L@LISTSERV.UGA.EDU>
From: Quentin McMullen <quentin_mcmullen@BROWN.EDU>
Subject: Re: tip macro Put_
On Fri, 12 Mar 2004 10:19:40 -0500, Fehd, Ronald J. (PHPPO) <rjf2@CDC.GOV>
wrote:
>I developed this module to write the parameter list
>of a parameterized %include program,
>either of routine/sub-routine/module
>
>testers wanted,
>commentary, critique (always) welcome
<snip>
Hi Ron,
Thanks for sharing this. In the macro context, there are two things I
might like from a macro %showscope():
1) A list of all variables local to the calling macro. (This is
equivalent to a list of macro parameters, if the call to %showscope is
early enough).
2) A list of all variables that are *available* to the calling macro (i.e.
those that are local or exist in an outer scope).
I've got a macro that comes close to what I want (with lots of ugly SQL
code that could no doubt be improved upon):
4817 options nomprint;
4818
4819 %*Ex 1 seems to work okay;
4820 %let a=1;
4821 %let z=0;
4822 %macro outer(x=,y=,z=);
4823 %showscope(macro=outer)
4824 %inner(x=9,y=9)
4825 %mend outer;
4826
4827 %macro inner(x=,y=);
4828 %showscope(macro=inner)
4829 %mend inner;
4830
4831 %outer(x=1,y=2,z=3)
NOTE: Parameters local to outer are: &X[SCOPE:OUTER]=1 &Y[SCOPE:OUTER]=2 &Z
[SCOPE:OUTER]=3
NOTE: Macro variabls available to outer are: &X[SCOPE:OUTER]=1 &Y
[SCOPE:OUTER]=2
&Z[SCOPE:OUTER]=3 &A[SCOPE:GLOBAL]=1
NOTE: Parameters local to inner are: &X[SCOPE:INNER]=9 &Y[SCOPE:INNER]=9
NOTE: Macro variabls available to inner are: &X[SCOPE:INNER]=9 &Y
[SCOPE:INNER]=9
&Z[SCOPE:OUTER]=3 &A[SCOPE:GLOBAL]=1
The logic for the list of "available" variables is tricky. As the Maven
knows well, when %showscope() runs inside inner, it will need to look
first to the symbol table of %INNER, then to the symbol table of %OUTER,
then to the global symbol table. It works fine in the above case with
only one level of macro nesting. However, if there are 2 levels of
nesting (i.e. %inner called by %outer called by %bigouter), I had
problems. %showscope knows it should check the symbol table for %inner
first, and the global table last, but it doesn't know the order for
searching tables from %outer and %bigouter. I gave up on this (for now :)
Below is my %showscope, certainly not ready for production. But not too
much wasted effort for a Friday late afternoon/evening....
%macro showscope
(macro= /*name of macro whos scope is checked*/
);
/**********************************************************************
Macro: showscope
Abstract: Build a list of all defined macro variables in current
scope (or outer scope). That is, all macro variables
that will resolve, excluding automatic macro vars.
Usage:
Example:
%macro inner(x=,y=);
%showscope(macro=inner)
%mend inner;
%inner(x=1,y=1)
Description:
Parameters: macro= name of macro whos scope is checked
Author: Quentin McMullen
Date: 03/12/2004
Category:
Keyword:
OS:
Notes: This ignores issue of offset for long macro variables.
**********************************************************************/
%local
mvars1 /*list of local macro vars*/
mvars2 /*list of all available macro vars (local or outer scope)*/
;
%*List of vars that are local to calling macro;
proc sql noprint;
select '%nrstr(&)'||trim(name)||'[SCOPE:'||trim(scope)||']='||
trim(value) into :mvars1 separated by " "
from dictionary.macros
where scope="%upcase(¯o)"
;
quit;
%*List of vars that are local to calling macro ;
%*or are available to calling macro cuz they exist in outer local ;
%*scope or global scope. If a var exists in both local scope and ;
%*an outer scope, we take value from the first local scope in which ;
%*it is found. So macro value comes from either: a) local symbol table ;
%*b) local table of calling macro, or c) global table. ;
%*Note when there are multiple nestings of calling macros, I cant tell ;
%*which local value to use. ;
proc sql noprint;
select '%nrstr(&)'||trim(name)||'[SCOPE:'||trim(scope)||']='||
trim(value) into :mvars2 separated by " "
from
( /*local vars*/
select name,value,scope
from dictionary.macros
where scope="%upcase(¯o)"
union corr all
/*vars local to outer scope,
vars local to more than one outer scope are ignored
*/
select name,value,scope
from dictionary.macros
where scope NOT IN ("GLOBAL" "AUTOMATIC" "SHOWSCOPE")
and name NOT IN
(select name
from dictionary.macros
where scope="%upcase(¯o)"
)
group by name
having count(*)=1 /*this is bad, ignores mvars defined in multiple
outer scopes*/
union corr all
/*global vars*/
select name,value,scope
from dictionary.macros
group by name
having count(*)=1 and scope="GLOBAL"
)
;
quit;
%put NOTE: Parameters local to ¯o are: &mvars1;
%put NOTE: Macro variabls available to ¯o are: &mvars2;
%mend showscope;
options nomprint;
%*Ex 1 seems to work okay;
%let a=1;
%let z=0;
%macro outer(x=,y=,z=);
%showscope(macro=outer)
%inner(x=9,y=9)
%mend outer;
%macro inner(x=,y=);
%showscope(macro=inner)
%mend inner;
%outer(x=1,y=2,z=3)
%*Ex 2 breaks when there are multiple outer scopes ;
%*macro var Z is available to inner, but I cant see how ;
%*to have %showscope() know to pull value from scope of %outer ;
%*rather than scope of %bigouter. ;
%macro bigouter(z=);
%outer(x=0,y=0,z=0)
%mend bigouter;
%bigouter(z=1)
Kind Regards,
--Quentin