diff options
author | Karl Berry <karl@freefriends.org> | 2010-06-18 15:55:40 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2010-06-18 15:55:40 +0000 |
commit | 6a610c4ffff82f7e5188bd0154b7859244e02447 (patch) | |
tree | 39e69457f4d5831b124063e76e5ad654679ae4ee /Master/texmf-dist | |
parent | 79527eba1a0ef70a5b911ebacf7fdc03a24615e9 (diff) |
new generic package texapi (17jun10)
git-svn-id: svn://tug.org/texlive/trunk@19035 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist')
-rw-r--r-- | Master/texmf-dist/doc/generic/texapi/README | 23 | ||||
-rw-r--r-- | Master/texmf-dist/doc/generic/texapi/texapi-doc.pdf | bin | 0 -> 188234 bytes | |||
-rw-r--r-- | Master/texmf-dist/doc/generic/texapi/texapi-doc.tex | 1865 | ||||
-rw-r--r-- | Master/texmf-dist/tex/generic/texapi/texapi.tex | 899 |
4 files changed, 2787 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/generic/texapi/README b/Master/texmf-dist/doc/generic/texapi/README new file mode 100644 index 00000000000..83581ee38ba --- /dev/null +++ b/Master/texmf-dist/doc/generic/texapi/README @@ -0,0 +1,23 @@ +This is the README file for the texapi package. +Author: Paul Isambert. +E-mail: zappathustra AT free DOT fr +Comments and suggestions are welcome. +Date: June 2010. + +The package is set of macros to write +format-independent (and -aware) packages. +It is, of course, format-independent, +and requires e-TeX. +Relevant information can be found in texapi-doc.pdf + +Files in this distributions: + +texapi.tex => The package (no wrapper files for format, of course). +texapi-doc.tex => The source of the documentation. +texapi-doc.pdf => The typeset documentation. + +To process the documentation source, you need the +piTeX files (available on CTAN) on top of plain TeX +with LuaTeX at least v.0.6. Non-free fonts are used. + +Licensing of this package is covered by LPPL.
\ No newline at end of file diff --git a/Master/texmf-dist/doc/generic/texapi/texapi-doc.pdf b/Master/texmf-dist/doc/generic/texapi/texapi-doc.pdf Binary files differnew file mode 100644 index 00000000000..f0ca5546c18 --- /dev/null +++ b/Master/texmf-dist/doc/generic/texapi/texapi-doc.pdf diff --git a/Master/texmf-dist/doc/generic/texapi/texapi-doc.tex b/Master/texmf-dist/doc/generic/texapi/texapi-doc.tex new file mode 100644 index 00000000000..b50b850f928 --- /dev/null +++ b/Master/texmf-dist/doc/generic/texapi/texapi-doc.tex @@ -0,0 +1,1865 @@ +\input pitex % which \input's texapi (and YaX) + +\directlua{ + fonts.expansions.setups['threepercent'].shrink = 4 + fonts.expansions.setups['threepercent'].stretch = 4 + } + +\setparameter document: + author = "Paul Isambert" + title = \texapi + pdftitle = TeXapi + version = 1.0 % hopefully... + date = "\the\month/\the\day/\the\year" + subject = "The TeXapi documentation." + display = outlines + +\setparameter page: + pagewidth = 32cm + pageheight = 30cm + lines = 45 + hsize = 10cm + baselineskip = 14pt + left = 10cm + top = 5cm + parindent = 2em + +\setparameter font: + command = \titlefont + name = ChaparralPro- + size = 20pt + roman = Regular + +\setparameter font: + command = \tmainfont + name = ChaparralPro- + size = 12pt + roman = Regular + bold = Bold + italic = Italic + +\setparameter font: + command = \csmainfont + name = ChaparralPro- + size = 11pt + roman = Italic + bold = Bold + italic = Regular + +\def\mainfont{% + \ifcslist \csmainfont + \else \tmainfont \fi} + +\setparameter font: + command = \tcodefont + name = ltype + size = 9.5pt + type = ttf + roman = {} + bold = b + italic = o + +\setparameter font: + command = \cscodefont + name = ltype + size = 8.5pt + type = ttf + roman = {} + bold = b + italic = o + +\def\codefont{% + \ifcslist \cscodefont + \else \tcodefont\fi} +\def\tcode#1{{\codefont#1}}% + +% +% +% OUTPUT +% +\newbox\codebox +\newdimen\sepdim +\sepdim=1.2pc + +\output={% + \setbox255=\vtop{\unvbox255} + \setbox\codebox=\vtop{\unvbox\codebox} + \ht255=\baselineskip + \ht\codebox=\baselineskip + \unless\ifx\bookmarktitle\prevbookmarktitle + {\def\TeX{TeX}\pdfoutline goto num \count0 {\bookmarktitle}} + \fi + \global\let\prevbookmarktitle\bookmarktitle + \shipout\vbox{% + \vbox to 0pt{% + \vss + \kern-20pt\hbox{\kern-120pt \pdfdest num \count0 xyz}\kern20pt + \hbox to \hsize{% + \spaceskip .8em plus .6em minus .3em + \titlefont\sc\descriptiontitle} + \kern2\baselineskip}% + \hbox{% + \box255 + \kern\sepdim + \pdfliteral{ + q + .95 g + -3 200 300 -1000 re f + Q + } + \box\codebox + }% + } + \advancepageno + } + +% +% +% \cslist + +\def\red{\color{.9 0 0}} +\def\rcom#1{\red{\com#1}} +\newbox\cslistbox +\newife\ifexpcs +\newif\ifcslist +\newbox\tempbox +\newdimen\tempdimen +\newdimen\cswidth +\newcount\cslistcount +\def\zilch{\zilch} +\extraboxspace=1.5pt +\def\macrobox{\colorbox{.92 .92 .92}} +\def\definecs#1#2{ + \quitvmode + \hbox to\cswidth{\iffexpcs\macrobox{\unless\ifx#1\zilch\rcom#1\fi}\hfill}% + \setcatcodes{/=13}% + \scantextokens{#2}% + \restorecatcodes + } +\def\longdefinecs#1#2\end{\definecs#1{#2}} +\def\getwidth#1#2\end{% + \bgroup + \setbox0=\hbox{\codefont\string#1}% + \ifdim\wd0>\cswidth + \global\cswidth=\wd0 + \fi + \egroup + } +\def\cslist#1{% + \vskip\baselineskip + \cslisttrue \cslistcount=0 + \cswidth=0pt + \dofornoempty{#1,}##1,{% + \deftrim\temp{##1}% + \passexpanded{\iffprefix!}{\temp} + {\passexpanded{\removeprefixin!}{\temp}\temp}% + \expandafter\getwidth\temp\end + }{}% +% + \tempdimen=0pt + \dofornoempty{#1,}##1,{% + \advance\cslistcount1 + \deftrim\temp{##1}% + \passexpanded{\ifprefix!}{\temp} + {\expcstrue\passexpanded{\removeprefixin!}{\temp}\temp} + {\expcsfalse}% + \setbox\tempbox\hbox{\expandafter\longdefinecs\temp\end\kern\sepdim}% + \ifdim\wd\tempbox>\tempdimen + \tempdimen=\wd\tempbox + \fi + }{}% +% + \setbox\cslistbox=\vtop{% + \dofornoempty{#1,}##1,{% + \deftrim\temp{##1}% + \passexpanded{\ifprefix!}{\temp} + {\expcstrue\passexpanded{\removeprefixin!}{\temp}\temp} + {\expcsfalse}% + \llap{\hbox to\tempdimen{\expandafter\longdefinecs\temp\end\hfil}}% + }{}% + }% + \dp\cslistbox=0pt + \noindent \box\cslistbox + \def\par{% + \endgraf + \let\par\endgraf + \ifnum\prevgraf<\cslistcount + \vskip\the\numexpr(\cslistcount-\prevgraf)\baselineskip + \fi}% + \cslistfalse \ignorespaces + } +% +\bgroup +\setcatcodes{/=13} +\gdef/#1/{\arg{#1}} +\egroup + +\def\true{\arg{true}} +\def\false{\arg{false}} +\def\csarg{\arg{csname}} +\def\comarg{\arg{command}} + + +% +% EXAMPLES +% + +\def\processexample{% + \global\setbox\codebox=\vbox{% + \parindent0pt + \ifvoid\codebox + \quitvmode\vrule height \topskip depth 0pt width 0pt + \else + \dimen0=\dp\codebox + \box\codebox + \vskip\baselineskip + \color{1 1 1}{\hrule width \hsize height 1pt depth 1pt}% + \kern-2pt + \vskip\baselineskip + \prevdepth=\dimen0 + \fi + \let\exampleskip\relax + \cslisttrue \codefont + \typesetexample + \vskip\baselineskip + \mainfont + \doexample + }% + } + +\newblock\description + {\getdescriptiontitle} + {\penalty-10000\relax} + +\def\getdescriptiontitle#1{% + \def\bookmarktitle{#1}% + \def\descriptiontitle{}% + \dofor{#1 }##1 {% + \dofor{##1}####1{% + \addright\descriptiontitle{\lowercase{####1}\hskip0pt plus 8pt minus 2pt}% + }{}% + \addright\descriptiontitle{\unskip\spacecs}% + }{}% + \addright\descriptiontitle{\unskip}% + } +\def\texapi{% + {\codefont texapi}% + \antigobblespace + } + + +\mainfont + + + +\description{Writing macros with texapi} + +\setbox\codebox=\vbox to \hsize{% + \csmainfont + \hfill \vrule height\topskip width0pt Author: \usevalue document : author \par + \hfill Version: \usevalue document : version \par + \hfill Date: \usevalue document : date \par + \vskip\baselineskip + \noindent Typeset in Chaparral Pro (Carol Twombly) + and Lucida Sans Typewriter (Charles Bigelow and Kris Holmes) + with Lua\TeX\ v.\the\luatexversion.\luatexrevision. + + } + +The first motivation for this set of macros is selfish: +after rewriting the same lines over and over and wasting +so many excruciating (yes!) hours debugging intricate loops +with one typo, I decided I could use a toolkit containing +the painful code without errors (hopefully) and use it +for future packages. + +The second motivation is more ambitious: I think it's a pity +so many packages are written for one format and are thus +unusable outside it, even though those packages could be useful +to anybody. This is so, I believe, because a format mixes two +different things: decisions about typesetting (mainly) and utility macros. +The former are the essence of a format, whereas the latter are just +shorthands you can use or not, replace, or ignore completely. +But the fact is that users of a format tend to use the utility macros +shipped with it, and thus writes macros that can't be reused elsewhere, +even though nothing crucial hinges on what utility macros one uses. +Thus, \texapi aims at providing a good deal of this kind of macros +whitout relying on any particular format, so that one can write +code without having to take into account how it will be used. +Moreover, \texapi is also format-aware, meaning some commands +are defined differently depending on the format being used, +and one doesn't have to create as many macros as there are formats. + +There is at least one basic assumptions, namely that formats +should contain plain \TeX's allocation macros. This the case for +all formats I know. + +\vskip\baselineskip +In what follows, commands have a \macrobox{grey background} when they +are fully expandable, e.g. they can be used inside \com\csname...\com\endcsname, +provided you don't use them with unexpandable arguments, of course. +On the other hand, all unexpandable commands are protected. + +Arguments are denoted by \arg{text}, where `text' makes the intended use +clearer and doesn't denote any particular type of argument, except in +the case of \comarg, which denotes a control sequence (something expandable, +actually), and \csarg, which denotes an argument suitable to \com\csname. +Braces are indicated only when mandatory, but of course they can be used to delimit +arguments as usual. + +\description/ + + + +\description{Engine and format detection} + +\cslist{ + \texenginenumber + } +This is a \com\chardef'ined number set according to the +engine used: 0 means e-\TeX, or an unknown engine with +e-\TeX\ extensions; 1 means Xe\TeX\ (detected because +\com\XeTeXinterchartoks exists); 2 means pdf\TeX\ (detected +thanks to \com\pdfstrcmp); 3 means Lua\TeX\ (detected thanks +to \com\directlua). Numbering here allows one to detect a +pdf\TeX-based engine with \com\texenginenumber>1. Con\TeX t +has an equivalent \com\texengine, with pdf\TeX=1 and Xe\TeX=2, +though. + +\cslist{ + \formatnumber + } +This number does the same with formats. Here, 0 means an unknown +format, 1 means plain (because \com\fmtname is `\tcode{plain}'), +2 means eplain (because \com\fmtname is `\tcode{eplain}'), +3 means Con\TeX t (because there exists an \com\inspectnextoptionalcharacter +command), 4 means La\TeX2e (because \com\fmtname is `\tcode{LaTeX2e}') +and 5 means La\TeX3 (because there exists an \com\ExplSyntaxOn command). +There's no distinction yet between La\TeX3 on top of La\TeX2e and La\TeX3 +as a format per se. Since \com\formatnumber is set only if it doesn't +already exist, you can write your package with, say, \tcode{code.tex} +containing the main code and \tcode{code.sty} and \tcode{t-code.tex} +as wrapper files for La\TeX\ and Con\TeX t respectively, with \com\formatnumber +already set accordingly. + +\Example +\def\myengine{% + \ifcase\texenginenumber + e-\or Xe\or pdf\or Lua\fi\TeX + } +\def\myformat{% + \ifcase\formatnumber + unknown\or plain\or eplain\or + ConTeXt\or LaTeX\or LaTeX3\fi + } +This documentation has been typeset +with \myformat\ under \myengine. +\Example/ + +\cslist{ + \priminput, + \primunexpanded + } +Both La\TeX\ and Con\TeX t redefines \com\input, and in Con\TeX t +\com\unexpanded has not the meaning of the e-\TeX\ primitive. These +two commands are thus the primitive \com\input and \com\unexpanded +respectively. + +\cslist{ + \loadmacrofile/file/ + } +The behavior of this command depends on \com\formatnumber. The \arg{file} +should be given without extension, and the following happens: in Con\TeX t, +\com\usemodule\tcode{[\arg{file}]} is executed, in La\TeX\ \com\RequirePackage\barg{file} +is used, and in other formats it is simply \com\input\arg{file}\tcode{.tex}. +This makes sense only with packages that are distributed as described above, +i.e. with the main code in one file and wrapper files for La\TeX\ and Con\TeX t, +like Ti\ital{k}Z or \ital{librarian}. + +\cslist{ + \senderror/package//message/ + } +This sends an error message according to the format's custom. In \tcode{plain} +and \tcode{eplain} (and in an unknown format), it produces +\tcode{\com\errmessage\string{\arg{package} error: \arg{message}\string}}. +In La\TeX, we get \com\PackageError\barg{package}\barg{message}{\codefont\string{\string}} +(no help message) and in Con\TeX t +\tcode{\com\writestatus\string{\arg{package} error\string}}\barg{message} +(which is far from Con\TeX t's sophisticated communication system but, well...). + +\description/ + + + + + + + +\description{Argument manipulation} + +\cslist{ + !\emptycs, + !\spacecs, + \spacechar + } +Pretty useful macros whose meaning is clear, but whatever: \com\emptycs is +an emptily defined command, \com\spacecs expands to a space, and \com\spacechar +denotes a space, i.e. it is an implicit space and not really a macro. + +\cslist{ + !\gobbleone, + !\gobbleoneand/code/ + } +Those, as you might imagine, gobble the following argument; the second version +also excutes \arg{code} afterwards. There are actually nine +such commands in each case, and they are (for the sake of completeness) +\com\gobbleone, \com\gobbletwo, \com\gobblethree, \com\gobblefour, +\com\gobblefive, \com\gobblesix, \com\gobbleseven, \com\gobbleeight\ +(watch out, two \ital{e}'s) and \com\gobblenine for the first version, +and \com\gobbleoneand, \com\gobbletwoand, \com\gobblethreeand, +\com\gobblefourand, \com\gobblefiveand, \com\gobblesixand, +\com\gobblesevenand, \com\gobbleeightand and \com\gobblenineand. Note +that \com\gobblenineand\arg{code} takes two expansion steps to +return \arg{code}, instead of only one in the other cases. + +\Example +This is \gobbletwoand{very } uninteresting. +\Example/ + +\cslist{ + !\unbrace/code/ + } +This is the kind of command you probably can't see the point of until +you need it. It returns its \arg{code} untouched, but with outermost +braces removed if any. + +\cslist{ + !\swapargs/arg1//arg2/, + !\swapbraced/arg1//arg2/, + !\swapleftbraced/arg1//arg2/, + !\swaprightbraced/arg1//arg2/ + } +The first of those returns \arg{arg2}\arg{arg1} into the stream, without +any brace to delimit them. +On the contrary, \com\swapbraced returns \barg{arg2}\barg{arg1}. And, as +you might imagine, \com\swapleftbraced returns +\barg{arg2}\arg{arg1} whereas \com\swaprightbraced returns \arg{arg2}\barg{arg1}. + +\cslist{ + !\passexpanded/arg1//arg2/, + !\passexpandednobraces/arg1//arg2/ + } +The first one returns \arg{arg1}\barg{arg2 expanded once} and the second +\arg{arg1}\arg{arg2 expanded once}. It's some sort +of long \com\expandafter built on \com\swapargs and associates, +and if \arg{arg1} is a single token it's faster to use \com\expandafter +itself. It's not a real \com\expandafter, though, since \arg{arg2} is +expanded to the left of \arg{arg1} and then moved back to its +right. Which, with e.g. an \com\else as \arg{arg2}, will lead +to results you probably haven't foreseen and expected. +If \arg{arg2} is some material you want to turn into a command with +\com\csname, see \com\passcs below. + +\Example +\def\foo#1#2{\detokenize{(1=#1,2=#2)}} +\def\bar{two} +\foo{one}\bar +\passexpanded{\foo{one}}\bar +\Example/ + +\description/ + + + + + + + + + +\description{Defining \& using commands} + +\cslist{ + \defcs\csarg/parameter text/\barg{definition}, + \edefcs\csarg/parameter text/\barg{definition}, + \gdefcs\csarg/parameter text/\barg{definition}, + \xdefcs\csarg/parameter text/\barg{definition} + } +These work exactly like \com\def, \com\edef, \com\gdef +and \com\xdef, except they define a command with name +\csarg. The \arg{parameter text} is the usual one, +and any the space at the beginning is significant. I.e. +\verb/\defcs{foo}#1{...}/ and \verb/\defcs{foo} #1{...}/ +aren't equivalent at all. Prefixes can be appended as with +\com\def. + +\Example +\defcs{foo}#1{This is foo: #1.} +\foo{bar} +\Example/ + + +\cslist{ + \letcs\csarg\comarg, + \lettocs\comarg\csarg, + \letcstocs\csarg\csarg + } +These \com\let the first command or command named \csarg\ to +the meaning of the second one. In both \com\lettocs and \com\letcstocs, +if the command with name \csarg\ is undefined, it is not let to \com\relax. +So these are different from \com\let with \com\expandafter's. The \com\letcs +command can also be used to create an implicit character, of course. + +\Example +\expandafter\let\expandafter\foo + \csname undefined\endcsname +\lettocs\bar{reallyundefined} +\letcstocs{reallyundefined}{reallyundefined} + +Compare this: \meaning\foo, +and that: \meaning\bar. +And better yet: \meaning\reallyundefined. +\Example/ + + +\cslist{ + \addleft\comarg/material/, + \addleftcs\csarg/material/, + \eaddleft\comarg/material/, + \eaddleftcs\csarg/material/ + } +This redefines \comarg\ or a command named \csarg\ to +itself with \arg{material} added at the beginning. The e-variant +performs an \com\edef so that \arg{material} is fully expanded +(but not \comarg). The usual prefixes can be appended. + +\cslist{ + \addright\comarg/material/, + \addrightcs\csarg/material/, + \eaddright\comarg/material/, + \eaddrightcs\csarg/material/ + } +This is the same thing as above, but the material is added +at the end. In both the left and right version, the command +thus redefined should be a simple command working by itself +(i.e. no argument and no delimiter). In the \csarg\ case, +no check is performed to ensure that \csarg\ is defined +(but in the worst case it ends up as \com\relax, because of +its being called after the implicit \com\def\ (get it?)). + +\Example +\defcs{foo}{bar} +\addleftcs{foo}{In a } +\addright\foo{ (how fascinating).} +\foo +\Example/ + +\cslist{ + !\usecs\csarg, + !\usecsafter\csarg, + !\passcs/code/\csarg, + !\passexpandedcs/code/\csarg, + \noexpandcs\csarg, + \unexpandedcs\csarg + } +Various ways to use a command with name \csarg: +\com\usecs performs a simple \com\csname\csarg\com\endcsname\ +(and doesn't even check whether \csarg\ is defined or not, so +this might relax it a little bit), \com\usecsafter does the equivalent +of \com\expandafter\com\command, \com\passcs puts \csarg\ as a real +(unbraced) command after \arg{code}, whereas \com\passexpandedcs passes +the expansion of the control sequence with name \csarg\ to \arg{code}; \com\noexpandcs and \com\unexpandedcs +return \csarg\ with a \com\noexpand prefix or its expansion as argument to +\com\unexpanded\ (\com\primunexpanded, really). + +\Example +\def\bar{whatever} \def\foo#1{[#1]} +I use it: \usecs{bar}, +I use it after: \usecsafter{foo}\bar, +and I pass it: \passcs\foo{bar}. +\Example/ +\Example +\def\foo{\bar} +I don't expand it: +\edef\foobar{\noexpandcs{foo}}% +\meaning\foobar. +Or just a little bit: +\edef\foobar{\unexpandedcs{foo}}% +\meaning\foobar. +\Example/ + +\cslist{ + !\commandtoname\comarg + } +This returns the name of \comarg, i.e. \comarg\ without its backslash +(and made of catcode-12 characters, since it's based on \com\string). + +\description/ + + + + + + + + + +\description{Tests with commands} + +\cslist{ + !\reverse + } +Conditionals in \texapi\ (not only those on this page) can +be prefixed with \com\reverse, so that if they're true the +\false\ argument is executed (if specified), and if they're +false, the \true\ argument is executed. So this is equivalent +to \com\unless. + +\cslist{ + !\ifcommand\comarg\true\false, + !\iffcommand\comarg\true + } +This conditional executes \true\ if \comarg\ is defined. +So it is a straight version of \com\ifdefined. The \com\iff... +version, like all \texapi's \com\iff..., considers only the +\true\ case (which becomes the \false\ case if the conditional +is prefixed with \com\reverse). + +\Example +\ifcommand\TeX{Cool}{Too bad}. +Nothing: \iffcommand\undefined{Whatever}. +\Example/ + +\cslist{ + !\ifcs\csarg\true\false, + !\iffcs\csarg\true + } +Same as above, but with an \com\ifcsname this time. +It goes without saying that \csarg\ isn't let to \com\relax +thereafter if it was undefined. + +\Example +\reverse\iffcs{undefined}{This command is undefined.} +\Example/ + +\cslist{ + !\ifemptycommand\comarg\true\false, + !\iffemptycommand\comarg\true + } +This is true if \comarg\ is defined with an empty +definition text, i.e. it is equivalent to \com\emptycs. +This is not true if \comarg\ takes arguments, though, +so \com\gobbleone isn't empty in this sense. + +\cslist{ + !\ifemptycs\csarg\true\false, + !\iffemptycs\csarg\true + } +Same as above with a command named \csarg. + +\Example +\def\foo{} +\ifemptycommand\foo{Empty}{Not empty}. +\reverse\iffemptycs{gobbleone}{It ain't empty}. +\Example/ + + + +\cslist{ + !\ifxcs\csarg\comarg\true\false, + !\iffxcs\csarg\comarg\true + } +This is true if \csarg\ has the same definition as \comarg, +or they're both undefined. + +\cslist{ + !\ifxcscs\csarg\csarg\true\false, + !\iffxcscs\csarg\csarg\true + } +This is true if both \csarg's have the same definition, +or they're both undefined. + +\Example +\iffxcs{undefined}\undefinedtoo + {Same definitions.} +\ifxcscs{foo}{TeX} + {These are the same} + {These are different}. +\Example/ + +\description/ + + + + + + + + + + +\description{Various conditionals} + +\cslist{ + \newife\comarg + } +This defines a conditional like plain \TeX's \verb/\newif/, +except it takes two arguments (the \true\ and \false\ values) +instead of an \com\else ... \com\fi structure. Besides, +this conditional is reversible with \com\reverse, and +a `double-f\kern1pt' (i.e. \com\iff...) version is also created, +which takes the \true\ part only. As with +\verb/\newif/, \comarg\ must begin with \tcode{if}. +(The \ital{e} means \ital{expandable}, although there's +nothing more expandable in the conditionals thus constructed +than in those defined with \verb/\newif/, but anyway.) + +\Example +\newife\iffoo +\iffoo{There is foo}{There is no foo}. +\footrue +\reverse\iffoo{There is no foo}{There is foo}. +\ifffoo{With three f's in a row}. +\Example/ + +\cslist{ + !\straightenif/\TeX\ conditional//arg/\true\false, + !\straighteniff/\TeX\ conditional//arg/\true + } +Apart from \com\ifdefined and \com\ifcsname\ (in the guise +of \com\ifcommand and \com\ifcs respectively), none of \TeX's +primitive conditionals are redefined in a straight fashion, +i.e. with two arguments instead of \com\else ... \com\fi. +These commands let you use \TeX's conditional in such a way. +\arg{\TeX\ conditional} means such a primitive without a +backslash (so this construction can be used inside real +conditionals), e.g. \tcode{ifnum} or \tcode{ifvoid}. +The \arg{arg} is whatever you normally feed to this conditional. +It is brutally concatenated, and you're the one in charge +of adding space if needed, as for instance with \tcode{ifnum}. +Chaos will ensue if you fail to do so. With conditionals that don't +require anything, e.g. \tcode{iftrue} or \tcode{ifvmode}, +leave \arg{arg} empty (but don't forget it). +Finally, \true\ and \false\ are executed accordingly, and the +whole macro can be prefixed with \com\reverse. + +\Example + % See this space? +\straightenif{ifnum}{1=1 }{Reality is preserved} + {Bad news}. +\reverse\straighteniff{if}{ab} + {Different letters, obviously.} +\straightenif{iftrue}{}{Good}{Bad}. +\Example/ + + + + +\cslist{ + !\afterfi/code/, + !\afterdummyfi/code/ + } +You shouldn't use these. The first one closes the +current conditional and executes \arg{code}. The second +one lets go one \com\fi and executes \arg{code}. So these +are kinds of \com\expandafter's when \arg{code} isn't just +a command. Anything before the incoming \com\fi is gobbled. +The reason why you should use one or the other +should be clear to you, otherwise you'll probably be messing +with a conditional. + +\Example +\iftrue + \afterdummyfi{\afterfi{Here we are.}} +\else + \iffalse + Whatever. + \fi +\fi +\Example/ + +\description/ + + + + + + + + +\description{Poking at what comes next} + +\cslist{ + \nospace/code/ + } +This gobbles any incoming space, if any, and executes \arg{code}. +Of course it doesn't require there to be any space to work properly. + +\vskip\baselineskip + +All the following conditionals can be prefixed with \com\reverse. +And in case your head's buzzing, their names are quite regular: +take an \com\if, \com\ifcat or \com\ifx, add `\tcode{next}', and create +variants by doubling the \tcode{f} and/or adding \tcode{nospace} +at the end. + +\Example +\nospace{foo} bar +\Example/ + +\cslist{ + \ifnext/token/\true\false, + \iffnext/token/\true, + \ifnextnospace/token/\true\false, + \iffnextnospace/token/\true + } +These poke at the next token and see whether it +has the same character code as \arg{token}. In other +words, an \com\if test is performed between \arg{token} +and the next token in the input stream. However, neither +\arg{token} nor the incoming token are expanded, so that +they can be control sequences and no unwanted expansion +will occur. Control sequences are all equal according to +this test (which can very well take an undefined control +sequence as \arg{token}). The nospace version must be +pretty clear: the macro discards all incoming spaces until +it finds a non-space token to test (unfortunately, +an implicit space and a space character are undistinguishable +as far as this test (and the next ones) is concerned, so in +the very unlikely case where an implicit space was waiting +in the stream, it'll be gobbled in the nospace variant). + +\Example +Here comes \ifnext e{an }{a }e. +Here comes \reverse\ifnext e{a }{an }b. +Here comes +\iffnextnospace\foo{a control sequence: } \TeX. +\Example/ + +\cslist{ + \ifcatnext/token/\true\false, + \iffcatnext/token/\true, + \ifcatnextnospace/token/\true\false, + \iffcatnextnospace/token/\true + } +These are the same as above with an \com\ifcat test +instead of \com\if. Again, control sequences aren't expanded +and they all have the same category code. + +\Example +\def\tex{\TeX\iffcatnext a{ }} +A \tex is a \tex is a \tex. +\Example/ + +\cslist{ + \ifxnext/token/\true\false, + \iffxnext/token/\true, + \ifxnextnospace/token/\true\false, + \iffxnextnospace/token/\true + } +Once again like the previous commands, this time with +an \com\ifx, i.e. the definitions of control sequences +are compared, and in case \arg{token} and/or the next +token are unexpandable thing, both character code and +category code are compared. So these are performing +real \com\ifx tests. + +\Example +\def\foo{not \string\TeX} +\reverse\iffxnextnospace\TeX + {The incoming command isn't \string\TeX: } \foo. +\Example/ + +\description/ + + + + + + +\description{String manipulation} + +\cslist{ + !\ifstring/string1//string2/\true\false, + !\iffstring/string1//string2/\true + } +These return \true\ if the two strings are identical. +Category codes aren't taken into account when strings +are compared. + +\Example +Two \ifstring{abc}{abc}{equal}{unequal} strings +and an \reverse\iffemptystring{something}{unempty} one. +\Example/ + + +\cslist{ + !\ifemptystring/string/\true\false, + !\iffemptystring/string/\true + } +These return \true\ if \arg{string} is empty. + + + + +\cslist{ + \newstring/string/ + } +The following operations (\com\ifprefix, \com\removesuffix, etc.) +aren't fully expandable by default. However, if a string has been +previously declared with \com\newstring, they magically become +fully expandable. + +So, in what follows, macros aren't marked as expandable, although +they can be if the preceding condition is fulfilled. Besides, these +macro aren't \com\protected even though their default behavior would +require that they be. But you can always append a \com\noexpand to +an unprotected command, whereas you cannot force the execution of a +protected one. (This protecting issue is of course totally irrelevant +for the \com\removeprefixin and \com\removesuffixin commands, which aren't +expandable by definition and are thus protected.) + + + + +\cslist{ + \ifprefix/prefix//string/\true\false, + \iffprefix/prefix//string/\true + } +This test is true if \arg{string} begins with \arg{prefix}. +Category codes do matter. + +\Example +\newstring{abc} +\edef\foo{\ifprefix{abc}{abcd}{True}{False}.} +\edef\bar{\reverse\iffsuffix{abc}{whatever}{No suffix}.} +\edef\foobar{\ifcontains{abc}{gee}{Yes}{No}.} +\meaning\foo\par +\meaning\bar\par +\meaning\foobar +\Example/ + +\cslist{ + \ifsuffix/suffix//string/\true\false, + \iffsuffix/suffix//string/\true + } +True if \arg{string} ends with \arg{suffix}. + +\cslist{ + \ifcontains/string1//string2/\true\false, + \iffcontains/string1//string2/\true + } +Finally, this is true if \arg{string2} contains \arg{string1}. + + +\cslist{ + \removeprefix/prefix//string/, + \removesuffix/suffix//string/ + } +These return \arg{string} without \arg{prefix} (resp. +\arg{suffix}). No test is performed to check that +\arg{string} indeed begins (resp. ends) with \arg{prefix} +(resp \arg{suffix}), so these macros make sense only after +the adequate tests. + +\cslist{ + \removeprefixand/prefix//string//code/, + \removesuffixand/suffix//string//code/ + } +These do the same as the previous one, but feed the +resulting string to \arg{code}, between braces. Once again, +no test is performed beforehand. + +\cslist{ + \removeprefixin/prefix//string/\comarg, + \removesuffixin/suffix//string/\comarg + } +These define \comarg\ as \arg{string} without \arg{prefix} +(resp. \arg{suffix}). No test either. Sorry. + +\cslist{ + \splitstring/string1//string2//code/ + } +This cuts \arg{string2} in two at \arg{string1}'s first occurrence +and passes the two parts as braced arguments to \arg{code}. And, +again: no test. + +\Example +\def\record#1 : #2.{% + \par\bgroup + \it\ifprefix*{#1}{\removeprefix*{#1} [live]}{#1} + \egroup + (\ifcontains/{#2}{\splitstring/{#2}{\dodate}}{#2}) + } +\def\dodate#1#2{recorded #1, released #2} + +A somewhat incomplete list of fantastic +records by Frank Zappa: +\record Absolutely Free : 1967. +\record The Grand Wazoo : 1972. +\record L\"ather : 1977/1996. +\record *Make a Jazz Noise Here : 1988/1991. +\Example/ + +\description/ + + + + + + +\description{Various things on the same page} + +\cslist{ + \setcatcodes\barg{list} + } +The \arg{list} argument here means comma separated +\arg{characters}\verb/=/\arg{category code}, with +an \ital{s} to \ital{characters} because you can +concatenate them if you want them to share the same +\arg{category code}. So, as you might have guessed, +this set all \arg{characters} to characters with +catcode \arg{category code}. And it also sets +\com\restorecatcodes accordingly. The changes are +local. The \verb/#/ character requires a backslash +(so do braces and the backslash itself, but that's obvious). + +\cslist{ + \resetcatcodes + } +This restores the catcodes of the characters changed +with the previous command, which is cumulative, i.e. +\com\restorecatcodes restores catcodes changed by all preceding +\com\setcatcodes commands, not only the last one. Since +changes are local, \com\restorecatcodes may be useless in a group +(and the effect of \com\restorecatcodes itself is local too). + +\Example +\setcatcodes{\\\#\{\}\%=12,\|=0} +Hey, were're verbatimizing: +\def\foo#1{\bar{#1}}% +|restorecatcodes +\Example/ + +\Example +\bgroup +And now in a group:\par +\setcatcodes{z=13} +\defz{ZZZZZZZZZZZZZZZZZZZZZZZZZZ} +I'm sleeping: z.\par +\egroup +And I'm not: z. +\Example/ + + + +\vskip\baselineskip +The trimming macros below are adapted from +Will Robertson's \tcode{trimspace} package. + + +\cslist{ + !\trimleft/string/, + !\trimright/string/, + !\trim/string/ + } +These return \arg{string} with one space removed at the beginning +or end or both. There's no need to check beforehand whether there +are indeed such spaces. + +\Example ++\trim{ bar }+ +\Example/ + +\cslist{ + !\passtrimleft/string//code/, + !\passtrimright/string//code/, + !\passtrim/string//code/ + } +These return \arg{string} trimmed of spaces as a braced argument to +\arg{code}. + +\cslist{ + \deftrimleft\comarg/string/, + \deftrimright\comarg/string/, + \deftrim\comarg/string/ + } +The same thing again, except now those commands define \comarg\ +to \arg{string}, etc. + +\Example +\deftrimleft\foo{ bar } ++\foo+ +\Example/ + +\description/ + + + + + + + +\description{While statements} + +\cslist{% + !\repeatuntil/number//code/ + } +This executes \arg{code} \arg{number} times. The \arg{number} +argument can be a \com\count register, an integer defined with +\com\chardef, etc., and of course a string of digits. In any case, +it is really an argument and must be surrounded by braces if it is +made of more than one token. + +\Example +We have seen \repeatuntil\pageno{I} pages.\par +\edef\foo{\repeatuntil3{.}} +\meaning\foo +\Example/ + + +\cslist{ + !\dowhile/condition//code/ + } +This repeats \arg{code} while \arg{condition} is true. The latter +must be a `straight' \tcode{if}, i.e. either one of \texapi's +\com\if... or a \com\straightenif\barg{\TeX\ conditional} construction, +in both cases without the \true\ and \false\ arguments, because \true\ is actually +\arg{code}, and \false\ would make no sense. Finally, the conditional +must be a simple \com\if..., not an \com\iff... version. Once again, this +makes sense: the \ital{if and only if} clause is implicit in a \ital{while} +statement. If you use an \com\iff..., you'll end up with many empty braces, +which is harmless unless you're in a context of expansion. You can use \com\reverse +in \arg{condition}. + +\Example +\newife\ifbreakloop \def\foo{} +\dowhile{\reverse\ifbreakloop} + {\addleft\foo{a}% + \passexpanded\iffstring\foo{aaaa} + \breaklooptrue} +\foo +\Example/ + +\Example +\edef\foo{% + The inconvenience of iff...: + \dowhile{\straighteniff{ifnum}{4=5 }} + {whatever} + } +\meaning\foo +\Example/ + + + +\cslist{ + \newwhile\comarg/number//transformations//code/ + } +The \com\dowhile macro is not very powerful since +you must generally change something somewhere to make +it stops, and thus its expandability is somewhat perfunctory. +That's why there is \com\newwhile. It creates an expandable \comarg\ which takes +\arg{number} arguments (up to 9, as usual) and repeats \arg{code} +indefinetely. So, at first sight, it's bad news. But the point +is \arg{code} is supposed to launch the \com\breakwhile macro below sooner or +later, i.e. stop the loop. Besides, on each iteration (barring the first), \arg{transformations} +are applied to the arguments, and this means: the first argument is +replaced by the first transformation, the second argument by the second +transformation, etc. So there must be as many transformations as there +are arguments, transformations themselves being just code that can +make reference to the arguments. If you don't want to transform an +argument, just repeat it in the transformation. + +\cslist{ + !\breakwhile/code/ + } +This breaks the current while loop and executes \arg{code}, which can make +reference to the arguments of the loop. + +\cslist{ + !\changewhile/new arguments/ + } +This replaces the default \arg{transformations} defined with \com\newwhile +and passes the \arg{new arguments} for the next iteration. There +must be as many arguments as required by the loop. The original +\arg{transformations} remain in force for the next iterations. + +\Example + % Transformations. +\newwhile\largestsquare2{\numexpr(#1+1)}{#2}{% + \reverse\straighteniff{ifnum}{\numexpr(#1*#1)<#2 } + {The largest number whose square + is smaller than #2 is + \breakwhile{\the\numexpr(#1-1).}}} +\largestsquare{1}{50}\par +\largestsquare{1}{200} +\Example/ + + +\description/ + + + + +\description{For statements on the fly} + +\cslist{ + \dofor/list//parameter text/\barg{definition}/coda/ + } +This runs \arg{definition} on each occurrence of +\arg{parameter text} in \arg{list}. The \arg{parameter text} +is a real one, hence the braces around \arg{definition}. +The \arg{coda} is executed if and only if the loop goes +to its natural end, i.e. it is not terminated by one of +the commands below. It must be present, even if you don't +want one (in which case, leave it empty), and it can't make +any reference to the arguments of the parameter text. +A loop thus executed is absolutely +not expandable. You can embed as many loops as you want +(but don't forget to double the \verb/#/). + +\cslist{ + \dofornoempty + } +This is the same as above, except \arg{definition} is not executed +when the \ital{first} argument is empty. + + +\cslist{ + \breakfor/code/ + } +This breaks the current loop and executes \arg{code}; +the \arg{coda} of the loop is not executed. + +\cslist{ + \retrieverest/code/ + } +This also breaks the loop, but it retrieves the remaining +arguments in the list and pass them as a braced argument +to \arg{code}. + +\cslist{ + \pausefor/code/ + } +This interrupts the loop and executes \arg{code}; the loop +being interrupted means you're in the middle of the list, and +you can process it. Such a pause must be terminated by a +\com\resumefor if you don't want nasty internal code +to surface. + +\cslist{ + \resumefor\com\dofor + } +This restarts the current loop. It is necessary to specify +\com\dofor, because \com\resumefor is more general and is +used to restart any kind of loop, especially those defined +with \com\newfor (see next page). + +\Example +\dofor{a,b,c,}#1,{[#1]}{} +\Example/ + +\Example +\dofor{(a=13)(b=3)(c=54)(d=33)(e=22)}(#1=#2){% + \straighteniff{ifnum}{#2>50 } + {\breakfor{There's a number larger than 50: #1=#2.}}} + {No number larger than 50.} +\Example/ + +\Example +\dofornoempty{dd,e,,acb,3,ee4,,,}#1,{% + \dofor{#1}##1{[##1]}{}...% + }{} +\Example/ + +\vskip\baselineskip + +The \com\dofor loop does not perform any kind of normalisation +on the list. I.e. the list must be exactly designed to match +the parameter text, including spaces and other unwelcome guests. + +The \com\dofor macro is useful for straightforward loops used once +or twice in a document. But for fully fledged total-control +fully expandable hey-that's-too-cool loops, you should use the +\com\newfor construction. + + +\description/ + + + + + + + + + + + + + +\description{For statements: first steps} + +\cslist{ + \newfor\comarg\barg{optional passed arguments}, + \zilch\kern-4em/parameter text/\barg{definition}[/optional coda/] + } +This creates a recursive \comarg\ that will consume all input +with structure \arg{parameter text}. Let's forget +\barg{optional arguments} for a while, since they're optional +(albeit braced). Let's forget the optional coda as well. So +it boils down to: + +\vskip\baselineskip +\noindent\com\newfor\comarg\arg{parameter text}\barg{definition} +\vskip\baselineskip + +\noindent so that basically \com\newfor works like \com\def. +The \arg{parameter text} is a real parameter +text as with \com\def, just like \barg{definition} is a real +definition, hence the braces. The only difference is there must +be at least one argument, because we need something to loop +upon. I.e. \arg{parameter text} is at least \verb/#1/. + +Now you can launch \comarg\ on an argument which is made of as +many occurrences of \arg{parameter text} as you wish, and on each +occurrence \arg{definition} will be executed. So you've created +a loop. And the good news is that this loop is fully expandable. + +It is your job to make sure that what is fed to \comarg\ has +the correct argument structure. + +If \arg{coda} is specified, it is executed when the loop ends, +if it ends naturally, i.e. by exhausting its input, and not +by some of the loop-breaking commands on the next page. There +can be no call to arguments of \arg{parameter text} in the +\arg{coda}, e.g. + +\example +\newfor\foo#1{...}[...#1...] +\example/ + +\noindent is impossible. (You'll get raw inner code.) Such +reference to arguments in the \arg{coda} is possible only +with passed arguments, as you'll see in two pages +from here. + +(Note that if there's no \arg{coda}, any space will be gobbled +after \barg{definition}. This is so because I thought it was better +to be able to write \tcode{[\arg{code}]} after a space, e.g. +a line end, than to stick it to \barg{definition}, even though +that brings this little inconvenience, which is probably harmless +since \com\newfor is very unlikely to end up anywhere in horizontal +mode, i.e. in a paragraph.) + +Macro thus created can be freely embedded into one another. + +\Example +\newfor\foo#1,{(#1)} +\foo{a,b,c,} +\Example/ + + +\cslist{ + \newfornoempty + } +The \com\newfornoempty is similar to \com\newfor, except \arg{definition} +is not executed in the case the \ital{first} argument is empty. + + +\Example +\newfornoempty\foo(#1,#2){[#1/#2]} + [Input exhausted.] +\edef\bar{\foo{(a,b)(c,d)(,e)(f,)}} +\meaning\bar +\Example/ + +\Example +\newfor\values#1=#2,{% + The value of #1 is #2.\par + } +\def\setvalues#1{% + \ifsuffix,{#1}{\values{#1}} + {\values{#1,}}% + } +\setvalues{A=12,B=45,} +\setvalues{C=34} +\Example/ + +\Example +\newstring, % \pdfliteral requires full expansion! +\def\drawline#1{ + 0 0 m % Initializes the path + \ifsuffix,{#1}{\drawlinefor{#1}} + {\drawlinefor{#1,}} + } % l = line +\newfor\drawlinefor#1 #2,{#1 #2 l }[S]% S = draw path +\pdfliteral{ + q % kind of PDF \bgroup + 1 0 0 RG \drawline{20 10, 40 -15, 100 0,} + 0 1 0 RG \drawline{30 -15, 60 10, 130 0} + Q} % kind of PDF \egroup +\Example/ + +\description/ + + + + + + + + +\description{For statements: interruptions} + +\noindent (The commands on this page are the same as those +introduced with \com\dofor; they're explained +more thoroughly here.) + +\cslist{ + !\breakfor/code/ + } +Used inside a loop created with \com\newfor, this +interrupts it, gobbles the remaining input, and +executes \arg{code}. If the loop had a \arg{coda}, +it is not executed. Any material between the \com\breakfor +command and the end of the definition of the loop +is gobbled. It is especially bad with conditionals, +so you should use \com\afterfi, or better yet a +\com\straightenif version. + +\Example +\newfor\foo#1{% + \straighteniff{if}{\noexpand#1z} + {\breakfor{There is a `z'!}} + #1... % This will be gobbled. + }[There is no `z'...] + +\foo{abcdef}\par +\foo{abzdef} +\Example/ + +\cslist{ + !\retrieverest/code/ + } +This does the same thing as \com\breakfor, i.e. breaks the +current loop, but it passes the rest of the material initially +passed to the loop as a braced argument to \arg{code}. Arguments +in that remaining material +aren't extracted from their surrounding delimiters, if any. + +\Example +\def\remainder#1{(And `#1' was still to come.)} +\newfor\foo#1=#2,{% + \unless\ifnum#1=#2 + \afterfi{% Thrilling... + \retrieverest{There is a false equation! + \remainder}}% + \fi} +\foo{3=3,2=2,451=451,7=4,78=78,9=0,} +\Example/ + + +\cslist{ + !\pausefor/code/, + !\resumefor/loop command/ + } +The \com\pausefor command stops the loop and executes \arg{code}. +That means that you're in the middle of the material being processed and +you can act on it. It is useful if the material isn't totally +regular. For instance, a typical Bib\TeX\ entry is a list of +`\tcode{\arg{field}=\arg{value}}' pairs, with each pair +terminated by a comma and the \arg{value} either between braces +or quotes. Thus, you can't have a simple + +\example +\newfor\bibfor#1=#2,{#1...#2} +\example/{}{} + +\noindent to process the entry, because a \arg{value} may be delimited +by quotes and still contain a comma, and quotes mean nothing to \TeX, +so the comma will be mistaken for the delimiter. An oversimplified +solution with \com\pausefor can be seen on the right. The loop actually +works on the predictable part only (before the equal sign), is interrupted, +the value is retrieved, and the loop is resumed. +(Why one would want to process a Bib\TeX\ entry +with \TeX\ in the first place is a question I can personally answer.) + +Once \com\pausefor is used, there must be somewhere down your code +a \com\resumefor\comarg\ statement, to launch the loop again, otherwise +you'll end up stumbling on some nasty internal code. It is impossible +to know (in a perfectly expandable way) the loop we're currently in, hence +the \comarg\ as a argument to \com\resumefor: it is the loop one wants to +start again. Yes, it means you can also process the rest of the material +with another loop, the consequences of which I leave it to you to ponder. + + +\Example +\newfor\bibfor#1={% + \pausefor{\getvalue{#1}}} +\def\getvalue#1{% + \trim{#1}: + \ifnextnospace"{\getquotevalue} + {\getcommavalue} + } +\def\getquotevalue"#1",{\showvalue{#1}} +\def\getcommavalue#1,{\showvalue{#1}} +\def\showvalue#1{% + {\it\trim{#1}}.\par\resumefor\bibfor} + +\bibfor{ + Author = {John Doe}, + Title = "Me, myself and I", + Year = 1978,} +\Example/ + +\description/ + + + + + + + + + + +\description{For statements: passed arguments} + + +Suppose you want to retrieve the largest number in +a list a numbers. The first example on the right shows +you how to do so. But this solution won't work +if you need the loop to be expandable, because there's +a number assignment. + +\Example +\newcount\largest +\newfor\findlargest#1,{% + \ifnum#1>\largest + \largest=#1 + \fi} + [The largest number is \the\largest.] + +\findlargest{45,33,1,4844,12,655,} +\Example/ + +That's why loops defined with \com\newfor can pass +arguments from one iteration to the next. The number of those arguments +are the \barg{optional passed arguments} in the description +of \com\newfor two pages ago. So, a typical fully-fledged +use of \com\newfor is: + +\example +\newfor\myloop{2}#3=#4,{...#1...#2...#3...#4...} + [...#1...#2...] +\example/ + +\noindent which means that \com\myloop takes four arguments, +two of which are actually passed arguments, the third and the +four being in the recursive list that \com\myloop runs on. +Besides, as you can see, passed arguments can appear in +\arg{coda}. +Now a call to \com\myloop looks like: + +\example +\myloop{one}{two}{a=1,b=2,...} +\example/ + +\noindent where \tcode{one} and \tcode{two} are passed +arguments. There can be up to 8 passed arguments (since there must +be at least one argument to loop on), and if there are +{\it n} of them, numbering of arguments in \arg{parameter text} must +start at {\it n}+1, as in the above example. + + +\cslist{ + \passarguments/arg1//arg2/... + } +Passed arguments are automatically retrieved from one iteration +to the next. However, if you can't change them, they aren't very +interesting. Hence this command: it passes \arg{arg1}, \arg{arg2}, etc., +to the next iteration, replacing the previous ones. There +must be as many arguments to \com\passarguments as required +by the loop, even if you don't want to pass new values for +all (in which case, just pass the previous value). +Beware: \com\passarguments ends the current iteration, just +like \com\breakfor, +and any remaining material in the definition of the loop +is gobbled. + +Thus, the second version of our \com\findlargest command +works as follows: it takes one harmless passed argument, +and loops on the following list. Obviously, 45 is larger +than 0, so it is passed as the new first argument; then, +33 is not larger than 45, so nothing happen, and 45 is +implicitly passed again as the first argument, and so +on and so forth, until finally the \arg{coda} prints +the largest number in the list. And, as illustrated +by the \com\edef, everything expands nicely. + +\Example +\newfor\findlargest{1}#2,{% + \straighteniff{ifnum}{#2>#1 } + {\passarguments{#2}}% + } + [The largest number is #1.] + +\edef\foo{\findlargest{0}{45,33,1,4844,12,655,}} +\meaning\foo +\Example/ + +\description/ + + + + + + + + +\description{For statements: examples} + + +Loops created with \com\newfor are somewhat tricky +to get a hand on, so here are some examples. First +of all, you might think that it would be nice to +be able to define a loop whose argument structure +is defined but not its replacement text, so that you +can call it on similar lists but with different +operations. For instance, a generic loop that works +on all comma-separated lists. You can't do that exactly +with \com\newfor, but you can easily use passed arguments +to do something similar, e.g.: + +\example +\newfor\commalist{1}#2,{#1{#2}} +\commalist\tree{leaf,fruit,twig,} +\commalist\scale{b minor,f sharp,whatever lydian} +\example/ + +\noindent with \com\tree and \com\scale defined to +process one argument: \com\commalist itself has no real +definition, and you don't have to bother about passed +arguments (although you can still use them). + +\Example +\newfor\sortnum{1}#2,{% + \subsortnum{#2}{}{#1}% + }[Sorted list: #1] +\newfor\subsortnum{2}#3,{% + \straightenif{ifnum}{#1<#3 } + {\retrieverest{\passtosortnum{#2#1,#3,}}} + {\passarguments{#1}{#2#3,}}% + }[\passarguments{#2#1,}] +\def\passtosortnum#1#2{\passarguments{#1#2}} + +\sortnum{}{5,12,-161,3,0,63,22,-45,} +\Example/ + +The first example sorts a list of numbers separated +by commas. The first loop, \com\sortnum, takes a passed +argument which contains the numbers already sorted (so +it is empty at the beginning) and it runs on the list +to be sorted. The second loop, \com\subsortnum, takes +two passed arguments: the first one is the number under +investigation, the second one is the list of numbers +smaller than the number under investigation (so it is +empty too at the beginning), and it is updated each +time we find such a number as the third, non-passed +arguments to \com\subsortnum, which is an element of +the list of already sorted numbers as preserved in +\com\sortnum's first passed argument... got that? + +Let's follow some iterations. The first call is: + +\example + % incoming arguments +\sortnum{}5,12,-161,3,0,63,22,-45, +\example/ + +\noindent and it calls + +\example +\subsortnum{5}{}{} +\example/ + +\noindent so that \com\subsortnum terminates immediately: +it has no input. So it calls its coda: + +\example +\passarguments{5,} +\example/ + +\noindent (where \tcode{5} is really the first argument following +the empty second one). Since \com\subsortnum has terminated, this +call to \com\passarguments is for \com\sortnum, hence the following +iteration is: + +\example + % incoming arguments +\sortnum{5,}12,-161,3,0,63,22,-45, +--> \subsortnum{12}{}5, +\example/ + +\noindent Ah, something new. 12 is larger than 5, so the conditional +is false. So \com\subsortnum passes the following to itself: + +\example +\passarguments{12}{5,} +--> \subsortnum{12}{5,}{} +\example/ + +\noindent and once again it terminates, hence: + +\example +\passarguments{5,12,} % incoming arguments +--> \sortnum{5,12,}-161,3,0,63,22,-45, + % incoming argument +--> \subsortnum{-161}{}5,12, +\example/ + +\noindent and obviously -161 is smaller than 5, so the rest of the list +is retrieved with \com\retrieverest and passed as the second argument of +\com\passtosortnum. Once again, since this terminates \com\subsortnum, +\com\passarguments in \com\passtosortnum is for \com\sortnum: + +\example +\passtosortnum{-161,5,}{12,} + % incoming arguments +--> \sortnum{-161,5,12,}3,0,63,22,-45, + % incoming arguments +--> \subsortnum{3}{}-161,5,12, +--> \subsortnum{3}{-161,}5,12, +--> \passarguments{161,3,5,12,} +--> \sortnum{-161,3,5,12,}0,63,22,-45, +... +\example/ + + +\noindent and so on and so forth. + +Replace the test with any other one and you +have a generic sorting function, as in the example +on the right, which sorts entries alphabetically +or chronologically. It is possible to make things both +cleverer and simpler. (The Lua code compares two +strings, and it could very well have handled the +\com\year version.) + +\Example +\newfor\sortbooks{2}#3(#4),{% + \subsortbooks#1{#3(#4)}{}{#2} + }[\bgroup\it#2\egroup] +\newfor\subsortbooks{3}#4(#5),{% + #1#2{#4}{#5}{#3} + }[\passarguments#1{#3#2,}] + +\def\alpha#1(#2)#3#4#5{% + \directlua{ + if "#1"<"#3" then + tex.print("\noexpand\\firstoftwo") + else + tex.print("\noexpand\\secondoftwo") + end} + {\retrieverest{% + \passtosortbooks\alpha{#5#1(#2),#3(#4),}}} + {\passarguments\alpha{#1(#2)}{#5#3(#4),}} + } +\def\year#1(#2)#3#4#5{ + \straightenif{ifnum}{#2<#4 } + {\retrieverest{% + \passtosortbooks\year{#5#1(#2),#3(#4),}}} + {\passarguments\year{#1(#2)}{#5#3(#4),}} + } +\def\passtosortbooks#1#2#3{\passarguments#1{#2#3}} + +\def\books{ + Oblivion (2004), + Infinite Jest (1996), + Brief Interviews with Hideous Men (1999), + Girl with Curious Hair (1989), + The Broom of the System (1987), + The Pale King (2011), + } +David Foster Wallace's books in alphabetical order:\par +\passexpanded{\sortbooks\alpha{}}\books \par +David Foster Wallace's books ordered by date:\par +\passexpanded{\sortbooks\year{}}\books +\Example/ + +\vskip\baselineskip + +The next example is a palindrome detector: it returns +true if the string it is fed is made of a string followed +by itself reverse (which is not the exact definition of +a palindrome, which is a string that is its own reverse, +but we keep things simple). + +The first loop, \com\palincount, simply counts the number +of characters in the string; it also reaccumulates it +as its second argument, something that could be avoided +if there was a wrapper macro. Once it is finished, it +passes the original string along with half the number +of characters to \com\palincheck, which simply accumulates +in reverse this number of characters, by decreasing it +on each iteration. Once this number is exhausted, it +compares what it has accumulated to what there remains +to be processed, and if both strings match, the original +string is a palindrome. + +\Example +\newfor\palincount{2}#3{% + \passarguments{\numexpr(#1+1)}{#2#3}% + }[\palincheck{\numexpr(#1/2)}{}{#2}] +\newfor\palincheck{2}#3{% + \reverse\straightenif{ifnum}{\numexpr(#1-1)>0 } + {\retrieverest{\compare{#3#2}}} + {\passarguments{\numexpr(#1-1)}{#3#2}}% + } +\def\compare#1#2{% + \ifstring{#1}{#2}{TRUE}{FALSE}% + } + +\edef\foo{\palincount{0}{}{abcdeffedcba}} +\edef\bar{\palincount{0}{}{abcdff}} +\meaning\foo, \meaning\bar +\Example/ + + +\description/ + + +\bye + + + + + + + + + + +\sortbooks{}{% + The Broom of the System (1987),% + Infinite Jest (1995),% + Girl with Curious Hair (1989),% + Brief Interviews with Hideous Men (1999),% + Oblivion (2004),% + The Pale King (2011),} + +\newfor\palin{2}#3{% + \passargs{\numexpr(#1+1)}{#2#3}% + }{\palincheck{\numexpr(#1/2)}{}{#2}} +\newfor\palincheck{2}#3{% + \reverse\straightenif{ifnum}{\numexpr(#1-1)>0 } + {\retrievefor{\compare{#3#2}}} + {\passargs{\numexpr(#1-1)}{#3#2}} + }{} +\def\compare#1#2{% + \ifstring{#1}{#2}{TRUE}{FALSE}% + } + + ++\edef\foo{\palin{0}{}{in noctes .. setconni}}+\foo diff --git a/Master/texmf-dist/tex/generic/texapi/texapi.tex b/Master/texmf-dist/tex/generic/texapi/texapi.tex new file mode 100644 index 00000000000..11b31c9eadf --- /dev/null +++ b/Master/texmf-dist/tex/generic/texapi/texapi.tex @@ -0,0 +1,899 @@ +% This is the texapi package. +% Relevant information can be found in texapi-doc.pdf +% +% Author: Paul Isambert. +% E-mail: zappathustra AT free DOT fr +% Comments and suggestions are welcome. +% Date: June 2010. +% +\ifx\texapialreadyloaded\somethingtotallyundefined +\else + \expandafter\endinput +\fi +\let\texapialreadyloaded\relax +\ifx\eTeXversion\somethingtotallyundefined + \errmessage{texapi error: You need eTeX to run this. I quit} + \expandafter\endinput +\fi +% +\expandafter\edef\csname pi@restorecatcodes\endcsname{% + \catcode`\noexpand\@=\the\catcode`@\relax + \catcode`\noexpand\_=\the\catcode`_\relax + } +\catcode`\@=11 \catcode`\_=11 +% +% +% +% +% +% ENGINES +% +\chardef\texenginenumber=0 +\ifdefined\XeTeXinterchartoks + \chardef\texenginenumber=1 +\else +% LuaTeX doesn't have \pdfstrcmp anymore. + \ifdefined\directlua + \chardef\texenginenumber=3 + \else + \ifdefined\pdfstrcmp + \chardef\texenginenumber=2 + \fi + \fi +\fi +% +% +% +% +% FORMATS +% +% It'd be safer if \formatnumber was already +% defined in a wrapper file for ConTeXt and LaTeX. +% +\unless\ifdefined\formatnumber + \chardef\formatnumber=0 + \def\pi@temp{plain} + \ifx\fmtname\pi@temp + \chardef\formatnumber=1 + \else + \def\pi@temp{eplain} + \ifx\fmtname\pi@temp + \chardef\formatnumber=2 + \else + % I'm not sure what ConTeXt's doing with \fmtname, + % so I make do with this lovely control sequence. + \ifdefined\inspectnextoptionalcharacter + \chardef\formatnumber=3 + \else + \def\fmtname{LaTeX2e} + \ifx\fmtname\pi@temp + \chardef\formatnumber=4 + \ifdefined\ExplSyntaxOn + \chardef\formatnumber=5 + \fi + \fi + \fi + \fi + \fi +\fi +% +% We need to get some primitives back. +% More should be done, probably. +% +\ifnum\formatnumber=3 + % ConTeXt + \let\priminput\normalinput + \let\primunexpanded\normalunexpanded + \def\loadmacrofile#1{\usemodule[#1]} + \def\senderror#1#2{% + \writestatus{#1 error}{#2}% + } +\else + \ifnum\formatnumber=4 + % LaTeX2e + \let\priminput\@@input + \let\primunexpanded\unexpanded + \let\loadmacrofile\RequirePackage + \def\senderror#1#2{% + \PackageError{#1}{#2}{}% + } + \else + \ifnum\formatnumber=5 + % LaTeX3 + % I don't know if this is of any good. + % LaTeX3 will either not rename those primitives, + % or turn all primitives to error messages (nightmare), + % as far as I can see. + \expandafter\let\expandafter\priminput\csname tex_input:D\endcsname + \expandafter\let\expandafter\primunexpanded\csname etex_unexpanded:D\endcsname + \let\loadmacrofile\RequirePackage + \def\senderror#1#2{% + \PackageError{#1}{#2}{}% + } + \else + % Plain and eplain, or an unknown format. + \let\priminput\input + \let\primunexpanded\unexpanded + \def\loadmacrofile#1{\priminput#1 } + \def\senderror#1#2{% + \errmessage{#1 error: #2}% + } + \fi + \fi +\fi +% +% +% +% +% INTERNAL UTILITIES +% +\def\pdef{\protected\def} +\def\pi@terminator{\senderror{texapi}{This shouldn't be happening}} +\long\def\pi@gobbletoterminator#1#2\pi@terminator{#1} % I generally want something to happen after a terminator. +% +% +% +% +% ARGUMENTS MANIPULATION +% +\long\def\gobbleone#1{} \long\def\gobbletwo#1#2{} \long\def\gobblethree#1#2#3{} +\long\def\gobblefour#1#2#3#4{} \long\def\gobblefive#1#2#3#4#5{} \long\def\gobblesix#1#2#3#4#5#6{} +\long\def\gobbleseven#1#2#3#4#5#6#7{} \long\def\gobbleeight#1#2#3#4#5#6#7#8{} \long\def\gobblenine#1#2#3#4#5#6#7#8#9{} +% +\long\def\gobbleoneand#1#2{#1} \long\def\gobbletwoand#1#2#3{#1} \long\def\gobblethreeand#1#2#3#4{#1} +\long\def\gobblefourand#1#2#3#4#5{#1} \long\def\gobblefiveand#1#2#3#4#5#6{#1} \long\def\gobblesixand#1#2#3#4#5#6#7{#1} +\long\def\gobblesevenand#1#2#3#4#5#6#7#8{#1} \long\def\gobbleeightand#1#2#3#4#5#6#7#8#9{#1} +\long\def\gobblenineand#1#2#3#4#5#6#7#8#9{\gobbleoneand{#1}} +% +\long\def\unbrace#1{#1} \long\def\firstoftwo#1#2{#1} \long\def\secondoftwo#1#2{#2} +% +\long\def\swapargs#1#2{#2#1} \long\def\swapbraced#1#2{{#2}{#1}} +\long\def\swapleftbraced#1#2{{#2}#1} \long\def\swaprightbraced#1#2{#2{#1}} +% +\long\def\passexpanded#1#2{\expandafter\swaprightbraced\expandafter{#2}{#1}} +\long\def\passexpandednobraces#1#2{\expandafter\swapargs\expandafter{#2}{#1}} +% +\def\emptycs{} \def\spacecs{ } +\bgroup +\def\:{\global\let\spacechar= }\: % +\egroup +% +% +% +% +% COMMAND DEFINITIONS +% +% All the \...cs stuff must be \long, because +% even though \par is (generally) unexpandable +% it can be \string'ed. +\long\def\usecs#1{\csname#1\endcsname} +\long\def\usecsafter#1{\expandafter\expandafter\csname#1\endcsname} +\long\def\noexpandcs#1{\expandafter\noexpand\csname#1\endcsname} +\long\def\unexpandedcs#1{\primunexpanded\expandafter\expandafter\expandafter{\csname#1\endcsname}} +\long\def\passcs#1#2{\expandafter\swapargs\csname#2\endcsname{#1}} +\long\def\passexpandedcs#1#2{\passcs{\passexpanded{#1}}{#2}} +\long\def\commandtoname#1{\expandafter\gobbleone\string#1} +% +\long\pdef\defcs#1{\expandafter\def \csname#1\endcsname} +\long\pdef\edefcs#1{\expandafter\edef \csname#1\endcsname} +\long\pdef\gdefcs#1{\expandafter\gdef \csname#1\endcsname} +\long\pdef\xdefcs#1{\expandafter\xdef \csname#1\endcsname} +\long\pdef\letcs#1{\expandafter\let \csname#1\endcsname} +% If \csname#2\endcsname is undefined, +% we don't want to set it to \relax. +\long\pdef\lettocs #1#2{\ifcs{#2}{\expandafter\let\expandafter#1\csname#2\endcsname}{\let#1\somethingtottallyundefined}} +\long\pdef\letcstocs #1#2{% + \ifcs{#2} + {\expandafter\expandafter\expandafter\let\expandafter\expandafter\csname#1\endcsname\csname#2\endcsname} + {\expandafter\let\csname#1\endcsname\somethingtotallyundefined}% + } +% +\long\pdef\addleft#1#2{% + \edef#1{\primunexpanded{#2}\primunexpanded\expandafter{#1}}% + } +\long\pdef\eaddleft#1#2{% + \edef#1{#2\primunexpanded\expandafter{#1}}% + } +\long\pdef\addleftcs#1#2{% + \edefcs{#1}{\primunexpanded{#2}\unexpandedcs{#1}}% + } +\long\pdef\eaddleftcs#1#2{% + \edefcs{#1}{#2\unexpandedcs{#1}}% + } +\long\pdef\addright#1#2{% + \edef#1{\primunexpanded\expandafter{#1}\primunexpanded{#2}}% + } +\long\pdef\eaddright#1#2{% + \edef#1{\primunexpanded\expandafter{#1}#2}% + } +\long\pdef\addrightcs#1#2{% + \edefcs{#1}{\unexpandedcs{#1}\primunexpanded{#2}}% + } +\long\pdef\eaddrightcs#1#2{% + \edefcs{#1}{\unexpandedcs{#1}#2}% + } +% +% +% +% COMMAND TESTING +% +% +\def\reverse#1{% + \ifcs{pi@reverse_\commandtoname#1} + {\usecs{pi@reverse_\commandtoname#1}} + {\senderror{texapi}{You can't use `\string\reverse' before `\string#1'}#1}% + } +% +\def\pi@ifdef_simple#1#2{% + \long\def#1##1{% + #2% + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi + }% + \defcs{pi@reverse_\commandtoname#1}{\expandafter\unless#1}% + % \iff... version + \long\defcs{if\expandafter\gobbletwo\string#1}##1{% + #2% + \expandafter\unbrace + \else + \expandafter\gobbleone + \fi + }% + \defcs{pi@reverse_if\expandafter\gobbletwo\string#1}{\passcs{\expandafter\unless}{if\expandafter\gobbletwo\string#1}}% + } +% +\pi@ifdef_simple\ifcommand{\ifdefined#1} +\pi@ifdef_simple\ifcs{\ifcsname#1\endcsname} +\pi@ifdef_simple\ifemptycommand{\ifx#1\emptycs} +% +\def\pi@ifdef_onearg#1{% + \long\defcs{pi@reverse_\commandtoname#1}##1##2##3{#1{##1}{##3}{##2}}% + % This is the \iff... version + \long\defcs{if\expandafter\gobbletwo\string#1}##1##2{#1{##1}{##2}{}}% + \long\defcs{pi@reverse_if\expandafter\gobbletwo\string#1}##1##2{#1{##1}{}{##2}}% + % In many cases, \long is better, otherwise it does no real harm. + \long\def#1##1% + } +\pi@ifdef_onearg\ifemptycs{% + \ifcs{#1} +% No point using \ifxcs, that would run useless tests. + {\expandafter\ifx\csname#1\endcsname\emptycs + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi} + \secondoftwo + } +\def\pi@ifdef_twoarg#1{% + \long\defcs{pi@reverse_\commandtoname#1}##1##2##3##4{#1{##1}{##2}{##4}{##3}}% + \long\defcs{if\expandafter\gobbletwo\string#1}##1##2##3{#1{##1}{##2}{##3}{}}% + \long\defcs{pi@reverse_if\expandafter\gobbletwo\string#1}##1##2##3{#1{##1}{##2}{}{##3}}% + \long\def#1##1##2% + } +% +\pi@ifdef_twoarg\ifxcs{% + \ifcs{#1} + {\expandafter\ifx\csname#1\endcsname#2% + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi} + {\ifcommand#2\secondoftwo\firstoftwo} + } +% +\pi@ifdef_twoarg\ifxcscs{% + \ifcs{#1} + {\ifcs{#2} + {\passcs{\expandafter\ifx\csname#1\endcsname}{#2}% + \expandafter\firstoftwo\else\expandafter\secondoftwo\fi} + {\secondoftwo}} + {\ifcs{#2} + {\secondoftwo} + {\firstoftwo}} + } +% +% +% +% +% CUSTOM \IF'S +% +\pdef\newife#1{% + \expandafter\passexpandednobraces\expandafter\pi@newif_strip\expandafter{\expandafter\gobbleone\string#1\pi@terminator} + } +\bgroup +\uccode`2=`i \uccode`3=`f +\uppercase{ + \gdef\pi@newif_strip23#1\pi@terminator{% + \pi@newif_def{#1}% + } + } +\egroup +% +\def\pi@newif_def#1{% + \letcs{if#1}\secondoftwo + \long\edefcs{pi@reverse_if#1}##1##2{\noexpandcs{if#1}{##2}{##1}}% + \edefcs{iff#1}{\noexpandcs{if#1}\noexpand\unbrace\noexpand\gobbleone}% + \edefcs{pi@reverse_iff#1}{\noexpandcs{if#1}\noexpand\gobbleone\noexpand\unbrace}% + \defcs{#1true}{% + \letcs{if#1}\firstoftwo + }% + \defcs{#1false}{% + \letcs{if#1}\secondoftwo + }% + } +% +\long\def\straightenif#1#2{% + \usecs{#1}#2% + \expandafter\firstoftwo + \else + \expandafter\secondoftwo + \fi + } +\long\def\straighteniff#1#2{% + \usecs{#1}#2% + \expandafter\unbrace + \else + \expandafter\gobbleone + \fi + } +\long\def\pi@reverse_straightenif#1#2{% + \usecs{#1}#2% + \expandafter\secondoftwo + \else + \expandafter\firstoftwo + \fi + } +\long\def\pi@reverse_straighteniff#1#2{% + \usecs{#1}#2% + \expandafter\gobbleone + \else + \expandafter\unbrace + \fi + } +% +% +% +% +% This is dangerous. But useful to get +% material across a conditional. +% +\long\def\afterfi#1#2\fi{\fi#1} +\long\def\afterdummyfi#1#2\fi{#1} +% +% +% +% +% WHAT COMES NEXT +% +\newtoks\pi@temp_tokenA +\newtoks\pi@temp_tokenB +\long\pdef\nospace#1{% + \pi@temp_tokenA={#1}% + \def\pi@nospace_repeat{% + \expandafter\nospace\expandafter{\the\pi@temp_tokenA}% + }% + \futurelet\pi@temp\pi@nospace_check + } +\def\pi@nospace_check{% + \ifx\pi@temp\spacechar + \afterfi{% + \afterassignment\pi@nospace_repeat + \let\pi@temp= }% + \else + \expandafter\the\expandafter\pi@temp_tokenA + \fi + } +% +\def\pi@ifdef_next#1#2#3{% + \pdef#1{\pi@ifnext_any#2}% + \protected\defcs{\commandtoname#1nospace}{\pi@ifnext_nospace#2}% + \protected\defcs{if\expandafter\gobbletwo\string#1}{\pi@iffnext_any#2}% + \protected\defcs{if\expandafter\gobbletwo\string#1nospace}{\pi@iffnext_nospace#2}% +% \reverse version + \protected\defcs{pi@reverse_\commandtoname#1}{\pi@ifnext_any#3}% + \protected\defcs{pi@reverse_\commandtoname#1nospace}{\pi@ifnext_nospace#3}% + \protected\defcs{pi@reverse_if\expandafter\gobbletwo\string#1}{\pi@iffnext_any#3}% + \protected\defcs{pi@reverse_if\expandafter\gobbletwo\string#1nospace}{\pi@iffnext_nospace#3}% + } +% +\def\pi@unlessif{\unless\if} +\pi@ifdef_next\ifnext\if\pi@unlessif +\def\pi@unlessifcat{\unless\ifcat} +\pi@ifdef_next\ifcatnext\ifcat\pi@unlessifcat +\def\pi@unlessifx{\expandafter\unless\pi@ifx} +\pi@ifdef_next\ifxnext\pi@ifx\pi@unlessifx +% We remove the \noexpand's. +\long\def\pi@ifx#1#2#3#4{\ifx#2#4} +% Would the following be faster? +% \long\def\pi@ifx\noexpand#1\noexpand#2{\ifx#1#2} +% I wonder. +\long\def\pi@ifnext_any#1#2#3#4{% + \let\pi@ifnext_test=#1% + \let\pi@ifnext_token= #2% + \pi@temp_tokenA={#3}% + \pi@temp_tokenB={#4}% + \futurelet\pi@temp\pi@ifnext_do + } +\long\def\pi@ifnext_nospace#1#2#3#4{% + \nospace{\pi@ifnext_any#1#2{#3}{#4}}% + } +\long\def\pi@iffnext_any#1#2#3{% + \pi@ifnext_any#1#2{#3}{}% + } +\long\def\pi@iffnext_nospace#1#2#3{% + \nospace{\pi@ifnext_any#1#2{#3}{}}% + } +\def\pi@ifnext_do{% + \pi@ifnext_test\noexpand\pi@temp\noexpand\pi@ifnext_token + \expandafter\the\expandafter\pi@temp_tokenA + \else + \expandafter\the\expandafter\pi@temp_tokenB + \fi + } +% +% +% +% +% FOR LOOPS +% +\newcount\pi@dofor_count +\newif\ifpi@dofor_noempty +\pdef\dofor{% + \pi@dofor_noemptyfalse\pi@dofor_getlist + } +\pdef\dofornoempty{% + \pi@dofor_noemptytrue\pi@dofor_getlist + } +\long\def\pi@dofor_getlist#1{% + \nospace{\pi@dofor_getparameter{#1}}% + } +\long\def\pi@dofor_getparameter#1#2#{% + \pi@dofor_dolist{#1}{#2}% + } +\long\def\pi@dofor_dolist#1#2#3{% +% We keep count so loops can be nested. + \global\advance\pi@dofor_count1 + \ifpi@dofor_noempty + \long\defcs{pi@dofor_loop\the\pi@dofor_count}#2{% + \ifemptystring{##1} + {\pi@dofor_control} + {#3\pi@newfor_markloop{pi@dofor_control}}% + }% + \else + \long\defcs{pi@dofor_loop\the\pi@dofor_count}#2{#3\pi@newfor_markloop{pi@dofor_control}}% + \fi + \pi@dofor_control#1\pi@terminator + } +\def\pi@dofor_control{% + \ifxnext\pi@terminator + {\global\advance\pi@dofor_count-1 +% Unbrace the fourth argument. + \pi@gobbletoterminator\unbrace} + {\usecs{pi@dofor_loop\the\pi@dofor_count}}% + } +\let\pi@dofor_originalcontrol\pi@dofor_control +\let\resumedofor\pi@dofor_control +\long\pdef\breakdofor#1#2\pi@dofor_control#3\pi@terminator{% + \global\advance\pi@dofor_count-1 + % Gobble the fourth argument (executed if the loop lives to its own natural end). + \gobbleoneand{#1}% + } +\def\pi@dofor_retrieverest#1#2\pi@terminator{% + \global\advance\pi@dofor_count-1 + \gobbleoneand{#1{#2}}% + }% +\long\pdef\pausedofor#1#2\pi@dofor_control{#1} +% +% +% +% +% CUSTOM FOR LOOPS +% And here's the big one. +% +\pdef\newfor#1{% + \pi@dofor_noemptyfalse + \ifcatnext\bgroup{\pi@newfor_pass{#1}} + {\pi@newfor_pass{#1}{0}}% + } +\pdef\newfornoempty#1{% + \pi@dofor_noemptytrue + \ifcatnext\bgroup{\nospace{\pi@newfor_pass{#1}}} + {\nospace{\pi@newfor_pass{#1}{0}}}% + } +\long\def\pi@newfor_pass#1#2#3#{% + \pi@newfor_getcoda{#1}{#2}{#3} + } +\long\def\pi@newfor_getcoda#1#2#3#4{% + \ifnextnospace[{\pi@newfor_create{#1}{#2}{#3}{#4}} + {\pi@newfor_create{#1}{#2}{#3}{#4}[]}% + } +\let\pi@newfor_pi@nf_tm_\relax +\let\pi@nf_tm\pi@terminator +\let\pi@newfor_preloop\emptycs +\long\def\pi@newfor_create#1#2#3#4[#5]{% + \def\pi@temp##1##2##3##4##5##6##7##8##9{#3}% + \pi@temp_tokenA={\pi@newfor_define#1{#3}{#4}{#5}}% + \ifcase#2\relax + \the\pi@temp_tokenA 1{}{}{\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm}\unbrace + \or \the\pi@temp_tokenA 2{##1}{{##1}}{{}\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm}\gobbleoneand + \or \the\pi@temp_tokenA 3{##1##2}{{##1}{##2}}{{}{}\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm}\gobbletwoand + \or \the\pi@temp_tokenA 4{##1##2##3}{{##1}{##2}{##3}}{{}{}{}\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm}\gobblethreeand + \or \the\pi@temp_tokenA 5{##1##2##3##4}{{##1}{##2}{##3}{##4}}{{}{}{}{}\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm}\gobblefourand + \or \the\pi@temp_tokenA 6{##1##2##3##4##5}{{##1}{##2}{##3}{##4}{##5}}{{}{}{}{}{}\pi@nf_tm\pi@nf_tm\pi@nf_tm\pi@nf_tm}\gobblefiveand + \or \the\pi@temp_tokenA 7{##1##2##3##4##5##6}{{##1}{##2}{##3}{##4}{##5}{##6}}{{}{}{}{}{}{}\pi@nf_tm\pi@nf_tm\pi@nf_tm}\gobblesixand + \or \the\pi@temp_tokenA 8{##1##2##3##4##5##6##7}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}}{{}{}{}{}{}{}{}\pi@nf_tm\pi@nf_tm}\gobblesevenand + \or \the\pi@temp_tokenA 9{##1##2##3##4##5##6##7##8}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}}{{}{}{}{}{}{}{}{}\pi@nf_tm}\gobbleeightand + \else \senderror{texapi}{You can't have more than 8 passed arguments. Definition aborted}% + \fi + } +% +% \newfor\foo{<n>}<parameters>{<definition>}{<coda>} +% |#1 |#2 |#3 |#4 +% #5 = <n>+1 +% #6 = ##1##2...##n +% #7 = {##1}{##2}...{##n} +% #8 = <parameters> with terminators as arguments, to mark the end of the loop. +% #9 = \gobble<n>and +% +% So, for instance, something like \edef#1#6###5 means +% (without the double hashes): +% \edef\foo#1#2...#n#<n+1>, e.g. with n=3 +% \edef\foo#1#2#3#4 +% hence \foo{<first>}{<second>}{<third>}{<list>} +% |_________ passed _________| +% +% and this calls: +% \pi@newfor_userdefined:foo#7###5 +% => \pi@newfor_userdefined:foo{#1}{#2}{#3}#4<terminators> +% => \pi@newfor_userdefined:foo{<first>}{<second>}{<third>}<list><terminators> +% and of course \pi@newfor_userdefined:foo sees only the head of <list>. +% +% The first argument of <list> is tested to check whether it's a terminator +% (and whether it's empty in the noempty case) in which case the <coda>, +% i.e. #4, is executed. Otherwise <definition>, i.e. #3, is launched. +% +% \pi@newfor_markloop is just \usecs; but it's also a delimiter for +% \breakfor, \passarguments, etc., i.e. it delimits the code to be gobbled. +% \pi@newdor_preloop is a dummy macro that expands to nothing but which +% is useful in one special case: viz. \passarguments{<arg>} with only one +% argument passed, and at the end of a loop so that there's no intervening +% code; \passarguments calls \getargs@pi@newfor_userdefined:foo on its +% arguments, which should be properly braced; but because TeX removes +% braces around delimited arguments that fit exactly into the delimiters, +% \passarguments as just described would pass an unbraced argument, hence kertwang. +% \pi@newfor_preloop ensures that <arg> is never the sole properly fitted +% and hence unbracing-prone argument. It is then used by \getargs@pi@newfor_userdefined:foo +% to remove remaining code, if any. +% +% +% +% The definition is pretty ugly, but it works, and +% you should've seen what it looked like when I started. +% +\long\def\pi@newfor_define#1#2#3#4#5#6#7#8#9{% + \long\edef#1#6###5{% + \noexpandcs{pi@newfor_userdefined:\commandtoname#1}% + #7###5\primunexpanded\expandafter{\pi@temp#8\pi@terminator}% + }% + \ifpi@dofor_noempty + \long\defcs{pi@newfor_userdefined:\commandtoname#1}#6#2{% + \ifcs{pi@newfor_\commandtoname###5_} + {\pi@gobbletoterminator{#4}}% + {\ifemptystring{###5} + {\usecs{pi@newfor_userdefined:\commandtoname#1}#7} + {#3\pi@newfor_preloop\pi@newfor_markloop{pi@newfor_userdefined:\commandtoname#1}#7}}% + }% + \else + \long\defcs{pi@newfor_userdefined:\commandtoname#1}#6#2{% + \ifcs{pi@newfor_\commandtoname###5_} + {\pi@gobbletoterminator{#4}} + {#3\pi@newfor_preloop\pi@newfor_markloop{pi@newfor_userdefined:\commandtoname#1}#7}% + }% + \fi + \long\defcs{getargs@pi@newfor_userdefined:\commandtoname#1}#6###5\pi@newfor_preloop{% + #9{\usecs{pi@newfor_userdefined:\commandtoname#1}#7}% + }% + \passexpandednobraces{\long\defcs{retrievefor@pi@newfor_userdefined:\commandtoname#1}##1##2}% + {\pi@temp#8\pi@terminator}% + {\passexpanded{##1}{#9{}##2}}% + } +% +\long\def\pi@newfor_markloop#1{\usecs{#1}} +\long\def\passarguments#1\pi@newfor_markloop#2{% + \usecs{getargs@#2}#1% + } +\long\def\breakfor#1#2\pi@newfor_markloop#3#4\pi@terminator{% + \ifstring{#3}{pi@dofor_control}% + {\gobbleoneand{#1}} + {#1}% + } +\long\def\pausefor#1#2\pi@newfor_markloop#3{#1} +\long\def\resumefor#1{% + \ifstring{#1}{\dofor} + {\pi@dofor_control} + {\usecs{pi@newfor_userdefined:\commandtoname#1}}% + } +\long\def\retrieverest#1#2\pi@newfor_markloop#3{% + \ifstring{#3}{pi@dofor_control}% + {\pi@dofor_retrieverest{#1}} + {\usecs{retrievefor@#3}{#1}}% + } +% +% +% +% +% WHILE LOOPS +% +\long\def\repeatuntil#1{% + \pi@repeatuntil_loop{\numexpr(0)}{#1}% + } +\long\def\pi@repeatuntil_loop#1#2#3{% + \ifnum#2>#1\relax + \expandafter\unbrace + \else + \expandafter\gobbleone + \fi + {#3\pi@repeatuntil_loop{\numexpr(#1+1)}{#2}{#3}}% + } +% +\long\def\dowhile#1#2{% + #1{#2\dowhile{#1}{#2}}{}% + } +% +% +% +% +% CUSTOM WHILE LOOPS +% +% \newdowhile\foo<nb of args><trarg_1>...<trarg_n><body> +% where <trarg_1>...<trarg_n> are the transformations of <arg_1>...<arg_n> +% from one iteration to the next. +% +% The \newhile is basically a simple version of \newfor. +% +\long\pdef\newwhile#1#2{% + \def\pi@temp{#1}% + \ifcase#2 + \expandafter\pi@newwhile_getzero + \or\expandafter\pi@newwhile_getone + \or\expandafter\pi@newwhile_gettwo + \or\expandafter\pi@newwhile_getthree + \or\expandafter\pi@newwhile_getfour + \or\expandafter\pi@newwhile_getfive + \or\expandafter\pi@newwhile_getsix + \or\expandafter\pi@newwhile_getseven + \or\expandafter\pi@newwhile_geteight + \or\expandafter\pi@newwhile_getnine + \fi + } +\long\def\pi@newwhile_getzero{% + \expandafter\pi@newwhile_create\pi@temp{}{}{}\unbrace} +\long\def\pi@newwhile_getone#1{% + \expandafter\pi@newwhile_create + \pi@temp{##1}{{##1}}{{#1}}\gobbleoneand} +\long\def\pi@newwhile_gettwo#1#2{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2}{{##1}{##2}}{{#1}{#2}}\gobbletwoand} +\long\def\pi@newwhile_getthree#1#2#3{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2##3}{{##1}{##2}{##3}}{{#1}{#2}{#3}}\gobblethreeand} +\long\def\pi@newwhile_getfour#1#2#3#4{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2##3##4}{{##1}{##2}{##3}{##4}}{{#1}{#2}{#3}{#4}}\gobblefourand} +\long\def\pi@newwhile_getfive#1#2#3#4#5{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2##3##4##5}{{##1}{##2}{##3}{##4}{##5}}{{#1}{#2}{#3}{#4}{#5}}\gobblefiveand} +\long\def\pi@newwhile_getsix#1#2#3#4#5#6{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2##3##4##5##6}{{##1}{##2}{##3}{##4}{##5}{##6}}{{#1}{#2}{#3}{#4}{#5}{#6}}\gobblesixand} +\long\def\pi@newwhile_getseven#1#2#3#4#5#6#7{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2##3##4##5##6##7}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}}{{#1}{#2}{#3}{#4}{#5}{#6}{#7}}\gobblesevenand} +\long\def\pi@newwhile_geteight#1#2#3#4#5#6#7#8{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2##3##4##5##6##7##8}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}}{{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}}\gobbleeightand} +\long\def\pi@newwhile_getnine#1#2#3#4#5#6#7#8#9{% + \expandafter\pi@newwhile_create + \pi@temp{##1##2##3##4##5##6##7##8##9}{{##1}{##2}{##3}{##4}{##5}{##6}{##7}{##8}{##9}}{{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}}\gobblenineand} +% +% +% \newwhile\foo<n><trans_1><trans_2>...<trans_n><body> +% +% Below: +% #1 = \foo +% #2 = ##1##2...##n +% #3 = {##1}{##2}...{##n} +% #4 = <trans_1><trans_2>...<trans_n> +% #5 = \gobble<n>and +% #6 = <body> +% +% ... so that \foo (#1) takes <n> arguments (#2), executes <body> (#6), +% and runs its internal version (...newhile_userdefined...) on these same arguments +% properly braced (#3). This internal version takes of course <n> arguments +% and returns \foo with the arguments transformed (#4). +% ..._preloop and ..._markloop are just markers, although the is equivalent to \usecs +% so as to launch the following macro. +% +\long\def\pi@newwhile_create#1#2#3#4#5#6{% + \long\def#1#2{% + #6\pi@newwhile_preloop\pi@newwhile_markloop{pi@newwhile_userdefined:\commandtoname#1}#3% + }% + \long\defcs{pi@newwhile_userdefined:\commandtoname#1}#2{#1#4}% + \long\defcs{changewhile@pi@newwhile_userdefined:\commandtoname#1}#2{\pi@gobbletoterminator{#5{#1#3}}}% + \letcs{breakwhile@pi@newwhile_userdefined:\commandtoname#1}#5% + } +% +\let\pi@newwhile_preloop\emptycs +\let\pi@newwhile_markloop\usecs +\long\def\changewhile#1\pi@newwhile_markloop#2{% + \usecs{changewhile@#2}#1\pi@terminator + } +\long\def\breakwhile#1#2\pi@newwhile_markloop#3{% + \usecs{breakwhile@#3}{#1}% + } +% +% +% +% +% CATCODE SETTINGS +% +\pdef\setcatcodes#1{% + \ifsuffix,{#1}{\pi@setcatcodes_loop{#1}} + {\pi@setcatcodes_loop{#1,}}% + } +\def\restorecatcodes{% + \def\restorecatcodes{}% + } +\def\pi@setcatcodes_loop#1{% + \dofor{#1}##1=##2,{% + \dofor{##1}####1{% + \ifemptycommand\restorecatcodes + {\def\restorecatcodes{\def\restorecatcodes{}}}% + {}% + \eaddright\restorecatcodes{% + \catcode`\noexpand####1=\the\catcode`####1\relax}% + \catcode`####1=##2\relax}{}}{}% + } +% +% +% +% +% STRINGS +% +\let\pi@thisisjustanemptystring_donottouchit\relax +\pi@ifdef_simple\ifemptystring{\ifcsname pi@thisisjustanemptystring_donottouchit\detokenize{#1}\endcsname} +% +\pi@ifdef_twoarg\ifstring{% + \passcs\gobbleone{pi@ifstring_string\detokenize{#1:#1}}% + \ifcs{pi@ifstring_string\detokenize{#1:#2}} + } +% +\long\pdef\newstring#1{% + \long\gdefcs{pi@strings_ifprefix:\detokenize{#1}}##1#1##2\pi@terminator{% + \ifemptystring{##1}\firstoftwo\secondoftwo}% + \long\gdefcs{pi@strings_removeprefix:\detokenize{#1}}#1##1\pi@terminator{##1}% + \long\gdefcs{pi@strings_removeprefixand:\detokenize{#1}}#1##1\pi@terminator##2{##2{##1}}% +% + \long\gdefcs{pi@strings_ifsuffix:\detokenize{#1}}##1#1\pi@terminator##2\pi@terminator{% + \ifemptystring{##2}\secondoftwo{\pi@gobbletoterminator\firstoftwo}}% + \long\gdefcs{pi@strings_removesuffix:\detokenize{#1}}##1#1\pi@terminator{##1}% + \long\gdefcs{pi@strings_removesuffixand:\detokenize{#1}}##1#1\pi@terminator##2{##2{##1}}% +% + \long\gdefcs{pi@strings_ifcontains:\detokenize{#1}}##1#1##2\pi@terminator{% + \ifemptystring{##2}\secondoftwo\firstoftwo}% + \long\gdefcs{pi@strings_splitstring:\detokenize{#1}}##1#1##2\pi@terminator##3{% + ##3{##1}{##2}}% + } +% +\pi@ifdef_twoarg\ifprefix{% + \ifemptystring{#1}\firstoftwo + {\ifcs{pi@strings_ifprefix:\detokenize{#1}} + {\usecs{pi@strings_ifprefix:\detokenize{#1}}#2#1\pi@terminator} + {\long\def\pi@temp##1#1##2\pi@terminator{\ifemptystring{##1}\firstoftwo\secondoftwo}% + \pi@temp#2#1\pi@terminator}} + } +% +\pi@ifdef_twoarg\ifsuffix{% + \ifemptystring{#1}\firstoftwo % An empty string is always a suffix. And it'd ruin the test. + {\ifcs{pi@strings_ifsuffix:\detokenize{#1}} + {\usecs{pi@strings_ifsuffix:\detokenize{#1}}#2\pi@terminator#1\pi@terminator\pi@terminator} + {\long\def\pi@temp##1#1\pi@terminator##2\pi@terminator{\ifemptystring{##2}\secondoftwo{\pi@gobbletoterminator\firstoftwo}}% + \pi@temp#2\pi@terminator#1\pi@terminator\pi@terminator}} + } +% +\pi@ifdef_twoarg\ifcontains{% + \ifemptystring{#1}\firstoftwo + {\ifcs{pi@strings_ifcontains:\detokenize{#1}} + {\usecs{pi@strings_ifcontains:\detokenize{#1}}#2#1\pi@terminator} + {\long\def\pi@temp##1#1##2\pi@terminator{\ifemptystring{##2}\secondoftwo\firstoftwo}% + \pi@temp#2#1\pi@terminator}} + } +% +\long\def\removeprefix#1#2{% + \ifcs{pi@strings_removeprefix:\detokenize{#1}} + {\usecs{pi@strings_removeprefix:\detokenize{#1}}#2\pi@terminator} + {\long\def\pi@temp#1##1\pi@terminator{##1}% + \pi@temp#2\pi@terminator}% + } +\long\def\removeprefixand#1#2#3{% + \ifcs{pi@strings_removeprefixand:\detokenize{#1}} + {\usecs{pi@strings_removeprefixand:\detokenize{#1}}#2\pi@terminator{#3}} + {\long\def\pi@temp#1##1\pi@terminator{#3{##1}}% + \pi@temp#2\pi@terminator}% + } +\long\pdef\removeprefixin#1#2#3{% + \long\def\pi@temp#1##1\pi@terminator{\def#3{##1}}% + \pi@temp#2\pi@terminator + } +% +\long\def\removesuffix#1#2{% + \ifcs{pi@strings_removesuffix:\detokenize{#1}} + {\usecs{pi@strings_removesuffix:\detokenize{#1}}#2\pi@terminator} + {\long\def\pi@temp##1#1\pi@terminator{##1}% + \pi@temp#2\pi@terminator}% + } +\long\def\removesuffixand#1#2#3{% + \ifcs{pi@strings_removesuffixand:\detokenize{#1}} + {\usecs{pi@strings_removesuffixand:\detokenize{#1}}#2\pi@terminator{#3}} + {\long\def\pi@temp##1#1\pi@terminator{#3{##1}}% + \pi@temp#2\pi@terminator}% + } +\long\pdef\removesuffixin#1#2#3{% + \long\def\pi@temp##1#1\pi@terminator{\def#3{##1}}% + \pi@temp#2\pi@terminator + } +% +\long\def\splitstring#1#2#3{% + \ifcs{pi@strings_splitstring:\detokenize{#1}} + {\usecs{pi@strings_splitstring:\detokenize{#1}}#2\pi@terminator{#3}} + {\long\def\pi@temp##1#1##2\pi@terminator{#3{##1}{##2}}% + \pi@temp#2\pi@terminator}% + } +% +% +% +% +% TRIMMING +% (adapted from Will Robertson's trimspace) +% +\long\def\passtrimleft#1#2{% + \expandafter\swaprightbraced\expandafter{\romannumeral-`\.\noexpand#1}{#2}% + } +\def\trimleft#1{% + \passtrimleft{#1}\unbrace + } +\bgroup +\catcode`\Q=3 +\long\gdef\passtrimright#1{% + \pi@passtrimright_first#1Q Q% + }% +\long\gdef\pi@passtrimright_first#1 Q{% + \pi@passtrimright_second#1Q% + } +\long\gdef\pi@passtrimright_second#1Q#2#3{% + #3{#1}% + } +\egroup +\def\trimright#1{% + \passtrimright{#1}\unbrace + } +\long\def\passtrim#1{% + \passtrimleft{#1}\passtrimright + } +\def\trim#1{% + \passtrim{#1}\unbrace + } +\long\pdef\deftrimleft#1#2{% + \passtrimleft{#2}{\def#1} + } +\long\pdef\deftrimright#1#2{% + \passtrimright{#2}{\def#1} + } +\long\pdef\deftrim#1#2{% + \passtrim{#2}{\def#1}% + } + +\pi@restorecatcodes +\endinput
\ No newline at end of file |