1

Based on this post, I can get a list of compiled macros in a program, ordered by their first appearance, using the following code:

%macro FirstMacro();
  %put Hello, World!;
%mend;

%macro SecondMacro();
  %put Goodbye, cruel world!;
%mend;

proc sql;
  select objname, created
  from dictionary.catalogs
  where objtype='MACRO' and libname ^= 'SASHELP'
  order by created
  ;
quit;

This, however, gives all the macros in uppercase.

               Object Name                           Date Created
               --------------------------------------------------
               FIRSTMACRO                        09FEB17:16:12:31
               SECONDMACRO                       09FEB17:16:12:31

I use PascalCase for my macro names (as illustrated above). Is there a way to get a similar list, but with case sensitivity preserved?

It seems like PROC SCAPROC might provide such a list, but it's unclear to me. There is a note from when PROC SCAPROC was first released (in 9.2) that indicates that PROC SCAPROC doesn't provide support for when a %MACRO is used. However, the note says this will be a feature in future versions of SAS. With SAS 9.4 it's unclear whether the feature has been added yet.

The only other alternative I can think of is to write a script in some other language which analyzes the text above for the macro names.

Community
  • 1
  • 1
Lorem Ipsum
  • 4,020
  • 4
  • 41
  • 67
  • 1
    What is the purpose for this particular exercise? IE, once you have this list, what's it going to be used for? – Joe Feb 09 '17 at 21:46
  • Macro names are not case sensitive so why would SAS store them in mixed case? – Tom Feb 09 '17 at 21:54
  • @Tom I'm going to guess (and I asked for this reason) that OP is using this to generate code and/or for an EG add-in to improve the rather limited macro autocomplete functionality, and wants the PascalCase text as an output. (If OP is indeed writing an add-in for this, I would love to have it, and/or help write it - this would be a super useful tool!) – Joe Feb 09 '17 at 21:59
  • In my particular case, I need the list for documentation purposes. – Lorem Ipsum Feb 16 '17 at 15:06

3 Answers3

0

So, it's feasible this may give you what you want, at least with the above example.

It does not expand the macro when it's run - meaning, you don't get to see what happens inside the macro, as it runs. This may be a problem if you are generating macros at runtime, i.e. using your data to create macros - like

data _null_;
  set something;
  call execute ('%macro ',name_var,'; ... ; %mend;');
run;

That may not show up properly. But if you're just writing macros in code and compiling them, then yes, you might be able to use this - though you have to run the compilation step at least as part of this (this doesn't analyse plaintext, it analyzes a job while it runs).

So if I do something like this:

proc scaproc;
  record 'c:\temp\recording.txt' expandmacros;
run;

%macro PascalCaseMacro();
  %put PascalCaseMacro;
%mend;

%PascalCaseMacro;

proc scaproc; 
   write; 
run;

I get output like this:

/* JOBSPLIT: PROCNAME DATASTEP */
/* A bunch of lines like that with JOBSPLIT at the start */


%macro PascalCaseMacro();
  %put PascalCaseMacro;
%mend;

%PascalCaseMacro;

/* Some more JOBSPLIT lines */

You could then parse that text file for lines that started with %macro.

I do think the technical answer to your question is "no", they have not implemented this: while it does update the technical information with some accesses of the macro catalog, it doesn't ever include in the detail of what macro was run (in the /* JOBSPLIT */ lines). That's really what that technical note is talking about, I think; i.e., this is not perfectly useful for optimizing macro code. The actual code that generates the macro (which I think is all you need) is there. (However, if you have a stored-compiled-macro, that would not be available.)

Joe
  • 62,789
  • 6
  • 49
  • 67
0

I was able to adapt the Windows Batch Script from this post to get a list of case sensitive macros defined in a given program.

REM GetListOfDefinedMacros.bat

@ECHO OFF

SET /P SASFILE= Enter SAS filepath: 
SET SASFILE=%SASFILE:"=%

ECHO.
ECHO The defined macros are:
ECHO.

FOR /F "tokens=*" %%A IN ('FINDSTR "macro" "%SASFILE%" ^| FINDSTR "("') DO CALL :FindString "%%A"
ECHO. 
pause
GOTO :eof

:FindString
SET String=%~1
SET String=%String:*macro =%
SET String=%String:)=X%
SET String=%String:(=`%
FOR /F "tokens=1 delims=`" %%A IN ('ECHO.%String%') DO ECHO.%%A
GOTO :eof

The program prompts the user for the SAS program's full file path. It then parses the code, looking for lines which contain both "macro" and "(", returning the any characters that lie between them.

The following assumptions must hold:

  • each line contains a single programming statement; "macro" and "(" must be on the same line,
  • a macro is defined using parentheses; it will not work for %macro MacroName; style definitions.

Note that the script doesn't list compiled macros. It can only detect macros which have definitions within the given program. Macros compiled through %include or AUTOCALL will not show up.

For example,

/*C:\temp\sample sas program.sas*/

%macro FirstMacro();
  %put NOTE: Hello, world!;
%mend;

%macro SecondMacro();
  %put ERROR: Goodbye, cruel world!;
%mend;

%macro ThirdMacro(input);
  %if &input. = hi %then %FirstMacro();
  %else %SecondMacro();
%mend;

%ThirdMacro(hi);
%ThirdMacro(bye);

This gives the following output to the command prompt:

Enter SAS filepath: "C:\temp\sample sas program.sas"

The defined macros are:

FirstMacro
SecondMacro
ThirdMacro

Press any key to continue . . .

From there, you can copy and paste the list. Or, the code could be modified to redirect the list to a file.

Community
  • 1
  • 1
Lorem Ipsum
  • 4,020
  • 4
  • 41
  • 67
0

Here is a native SAS solution which should rely only on BASE capabilities. It is intended for use in the Enhanced Editor on Windows.

%macro ListMacroDefinitions();
  /*Get file-path of current program's source code.*/
  proc sql noprint;
    select distinct xpath
    into : _SASProgramPath trimmed
    from dictionary.extfiles
    where xpath contains "%sysget(SAS_EXECFILENAME)"
    ;
  quit;

  /*Read in source code, parse for macro names, and
    output to log.*/
  options nonotes;
  data _null_;
    infile "&_SASProgramPath." dlm = '```';
    length 
      line        $ 256
      macro_name  $ 32
    ;
    input line;

    if line =: '%macro' then do;
      indexLeftParenthesis  = index(line, '(');
      macroNameLength       = indexLeftParenthesis - 8;
      macro_name            = substr(line, 8, macroNameLength); 
      put macro_name;
    end;
  run;
  options notes;
  dm 'wpgm';
%mend;

It works by first determining what the current program's name is using %sysget(SAS_EXECFILENAME). The %SYSGET function returns the value of the environment variable SAS_EXECFILENAME which

specifies the filename of the current SAS source file, if it is submitted from the enhanced editor.

It then looks in the DICTIONARY.EXTFILES table and extracts a file path associated with name of the current source file (i.e. the program currently being edited). This file path is assigned to a macro variable.

Next, a data step reads in the source file of the current program. It reads the file line by line setting a non-existent delimiter (three back ticks) in the INFILE statement.

By default, if the INPUT statement tries to read past the end of the current input data record, then it moves the input pointer to column 1 of the next record to read the remaining values.

Defining the delimiter to a string which never appears in the source code causes the line read in to never be parsed and thus be read in whole. Each line is checked for whether it contains a macro definition (via the =: operator). Specifically, it looks for the string '%macro'. We then manually parse each definition line for the characters between the '%macro ' statement and the first parenthesis. The result is output to the log. During this process, notes are disabled. Finally, the windowing command wpgm is issued. When the macro names are written to the log, the log window is selected. The wpgm command returns focus to the last program window, which should be the program currently being edited.

The whole process is put inside a macro. If you place the macro in an AUTOCALL library (found by issuing %put %sysfunc(pathname(sasautos));), you can get a list of all the macros defined within your current program. Note that for this list to be complete, you must save your current program before running the macro.

Lorem Ipsum
  • 4,020
  • 4
  • 41
  • 67