I can offer a macro
\ExtractKthArg{⟨TeX-⟨number⟩-quantity with integer-value K⟩}%
{⟨tokens in case list of undelimited arguments doesn't have a k-th argument⟩}%
{⟨list of undelimited arguments⟩}%
which does the following:
In case there is no ⟨K⟩-th argument in the ⟨list of undelimited arguments⟩:
Does deliver ⟨tokens in case list of undelimited arguments doesn't have a k-th argument⟩.
In case there is a ⟨K⟩-th argument in the ⟨list of undelimited arguments⟩:
Does deliver that ⟨K⟩-th argument with one level of braces removed.
Examples:
\ExtractKthArg{0}{not available}{ABCDE}
yields: not available
\ExtractKthArg{3}{not available}{ABCDE}
yields: C
\ExtractKthArg{3}{not available}{AB{CD}E}
yields: CD
\ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}}
yields: 004
\ExtractKthArg{6}{not available}{{001}{002}{003}}
yields: not available
You can easily define \makeAlph
in terms of \ExtractKthArg
as follows:
\newcommand\makeAlph[2]{%
\ExtractKthArg{\number\numexpr#1+1\relax}{#2}{abcdefghijklmnopqrstuvwxyz}%
}%
This way \makeAlph
takes two arguments, the first argument being the TeX-⟨number⟩-quantity to convert to character, the second argument denoting tokens to be delivered in case of the first argument not being in range 0..25.
With the minimal working example below it is just relied on \makeAlph
's 1st argument being a TeX-⟨number⟩-quantity. There is no checking if \makeAlph
's 1st argument is a TeX-⟨number⟩-quantity: The 1st argument is a set of tokens, thus is something that might form an arbitrary (expansion-based) algorithm. Checking if the result of carrying out such an algorithm yields a TeX-⟨number⟩-quantity subsumes checking if such an algorithm terminates at all/terminates without error-message. This in turn reminds me of the halting-problem ...
!!!\makeAlph
's 1st argument not being a TeX-⟨number⟩-quantity may result in all kinds of unpredictable behavior, leading to inscrutable error-messages!!!
Here comes the minimal working example:
\errorcontextlines=10000
\documentclass{article}
\makeatletter
%% Code for \ExtractKthArg
%%=============================================================================
%% Paraphernalia:
%% \UD@firstoftwo, \UD@secondoftwo, \UD@PassFirstToSecond, \UD@Exchange,
%% \UD@stopromannumeral, \UD@CheckWhetherNull
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is empty>}%
%% {<Tokens to be delivered in case that argument
%% which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
\romannumeral\expandafter\UD@secondoftwo\string{\expandafter
\UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
\UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
\UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
\expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%=============================================================================
%% Extract K-th inner undelimited argument:
%%
%% \ExtractKthArg{<integer K>}%
%% {<tokens in case list of undelimited arguments doesn't have a k-th argument>}%
%% {<list of undelimited arguments>}%
%%
%% In case there is no K-th argument in <list of indelimited arguments> :
%% Does deliver <tokens in case list of undelimited arguments doesn't have a k-th argument.
%% In case there is a K-th argument in <list of undelimited arguments> :
%% Does deliver that K-th argument with one level of braces removed.
%%
%% Examples:
%%
%% \ExtractKthArg{0}{not available}{ABCDE} yields: not available
%%
%% \ExtractKthArg{3}{not available}{ABCDE} yields: C
%%
%% \ExtractKthArg{3}{not available}{AB{CD}E} yields: CD
%%
%% \ExtractKthArg{4}{not available}{{001}{002}{003}{004}{005}} yields: 004
%%
%% \ExtractKthArg{6}{not available}{{001}{002}{003}} yields: not available
%%
%%=============================================================================
\newcommand\ExtractKthArg[2]{%
\romannumeral%
% #1: <integer number K>
% #2: <action if there is no K-th argument>
\expandafter\UD@ExtractKthArgCheck
\expandafter{\romannumeral\number\number#1 000}{#2}%
}%
\newcommand\UD@ExtractKthArgCheck[3]{%
\UD@CheckWhetherNull{#1}{\UD@stopromannumeral#2}{% empty
\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}#1}{#2}{#3}%
}%
}%
\begingroup
\def\UD@ExtractFirstArgLoop#1{%
\endgroup
\@ifdefinable\UD@RemoveTillFrozenrelax{%
\long\def\UD@RemoveTillFrozenrelax##1##2#1{{##1}}%
}%
\newcommand\UD@ExtractKthArgLoop[3]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo##3{}.}{\UD@stopromannumeral##2}{%
\UD@CheckWhetherNull{##1}{%
\UD@ExtractFirstArgLoop{##3#1}%
}{%
\expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}##3}%
{\expandafter\UD@ExtractKthArgLoop\expandafter{\UD@firstoftwo{}##1}{##2}}%
}%
}%
}%
}%
\expandafter\expandafter\expandafter\UD@ExtractFirstArgLoop
\expandafter\expandafter\expandafter{%
\expandafter\expandafter\ifnum0=0\fi}%
%% Usage of frozen-\relax as delimiter is for speeding things up by reducing the
%% amount of iterations needed. I chose frozen-\relax because David Carlisle
%% pointed out in <https://tex.stackexchange.com/a/578877>
%% that frozen-\relax cannot be (re)defined in terms of \outer and cannot be
%% affected by \uppercase/\lowercase.
%%
%% \UD@ExtractFirstArg's argument may contain frozen-\relax:
%% The only effect is that internally more iterations are needed for
%% obtaining the result.
\newcommand\UD@ExtractFirstArgLoop[1]{%
\expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
{\expandafter\UD@stopromannumeral\UD@firstoftwo#1{}}%
{\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillFrozenrelax#1}}%
}%
%% End of code for \ExtractKthArg.
\makeatother
\newcommand\makeAlph[2]{%
\ExtractKthArg{\number\numexpr#1+1\relax}{#2}{abcdefghijklmnopqrstuvwxyz}%
}%
\begin{document}
\noindent
\verb|\makeAlph{0}{\makeAlpherrordefault}| yields: \makeAlph{0}{\makeAlpherrordefault}\\
\verb|\makeAlph{1}{\makeAlpherrordefault}| yields: \makeAlph{1}{\makeAlpherrordefault}\\
\verb|\makeAlph{2}{\makeAlpherrordefault}| yields: \makeAlph{2}{\makeAlpherrordefault}\\
\verb|\makeAlph{3}{\makeAlpherrordefault}| yields: \makeAlph{3}{\makeAlpherrordefault}\\
\verb|\makeAlph{4}{\makeAlpherrordefault}| yields: \makeAlph{4}{\makeAlpherrordefault}\\
\verb|\makeAlph{5}{\makeAlpherrordefault}| yields: \makeAlph{5}{\makeAlpherrordefault}\\
\verb|\makeAlph{6}{\makeAlpherrordefault}| yields: \makeAlph{6}{\makeAlpherrordefault}\\
\verb|\makeAlph{7}{\makeAlpherrordefault}| yields: \makeAlph{7}{\makeAlpherrordefault}\\
\verb|\makeAlph{8}{\makeAlpherrordefault}| yields: \makeAlph{8}{\makeAlpherrordefault}\\
\verb|\makeAlph{9}{\makeAlpherrordefault}| yields: \makeAlph{9}{\makeAlpherrordefault}\\
\verb|\makeAlph{10}{\makeAlpherrordefault}| yields: \makeAlph{10}{\makeAlpherrordefault}\\
\verb|\makeAlph{11}{\makeAlpherrordefault}| yields: \makeAlph{11}{\makeAlpherrordefault}\\
\verb|\makeAlph{12}{\makeAlpherrordefault}| yields: \makeAlph{12}{\makeAlpherrordefault}\\
\verb|\makeAlph{13}{\makeAlpherrordefault}| yields: \makeAlph{13}{\makeAlpherrordefault}\\
\verb|\makeAlph{14}{\makeAlpherrordefault}| yields: \makeAlph{14}{\makeAlpherrordefault}\\
\verb|\makeAlph{15}{\makeAlpherrordefault}| yields: \makeAlph{15}{\makeAlpherrordefault}\\
\verb|\makeAlph{16}{\makeAlpherrordefault}| yields: \makeAlph{16}{\makeAlpherrordefault}\\
\verb|\makeAlph{17}{\makeAlpherrordefault}| yields: \makeAlph{17}{\makeAlpherrordefault}\\
\verb|\makeAlph{18}{\makeAlpherrordefault}| yields: \makeAlph{18}{\makeAlpherrordefault}\\
\verb|\makeAlph{19}{\makeAlpherrordefault}| yields: \makeAlph{19}{\makeAlpherrordefault}\\
\verb|\makeAlph{20}{\makeAlpherrordefault}| yields: \makeAlph{20}{\makeAlpherrordefault}\\
\verb|\makeAlph{21}{\makeAlpherrordefault}| yields: \makeAlph{21}{\makeAlpherrordefault}\\
\verb|\makeAlph{22}{\makeAlpherrordefault}| yields: \makeAlph{22}{\makeAlpherrordefault}\\
\verb|\makeAlph{23}{\makeAlpherrordefault}| yields: \makeAlph{23}{\makeAlpherrordefault}\\
\verb|\makeAlph{24}{\makeAlpherrordefault}| yields: \makeAlph{24}{\makeAlpherrordefault}\\
\verb|\makeAlph{25}{\makeAlpherrordefault}| yields: \makeAlph{25}{\makeAlpherrordefault}
\bigskip
\noindent
\verb|\newcounter{scratchcounter}|\newcounter{scratchcounter}\\
\verb|\setcounter{scratchcounter}{12}|\setcounter{scratchcounter}{12}\\
\verb|\makeAlph{\value{scratchcounter}}{\makeAlpherrordefault}| yields: \makeAlph{\value{scratchcounter}}{\makeAlpherrordefault}
\bigskip
\noindent
% \makeAlpherrordefault delivers an error-message. Instead we want the phrase "Error-Tokens".
% \verb|\makeAlph{-1}{\makeAlpherordefault}| yields: \makeAlph{-1}{\makeAlpherordefault}\\
\verb|\makeAlph{-1}{Error-Tokens.}| yields: \makeAlph{-1}{Error-Tokens.}\\
% \verb|\makeAlph{26}{\makeAlpherrordefault}| yields: \makeAlph{26}{\makeAlpherrordefault}\\
\verb|\makeAlph{26}{Error-Tokens.}| yields: \makeAlph{26}{Error-Tokens.}
\bigskip
\noindent
\verb|\setcounter{scratchcounter}{28}|\setcounter{scratchcounter}{28}\\
%\verb|\makeAlph{\value{scratchcounter}}{\makeAlpherrordefault}| yields: \makeAlph{\value{scratchcounter}}{\makeAlpherrordefault}\\
\verb|\makeAlph{\value{scratchcounter}}{Error-Tokens.}| yields: \makeAlph{\value{scratchcounter}}{Error-Tokens.}
\end{document}
