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

Is there a way to reference a non-existing macro variable in a parameter of a macro call without getting a warning of a non-declared macro variable?

 

E.g. %report (footnotes = &line | footnote1 | footnote2, ...).

 

I have to use a standard macro which declares line as global and sets it according to the linesize value. I can't declare it as global before calling the macro, then it would be resolved in the call directly, and the value set in the macro is not reflected. Note that the macro works correctly, because &line is not resoved in the call but only later after line is defined within the macro and the parameter footnotes is used after.

 

I see 2 possible solutions to the problem:

1. somehow preventing &line to be resolved during the call but enabling later resolution. Quoting does not help here because I can't change the macro to add an %unquote call.

2. finding a macro macro option similar to the system option var_init_chk = nonote, but I didn't find something like that.

1 ACCEPTED SOLUTION

Accepted Solutions
Tom
Super User Tom
Super User

If you really really need to stick &LINE into your call then here is a trick that does it.

&&%upcase(line)

Example:

%macro test(x);
%let line=Set later;
%put ACTUAL X=%superq(x);
%put EFFECTIVE &=x;
%mend test;
%let line=Set earlier;
%test(x=&line);
%test(x=&&%upcase(line));

View solution in original post

12 REPLIES 12
Astounding
PROC Star

I don't know of such an option, but ...

 

Couldn't you just remove &LINE from the macro call, and change the interior logic of the macro to add it back in once the value has been set?  If you are really not allowed to fiddle with %REPORT at all, perhaps you could construct:

 

%macro report2 (footnotes_without_line=);

   %report (footnotes=&footnotes_without_line)

   %* Additional logic to add back in the value of &LINE;

%mend report2;

 

However, that may not help since %REPORT finishes before you get to adjust the value of &FOOTNOTES.

HWSteinberg
Calcite | Level 5
Thanks for your suggestion. Indeed I have a workaround, the standard macro
only provides the footnote statements, and using SASHELP.VTITLE I move the
footnotes 1 number up after putting &line into footnote1 with an additional
macro. The output is then correctly created. My concern is to avoid both the
additional macro and the WARNING.
Kurt_Bremser
Super User

Use a wrapper macro to detect the presence of a macro variable with %symexist:

%macro checkvar(varname);
%if %symexist(&varname) %then &&varname; %else %str( );
%mend;

title "%checkvar(xxx)";

proc print data=sashelp.class (obs=1) noobs;
run;

%let xxx=this is a test;

title "%checkvar(xxx)";

proc print data=sashelp.class (obs=1) noobs;
run;

%symdel xxx;

The first proc print has an empty title, no WARNINGs in the log.

HWSteinberg
Calcite | Level 5
I know that the macro variable

1.) does not exist

2.) is not allowed to exist before the macro call because it would possibly
have a wrong value to which it is resolved too early.

3.) is created and declared as global within the macro

4.) is correctly set in the macro and resolved afterwards correctly when
using the footnotes parameter


Tom
Super User Tom
Super User

I don't understand what the problem is.

It sounds like you want to conditionally update the value of the FOOTNOTES macro variable to contain the value of the LINE macro variable.

%macro report (footnotes .... );
%if %symexist(line) %then footnotes=&line|&footnotes;
....
%mend;

%report(footnotes=footnote1 | footnote2, ...).

 

Tom
Super User Tom
Super User

If you really really need to stick &LINE into your call then here is a trick that does it.

&&%upcase(line)

Example:

%macro test(x);
%let line=Set later;
%put ACTUAL X=%superq(x);
%put EFFECTIVE &=x;
%mend test;
%let line=Set earlier;
%test(x=&line);
%test(x=&&%upcase(line));
HWSteinberg
Calcite | Level 5
Thanks for your reply. I'm not quite sure if I understand you correctly. I
think your macro test stands for my macro report. But I can't change this
standard macro of my client. To achieve your result, I understand that I
would have to introduce the %superq function in the macro which I can't do.
But your values "SET EARLIER" and "SET LATER" bring me to another idea: I
can omit &line from the footnotes parameter in a first call of my macro,
coming out of the macro, line is correctly globally defined and I call the
macro a 2nd time with &line in the call. Fortunately the macro does not
produce any external output, and it's no harm to run it twice.

So thanks again

Hans
HWSteinberg
Calcite | Level 5

OK I now see &&%upcase(line) is the trick to hide line as a macro variable and, after resolving && to & in the call of macro test, within the macro x=&LINE is available where &LINE is not yet resolved. &&%substr(line,1) has the same effect.

 

Great!

HWSteinberg
Calcite | Level 5

Me again: the program works fine, but why? Is there a restriction when resolving parameters in a macro call, that a value is passed only once? In open code, &&%upcase(line) is resolved in a first pass to &LINE, in a second pass then to the value of the macro variable line. Is this second pass omitted when &&%upcase(line) is the value of a parameter in a macro call? I don't find any documentation on that.

 

2nd question: why results

%test(x=&%upcase(line));

in:

ACTUAL X=&%upcase(line)
EFFECTIVE X=&%upcase(line)

I'd think in the first pass & is kept, %upcase(line) is evaluated to LINE, together &LINE, and without 2nd pass giving the same as with 2 ampersands where in the 1st pass && is resolved to & and %upcase(line) to LINE:

ACTUAL X=&LINE
EFFECTIVE X=Set later

 

Tom
Super User Tom
Super User

It is not the macro that is the issue really.  The issue is that you need to get the & into the value of the macro variable (every macro parameter is a local macro variable in the macro's symbol space) without it being macro quoted so that later when the macro processor resolves the value the & will trigger the macro processor to resolve the reference. You will have similar issues trying to force an & into the value of macro variable when using the %LET statement also.

 

The easiest way to get un protected & and % into macro variables is to use SAS code (instead of macro code) to set the macro variable value.  For example you can use CALL SYMPUTX() function in a data step or the INTO keyword in PROC SQL code.  There you can use single quotes around the string to prevent the macro processor from evaluating the & reference too soon.  But the single quotes are not actually part of the value like they would be in macro code.

 

data _null_;
  call symputx('mymvar','This will resolve reference to LINE later. LINE=&line.');
run;
proc sql noprint;
  select 'This will resolve reference to LINE later. LINE=&line.'
    into :mymvar
    from sashelp.class(obs=1)
  ;
quit;

 

HWSteinberg
Calcite | Level 5

All now works fine, my program does what I want it to do. But I'm still wondering why &%upcase(line) does not work but &&%upcase(line) works as posted on 04-27-2019 08:18 AM, just for my understanding.

Tom
Super User Tom
Super User

@HWSteinberg wrote:

All now works fine, my program does what I want it to do. But I'm still wondering why &%upcase(line) does not work but &&%upcase(line) works as posted on 04-27-2019 08:18 AM, just for my understanding.


Not sure WHY it doesn't work. But HOW it doesn't work is that the value &LINE is not put into the macro variable.  Instead the value &%upcase(line) is put into the macro variable.

1719  %let one=&%upcase(line);
1720  %let two=&&%upcase(line);
1721  %put one=%superq(one);
one=&%upcase(line)
1722  %put two=%superq(two);
two=&LINE

I suspect something about how it is breaking the string into tokens.  So with one & it treats it as one token without any macro code to resolve. With two & it sees it as two tokens. The first converts to & and the stops and the second converts to LINE.

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
  • 12 replies
  • 1377 views
  • 1 like
  • 4 in conversation