
/*
Macro was inspired by Leonid Batkhan's blog post:

"Embedding any code anywhere into SAS programs" from May 30, 2023.

Link:
https://blogs.sas.com/content/sgf/2023/05/30/embedding-any-code-anywhere-into-sas-programs/
*/


/*** HELP START ***//*
 
## >>> `%mInclude()` macro: <<< <a name="minclude-macro"></a> #######################  

The mInclude() macro is a macrolanguage version of the SAS `%include` statement.
But it allows for "embedding any code anywhere into SAS programs".

Macro was inspired by *Leonid Batkhan* and his blog post:

"Embedding any code anywhere into SAS programs" from May 30, 2023.

Link: `https://blogs.sas.com/content/sgf/2023/05/30/embedding-any-code-anywhere-into-sas-programs/`

The implementation presented, in contrary to inspiration source, is 
based on the `doSubL()` function and a list of global
macro variables of the form `______<N>` (six underscores and a number).

See examples below for the details.

The `%mInclude()` macro executes like a pure macro code.

### SYNTAX: ###################################################################

The basic syntax is the following, the `<...>` means optional parameters:
~~~~~~~~~~~~~~~~~~~~~~~sas
%mInclude(
  < f>
  <,source=> 
  <,lrecl=>
  <,symdel=>
)
~~~~~~~~~~~~~~~~~~~~~~~

**Arguments description**:

1. `f`            - *Required*, a SAS `fileref` or a **quoted** path 
                    to the included file.

*. `source=0`     - *Optional*, default value is `0`.
                    Set to `1` if the source should be printed in the log.

*. `lrecl=32767`  - *Optional*, default value is `32767`.
                    Sets the `lrecl` value for the file width.

*. `symdel=1`     - *Optional*, default value is `1`.
                    Indicates if the global macro variables
                    `______1` to `______N` should be deleted
                    when the macro ends.

---

*//*** HELP END ***/


/* minclude.sas */

%macro mInclude(
 f           /* sas fileref or a _quoted_ path to the file */
,source=0    /* indicate if the source should be printed in the log */
,lrecl=32767 /* sets file width */
,symdel=1
);
  %local i N;
  %let N = 0;
  %let i = %sysfunc(DoSubL(%str(
    options nonotes nosource;
    data _null_;
      infile &f. lrecl=&lrecl. end=END;
      input;
      call symputx(cats("______", _N_), _infile_, "L");
      if END then call symputx("N", _N_, "L");
    run;
  )));
%if 1 = %superq(source) %then 
  %do;
    %put ++ mIncluded code: ++;
    %put ;
    %do i = 1 %to &N.;
      %put %str( +) %superq(______&i);
    %end;
    %put ;
    %put ++++++++++++++++++++++;
  %end;
%do i = 1 %to &N.;
&&______&i.
%if 1 = %superq(symdel) %then 
  %symdel ______&i /nowarn;
%end;
%mend mInclude;


/*** HELP START ***//*
 
### EXAMPLES AND USECASES: ####################################################

**EXAMPLE 1.** Embedding text in statements (the `%include` won't work here):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  resetline;
  filename f "%workpath()/testFile1.txt";
  filename f list;

  data _null_;
    file f;
    put "13 14 15";
  run;

  resetline;
  data testDataset;
    set sashelp.class;
    where age in ( %mInclude(f) );
  run;

  data testDataset2;
    set sashelp.class;
    where age in ( %mInclude(f,source=1) );
  run;

  filename f clear;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 2.**  Embedding with direct path (mind those quotes!):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  resetline;
  filename f "%workpath()/testFile2.txt";
  filename f list;

  %let someGlobalMacroVariable=17;

  data _null_;
    file f;
    put "options mprint;";
    do i=1 to 3;
      put "data y; x = " i "; run;";
      put '%macro A' i +(-1) '(); %put ' i ' ** &someGlobalMacroVariable.; %mend; %A' i +(-1) '()';
    end;
    put "options nomprint;";
  run;

  resetline;
  %mInclude("%workpath()/testFile2.txt")

  %mInclude("%workpath()/testFile2.txt",source=1)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 3.**  Embedding SQL code inside the pass through execution:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  resetline;
  filename f2 "%workpath()/testSql.txt";

  data _null_;
  file f2;
  input;
  put _infile_;
  cards4;
  select 
    c2.make
  , c2.model
  , c2.type
  , c2.invoice
  , c2.date 
   
  from 
    public.CARS_EU c2
   
  where 
    c2.cylinders > 4 
    and 
    c2.date > '2023-04-02'
  ;;;;
  run;


  title 'the %include fails';
  proc sql;
  connect to POSTGRES as PSGDB (
    server="127.0.0.1" 
    port=5432 
    user="user" 
    password="password" 
    database="DB"
  );

  select * from connection to PSGDB
    (
      %Include f2 / source2;
    )
  ;

  disconnect from PSGDB;
  quit;

  title 'the %mInclude works';
  proc sql;
  connect to POSTGRES as PSGDB (
    server="127.0.0.1" 
    port=5432 
    user="user" 
    password="password" 
    database="DB"
  );


  select * from connection to PSGDB
    (
      %mInclude(f2, source=1)
    )
  ;

  disconnect from PSGDB;
  quit;

  title;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 4.** In a limited way and with help of the `resolve()` function, 
               it even works with IML's interface to R:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas

resetline;
filename f3 TEMP;

data _null_;
  file f3;
  infile cards4;
  input;
  put _infile_ ';'; %* a "semicolon" trick for R statements separation *;
cards4;
rModel <- lm(Weight ~ Height, data=Class, na.action="na.exclude")
print (rModel$call)
print (rModel)
;;;;
run;


proc iml;
  codeText = resolve(' %mInclude(f3, source=1) ');
  print codeText;

  call ExportDataSetToR("Sashelp.Class", "Class" );
  submit codeText / R;
     &codeText
  endsubmit;
quit;

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---

*//*** HELP END ***/

/**###################################################################**/
/*                                                                     */
/*  Copyright Bartosz Jablonski, since 2023.         */
/*                                                                     */
/*  Code is under the MIT license. If you want - you can use it.       */
/*  But it comes with absolutely no warranty whatsoever.               */
/*  If you cause any damage or something - it will be your own fault.  */
/*  You've been warned! You are using it on your own risk.             */
/*  However, if you decide to use it don't forget to mention author:  */
/*  Bartosz Jablonski (yabwon@gmail.com)                               */
/*                                                                     */
/**###################################################################**/

























