Hello,
I have the following call to macro %maxl using call execute. It is called based on certain conditions within another macro.
call execute('%nrbquote(%maxl(dsn='||currds||',
lib1='||librar1||',
lib2='||librar2||',
pdbf='||pos1||',
lib1f='||pos2||',
setstr='||string2||',
lib2f='||pos3||'));');
string2 will have a value like "set &lib1..&dsn;". I don't want the macro variable references within string2 to be resolved until string2 is used in the maxl macro. However, even with the use of %nrbquote SAS is trying to resolve the macro variable references in the string. Here is a excerpt from the log:
MLOGIC(MAXL): Beginning execution.
WARNING: Apparent symbolic reference LIB1 not resolved.
WARNING: Apparent symbolic reference DSN not resolved.
LIB1 and LIB2 are then resolved.
MLOGIC(MAXL): Parameter SETSTR has value "set &lib1..&dsn;"
Can anyone spot what I am missing? Thanks!
You want the value on one parameter to your macro call to refer to the values being passed as other arguments to the same macro call? That is tricky enough without also adding the extra confusion of using CALL EXECUTE() to generate and run the macro call.
Why not just generate the value of SETSTR using the same information you used to generate the values of LIB1, LIB2 and DSN instead of muddy thinks up with the cross references.
data test;
dsn='ds1';
lib1='sd2';
lib2='sd3';
setstr=catx(' ','set',catx('.',lib1,dsn),catx('.',lib2,dsn));
...
run;
%NRSTR() is what you want.
%NRSTR is used to mask triggers during macro compilation, rather than macro execution. This is different (earlier) the data step compilation and data step execution.
The concept of macro compilation vs macro execution breaks down (for me at least), when you use quoting function in 'open code' (i.e. outside of a macro definition).
One easy rule is "use %str() / %nrstr() if you can see the symbol you are trying to mask, if you cannot see the symbol (because the symbol only appears as a results of resolving a macro reference), use %bquote/%superq)." In this case, you are trying to mask the % sign you can see in %max, so using %nrstr is appropriate.
Then end result should be that the CALL EXECUTE only generates the macro call to %max (this is all you should see in the log with with + prefixes indicating the lines generated by CALL EXECUTE), the CALL EXECUTE will not actually execute the macro.
Thanks for your response.
Thanks for sharing the log.
This CALL EXECUTE is working with the %NRSTR, I would go back to look at how the macro call works, without call execute.
As shown in the CALL EXECUTE log, currently you are generating the macro call:
%maxl(dsn=DS1
,lib1=SD2
,lib2=SD3
,pdbf=Y
,lib1f=Y
,setstr="set &lib1..&dsn &lib2..&dsn;"
,lib2f=Y)
Is that the call you want? If you submit that call, it will try to resolve the macro references &lib1 &dsn &lib2 before they exist. Do you really want the set statement inside of quotes?
Just like with using the macro language to generate SAS code, you want to start with working SAS code, when you're using CALL EXECUTE to generate macro calls, you want to start with a working macro call.
Maybe the following would work as a macro call:
%maxl(dsn=DS1
,lib1=SD2
,lib2=SD3
,pdbf=Y
,lib1f=Y
,setstr=%nrstr(set &lib1..&dsn &lib2..&dsn)
,lib2f=Y)
With that approach you would need to %unquote the macro parameter SETSTR inside of the definition of %maxl.
Once you have a macro call to %maxl that is working like you want, then you can go back to using CALL EXECUTE to generate the macro call.
You want the value on one parameter to your macro call to refer to the values being passed as other arguments to the same macro call? That is tricky enough without also adding the extra confusion of using CALL EXECUTE() to generate and run the macro call.
Why not just generate the value of SETSTR using the same information you used to generate the values of LIB1, LIB2 and DSN instead of muddy thinks up with the cross references.
data test;
dsn='ds1';
lib1='sd2';
lib2='sd3';
setstr=catx(' ','set',catx('.',lib1,dsn),catx('.',lib2,dsn));
...
run;
You were right - I already had what I needed! I was complicating matters. I'm not sure it was possible to mask the triggers the way the string was.
Thanks for taking the time to look at it 🙂
Use %NRSTR(). And unless the values of your data step variable include other string you need to protect just wrap it around the macro name (or even just the % itself).
call execute(cats('%nrstr(%maxl)'
,'(dsn=',currds
,',lib1=',librar1
,',lib2=',librar2
,',pdbf=',pos1
,',lib1f=',pos2
,',setstr=',string2
,',lib2f=',pos3
,');'
);
Thanks but the problem is the dataset variable string2 does contain macro variable triggers and these are not being masked. When you say, just wrap it around the macro name (or even just the % itself), I'm not sure what you mean?
@KT0 wrote:
Thanks but the problem is the dataset variable string2 does contain macro variable triggers and these are not being masked. When you say, just wrap it around the macro name (or even just the % itself), I'm not sure what you mean?
Why would you want to mask the values generated from your dataset variables? Those are the values you are using the generate the call to macro. Put the value into the dataset variable that makes the macro call work.
A typical example. You have some macro you want to call multiple times. For example consider the project to import a series of files. You have a macro that can import one. You make a dataset with the list of files. Then you use the dataset to call the macro once for each file. To make it easier to set a macro variable at the top with the path of where to find the files. Then you use the macro variable when generating the code. When do you want that macro variable to be evaluated? When the SAS code is pushed onto the stack?
Code:
data files;
infile "dir /b &path\su*.csv" pipe truncover;
input filename $200.;
call execute(cats('%nrstr(%import)(filename=','&path\',filename,',dsname=',scan(filename,1,'.'),')'));
run;
Log:
NOTE: CALL EXECUTE generated line. 1 + %import(filename=c:\downloads\Subaru.csv,dsname=Subaru) SYSMACRONAME=IMPORT FILENAME=c:\downloads\Subaru.csv DSNAME=Subaru 2 + %import(filename=c:\downloads\Suzuki.csv,dsname=Suzuki) SYSMACRONAME=IMPORT FILENAME=c:\downloads\Suzuki.csv DSNAME=Suzuki
Or when code is pulled back off the stack to run?
Code:
data files;
infile "dir /b &path\su*.csv" pipe truncover;
input filename $200.;
call execute(cats('%nrstr(%import(filename=','&path\',filename,',dsname=',scan(filename,1,'.'),'))'));
run;
Log:
1 + %import(filename=&path\Subaru.csv,dsname=Subaru) SYSMACRONAME=IMPORT FILENAME=c:\downloads\Subaru.csv DSNAME=Subaru 2 + %import(filename=&path\Suzuki.csv,dsname=Suzuki) SYSMACRONAME=IMPORT FILENAME=c:\downloads\Suzuki.csv DSNAME=Suzuki
But what happens if the macro has a bug that changes the value of PATH when it runs?
Now the second call uses the path as changed by the first call.
1 + %import(filename=&path\Subaru.csv,dsname=Subaru) SYSMACRONAME=IMPORT FILENAME=c:\downloads\Subaru.csv DSNAME=Subaru 2 + %import(filename=&path\Suzuki.csv,dsname=Suzuki) SYSMACRONAME=IMPORT FILENAME=c:\someother_path\Suzuki.csv DSNAME=Suzuki
Perhaps for some situations that is what you want, but probably not.
You need to wrap the self references in some type of macro quoting to get them into the macro without the macro processor attempting to expand them during the macro call.
Let's make a simple macro to demonstrate with:
%macro maxl(dsn,lib1,lib2,setstr);
%put &=setstr;
%let x=%unquote(&setstr);
%put &=x;
%mend;
If we call it without any macro quoting in the call we get the warnings (or we get the values some other already existing macro variables with those same names).
2870 %maxl(dsn=DS1,lib1=SD2,lib2=SD3,setstr="set &lib1..&dsn &lib2..&dsn;") WARNING: Apparent symbolic reference LIB1 not resolved. WARNING: Apparent symbolic reference DSN not resolved. WARNING: Apparent symbolic reference LIB2 not resolved. WARNING: Apparent symbolic reference DSN not resolved. SETSTR="set SD2.DS1 SD3.DS1;" X="set SD2.DS1 SD3.DS1;"
If we add macro quoting the warnings go away.
2871 %maxl(dsn=DS1,lib1=SD2,lib2=SD3,setstr=%nrstr("set &lib1..&dsn &lib2..&dsn;")) SETSTR="set &lib1..&dsn &lib2..&dsn;" X="set SD2.DS1 SD3.DS1;"
Now let's add the CALL EXECUTE wrapper.
If we just pass that exact same string that worked we don't get the warnings, but the macro executes during the running of CALL EXECUTE and the code it generates is pushed onto the stack to run after the dataset step. If the macro was doing anything that required evaluating the resutls of the SAS code generated earlier by the macro then you will have a timing problem. Plus your SAS log will be really ugly and impossible to figure out. With our simple test macro we get a note that the macro did not generate any actual SAS code to push onto the stack.
2872 data _null_; 2873 call execute('%maxl(dsn=DS1,lib1=SD2,lib2=SD3,setstr=%nrstr("set &lib1..&dsn &lib2..&dsn;"))'); 2874 run; SETSTR="set &lib1..&dsn &lib2..&dsn;" X="set SD2.DS1 SD3.DS1;" NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds NOTE: CALL EXECUTE routine executed successfully, but no SAS statements were generated.
So let's add some macro quoting in the string we pass to CALL EXECUTE.
First let's just protect the macro call itself from being seen while the code is pushed onto the stack. So the code that gets pushed to run now looks right. But the results look like what happens without the %NRSTR() around the value being passed for the SETSTR parameter.
2875 data _null_; 2876 call execute('%nrstr(%maxl)(dsn=DS1,lib1=SD2,lib2=SD3,setstr=%nrstr("set &lib1..&dsn &lib2..&dsn;"))'); 2877 run; NOTE: DATA statement used (Total process time): real time 0.01 seconds cpu time 0.01 seconds NOTE: CALL EXECUTE generated line. WARNING: Apparent symbolic reference LIB1 not resolved. WARNING: Apparent symbolic reference DSN not resolved. WARNING: Apparent symbolic reference LIB2 not resolved. WARNING: Apparent symbolic reference DSN not resolved. 1 + %maxl(dsn=DS1,lib1=SD2,lib2=SD3,setstr="set &lib1..&dsn &lib2..&dsn;") SETSTR="set SD2.DS1 SD3.DS1;" X="set SD2.DS1 SD3.DS1;"
How can we fix this? We could wrap the new %NRSTR() around the whole string we pass to CALL EXECUTE.
2878 data _null_; 2879 call execute('%nrstr(%maxl(dsn=DS1,lib1=SD2,lib2=SD3,setstr=%nrstr("set &lib1..&dsn &lib2..&dsn;")))'); 2880 run; NOTE: DATA statement used (Total process time): real time 0.00 seconds cpu time 0.00 seconds NOTE: CALL EXECUTE generated line. 1 + %maxl(dsn=DS1,lib1=SD2,lib2=SD3,setstr=%nrstr("set &lib1..&dsn &lib2..&dsn;")) SETSTR="set &lib1..&dsn &lib2..&dsn;" X="set SD2.DS1 SD3.DS1;"
Now we get a nice SAS log and the macro references make their way into the value SETSTR without being resolved either during the running of CALL EXECUTE() or during the calling of the MAXL macro.
Of course you could also just use single quotes around the value SETSTR instead of double quotes. But then you would need to use DEQUOTE() instead of %UNQUOTE() to bring the macro references to life.
%macro maxl(dsn,lib1,lib2,setstr);
%put &=setstr;
%let x=%sysfunc(dequote(&setstr));
%put &=x;
%mend;
2898 %maxl(dsn=DS1,lib1=SD2,lib2=SD3,setstr='set &lib1..&dsn &lib2..&dsn;') SETSTR='set &lib1..&dsn &lib2..&dsn;' X=set SD2.DS1 SD3.DS1;
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
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.