A Practical Guide To Macro Quoting and Working With Macro Variables
A Practical Guide To Macro Quoting and Working With Macro Variables
Paper IS06
ABSTRACT
Macro quoting in SAS changed over the years and new functions w ere added as extensions of the old ones. Having
many different functions to choose from can be confusing, especially w here in many scenarios more than one
function can be used to achieve the same result. Complete macro language w as introduced in SAS version 5. This
paper is a simple guide as to w hich functions are absolutely necessary to know as they cover most real life scenarios,
and w hich ones are less important. It also provides information on w orking w ith quoted macro variables in an efficient
w ay, and on using a data step as a pow erful tool for processing such macro variables.
INTRODUCTION
Whenever macro variables or macros are used there is often a need to mask tokens [Patterson & Remigio 2007]. It
makes sense to use macro functions that w ould mask all of those tokens, as using ones that mask only some creates
a risk that a macro might not w ork properly in some cases. There is also a task of accessing and extracting
information w ithin macro variables that have quoting applied, in a w ay that prevents unmatched tokens from
producing errors or w arnings. Finally, the use of a data step is discussed as a pow erful w ay of w orking w ith macro
variables. This discussion is follow ed by 3 practical examples from day to day w ork.
The use of a data step is discussed as an alternative and pow erful tool to process macro variables in a similar w ay as
any other variable. Specifically the functions that are discussed are:
SYMGET() – used to load a macro variable into a dataset variable
CALL SYMPUTX() – used to create a macro variables in a data step
RESOLVE() – used to unmask tokens w ithin a data step - similar to %UNQUOTE() in macro language.
Outputs from the SAS log w indow are presented in this paper like:
Log output message.
MASKING TOKENS
As described in [Patterson & Remigio 2007] there are 3 types of tokens to mask and 7 macro quoting functions to d o
it. Some of the new functions are extensions of the older ones . This paper primarily focuses on %NRSTR(),
%NRBQUOTE() and %SUPERQ(). Those 3 macro quoting functions w ere introduced to extend the functionality of
older functions. All 7 functions can be summarized as follow s:
%NRSTR() is an extension of %STR()
%NRBQUOTE() is an extension of %QUOTE(), %NRQUOTE(), %BQUOTE()
%SUPERQ() – is unique and its use is required in some scenarios.
1
Before going into more detail, one important thing to remember is that unmatched tokens ‘ “ ( ) need to be masked
w ith % w hen using %NRSTR(), but do not have to be masked this w ay w hen using %NRBQUOTE(), for example:
%NRSTR(This is test%’d)
%NRBQUOTE(This is test’d).
Now let’s look into characteristics of these functions. As they all mask 3 types of tokens, choosing w hich one to use
depends on w hen tokens are to be resolved:
At compilation - %NRSTR()
At execution - %NRBQUOTE()
Never - %SUPERQ().
%macro test(var=);
%if &var=%NRSTR(New & old ‘s ) %then %put %NRSTR(%increase);
%else %put Wrong result;
%mend test;
In this case it is not advisable to use %NRBQUOTE() as it w ould produce a w arning. It should be used w hen the
macro is called, that is w hen it is executed:
The above code produces the desired result. In the above scenario %NRSTR() w ould w ork as w ell. To illustrate the
difference let’s create a macro variable that w ill be passed as a parameter to the macro:
%test(var=%NRBQUOTE(&newvar));
%increase
%test(var=%NRSTR(&newvar));
Wrong result
In this example only %NRBQUOTE() gives desired results as it resolves the macro variable at execution but still
masks tokens inside it, w hile %NRSTR() is masking ‘&new var’ and prevents resolution of a macro trigger ‘&’.
%macro test1(var=);
%if &var=%STR(New & old ‘s) %then %put True;
%mend test1;
%test1(var=%NRBQUOTE(New & old ‘s));
The problem w ith this approach is that during execution errors are printed:
ERROR: A character operand was found in the %EVAL function or %IF
condition where a numeric operand is required. The condition was:
&var=New & old ‘s
ERROR: The macro TEST1 will stop executing.
Finally, here is the same scenario, this time using %QUOTE(). % needs to be added in this case to mask unmatched
tokens:
%macro test2(var=);
%if &var=%NRSTR(New & old ‘s) %then %put True;
%mend test2;
%test2(var=%quote(New & old %‘s));
2
Running the above code results in similar errors as the example using %STR(). Both examples show that w hen
macro triggers need to be masked at compilation and execution, respectively %STR() and %QUOTE() cannot be
used. %NRSTR() and %NRBQUOTE() mask macro triggers and in all other scenarios provide exactly the same
functionality as %STR() and %QUOTE(), therefore it is recommended that %NRSTR() and %NRBQUOTE() are
preferentially used.
%SUPERQ()
In some w ays %SUPERQ() is the simplest function to understand. It masks all tokens w ithin the macro
variable/parameter until they are explicitly unmasked. It’s useful w hen all macro triggers are to be masked until the
user specifically w ants them to be resolved. To illustrate, let’s look at some scenarios.
First let’s create a macro variable w ith macro triggers that can be treated as macro variables or macros:
data _null_;
CALL SYMPUTX('test1','%CI and &x1');
run;
This step is necessary as %SUPERQ() only takes a macro variable as an argument. Let’s also create a simple macro
to print:
%macro test3(var=);
%put &var;
%mend test3;
Now let’s call the macro using all 3 macro quoting functions:
%test3(var=%NRSTR(&test1));
&test1
%test3(var=%NRBQUOTE(&test1));
WARNING: Apparent invocation of macro CI not resolved.
WARNING: Apparent symbolic reference X1 not resolved.
%test3(var=%SUPERQ(test1));
%CI and &x1
%NRBQUOTE() gives w arnings, w hile both %SUPERQ() and %NRSTR() produce results, though those results differ.
As stated above the reason for the difference is that %NRSTR() treats the input function argument &test1 as text. It is
not desirable as the task is not to mask w ord ‘&test1’ but the value of the macro variable &test1. Note that if the value
‘%CI and &x1’ w as to be processed before the macro variable is unmasked, then using %NRSTR() w ould prevent it.
ADDITIONAL NOTES
There are some cases w hen using %NRSTR() is the only option w hile relying on macro language. If there w as a
string to be passed like “text and &test1” then neither %NRBQUOTE nor %SUPERQ() w ould w ork:
%SUPERQ() can only handle a macro variable as an input and if it w as used in the above example, it w ould try to
resolve the w hole string. Caution is advised w hen using %SUPERQ() or %NRSTR() in cases w here the macro
triggers are to be resolved inside the code. The masked macro variables need to be explicitly unmasked by using a
specific code in those cases.
There is also a very interesting scenario w hen using single and double quotes, w hich complicates matters further.
While a practical scenario may be unlikely, consider the below example:
%macro test4(var=);
%put &var;
%put %NRBQUOTE(&var);
%mend test4;
3
%test4(var=%NRSTR(text and &test1)); *Works fine;
%test4(var=%NRSTR('text and &test1')); *Produces warnings;
WARNING: Apparent invocation of macro CI not resolved.
WARNING: Apparent symbolic reference X1 not resolved.
The code w ith a single quotation gives w arnings as single quotes prevent macro quoting from being applied to a
macro variable &test1. Code w ithout %NRBQUOTE w orks as it just displays text, w hile code w ith %NRBQUOTE
resolves during execution, therefore giving w arning.
Firstly, to create a macro variable, the function CALL SYMPUTX should be used. It is an extension to CALL
SYMPUT, w ith tw o functions. Firstly, it left justifies and trims trialing blanks from a numeric value. Secondly , it allow s
user to specify the scope of the created macro variable. Let’s look at some of the characteristics of CALL SYMPUTX:
%let x2=abc;
data _null_;
*This code w orks as both % and & are masked as text.;
CALL SYMPUTX('test1','%CI and &x1');
*As in any case w hen using macro variables in a data step, double quotes w ill cause the macro triggers to
resolve and produce w arnings.;
CALL SYMPUTX('test2',"%CI and &x1");
*To pass both triggers w hile only a macro variable is to be resolved, more complicated code needs to be
used:;
CALL SYMPUTX('test3','%CI and'||"&x2");
On the topic of local and global variables it is very important to remember that if a macro variable exists in multiple
symbol tables, the value stored in the most local one is used. It might then be advisable to use ‘F’ as a third option in
CALL SYMPUTX, to ensure that the variable is created in the most local symbol table.
Another action that can be performed in a data step is to load a macro variable content into a dataset variable by
calling the function SYMGET(). It can then be processed using a data step functionality and afterw ards be output
using CALL SYMPUTX, for example:
It is advisable to specify the lengths of the variables being used to avoid potential truncation.
4
PROCESSING QUOTED VARIABLES WITH TOKENS
When using a data step there is no difference in processing a macro variable w ith or w ithout tokens. A macro variable
is loaded into a dataset variable as a string and can be processed as such, no matter w hat kind of tokens it holds.
In the macro language there are 2 main functions that can be used w hen processing macro variables w ith tokens -
%QSCAN() and %QSUBSTR(). They both allow processing of macro variables that cannot be dealt w ith by using
%SCAN() and %SUBSTR(). Let’s look at examples:
Even though %SCAN() and %SUBSTR() are not producing any immediate w arnings or errors they effectively unmask
the single quotation mark, leaving an unbalanced quote affecting all code that follow s. This problem in the code is not
easy to debug.
As is show n in an example later in the paper, %QSCAN() and %QSUBSTR() do not replace %SCAN() and
%SUBSTR() in all scenarios, so in fact they cannot be regarded as an extension of them.
UNMASKING VARIABLES
Macro variables can be unmasked before any processing is done. This can be achieved both by the use of macro
language or a data step. In macro language the function %UNQUOTE() is used. A simple example illustrates how it
w orks:
data _null_;
CALL SYMPUTX('test1','&x1 and &x2');
run;
%let x1=5;
%let x2=15;
%let new=%SUPERQ(test1);
%put &new;
&x1 and &x2
%put %UNQUOTE(&new);
5 and 15
%let new1=%NRSTR(&test1);
%put &new1;
&test1
%put %UNQUOTE(&new1);
5 and 15
It does not matter w hich function w as used for quoting. Even a macro variable masked w ith %NRSTR() is resolved
correctly.
A similar result can be achieved using a data step. The function RESOLVE() is used. As it processes variable in a
dataset, a macro variable value needs to be first loaded into a dataset variable using SYMGET():
Data _null_;
title = SYMGET('test1');
put title;
title2 = RESOLVE(title);
put title2;
run;
&x1 and &x2
5 and 15
5
ADVANTAGE OF USING DATA_NULL_
Even though in the above example there is no difference betw een macro language and a data step, the follow ing
example show s that using a data step is a much better w ay of processing unmasked macro variables.
When unmasking macro variables caution is advised as all masked tokens get unmasked and have potential to
create unbalanced quotes or an open parenthesis. That is one of the reasons w hy it is advisable to use a data step
instead of macro language. As all unmatched tokens need matching, if a macro variable is unmasked in macro
language it results in either unbalanced quotes or an open parenthesis:
data _null_;
CALL SYMPUTX('test1','% CI and "( . , &x1');
run;
%let x1=abc;
%let new=%SUPERQ(test1);
%put &new;
% CI and "( . , &x1
%put %UNQUOTE(&new);
The last line does not produce a result in the log w indow , because it results in an unbalanced double quote. If such a
string needs to be unmasked, for example if it holds macro variables that need to be resolved, then the follow ing data
step can be used:
Data _null_;
length test test1 $80;
test = SYMGET('new');
put test;
test1 = RESOLVE(test);
put test1;
run;
The above code produces the desired results and the string can be processed w ithin a data step. That w ay potential
problems w ith unbalanced quotes or an open parenthesis are avoided.
PRACTICAL EXAMPLES
EXAMPLE 1: PROCESSING A SERIES OF FOOTNOTES FROM A SINGLE MACRO VARIABLE HOLDING MULTIPLE
FOOTNOTES
Here, a series of footnotes for a report are being passed through a single macro variable. Let’s create such a
variable:
The task in this example is to extract and process the last footnote stored in the macro variable &foot_note. As
footnotes are separated by | and contain all kinds of tokens, it is best to use %QSCAN() function:
%let last=%QSCAN(&foot_note,-1,|);
%put &last;
CI: confidence interval ; XXX:unknown & % ‘; SE: standard error ;
SOC: standard of care
The below code might seem like over programming at first glance, but is in fact necessary. Using only %QSCAN()
might give us easily the first and last footnote, but getting middle ones w ould require extra coding. A cleaner w ay is to
find the position of the last footnote and then using %QSUBSTR(), select the last and all other footnotes:
6
%let pos=%index(&foot_note,&last);
%put &pos;
%let end_notes1=%QSUBSTR(&foot_note,1,&pos-1);
%let end_notes2=%QSUBSTR(&foot_note,&pos); *Duplicated w ith %QSCAN() code above;
%put &end_notes1.;
a: First footnote| b: Second footnote| C: Third footnote|
%put &end_notes2.;
CI: confidence interval ; XXX:unknown & % ‘; SE: standard error ;
SOC: standard of care
From this point &end_notes2 can be processed separately. The takeaw ay point of this example is that a macro
variable that contains all kinds of tokens has been processed successfully. This could not be achieved using
%SCAN() and %SUBSTR().
EXAMPLE 2: PROCESSING A MACRO VARIABLE THAT HOLDS REPORT TITLE IN A DATA STEP
The below example show s how a macro variable can be loaded into a variable and then easily processed using a
data step functions. The task is to process report titles for display in an rtf file. Titles should be displayed in separate
lines and this is achieved by the use of RTF tag ‘\line’:
data _null_;
CALL SYMPUTX('title',"This is title(|Subtitle one'|Second subtitle &");
run;
%let title1=%SUPERQ(title);
%put &title1;
This is title(|Subtitle one'|Second subtitle &
data _null_;
length title $ 3000;
title = SYMGET('title1');
title=tranwrd(title, '|', '\line ');
CALL SYMPUTX('title2',title,'G');
run;
%put %SUPERQ(title2);
This is title(\line Subtitle one'\line Second subtitle &
It is simple to make this transformation in a data step w ithout w orrying about tokens. An additional advantage is that
using a data step is more intuitive than using macro language. Using a data step also makes code more transparent,
especially in complex scenarios.
EXAMPLE 3: CREATING MULTIPLE VARIABLES BASED ON VALUES STORED IN A SINGLE MACRO VARIABLE
As mentioned previously, it is not alw ays possible to replace %SCAN() w ith %QSCAN().The below example involves
creating a new variable w hose name is partly derived from an element in a set of time points stored in a macro
variable. Just to be safe it w ould seem logical to use %QSCAN() just in case any special characters are encountered.
How ever, consider below w hen %SCAN() is used:
data new;
rate1_scan_%scan(&time_points.,2)=1;
run;
7
This code runs correctly. Now , let’s use %QSCAN() function instead:
data new;
rate1_QSCAN_%QSCAN(&time_points.,2)=1;
run;
The above code may seem correct, but an error is generated because the masking placed on the value selected by
%QSCAN() is causing the string to be tokenized incorrectly. This can be corrected by using the %UNQUOTE
function. This effectively means that %QSCAN() on its ow n cannot replace %SCAN() in this case.
The follow ing code runs correctly and produces the same result as %SCAN():
data new;
rate1_QSCANu_%UNQUOTE(%QSCAN(&time_points.,2))=1;
run;
%let time_points=3456;
data new;
rate1_scan_%subSTR(&time_points.,3,1)=1;
rate1_QSCAN_%QSUBSTR(&time_points.,3,1)=1;
output;
run;
It gives a similar error as above. This kind of scenario is unlikely to occur in normal use but illustrates issues that
might be encountered w hen using %QSUBSTR().
SUMMARY
Below are the pros and cons of the methods and functions discussed in this paper. It is good to be aw are of those:
8
Method Advantages Disadvantages/Lim itations
Data step functions
SYMGET() Loads a macro variable into a It usually requires assigning the length of the dataset
dataset variable. variable, in w hich the value of a macro variable is loaded.
This is to avoid potential truncation w hen the dataset
variable is processed.
CALL Allow s easy creation of a number Macro variables can be created from variables values in a
SYMPUTX() of macro variables. data set. It is convenient in some cases but it is not easily
Allow s both global and local visible w hat macro variables are created and w hat are
variables to be created. their values.
RESOLVE() Best function to resolve macro Cannot be directly used on a macro variable – requires
variables as it masks all tokens SYMGET() to be used first.
and resolves macro variables and
macros.
CONCLUSION
Working w ith macro variables can be difficult, but it can be simplified by the use of the new est SAS programming
framew ork. It is advisable to use the new est macro language functions %NRSTR(), %NRBQUOTE() and SUPERQ().
It is important to understand in detail w hat the differences betw een those three functions are. Don’t forget that macro
variables can also be read into a data step and be processed in the same w ay as any other dataset variables. Use of
a data step is recommended w hen macro variables require extensive processing, as it decreases complexity of
programming and improves code transparency.
REFERENCES
Brian Patterson, Mylene Remigio, “Don’t %QUOTE() Me on This: A Practical Guide to Macro Quoting Functions ”,
SAS Global Forum 2007,
http://w w w2.sas.com/proceedings/forum2007/152-2007.pdf
RTF Tags
https://w ww.microsoft.com/en-gb/dow nload/details.aspx?id=10725
ACKNOWLEDGMENTS
The author w ould like to express appreciation to Carl Herremans and Frederic Coppin for his input during the
preparation of this paper.
RECOMMENDED READING
Chang Y. Chung, John King, ‘IS THIS MACRO PARAMETER BLANK?’, SAS Global Forum 2009
http://changchung.com/dow nload/022-2009.pdf
Beilei Xu, Lei Zhang, ‘Take and In-Depth Look at the %EVAL Function’ NESUG 17.
http://w w w.lexjansen.com/nesug/nesug04/pm/pm26.pdf
CONTACT INFORMATION
Your comments and questions are valued and encouraged. Contact the author at:
Daw id Militow ski
HTA Statistical Programmer
MSD
Hertford Rd, Hoddesdon, EN11 9BU, United Kingdom
Work Phone: +44 1992 452 730
Email: daw id.militow [email protected]
Web: http://w w w.msd.com
SAS and all other SAS Institute Inc. product or service names are registered trademarks or trademarks of SAS
Institute Inc. in the USA and other countries. ® indicates USA registration.