Nested macro definition causes :Open code statement recursion detected.
however move the nested macro definition to outside of main macro will cure the code.
But why? Syntax-wise they are both valid.
Anyone can explain to me? Thank you.
%macro upcaseDSN(dsn); %macro NameWithoutLibOpt(dsn); %if %index(&dsn,.) %then %scan(&dsn,2,%str(.%()); %else %scan(&dsn,1,%str(.%()); %mend; %upcase(%NameWithoutLibOpt(&dsn)) %mend; %put %upcaseDSN(%str(sashelp.class(keep=age))); %* expect to see CLASS but failed;
252 253 254 %macro upcaseDSN(dsn); 255 %macro NameWithoutLibOpt(dsn); 256 %if %index(&dsn,.) %then %scan(&dsn,2,%str(.%()); 257 %else %scan(&dsn,1,%str(.%()); 258 %mend; 259 %upcase(%NameWithoutLibOpt(&dsn)) 260 %mend; 261 %put %upcaseDSN(%str(sashelp.class(keep=age))); MLOGIC(UPCASEDSN): Beginning execution. MLOGIC(UPCASEDSN): Parameter DSN has value sashelp.class(keepage) ERROR: Open code statement recursion detected. SYMBOLGEN: Macro variable DSN resolves to sashelp.class(keep=age) SYMBOLGEN: Some characters in the above value which were subject to macro quoting have been unquoted for printing. ERROR: Expected %DO not found. ERROR: Skipping to next %END statement.
modified code are fine. but why?
%macro NameWithoutLibOpt(dsn); %if %index(&dsn,.) %then %scan(&dsn,2,%str(.%()); %else %scan(&dsn,1,%str(.%()); %mend; %macro upcaseDSN(dsn); %upcase(%NameWithoutLibOpt(&dsn)) %mend; %put %upcaseDSN(%str(sashelp.class(keep=age))); %* get CLASS by moving nested macro out ;
273 %* get CLASS by moving nested macro out ; 274 option nosymbolgen nomlogic; 275 %macro NameWithoutLibOpt(dsn); 276 %if %index(&dsn,.) %then %scan(&dsn,2,%str(.%()); 277 %else %scan(&dsn,1,%str(.%()); 278 %mend; 279 280 %macro upcaseDSN(dsn); 281 282 %upcase(%NameWithoutLibOpt(&dsn)) 283 %mend; 284 %put %upcaseDSN(%str(sashelp.class(keep=age))); CLASS 285 %* get CLASS by moving nested macro out ;
Thank everyone who replied. I want to emphasize one thing here. I am not looking for another solution of this example of dataset name extraction. It is not the point but an example to show what the issue is. The actual code is more complicated and with some other purposes. I am also not arguing the nested style is a good way or not. Occasionally we do need to have small submacros go with main macro for some purpose. Here i need to know the key point of the reason why it is working/not working without any difference but the location of submacro. Let me know if you get the answer to this particular point. I have a remote guess of resolving phase/execution phase difference but if someone got a better and deeper understanding is much helpful. Thank you.
The open code environment in which you call the %put statement that executes the macro code seems to be the issue.
I do not figure exactly why neither.
If you nest the whole code one more time it works:
%macro doit(); %macro upcaseDSN(dsn); %local v; %macro NameWithoutLibOpt(ds=); %if %index(%superq(ds),.) %then %scan(%superq(ds),2,.); %else %scan(%superq(ds),1,.); %mend NameWithoutLibOpt; %NameWithoutLibOpt(ds=%superq(dsn)); %mend upcaseDSN; %put %upcaseDSN(sashelp.class2); %mend doit; %doit;
%macro doit(); %macro upcaseDSN(dsn); %macro NameWithoutLibOpt(dsn); %if %index(&dsn,.) %then %scan(&dsn,2,%str(.%()); %else %scan(&dsn,1,%str(.%()); %mend; %upcase(%NameWithoutLibOpt(&dsn)) %mend; %put %upcaseDSN(%str(sashelp.class(keep=age))); %* expect to see CLASS but failed; %mend doit; %doit;
- Cheers -
Thanks, it turns out the problem is the resolving precedence of macro/plain statement. %put and %mcr has the same priority on execution and finally the delayed resolving of macro definition such as '%put %inside(%macro inside).....' is invalid. But if I use 'put' in data step it is fine. It is tricky but it is how SAS was built on the past decades. it is hard to give away the worked tech to other new tech even it could be better.
%macro outside(); %macro inside(); Some Text %mend inside; %inside() %mend outside; data ahuige; put "%outside"; run;
Nesting macro definitions is never a good idea. When you do this, each time the outer macro executes, the inner macro has to be compiled. I'm not sure I would call nested macro definitions "valid syntax".
That said, I agree your question is interesting. Here's a simpler example that replicates the issue. Note that the %inside macro is never even called:
%macro outside(); %macro inside(); Some Text %mend inside; %mend outside;
If you call the macro outside, it correctly returns nothing, but it works fine. And you can see that %INSIDE is compiled when %OUTSIDE executes:
1 options mcompilenote=all ; 2 %macro outside(); 3 %macro inside(); 4 Some Text 5 %mend inside; 6 %mend outside; NOTE: The macro OUTSIDE completed compilation without errors. 5 instructions 100 bytes. 7 8 %outside() NOTE: The macro INSIDE completed compilation without errors. 5 instructions 64 bytes.
But note that above I called %OUTSIDE as a macro statement. You have a function-style macro, which you want to use within a macro statement. If I do that with %OUTSIDE, I get similar errors:
10 %put %outside(); ERROR: Open code statement recursion detected. ERROR: Macro keyword MEND appears as text. Some Text
It looks to me like when the macro processor is in the middle of executing the %PUT statement, and executes %OUTSIDE, it can't pause to compile %INSIDE, and it gets confused.
I think the best solution is to avoid nesting macro definitions.
Hi, I do like your way of testing this. it is interesting because I do use nested macro in my own SAS kit library in some code for a decade and as I said some sub-macros are only serving the purpose for a main macro and separating them might be meaningless since no other macro will call it anyway. one straight example is if I have a heavy code macro I might have it as part I and Part II... just for a reviewing purpose so a %macro_part1 and %macro_part2 are used as organizing purpose only and this way works fine. Never a fatal error like this occured before. so, I might avoid nesting as necessary but by discussing and getting a nailed answer would be even better. I will welcome more suggestions on this topic. Thank you for your insight on this.
I'm definitely a fan of using macros to create modular code, but still opposed to nesting macro definitions. If you do:
%macro main() ; %macro part1() ; %put part1 running ; %mend part1 ; %macro part2() ; %put part2 running ; %mend part2 ; %part1() %part2() %mend main ; %main()
The code works, but you haven't achieved any real benefit from the nested definitions. The macro %PART2 can still be called outside of %MAIN. And every time you run %MAIN, both %PART1 and %PART2 are re-compiled.
I think the only benefit you gain from this is that it looks like %PART1 and %PART2 are part of %MAIN. But that appearance is misleading.
I would code this as:
%macro main() ; %part1() %part2() %mend main ; %macro part1() ; %put part1 running ; %mend part1 ; %macro part2() ; %put part2 running ; %mend part2 ; %main()
If the definition of main is stored in an autocall library as main.sas, you can still put helper macros in that same file. All of the code in main.sas will be executed the first time you call %main, which will compile all the macros. You just need to be careful to avoid naming collisions across helper macros. I've seen people do stuff like:
*main.sas stored in autocall library; %macro main() ; %main_part1() %main_part2() %mend main ; %macro main_part1() ; %put part1 running ; %mend main_part1 ; %macro main_part2() ; %put part2 running ; %mend main_part2 ;
So the helper macros get named with a prefix of the main macro.
I think it can make sense to have the code in place
For example to ease the review by another programmer
I would definetely prefer such a nesting compared to a modular structure
Particularly if the nested macro is unable to run alone somewhere else
I've seen too much useless copy N paste
%macro main() ; <...code...> %macro sub(param=); <...code...> %mend sub; %subsub(param=a); %subsub(param=b); %subsub(param=c); %mend main ; %main()
- Cheers -
One reason I don't like the idea of nested macro definitions is the fact that the macros themselves are not actually nested. That is, even if you nest the definition of %sub within the definition of %main, when it is compiled %sub exists as a compiled macro separate from %main, and there is no nesting of the compiled macros. You could even delete %main and %sub would still exist, because they are fully independent. So the code suggests an organization of the macros which is not accurate. Some people mistakenly believe that %sub is "local to" %main, i.e. only exists within %main, and can only be used within %main. They think that by nesting macro definitions they have created a "scope" for a macro, but SAS does not have a scope for macros. It only has a scope for macro variables.
I feel the same way about placing global statements inside of a step, e.g. title statement:
proc print data=sashelp.class ; title1 "My Title" ; run; title1 ;
You see that a lot in SAS documentation, and it's how I was trained to do it. But a wise mentor pointed out to me that TITLE is a global statement, why would you put it "inside" a step when it is not part of the step, and it has a global effect. I think it's clearer to code as:
title1 "My Title" ; proc print data=sashelp.class ; run; title1 ;
Don't see why you need to nest them to put them together. In fact it just makes it HARDER to put them together.
If they are separate files for each macro and you want to make one large program just concatenate the macro definitions (in any order) and append the open code that uses them to the bottom.
hi @Quentin , after your inspiring hint I think now I have a solid explanation:
the nested macro [inside] was not complied until %outside is called. that means the compiling is delayed till called but the %put. But when it is called , the "%put %macro xxxxx ....." is not a valid syntax. That means an error. however if it is put outside as a separated macro its compilting was done already and the call %put [compiled context] is %put some text; this is totally fine. oh, it is very interesting. now I know how to nest macros and not to call with other partial code with it. thank you and others for testing and give me the hints. thank you all. 🙂
%macro outside(); %macro inside(); Some Text %mend inside; %inside() %mend outside; %put %outside; %*############### next phase the code is evolved as %put [code inside including definition of macro inside] it is now syntax-wise incorrect. ; %put [ %macro inside(); Some Text %mend inside; %inside() ] ;
There is no reason to nest the DEFINITION of macros. The address space for compiled macros is FLAT. You cannot have a "local" version of a macro. Nesting the definitions will just confuse the poor programmer who has to try to understand your code.
If your issue is indeed caused by nesting the macro definition then that is just another reason to not nest the definitions.
Registration is open! SAS is returning to Vegas for an AI and analytics experience like no other! Whether you're an executive, manager, end user or SAS partner, SAS Innovate is designed for everyone on your team. Register for just $495 by 12/31/2023.
If you are interested in speaking, there is still time to submit a session idea. More details are posted on the website.
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.