%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% Module:    ZZTeX Programming Constructs
%
% Synopsis:  This module contains definitions for ZZTeX's programming
%            constructs.  These constructs provide various extensions
%            to TeX's sometimes meager repertoire of programming
%            features.
%
% Author:    Paul C. Anagnostopoulos
% Created:   27 March 1989
%
% Copyright 1989--2020 by Paul C. Anagnostopoulos
% under The MIT License (opensource.org/licenses/MIT)
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%                       Token Hackery
%                       ----- -------


% We need a name for a space token.

\def \\{\let \zspacetoken = } \\ % Now \zspacetoken is a space token.

% This macro discards the next token.

\def \discardtok #1{}%                                  token

% This macro removes the `pt' following a dimension.

{\catcode `\p = \catother \catcode `\t = \catother

\gdef \zremovedu #1pt{#1}

} % \catcode

% Using that macro, we can define a macro that "converts" a dimen register
% into a factor.

\def \thefactor #1{\expandafter\zremovedu \the#1}

% This macro extracts the contents of a macro and returns it as plain text.
% Usage: \expandafter\zdefof \meaning\macro\zmark

\def \zdefof #1:->#2\zmark{#2}

%                       Control Sequence Names
%                       ------- -------- -----


\def \name #1{%                                         {\tokens}
  \csname \expandafter\discardtok \string#1\endcsname}

\def \withname #1#2{%                                   {\command}{\tokens}
  \expandafter#1\csname \expandafter\discardtok \string#2\endcsname}

%                       Macro Expansion
%                       ----- ---------


\def \expandaftertwice {\expandafter\expandafter\expandafter}
\def \expandafterthrice {\expandafter\expandafter\expandafter\expandafter
                         \expandafter\expandafter\expandafter}

\def \zoneoftwo #1#2{#1}%               {first}{second}
\def \ztwooftwo #1#2{#2}%               {first}{second}

\def \zoneofthree   #1#2#3{#1}%         {first}{second}{third}
\def \ztwoofthree   #1#2#3{#2}%         {first}{second}{third}
\def \zthreeofthree #1#2#3{#3}%         {first}{second}{third}

\def \zoneoffour   #1#2#3#4{#1}%         {first}{second}{third}{fourth}
\def \ztwooffour   #1#2#3#4{#2}%         {first}{second}{third}{fourth}
\def \zthreeoffour #1#2#3#4{#3}%         {first}{second}{third}{fourth}
\def \zfouroffour  #1#2#3#4{#4}%         {first}{second}{third}{fourth}

%                       Flags (Booleans)
%                       ----- ----------

% The boolean literals \true and \false are appropriate for use with
% the \if command, which tests the codes of the next two characters.

\def \true {TT}
\def \false {FL}

\def \setflag #1=#2{\edef #1{#2}}%              \flag = boolean

%                       IF and Predicates
%                       -- --- ----------

% A "predicate" is a macro that returns \true or \false as its value.
% Such values are suitable for use with the \if conditional.  For example:
%
%   \if \oddp{\x} <then-clause> \else <else-clause> \fi

% A predicate can be used with \setflag as follows:
%
%   \setflag \flag = {<predicate>}

% Here are the predicates for TeX's repertoire of conditional
% commands.  These might be more appropriately interspersed with
% other definitions in this module, but what the heck.
% Some additional "obvious" predicates are defined.


\def \eqlp   #1#2{\ifnum #1 = #2\true \else \false \fi}
\def \neqlp  #1#2{\ifnum #1 = #2\false \else \true \fi}
\def \lssp   #1#2{\ifnum #1 < #2\true \else \false \fi}
\def \gtrp   #1#2{\ifnum #1 > #2\true \else \false \fi}
\def \zerop  #1{\ifnum #1 = 0\true \else \false \fi}
\def \onep   #1{\ifnum #1 = 1\true \else \false \fi}
\def \posp   #1{\ifnum #1 > 0\true \else \false \fi}
\def \negp   #1{\ifnum #1 < 0\true \else \false \fi}
\def \oddp   #1{\ifodd #1\true \else \false \fi}
\def \evenp  #1{\ifodd #1\false \else \true \fi}
\def \rangep #1#2#3{\if \orp{\lssp{#1}{#2}}{\gtrp{#1}{#3}}\false \else
                                                          \true \fi}
\def \tensp  #1{\rangep{#1}{10}{19}}

\def \dimeqlp   #1#2{\ifdim #1 = #2\true \else \false \fi}
\def \dimneqlp  #1#2{\ifdim #1 = #2\false \else \true \fi}
\def \dimlssp   #1#2{\ifdim #1 < #2\true \else \false \fi}
\def \dimgtrp   #1#2{\ifdim #1 > #2\true \else \false \fi}
\def \dimzerop  #1{\ifdim #1 = 0pt\true \else \false \fi}
\def \dimposp   #1{\ifdim #1 > 0pt\true \else \false \fi}
\def \dimnegp   #1{\ifdim #1 < 0pt\true \else \false \fi}

\def \vmodep     {\ifvmode \true \else \false \fi}
\def \hmodep     {\ifhmode \true \else \false \fi}
\def \mathmodep  {\ifmmode \true \else \false \fi}
\def \textmodep  {\ifmmode \false \else \true \fi}
\def \innermodep {\ifinner \true \else \false \fi}

\long\def \codeeqlp #1#2{\if #1#2\true \else \false \fi}

\long\def \cateqlp #1#2{\ifcat #1#2\true \else \false \fi}

\long\def \tokeqlp  #1#2{\ifx #1#2\true \else \false \fi}
\long\def \xtokeqlp #1#2{\expandafter\ifx #1#2\true \else \false \fi}

\long\def \definedp #1{%
  \expandafter\ifx \csname \expandafter\discardtok \string#1\endcsname
                   \relax \false \else \true \fi}

\long\def \undefinedp #1{%
  \expandafter\ifx \csname \expandafter\discardtok \string#1\endcsname
                   \relax \true \else \false \fi}
\def \zempty {}
\def \emptydefp #1{\ifx #1\zempty \true \else \false \fi}%       {\name}

\let \emptylistp = \emptydefp

\long\def \emptyargp #1{%                               {#n}
  \zempargp #1\zempargq\zmark}
\long\def \zempargp #1#2\zmark{%
  \ifx #1\zempargq \true \else \false \fi}
\def \zempargq {\zempargq}

\def \emptytoksp #1{%                                   {\tokenreg}
  \expandafter\zemptoksp \the#1\zmark}

\long\def \zemptoksp #1\zmark{\emptyargp{#1}}

\def \voidboxp #1{\ifvoid #1\true \else \false \fi}
\def \hboxp #1{\ifhbox #1\true \else \false \fi}
\def \vboxp #1{\ifvbox #1\true \else \false \fi}

\def \eofp #1{\ifeof #1\true \else \false \fi}


% Flags can also be used as predicates, as in:
%
%   \if \flaga <then-clause> \else <else-clause> \fi


% Now here we have predicates for the common logical operators.

\def \notp #1{\if #1\false \else \true \fi}

\def \andp #1#2{\if #1#2\else \false \fi}%              {boolean1}{boolean2}

\def \orp  #1#2{\if #1\true \else #2\fi}%               {boolean1}{boolean2}

%                       Pseudo-Predicates
%                       -----------------


\def \stringeql #1#2#3{%                {\return-flag}{string1}{string2}
  \edef \zstra {#2}%
  \edef \zstrb {#3}%
  \edef #1{\ifx \zstra\zstrb \true \else \false \fi}}


\def \gluevaries #1#2{%                 {\return-flag}{glue}
  \zskipa = #2\relax
  \tdimena = \zskipa
  \stringeql{#1}{\the\zskipa}{\the\tdimena}%
  \edef #1{\if #1\false \else \true \fi}}

%                       Arithmetic
%                       ----------


\def \negate #1{\multiply #1 by -1\relax}%              {\register}

\def \increment #1{\advance #1 by 1\relax}%             {\count}

\def \decrement #1{\advance #1 by -1\relax}%            {\count}

%                       Looping
%                       -------


\def \loop #1\repeat{%          body [actions] \if condition [actions] \repeat
  \def \zloopbody {#1}%
  \zloop}

\def \zloop {%
  \zloopbody
  %\if condition
    \let \zloopnext = \zloop
  \else
    \let \zloopnext = \relax
  \fi
  \zloopnext}

\let \repeat = \fi