%% This is part of the OpTeX project, see http://petr.olsak.net/optex \_codedecl \eoldef {OpTeX useful macros <2024-10-31>} % preloaded in format \_doc ----------------------------- We define \`\opinput` `{}` macro which does `\input {}` but the catcodes are set to normal catcodes (like \OpTeX/ initializes them) and the catcodes setting is returned back to the current values when the file is read. You can use `\opinput` in any situation inside the document and you will be sure that the file is read correctly with correct catcode settings. To achieve this, we declare \`\optexcatcodes` catcode table and \`\plaintexcatcodes`. They save the commonly used catcode tables. Note that `\catcodetable` is a part of \LuaTeX/ extension. The catcodetable stack is implemented by \OpTeX/ macros. The \`\setctable` `` pushes current catcode table to the stack and activates catcodes from the ``. The \`\restorectable` returns to the saved catcodes from the catcode table stack. The `\opinput` works inside the catcode table stack. It reads `\optexcatcodes` table and stores it to \`\_tmpcatcodes` table. This table is actually used during `\input` (maybe catcodes are changed here). Finally, `\_restoretable` pops the stacks and returns to the catcodes used before `\opinput` is run. \_cod ----------------------------- \_def\_opinput #1{\_setctable\_optexcatcodes \_savecatcodetable\_tmpcatcodes \_catcodetable\_tmpcatcodes \_input {#1}\_relax\_restorectable} \_newcatcodetable \_optexcatcodes \_newcatcodetable \_plaintexcatcodes \_newcatcodetable \_tmpcatcodes \_public \optexcatcodes \plaintexcatcodes \opinput ; \_savecatcodetable\_optexcatcodes {\_catcode`_=8 \savecatcodetable\plaintexcatcodes} \_doc ----------------------------- The implementation of the catcodetable stack follows. The current catcodes are managed in the `\catcodetable0`. If the `\setctable` is used first (or at the outer level of the stack), then the `\catcodetable0` is pushed to the stack and the current table is re-set to the given ``. The numbers of these tables are stacked to the \`\_ctablelist` macro. The `\restorectable` reads the last saved catcode table number from the `\_ctablelist` and uses it. \_cod ----------------------------- \_catcodetable0 \_def\_setctable#1{\_edef\_ctablelist{{\_the\_catcodetable}\_ctablelist}% \_catcodetable#1\_relax } \_def\_restorectable{\_ea\_restorectableA\_ctablelist\_relax} \_def\_restorectableA#1#2\_relax{% \_ifx^#2^\_opwarning {You can't use \_noindent\restorectable without previous \_string\setctable}% \_else \_def\_ctablelist{#2}\_catcodetable#1\_relax \_fi } \_def\_ctablelist{.} \_public \setctable \restorectable ; \_doc ----------------------------- When a special macro is defined with different catcodes then \`\normalcatcodes` can be used at the end of such definition. The normal catcodes are restored. The macro reads catcodes from `\optecatodes` table and sets it to the main catcode table 0. \_cod ----------------------------- \_def\_normalcatcodes {\_catcodetable\_optexcatcodes \_savecatcodetable0 \_catcodetable0 } \_public \normalcatcodes ; \_doc ----------------------------- The \`\load` `[]` loads files specified in comma separated ``. The first space (after comma) is ignored using the trick `#1#2,`: first parameter is unseparated. The `\load` macro saves information about loaded files by setting `\_load:` as a defined macro. If the \`\_afterload` macro is defined then it is run after `\_opinput`. The catcode setting should be here. Note that catcode setting done in the loaded file is forgotten after the `\opinput`. \_cod ----------------------------- \_def \_load [#1]{\_savemathsb \_loadA #1,,,\_end \_restoremathsb} \_def \_loadA #1#2,{\_ifx,#1 \_ea \_loadE \_else \_loadB{#1#2}\_ea\_loadA\_fi} \_def \_loadB #1{% \_ifcsname _load:#1\_endcsname \_else \_isfile {#1.opm}\_iftrue \_opinput {#1.opm}\_else \_opinput {#1}\_fi \_sxdef{_load:#1}{}% \_trycs{_afterload}{}\_let\_afterload=\_undefined \_fi } \_def \_loadE #1\_end{} \_public \load ; \_doc ----------------------------- The declarator \`\optdef``\macro [] {}` defines the `\macro` with the optional parameter followed by normal parameters declared in ``. The optional parameter must be used as the first parameter in brackets `[...]`. If it isn't used then is taken into account. The `` can use `\the\opt` because optional parameter is saved to the \`\opt` tokens register. Note the difference from \LaTeX/ concept where the optional parameter is in `#1`. \OpTeX/ uses `#1` as the first normal parameter (if declared). The \`\nospaceafter` ignores the following optional space at expand processor level using the negative `\romannumeral` trick. The \`\nospacefuturelet` bahaves like `\futurelet` primitive, but it ignores the following optional space and works at expand processor level. \_cod ----------------------------- \_newtoks\_opt \_def\_optdef#1[#2]{% \_def#1{\_isnextchar[{\_cs{_oA:\_csstring#1}}{\_cs{_oA:\_csstring#1}[#2]}}% \_sdef{_oA:\_csstring#1}[##1]{% \_immediateassignment\_opt={##1}\_cs{_oB:\_csstring#1\_nospaceafter}}% \_sdef{_oB:\_csstring#1\_nospaceafter}% } \_def\_nospaceafter#1{\_ea#1\_romannumeral-`\.\_noexpand} \_def\_nospacefuturelet#1#2{\_ea\_immediateassignment \_ea\_futurelet\_ea#1\_ea#2\_romannumeral-`\.\_noexpand} \_public \opt \optdef \nospaceafter \nospacefuturelet ; \_doc ----------------------------- \`\_noprefix` `` works like `\csstring` ``, but ignores not only the first backlash but the second~\"`_`" ignores too (if it follows the backslash). \_cod ----------------------------- \_def\_noprefix#1{\_ea\_noprefixA \_csstring#1\_empty\_fin} \_def\_noprefixA #1#2\_fin{\_if _#1\_else #1\_fi #2} \_doc ----------------------------- The declarator \`\eoldef``\macro #1{}` defines a `\macro` which scans its parameter to the end of the current line. This is the parameter `#1` which can be used in the ``. The catcode of the `\endlinechar` is reset temporarily when the parameter is scanned. The macro defined by \^`\eoldef` cannot be used with its parameter inside other macros because the catcode dancing is not possible here. But the \`\bracedparam``\macro{}` can be used here. The `\bracedparam` is a prefix that re-sets temporarily the `\macro` to a `\macro` with normal one parameter. The \`\skiptoeol` macro reads the text to the end of the current line and ignores it. \_cod ----------------------------- \_def\_eoldef #1{\_def #1{\_begingroup \_catcode`\^^M=12 \_eoldefA #1}% \_ea\_def\_csname _eol:\_noprefix #1\_endcsname} \_catcode`\^^M=12 % \_def\_eoldefA #1#2^^M{\_endgroup\_csname _eol:\_noprefix #1\_endcsname{#2}}% \_normalcatcodes % \_eoldef\_skiptoeol#1{} \_def\_bracedparam#1{% \_trycs{_eol:\_noprefix#1}% {\_errmessage{\_string\bracedparam: \_string#1 isn't defined by \_string\eoldef}}% } \_public \eoldef \skiptoeol \bracedparam ; \_doc ----------------------------- \`\scantoeol``\macro ` scans the `` in verbatim mode and runs the `\macro{}`. The `\macro` can be defined `\def\macro#1{...\scantextokens{#1}...}`. The new tokenization of the parameter is processed when the parameter is used, no when the parameter is scanned. This principle is used in definition of \^`\chap`, \^`\sec`, \^`\secc` and \^`\_Xtoc` macros. It means that user can write \code{\\sec text `&` text} for example. Inline verbatim works in title sections. The verbatim scanner of `\scatoeol` keeps category 7 for `^` in order to be able to use `^^J` as comment character which means that the next line continues. \_cod ----------------------------- \_def\_scantoeol#1{\_begingroup \_setscancatcodes \_scantoeolA #1} \_def\_setscancatcodes{\_setverb \_catcode`\^^M=12\_catcode`\^=7\_catcode`\ =10\_catcode`\^^J=14 } \_catcode`\^^M=12 % \_def\_scantoeolA#1#2^^M{\_endgroup #1{#2}}% \_normalcatcodes % \_public \scantoeol ; \_doc ----------------------------- The \`\replstring``\macro{}{}` replaces all occurrences of `` by `` in the `\macro` body. The `\macro` must be defined without parameters. The occurrences of `` are not replaced if they are \"hidden" in braces, for example `...{......}...`. The category codes in the `` must exactly match. How it works: `\replstring\foo{}{}` prepares `\_replacestringsA#1{...}` and runs `\_replacestringsA?!`. So, `#1` includes the first part of before first . It is saved to \`\_repltoks` and `\_replacestringsB` is run with `\_replacestingsC` in a loop. It finishes processing (if final `!` is scanned) or appends the next part to `\_repltoks` separated by and continues loop. The final part of the \^`\replstring` macro removes the last `?` from resulting `\_repltoks` and defines a new version of the `\foo`. \_cod ----------------------------- \_newtoks\_repltoks \_catcode`!=3 \_catcode`?=3 \_long\_def\_replstring #1#2#3{% \replstring #1{stringA}{stringB} \_long\_def\_replacestringsA##1#2{\_repltoks\_ea{##1}\_replacestringsB\_empty}% \_long\_def\_replacestringsB##1#2{\_ea\_replacestringsC\_ea{##1}}% \_long\_def\_replacestringsC##1{\_ifx!##1\_empty\_else \_toksapp\_repltoks{#3##1}\_ea\_replacestringsB\_ea\_empty\_fi}% \_ea\_replacestringsA \_ea\_empty#1?#2!#2% \_ea\_replacestringsD \_ea\_empty\_the\_repltoks#1} \_long\_def\_replacestringsD #1?#2{\_repltoks\_ea{#1}\_edef#2{\_the\_repltoks}}% \_normalcatcodes \_public \replstring ; \_doc ----------------------------- You can find three special tricks in the \^`\replstring` code: \begitems * Adding and removing `?` character: without this the `\def\b{xxxA}\replstring\b{AA}{BB}` will cause an error. * Using `\_toksapp` instead `\addto`: without this you cannot have $`#`_6$ in the string. Moreover, it significantly reduces the calculation time. Unfortunately, it does \^`\replstring` unexpandable. * Using `\_empty` before scanning separated parameters and removing it after scanning by \^`\ea`: without this \TeX/ removes braces from `{xxx}`. \enditems The \^`\replstring` macro isn't expandable. But you can use \ulink[http://petr.olsak.net/optex/optex-tricks.html\#xreplstring]{\OpTeX/ trick 0136}. The expandable \^`\xreplstring` macro with more features is defined by Lua code here. And \ulink[http://petr.olsak.net/optex/optex-tricks.html\#replmacro]{\OpTeX/ trick 0137} defines \^`\replmacro` which enables more general modifications of macros by regular expressions. \_cod ----------------------------- \_doc ----------------------------- The \`\catcode` primitive is redefined here. Why? There is very common cases like \code{\\catcode`}`` or `\catcode"` but these characters \code{\`} or \code{"} can be set as active (typically by `\verbchar` macro). Nothing problematic happens if re-defined `\catcode` is used in this case. If you really need primitive `\catcode` then you can use `\_catcode`. \_cod ----------------------------- \_def\catcode#1{\_catcode \_if`\_noexpand#1\_ea`\_else\_if"\_noexpand#1"\_else \_if'\_noexpand#1'\_else \_ea\_ea\_ea\_ea\_ea\_ea\_ea#1\_fi\_fi\_fi} \_doc ----------------------------- The \`\removespaces` `{}` expands to . \nl The `\_ea`\`\ignorept``\the` expands to a decimal number `\the` but without `pt` unit. \_cod ----------------------------- \_def\_removespaces #1 {\_isempty{#1}\_iffalse #1\_ea\_removespaces\_fi} \_ea\_def \_ea\_ignorept \_ea#\_ea1\_detokenize{pt}{#1} \_public \removespaces \ignorept ; \_doc ----------------------------- If you do `\let\foo=a` then it is not simple to return from `\foo` to the original character code of `a`. You can write \code{`a} but you cannot write \code{`\\foo}. The macro \`\cstochar``` solves this problem. If the sequence is equal to a character then it expands to this character (always with catcode 12). If it isn't equal to a character then it expands to nothing. You can say \code{\\expanded{`\\cstochar\\foo}} if you want to extract the character code. \_cod ----------------------------- \_def\_cstochar#1{\_ea\_cstocharA\_meaning#1 {} {} \_fin} \_def\_cstocharA#1 #2 #3 #4\_fin{\_isinlist{#1#2}-\_iffalse #3\_fi} \_public \cstochar ; \_doc ----------------------------- You can use expandable \`\bp``{}` converter from \TeX/ `` (or from an expression accepted by `\dimexpr` primitive) to a decimal value in big points (used as natural unit in the PDF format). So, you can write, for example: \begtt \pdfliteral{q \_bp{.3\hsize-2mm} \_bp{2mm} m 0 \_bp{-4mm} l S Q} \endtt You can use expandable \`\expr``{}` for analogical purposes. It expands to the value of the `` at expand processor level. The `` can include `+-*/^()` and decimal numbers in common syntax. Moreover, `a//b` means integer division and `a\%b` is remainder. The math functions (and pi constant) have to be prefixed by `math.`, because it is processed by Lua interpreter. For example `\expr{math.pi*math.sqrt(2)}`. The list of available functions is in \ulink[https://www.lua.org/manual/5.3/manual.html\#6.7]{Lua manual}. You can set the number of decimal digits after decimal point of the results of `\bp` and `\expr` by optional syntax `\bp[]{}` and `\expr[]{}`. Default is \`\_decdigits`. The usage of prefixed versions `\_expr` or `\_bp` is more recommended for macro programmers because a user can re-define the control sequences `\expr` or `\bp`. \_cod ----------------------------- \_def\_decdigits{3} % digits after decimal point in \_bp and \_expr outputs. \_def\_pttopb#1{% \_directlua{tex.print(string.format('\_pcent.#1f', token.scan_dimen()/65781.76))}% pt to bp conversion } \_def\_bp{\_isnextchar[{\_bpA}{\_bpA[\_decdigits]}} \_def\_bpA[#1]#2{\_pttopb{#1}\_dimexpr#2\_relax} \_def\_expr{\_isnextchar[{\_exprA}{\_exprA[\_decdigits]}} \_def\_exprA[#1]#2{\_directlua{tex.print(string.format('\_pcent.#1f',#2))}} \_public \expr \bp ; \_doc ----------------------------- The \^`\expr` and \^`\bp` macros return their results with given number of decimal digits even if there are trailing zeros. There is the \^`\nnum` macro to \"normalize" such decimal numbers. \`\nnum``{}` expands its parameter and removes trailing zeros after decimal point and removes the decimal point if nothing follows. For example, use `\nnum{\expr[10]{}}`. The `\nnum` macro is fully expandable. \_cod ----------------------------- \_def\_nnum #1{\_ea\_nnumA\_expanded{#1}.\_fin} \_def\_nnumA #1.#2\_fin{#1\_ifx~#2~\_else \_nnumB #20.\_fin \_fi} \_def\_nnumB #10.#2\_fin{\_ifx~#2~\_nnumC#1\_else \_nnumB #1.0.\_fin \_fi} \_def\_nnumC #1.{\_ifx~#1~\_else .#1\_fi} \_public \nnum ; \_doc ------------------ You can write \^`\setpos``[