Russ Tyndall

5月 172019
 
Did you know that you can run Lua code within Base SAS? This functionality has been available since the SAS® 9.4M3 (TS1M3) release. With the LUA procedure, you can submit Lua statements from an external Lua script or just submit the Lua statements using SAS code. In this blog, I will discuss what PROC LUA can do as well as show some examples. I will also talk about a package that provides a Lua interface to SAS® Cloud Analytic Services (CAS).

What Is Lua?

Lua is a lightweight, embeddable scripting language. You can use it in many different applications from gaming to web applications. You might already have written Lua code that you would like to run within SAS, and PROC LUA enables you to do so.
With PROC LUA, you can perform these tasks:

  • run Lua code within a SAS session
  • call most SAS functions within Lua statements
  • call functions that are created using the FCMP procedure within Lua statements
  • submit SAS code from Lua
  • Call CAS actions

PROC LUA Examples

Here is a look at the basic syntax for PROC LUA:

proc lua <infile='file-name'> <restart> <terminate>;

Suppose you have a file called my_lua.lua or my_lua.luc that contains Lua statements, and it is in a directory called /local/lua_scripts. You would like to run those Lua statements within a SAS session. You can use PROC LUA along with the INFILE= option and specify the file name that identifies the Lua source file (in this case, it is my_lua). The Lua file name within your directory must contain the .lua or. luc extension, but do not include the extension within the file name for the INFILE= option. A FILENAME statement must be specified with a LUAPATH fileref that points to the location of the Lua file. Then include the Lua file name for the INFILE= option, as shown here:

filename luapath '/local/lua_scripts';
proc lua infile='my_lua';

This example executes the Lua statements contained within the file my_lua.lua or my_lua.luc from the /local/lua_scripts directory.

If there are multiple directories that contain Lua scripts, you can list them all in one FILENAME statement:

filename luapath ('directory1', 'directory2', 'directory3');

The RESTART option resets the state of Lua code submissions for a SAS session. The TERMINATE option stops maintaining the Lua code state in memory and terminates the Lua state when PROC LUA completes.

The syntax above discusses how to run an external Lua script, but you can also run Lua statements directly in SAS code.

Here are a couple of examples that show how to use Lua statements directly inside PROC LUA:

Example 1

   proc lua; 
   submit; 
      local names= {'Mickey', 'Donald', 'Goofy', 'Minnie'} 
      for i,v in ipairs(names) do 
         print(v) 
   end 
   endsubmit; 
   run;

Here is the log output from Example 1:

NOTE: Lua initialized.
Mickey
Donald
Goofy
Minnie
NOTE: PROCEDURE LUA used (Total process time):
      real time           0.38 seconds
      cpu time            0.10 seconds

Example 2

   proc lua;
   submit;
      dirpath=sas.io.assign("c:\\test")
      dir=dirpath:opendir()
      if dir:has("script.txt") then print ("exists")
      else print("doesn't exist")
      end
   endsubmit;
   run;

Example 2 checks to see whether an external file called script.txt exists in the c:\test directory. Notice that two slashes are needed to specify the backslash in the directory path. One backslash would represent an escape character.

All Lua code must be contained between the SUBMIT and ENDSUBMIT statements.

You can also submit SAS code within PROC LUA by calling the SAS.SUBMIT function. The SAS code must be contained within [[ and ]] brackets. Here is an example:

   proc lua; 
   submit;
      sas.submit [[proc print data=sashelp.cars; run; ]]
   endsubmit;
   run;

Using a Lua Interface with CAS

Available to download is a package called SWAT, which stands for SAS Scripting Wrapper for Analytics Transfer. This is a Lua interface for CAS. After you download this package, you can load data into memory and apply CAS actions to transform, summarize, model, and score your data.

The package can be downloaded from this Downloads page: SAS Lua Client Interface for Viya. After you download the SWAT package, there are some requirements for the client machine to use Lua with CAS:

  1. You must use a 64-bit version of either Lua 5.2 or Lua 5.3 on Linux.

    Note: If your deployment requires newer Lua binaries, visit http://luabinaries.sourceforge.net/.
    Note: Some Linux distributions do not include the required shared library libnuma.so.1. It can be installed with the numactl package supplied by your distribution's package manager.

  2. You must install the third-party package dependencies middleclass (4.0+), csv, and ee5_base64, which are all included with a SAS® Viya® installation.

For more information about configuration, see the Readme file that is included with the SWAT download.

I hope this blog post has helped you understand the possible ways of using Lua with SAS. If you have other SAS issues that you would like me to cover in future blog posts, please comment below.

To learn more about PROC LUA, check out these resources:

Using the Lua programming language within Base SAS® was published on SAS Users.

4月 212018
 

Have you ever been working in the macro facility and needed a macro function, but you could not locate one that would achieve your task? With the %SYSFUNC macro function, you can access most SAS® functions. In this blog post, I demonstrate how %SYSFUNC can help in your programming needs when a macro function might not exist. I also illustrate the formatting feature that is built in to %SYSFUNC. %SYSFUNC also has a counterpart called %QSYSFUNC that masks the returned value, in case special characters are returned.
%SYSFUNC enables the execution of SAS functions and user-written functions, such as those created with the FCMP procedure. Within the DATA step, arguments to the functions require quotation marks, but because %SYSFUNC is a macro function, you do not enclose the arguments in quotation marks. The examples here demonstrate this.

%SYSFUNC has two possible arguments. The first argument is the SAS function, and the second argument (which is optional) is the format to be applied to the value returned from the function. Suppose you had a report and within the title you wanted to issue today’s date in word format:

   title "Today is %sysfunc(today(),worddate20.)";

The title appears like this:

   "Today is               July 4, 2018"

Because the date is right-justified, there are leading blanks before the date. In this case, you need to introduce another function to remove the blank spaces. Luckily %SYSFUNC enables the nesting of functions, but each function that you use must have its own associated %SYSFUNC. You can rewrite the above example by adding the STRIP function to remove any leading or trailing blanks in the value:

   title "Today is %sysfunc(strip(%sysfunc(today(),worddate20.)))";

The title now appears like this:

    "Today is July 4, 2018"

The important thing to notice is the use of two separate functions. Each function is contained within its own %SYSFUNC.

Suppose you had a macro variable that contained blank spaces and you wanted to remove them. There is no macro COMPRESS function that removes all blanks. However, with %SYSFUNC, you have access to one. Here is an example:

   %let list=a    b    c; 
   %put %sysfunc(compress(&list));

The value that is written to the log is as follows:

   abc

In this last example, I use %SYSFUNC to work with SAS functions where macro functions do not exist.

The example checks to see whether an external file is empty. It uses the following SAS functions: FILEEXIST, FILENAME, FOPEN, FREAD, FGET, and FCLOSE. There are other ways to accomplish this task, but this example illustrates the use of SAS functions within %SYSFUNC.

   %macro test(outf);
   %let filrf=myfile;
 
   /* The FILEEXIST function returns a 1 if the file exists; else, a 0
   is returned. The macro variable &OUTF resolves to the filename
   that is passed into the macro. This function is used to determine
   whether the file exists. In this case you want to find the file
   that is contained within &OUTF. Notice that there are no quotation
   marks around the argument, as you will see in all cases below. If
   the condition is false, the %ELSE portion is executed, and a
   message is written to the log stating that the file does not
   exist.*/
 
   %if %sysfunc(fileexist(&outf)) %then %do;
 
   /* The FILENAME function returns 0 if the operation was successful; 
   else, a nonzero is returned. This function can assign a fileref
   for the external file that is located in the &OUTF macro 
   variable. */
 
   %let rc=%sysfunc(filename(filrf,&outf));
 
   /* The FOPEN function returns 0 if the file could not be opened; 
   else, a nonzero is returned. This function is used to open the
   external file that is associated with the fileref from &FILRF. */
 
   %let fid=%sysfunc(fopen(&filrf));
 
   /* The %IF macro checks to see whether &FID has a value greater
   than zero, which means that the file opened successfully. If the
   condition is true, we begin to read the data in the file. */
 
   %if &fid > 0 %then %do;
 
   /* The FREAD function returns 0 if the read was successful; else, a
   nonzero is returned. This function is used to read a record from
   the file that is contained within &FID. */
 
   %let rc=%sysfunc(fread(&fid));
 
   /* The FGET function returns a 0 if the operation was successful. A
   returned value of -1 is issued if there are no more records
   available. This function is used to copy data from the file data 
   buffer and place it into the macro variable, specified as the
   second argument in the function. In this case, the macro variable
   is MYSTRING. */   
 
   %let rc=%sysfunc(fget(&fid,mystring));
 
   /* If the read was successful, the log will write out the value
   that is contained within &MYSTRING. If nothing is returned, the
   %ELSE portion is executed. */
 
   %if &rc = 0 %then %put &mystring;
   %else %put file is empty;
 
   /* The FCLOSE function returns a 0 if the operation was successful;
   else, a nonzero value is returned. This function is used to close
   the file that was referenced in the FOPEN function. */
 
   %let rc=%sysfunc(fclose(&fid));
   %end;
 
   /* The FILENAME function is used here to deassign the fileref 
   FILRF. */
 
   %let rc=%sysfunc(filename(filrf));
   %end;
   %else %put file does not exist;
   %mend test;
   %test(c:\testfile.txt)

There are times when the value that is returned from the function used with %SYSFUNC contains special characters. Those characters then need to be masked. This can be done easily by using %SYSFUNC’s counterpart, %QSYSFUNC. Suppose we run the following example:

   %macro test(dte);
   %put &dte;
   %mend test;
 
   %test(%sysfunc(today(), worddate20.))

The above code would generate an error in the log, similar to the following:

   1  %macro test(dte);
   2  %put &dte;
   3  %mend test;
   4
   5  %test(%sysfunc(today(), worddate20.))
   MLOGIC(TEST):  Beginning execution.
   MLOGIC(TEST):  Parameter DTE has value July 20
   ERROR: More positional parameters found than defined.
   MLOGIC(TEST):  Ending execution.

The WORDDATE format would return the value like this: July 20, 2017. The comma, to a parameter list, represents a delimiter, so this macro call is pushing two positional parameters. However, the definition contains only one positional parameter. Therefore, an error is generated. To correct this problem, you can rewrite the macro invocation in the following way:

   %test(%qsysfunc(today(), worddate20.))

The %QSYSFUNC macro function masks the comma in the returned value so that it is seen as text rather than as a delimiter.

For a list of the functions that are not available with %SYSFUNC, see the “How to expand the number of available SAS functions within the macro language was published on SAS Users.

6月 162017
 

Using parameters within the macro facilityHave you ever written a macro and wondered if there was an easy way to pass values to the macro? You can by using macro parameters. Macro parameters enable you to pass values into the macro at macro invocation, and set default values for macro variables within the macro definition. In this blog post, I also discuss how you can pass in a varying number of parameter values.

There are two types of macro parameters: positional and keyword.

Positional Parameters

You can use positional parameters to assign values based on their position in the macro definition and at invocation. The order that you use to specify the values must match the order in which they are listed in the %MACRO statement. When specifying multiple positional parameters, use a comma to separate the parameters. If you do not pass a value to the macro when it is invoked, a null value is assigned to the macro variable specified in the %MACRO statement.

Here is an example:

%macro test(var1,var2,var3);                                                                                                            
 %put &=var1;                                                                                                                           
 %put &=var2;                                                                                                                           
 %put &=var3;                                                                                                                           
%mend test;                                                                                                                             
 
/** Each value corresponds to the position of each variable in the definition. **/ 
/** Here, I am passing numeric values.                                         **/                                                            
%test(1,2,3)                                                                                                                            
/** The first position matches with var1 and is given a null value.            **/                                                             
%test(,2,3)                                                                                                                             
/** I pass no values, so var1-var3 are created with null values.               **/                                                             
%test()                                                                                                                                 
/** The first value contains a comma, so I use %STR to mask the comma.         **/                                                             
/** Otherwise, I would receive an error similar to this: ERROR: More           **/
/** positional parameters found than defined.                                  **/                                                             
%test(%str(1,1.1),2,3)                                                                                                                  
/** Each value corresponds to the position of each variable in the definition. **/ 
/** Here, I am passing character values.                                       **/                                                            
%test(a,b,c) 
/** I gave the first (var1) and second (var2) positions a value of             **/
/** b and c, so var3 is left with a null value.                                **/                                                             
%test(b,c)

 

Here are the log results:

173  /** Each value corresponds to the position of each variable in the definition. **/
174  /** Here, I am passing numeric values.                                         **/
175  %test(1,2,3)
VAR1=1
VAR2=2
VAR3=3
176  /** The first position matches with var1 and is given a null value.            **/                                                             
177  %test(,2,3)
VAR1=
VAR2=2
VAR3=3
 
178  /** I pass no values, so var1-var3 are created with null values.               **/
179  %test()
VAR1=
VAR2=
VAR3=
180  /** The first value contains a comma, so I use %STR to mask the comma.         **/                                                             
181  /** Otherwise, I would receive an error similar to this: ERROR: More           **/
182  /** positional parameters found than defined.                                  **/                                                             
183  %test(%str(1,1.1),2,3)
VAR1=1,1.1
VAR2=2
VAR3=3
184  /** Each value corresponds to the position of each variable in the definition. **/
185  /** Here, I am passing character values.                                       **/
186  %test(a,b,c)
VAR1=a
VAR2=b
VAR3=c
187  /** I gave the first (var1) and second (var2) positions a value of             **/
188  /** b and c, so var3 is left with a null value.                               **/
189  %test(b,c)
VAR1=b
VAR2=c
VAR3=

 

Keyword Parameters

The benefit of using keyword parameters is the ability to give the macro variables a default value within the macro definition. When you assign values using keyword parameters, you must include an equal sign after the macro variable name.

Here is an example:

%macro test(color=blue,id=123);                                                                                                         
 %put &=color;                                                                                                                          
 %put &=id;                                                                                                                             
%mend test;                                                                                                                             
 
/** Values passed to the macro overwrite default values from the definition. **/                                                                 
%test(color=red,id=456)                                                                                                                 
/** Passing in no values allows the default values to take precedence.      **/                                                                 
%test()                                                                                                                                 
/** You are not required to pass in a value for each keyword parameter.    **/                                                                 
%test(color=green)                                                                                                                      
/** The order of variables does not matter.                               **/                                                                                                 
%test(id=789,color=yellow)

 

Here are the log results:

270  /** Values passed to the macro overwrite default values from the definition. **/
271  %test(color=red,id=456)
COLOR=red
ID=456
272  /** Passing in no values allows the default values to take precedence.     **/
273  %test()
COLOR=blue
ID=123
274  /** You are not required to pass in a value for each keyword parameter.   **/
275  %test(color=green)
COLOR=green
ID=123
276  /** The order of variables does not matter.                              **/
277  %test(id=789,color=yellow)
COLOR=yellow
ID=789

 

If the macro definition combines positional and keyword parameters, positional parameters must come first. If you do not follow this order, this error is generated:

ERROR: All positional parameters must precede keyword parameters.

 

Here is an example:

%macro test(val,color=blue,id=123);                                                                                                     
 %put &=color;                                                                                                                          
 %put &=id;                                                                                                                             
 %put &=val;                                                                                                                            
%mend test;                                                                                                                             
 
/** The positional parameter is listed first. **/                                                                 
%test(1,color=red,id=456)
 
Here are the log results:
 
318  /** The positional parameter is listed first. **/                                                                 319  %test(1,color=red,id=456)
COLOR=red
ID=456
VAL=1

 

PARMBUFF

The PARMBUFF option creates a macro variable called &SYSPBUFF that contains the entire list of parameter values, including the parentheses. This enables you to pass in a varying number of parameter values. In the following example, you can pass any number of parameter values to the macro. This following example illustrates how to parse each word in the parameter list:

%macro makes/parmbuff; 
  /** The COUNTW function counts the number of words within &SYSPBUFF.            **/                                                                                                                 
   %let cnt=%sysfunc(countw(&syspbuff)); 
  /** The %DO loop increments based on the number of words returned to the macro. **/
  /** variable &CNT.                                                              **/                                
   %do i= 1 %to &cnt;  
  /** The %SCAN function extracts each word from &SYSPBUFF.                      **/                                                                                                                  
     %let make=%scan(&syspbuff,&i);                                                                                                     
     %put &make;                                                                                                                        
   %end;                                                                                                                                
%mend makes;                                                                                                                            
 
%makes(toyota,ford,chevy)

 

Here are the log results:

19  %macro makes/parmbuff;
20    /** The COUNTW function counts the number of words within &SYSPBUFF.            **/
21     %let cnt=%sysfunc(countw(&syspbuff));
22    /** The %DO loop increments based on the number of words returned to the macro  **/
23    /** variable &CNT.                                                              **/
24     %do i= 1 %to &cnt;
25    /** The %SCAN function extracts each word from &SYSPBUFF.                       **/
26       %let make=%scan(&syspbuff,&i);
27       %put &make;
28     %end;
29  %mend makes;
30
31  %makes(toyota,ford,chevy)
toyota
ford
chevy

 

When you specify the PARMBUFF option and the macro definition includes both positional and keyword parameters, the parameters still receive values when you invoke the macro. In this scenario, the entire invocation list of values is assigned to &SYSPBUFF. Here is an example:

%macro test(b,a=300)/parmbuff;                                                                                                      
 %put &=syspbuff;                                                                                                                        
 %put _local_;                                                                                                                          
%mend;                                                                                                                                  
 
%test(200,a=100)

 

Here are the log results:

SYSPBUFF=(200,a=100)
TEST A 100
TEST B 200

 

Notice that &SYSPBUFF includes the entire parameter list (including the parentheses), but each individual parameter still receives its own value.

If you need to know all the parameter values that are passed to the macro, specify the PARMBUFF option in the macro definition to get access to &SYSPBUFF, which contains all the parameter values. For more information about PARMBUFF, see %MACRO Statement in SAS® 9.4 Macro Language: Reference, Fifth Edition.

I hope this blog post has helped you understand how to pass values to a macro. If you have SAS macro questions that you would like me to cover in future blog posts, please comment below.

Using parameters within the macro facility was published on SAS Users.

10月 212016
 

ProblemSolversHave you ever needed to run code based on the client application that you are using? Or have you needed to know the version of SAS® software that you are running and the operating system that you are running it on? This blog post describes a few automatic macro variables that can help with gathering this information.

Application Name

You can use the &_CLIENTAPP macro variable to obtain the name of the client application. Here are some details:

  • Referencing &_CLIENTAPP in SAS® Studio returns a value of SAS Studio
  • Referencing &_CLIENTAPP in SAS® Enterprise Guide® returns a value of ‘SAS Enterprise Guide
    Note: The quotation marks around SAS Enterprise Guide are part of the value.

Program Name

You can use the &SYSPROCESSNAME macro variable to obtain the name of the current SAS process. Here are some details:

  • Referencing &SYSPROCESSNAME interactively within the DMS window returns a value of DMS Process
  • Referencing &SYSPROCESSNAME in the SAS windowing environment of your second SAS session returns a value of DMS Process (2)
  • Referencing &SYSPROCESSNAME in SAS Enterprise Guide or SAS Studio returns a value of Object Server
  • Referencing &SYSPROCESSNAME in batch returns the word Program followed by the name of the program being run (for example: Program 'c:test.sas')
    Note: For information about other techniques for retrieving the program name, see SAS Note 24301: “How to retrieve the program name that is currently running in batch mode or interactively.”

Example

The following code illustrates how you can use both of these macro variables to check which client application you are using and display a message in the SAS log based on that result:

%macro check;
 
  %if %symexist(_clientapp) %then %do;
   %if &amp;_clientapp = SAS Studio %then %do;
    %put Running SAS Studio;
   %end;
   %else %if &amp;_clientapp= 'SAS Enterprise Guide' %then %do;
    %put Running SAS Enterprise Guide; 
   %end;
  %end;
 
  %else %if %index(&amp;sysprocessname,DMS) %then %do;
    %put Running in Display Manager;
  %end;
  %else %if %index(&amp;sysprocessname,Program) %then %do;
     %let prog=%qscan(%superq(sysprocessname),2,%str( ));
     %put Running in batch and the program running is &amp;prog;
  %end;
 
  %mend check;
 %check

SAS Session Run Mode or Server Type

Another helpful SAS read-only automatic macro variable is &SYSPROCESSMODE. You can use &SYSPROCESSMODE to obtain the current SAS session run mode or server type name. Here is a list of possible values:

• SAS Batch Mode

• SAS/CONNECT Session 

• SAS DMS Session

• SAS IntrNet Server

• SAS Line Mode

• SAS Metadata Server

• SAS OLAP Server

• SAS Pooled Workspace Server

• SAS Share Server

• SAS Stored Process Server

• SAS Table Server

• SAS Workspace Server

Operating System and Version of SAS

Having the information detailed above is helpful, but you might also need to know the operating system and exact version of SAS that you are running. The following macro variables help with obtaining this information.

You can use &SYSSCP and &SYSSCPL to obtain an abbreviation of the name of your operating system.  Here are some examples:

macrovariables

For a complete list of values, see the “SYSSCP and SYSSCPL Automatic Macro Variables” section of SAS® 9.4 Macro Language: Reference, Fourth Edition.

SAS Release

&SYSVLONG4 is the most informative of the macro variables that provide SAS release information. You can use it to obtain the release number and maintenance level of SAS as well as a four-digit year. Here is an example:

%put &amp;sysvlong4;

This code would print something similar to the following in the log:

9.04.01M3D06292015

Here is what this output means:

SAS release: 9.04.01

Maintenance level: M3

Ship Event date: D06292015

I hope that some of the tools described above are useful to you for obtaining information about your SAS environment. If you have any questions, please contact SAS Technical Support, and we will be happy to assist you. Thank you for using SAS!

tags: macro, Problem Solvers, SAS Macro, SAS Programmers

Macro variables that provide information about your SAS® environment was published on SAS Users.

4月 222016
 

ProblemSolversThe DS2 programming language gives you the following powerful capabilities:

  • The precision that results from using the new supported data types
  • Access to the new expressions, write methods, and packages available in the DS2 syntax
  • Ability to execute SAS Federated Query Language (FedSQL) from within the DS2 program
  • Ability to execute code outside of a SAS session such as on SAS® High-Performance Analytics Server or the SAS® Federation Server
  • Access to the threaded processing in products such as the SAS® In-Database Code Accelerator, SAS High-Performance Analytics Server, and SAS® Enterprise Miner™

Some DATA step functionality is not available in DS2, at least not how you are used to. However, don’t lose hope, because this article discusses ways to mimic some of the missing DATA step features within DS2.

Simulate Missing SET Statement Data Set Options

Many of the SET statement options are not allowed within DS2, such as the OBS= data set option. However, you can simulate some of these options by using FedSQL. For example, you can use a LIMIT clause similar to the following in the place of OBS=:

{select * from work.temp limit 10}.

Here is an example:

data one;                                                                                                                               
   do i = 1 to 100;                                                                                                                       
      output;                                                                                                                               
   end;                                                                                                                                   
run;                                                                                                                                    
                                                                                                                                        
proc ds2;                                                                                                                               
data new(overwrite=yes);                                                                                                               
   method run();                                                                                                                         
      set {select * from work.one limit 10};                                                                                               
   end;                                                                                                                                  
enddata;                                                                                                                               
run;                                                                                                                                    
quit;                                                                                                                                   
                                                                                                                                        
proc print data=new;                                                                                                                    
run;    

 

Prevent Errors from Duplicate Data Set Names

In a DATA step, an automatic overwrite occurs when you issue a DATA statement that contains a data set name that was used previously in your SAS session.

For example, in the DATA step, you can add the following code:

data one;
   x=100;
run;
data one;
   x=200;
run;

This code overwrites the previous ONE data set. However, this automatic overwrite does not occur within DS2, and an error is generated if a specified data set name already exists. To work around this problem, you can use the OVERWRITE option as shown below.
proc ds2;
data one(overwrite=yes);
   dcl double x;
   method init();
      x=100;
   end;
run;
quit;

Specify Name Literals

In a DATA step, you can use name literals. However, in DS2, they are specified differently.

In Base SAS® with the VALIDVARNAME system option set to ANY, you can use a name literal like the following:

'My var'n=100;

This strategy does not work in DS2, but you can use double quotation marks to get the same results:

"My var"=100;

Substitute Missing Statements

The ATTRIB, LABEL, LENGTH, FORMAT, and INFORMAT statements are missing from DS2. However, you can use the DECLARE statement with the HAVING clause to perform these functions.

Here is an example:

dcl double aa having
   label 'var aa'
   format comma8.2;

Create an Array

In Base SAS, you use the ARRAY statement in a DATA step to create an array to reference variables within the program data vector (PDV). Here is an example:

data one;                                                                                                                               
   array list(4) x1-x4;                                                                                                                  
   do i = 1 to 4;                                                                                                               
      list(i)=i;                                                                                                                          
   end;                                                                                                                                 
run;

The ARRAY statement does not exist in DS2, but the following code shows how to use an equivalent statement called VARARRAY:
proc ds2;                                                                                                                               
data one(overwrite=yes);                                                                                                               
   vararray double x[4];                                                                                                                 
   declare double i;                                                                                                                     
   method init();                                                                                                                       
   do i = 1 to 4;                                                             
      x[i]=i;                                                                                                                            
   end;                                                                                                                                
   end;                                                                                                                                 
enddata;                                                                                                                               
run;                                                                                                                                    
quit;  

Note: The VARARRAY statement must be outside the METHOD statement.

Enable Macro Variable Resolution

To reference a macro variable as a character value in the DATA step, you place double quotation marks around the macro variable as shown in the following example:

%let val=This is a test;
data _null_;
   dval=”&amp;val”;
   put dval=;
run;

In DS2, double quotation marks are used only to delimit an identifier. Single quotation marks are required to delimit constants. If the above code was run within DS2, a warning similar to the following would occur:

Solutions for missing DATA step features within DS2

To get a better understanding of the difference between an identifier and constant text, consider the following two examples:

VARA=’test’;
VARB=”vara”;

Within DS2, the first assignment statement creates a variable called VARA and assigns it a text string of test. The second assignment statement creates a variable called VARB and also assigns it a text string of test. Since the second assignment statement is using double quotation marks, vara is seen as an identifier and the identifier’s value is placed into the variable VARB.

Since constant text is represented by single quotation marks in DS2, there needs to be a way to resolve the macro variable within quotation marks. Luckily, within DS2, there is a SAS supplied autocall macro called %TSLIT that enables macro variable resolution within single quotation marks.  Here is an example:

%let val=This is a test;                                                                                                                
proc ds2;                                                                                                                               
data _null_;                                                                                                                           
   method init();                                                                                                                         
      declare char(14) dval;                                                                                                               
      dval=%tslit(&amp;val);                                                                                                                  
      put dval=;                                                                                                                          
   end;                                                                                                                                   
enddata;                                                                                                                               
run;                                                                                                                                    
quit;

I hope this blog post has been helpful. If you have any questions, please contact SAS Technical Support and we will be happy to assist you. Thanks for using SAS!

 

tags: Problem Solvers, PROC DS2, SAS Programmers

Solutions for missing DATA step features within DS2 was published on SAS Users.

2月 132015
 

Have you ever created a SAS macro variable and at resolution time received a warning that it did not exist? Many times this warning is because your program referenced the macro variable outside the scope it was created in.

Every macro variable created is stored in one of two symbol tables. The symbol table lists the macro variable name and its value and determines its scope. Global macro variables, or those stored in the global symbol table, exist for the duration of the SAS session and can be referenced anywhere except in the CARDS and DATALINES statements. Local macro variables, or those stored in a local symbol table, exist only during the execution of the macro in which the variable is created.

This post will help you determine which scope a macro variable will be defined in. I will also show a nice feature of CALL SYMPUTX for assigning the scope for macro variables. Lastly, I will discuss some SAS functions that help determine if a macro variable exists in one of the two scopes.

More about the two types of scope

Global macro variables include the following:

  • all automatic macro variables except SYSPBUFF
  • macro variables created outside of any macro definition
  • macro variables created on a %GLOBAL statement
  • most macro variables created by CALL SYMPUT/CALL SYMPUTX, except in special cases

Local macro variables include the following:

  • macro parameters
  • macro variables created on a %LOCAL statement
  • macro statements that define macro variables within a macro definition, such as %LET and the iterative %DO statement (if the variable does not already exist globally or a %GLOBAL statement is not used)

The following is a nice diagram that illustrates what happens when creating a macro variable. This diagram does not apply when a %LOCAL statement is used to create the variable.

macroscope1

Difference in scope between CALL SYMPUT and %LET

CALL SYMPUT and %LET are the two most popular ways of creating a macro variable so we will focus on those two methods.

In the first example, notice that CALL SYMPUT placed the macro variable on the global symbol table because once it determines the macro variable does not exist, it places it in the first non-empty symbol table it finds, starting with the most local scope. In this case, this is the global table (global table is never empty as it contains the SAS automatic variables).

%macro test;                                                                                                                       
 data one;                                                                                                                          
    call symput('bbb',100);                                                                                                          
 run;                                                                                                                            
%put _user_;                                                                                                                    
%mend test;                                                                                                                       
%test                                                                                                                          

SAS Log results:

GLOBAL BBB 100

In this example, %LET placed the macro variable CCC in the local symbol table because once it determines the macro variable does not exist it creates the variable in the scope for the current macro. In this case, this is the local table since we are within the local scope of the macro test2. A macro's local symbol table is empty until the macro creates at least one macro variable.

%macro test2;                                                                                                                           
 %let ccc=200;                                                                                                                          
 %put _user_;                                                                                                                           
%mend test2;                                                                                                                            
%test2

SAS Log results:

TEST2 CCC 200

Problems with nesting local variables

Local symbol tables can also be nested within each other. In this example we have one global macro variable called OUTER. One local macro variable, AA, local to the macro TEST and one local macro variable, BB, local to the macro test2.  Since the macro TEST2 is nested within the TEST macro, the local table for TEST2 is also nested within the local table for TEST. The diagram below shows the intended scope for local variables AA and BB:

macroscope2

Processing the following code reveals problems with referencing:

%let outer=500;                                                                                                                         
%macro test2;                                                                                                                           
 %let bb=200;                                                                                                                           
 %put &aa;                                                                                                                              
%mend test2;                                                                                                                            
                                                                                                                                        
%macro test;                                                                                                                            
 %let aa=100;                                                                                                                           
 %test2                                                                                                                                 
 %put &bb;                                                                                                                              
%mend test;                                                                                                                             
                                                                                                                                        
%test

SAS Log results:

100
WARNING: Apparent symbolic reference BB not resolved.
&bb

Why did this happen? The first %PUT encountered (%PUT &AA ) resolves to 100 even though we are within the TEST2 macro, but this is because TEST2 is nested within TEST. The second %PUT encountered (%PUT &BB) does not resolve because BB is local to the TEST2 macro and is not known to macro TEST.

 Using CALL SYMPUTX to assign scope

To force macro variables created by CALL SYMPUT to be global when the local table is not empty, use a %GLOBAL statement, listing all the variables. This would be difficult if creating a list of macro variables.  Now with the addition of CALL SYMPUTX this task is much easier.  CALL SYMPUTX contains an argument that specifies the scope in which to place the macro variable. The following values are valid as the first non-blank character in symbol-table:

G specifies the global symbol table, even if the local symbol table exists.
L specifies the most local symbol table that exists, which will be the global symbol table, if used outside a macro.
F specifies that if the macro variable exists in any symbol table, CALL SYMPUTX uses the version in the most local symbol table in which it exists. If the macro variable does not exist, CALL SYMPUTX stores the variable in the most local symbol table.

Suppose you are inside a macro and want to create a macro variable for each observation from a data set. The example below would fail because the macro variables val1-val3 are local to the macro TEST and only exist within the TEST macro.  Once TEST has finished executing those macro variables are deleted.

data one;                                                                                                                               
input name $;                                                                                                                           
cards;                                                                                                                                  
abc                                                                                                                                     
def                                                                                                                                     
ghi                                                                                                                                     
;                                                                                                                                       
                                                                                                                                        
%macro test(dsn);                                                                                                                       
data _null_;                                                                                                                            
 set one;                                                                                                                               
   call symput('val'||strip(_n_),name);                                                                                            
run;                                                                                                                                    
%mend test;                                                                                                                             
                                                                                                                                        
%test(one);                                                                                                                             
                                                                                                                                        
%put &val1 &val2 &val3;

In the past you would have to add something like the following before the CALL SYMPUT statement to make the macro variables global:

call execute('%global val'||strip(_n_)||';');

Now with CALL SYMPUTX this is easy. Just change the CALL SYMPUT above to the following. The ‘g’ argument makes all the macro variables created by this call routine global.

call symputx('val'||strip(_n_),name,’g’);

How to determine where macro variables are stored

Here are a few statements that are very helpful in determining which symbol table the macro variables are stored in:

  • %PUT _USER_;
  • %PUT _LOCAL_;
  • %PUT _GLOBAL_;

There are times you may want to find out if a macro variable exists in a certain scope. There are three functions that might help in this situation.

  • %SYMEXIST(mac_var) – returns 1 if macro variable exist, otherwise 0
  • %SYMGLOBL(mac_var) – returns 1 if macro variable exist in global scope, otherwise 0
  • %SYMLOCAL(mac_var) – returns 1 if macro variable exists in local scope, otherwise 0

I hope this blog post has been helpful. If you have other SAS macro questions you’d like me to cover in future blogs, please comment below.

tags: macro, Problem Solvers, SAS Programmers
11月 142014
 

Have you ever received an error or warning in SAS macro and did not know what to do next or even where to look?  Now there is an answer! And debugging your SAS macros just got easier.

All macro errors and warnings are now documented in the SAS 9.4 Macro Language Reference, Third Edition. You can find them here:

Each error or warning entry begins with the text of the message followed by a two-colum table that lists each possible cause and a suggested solution for each cause. Here are samples from each section of the appendix that illustrate how the information is presented.

Let us know if this new feature in the SAS Macro Facility documentation helps you to investigate your own possible cause and solution for any macro error you may be encountering.

Sample SAS macro error message

Sample of SAS macro error causes and solutions

Sample SAS macro warning message

SAS macro warning message with cause and solutons

tags: macro, Problem Solvers, SAS Programmers
8月 152014
 

Are there times when you need to pass special characters to a macro variable but cannot find the right technique to accomplish the task?  In this article I’ll discuss the different macro quoting functions and give a simple technique to help you determine which macro quoting function to use.

Why do we need macro quoting?  The SAS macro language is a character-based language. With macro, you can specify special characters as text.  Some of these special characters (for example, a semicolon or percent sign) are part of the SAS language instruction set, so we need a way for the macro processor to interpret a particular special character when it’s being used as text in the macro language.  Macro quoting functions tell the macro processor to treat these special characters as text rather than as part of the macro language.  Without macro quoting function, you would have no way to mask the real meaning of these special characters or mnemonics.

This post will list some all-purpose functions, tell how to determine when to use each type, and show you how to unmask, or unquote special characters.

Two types of macro quoting functions

There are basically two types of macro quoting functions:  compile time and execution time.

Compile time functions:

  • % STR
  • % NRSTR

%STR and %NRSTR mask the same characters except %NRSTR also masks & and % signs.

Execution time functions:

  • %SUPERQ   (Reminder: do not use & with macro variable name)
  • %BQUOTE

There are other execution-time functions, but these are the only two you really need. %BQUOTE and %SUPERQ mask the same characters except %SUPERQ does not attempt to resolve a macro variable reference or a macro invocation that occurs in the value of the specified macro variable.  %SUPERQ masks everything without any further resolution which makes it the best execution-time quoting function to use.

How to determine which type to use

Many times the hardest part of macro quoting is knowing whether you need a compile time function or an execution time function.  A simple technique that will help you easily choose the right one is to follow this motto:

If you can see the problem, it is a compile issue; otherwise, it is execution time.

These examples illustrate when a compile time function is needed.  You can see the potential problem in each of the examples:

macroquote1

There are a few special cases when you have an unmatched quotation mark or an unmatched parenthesis. These unmatched characters must be preceded with a percent sign when assigning a value at compile time.  For example:

macroquote2

These examples illustrate when an execution time function is needed.  You cannot see the potential problem in each of the examples:

macroquote3

Unquoting special characters

Now that we have seen how to mask special characters, we also need a way to reverse the masking. This reversal is referred to as unquoting which means to restore the significance of symbols in an item that was previously masked by a macro quoting function.

Generally, once you use a macro quoting function on a value, the macro processor retains that masking on the value. There are three cases where the unquoting is done for you:

  • The %UNQUOTE function was used.
  • The item leaves the word scanner and is passed to the DATA step compiler, SAS Macro Facility, or other parts of the SAS System.
  • The value is returned from the %SCAN, %SUBSTR, or %UPCASE function.

There are two cases where you might need to use the %UNQUOTE function to restore the original significance to a masked item:

  • When you want to use a value with its restored meaning later in the same macro in which its value was previously masked by a macro quoting function.
  • In some cases, masking text with a macro quoting function changes the way the word scanner tokenizes it, producing SAS statements that look correct but the SAS compiler does not recognize. This is the most common need for %UNQUOTE.

macroquote4

The code generated by the macro may look correct, but an error is generated because the masking placed on the value from the %QSCAN (to be discussed later) is causing the string to be tokenized incorrectly.  This can be corrected by using the %UNQUOTE function.
%unquote(val&v) = &v/500;

Resolving macro variables within single quotes

Many times single quotes are required around a string where the string is made up of a macro variable.  Since macro triggers of percent and ampersand are seen as text within single quotes, a common request is how to resolve a macro variable within single quotes.  We can use %UNQUOTE along with %STR for this task:

macroquote6

An easy way to determine if a quoting function has been used on a macro variable is to use the following statement to print out the values:
%PUT _USER_;

Here is a screenshot of a resulting SAS log. Notice the unusual spaces and characters. These represent the masking done by a quoting function.  Quoting functions mark the beginning and ending of the string as well as any special character.

macroquote7

Other quoting functions

There are a few other functions that mask a result that may contain special characters.  These functions all begin with Q:  %QSYSFUNC, %QSCAN, %QSUBSTR and %QUPCASE. Here are a few examples:

macroquote8

I hope this blog post has been helpful. If you have other SAS macro questions you’d like me to cover in future blogs, please comment below.

tags: macro, Problem Solvers, SAS Programmers