BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
KT0
Calcite | Level 5 KT0
Calcite | Level 5

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!

 

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

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;

 

View solution in original post

15 REPLIES 15
KT0
Calcite | Level 5 KT0
Calcite | Level 5
I did try it although I didn't really expect it to work as my understanding is it masks the macro triggers at compilation.
Quentin
Super User

%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.

BASUG is hosting free webinars ! Check out our recordings of past webinars: https://www.basug.org/videos. Be sure to subscribe to our email list for notification of future BASUG events.
KT0
Calcite | Level 5 KT0
Calcite | Level 5

Thanks for your response.

 
I can see the difference in the log when I use %nrbquote and %nrstr - however there is still an issue when I use %nrstr. I was focussed (probably wrongly) on the hidden triggers in string2 (which I was expecting to be revealed during execution) rather than the more obvious %maxl trigger that would be detected during compilation! As you can see below in the log, SAS is still trying to resolve the hidden triggers contained in the string2 variable when I use %nrstr - any ideas how to stop this happening?
 
 
1      + %maxl(dsn=DS1
                                                                   ,
MLOGIC(MAXL):  Beginning execution.
2      +          lib1=SD2
                                                                       ,
3      +          lib2=SD3
                                                                           ,
4      +                  pdbf=Y       ,                                                     lib1f=Y       ,
         setstr="set &lib1..&dsn &lib2..&dsn;"  ,
WARNING: Apparent symbolic reference DSN not resolved.
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.
5      +    lib2f=Y       );
MLOGIC(MAXL):  Parameter DSN has value DS1
MLOGIC(MAXL):  Parameter LIB1 has value SD2
MLOGIC(MAXL):  Parameter LIB2 has value SD3
MLOGIC(MAXL):  Parameter PDBF has value Y
MLOGIC(MAXL):  Parameter LIB1F has value Y
MLOGIC(MAXL):  Parameter SETSTR has value "set  &lib1..&dsn &lib2..&dsn;"
MLOGIC(MAXL):  Parameter LIB2F has value Y
Quentin
Super User

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.

BASUG is hosting free webinars ! Check out our recordings of past webinars: https://www.basug.org/videos. Be sure to subscribe to our email list for notification of future BASUG events.
Tom
Super User Tom
Super User

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;

 

KT0
Calcite | Level 5 KT0
Calcite | Level 5

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 🙂

KT0
Calcite | Level 5 KT0
Calcite | Level 5
Thanks for your response.

I can see the difference in the log when I use %nrbquote and %nrstr - however there is still an issue when I use %nrstr. I was focussed (probably wrongly) on the hidden triggers in string2 (which I was expecting to be revealed during execution) rather than the more obvious %maxl trigger that would be detected during compilation! As you can see below in the log, SAS is still trying to resolve the hidden triggers contained in the string2 variable when I use %nrstr - any ideas how to stop this happening?


1 + %maxl(dsn=DS1
,
MLOGIC(MAXL): Beginning execution.
2 + lib1=SD2
,
3 + lib2=SD3
,
4 + pdbf=Y , lib1f=Y ,
setstr="set &lib1..&dsn &lib2..&dsn;" ,
WARNING: Apparent symbolic reference DSN not resolved.
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.
5 + lib2f=Y );
MLOGIC(MAXL): Parameter DSN has value DS1
MLOGIC(MAXL): Parameter LIB1 has value SD2
MLOGIC(MAXL): Parameter LIB2 has value SD3
MLOGIC(MAXL): Parameter PDBF has value Y
MLOGIC(MAXL): Parameter LIB1F has value Y
MLOGIC(MAXL): Parameter SETSTR has value "set &lib1..&dsn &lib2..&dsn;"
MLOGIC(MAXL): Parameter LIB2F has value Y

Ksharp
Super User
%nrbquote would resolve macro variable , if macro variable can't be resolved then MASKED it .
It is unlike %superq() which would mask macro variables immediately .
Tom
Super User Tom
Super User

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
,');'
);
KT0
Calcite | Level 5 KT0
Calcite | Level 5

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?

Tom
Super User Tom
Super User

@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.

 

 

 

 

KT0
Calcite | Level 5 KT0
Calcite | Level 5
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?
Tom
Super User Tom
Super User

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;

 

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
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
  • 985 views
  • 9 likes
  • 4 in conversation