%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % 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} \else \fi % A predicate can be used with \setflag as follows: % % \setflag \flag = {} % 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 \else \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