From 6a610c4ffff82f7e5188bd0154b7859244e02447 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Fri, 18 Jun 2010 15:55:40 +0000 Subject: new generic package texapi (17jun10) git-svn-id: svn://tug.org/texlive/trunk@19035 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/doc/generic/texapi/texapi-doc.tex | 1865 ++++++++++++++++++++ 1 file changed, 1865 insertions(+) create mode 100644 Master/texmf-dist/doc/generic/texapi/texapi-doc.tex (limited to 'Master/texmf-dist/doc/generic/texapi/texapi-doc.tex') 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 -- cgit v1.2.3