BookmarkSubscribeRSS Feed
KevinViel
Pyrite | Level 9

I attempted to modularize several macros, one of which calls the REPORT procedure.  The default call using %DO loop in the COLUMN statement:

 

%do __i = 1 %to &num_of_cols. ;
   col&__i.
%end ;

A more complex call might intend the following:

( "Group 1"
  %do __i = 1 %to %eval( &num_of_cols. - 1 ) ;
        col__&i.
  %end ;
)
col__&i.

Since the first macro generates NUM_OF_COLS (dynamically), I want to pass that value from the calling program, through the first macro, and have it resolve in the second macro, which is called by the first.

 

I have tried a variety of %str(%%)do and %str(%%)str(%%) do to now avail.

 

I would appreciate any tips, corrections, or references.

 

Thank you,

 

Kevin

7 REPLIES 7
sbxkoenk
SAS Super FREQ

Hello,

 

I cannot entirely follow your reasoning, but you may be helped with the %GLOBAL Macro Statement.

%GLOBAL Macro Statement :

Creates macro variables that are available during the execution of an entire SAS session.

 

Syntax
%GLOBAL macro-variable(s);

 

Koen

Astounding
PROC Star

You'll have to supply more details about what is in the macros, and how the first macro calls the second.

 

When one macro calls another, its macro variables are automatically available.  So it's not possible to imagine where the problem lies.  

KevinViel
Pyrite | Level 9

Tom,

 

  That was my conclusion before posting.  I was providing the users some "free code" (free text) ability, but I will have to limit it.  Thank you for the example.  I wonder if  %sysfunc(dequote()) and %unquote() perform the same?  I will look it up in my free time, i.e. after I get out from under the wall of work and emails from being off a week.

 

Happy New Year!

 

Kevin

Tom
Super User Tom
Super User

%UNQUOTE() removes macro quoting.  DEQUOTE() removes actual quotes.  If you use %SYSFUNC() to call DEQUOTE() then the result has the macro quoting removed.

 

It will be easier for users to pass in values that contain macro triggers by using physical single quotes then figuring out the complex macro quoting rules/functions.

Quentin
Super User

Hi Kevin,

 

I'm not understanding your question, can you explain more?

 

It looks like you're trying to pass in the upper limit for the %do loop, right?

 

If you do something like:

 

%macro inner (limit=);
  %local i ;

  %put Before unquoting: &=limit ;
  %let limit=%unquote(&limit) ;
  %put After unquoting: &=limit ;

  %do i=1 %to &limit ;
    %put &=i ;
  %end ;

%mend inner ;

 

 

That will allow users to do a simple call like:

 

%inner(limit=3)

Users could also do more complex calls like:

 

 

%let num_of_cols=3 ;

%inner(limit=&num_of_cols)
%inner(limit=%nrstr(&num_of_cols))
%inner(limit=%nrstr(%eval(&num_of_cols -1)))

 

 

So in those last two calls, the user has used %NRSTR() to delay resolution of the macro reference to NUM_OF_COLS, until it has been %UNQUOTED inside the macro.

 

Depending on your use case, you might want to set the default value of LIMIT to be %nrstr(&num_of_cols). That would make the macro more tightly coupled to the calling environment, and less of a utility macro.

 

If the users are at an advanced  macro language level where they would *want* to pass a complex value like 

%eval( &num_of_cols. - 1 )

I think it's reasonable to teach them to add %nrstr() when necessary to delay resolution of macro references.

 

KevinViel
Pyrite | Level 9

Quentin,

 

  That would work on a more rigid macro:

 

               column %if %nrbquote(&report_column.) = %str()
                     %then
                        %do ;
                            section_order_1
                            row_order_1
                            row_1
                            %do __i = 1 %to &num_of_cols. ;
                               col&__i.
                            %end ;
                        %end ;
                      %else
                         %do ;
                             %unquote(&report_column.)
                         %end ;
                     ;

This current version does not allow column headers, for instance.  Consider that COL1 and COL2 should have one header and COL3 and COL4 another and COL5 none.

 

This was an issue when the macro was three deep: a macro called a macro that called this macro.  I have removed the embedding and the program calls the three macros in succession.

 

Interesting, at least for me, but I am on a timer 🙂

 

Thanks,

 

Kevin

Tom
Super User Tom
Super User

A word of advice.  Don't.  It is not worth the headache.

 

You cannot inject something like a %DO loop into an already compiled and running macro. But you could call some other macro that runs a %DO loop.

 

Add single quotes around the macro code you want to pass in and the use DEQUOTE() to remove them when you want it to run.

%macro some_function(a,b,c);
%put &=a &=b &=c;
%mend;
%macro mymacro(parm1,parm2,subroutine);
%put &=parm1 &=parm2;
%sysfunc(dequote(&subroutine));
%mend mymacro;

%mymacro(parm1=xx,parm2=yy,subroutine='%some_function(1,2,3)');

Result

730   %mymacro(parm1=xx,parm2=yy,subroutine='%some_function(1,2,3)');
PARM1=xx PARM2=yy
A=1 B=2 C=3

 

hackathon24-white-horiz.png

The 2025 SAS Hackathon has begun!

It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.

Latest Updates

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 7 replies
  • 1615 views
  • 4 likes
  • 5 in conversation