diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/inlinedef/inlinedef.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/inlinedef/inlinedef.dtx | 1220 |
1 files changed, 1220 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/inlinedef/inlinedef.dtx b/Master/texmf-dist/source/latex/inlinedef/inlinedef.dtx new file mode 100644 index 00000000000..6d4966a1007 --- /dev/null +++ b/Master/texmf-dist/source/latex/inlinedef/inlinedef.dtx @@ -0,0 +1,1220 @@ +% \iffalse +% +% inlinedef.dtx +% Copyright 2008 Stephen D. Hicks +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3 +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3 or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Stephen D. Hicks +% +% This work consists of the files +% README +% inlinedef.dtx (this file) +% inlinedef.ins +% inlinedef.pdf +% and the derived files +% inlinedef.sty +% inlinetest.tex +% +% Run LaTeX on this file to produce documentation. +% Run LaTeX on the .ins file to produce the package. +%<*driver> +\ProvidesFile{inlinedef.dtx} +%</driver> +% +%<package>\NeedsTeXFormat{LaTeX2e} +%<package>\ProvidesPackage{inlinedef}% +%<testsuite>\documentclass{minimal} +%<testsuite>\usepackage{inlinedef}% + [2008/07/10 v1.0 Inlined expansions within definitions] +%<*driver> +\documentclass{ltxdoc} +\CheckSum{1876} +%\OnlyDescription % (un)comment this line to show (hide) source code +\RecordChanges +\EnableCrossrefs +\CodelineIndex % (un)comment this line to index source by page (line) +\begin{document} + \newcommand*{\Lopt}[1]{\textsf {#1}} + \parindent0pt + \def\*#1{\texttt{\string#1}} %% sdh - |...| doesn't work in headings + \makeatletter + % + \newcount\mac@depth\mac@depth\z@ + \newcommand\@macros{}\newcommand\@endmacros{} + \catcode`&3 %% we use a funny catcode to ensure never used. + \def\@macros#1,{\macro{#1}\global\advance\mac@depth\@ne\relax + \@ifnextchar&\@gobble\@macros} + \def\@endmacros{\let\mac@next\relax\ifnum\mac@depth>\z@ + \endmacro\let\mac@next\@endmacros + \global\advance\mac@depth\m@ne\fi\mac@next} + \newenvironment{macros}[1]{\@macros#1,&}{\@endmacros} + \catcode`&4 %% put it back + % Stop indexing at a certain point + \newcommand\StopIndexing{\let\special@index\@gobble} + % done... + \makeatother %% must be balanced for character table to work properly + % + % Don't index unimportant names... + \DoNotIndex{\@ne,\@whilesw,\@gobble,\@eha,\@ehd,\@empty,\xa} + \DoNotIndex{\advance,\begin,\begingroup,\xdef} + \DoNotIndex{\catcode,\DeclareRobustCommand,\def,\detokenize,\edef} + \DoNotIndex{\end,\endgroup,\errorcontextlines,\expandafter,\foo} + \DoNotIndex{\futurelet,\gdef,\global,\let,\long} + \DoNotIndex{\m@ne,\makeatletter,\makeatother,\meaning,\message} + \DoNotIndex{\MessageBreak,\newcommand,\newcount,\newif,\newtoks} + \DoNotIndex{\noexpand,\outer,\PackageError,\protect,\protected@edef} + \DoNotIndex{\relax,\renewcommand,\space,\string,\the\toks,\undefined} + \DoNotIndex{\ifcat,\ifnum,\ifx,\else,\fi,\fi,\fi} + % Do the stuff + \DocInput{inlinedef.dtx} + \setcounter{IndexColumns}{2} + \def\bang{\texttt{!}} + \PrintIndex + %\PrintChanges +\end{document} +%</driver> +% \fi +% \changes{v1.0}{2008/07/05} +% {(SDH) Initial version.} +% +% \GetFileInfo{inlinedef.dtx} +% \title{The \textsf{inlinedef}% +% \iffalse - don't include this for now... +% \thanks{\textsf{darcs} repository at +% \texttt{http://www.physics.cornell.edu/$\sim$shicks/darcs/latex/inlinedef/}. +% Patch submissions welcome.}\fi +% package} +% \author{Stephen Hicks\thanks{email: \texttt{sdh33@cornell.edu}}} +% \date{\fileversion{} -- \filedate} +% \maketitle +% +% \section{Usage} +% +% \subsection{The problem} +% Often package writers want to redefine certain macros to do slightly +% more than what they did previously, adding a control sequence or two +% to the beginning or the end of the definition. The easiest way to +% accomplish this is to use something like +% \begin{verbatim} +% \let\old@macro\macro +% \def\macro{...\old@macro...}\end{verbatim} +% But this sort of construction can cause problems if another package +% also wants to redefine the same macro and happens to choose the same +% name to save it to. It's also an ugly solution in that it pollutes +% the global namespace with extra macro names. A much cleaner solution +% is to define the new macro with the old macro expanded inline, as in +% |\edef\macro{...\macro...}|. This is generally problemmatic because +% there are often undefined control sequences and macros that we don't +% want to expand quite yet. A compromise is to use |\expandafter|, but +% this leads to error-prone and unreadable code: +% \begin{verbatim} +% \expandafter\def\expandafter\macro\expandafter{\expandafter +% ...\macro...}\end{verbatim} +% +% \subsection{The solution} +% What we really want is a way to expand just a few +% tokens in the definition and leave the rest untouched. +% \iffalse - not ready yet... +% The package |expansions| provides a way to do this more +% generally, but in the case of definitions, this package is more +% expressive and simpler. +% \fi +% We provide a command \DescribeMacro\Inline|\Inline| +% that can be inserted before a |\def| or |\gdef| (optionally prefixed +% by |\long|, |\outer|, and/or |\global|, as in +% |\Inline\long\outer\gdef...|). Within |\Inline| definitions, +% only tokens preceded by \DescribeMacro\Expand|\Expand| are expanded. +% Thus, the previous example becomes +% \begin{verbatim} +% \Inline\def\macro{...\Expand\macro...}\end{verbatim} +% +% \subsection{Special commands} +% While nearly everything can be done with |\Expand| alone, we provide +% a few more keywords for completeness and convenience. +% \begin{itemize} +% \item\DescribeMacro\Expand|\Expand| - Performs a single expansion +% on the token or group +% immediately following and places the result directly into the +% definition without further processing. In the case of a group, +% only the first token is expanded (although |\expandafter|s may +% be used to expand a different token), and +% the outermost grouping braces are discarded. +% \item\DescribeMacro\MultiExpand|\MultiExpand|\marg{number} - +% Expands the following token or group the given number of times. +% For example, +% \begin{verbatim} +% \MultiExpand{3}{\expandafter\expandafter\expandafter\a +% \expandafter\b\c}\end{verbatim} +% expands first |\c|, then |\b|, then |\a|, and inserts the whole +% expansion into the definition with no braces. Note that the braces +% are important. Otherwise it will just try to expand the first +% |\expandafter| three times, which is clearly wrong. +% \item\DescribeMacro\UnsafeExpand|\UnsafeExpand| - This version simply +% inserts an |\expandafter|, performing the expansion as in |\Expand| +% above, but reinserting the result back +% into the stream to be processed. Thus, any tokens like |\Expand| or +% |\Super| in the expansion will be acted on. Unlike the previous two +% commands, groups are \emph{not\/} treated differently. +% \item\DescribeMacro\NoExpand|\NoExpand| - If a token is preceeded +% by |\NoExpand| then it is inserted in the definition exactly as-is. +% This is required to insert any of the special tokens |\Expand|, +% |\NoExpand|, etc, as well as the internal token |\Q@END|, into a +% definition. If the token immediately following |\NoExpand| is +% an open-brace then the entire text of the group will be inserted +% without expansion, and the outer level of grouping will be lost. +% \item\DescribeMacro\Super|\Super| - When redefining an already +% existing macro, |\Super| will expand to the previous definition of +% the macro. Any macro parameters are automatically substituted. +% If the macro is undefined, or if the new parameter text doesn't match +% with the old text, then this will cause an error. +% \item\DescribeMacro\Recurse|\Recurse| - This is complementary to +% |\Super| and, while not strictly necessary, is included for clarity. +% |\Recurse| is equivalent to +% |\NoExpand\macro| when defining |\macro|. However, since |\macro| +% is, by default, not expanded anyway, this is a bit redundant. +% \end{itemize} +% +% \goodbreak +% \subsection{Calling options} +% When the name of the macro we're defining is encountered, there are +% three different ways we might proceed: leave it alone (|\NoExpand|), +% expand it with implicit parameters (|\Super|), or expand it with +% explicit parameters (|\UnsafeExpand|). +% We therefore allow zero, one, or two stars to come after |\Inline| to +% change this behavior. +% \begin{itemize} +% \item\DescribeMacro\Inline|\Inline| - Without any stars, we default +% to leaving the macro name alone, as in |\NoExpand\macro|. +% This is the most consistent behavior with the rest of the package and +% works regardless of whether the macro is being defined or redefined. +% \item\DescribeMacro{\Inline*}|\Inline*| - With a single star, we treat +% the macro name as a +% call to |\Super| and expand it with parameters inserted automatically. +% This is preferred over |\Expand| because it doesn't lead to the possible +% surprises in the case of recursively-defined macros. +% \item\DescribeMacro{\Inline**}|\Inline**| - Finally, with two stars, +% the macro name is treated as if it were preceeded by |\UnsafeExpand|. +% Any parameters must be inserted explicitly, and the expansion is +% itself subject to inline processing. Note that this form is the most +% dangerous. +% \end{itemize} +% +% \index{Inline\bang=\verb!*+\Inline+\bang|usage} +% \begingroup\def\index#1{}%\iffalse - index by hand! - \fi +% \DescribeMacro{\Inline!}\endgroup +% One final option applies only in the case of redefining an already-existing +% macro. In this case, if the parameter text of the new definition differs +% from the parameter text of the old definition, we will produce an error. +% This error can be suppressed by adding a bang to the end of |\Inline| +% (either before or after the stars), acknowledging that any ill consequences +% that result are your own fault. +% +% \iffalse - this part not yet implemented! +% The last special +% token is \DescribeMacro\Args|\Args|. |\Args| inserts a protected +% version of the argument text, that can be used when recursing, as +% in |\Recurse\Args|, to make the exact same call again. Otherwise, +% when using |\Recurse|, you are responsible for passing the arguments +% yourself, unlike |\Super|, which behaves like a hypothetical +% |\ExpandAfter\ExpandAfter\ExpandAfter\Expand\ExpandAfter\Recurse\Args| +% (though |\ExpandAfter| doesn't currently +% exist). +% \fi +% +% \subsection{Known issues} +% \begin{itemize} +% \item If a macro is defined with a character other than |#| catcoded to +% 6, then |\Super| will fail unless the same character is used in the +% redefinition. +% \end{itemize} +% +% \subsection{Related packages} +% \begin{description} +% \item[moredefs] The \textsf{moredefs} package in the \textsf{frankenstein} +% collection provides some similar syntactic sugar, but is not as +% expressive. +% \end{description} +% +% \StopEventually{} +% +% \filbreak +% \makeatletter +% \section{Implementation} +% \begin{macro}{\xa} +% Make the |@|-sign into a letter for use in macro names. As long as the +% packages are well-behaved, we can put this here and not later. We also +% define |\xa| to be |\expandafter| for convenience. +% \begin{macrocode} +%<*package> +\makeatletter +\let\xa\expandafter +% \end{macrocode} +% \end{macro} +% +% \begin{macros}{\ifID@aborted,\ifID@star,\ifID@starstar,\ifID@bang} +% We define a conditional so that we can gracefully abort in case of an error. +% \begin{macrocode} +\newif\ifID@aborted +\newif\ifID@star +\newif\ifID@starstar +\newif\ifID@bang +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@toks,\ID@count} +% At some point we need to stop using the internal toks registers and allocate +% our own, because somebody might want to |\Expand{\the\toks@}| and expect +% something else. We can get by with a single one though by defining a +% |\ID@pdef| that doubles all the |#| signs and then does a regular def, so that +% |\ID@pdef\cs\ID@toks ... \cs| will be the same as |\the\ID@toks|. +% \begin{macrocode} +\newtoks\ID@toks +\newcount\ID@count +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\Inline,\ID@scandef} +% These are the macros that get it all started. |\Inline| opens up +% a group (which is closed at the end of |\ID@def|) and initializes a +% toks register (we don't bother allocating it since we're in a group +% and don't call any \LaTeX{} or \TeX{} macros that make use of allocated +% toks registers. Then we scan the tokens until we +% find either an |\edef| or an |\xdef|. If it's anything else, we just +% add it to the toks register. We also have a list of bad tokens that +% will cause an error message, so that we don't go too far before figuring +% out what went wrong. Should |\Inline| be |\outer|? +% \begin{macrocode} +\DeclareRobustCommand\Inline{% + \begingroup + % Define a few ``quarks'' + \def\Expand{\Expand}\def\Super{\Super}% + \def\UnsafeExpand{\UnsafeExpand}\def\MultiExpand{\MultiExpand}% + \def\Recurse{\Recurse}\def\NoExpand{\NoExpand}% + \def\Q@END{\Q@END}% + % Define a toks register + \ID@toks{}% + % Signal that we need to look for a star + \@testtrue\ID@starfalse\ID@starstarfalse\ID@bangfalse + % Start scanning for \def or \gdef + \futurelet\@foo\ID@scandef +} +\newcommand\ID@scandef{% + \let\next\ID@saveprefix % Default behavior + % If this is the first few tokens after the \Inline, check for * or ! + \if@test + \ifx\@foo*% + \ifID@star + \ifID@bang\let\next\ID@sd@lastcheck\else\let\next\ID@sd@checkagain\fi + \ID@starstartrue + \else + \let\next\ID@sd@checkagain + \ID@startrue + \fi + \fi + \ifx\@foo!% + \ifID@bang\else % two bangs - can this be anything but an error? + \ID@bangtrue + \xa\let\xa\next\ifID@starstar\ID@sd@lastcheck\else\ID@sd@checkagain\fi + \fi + \fi + \fi + % Now look for a \def or \gdef + \ifx\@foo\def + \def\next{\ID@start\def}% + \fi + \ifx\@foo\gdef + \def\next{\ID@start\gdef}% + \fi + \ifcat\noexpand\@foo\space + \def\next{\ID@toks\xa\xa\xa{\xa\the\xa\ID@toks\space}% + \xa\futurelet\xa\@foo\xa\ID@scandef\ID@unspace}% copied from ID@space + \fi + % Error checking (minimal) + \@testfalse + \ifx\@foo\edef\@testtrue\fi\ifx\@foo\xdef\@testtrue\fi + \ifx\@foo\newcommand\@testtrue\fi\ifx\@foo\renewcommand\@testtrue\fi + \ifx\@foo\DeclareRobustCommand\@testtrue\fi + \if@test\PackageError{inlinedef}{Only \protect\def\space and \protect\gdef\space are + allowed after \protect\Inline,\MessageBreak but some other type of + definition was found}\@eha\let\next\ID@abort\fi + \@testfalse + \ifx\@foo\bgroup\@testtrue\fi\ifx\@foo\let\@testtrue\fi + \if@test\PackageError{inlinedef}{No \protect\def\space or \protect\gdef\space found + after \protect\Inline}\@ehd\def\next{\ID@abort{}}\fi + \next +} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@sd@checkagain,\ID@sd@lastcheck} +% These just get the scandef loop started again and set |\@testtrue| if we're +% still looking for stars and/or bangs. +% \begin{macrocode} +\def\ID@sd@checkagain#1{\@testtrue\futurelet\@foo\ID@scandef} +\def\ID@sd@lastcheck#1{\futurelet\@foo\ID@scandef} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@saveprefix,\ID@abort,\ID@start} +% These are the three macros called by |\ID@scandef| to either save the +% prefix (|\long|, |\outer|, etc) to a token register, (attempt to) abort +% the procedure in case of an error, or else get the definition started +% once we find the |\edef| or |\xdef|. +% \begin{macrocode} +\newcommand*\ID@saveprefix[1]{% + \ID@toks\xa{\the\ID@toks#1}% + \futurelet\@foo\ID@scandef +} +% \end{macrocode} +% In case the error was just the wrong type of |\def|, we consume up to and +% including the first explicit group. +% \begin{macrocode} +\newcommand\ID@abort{}\def\ID@abort#1#{\endgroup\@gobble} +% \end{macrocode} +% To get the definition process started, we take |#1| as the definition +% command to save (either |\def| or |\gdef|), |#2| as the command that +% was provided (which we discard), |#3| is the name of the macro to define, +% and |#4| is the parameter text, delimited by a begin-group character. +% \begin{macrocode} +\newcommand\ID@start{}\def\ID@start#1#2#3#4#{% + \xa\def\xa\ID@prefix\xa{\the\ID@toks#1}% + \ID@def#3{#4}% +} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@fixparams,\ID@fp@start} +% In order for |\Super| to work properly, we need to fix the parameter +% list to put the |#1| in braces, since it actually consists of two tokens. +% Therefore, |\ID@fixparams| takes everything between it and |\Q@END| +% and puts it in |\toks@fixedparams|. If it finds a |#|, then it checks +% whether the argument is delimited or not, and if not, it inserts +% a pair of braces. We currently define these with |\newcommand*|, though +% if there were a reason we could conceivably make them |\long|. +% Update: we now use |\ID@toks| and then define |\ID@fixedparams| from there. +% \begin{macrocode} +\newcommand*\ID@fixparams{\begingroup\ID@toks{}\futurelet\@foo\ID@fp@start} +\newcommand*\ID@fp@start{% + \let\next\ID@fp@normal + \ifx\@foo\Q@END\let\next\ID@fp@end\fi + \ifcat\noexpand\@foo##\let\next\ID@fp@param\fi % was \ifx\@foo - broken? + \ifcat\noexpand\@foo\space + \def\next{\ID@toks\xa\xa\xa{\xa\the\xa\ID@toks\space}% + \xa\futurelet\xa\@foo\xa\ID@fp@start\ID@unspace}% copied from ID@space + \fi + \next +} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@fp@normal,\ID@fp@param,\ID@fp@end} +% These are the two commands that |\ID@fixparams| calls to actually +% consume each token, depending on whether it was a parameter. +% \begin{macrocode} +\newcommand*\ID@fp@normal[1]{% + \ID@toks\xa{\the\ID@toks#1}\futurelet\@foo\ID@fp@start +} +\newcommand*\ID@fp@param[2]{% + % We used to just use ###2 but need two more now... + % Need another doubling because we're now using it inside a def... + \def\@arg{#########2}% assume delimited unless we find # or \Q@END + \ifcat\noexpand\@foo##\def\@arg{{#########2}}\fi + \ifx\@foo\Q@END\def\@arg{{#########2}}\fi + \ID@toks\xa\xa\xa{\xa\the\xa\ID@toks\@arg}% + \futurelet\@foo\ID@fp@start +} +\newcommand*\ID@fp@end[1]{% + \xa\endgroup\xa\def\xa\ID@fixedparams\xa{\the\ID@toks}% +} +% \end{macrocode} +% This should deal with everything \emph{except} a single |#|, +% but that's a hairy situation in the first place and we really don't +% want to allow using |\Super| in that case. We could probably make +% an error message to say so. The only other alternative would be to +% ``go back in time'' and change the last |{#1}| to a |#1{}| and even +% then we end up with an extra |{}| on the input stream. I can't actually +% figure out how to test if this has happened in |...@insertp|, anyway. +% \end{macros} +% +% \begin{macros}{\ID@def} +% Here is where the ``main loop'' is initiated. We start by +% pretending to allocate another token register (though we actually +% just |\toksdef| it), and then define a number of quarks which we +% use as delimiters for various purposes. Finally, we start scanning. +% Afterwards, we test if there was an error and if not, we expand the +% definition command \emph{after} the |\endgroup| so that we can clean +% up all the local variables. +% \begin{macrocode} +\newcommand\ID@def[3]{% + % Other definitions + \global\ID@abortedfalse + \let\@reservedc#1% + \def\@macroname{#1}% for error message + \ID@fixparams#2\Q@END + % These are used by \Super but easier to define here + \def\@reservedb#2{}% + \edef\@reservedb{\xa\ID@getprefix\meaning\@reservedb\Q@END}% + \ifx#1\undefined % hopefully nobody's going around defining \undefined + \let\@reserveda\undefined + \else + \edef\@reserveda{\xa\ID@getprefix\meaning#1\Q@END}% + \fi + % Scan it all into \ID@toks + \ifID@bang\else\ID@checkusage\fi + \ifID@aborted\else + \ID@toks{}\ID@scan#3\Q@END{}% we need the {} so that the the #1# works... + \fi + \ifID@aborted + \def\command{}% gracefully ignore + \else + \let#1\relax % don't want it expanded in the |\edef| below + % We don't need to worry about scope anymore + \toks0\ID@toks % likely redundant, but what if ID@toks=1 or 2? + \toks1\xa{\ID@prefix}% (easiest way to avoid expansion...) + \toks2{#2}% + \edef\command{\the\toks1#1\the\toks2{\the\toks0}}% + % We could also write this with 3 levels of \xa... + \fi + \global\ID@toks\xa{\ID@fixedparams}% just to test... + \expandafter\endgroup\command +} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@scan,\ID@switch} +% This is the main loop. We look at each token in turn and deal with it, +% mostly by inserting it into |\ID@toks|. If it's a |\Q@END| then we're done. +% If it's |\Super| or |\Expand| then we need to do something special. +% If it's a space, then we need to add the space to +% |\ID@toks|. Finally, if it's a |\bgroup| then we need to figure out whether +% it's an explicit or an implicit group. In the former case, we descend into +% it (writing |{...}| to |\ID@toks|) and in the latter, we just pick it up like +% normal. +% \begin{macrocode} +\newcommand\ID@scan{\futurelet\@foo\ID@switch} +\newcommand\ID@switch{% + \let\next\ID@normal + \ifx\@foo\Q@END + \let\next\@gobble + \fi + \ifx\@foo\@reservedc % macro name... what to do? + \ifID@star + \ifID@starstar + \let\next\ID@expandmacro + \else + \let\next\ID@expandsuper + \fi + \fi + \fi + \ifx\@foo\Super + \let\next\ID@expandsuper + \fi + \ifx\@foo\Expand + \let\next\ID@expandnext + \fi + \ifx\@foo\UnsafeExpand + \let\next\ID@expandunsafe + \fi + \ifx\@foo\MultiExpand + \let\next\ID@expandmulti + \fi + \ifx\@foo\NoExpand + \let\next\ID@noexpandnext + \fi + \ifx\@foo\Recurse + \def\next{\xa\xa\xa\ID@scan\xa\xa\xa\NoExpand\xa\@macroname\@gobble}% + \fi + \ifcat\noexpand\@foo\space + \let\next\ID@space + \fi + \ifcat\noexpand\@foo\bgroup + \let\next\ID@trygroup + \fi + \next +} +% \end{macrocode} +% \end{macros} +% \begin{macros}{\ID@space,\ID@unspace} +% It's a bit tricky to deal with spaces properly. In particular, +% picking up just a space from the token list takes some doing. We +% need a fully-expandable macro so that the whole thing disappears. +% |\ID@space| then adds a space to |\ID@toks| and then expands +% |\ID@unspace| after |\ID@scan| so that the |\futurelet| sees the +% next token after the space and can deal with it properly. +% We need the |\expandafter| in defining |\ID@unspace| to actually get the +% space token into the parameter text; otherwise, it gets +% gobbled up by the lexer after reading the control sequence name. +% \begin{macrocode} +\newcommand\ID@space{% + \ID@toks\xa\xa\xa{\xa\the\xa\ID@toks\space}% + \xa\ID@scan\ID@unspace +} +\newcommand\ID@unspace{} +\xa\def\xa\ID@unspace\space{} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@trygroup,\ID@recurse} +% The next two macros are used to check if the |\bgroup| token was an +% explicit or an implicit grouping character. If it's explicit then +% the next macro that takes an argument will scoop the whole thing up +% at once, and so we need to be aware of this to deal with it. +% |\ID@trygroup| uses the special |#| delimiter and compares the argument +% with |\@empty| to see if anything comes before the next |{}|. If it +% doesn't find anything then it was an explicit group and we recurse. +% One consequence of this is that we always need to put a |{}| after +% |\Q@END| so that we don't get an error here. +% \begin{macrocode} +\newcommand\ID@trygroup{} +\long\def\ID@trygroup#1#{% check for explicit/implicit grouping! + \def\@reservedd{#1}% + \xa\let\xa\next + \ifx\@reservedd\@empty\ID@recurse\else\ID@normal\fi + \next#1% +} +% \end{macrocode} +% Here we need to do some gymnastics to get the |{| and |}| tokens into +% the toks register. It would be easiest if we could just add them one +% at a time, but we can only add \emph{balanced text}, so we need to +% expand the whole thing \emph{first} and then add it back to the register +% we expanded it into. Thus, we enter a new level of grouping to save +% the contents of |\ID@toks|, expand the inner group, and use |\expandafter| +% across an |\endgroup| to get the correct tokens in the right place in +% |\ID@toks|. +% \begin{macrocode} +\newcommand\ID@recurse[1]{% + \begingroup\ID@toks{}% start a new level of grouping and empty \ID@toks + \ID@scan#1\Q@END{}% % parse... + \xa\endgroup\xa % this fiasco should get the job done...! + \ID@toks\xa\xa\xa{\xa\the\xa\ID@toks\xa{\the\ID@toks}}% + \ID@scan +} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@normal,ID@noexpandnext} +% This is what we do when it's not anything special. +% \begin{macrocode} +\newcommand\ID@normal[1]{\ID@toks\xa{\the\ID@toks#1}\ID@scan} +\newcommand\ID@noexpandnext[2]{\ID@toks\xa{\the\ID@toks#2}\ID@scan} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@checkusage,\ID@checkredef} +% Here we define tests that will issue errors if the parameter +% texts aren't the same, or the original function isn't defined. +% \begin{macrocode} +\newcommand*\ID@checkusage{% + % Make sure parameter lists are the same, does nothing if undefined + \ifx\@reserveda\@reservedb + \else + % Error messages + \ifx\@reserveda\undefined % undefined - okay + \else + \global\ID@abortedtrue + \ifx\@foo\Super + \PackageError{inlinedef}{Cannot use \protect\Super\space in \expandafter + \protect\@macroname\space because\MessageBreak + parameter lists don't match:\MessageBreak + `\@reservedb' (new) != `\@reserveda' (old)}\@eha + \else + \ifID@bang % auto-expansion forbidden + \PackageError{inlinedef}{Cannot use \protect\Inline* auto-expansion in + \expandafter\protect\@macroname\MessageBreak + because parameter lists don't match:\MessageBreak + `\@reservedb' (new) != `\@reserveda' (old)}\@eha + \else + \PackageError{inlinedef}{Parameter lists for + \expandafter\protect\@macroname\space don't match:\MessageBreak + `\@reservedb' (new) != `\@reserveda' (old)\MessageBreak + Use !-form of \protect\Inline\space to ignore this}\@eha + \fi + \fi + \fi + \fi +} +\newcommand*\ID@checkredef{% + \ifx\@reserveda\undefined % undefined - okay + \PackageError{inlinedef}{Cannot use \ifx\@foo\Super\protect\Super\space + \else\protect\Inline** \fi in \expandafter\protect\@macroname\space + because \MessageBreak it hasn't been defined yet}% + \@eha + \global\ID@abortedtrue + \fi +} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@expandsuper,\ID@expandnext,\ID@expandmulti,% +% \ID@expandunsafe,\ID@expandmacro} +% These correspond to the two special tokens, |\Super| and |\Expand|. +% The first one tests that the parameter list is alright and that the +% original command wasn't undefined. If all is well, it expands everything +% in the right order. The second one is simpler, just inserting an +% |\expandafter| before the continuation (|\ID@scan|) to expand whatever +% comes next once. There is (yet) no way to fully-expand, although several +% |\Expand|s and |\expandafter|s can be stacked cleverly to expand several +% things in a specific order. +% \begin{macrocode} +\newcommand*\ID@expandsuper[1]{% + \ID@checkusage\ID@checkredef + \ifID@aborted\else + \ID@toks\xa\xa\xa\xa\xa\xa\xa + {\xa\xa\xa\the\xa\xa\xa\ID@toks\xa\@reservedc\ID@fixedparams}% + \fi + \ID@scan +} +\newcommand\ID@expandnext[2]{% + \ID@toks\xa\xa\xa{\xa\the\xa\ID@toks#2}\ID@scan +} +\newcommand\ID@expandmulti[3]{% + \begingroup % #1 is the \MultiExpand... + \ID@count#2\relax % this will need to be allocated too! + \ID@toks{#3}% + \@testtrue\ifnum\ID@count<\@ne\@testfalse\fi + \@whilesw\if@test\fi{% + \ID@toks\xa\xa\xa{\the\ID@toks}% one expansion... + \advance\ID@count\m@ne\ifnum\ID@count<\@ne\@testfalse\fi + }% + \xa\endgroup\xa\ID@toks\xa\xa\xa{\xa\the\xa\ID@toks\the\ID@toks}\ID@scan +} + +\newcommand*\ID@expandunsafe[1]{\expandafter\ID@scan} +\newcommand*\ID@expandmacro[1]{\expandafter\ID@scan\@reservedc} +% \end{macrocode} +% \end{macros} +% +% \begin{macros}{\ID@getprefix} +% This is used to compare argument lists. +% \begin{macrocode} +\newcommand\ID@getprefix{}\long\def\ID@getprefix#1:#2->#3\Q@END{\detokenize{#2}} +% \end{macrocode} +% \end{macros} +% +% Finally we clean up by restoring |@|'s catcode. +% \begin{macrocode} +\makeatother +%</package> +% \end{macrocode} +% +% \StopIndexing +% \filbreak +% \section{Test suite} +% We include a somewhat-comprehensive test suite to make sure that everything +% is working. If it works properly, it should output nothing. +% +% First we define a few helper-functions to test for errors, etc. +% \begin{macrocode} +%<*testsuite> +\makeatletter +\errorcontextlines=10 +\def\WantError#1#2#3{% + \let\WE@packageerror\PackageError + \def\PackageError##1##2##3{% + \protected@edef\@goterror{##2}\protected@edef\@wanterror{#2}% + \edef\@goterror{\xa\detokenize\xa{\@goterror}}% + \edef\@wanterror{\xa\detokenize\xa{\@wanterror}}% + \protected@edef\@gotpackage{##1}\protected@edef\@wantpackage{#1}% + \edef\@gotpackage{\xa\detokenize\xa{\@gotpackage}}% + \edef\@wantpackage{\xa\detokenize\xa{\@wantpackage}}% + \global\let\PackageError\WE@packageerror + \@tempswafalse + \ifx\@gotpackage\@wantpackage\else\message{^^J(arg 1 differs)^^J}\@tempswatrue\fi + \ifx\@goterror\@wanterror\else\message{^^J(arg 2 differs)^^J}\@tempswatrue\fi + \ifx#3##3\else\message{^^J(arg 3 differs)^^J}\@tempswatrue\fi + \if@tempswa\PackageError{inlinedef (test)}{wrong error}\@eha\PackageError{##1}{##2}##3\fi + }% +} + +\def\CheckError{% + \ifx\PackageError\WE@packageerror\else + \PackageError{inlinedef (test)}{expected error not thrown}\@eha\fi + \global\let\PackageError\WE@packageerror +} +\newcommand\CheckDefinition[1][]{\@CheckDefinition{#1}} +\def\@CheckDefinition#1#2#3#{\@checkdefn{#1}#2{#3}} +\def\@checkdefn#1#2#3#4{#1\def\@reserveda#3{#4}\ifx#2\@reserveda\else + \message{^^J^^J\meaning#2^^J(got)vs(wanted)^^J\meaning\@reserveda^^J^^J} + \PackageError{inlinedef (test)}{definition of \detokenize{#2}didn't match}\@eha\fi +} +\let\eha\@eha\let\ehd\@ehd +\makeatother +% \end{macrocode} +% +% Here we predefine copies of the errors so that we can look for them easily +% \begin{macrocode} +\catcode`\#=12 +\def\pound{#} +\catcode`\#=6 + +\def\WantSuperNoMatch#1#2#3{% + \WantError{inlinedef}{Cannot use \protect\Super\space in + \protect#1\space because\MessageBreak + parameter lists don't match:\MessageBreak + `#3' (new) != `#2' (old)}\eha +} +\def\WantStarNoMatch#1#2#3{% + \WantError{inlinedef}{Cannot use \protect\Inline* auto-expansion in + \protect#1\MessageBreak because + parameter lists don't match:\MessageBreak + `#3' (new) != `#2' (old)}\eha +} +\def\WantNoMatchBang#1#2#3{% +\WantError{inlinedef}{Parameter lists for + \protect#1\space don't match:\MessageBreak + `#3' (new) != `#2' (old)\MessageBreak + Use !-form of \protect\Inline\space to ignore this}\eha +} +\def\WantOnlyDefGdef{% + \WantError{inlinedef}{Only \protect\def\space and \protect\gdef\space are + allowed after \protect\Inline,\MessageBreak but some other type of + definition was found}\eha +} +\def\WantNoDefGdef{% + \WantError{inlinedef}{No \protect\def\space or \protect\gdef\space found + after \protect\Inline}\ehd +} +\def\WantSuperNoRedef#1{% + \WantError{inlinedef}{Cannot use \protect\Super\space in \protect#1\space + because \MessageBreak it hasn't been defined yet}\eha +} +% \end{macrocode} +% +% Now we start the actual tests. +% \begin{macrocode} + % I. Basic stuff + % A. Simple definition +\let\a\undefined +\Inline\def\a{b} +\CheckDefinition\a{b} + + % B. Simple redefinition +\def\a{b} +\Inline\def\a{d} +\CheckDefinition\a{d} + + % C. Erroneous redefinition (needs !) +\def\a{b} +\WantNoMatchBang\a{}{\pound1} +\Inline\def\a#1{c} +\CheckError +\CheckDefinition\a{b} % shouldn't have changed + +\def\a{b} +\Inline!\def\a#1{c} +\CheckDefinition\a#1{c} + + % D. Local/global definition +\def\a{b} +\begingroup +\Inline\def\a{c} +\endgroup +\CheckDefinition\a{b} + +\begingroup +\Inline\gdef\a{c} +\endgroup +\CheckDefinition\a{c} + +{\Inline\global\def\a{d}} +\CheckDefinition\a{d} + + % E. Collecting arguments +\Inline\long\def\a{e} +\CheckDefinition[\long]\a{e} + +\Inline\outer\def\a{f} +\edef\a{\meaning\a} +\edef\b{\detokenize{\outer macro:->f}} +\xa\CheckDefinition\xa\a\xa{\b} + +\Inline\long\outer\def\a{g} +\edef\a{\meaning\a} +\edef\b{\string\long\string\outer\space\detokenize{macro:->g}} +\xa\CheckDefinition\xa\a\xa{\b} + +\def\a{g} +\Inline!\long\def\a#1{h} +\CheckDefinition[\long]\a#1{h} + + % II. Special tokens + % A. Recursion +\def\a{b} +\Inline\def\a{a\a c} +\CheckDefinition\a{a\a c} + + % B. Expansion +\def\a{b} +\Inline\def\a{a\Expand\a c} +\CheckDefinition\a{abc} + +\def\a{b} +\Inline\def\a{\Expand a\Expand\a\Expand c} +\CheckDefinition\a{abc} + +\def\a{\b} +\def\b{c} +\Inline\def\a{a\Expand\a c} +\CheckDefinition\a{a\b c} + +\toks0{b}\toks1{d} +\Inline\def\a{a\the\toks0c\the\toks1e} +\CheckDefinition\a{a\the\toks0c\the\toks1e} + +\Inline\def\a{a\Expand{\the\toks0}c\Expand{\the\toks1}e} +\CheckDefinition\a{abcde} + +\Inline\def\a{\Expand{a\the\toks0}c\Expand{\the\toks1}e} +\CheckDefinition\a{a\the\toks0cde} + +\Inline\def\a{\Expand{\expandafter a\the\toks0}c\Expand{\the\toks1e}} +\CheckDefinition\a{abcde} + + % C. MultiExpand +\def\x{\y} +\def\y{\z} +\def\z{0} +\Inline\def\a{a\MultiExpand0\x b} +\CheckDefinition\a{a\x b} + +\Inline\def\a{a\MultiExpand1\x b} +\CheckDefinition\a{a\y b} + +\Inline\def\a{a\MultiExpand2\x b} +\CheckDefinition\a{a\z b} + +\Inline\def\a{a\MultiExpand3\x b} +\CheckDefinition\a{a0b} + +\Inline\def\a{a\MultiExpand{10}\x b} +\CheckDefinition\a{a0b} + + % i. use with \expandafter +\Inline\def\a{a\MultiExpand2{\expandafter\expandafter\x\x}b} +\CheckDefinition\a{a\y\y b} + +\Inline\def\a{a\MultiExpand1{\expandafter\expandafter\x\x}b} +\CheckDefinition\a{a\expandafter\y\x b} + +\Inline\def\a{a\MultiExpand2{\expandafter\expandafter\expandafter\x\x}b} +\CheckDefinition\a{a\x\z b} + +\Inline\def\a{a\MultiExpand3{\expandafter\expandafter\expandafter\x\x}b} +\CheckDefinition\a{a\y\z b} + +\Inline\def\a{a\MultiExpand4{\expandafter\expandafter\expandafter\x\x}b} +\CheckDefinition\a{a\z\z b} + +\Inline\def\a{a\MultiExpand5{\expandafter\expandafter\expandafter\x\x}b} +\CheckDefinition\a{a0\z b} + + % D. UnsafeExpand +\def\x{b\Super c} +\Inline\def\a{a\Expand\x d} +\CheckDefinition\a{ab\Super cd} + +\def\a{0} +\Inline\def\a{a\UnsafeExpand\x d} +\CheckDefinition\a{ab0cd} + +\def\a{b\Super d} +\Inline\def\a{a\UnsafeExpand\a e} +\CheckDefinition\a{abb\Super dde} + +\def\a{b\Super d} +\Inline**\def\a{a\a e} +\CheckDefinition\a{abb\Super dde} + + % Would be nice if we could catch TeX capacity exceeded errors... + % Then try \def\a{b\a d}\Inline**\def\a{a\a e} + +\def\x#1{b#1d} +\Inline\def\a{a\x ce} +\CheckDefinition\a{a\x ce} + +\Inline\def\a{a\UnsafeExpand\x ce} +\CheckDefinition\a{abcde} + + % E. NoExpand +\Inline\def\a{a\NoExpand\Expand\x b} +\CheckDefinition\a{a\Expand\x b} + +\Inline*\def\a{a\NoExpand\a b} +\CheckDefinition\a{a\a b} + +\Inline**\def\a{a\NoExpand\a b} +\CheckDefinition\a{a\a b} + +\Inline\def\a{a\NoExpand{\Expand\x\Expand\y}b} +\CheckDefinition\a{a\Expand\x\Expand\y b} + + % F. Super +\def\a{bcd} +\Inline\def\a{a\Super e} +\CheckDefinition\a{abcde} + +\def\a#1{b#1d} +\Inline\def\a#1{a\Super e} +\CheckDefinition\a#1{ab#1de} + +\def\a#1{b#1d} +\Inline*\def\a#1{a\Super e} +\CheckDefinition\a#1{ab#1de} + +\def\a#1{b#1d} +\Inline**\def\a#1{a\Super e} +\CheckDefinition\a#1{ab#1de} + + % G. Recurse +\def\a{q} +\Inline\def\a{a\Recurse b} +\CheckDefinition\a{a\a b} + +\Inline*\def\a{a\Recurse b} +\CheckDefinition\a{a\a b} + +\Inline**\def\a{a\Recurse b} +\CheckDefinition\a{a\a b} + + % III. Tricky parsing + % A. Spaces +\def\a{b c d} +\Inline\def\a{a \Super e} +\CheckDefinition\a{a b c de} + +\def\a{b c d} +\Inline\def\a{a \Expand\a e} +\CheckDefinition\a{a b c de} + +\def\a{b c d} +\Inline\def\a{a \Expand{\a} e} +\CheckDefinition\a{a b c d e} + +\Inline\def\a{a\NoExpand{\Expand\x\Expand\y} b} +\xa\CheckDefinition\xa\a\xa{\xa a\xa\Expand\xa\x\xa\Expand\xa\y\space b} + + % B. Grouping +\def\a{b{c d}e} +\Inline\def\a{{a\Super}f\Super} +\CheckDefinition\a{{ab{c d}e}fb{c d}e} + +\Inline\def\a{{ }{}} +\Inline\def\a{{\Expand\a\a}{} {{\Super}{}}} +\CheckDefinition\a{{{ }{}\a}{} {{{ }{}}{}}} + + % C. Parameters +\def\a#1bcd#2{[#1...#2]} +\Inline\def\a#1bcd#2{a\Super b} +\CheckDefinition\a#1bcd#2{a[#1...#2]b} + +\def\a#1\##2{y} +\Inline\def\a#1\##2{x\UnsafeExpand\a{#1}\#{#2}z} +\CheckDefinition\a#1\##2{xyz} + +\def\a#1\##2{#1y#2} +\Inline\def\a#1\##2{x\UnsafeExpand\a{#1}\#{#2}z} +\CheckDefinition\a#1\##2{x#1y#2z} + + % i. spaces! +\def\a #1 {y} +\Inline\def\a#1 {x\Super z} +\CheckDefinition\a#1 {xyz} + +\xa\def\xa\a\space{y} +\xa\Inline\xa\def\xa\a\space{x\Super z} +\xa\CheckDefinition\xa\a\space{xyz} + + % ii. funky catcodes + %%%% This test fails. + %\begingroup + % \catcode`&=6 + % \def\a&1{b#1d} + % \Inline\def\a#1{a\Super e} + % \CheckDefinition\a#1{b&1d} + %\endgroup + + % D. Active characters +\begingroup + \catcode`A=13 + \defA#1{b#1d} + \Inline\defA#1{aAe} + \CheckDefinitionA#1{aAe} + + \defA#1{b#1d} + \Inline*\defA#1{aAe} + \CheckDefinitionA#1{ab#1de} +\endgroup + + % IV. Auto-expansion + +\def\a#1{y} +\Inline\def\a#1{x\a z} +\CheckDefinition\a#1{x\a z} + +\def\a#1{y} +\Inline*\def\a#1{x\a z} +\CheckDefinition\a#1{xyz} + +\def\a#1{y} +\Inline**\def\a#1{x\a{#1}z} +\CheckDefinition\a#1{xyz} + + % A. With delimited arguments +\def\a[#1]#2{#1y#2} +\Inline*\def\a[#1]#2{x\a z} +\CheckDefinition\a[#1]#2{x#1y#2z} + +\def\a[#1]#2{#1y#2} +\Inline**\def\a[#1]#2{x\a[#1]{#2}z} +\CheckDefinition\a[#1]#2{x#1y#2z} + + % V. Errors + +\def\bar#1{d #1 f} +\def\x{b} + +\WantSuperNoMatch\a{\pound1}{.\pound1} +\def\a#1{x} +\Inline!\def\a.#1{y\Super} +\CheckError +\CheckDefinition\a#1{x} + +\WantStarNoMatch\a{\pound1}{.\pound1} +\def\a#1{x} +\Inline*!\def\a.#1{y\a} +\CheckError +\CheckDefinition\a#1{x} + +\def\a#1{x} +\Inline!\def\a.#1{y} % ok +\CheckDefinition\a.#1{y} + +\WantOnlyDefGdef +\def\foo{a} +\Inline\edef\foo{b} +\CheckError +\CheckDefinition\foo{a} +\let\foo\undefined + +\WantOnlyDefGdef +\Inline\global\outer\xdef{} +\CheckError + +\WantOnlyDefGdef +\Inline\global\outer\abc\space vbcda s \newcommand{} +\CheckError + +\WantNoDefGdef +\Inline\let\relax\relax +\CheckError + +\WantNoDefGdef +\Inline{} +\CheckError + +\WantSuperNoRedef\foo +\Inline\def\foo#1{a \Expand\x\space cd #1 fg \x\space i\Super} +\CheckError + +\WantNoMatchBang\a{}{\pound1} +\def\a{b} +\Inline\def\a#1{a\a c} +\CheckError + + % Miscellaneous (read: "old") tests + +\def\test#1{d #1 f} +\Inline\def\test#1{a \Expand\x\space c\Super g \x\space i} +\CheckDefinition\test#1{a b\space cd #1 fg \x\space i} + +\Inline*\def\bar#1{a \Expand\x\space c\bar g \x\space i} +\CheckDefinition\bar#1{a b\space cd #1 fg \x\space i} + +\def\bar#1{d #1 f} +\Inline**\def\bar#1{a \Expand\x\space c\bar{#1}g \x\space i} +\CheckDefinition\bar#1{a b\space cd #1 fg \x\space i} + +\Inline\def\foo#1{a \Expand\x\space cd #1 fg \x\space i} +\CheckDefinition\foo#1{a b\space cd #1 fg \x\space i} + +\def\a{b} +\Inline!\def\a#1{a\Expand\a c} +\CheckDefinition\a#1{abc} + +\def\a{b} +\Inline!**\def\a#1{a\a c} +\CheckDefinition\a#1{abc} + +\Inline\def\a#1{a\a c} +\CheckDefinition\a#1{a\a c} + +\Inline\def\a#1{a\Recurse c} +\CheckDefinition\a#1{a\a c} + +\Inline!\def\a{a\NoExpand{b\Super c}d} +\CheckDefinition\a{ab\Super cd} + +\Inline*\def\a{gh\a jk} +\CheckDefinition\a{ghab\Super cdjk} + + % SURPRISE! unsafe expansion... +\def\a{ab\Super cd} +\Inline**\def\a{gh\a jk} +\CheckDefinition\a{ghabab\Super cdcdjk} + +\def\a{ab\Super cd} +\Inline\def\a{gh\UnsafeExpand\a jk} +\CheckDefinition\a{ghabab\Super cdcdjk} + +\def\x{\x a} % This is a fun one...! +\Inline\def\a{\MultiExpand{5}\x} +\CheckDefinition\a{\x aaaaa} + +\message{^^JAll tests completed.^^J} + +\begin{document} +\end{document} +%</testsuite> +% \end{macrocode} +% +% \makeatother +% \eject +% \Finale +% +% +% \iffalse +% +% The next line of code prevents DocStrip from adding the +% character table to the generated files(s). + +% Removed stuff + +\endinput +% +% \fi +% +%% \CharacterTable +%% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +%% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +%% Digits \0\1\2\3\4\5\6\7\8\9 +%% Exclamation \! Double quote \" Hash (number) \# +%% Dollar \$ Percent \% Ampersand \& +%% Acute accent \' Left paren \( Right paren \) +%% Asterisk \* Plus \+ Comma \, +%% Minus \- Point \. Solidus \/ +%% Colon \: Semicolon \; Less than \< +%% Equals \= Greater than \> Question mark \? +%% Commercial at \@ Left bracket \[ Backslash \\ +%% Right bracket \] Circumflex \^ Underscore \_ +%% Grave accent \` Left brace \{ Vertical bar \| +%% Right brace \} Tilde \~} +%% |