JezK
Edit File: more-macros.opm
%% This is part of the OpTeX project, see http://petr.olsak.net/optex \_codedecl \eoldef {OpTeX useful macros <2022-11-24>} % preloaded in format \_doc ----------------------------- We define \`\opinput` `{<file name>}` macro which does `\input {<file name>}` 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` `<catcode table>` pushes current catcode table to the stack and activates catcodes from the `<catcode table>`. 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 `<catcode table>`. 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` `[<filename-list>]` loads files specfied in comma separated `<filename-list>`. 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:<filename>` 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 [<opt default>] <params>{<replacement text>}` defines the `\macro` with the optional parameter followed by normal parameters declared in `<params>`. The optional parameter must be used as the first parameter in brackets `[...]`. If it isn't used then <opt default> is taken into account. The `<replacement text>` 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{\_opt={#2}\_isnextchar[{\_cs{_oA:\_csstring#1}}{\_cs{_oB:\_csstring#1}}}% \_sdef{_oA:\_csstring#1}[##1]{\_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 ----------------------------- The declarator \`\eoldef``\macro #1{<replacement text>}` 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 `<replacement text>`. 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{<parameter>}` 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 _\_csstring #1:M\_endcsname} \_catcode`\^^M=12 % \_def\_eoldefA #1#2^^M{\_endgroup\_csname _\_csstring #1:M\_endcsname{#2}}% \_normalcatcodes % \_eoldef\_skiptoeol#1{} \_def\_bracedparam#1{\_ifcsname _\_csstring #1:M\_endcsname \_csname _\_csstring #1:M\_ea \_endcsname \_else \_csname _in\_csstring #1\_ea \_endcsname \_fi } \_public \eoldef \skiptoeol \bracedparam ; \_doc ----------------------------- \`\scantoeol``\macro <text to end of line>` scans the `<text to end of line>` in verbatim mode and runs the `\macro{<text to end of line>}`. 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{<textA>}{<textB>}` replaces all occurrences of `<textA>` by `<textB>` in the `\macro` body. The `\macro` must be defined without parameters. The occurrences of `<textA>` are not replaced if they are \"hidden" in braces, for example `...{...<textA>...}...`. The category codes in the `<textA>` must exactly match. How it works: `\replstring\foo{<textA>}{<textB>}` prepares `\_replacestringsA#1<textA>{...}` and runs `\_replacestringsA<foo-body>?<textA>!<textA>`. So, `#1` includes the first part of <foo-body> before first <textA>. It is saved to \`\_tmptoks` and `\_replacestringsB` is run in a loop. It finishes processing or appends the next part to `\_tmptoks` separated by <textB> and continues loop. The final part of the macro removes the last `?` from resulting `\_tmptoks` and defines a new version of the `\foo`. The \^`\replstring` macro is not expandable, but you can create your expandable macro, for example: \begtt \def\replAB#1{\immediateassigned{\def\tmp{#1}\replstring\tmp{A}{B}}\the\_tmptoks} \replAB {text A \and A} % expands to "text B \and B" \endtt \_cod ----------------------------- \_newtoks\_tmptoks \_catcode`!=3 \_catcode`?=3 \_def\_replstring #1#2#3{% \replstring #1{stringA}{stringB} \_long\_def\_replacestringsA##1#2{\_tmptoks{##1}\_replacestringsB}% \_long\_def\_replacestringsB##1#2{\_ifx!##1\_relax \_else \_tmptoks\_ea{\_the\_tmptoks#3##1}% \_ea\_replacestringsB\_fi}% \_ea\_replacestringsA #1?#2!#2% \_long\_def\_replacestringsA##1?{\_tmptoks{##1}\_edef#1{\_the\_tmptoks}}% \_ea\_replacestringsA \_the\_tmptoks} \_normalcatcodes \_public \replstring ; \_doc ----------------------------- The \`\catcode` primitive is redefined here. Why? There is very common cases like \code{\\catcode`}`<something>` or `\catcode"<number>` 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` `<text with spaces >{}` expands to <textwithoutspaces>. \nl The `\_ea`\`\ignorept``\the<dimen>` expands to a decimal number `\the<dimen>` 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``<sequence>` 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``{<dimen>}` convertor from \TeX/ `<dimen>` (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``{<expression>}` for analogical purposes. It expands to the value of the `<expression>` at expand processor level with \`\_decdigits` digits after the decimal point. The `<expression>` can include `+-*/()` and decimal numbers in common syntax. The usage of prefixed versions \`\_expr` or \`\_bp` is more recommended 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{% \_directlua{tex.print(string.format('\_pcent.\_decdigits f', token.scan_dimen()/65781.76))}% pt to bp conversion } \def\_bp#1{\_ea\_pttopb\_dimexpr#1\_relax} \def\_expr#1{\_directlua{tex.print(string.format('\_pcent.\_decdigits f',#1))}} \_public \expr \bp ; \_doc ------------------ You can write \^`\setpos``[<label>]` somewhere and the position of such \^`\setpos``[<label>]` can be referenced by \^`\posx``[<label>]`, \^`\posy``[<label>]` and \^`\pospg``[<label>]`. The first two macros expand to $x$ and $y$ position measured from left-bottom corner of the page (dimen values) and \^`\pospg[<label>]` expands to the <gpageno>, i.e.\ to the page number counted from one at beginning of the document. These values are available in the second (and more) \TeX/ run, because the information is saved to `.ref` file and restored from it at the beginning of the \TeX/ job. If these values are not known then mentioned macros expand to 0sp, 0sp and 0. The following example implements `\linefrom[<label>]` and `\lineto[<label>]` macros. The line connecting these two points is drawn (after second \TeX/ run): \begtt \def\linefrom[#1]{\setpos[#1:f]\drawlinefromto[#1]} \def\lineto [#1]{\setpos[#1:t]} \def\drawlinefromto[#1]{\ifnum\pospg[#1:f]>0 \ifnum\pospg[#1:f]=\pospg[#1:t] \pdfliteral{q 0 0 m 1 0 0 RG % << red color \expr{\bp{\posx[#1:t]}-\bp{\posx[#1:f]}} \expr{\bp{\posy[#1:t]}-\bp{\posy[#1:f]}} l S Q}\fi\fi } This is a text.\linefrom[A]\par This is second paragraph with a text.\lineto[A] Try to reverse from-to and watch the changes. \endtt The coordinates are saved to the `.ref` file in the format \`\_Xpos``{<label>}{<x-pos>}{<y-pos>}`. The \^`\_Xpos` macro defines `\_pos:<label>` as `{<x-pos>}{<y-pos>}{<total-pg>}{<rel-pg>}`. We need to read only given parameter by `\_posi`, `\_posii` or `\_posiii` auxiliary macros. The implementation of \`\setpos`, \`\posx` and \`\posy` macros are based on `\padsavepos` `\pdflastxpos` and `\pdflastypos` pdf\TeX/ primitives. The \`\pospg` simply reads the data from the \~`\_currpage` macro. \_cod ------------------ \_def\_Xpos#1#2#3{\_sxdef{_pos:#1}{{#2}{#3}\_currpage}} \_def\_setpos[#1]{\_openref\_pdfsavepos \_ewref\_Xpos{{#1}\_unexpanded{{\_the\_pdflastxpos}{\_the\_pdflastypos}}}} \_def\_posx [#1]{\_ea \_posi \_expanded {\_trycs{_pos:#1}{{0}{}{}{}}sp}} \_def\_posy [#1]{\_ea \_posii \_expanded {\_trycs{_pos:#1}{{}{0}{}{}}sp}} \_def\_pospg[#1]{\_ea \_posiii \_expanded {\_trycs{_pos:#1}{{}{}{0}{}}}} \_def\_posi #1#2#3#4{#1} \_def\_posii #1#2#3#4{#2} \_def\_posiii #1#2#3#4{#3} \_public \setpos \posx \posy \pospg ; \_doc ------------------ The pair {\`\_doc` ... \`\_cod`} is used for documenting macros and to printing the technical documentation of the \OpTeX/. The syntax is: {\begtt \catcode`\<=13 \_doc <ignored text> <documentation> \_cod <ignored text> \endtt } The `<documentation>` (and `<ignored text>` too) must be `<balanced text>`. It means that you cannot document only the `{` but you must document the `}` too. \_cod ------------------ \_long\_def\_doc #1\_cod {\_skiptoeol} \_doc ------------------ \`\docgen` processes lines before `\_codedecl` because the version text in the macro `\_<pkg>_version` can be defined here. The package documentation can print it. \^`\docgen` prints banner to log because TeX doesn't do it when command line doesn't begin with the main file name after parameters. \_cod ------------------ \_def\_docgen #1 {\_ea \_docgenA \_input{#1.opm}} \_long \_def\_docgenA #1\_codedecl#2\_endcode #3\_doc {#1\_wlog{\_banner}\_skiptoeol} \_public \docgen ; \_endcode % ------------------------------------- 2022-11-24 \setpos, \posx, \posy moved from OpTeX trick here 2022-11-22 \docgen writes banner to log 2022-11-20 \docgen reads lines before \_codedecl in order to define pkg. version 2022-11-18 \cstochar introduced 2022-11-13 \docgen introduced, \bracedparam corrected, \scantoeol modified 2022-11-05 \replstring can be expandable using \immediateassigned: \toksapp replaced 2022-11-03 \nospacefuturelet added. 2022-10-27 \_savemathsb ...\_restoremathsb added to \load (eg. \load[xstring]) 2022-05-24 \optdef: used \csstring instead \string, bug fixed. 2021-04-25 \normalcatcodes: typo fixed 2021-04-22 \the\_currctable replaced by \the\_catcodetable, bug fixed. 2020-05-22 robust \catcode newly defined in order \catcode\string.... be possible 2020-05-03 \load macro introduced 2020-03-15 released