/*----- * group: Macro programming * purpose: Populate a macro array (N macro variables) from a delimited list of items */ %macro array ( array , items , sep = %str( ) , scope = RESOLVE , lets = , locals = ); %* Richard A. DeVenezia %* 031119 - Revised as [array], added scope, lets and locals %* 931109 - Initial coding as [makelist] %* %* create a macro array from a delimited list of items %* %* value is %* ---------------------------------------------------------------- %* array - macro array name, prefix of numbered macro variables that %* will contain the item values %* items - original list of items, separated by sep %* sep - separator between items of items (incoming) %* scope - scope of macro array variables. %* GLOBAL, INHERIT, RESOLVE %* GLOBAL - avoid if possible %* INHERIT - the invoker _must_ ensure the variables %* <&array>_size <&array>_1 ... <&array>_n exist %* prior to invoking %array. %* Why? because a macro is not allowed to create a macro variable %* in a local scope above its own. %* (It may however, access any macro variables in scope above itself) %* If this macro implicitly 'creates' a macro variable, it will %* be destroyed when the macro ends, and thus will not be available %* to invoker. %* RESOLVE - macro var named in lets will receive a quoted macro %* statement which is a series of %lets. The invoker is responsible %* for unquoting the statement to get macro vars in its scope. %* lets - name of macro var existing in invokers scope. %* upon return, the invoker should unquote the value %* to cause the macro array variables to be assigned. %* locals - name of macro var existing in invokers scope. %* upon return, the invoker should resolve this variable in a %* %local statement to ensure the variables in the lets variable %* will not accidently overwrite an existing macro variable in scope %* higher than invoker. (See examples at bottom) %*; %if (&array. =) %then %do; %put ERROR: array name is missing; %goto EndMacro; %end; %let scope = %upcase(&scope); %if 0 = %index (|GLOBAL|INHERIT|RESOLVE|, |&SCOPE.|) %then %do; %put ERROR: scope = &scope is unknown; %goto EndMacro; %end; %if (&scope = RESOLVE) and (&lets = ) %then %do; %put ERROR: scope=&scope requires an lets=; %goto EndMacro; %end; %let lets = %upcase(&lets); %if (&scope = RESOLVE) and (&lets = LETS) %then %do; %put ERROR: lets= can not be LETS, try lets=_let; %goto EndMacro; %end; %let locals = %upcase(&locals); %if (&locals=LOCALS) %then %do; %put ERROR: locals= can not be LOCALS, try locals=_local; %goto EndMacro; %end; %local item N local; %let N=1; %if (&scope = GLOBAL) %then %global &array._size; %if (&scope = RESOLVE) %then %let &lets = ; %if (&locals ^= ) %then %let &locals = &array._size; %let item = %scan(&items,&N,%quote(&sep)); %do %while (&item ^= ); %if &scope = GLOBAL %then %global &array.&N; %if (&scope = GLOBAL) or (&scope = INHERIT) %then %let &array.&N = &item; %else %let &lets = %nrquote(&&&lets)%nrstr(%let )&array.&N=&item%str(;); %if (&locals ^= ) %then %let &locals = &&&locals &array&N; %let N = %eval(&N + 1); %let item = %scan(&items,&N,%quote(&sep)); %end; %let N = %eval(&N - 1); %if (&scope = GLOBAL) or (&scope = INHERIT) %then %let &array._size = &N; %else %let &lets = %nrquote(&&&lets)%nrstr(%let )&array._size=&N%str(;); %EndMacro: %mend; /**html *

Sample code

*/ /* %array (bob, a b c d e, scope=GLOBAL) data _null_; do i = 1 to &bob_size; name = "BOB"||put(i,3.-L); value = symget (name); put i= name= value=; end; run; %* mis-application of INHERIT %* macro vars that will be the macro array should exist in invokers scope %* prior to using %array; %array (x, a b c d e, scope=INHERIT) %put &x_size; %* mis-application of INHERIT; %* dangerous because only some vars are available as GLOBAL, and others not %let y_size=; %let y1=; %let y3=; %array (y, a b c d e, scope=INHERIT) %put &y_size; %put &y1; %put &y2; %put &y3; %put &y4; %put &y5; %* mis-application of RESOLVE, init should exist prior to invocation; %array (z, a b c d e, scope=RESOLVE, lets=init); %put &lets; %* proper application of RESOLVE, init exists prior to invocation; %let lets=; %array (z, a b c d e, scope=RESOLVE, lets=init); %put &init; %unquote(&init) %put &z_size; %put &z1; %put &z2; %put &z3; %put &z4; %put &z5; %* proper application of RESOLVE, _let and _local exist prior to invocation; %macro foo; %local _let _local array; %let array=xyz; %array (&array, a b c d e, scope=RESOLVE, lets=_let, locals=_local); %local &_local; %unquote (&_let) %do i = 1 %to &&&array._size; %put &array&i=&&&array.&i; %end; %mend; %foo */