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

The letters() macro function allows to print a list of Roman
letters starting from `start` up to `end` incremented by `by`.
The letters list can be uppercases or lowercase (parameter `c=U` or `c=L`), 
can be quoted (e.g. `q=""` or `q=[]`), and can be separated by `s=`.

Values of `start`, `end`, and `by` have to be integers in range between 1 ad 26.

See examples below for the details.

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

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

The basic syntax is the following, the `<...>` means optional parameters:
~~~~~~~~~~~~~~~~~~~~~~~sas
%letters(
    range
  <,c=>
  <,q=>
  <,s=>
)
~~~~~~~~~~~~~~~~~~~~~~~

**Arguments description**:

1. `range`       - *Required*, letters selector in form `start:end:by`.
                   Lists letters from `start` to `end` by `by`. 
                   Values of `start`, `end`, and `by` are separated by 
                   colon and must be between 1 ad 26.
                   If value is outside range it is set to 
                   `start=1`, `en=26`, and `by=1`. If `end` is missing
                   then is set to value of `start`.
                   If `end` is smaller than `start` list is reversed

* `c = U`        - *Optional*, it is a lowercase letters indicator. 
                   Select `L` or `l`. Default value is `U` for upcase.

* `q = `         - *Optional*, it is a quite around elements of the list. 
                   Default value is empty. Use `%str()` for one quote symbol.
                   If there are multiple symbols, only the first and the 
                   second are selected as a preceding and trailing one, 
                   e.g. `q=[]` gives `[A] [B] ... [Z]`.

* `s = %str( )`  - *Optional*, it is a separator between 
                   elements of the list. Default value is space.

---

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

%macro letters(range,c=U,q=,s=%str( ));
%local start end by letters q1 q2;
%let letters =A B C D E F G H I J K L M N O P Q R S T U V W X Y Z;
%if %superq(range)= %then %do;&letters.%end;
%else
%do;
%let start=%sysfunc(inputn(%scan(%superq(range),1,:),best32.));
%let end  =%sysfunc(inputn(%scan(%superq(range),2,:),best32.));
%let by   =%sysfunc(inputn(%scan(%superq(range),3,:),best32.));

%if NOT %sysevalf(1<=%superq(start) AND %superq(start)<27) 
  %then %let start=1;
  %else %let start=%sysfunc(int(&start.));

%if %superq(end)=. %then %let end=%superq(start);
%else 
  %do;
    %if NOT %sysevalf(1<=%superq(end) AND %superq(end)<27) 
      %then %let end=26;
      %else %let end=%sysfunc(int(&end.));
  %end;

%if NOT %sysevalf(1<=%superq(by) AND %superq(by)<27) 
  %then %let by=1;
  %else %let by=%sysfunc(int(&by.));

%if %superq(end) < %superq(start) %then %let by=-&by.;

%if %superq(c)=L OR %superq(c)=l 
%then %let letters =a b c d e f g h i j k l m n o p q r s t u v w x y z;

%if %length(%superq(q))>1 %then
%do;
  %let q1=%qsubstr(%superq(q),1,1);
  %let q2=%qsubstr(%superq(q),2,1);
%end;
%else
%do;
  %let q1=%superq(q);
  %let q2=%superq(q);
%end;
&q1.%scan(&letters.,&start.)&q2.%do start=%eval(&start.+&by.) %to &end. %by &by.;&s.&q1.%scan(&letters.,&start.)&q2.%end;
%end;
%mend letters;

/* tests for stupid inputs */
/*
%put %letters(0:27:0);
%put %letters(0);
%put %letters(27);


%put %letters(a:b:c);
%put %letters(1:b:c);


%put %letters(1:26:.c);
%put %letters(1:.b:.c);
%put %letters(.a:.b:.c);


%put %letters(0.3:23.7:1.9);
*/


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

**EXAMPLE 1.** Space-separated list of capital letters from A to Z:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  %put %letters(1:26:1);

  %put %letters();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 2.** First, thirteenth, and last letter:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  %put %letters(1) %letters(13) %letters(26);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 3.** Every third lowercase letter, i.e. `a d g j m p s v y`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  %put %letters(1:26:3,c=L);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 4.** Lists with separators:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  %put %letters(1:26:2,s=#);
  %put %letters(1:26:3,s=%str(;));
  %put %letters(1:26:4,s=%str(,));
  %put %letters(1:26,s=);
  %put %letters(1:26,s==);
  %put %letters(1:26,s=/);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 5.** Every second letter with quotes:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  %put %letters(1:26:2,q=%str(%'));
  %put %letters(2:26:2,q=%str(%"));

  %put %letters(1:26:2,q='');
  %put %letters(2:26:2,q="");

  %put %letters(1:26:2,q=<>);
  %put %letters(2:26:2,q=\/);

  %put %letters(1:26:2,q=());
  %put %letters(2:26:2,q=][);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 6.** Mix of examples 4, 5, and 6:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  %put %letters(1:26,c=L,q='',s=%str(, ));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


**EXAMPLE 7.** If `end` is smaller than `start` list is reversed:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~sas
  %put %letters(26:1:2,q='');
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

---

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


/*
%put (%letters(1:26:2,s=%str(%)%()));
%put [%letters(2:26:2,s=%str(][))];

%put %str(/)%str(*)%letters(1:26,s=%str(*)%str(/ /)%str(*))%str(*)%str(/);
*/
