【问题标题】:Get case sensitive list of macros in program获取程序中区分大小写的宏列表
【发布时间】:2017-02-09 21:22:20
【问题描述】:

基于此post,我可以使用以下代码获取程序中已编译宏的列表,按首次出现的顺序排列:

%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;

然而,这会以大写形式给出所有宏。

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

我使用PascalCase 作为我的宏名称(如上图所示)。有没有办法获得类似的列表,但保留区分大小写?

似乎PROC SCAPROC 可能会提供这样一个列表,但我不清楚。有一个note 来自 PROC SCAPROC 首次发布(在 9.2 中),它表明 PROC SCAPROC 不支持何时使用 %MACRO。但是,该说明说这将成为未来版本的 SAS 的一个功能。对于 SAS 9.4,尚不清楚该功能是否已添加。

我能想到的唯一另一种选择是用其他语言编写一个脚本,分析上面的文本以获取宏名称。

【问题讨论】:

  • 这个特定练习的目的是什么? IE,一旦你有了这个列表,它会被用来做什么?
  • 宏名称不区分大小写,那么为什么 SAS 会以混合大小写的形式存储它们?
  • @Tom 我猜(我问这个原因)OP 正在使用它来生成代码和/或用于 EG 插件来改进相当有限的宏自动完成功能,并希望将 PascalCase 文本作为输出。 (如果 OP 确实为此编写了一个插件,我很想拥有它,和/或帮助编写它 - 这将是一个超级有用的工具!)
  • 在我的特殊情况下,我需要该列表用于文档目的。

标签: sas sas-macro


【解决方案1】:

所以,这可能会给你想要的东西,至少在上面的例子中是可行的。

它在运行时不会扩展宏 - 意思是,您无法看到宏内部发生了什么,它运行时。如果您在运行时生成宏,即使用您的数据创建宏,这可能是一个问题 - 就像

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

这可能无法正确显示。但是,如果您只是在代码中编写宏并编译它们,那么是的,您也许可以使用它 - 尽管您必须至少作为其中的一部分运行编译步骤(这不分析明文,它分析一个运行时工作)。

所以如果我这样做:

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

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

%PascalCaseMacro;

proc scaproc; 
   write; 
run;

我得到这样的输出:

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


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

%PascalCaseMacro;

/* Some more JOBSPLIT lines */

然后您可以解析该文本文件中以 %macro 开头的行。

我确实认为您的问题的技术答案是“否”,他们没有实现这一点:虽然它确实通过对宏目录的某些访问来更新技术信息,但它从来没有包含在什么宏的细节中已运行(在 /* JOBSPLIT */ 行中)。我认为,这就是技术说明所要谈论的内容;即,这对于优化宏代码并不是非常有用。生成宏的实际代码(我认为需要的全部)就在那里。 (但是,如果您有一个存储编译的宏,那将可用。)

【讨论】:

    【解决方案2】:

    我能够调整来自this 帖子的 Windows 批处理脚本,以获取给定程序中定义的区分大小写的宏列表。

    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
    

    程序提示用户输入 SAS 程序的完整文件路径。然后它解析代码,查找同时包含“macro”和“(”的行,返回它们之间的任何字符。

    以下假设必须成立:

    • 每一行包含一个编程语句; “macro”和“(”必须在同一行,
    • 使用括号定义宏;它不适用于%macro MacroName; 样式定义。

    请注意,该脚本不会列出已编译的宏。它只能检测在给定程序中有定义的宏。通过%include 或AUTOCALL 编译的宏不会出现。

    例如,

    /*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);
    

    这会向命令提示符提供以下输出:

    Enter SAS filepath: "C:\temp\sample sas program.sas"
    
    The defined macros are:
    
    FirstMacro
    SecondMacro
    ThirdMacro
    
    Press any key to continue . . .
    

    您可以从那里复制和粘贴列表。或者,可以修改代码以将列表重定向到文件。

    【讨论】:

      【解决方案3】:

      这是一个原生 SAS 解决方案,它应该只依赖于 BASE 功能。它旨在用于 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;
      

      它首先确定当前程序的名称是使用%sysget(SAS_EXECFILENAME)%SYSGET 函数返回 environment variable SAS_EXECFILENAME 的值

      指定当前 SAS 源文件的文件名,如果是 从增强编辑器提交。

      然后它在DICTIONARY.EXTFILEStable 中查找并提取与当前源文件(即当前正在编辑的程序)名称相关联的文件路径。这个文件路径被分配给一个宏变量。

      接下来,一个数据步骤会读入当前程序的源文件。它逐行读取文件,在INFILE 语句中设置一个不存在的分隔符(三个反引号)。

      默认情况下,如果 INPUT 语句尝试读取超出 当前输入数据记录,然后将输入指针移动到第 1 列 读取剩余值的下一条记录。

      将分隔符定义为从未出现在源代码中的字符串会导致读入的行永远不会被解析,因此会被整个读取。检查每一行是否contains 是一个宏定义(通过=: 运算符)。具体来说,它会查找字符串“%macro”。然后,我们手动解析 '%macro' 语句和第一个括号之间的字符的每个定义行。结果输出到日志。在此过程中,笔记被禁用。最后,发出窗口命令wpgm。将宏名称写入日志时,会选择日志窗口。 wpgm 命令将焦点返回到最后一个程序窗口,它应该是当前正在编辑的程序。

      整个过程都放在一个宏中。如果您将宏放在AUTOCALL 库中(通过发出%put %sysfunc(pathname(sasautos)); 找到),您可以获得当前程序中定义的所有宏的列表。请注意,要完成此列表,您必须在运行宏之前保存当前程序。

      【讨论】:

        猜你喜欢
        • 2012-02-03
        • 1970-01-01
        • 1970-01-01
        • 2019-02-06
        • 2013-04-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多