BookmarkSubscribeRSS Feed
armor
Calcite | Level 5

I sometimes use SAS macro variables to generate/store SAS code automatically. It was a nice way to do things for me, until yesterday. The following codes gives me errors:

"ERROR: The %DO statement is not valid in open code."

"ERROR: The %END statement is not valid in open code."

But apparently the %do and %end are within the macro boundary.

*===========================;

%macro test();

data t;

x=%nrstr("%do i=1 %to 5;");

y=%nrstr("%end;");

call symputx("y",y);

call symputx("x",x);

run;

&x.;

&y.;

%mend;

%test();

*===========================;

It looks like some bug that relates to how SAS interprets/resolves a macro variable that contains keywords such as "%do", "%end", etc.  I have tried various ways to bypass the problem but so far failed.

15 REPLIES 15
RW9
Diamond | Level 26 RW9
Diamond | Level 26

The &x.; is being resolved as %do I=1 %to 5;, however at that time the code has passed through the macro pre-processor and hence is in open code.  I would question why you are doing this at all, the macro language is there so you can create code which gets replicated or modified at pre-processor time.  Not sure why you need to have a macro to generate a loop?  If you really have to do that then:

data _null_;

     do I=1 to 5;

          call execute("%my_macro(....);");

     end;

run;

would probably be more effective though dependant on your scenario.  If you could expand you example I could provide further information.

armor
Calcite | Level 5

Thanks. Here is a self explanatory example of what I want to achieve -- I am relying on SAS to generate the code for me. The code structure is dynamic. For example, the # (in the example it is n=4) of %do loop level is an adjustable parameter.

%macro test1(n);

data s;

format x $100.; x="";

format y $100.; y="";

%do j=1 %to &n.;

x="%do i&j.=1 %"||"to 5;"||strip(x);

y="%end;"||strip(y);

%end;

call symputx("y",y);

call symputx("x",x);

run;

&x.;

&y.;

%mend;

%test1(n=4);

Astounding
PROC Star

The real question is a little different:  If the macro were to execute successfully, what would the generated SAS code look like?  It is entirely likely that the final solution will involve more DATA step code and less macro language ... but it's necessary to begin by looking at where you would like to end up.

armor
Calcite | Level 5

here is the final code that my sas code generates: when n=4 and when n=3 respectively.

*================================;

%macro test (n);

     %do i1=1 %to 5;

     %do i2=1 %to 5;

     %do i3=1 %to 5;

     %do i4=1 %to 5;

          /* do something */

     %end;%end%;end%;end%;

%mend test;

%test(n=4);

*================================;

*================================;

%macro test (n);

     %do i1=1 %to 5;

     %do i2=1 %to 5;

     %do i3=1 %to 5;

          /* do something */

     %end;%end%;end%;

%mend test;

%test(n=3);

*================================;

Astounding
PROC Star

It's tricky ... but you knew that.  It might be easiest to write code to a file, such as:

%macro test1 (n);

   data _null_;

   file myfile noprint;

   put 'macro test (n);';

   do i=1 to &n;

     i_char = left(put(i, 3.));

      put '%do i'   i_char   ' = 1 %to 5;';

   end;

   put '/* do something */';

   do i=1 to &n;

      put '%end;';

   end;

   put '%mend test;';

   run;

   %include myfile;

   %test (&n)

%mend test1;

%test1 (4)

I'll have to think about other approaches.

Is there a reason you need &n macro variables, each with its own loop?  Certainly it would be possible to code something like %do i=1 %to 5**n;  Perhaps you're presenting a simplified version and the "5" doesn't really get hard-coded?

armor
Calcite | Level 5

yes, the value 5 is not hard coded.

I was able to "auto-generate" codes that contains "do to;/end;" loop previously.  but now stuck with "%do %to;/%end;" loop

Let me go back to think about this and get back to you guys when I got some concrete idea.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Hi,

Could you provide a concrete example of what you input is, and what you want out.  You can try the below, this seems to generate the loops as your macro does:

data test (drop=i j);

  length loop ender $2000.;

  do i=1 to 10;

    do j=1 to 5;

      loop='do i'||strip(put(i,best.))||'=1 to 5; '||strip(loop);

      ender='end;'||strip(ender);

    end;

  end;

  call execute('data _null_;'||strip(loop)||strip(ender)||'run;');

run;

Personally I tend to favour this method of doing things when I have lots of loops, the reason is you can put all your parameters, loop, clause in a dataset, then just run over that generating the code as you go.

Astounding
PROC Star

Just a small piece of the puzzle, in case it helps ...

If the ending vlaue of 5 is not hard-coded, presumably you actually have something equivalent to:

%do i1=1 %to &i1_max.;

%do i2=1 %to &i2_max.;

You could get the proper number of loops with something along these lines:

%do i=1 %to &i1_max.*&i2_max.*&i3_max.;

Sometimes having the proper number of loops is sufficient, sometimes not.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Yes, not sure what the idea is here, your code basically resolves to (in my eyes):

%macro test (n);

     %do I=1 %to &n.;

          /* do something */

     %end;

%mend test;

%test;

I don't see why you are creating the string to hold your do loop, then adding it into the code later on?

data_null__
Jade | Level 19

%DO must be compiled and that is NOT what is happening with your program.

armor
Calcite | Level 5

I also wrote another piece of code which run successfully without error log-- this evidence supports the argument that "%DO must be compiled and that is NOT what is happening with your program.".

%macro test2();

data r;

z=%nrstr("%macro inner(); %do i=1 %to 5; data a&i; run; %end; %mend; %inner();");

call symputx("z",z);

run;

&z.;

%mend;

%test2();

Anyway, to me, SAS' not being able to accept "%DO/%END" in macro variables is a design deficiency. I will let you good folks know if I find any workarounds. thanks.

RW9
Diamond | Level 26 RW9
Diamond | Level 26

Its end of day for me, but am I missing something, surely the same could be achieved by:

%macro test (n);

     %do I = 1 %to &N.;

          %do J=1 %to 5;

               /* do something */

          %end;

     %end;

%mend test;

%test(n=4);

There are also a fair few other options, utilize arrays and call execute is one example I can think of, setup a dataset with all your loops in then generate the code.

armor
Calcite | Level 5

sorry but i guess they are different?

you code has 2 nested loop, but my code has 4 nested loop when n=4, and it can actually generate 100 nest loops if i set the parameter n=100 in my code.

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 15 replies
  • 4184 views
  • 0 likes
  • 5 in conversation