Resolving SAS MACRO parameters - macros

I am looking for a way to resolve the parameters of an already defined SAS MACRO. I know where to locate session defined macros (WORK.SASMACR/WORK.SASMAC1), but now I want to know go a step further and determine its parameters. So for example:
%macro test(val1, val2);
%put &val1 &val2;
%mend test;
/* With proc catalog I can find the defined macro... */
proc catalog cat=work.SASMAC1;
contents out=macros;
run;
/* How to create function/macro which takes the macro name and returns its arguments, eg:*/
%resolveMacroParams(test);
/* ...returns: */
val1 val2
Any help would be greatly appreciated!
Kind Regards,
Herman

You can do this if the macro was compiled with the SOURCE option. The following gives you a dataset with the macro text from which you could trivially obtain the arguments:
libname dummy 'c:\temp\';
options mstored sasmstore=dummy;
%macro test(val1, val2)/store source;
%put &val1 &val2;
%mend test;
filename mymacr temp;
%copy TEST/lib=dummy source out=mymacr;
data test;
infile mymacr truncover;
input #1 line $100.;
run;

You want something like values supplied as parameters to a function at its last call?
The definition of macro doesn't store it's values. Macro params are in local scope of that macro and their values are not anywhere AFTER the macro ends.
During execution you can find it in dictionary.macros:
So to keep it you'd have to change your macro like this:
%macro test(val1, val2);
proc sql;
create table TEST_VALS as
select * from dictionary.macros
where scope='TEST'
;
quit;
%mend test;
%test(11, 22);
or put those values into some global macro variables.

Related

Proc SQL macro variable not resolved

I wrote a proc SQl normally first without any macro variables and it works, now I would like to convert it to macro and it doesnt work, can you see where the problem is ?
%macro macrova(LIB=, DATA=);
proc sql noprint;
/*creating an in memory variable with all char variables from the dataset*/
select name into :names separated by ' '
from dictionary.columns where libname="&lib" and memname="&data" and type='char';
/*to make sure we have the same order of the variables an in-memory variable ORDER is created*/
SELECT NAME INTO: ORDER SEPARATED BY ' '
from sashelp.vcolumn where LIBNAME= "&lib" and memname="&datA" ;
quit;
%MEND;
OPTIONS MLOGIC SYMBOLGEN;
%macrova(LIB=SASHELP,DATA=CLASS)
%PUT &NAMES;
%PUT ℴ
LOG:
55 %PUT &NAMES;
WARNING: Apparent symbolic reference NAMES not resolved.
&NAMES
56 %PUT ℴ
WARNING: Apparent symbolic reference ORDER not resolved.
&ORDER
You need to either define the macro variables before calling the macro or add %GLOBAL statement to the macro. Otherwise the macro variabless will be created as local and disappear when the macro exits.
Note that there is no variable named ORDER in dictionary.columns, I assume you meant to use the VARNUM variable.
Also there is no need to run two queries to generate two macro variables.
%macro macrova(LIB=, DATA=);
%global names order ;
proc sql noprint;
select name
, varnum
into :names separated by ' '
, :order separated by ' '
from dictionary.columns
where libname=%upcase("&lib")
and memname=%upcase("&data")
and type='char'
order by 2
;
quit;
%mend macrova;
%macrova(LIB=SASHELP,DATA=CLASS)
%put &NAMES;
%put ℴ
The names are being created as LOCAL values inside the macro and are not available outside it. Try adding
%global NAMES ORDER;
to the macro before the SELECT statements.

SAS MACRO Quoting issue : passing string with macro trigger as macro parameter

Is this possible to pass a string with macro trigger as the macro parameter?
Please see the example code below:
options mprint;
%let string5='%abc%def%';
%macro test(string);
data _null_;
call execute('%test2('||&string.||')');
run;
%mend;
%macro test2(string2);
data test3;
a=%str(%')&string2.%str(%');
run;
%mend;
%test(&string5);
This code ran successfully but it tried to invoke the macro %abc and %def, which resulted in warnings.
If I tried to put it into quoting to mask the string, it gave syntax error, as shown below:
options mprint;
%let string5='%abc%def%';
%macro test(string);
data _null_;
call execute('%test2('||%superQ(string)||')');
run;
%mend;
%macro test2(string2);
data test3;
a=%str(%')&string2.%str(%');
run;
%mend;
%test(&string5);
ERROR 22-322: Syntax error, expecting one of the following: a name, a quoted string, a numeric constant, a datetime constant, a missing value, arrayname, (, +, -, INPUT, NOT, PUT, ^, _NEW_, ~.
Is there a way to fix this without warnings?
Thanks in advance!
Try this:
%let string5='%abc%def%';
%macro test(string);
data _null_;
call execute('%test2('||%nrstr("&string.")||')');
run;
%mend;
%macro test2(string2);
data test3;
a=%nrquote(&string2.);
run;
%mend;
%test(&string5);
It is usually simple enough in the macro to protect against special characters. For example you can use the %superq() function to quote an existing macro variables value.
where name like %unquote(%str(%')%superq(parm1)%str(%'))
Or use the symget() function in a data step to get the value without needing to expand the macro at all.
pattern = quote(symget('parm1'),"'");
But the hard part is making the macro call. You need to protect the characters to get the macro call to run. You can use similar functions in the macro call.
One useful thing to do is to instruct users to pass the parameter value as a quoted string and then the macro code you can remove the quotes when they are not needed.
%macro mymacro(parm1=);
%let parm1=%qsysfunc(dequote(&parm1));
...
%mend;
%mymacro(parm1='fred%')
Or you could ask them to pass the value by name.
%macro mymacro(mvar=);
%local pattern ;
%let pattern=%superq(&mvar);
...
%mend ;
%let my_pattern=%qsysfunc(dequote('fred%'));
%mymacro(mvar=my_pattern)
If this is already working the way you want and you just want to suppress this one warning message, you could consider setting option nomerror; before you run the section of code in question and then setting it back again afterwards.
Just update the final solution I choose. I gave up the way to pass macro parameters between different macros, instead I passed the macro variable name as a string. Sample codes are as below:
options mprint;
%let string5='%abc%def%';
%macro test(string);
data _null_;
call execute('%test2('||&string.||')');
run;
%mend;
%macro test2(string2);
data test3;
a=&&&string2.;
run;
%mend;
%test('string5');

Passing SAS dataset column as macro parameter

I have a SAS dataset with values
yyyymm
201605
201606
201607
201608
201609
I am trying to find a way to pass these values one at a time to macro such that
do while dataset still has value
%macro passdata(yyyymm);
end
How can I do this in SAS. Can someone please help with a sample/code snippet.
As mentioned by the prior comment, a way to pass parameters is through call execute routine. Note that this must be done in datastep environment. The lines are read from the set you input.
You can input multiple variables. Just add more variables in '||' separators. Note that the variables may have a lot of whitespaces in them. (==Do comparisons with care.)
Here is a small sample code. Tested.
data start_data;
input date_var ;
datalines;
201605
201606
201607
201608
201609
;
run;
%macro Do_stuff(input_var);
%put 'Line generates value ' &input_var;
%mend do_stuff;
data _null_;
set start_data;
call execute('%do_stuff('||date_var||')' );
run;
Try this example and try modifying to meet your needs... From the "source" dataset we can use call symput() to assign a macro token to each observation (differentiated by the SAS automatic dataset variable n so My_token1, My_token2, etc.) Once you have a set of macro variables defined, just loop through them! This program will print all the individual records from source to the SAS log:
data source;
do var=1000 to 1010;
output;
end;
run;
data _null_;
set source;
call symput(compress("My_token"||_n_),var);
run;
%put &my_token1 &my_token4;
%Macro neat;
%do this=1 %to 11;
*Check the log.;
%put &&My_token&this;
%end;
%mend;
%neat;

SAS Macro operations

I need to create sum of 4 variables multiple times each time with new set of variables. For e.g. A1=sum(a1,a2,a3,a4),B1=sum(b1,b2,b3,b4) & so on. So , I am trying to write a macro that will help me do it easily. Following is the code:
%macro SUM2(VAR1,var2,var3,VAR4);
data Subs_60_new;
set Subs_60;
substr(&var1,1,10)=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4);
I am using SAS EG for the same & when I run the macro I get the following note:
NOTE: Writing TAGSETS.SASREPORT13(EGSR) Body file: EGSR
& obviously when I try to execute the macro it throws an error.
Can some one help me out?
when calling a macro, you need to precede the macro name with a % symbol, eg as follows:
%macro SUM2(VAR1,var2,var3,VAR4);
data Subs_60_new;
set Subs_60;
substr(&var1,1,10)=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;
%sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4);
The NOTE is harmless. It is ERRORs and WARNINGs in general that you should be concerned with.
I'd point out that this will probably still throw an error, as you are trying to replace characters in a variable (&var1) that appears as though it should contain a numeric field (being part of a sum function). Given your description of what you are trying to achieve, I'd suggest adding the new variable name as another macro parameter - as follows:
%macro SUM2(VAR1,var2,var3,VAR4,varname);
data Subs_60_new;
set Subs_60;
&varname=sum(&var1,&var2,&var3,&var4);
run;
%mend sum2;
options mprint mlogic;
%sum2(ADDITIONAL_INFO_Q1,ADDITIONAL_INFO_Q2
,ADDITIONAL_INFO_Q3,ADDITIONAL_INFO_Q4
,MyNewVariable);

Compare macro value with dataset and goto next level

I am having a dataset which one variable is date. I passed some value to macro variable 'frodate'. Now i want if value is exist in dataset variable date then goto to a label.
but my program does not perform match. It is show condition is false,but value is in the dataset. Please help to do the same.
DATA _NULL_ ;
SET STOCK.NIFTY_IDX;
%IF &FRODATE = DATE %THEN %GOTO NEXT ;
RUN;
I don't understand what mistake i am doing. I know this is a very small problem, but i can't understand the same.
You are confusing macro language (%if) with sas program syntax (if). SAS Macro language is used to write sas programs - and so all macro statements are essentially resolved BEFORE the sas program runs. Data steps cannot (easily) drive macro logic, during that data step.
Also, macro language statements (such as %if) must be wrapped in a macro (%macro x; %mend;), which I assume is the case if you are successfully resolving FALSE. This is expected, unless your "&FRODATE" macro variable actually contains the character string "DATE" (macro only works with text, and it cannot easily read dataset variables).
Anyway, my interpretation of your question leads me to think that you need logic as follows:
/* create a sas macro as we are using macro logic */
%macro some_macro(frodate);
/* first run datastep and convert the dataset variable into a macro variable */
data _null_;
set stock.nifty_idx;
call symputx('Nifty_dt',date);
stop;
run;
/* the above is parsed and the macro processor receives a value for NIFTY_DT */
%if &frodate=&nifty_dt %then %goto next; /* if a match, go to the "label" */
%else %return; /* else exit the macro or some other logic */
%next: /* this is the "label", note the colon - not a semicolon */
%put success!;
%mend;
/* now RUN the macro to execute the logic.. */
%some_macro;
I understand my error and resolved the same. I do it in different manner.
%LET TEMDATE=%SYSFUNC(PUTN(&FDATE,DATE9.));
proc sql noprint;
select left(put(count(*),8.)) into :EXIST
from STOCK.NIFTY_&IDXFUT
where date = "&TEMDATE"d;
quit;
%IF &EXIST = 0 %THEN %DO......
Thanks for your valuable answer..