Multiple levels of macro variables resolve [duplicate] - sas

This question already has an answer here:
sas MACRO ampersand
(1 answer)
Closed 5 years ago.
I have email list in storage, which I include so that country specific people get notifications. But for demonstration purposes let use one:
I want to dynamically select address depending on variable &system:
%let SEList1 = "my.email#domain.eu";
%put system.list1.;
/*system.list1, which is ok*/
%put &system.list1.;
/*SElist1, which is ok*/
%put &&system.list1.;
/*SElist1, Hmm? Shouldn't this be "my.email#domain.eu"*/
%put &&&system.list1.;
/*"my.email#domain.eu", umm... ok? */
/*Lets keep going.... */
%put &&&&system.list1.;
/*SElist1. wut? */
%put &&&&&system.list1.;
/*"my.email#domain.eu"*/
%put &&&&&&system.list1.;
/*"my.email#domain.eu"*/
%put &&&&&&&system.list1.;
/*&"my.email#domain.eu"*/
%put &&&&&&&&system.list1.;
/*SElist1. You got to be kidding me?*/
Question:
a)Why does not the && not resolve to address, but &&& does?
b)What on Earth is happening with levels of &*4+

When the SAS macro processor sees two ampersands && it resolves that to one & and sets a flag that indicates it needs to reprocess the string for more macro variable resolutions.
So &&&system.list1. gets processed as && , &system. and list1. which gives you an intermediate value of &SElist1.. So on the second pass you get the value of your indirect macro variable reference.
178 %let SEList1 = "my.email#domain.eu";
179 %let system = SE ;
180 options symbolgen ;
181 %put &&&system.list1.;
SYMBOLGEN: && resolves to &.
SYMBOLGEN: Macro variable SYSTEM resolves to SE
SYMBOLGEN: Macro variable SELIST1 resolves to "my.email#domain.eu"
"my.email#domain.eu"
If you only had two && then the intermediate value is just &system.list1. which will split into &system. and list1. so the result should be SElist1.
You can add as many as you want and follow this rule. For example if you had 6 then after the first pass it would be the same as if you had started with 3 since each pair just gets reduced to one. So using 6 yields the same result as using 3 but it just needs an extra pass at resolving the string.

Related

'Open code statement recursion detected' when running %let %put statement (SAS Macro)

When I run the following, it works fine:
%let mname = ABC<2>;
%put &mname;
ABC<2>
%let mname2 = %scan("&mname.", 2, '<>');
%put &mname2;
2
However, when I changed mname2 to the following, it gives the error:
%let mname2 = %scan("&mname.", 1, '<');
%put &mname2;
ERROR: Open code statement recursion detected.
Any idea what is causing it?
Since you added quotes around the value passed to the %SCAN() function and then only selected one of the quotes as the output you have generated unbalanced quotes.
In regular SAS code (and most programming languages) to allow the compiler to tell the difference between a variable name or a keyword and a string literal you add quotes around the string
But the macro processor language is different. To the macro processor everything is a string and it just looks for the & or % triggers to tell whether there is anything it needs to operate on.
49 %let mname = ABC<2>;
50 %put &=mname;
MNAME=ABC<2>
51 %put %scan(&mname,1,<>);
ABC
52 %put %scan(&mname,2,<>);
2
If you think it is possible the %SCAN() function will return unbalanced quotes or other things that might need macro quoting use the %QSCAN() function instead.

%SCAN with M modifier giving incorrect results

Can anyone please suggest why do the below two codes have different outputs?
I have a macro var FILETYPE which resolves to ,File // missing first value.
%let b=%scan("&filetype",1,",",M);
%put &b;
%let c=%scan("&filetype",2,",",M);
%put &c;
%let d=%scan("&filetype",3,",",M);
%put &d;
%mend;
%tt;`
I get the following output:
SYMBOLGEN: Macro variable FILETYPE resolves to ,File
SYMBOLGEN: Macro variable B resolves to
SYMBOLGEN: Macro variable FILETYPE resolves to ,File
SYMBOLGEN: Macro variable C resolves to
SYMBOLGEN: Macro variable FILETYPE resolves to ,File
SYMBOLGEN: Macro variable D resolves to File
File
When macro var C should have resolved to File , but its still NULL and D gets the value File.
The same logic in a plain datastep works correctly.
data a;
b=scan("&filetype",1,',','M');
c=scan("&filetype",2,',','M');
run;
Output :
b=blank
c=File
Can someone please suggest how come the SCAN function with M modifier work okay in data step but not in macro?
It works exactly the same in macro code as in data step code. The problem is that you added double quotes to both the string to be scanned and to the list of delimiters to use. So the equivalent data step would be:
%let filetype=A,B;
data _null_;
length string word $100;
string=quote(symget('filetype'));
do i=1 to 4;
word=scan(string,i,'","','M');
put i= word=:$quote.;
end;
run;
Which produces this result:
i=1 word=""
i=2 word="A"
i=3 word="B"
i=4 word=""
What you probably meant to do in macro code was:
%scan(%superq(filetype),1,%str(,),M)
In normal SAS code you need to add quotes around string literals so that the parser knows that you didn't mean a variable name. In macro code that is not needed, everything is a string. You use macro triggers, & and %, to let the parser know when to treat it differently.
When generating a delimited list to be used in macro code then generate it with a different delimiter and avoid all of the macro quoting needed to deal with the commas.
... into :filetype separated by '|' ...
%scan(&filetype,1,|,m)
Don't double quote the arguments in macro invocations. You DO need to literalize the comma valued argument as %str(,) in order to have it NOT be considered an argument separator.
%scan ( &filetype , 1, %str(,), M )
If the filetype value contains macro important symbols, you may have to macro quote or superq the argument. If the return can contain macro important symbols that are not to be resolved you will want to use %QSCAN to mask them.

SAS accessing the macro variable name

I have some macro variables and each is assigned with a value. How can I get the name of the variables from their value?
For example I have assigned each person an age value. &Amy=12, &Peter=10.
I also have a macro function calculating something else, say weight.
%macro weight(name=);
%let weight=&name*10;
%put &name.'s weight is &weight.;
%mend;
if I run %weight(name=Amy) I want to get the result as "Amy's weight is 120".
how can i get the result as "Amy" instead of "12"?
Thanks
Nicely evil! That little single quote in the "Amy's" messes things up quite a bit. SAS seems to get confused about trying to evaluate the rest of the macro as a quoted string.
Start with the "magic string" to end all quotes, and make sure you've enabled macro output.
Once you do that, the following should work. Note the additional ampersands before name, as another responder suggested.
;*';*";*/;quit;
options mprint;
%let Amy = 12;
%macro weightmacro(name=);
%let weight=%sysevalf(&&&name..*10);
%put &name.s weight is &weight.;
%mend;
%weightmacro(name=Amy);
This will get you part of the way there. Note that having the apostrophe there is slightly problematic, you'll need to escape that or quote it out. I've ignored that for now. For the rest of your question:
Add more & to resolve a macro variable that's nested
Use %SYSEVALF() to do calculations with macro variables
%let Amy = 12;
%macro weight(name=);
%let weight=%sysevalf(&&&name*10);
%put &name. weight is &weight.;
%mend;
%weight(name=Amy);
If the point is the possessive noun that can be accomplished with %str(%');
357 %let Amy = 12;
358 %macro weightmacro(name=);
359 %let weight=%sysevalf(&&&name..*10);
360 %put &name.%str(%'s) weight is &weight.;
361 %mend;
362 %weightmacro(name=Amy);
Amy's weight is 120

SAS: Apparent symbolic reference not resolved for Macro reference

I have understood, and seen in other programs, that the following syntax is true.
%let variable = 'something';
statement name "&variable\othertext"; // something\othertext
However, in the code I have written I get this error message: Apparent symbolic reference not resolved. for the line LIBNAME REMOTE
%let month = 'JUN';
%let year = '18';
%let zos = ***********
signon zos ********************;
libname name "*********************************";
rsubmit;
libname remote "AAAA.BBBB.&month&year.SASLIB" access = readonly;
proc download inlib=remote outlib=name;
run;
libname remote clear;
endrsubmit;
signoff;
What am I missing?
The MONTH and YEAR macro variables are being defined in your local session, yet you're trying to resolve them in a remote session.
Use %SYSRPUT and %SYSLPUT to assign macro variables between sessions.
/* Local to remote */
%LET MONTH = 12 ;
%LET YEAR = 2018 ;
%SYSLPUT MONTH = &MONTH ;
%SYSLPUT YEAR = &YEAR ;
rsubmit ;
%PUT &MONTH &YEAR ;
/* resolves 12 and 2018 respectively */
/* remote to local */
%SYSRPUT FOO = BAR ;
endrsubmit ;
%PUT &FOO ; /* resolves to BAR */
More context would help, but most likely you are not understanding the role that period plays in resolving macro variable (symbol) references. To allow you to place letters and digits next to macro variable references SAS needs a way to tell where the name of the macro ends and the plain text starts. Period is used for that.
So if you wanted to generate this string
"AAAA.BBBB.JAN18.SASLIB"
from month and year values. First make sure to set the macro variables to the text you actually want. Quotes are just text to the macro processor.
%let month=JAN ;
%let year= 18;
Then in when you replace the values with macro variable references you will need an extra period after &YEAR so that one actually is generated. You should probably just get in the habit of always adding the period when referencing a macro variable.
"AAAA.BBBB.&month.&year..SASLIB"

SAS macro quoting: pass equals sign as macro argument

I am writing a macro that at some point calls some proc SQL code. I want the user to be able to specify arbitrary proc sql options (e.g. inobs=100 could be one of the input arguments to my macro).
I am having a very hard time quoting an argument that has an equality '=' character.
One of the issues is that I should also check if the macro argument is empty or not, and if it is not empty, only then add the specified options to the sql statement.
Below is an example non-working test that does not work and throws the
ERROR: The keyword parameter INOBS was not defined with the macro.
I have read this (http://www2.sas.com/proceedings/sugi28/011-28.pdf) and other SUGI's and tried many possible ways to quote and call the macro.
If somebody could provide a working example of the below function it would be greatly appreciated.
options mprint mlogic;
data have;
length x $8;
input x;
datalines;
one
two
three
;
proc sql inobs=2;
create table sql_output as
select *
from have;
quit;
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
%if "%left(%trim(&sqlOptions.))" ne "" %then %do;
&sqlOptions.
%end;
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from have;
quit;
%mend;
%pass_parameter_with_equal_sign(table=have, sqlOptions=%str(inobs=2))
title "SQL output:";
proc print data=sql_output; run;
title "Macro output:";
proc print data=macro_output; run;
If you remove the %if condition as follows it should work:
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
&sqlOptions.
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from have;
quit;
%mend;
The %if you have used is to check if &sqlOptions is not blank, this shouldn't matter if you use it as it is because its unconditional usage will give either:
proc sql inobs=2; /* in the case of &sqlOptions=inobs=2 */
or if there is no value supplied for &sqlOptions then you should see:
proc sql; /* i.e. no options specified */
So it should work with or without an argument.
Amir's solution is probably correct for your particular use case. But to answer the more general question, we need to look to the seminal paper on macro parameter testing, Chang Chung's Is This Macro Parameter Blank?.
His example C8 is the right one for you here, though some of the others will also work.
%if %sysevalf(%superq(param)=,boolean) %then ... /* C8 */
For example:
%macro test_me(param=);
%if %sysevalf(%superq(param)=,boolean) %then %put Empty;
%else %put Not Empty;;
%mend test_me;
%test_me(param=);
%test_me(param=MyParam);
%test_me(param=param=5);
%SUPERQ is most useful here because it avoids resolving the macro parameter. Instead, it keeps it as a macro parameter value - fully unresolved - and allows you to work with it in that fashion; so you have no risk of that pesky equal sign bothering you.
His C4 (just using SUPERQ without SYSEVALF) also works in this case, although he explains a few situations where it may have difficulty.
Ahh this was actually a tricky little problem you ran into. The issue was actually being caused by the calls to %trim() and %left().
Removing these results in code that works as intended (note I also removed the macro quoting around the parameter):
%macro pass_parameter_with_equal_sign(table=, sqlOptions=);
proc sql
%if "&sqlOptions" ne "" %then %do;
&sqlOptions
%end;
/* the semicolon to end the proc sql statement */
;
create table macro_output as
select *
from &table;
quit;
%mend;
%pass_parameter_with_equal_sign(table=sashelp.class, sqlOptions= inobs=2);
We can re-create the issue you were experiencing like so:
%put %trim(inobs=1);
Because the parameter was resolving to inobs=1, and %trim() doesn't have any named parameters, it was throwing a hissy fit. To correctly pass in a string that contains "inobs=1" we can do so like this:
%let param = inobs=1;
%put %trim(%str(&param));
Note: Amir's solution of removing the %if statement altogether is also the best way to design code like this. I'm just providing more details as to why you were having this issue.
Additional Explanation 1 - Why %left() and %trim are not needed
The top code snippet provides the same intended functionality as your original code that had the "%left(%trim(&sqlOptions.))". This is because beginning and ending whitespace is dropped from macro variables (including macro parameters) unless it is explicitly retained by using macro quoting. A simple example to show this is:
%let param = lots of spaces ;
%put ***&param***;
Gives:
***lots of spaces***
You can see that the internal whitespace is kept, but the left and right padding are gone. To keep whitespace, we can simply use the %str() function.
%let param = %str( lots of spaces );
%put ***&param***;
Gives:
*** lots of spaces ***
Additional Explanation 2 - Working with macros containing whitespace
If you actually did have whitespace on a macro variable that you needed to remove because it was quoted, and you wanted to use %left() and %trim() to do so, then things get a little wacky. Our variable can be created like so:
%let param = %str( inobs = 2 );
You can see we already have quoted the value with %str() in order to create it. This means we can now call one of the functions without having to quote it again:
%put %trim(&param); * ALREADY QUOTED AT CREATION SO THIS WORKS FINE;
However, if we then try and feed the result into the %left() function we're back to the original issue:
%put %left(%trim(&param)); * OOPS. DOESNT WORK;
Now I'm guessing here but I believe this is most likely because the %trim() function removes any macro quoting prior to returning a result. Kind of like this:
%put %unquote(%trim(&param));
This can be circumvented by re-quoting the returned result using %str() again:
%put %left(%str(%trim(&param)));
... or wrapping the original parameter with a %nrstr():
%let param = %str( inobs = 2 );
%put %left(%trim(%nrstr(&param)));
... or using %sysfunc() to call a datastep function:
%put %sysfunc(compress(&param));