Hi,
I'm working with sas 9.04.01M1P120413
I have some macros that create code lines.
The goal is to enter a few parameters and the macro creates, for example, a bunch of "index(var"item1") or index(var"item2")" etc.
However, some of these computed lines can get quite long in the log.
The goal would be to split them on multiple log lines.
I have an example macro and code below.
The line created by the macro in the log looks like this:
MPRINT(MYMAC): titi=1 or titi=2 or titi=3
Instead I would like to see something like this:
MPRINT(MYMAC): titi=1
MPRINT(MYMAC): or titi=2
MPRINT(MYMAC): or titi=3
Sort of like the one not created by the macro:
74 if titi=1
75 or titi=2
76 or titi=3
77 then tata=1;
Is there any way to achieve that?
Thanks!
Example code:
%macro mymac(var,val);
%let i=1;
%do %while(%scan(&val,&i,%str( ))^= );
%let item=%scan(&val,&i,%str( ));
%if &i=1 %then %do; &var=&item %end;
%else %do; or &var=&item %end;
%let i=%eval(&i+1);
%end;
%mend mymac;
data titi;
titi=1;
/* ends on a single line in the log */
if %mymac(titi,1 2 3) then toto=1;
/* ends on multiple lines in the log */
if titi=1
or titi=2
or titi=3
then tata=1;
run;
Note that MPRINT() will break the lines when the code is generated by a different macro.
So you could make a convoluted program that uses that fact to break up the MPRINT lines.
For example by using recursion.
%macro test(varname,list);
%local words i sep;
%let words=%sysfunc(countw(&list,%str( )));
%if &words=1 %then &varname=&list ;
%else %do i=1 %to &words ;
&sep %test(&varname,%scan(&list,&i,%str( )))
%let sep=or;
%end;
%mend test;
83 options mprint; 84 data x; 85 if %test(x,1 2 3) then z=2; MPRINT(TEST): x=1 MPRINT(TEST): or MPRINT(TEST): x=2 MPRINT(TEST): or MPRINT(TEST): x=3 86 run;
No.
If that is important to you then instead of using macro logic to generate the code you could write the code to a file and then %include the file.
Hmm... I'll see if I can manage to do that.
Thanks! 😃
You might consider instead of generating
Sort of like the one not created by the macro: 74 if titi=1 75 or titi=2 76 or titi=3
that you generate
if titi in (1 2 3)
which would likely completely remove any need for this macro as that statement is easier to put in the code than the macro call.
I suppose my choice of example is rather poor.
The macros I have to deal with use multiple index or tranwrd or stuff like that.
The example I chose is just a quick thing I can try various options with.
I apologize if I wasn't clear enough.
@tristevoix wrote:
I suppose my choice of example is rather poor.
The macros I have to deal with use multiple index or tranwrd or stuff like that.
The example I chose is just a quick thing I can try various options with.
I apologize if I wasn't clear enough.
The results of the macro processor are helpful but creating "pretty output" is not any where in the plan. If you want pretty code use @Tom's suggestion of writing to a text program file. That also has the added bonus of persisting from session to session for examination or minor modifications that you may need for that last odd-ball case into code that is problematic in macro code.
I'm trying that at the moment, but I'm having a hard time finding a way to create the file and then include it from the macro itself.
The macros can be used in both data steps and proc sql and that's something I'd like to keep available.
And I only know about file/put in a data step to write stuff to files.
Sounds like your macro might be generating only PART of a statement. What is called a "macro function".
If your macro is currently generating one or more full steps (proc or data) then you could convert it to generate code instead (with significant changes.)
Say you had this trivial macro.
%macro example;
data x;
set y;
if x=1
or x=2
or x=3
;
run;
%mend ;
You could recode it to generate a data step that writes SAS code and %INCLUDE it.
%macro example;
filename code temp;
data _null_;
file code ;
put 'if x=1'
/ 'or x=2'
/ 'or x=3'
/ ';'
;
run;
data x;
set y;
%include code / source2 ;
run;
%mend ;
Then yes, it's a macro function.
I've still lots to learn about the proper terminology. 😉
So, looks like I can't use the file technique, then.
And there seems to be no other way to achieve what I want.
Therefore if I can't solve my initial issue at the root then I must find a way to solve it from the log lines.
Thanks for you time, gentlemen. 😃
Note that MPRINT() will break the lines when the code is generated by a different macro.
So you could make a convoluted program that uses that fact to break up the MPRINT lines.
For example by using recursion.
%macro test(varname,list);
%local words i sep;
%let words=%sysfunc(countw(&list,%str( )));
%if &words=1 %then &varname=&list ;
%else %do i=1 %to &words ;
&sep %test(&varname,%scan(&list,&i,%str( )))
%let sep=or;
%end;
%mend test;
83 options mprint; 84 data x; 85 if %test(x,1 2 3) then z=2; MPRINT(TEST): x=1 MPRINT(TEST): or MPRINT(TEST): x=2 MPRINT(TEST): or MPRINT(TEST): x=3 86 run;
Though I did manage to find a way to solve the issue from the log lines, I very much prefer to avoid the issue altogether.
And this does just that.
I'm happy to see there's always someone more resourceful than I am and I'm even more happy when they agree to share that resourcefulness.
Thank you! =D
Glad you found a solution, and Tom's solution is pretty nifty! I would just encourage you to think about the trade-offs involved in implementing it. That is, programming is in large part about managing complexity. I understand that you want a more easily human-readable log file, which is a good thing. Tom's solution adds a significant amount of complexity (recursive macro : ) to the logic, and arguably makes the code itself more difficult to read.
So it's worth considering the judgement call: whether the benefit of a log with MPRINT line breaks is worth the added complexity.
A good suggestion indeed. 😃
Maybe I should explain my reasoning a bit more.
My task is to create a macro that can extract the code from the log, but in such a way that all macro variables used in the code are resolved (symbolgen lines) and all macro calls are replaced with the code lines (mprint lines). The goal is to produce a code the client can run without having access to the macros and such.
I managed to achieve that with the first program with which I was working. However, the second program was having the split mprint lines and the issue with such lines is that unlike standard code lines they don't start with a ! and stuff.
I had to find a way to put back together those split lines which I eventually did. However, it seems safer to me to not have to sew lines back together in the first place.
The macro I wish to update isn't itself overly complex to begin with.
Therefore, the added complexity seems acceptable if in the end I can more reliably put the program back together from the log.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.