% \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} % % %\NeedsTeXFormat{LaTeX2e} %\ProvidesPackage{inlinedef}% %\documentclass{minimal} %\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} % % \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 % % \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} % % \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 \~} %%