%% This is part of the OpTeX project, see http://petr.olsak.net/optex

\_codedecl \newif {Special if-macros, is-macros and loops <2024-02-19>} % preloaded in format

   \_doc ----------------------------
   \secc Classical \code{\\newif}
   The \`\newif` macro implements boolean value.
   It works as in plain \TeX. It means that
   after `\newif\ifxxx` you can use `\xxxtrue` or
   `\xxxfalse` to set the boolean value and use `\ifxxx true\else false\fi`
   to test this value. The default value is false.

   The macro \`\_newifi` enables to declare `\_ifxxx` and to use `\_xxxtrue` and
   `\_xxxfalse`. This means that it is usable for the internal namespace
   (`_`prefixed macros).
   \_cod ----------------------------

\_def\_newif #1{\_ea\_newifA \_string #1\_relax#1}
\_ea\_def \_ea\_newifA \_string\if #1\_relax#2{%
   \_sdef{#1true}{\_let#2=\_iftrue}%
   \_sdef{#1false}{\_let#2=\_iffalse}%
   \_let#2=\_iffalse
}
\_def\_newifi #1{\_ea\_newifiA \string#1\_relax#1}
\_ea\_def \_ea\_newifiA \_string\_if #1\_relax#2{%
   \_sdef{_#1true}{\_let#2=\_iftrue}%
   \_sdef{_#1false}{\_let#2=\_iffalse}%
   \_let#2=\_iffalse
}
\_public \newif ;

   \_doc ----------------------------
   \`\afterfi` `{<what to do>}<ignored>\fi` closes condition by `\fi` and processes
   <what to do>. Usage:
   \begtt \catcode`<=13
   \if<something> \afterfi{<result is true>} \else \afterfi{<result is false>} \fi
   \endtt
   Nested `\if..\afterfi{\if..\afterfi{...}\fi}\fi` are possible. Another approach is mentioned
   in \ulink[http://petr.olsak.net/optex/optex-tricks.html\#fiinif]{OpTeX trick 0098}
   which also solves the `\fi` in `\if` problem.
   \_cod ----------------------------

\_long\_def \_afterfi#1#2\_fi{\_fi#1}
\_long\_def \afterfi#1#2\fi{\_fi#1}

   \_doc ----------------------------
   \secc Loops
   The \`\loop` `<codeA> \ifsomething <codeB>` \`\repeat` loops `<codeA><codeB>`
   until `\ifsomething` is false. Then `<codeB>` is not executed and loop is
   finished. This works like in plain \TeX, but implementation is somewhat
   better (you can use `\else` clause after the `\ifsomething`).

   There are public version `\loop...\repeat` and private version
   \`\_loop` ...\`\_repeat`. You cannot mix both versions in one loop.

   The `\loop` macro keeps its original plain TeX meaning. It is not
   expandable and nested `\loop`s are possible only in a \TeX/ group.
   \_cod ----------------------------

\_long\_def \_loop #1\_repeat{\_def\_body{#1}\_iterate}
\_long\_def \loop #1\repeat{\_def\_body{#1}\_iterate}
\_let \_repeat=\_fi % this makes \loop...\if...\repeat skippable
\_let \repeat=\_fi
\_def \_iterate {\_body \_ea \_iterate \_fi}

   \_doc -----------------------------
   {\_let\_moremainpoints=\_relax
   \`\foreach` `<list>`\`\do` `{<what>}`
   repeats `<what>` for each element of the
   `<list>`. The `<what>` can include `#1` which is substituted by each
   element of the `<list>`. The macro is expandable.
   \nl
   \`\foreach` `<list>`\`\do` `<parameter-mask>{<what>}`
   reads parameters from <list> repeatedly and does <what> for each such reading.
   The parameters are declared by <parameter-mask>. Examples:
   \begtt
   \foreach (a,1)(b,2)(c,3)\do (#1,#2){#1=#2 }
   \foreach word1,word2,word3,\do #1,{Word is #1.}
   \foreach A=word1 B=word2 \do #1=#2 {"#1 is set as #2".}
   \endtt
   Note that `\foreach <list>\do {<what>}` is equivalent to `\foreach <list>\do #1{<what>}`.

   Recommendation: it is better to use private variants of \`\_foreach`.
   When the user writes `\input tikz` then `\foreach` macro is redefined in
   each TikZ environment.
   The private variants use \`\_do` separator instead `\do` separator.}
   \_cod -----------------------------

\_newcount\_frnum        % the numeric variable used in \fornum
\_def\_do{\_doundefined} % we need to ask \_ifx#1\_do ...

\_long\_def\_foreach #1\_do #2#{\_isempty{#2}\_iftrue
   \_afterfi{\_foreachA{#1}{##1}}\_else\_afterfi{\_foreachA{#1}{#2}}\_fi}
\_long\_def\_foreachA #1#2#3{\_putforstack
   \_immediateassignment \_long\_gdef\_fbody#2{\_testparam##1..\_iftrue #3\_ea\_fbody\_fi}%
   \_fbody #1#2\_finbody\_getforstack
}
\_long\_def\_testparam#1#2#3\_iftrue{\_ifx###1\_empty\_ea\_finbody\_else}
\_long\_def\_finbody#1\_finbody{}

\_long\_def\foreach #1\do#2#{\_isempty{#2}\_iftrue
   \_afterfi{\_foreachA{#1}{##1}}\_else\_afterfi{\_foreachA{#1}{#2}}\_fi}

   \_doc -----------------------------
   {\_let\_moremainpoints=\_relax
   \`\fornum` `<from>..<to>` \`\do` `{<what>}` or
   \`\fornumstep` `<num>: <from>..<to>` \`\do` `{<what>}`
   repeats `<what>` for each number from `<from>` to `<to>` (with step `<num>`
   or with step one). The `<what>` can include `#1` which is substituted by
   current number. The <from>, <to>, <step> parameters can be numeric expressions.
   The macro is expandable.\nl
   The test in the \`\_fornumB` says: if (<to> $\string<$ <current number>
   AND <step> is positive) or if (<to> $>$ <current number> AND <step> is
   negative) then close loop by `\_getforstack`. Sorry, the condition is
   writen by somewhat cryptoid \TeX/ language.}
   \_cod -----------------------------

\_def\_fornum#1..#2\_do{\_fornumstep 1:#1..#2\_do}
\_long\_def\_fornumstep#1:#2..#3\_do#4{\_putforstack
   \_immediateassigned{%
      \_gdef\_fbody##1{#4}%
      \_global\_frnum=\_numexpr#2\_relax
   }%
   \_ea\_fornumB\_ea{\_the\_numexpr#3\_ea}\_ea{\_the\_numexpr#1}%
}
\_def\_fornumB #1#2{\_ifnum#1\_ifnum#2>0<\_else>\_fi \_frnum \_getforstack
   \_else \_afterfi{\_ea\_fbody\_ea{\_the\_frnum}%
      \_immediateassignment\_global\_advance\_frnum by#2
      \_fornumB{#1}{#2}}\_fi
}
\_def\fornum#1..#2\do{\_fornumstep 1:#1..#2\_do}
\_def\fornumstep#1:#2..#3\do{\_fornumstep #1:#2..#3\_do}

   \_doc ----------------------------
   The `\foreach` and `\fornum` macros can be nested and arbitrary combined.
   When they are nested then use `##1` for the variable of nested level,
   `####1` for the variable of second nested level etc. Example:
   \begtt
   \foreach ABC \do {\fornum 1..5 \do {letter:#1, number: ##1. }}
   \endtt
   Implementation note:
   we cannot use \TeX/-groups for nesting levels because we want to do the
   macros expandable. We must implement a special for-stack which saves the
   data needed by `\foreach` and `\fornum`. The \`\_putforstack` is used
   when `\for*` is initialized and \`\_getforstack` is used when the
   `\for*` macro ends. The \`\_forlevel` variable keeps the current nesting
   level. If it is zero, then we need not save nor restore any data.
   \_cod ----------------------------

\_newcount\_forlevel
\_def\_putforstack{\_immediateassigned{%
   \_ifnum\_forlevel>0
      \_sxdef{_frnum:\_the\_forlevel\_ea}{\_the\_frnum}%
      \_global\_slet{_fbody:\_the\_forlevel}{_fbody}%
   \_fi
   \_incr\_forlevel
}}
\_def\_getforstack{\_immediateassigned{%
   \_decr\_forlevel
   \_ifnum\_forlevel>0
      \_global\_slet{_fbody}{_fbody:\_the\_forlevel}%
      \_global\_frnum=\_cs{_frnum:\_the\_forlevel}\_space
   \_fi
}}
\_ifx\_immediateassignment\_undefined % for compatibility with older LuaTeX
   \_let\_immediateassigned=\_useit \_let\_immediateassignment=\_empty
\_fi

   \_doc ----------------------------
   User can define own expandable \"foreach" macro by
   \`\foreachdef` `\macro <parameter-mask>{<what>}`
   which can be used by `\macro {<list>}`. The macro reads repeatedly parameters from <list>
   using <parameter-mask> and does <what> for each such reading. For example
   \begtt
   \foreachdef\mymacro #1,{[#1]}
   \mymacro{a,b,cd,efg,}
   \endtt
   expands to [a][b][cd][efg]. Such user defined macros are more effecive during processing
   than `\foreach` itself because they need not to operate with the for-stack.
   \_cod ----------------------------

\_def\_foreachdef#1#2#{\_toks0{#2}%
   \_long\_edef#1##1{\_ea\_noexpand\_csname _body:\_csstring#1\_endcsname
                     ##1\_the\_toks0 \_noexpand\_finbody}%
   \_foreachdefA#1{#2}}
\_long\_def\_foreachdefA#1#2#3{%
   \_long\_sdef{_body:\_csstring#1}#2{\_testparam##1..\_iftrue #3\_cs{_body:\_csstring#1\_ea}\_fi}}

\_public \foreachdef ;

   \_doc ----------------------------
   \secc Is-macros and selection of cases
   There are a collection of macros
   \^`\isempty`, \^`\istoksempty`, \^`\isequal`, \^`\ismacro`,
   \^`\isdefined`, \^`\isinlist`, \^`\isfile` and \^`\isfont`
   with common syntax:
   \begtt \catcode`\<=13
   \issomething <params> \iftrue <codeA> \else <codeB> \fi
   or
   \issomething <params> \iffalse <codeB> \else <codeA> \fi
   \endtt
   The `\else` part is optional. The `<codeA>` is processed if
   `\issomething<params>` generates true condition. The `<codeB>`
   is processed if `\issomething<params>` generates false condition.

   The `\iftrue` or `\iffalse` is an integral part of this syntax
   because we need to keep skippable nested `\if` conditions.

   Implementation note:
   we read this `\iftrue` or `\iffalse` into unseparated parameter and repeat
   it because we need to remove an optional space before this command.

   \medskip\noindent
   \`\isempty` `{<text>}\iftrue`
   is true if the `<text>` is empty. This macro is expandable.\nl
   \`\istoksempty` `<tokens variable>\iftrue`
   is true if the `<tokens variable>` is empty. It is expandable.
   \_cod ----------------------------

\_long\_def \_isempty #1#2{\_if\_relax\_detokenize{#1}\_relax \_else \_ea\_unless \_fi#2}
\_def \_istoksempty #1#2{\_ea\_isempty\_ea{\_the#1}#2}
\_public \isempty \istoksempty ;

   \_doc ----------------------------
   \`\isequal` `{<textA>}{<textB>}\iftrue`
   is true if the <textA> and <textB> are
   equal, only from strings point of view, category codes are ignored.
   The macro is expandable.
   \_cod ----------------------------

\_long\_def\_isequal#1#2#3{\_directlua{%
   if "\_luaescapestring{\_detokenize{#1}}"=="\_luaescapestring{\_detokenize{#2}}"
   then else tex.print("\_nbb unless") end}#3}
\_public \isequal ;

   \_doc ----------------------------
   \`\ismacro` `\macro{text}\iftrue` is true if macro is defined as {<text>}.
   Category codes are ignored in this testing. The macro is expandable.
   \_cod ----------------------------

\_long\_def\_ismacro#1{\_ea\_isequal\_ea{#1}}
\_public \ismacro ;

   \_doc ----------------------------
   \`\isdefined` `{<csname>}\iftrue` is true if `\<csname>` is defined.
   The macro is expandable.
   \_cod ----------------------------

\_def\_isdefined #1#2{\_ifcsname #1\_endcsname \_else \_ea\_unless \_fi #2}
\_public \isdefined ;

   \_doc ----------------------------
   \`\isinlist` `\list{<text>}\iftrue` is true if the
   `<text>` is included the macro body of the `\list`.
   The category codes are relevant here. The macro is expandable.
   \_cod ----------------------------

\_long\_def\_isinlist#1#2{%
   \_immediateassignment\_long\_def\_isinlistA##1#2##2\_end/_%
        {\_if\_relax\_detokenize{##2}\_relax \_ea\_unless\_fi}%
   \_ea\_isinlistA#1\_endlistsep#2\_end/_%
}
\_public \isinlist ;

   \_doc -----------------------------
   \`\isfile` `{<filename>}\iftrue` is true if the file <filename> exists and are
   readable by \TeX.
   \_cod -----------------------------

\_newread \_testin
\_def\_isfile #1{%
   \_openin\_testin ={#1}\_relax
   \_ifeof\_testin \_ea\_unless
   \_else \_closein\_testin
   \_fi
}
\_public \isfile ;

   \_doc -----------------------------
   \`\isfont` `{<fontname or [fontfile]>}\iftrue`
   is true if a given font exists. The result of this testing
   is saved to the \`\_ifexistfam`.
   \_cod -----------------------------

\_newifi \_ifexistfam
\_def\_isfont#1#2{%
   \_begingroup
       \_suppressfontnotfounderror=1
       \_font\_testfont={#1}\_relax
       \_ifx\_testfont\_nullfont \_def\_tmp{\_existfamfalse \_unless}
       \_else \_def\_tmp{\_existfamtrue}\_fi
   \_ea \_endgroup \_tmp #2%
}
\_public \isfont ;

   \_doc ----------------------------
   The macro \`\isnextchar` `<char>{<codeA>}{<codeB>}`
   has a different syntax than all other is-macros.
   It executes `<codeA>` if next character is equal to <char>.
   Else the `<codeB>` is executed. The macro is expandable.
   \_cod ----------------------------

\_long\_def\_isnextchar#1#2#3{\_immediateassignment
   \_def\_isnextcharA{\_isnextcharB{#1}{#2}{#3}}%
   \_immediateassignment\_futurelet \_next \_isnextcharA
}
\_long\_def\_isnextcharB#1{\_ifx\_next#1\_ea\_ignoresecond\_else\_ea\_usesecond\_fi}

\_public \isnextchar ;

   \_doc ----------------------------
   \`\casesof` `<token> <list of cases>` implements something similar to
   the `switch` command known from C language. It is expandable macro. The
   <list of cases> is a list of arbitrary number of pairs in the format
   `<token>`\,`{<what to do>}` which must be finalized by the pair
   `\_finc {<what to do else>}`. The optional spaces after <token>s
   and between listed cases are ignored. The usage of \^`\casesof` looks like:
   \begtt \catcode`<=13
   \casesof <token>
     <token-1> {<what to do if token=token-1>}
     <token-2> {<what to do if token=token-2>}
     ...
     <token-n> {<what to do if token=token-n>}
     \_finc   {<what to do in other cases>}
   \endtt
   The meaning of tokens are compared by `\ifx` primitive.
   The parts `<what to do>` can be finalized by a macro which can read more
   data from the input stream as its parameters.
   \_cod ----------------------------

\_long\_def \_casesof #1#2#3{\_ifx #2\_finc \_ea\_ignoresecond \_else \_ea\_usesecond \_fi
   {#3}{\_ifx#1#2\_ea\_ignoresecond \_else \_ea\_usesecond \_fi {\_finc{#3}}{\_casesof#1}}%
}
\_long\_def \_finc #1#2\_finc#3{#1}

\_public \casesof ;

   \_doc -----------------------------
   \`\qcasesof` `{<string>} <list of cases>` behaves like \^`\casesof` but it
   compares phrases with the given <string> using \^`\isequal`.
   The <list of cases> includes pairs
   `{<phrases>} {<what to do if string=phrase>}` finalized by a
   pair `\_finc {<what to do else>}`. The `<phrases>` is a single
   phrase or phrases separated by `|` which means \"or". For example
   the pair `{ab|cde|f} {<code>}` runs <code> if the given <string> is `ab` or `cde` or~`f`.
   The usage of `\qcasesof` can be found in
   \ulink[http://petr.olsak.net/optex/optex-tricks.html\#thedimen]{OpTeX trick 0132}.
   \_cod -----------------------------

\_long\_def \_qcasesof #1#2#3{\_ifx\_finc#2\_ea\_ignoresecond \_else \_ea\_usesecond \_fi
   {#3}{\_qcasesofA{#1}#2|\_qcasesofA|{\_finc{#3}}{\_qcasesof{#1}}}%
}
\_long\_def\_qcasesofA#1#2|{\_ifx\_qcasesofA#2\_ea\_usesecond \_else
   \_isequal{#1}{#2}\_iftrue \_qcasesofB \_fi \_afterfi{\_qcasesofA{#1}}\_fi
}
\_long\_def\_qcasesofB #1\_qcasesofA|#2#3{\_fi\_fi#2}

\_public \qcasesof ;

   \_doc -----------------------------
   \`\xcasesof` `<list of pairs>` extends the features of the macro \^`\casesof`.
   Each pair from the `<list of pairs>` is in the format
   `{<if statement>}{<what to do>}`, only the last pair must have the different
   format: `\_finc {<what to do else>}`. The `<if statement>` can be arbitrary
   primitive `\if*` condition (optionally prefixed by `\unless`) and it must be closed
   in its expansion. It means that `{\ifnum\mycount>0}` is bad, `{\ifnum\mycount>0 }`
   is correct. Optional spaces between parameters are ignored. Example:
   \begtt
   \message {The \tmpnum has \xcasesof
                     {\ifnum\tmpnum>0 } {positive}
                     {\ifnum\tmpnum=0 } {zero}
                     \_finc             {negative} value}
   \endtt
   The \^`\xcasesof` macro works with principle: first true condition wins,
   next conditions are not evaluated.
   \_cod -----------------------------

\_def \_xcasesof {\_nospacefuturelet\_next\_xcasesofA}
\_def \_xcasesofA {\_ifx\_next\_finc \_ea\_usesecond \_else \_ea \_xcasesofB \_fi}
\_long\_def \_xcasesofB #1#2{%
   #1\_ea\_ignoresecond\_else \_ea\_usesecond\_fi {\_finc{#2}}{\_xcasesof}%
}
\_public \xcasesof ;

\_endcode

2024-02-19 \qcasesof reimplemented, it supports | for more phrases.
2023-12-07 \_testparam define \long (big fixed).
2023-10-17 \qcasesof introduced, \xcasesof reimplemented.
2023-01-16 \isnextchar created expandable.
2022-12-02 \xcasesof: its first parameter is \long too.
2022-11-29 renamed to \casesof, \xcasesof.
2022-11-26 \casesby, \casesbyif introduced.
2022-05-04 \isinlist created expandable.
2022-03-30 \afterfi defined as \long.
2021-08-02 more robust \fornum: \fi moved by \afterfi
2021-02-03 public version of \loop and \foreach are \long
2020-05-22 \foreach, \fornum: all settings are global, independent on TeX group
2020-05-06 \isnextchar: \let\tmp=#1 -> \let\tmp= #1 (bug fix, #1 should be space)
2020-05-02 \newif bug fix
2020-04-15 \fornumstep 3: 1..12 instead \fornum 1..12\step 3
2020-04-15 \fornum, \foreach can be nested without groups
2020-04-01 implemented
