% \iffalse % collargs.edtx (this is not a .dtx file; to produce a .dtx, process it with edtx2dtx) %% %% This file is a part of CollArgs, a TeX package providing a command which can %% determine the argument scope of any command whose argument structure %% conforms to xparse's argument specification, available at %% https://ctan.org/pkg/advice and https://github.com/sasozivanovic/advice. %% %% Copyright (c) 2023- Saso Zivanovic %% (Sa\v{s}o \v{Z}ivanovi\'{c}) %% %% This work may be distributed and/or modified under the conditions of the %% LaTeX Project Public License, either version 1.3c of this license or (at %% your option) any later version. The latest version of this license is in %% https://www.latex-project.org/lppl.txt and version 1.3c or later is part of %% all distributions of LaTeX version 2008 or later. %% %% This work has the LPPL maintenance status `maintained'. %% The Current Maintainer of this work is Saso Zivanovic. %% %% The files belonging to this work and covered by LPPL are listed in %% (/doc/generic/collargs/)FILES. % % \fi % % \begin{macrocode} % % Package CollArgs provides commands |\CollectArguments| and % |\CollectArgumentsRaw|, which (what a surprise!) collect the arguments % conforming to the given (slightly extended) |xparse| argument specification. % The package was developed to help out with automemoization (see % section~\ref{sec:code:automemoization}). It started out as a few lines of % code, but had grown once I realized I want automemoization to work for % verbatim environments as well --- the environment-collecting code is based on % Bruno Le Floch's package |cprotect| --- and had then grown some more once I % decided to support the |xparse| argument specification in full detail, and to % make the verbatim mode flexible enough to deal with a variety of situations. % % The implementation of this package does not depend on |xparse|. Perhaps this % is a mistake, especially as the |xparse| code is now included in the base % \hologo{LaTeX}, but the idea was to have a light-weight package (not sure % this is the case anymore, given all the bells and whistles), to have its % functionality available in plain \hologo{TeX} and \hologo{ConTeXt} as well % (same as Memoize), and, perhaps most importantly, to have the ability to % collect the arguments verbatim. % % % \paragraph{Identification} %\ProvidesPackage{collargs}[2024/03/15 v1.2.0 Collect arguments of any command] %%D \module[ %%D file=t-collargs.tex, %%D version=1.2.0, %%D title=CollArgs, %%D subtitle=Collect arguments of any command, %%D author=Saso Zivanovic, %%D date=2024-03-15, %%D copyright=Saso Zivanovic, %%D license=LPPL, %%D ] %\writestatus{loading}{ConTeXt User Module / collargs} %\unprotect %\startmodule[collargs] % \paragraph{Required packages} %\RequirePackage{pgfkeys} %\input pgfkeys %\input t-pgfkey %\RequirePackage{etoolbox} %\input etoolbox-generic %\edef\resetatcatcode{\catcode`\noexpand\@\the\catcode`\@\relax} %\catcode`\@11\relax % % \begin{macro}{\toksapp,\gtoksapp,\etoksapp,\xtoksapp} % Macros for appending to a token register. We don't have to define them in % \hologo{LuaTeX}, where they exist as primitives. Same as these primitives, % out macros accept either a register number or a |\toksdef|fed control % sequence as the (unbraced) |#1|; |#2| is the text to append. \ifdefined\luatexversion \else \def\toksapp{\toks@cs@or@num\@toksapp} \def\gtoksapp{\toks@cs@or@num\@gtoksapp} \def\etoksapp{\toks@cs@or@num\@etoksapp} \def\xtoksapp{\toks@cs@or@num\@xtoksapp} \def\toks@cs@or@num#1#2#{% % Test whether |#2| (the original |#1|) is a number or a control sequence. \ifnum-2>-1#2 % It is a number. |\toks@cs@or@num@num| will gobble |\toks@cs@or@num@cs| % below. \expandafter\toks@cs@or@num@num % The register control sequence in |#2| is skipped over in the false % branch. \fi \toks@cs@or@num@cs{#1}{#2}% } % |#1| is one of |\@toksapp| and friends. The second macro prefixes the % register number by |\toks|. \def\toks@cs@or@num@cs#1#2{#1{#2}} \def\toks@cs@or@num@num\toks@cs@or@num@cs#1#2{#1{\toks#2 }} % Having either |\tokscs| or |\toks| in |#1|, we can finally do the % real job. \long\def\@toksapp#1#2{#1\expandafter{\the#1#2}}% \long\def\@etoksapp#1#2{#1\expandafter{\expanded{\the#1#2}}}% \long\def\@gtoksapp#1#2{\global#1\expandafter{\the#1#2}}% \long\def\@xtoksapp#1#2{\global#1\expandafter{\expanded{\the#1#2}}}% \fi % \end{macro} % % \begin{macro}{\CollectArguments,\CollectArgumentsRaw} % |\CollectArguments| takes three arguments: the optional |#1| is the option % list, processed by |pgfkeys| (given the grouping structure, these options % will apply to all arguments); the mandatory |#2| is the |xparse|-style % argument specification; the mandatory |#3| is the ``next'' command (or a % sequence of commands). The argument list is expected to start immediately % after the final argument; |\CollectArguments| parses it, effectively % figuring out its extent, and then passes the entire argument list to the % ``next'' command (as a single argument). % % |\CollectArgumentsRaw| differs only in how it takes and processes the % options. For one, these should be given as a mandatory argument. % Furthermore, they do not take the form of a keylist, but should deploy the % ``programmer's interface.'' |#1| should thus be a sequence of invocations % of the macro counterparts of the keys defined in % section~\ref{sec:code:collargs:keys}, which can be recognized as starting % with |\collargs| followed by a capital letter, e.g.\ |\collargsCaller|. % Note that |\collargsSet| may also be used in |#1|. (The ``optional,'' % i.e.\ bracketed, argument of |\CollectArgumentsRaw| is in fact mandatory.) \protected\def\CollectArguments{% \pgf@keys@utilifnextchar[\CollectArguments@i{\CollectArgumentsRaw{}}%] } \def\CollectArguments@i[#1]{\CollectArgumentsRaw{\collargsSet{#1}}} \protected\def\CollectArgumentsRaw#1#2#3{% % This group will be closed by |\collargs@.| once we grinded through the % argument specification. \begingroup % Initialize category code fixing; see section~\ref{sec:code:collargs:fix} % for details. We have to do this before applying the settings, so that % |\collargsFixFromNoVerbatim| et al can take effect. \global\let\ifcollargs@last@verbatim\ifcollargs@verbatim \global\let\ifcollargs@last@verbatimbraces\ifcollargs@verbatimbraces \global\collargs@double@fixfalse % Apply the settings. \collargs@verbatim@wrap{#1}% % Initialize the space-grabber. \collargs@init@grabspaces % Remember the code to execute after collection. \def\collargs@next{#3}% % Initialize the token register holding the collected arguments. \ifcollargsClearArgs \global\collargsArgs{}% \fi % Execute the central loop macro, which expects the argument specification % |#2| to be delimited from the following argument tokens by a dot. \collargs@#2.% } % \end{macro} % % \begin{macro}{\collargsSet} % This macro processes the given keys in the |/collargs| keypath. When it is % used to process options given by the end user (the optional argument to % |\CollectArguments|, and the options given within the argument % specification, using the new modifier |&|), its invocation should be % wrapped in |\collargs@verbatim@wrap| to correctly deal with the changes of % the verbatim mode. \def\collargsSet#1{\pgfqkeys{/collargs}{#1}} % \end{macro} % % \subsubsection{The keys} % \label{sec:code:collargs:keys} % % \begin{macro}{\collargs@cs@cases} % If the first argument of this auxiliary macro is a single control sequence, % then the second argument is executed. If the first argument starts with a % control sequence but this control sequence does not form the entire % argument, the third argument is executed. Otherwise, the fourth argument % is executed. % % This macro is defined in package CollArgs because we use it in key |caller| % below, but it is really useful in package Auto, where having it we don't % have to bother the end-user with a separate keys for commands and % environments, but automatically detect whether the argument of |auto| and % (|de|)|activate| is a command or an environment. \def\collargs@cs@cases#1{\collargs@cs@cases@i#1\collargs@cs@cases@end} \let\collargs@cs@cases@end\relax \def\collargs@cs@cases@i{\futurelet\collargs@temp\collargs@cs@cases@ii} \def\collargs@cs@cases@ii#1#2\collargs@cs@cases@end{% \ifcat\noexpand\collargs@temp\relax \ifx\relax#2\relax \expandafter\expandafter\expandafter\@firstofthree \else \expandafter\expandafter\expandafter\@secondofthree \fi \else \expandafter\@thirdofthree \fi } \def\@firstofthree#1#2#3{#1} \def\@secondofthree#1#2#3{#2} \def\@thirdofthree#1#2#3{#3} % \end{macro} % % \begin{collargskey}{caller} % \begin{macro}{\collargsCaller} % Every macro which grabs a part of the argument list will be accessed % through the ``caller'' control sequence, so that \hologo{TeX}'s reports % of any errors in the argument structure can contain a command name % familiar to the author.\footnote{The idea is borrowed from package % |environ|, which is in turn based on code from |amsmath|.} For example, % if the argument list ``originally'' belonged to command |\foo| with % argument structure |r()|, but no parentheses follow in the input, we want % \hologo{TeX} to complain that |Use of \foo doesn't match its definition|. % This can be achieved by setting |caller=\foo|; the default is % |caller=\CollectArguments|, which is still better than seeing an error % involving some random internal control sequence. It is also ok to set an % environment name as the caller, see below. % % The key and macro defined below store the caller control sequence into % |\collargs@caller|, e.g.\ when we say |caller=\foo|, we effectively % execute |\def\collargs@caller{\foo}|. \collargsSet{ caller/.code={\collargsCaller{#1}}, } \def\collargsCaller#1{% \collargs@cs@cases{#1}{% \let\collargs@temp\collargs@caller@cs }{% \let\collargs@temp\collargs@caller@csandmore }{% \let\collargs@temp\collargs@caller@env }% \collargs@temp{#1}% } \def\collargs@caller@cs#1{% % If |#1| is a single control sequence, just use that as the caller. \def\collargs@caller{#1}% } \def\collargs@caller@csandmore#1{% % If |#1| starts with a control sequence, we don't complain, but convert the % entire |#1| into a control sequence. \begingroup \escapechar -1 \expandafter\endgroup \expandafter\def\expandafter\collargs@caller\expandafter{% \csname\string#1\endcsname }% } \def\collargs@caller@env#1{% % If |#1| does not start with a control sequence, we assume that is an % environment name, so we prepend |start| in \hologo{ConTeXt}, and dress it % up into |\begin{#1}| in \hologo{LaTeX}. \expandafter\def\expandafter\collargs@caller\expandafter{% \csname %start% %begin{% #1% %}% \endcsname }% } \collargsCaller\CollectArguments % \end{macro} % \end{collargskey} % % \begin{macro}{\ifcollargs@verbatim,\ifcollargs@verbatimbraces} % The first of these conditional % signals that we're collecting the arguments in one of the verbatim % modes; the second one signals the |verb| mode in particular. \newif\ifcollargs@verbatim \newif\ifcollargs@verbatimbraces % \end{macro} % % \begin{collargskey}{verbatim, verb, no verbatim} % \begin{macro}{\collargs@verbatim@wrap} % These keys set the verbatim mode macro which will be executed by % |\collargsSet| after processing all keys. % The verbatim mode macros |\collargsVerbatim|, |\collargsVerb| and % |\collargsNoVerbatim| are somewhat complex; we postpone their definition % until section~\ref{sec:code:collargs:verbatim}. Their main effect is to % set conditionals |\ifcollargs@verbatim| and |\ifcollargs@verbatimbraces|, % which are be inspected by the argument type handlers --- and to make % the requested category code changes, of course. % % Here, note that the verbatim-selection code is not executed while the % keylist is being processed. Rather, the verbatim keys simply set the macro % which will be executed \emph{after} the keylist is processed, and this is % why processing of a keylist given by the user must be always wrapped in % |\collargs@verbatim@wrap|. \collargsSet{ verbatim/.code={\let\collargs@apply@verbatim\collargsVerbatim}, verb/.code={\let\collargs@apply@verbatim\collargsVerb}, no verbatim/.code={\let\collargs@apply@verbatim\collargsNoVerbatim}, } \def\collargs@verbatim@wrap#1{% \let\collargs@apply@verbatim\relax #1% \collargs@apply@verbatim } % \end{macro} % \end{collargskey} % % \begin{collargskey}{fix from verbatim, fix from verb, fix from no verbatim} % \begin{macro}{\collargsFixFromVerbatim,\collargsFixFromVerb,\collargsFixFromNoVerbatim} % These keys and macros should be used to request a category code fix, when % the offending tokenization took place prior to invoking % |\CollectArguments|; see section~\ref{sec:code:collargs:fix} for details. % While I assume that only |\collargsFixFromNoVerbatim| will ever be used % (and it is used by |\mmz|), we provide macros for all three transitions, % for completeness.\indentmacrocode \collargsSet{ fix from verbatim/.code={\collargsFixFromVerbatim}, fix from verb/.code={\collargsFixFromVerb}, fix from no verbatim/.code={\collargsFixFromNoVerbatim}, } % \noindentmacrocode \def\collargsFixFromNoVerbatim{% \global\collargs@fix@requestedtrue \global\let\ifcollargs@last@verbatim\iffalse } \def\collargsFixFromVerbatim{% \global\collargs@fix@requestedtrue \global\let\ifcollargs@last@verbatim\iftrue \global\let\ifcollargs@last@verbatimbraces\iftrue } \def\collargsFixFromVerb{% \global\collargs@fix@requestedtrue \global\let\ifcollargs@last@verbatim\iftrue \global\let\ifcollargs@last@verbatimbraces\iffalse } % \end{macro} % \end{collargskey} % % \begin{collargskey}{braces} % Set the characters which are used as the grouping characters in the full % verbatim mode. The user is only required to do this when multiple % character pairs serve as the grouping characters. The underlying macro, % |\collargsBraces|, will be defined in % section~\ref{sec:code:collargs:verbatim}. \collargsSet{ braces/.code={\collargsBraces{#1}}% } % \end{collargskey} % % \begin{collargskey}{environment} % \begin{macro}{\collargsEnvironment} % Set the environment name. % \indentmacrocode \collargsSet{ environment/.estore in=\collargs@b@envname } \def\collargsEnvironment#1{\edef\collargs@b@envname{#1}} \collargsEnvironment{} % \end{macro} % \end{collargskey} % % \begin{collargskey}{begin tag, end tag, tags} % \begin{macro}{\ifcollargsBeginTag,\ifcollargsEndTag,\ifcollargsAddTags} % When |begin tag|\slash|end tag| is in effect, the begin\slash end-tag will % be will be prepended/appended to the environment body. |tags| is a % shortcut for setting |begin tag| and |end tag| simultaneously. % \indentmacrocode \collargsSet{ begin tag/.is if=collargsBeginTag, end tag/.is if=collargsEndTag, tags/.style={begin tag=#1, end tag=#1}, tags/.default=true, } \newif\ifcollargsBeginTag \newif\ifcollargsEndTag % \end{macro} % \end{collargskey} % % \begin{collargskey}{ignore nesting} % \begin{macro}{\ifcollargsIgnoreNesting} % When this key is in effect, we will % ignore any \cs{begin}\marg{name}s and simply grab everything up to % the first \cs{end}\marg{name} (again, the markers are automatically % adapted to the format). \collargsSet{ ignore nesting/.is if=collargsIgnoreNesting, } \newif\ifcollargsIgnoreNesting % \end{macro} % \end{collargskey} % % \begin{collargskey}{ignore other tags} % \begin{macro}{\ifcollargsIgnoreOtherTags} % This key is only relevant in the % non-verbatim and partial verbatim modes in \hologo{LaTeX}. When it % is in effect, CollArgs checks the environment name following each % |\begin| and |\end|, ignoring the tags with an environment name other % than |\collargs@b@envname|. \collargsSet{ ignore other tags/.is if=collargsIgnoreOtherTags, } \newif\ifcollargsIgnoreOtherTags % \end{macro} % \end{collargskey} % % \begin{collargskey}[noindex]{(append/prepend) (pre/post)processor} % \begin{macro}[noindex]{\collargs(Append/Prepend)(Pre/Post)processor} % \begin{collargskey}[noprint]{append preprocessor, prepend preprocessor, % append postprocessor, prepend postprocessor} % \begin{macro}[noprint]{\collargsAppendPreprocessor,\collargsPrependPreprocessor,\collargsAppendPostprocessor,\collargsPrependPostprocessor} % These keys and % macros populate the list of preprocessors, % |\collargs@preprocess@arg|, and the list of post-processors, % |\collargs@postprocess@arg|, executed in |\collargs@appendarg|. \collargsSet{ append preprocessor/.code={\collargsAppendPreprocessor{#1}}, prepend preprocessor/.code={\collargsPrependPreprocessor{#1}}, append postprocessor/.code={\collargsAppendPostprocessor{#1}}, prepend postprocessor/.code={\collargsPrependPostprocessor{#1}}, } \def\collargsAppendPreprocessor#1{\appto\collargs@preprocess@arg{#1}} \def\collargsPrependPreprocessor#1{\preto\collargs@preprocess@arg{#1}} \def\collargsAppendPostprocessor#1{\appto\collargs@postprocess@arg{#1}} \def\collargsPrependPostprocessor#1{\preto\collargs@postprocess@arg{#1}} % \end{macro} % \end{collargskey} % \end{macro} % \end{collargskey} % % \begin{collargskey}[noindex]{clear (pre/post)processors} % \begin{macro}[noindex]{\collargsClear(Pre/Post)processors} % \begin{collargskey}[noprint]{clear preprocessors, clear postprocessors} % \begin{macro}[noprint]{\collargsClearPreprocessors,\collargsClearPostprocessors} % These keys and macros % clear the pre- and post-processor lists, which are initially empty as % well. \def\collargs@preprocess@arg{} \def\collargs@postprocess@arg{} \collargsSet{ clear preprocessors/.code={\collargsClearPreprocessors}, clear postprocessors/.code={\collargsClearPostprocessors}, } \def\collargsClearPreprocessors{\def\collargs@preprocess@arg{}}% \def\collargsClearPostprocessors{\def\collargs@postprocess@arg{}}% % \end{macro} % \end{collargskey} % \end{macro} % \end{collargskey} % % \begin{collargskey}[noindex]{(append/prepend) expandable (pre/post)processor} % \begin{macro}[noindex]{\collargs(Append/Prepend)Expandable(Pre/Post)processor} % \begin{collargskey}[noprint]{append expandable preprocessor, prepend expandable preprocessor, append expandable postprocessor, prepend expandable postprocessor} % \begin{macro}[noprint]{\collargsAppendExpandablePreprocessor,\collargsPrependExpandablePreprocessor,\collargsAppendExpandablePostprocessor,\collargsPrependExpandablePostprocessor} % These keys and macros simplify the definition of expandable % processors. Note that expandable processors are added to the same % list as non-expandable processors. \collargsSet{ append expandable preprocessor/.code={\collargsAppendExpandablePreprocessor{#1}}, prepend expandable preprocessor/.code={\collargsPrependExpandablePreprocessor{#1}}, append expandable postprocessor/.code={\collargsAppendExpandablePostprocessor{#1}}, prepend expandable postprocessor/.code={\collargsPrependExpandablePostprocessor{#1}}, } \def\collargsAppendExpandablePreprocessor#1{% \appto\collargs@preprocess@arg{% \collargsArg\expandafter{\expanded{#1}}% }% } \def\collargsPrependExpandablePreprocessor#1{% \preto\collargs@preprocess@arg{% \collargsArg\expandafter{\expanded{#1}}% }% } \def\collargsAppendExpandablePostprocessor#1{% \appto\collargs@postprocess@arg{% \collargsArg\expandafter{\expanded{#1}}% }% } \def\collargsPrependExpandablePostprocessor#1{% \preto\collargs@postprocess@arg{% \collargsArg\expandafter{\expanded{#1}}% }% } % \end{macro} % \end{collargskey} % \end{macro} % \end{collargskey} % % % \begin{collargskey}{no delimiters} % \begin{macro}{\ifcollargsNoDelimiters} % When this conditional is in effect, the % delimiter wrappers set by |\collargs@wrap| are ignored by % |\collargs@appendarg|. \collargsSet{% no delimiters/.is if=collargsNoDelimiters, } \newif\ifcollargsNoDelimiters % \end{macro} % \end{collargskey} % % \begin{collargskey}{clear args} % \begin{macro}{\ifcollargsClearArgs} % When this conditional is set to |false|, the global token register % |\collargsArgs| receiving the collected arguments is not cleared prior % to argument collection. \collargsSet{% clear args/.is if=collargsClearArgs, } \newif\ifcollargsClearArgs \collargsClearArgstrue % \end{macro} % \end{collargskey} % % \begin{collargskey}{return} % \begin{macro}{\collargsReturn} % Exiting |\CollectArguments|, should the next-command be followed by the % braced collected arguments, collected arguments as they are, or nothing? \collargsSet{% return/.is choice, return/braced/.code=\collargsReturnBraced, return/plain/.code=\collargsReturnPlain, return/no/.code=\collargsReturnNo, } \def\collargsReturnBraced{\def\collargsReturn{0}} \def\collargsReturnPlain{\def\collargsReturn{1}} \def\collargsReturnNo{\def\collargsReturn{2}} \collargsReturnBraced % \end{macro} % \end{collargskey} % % \begin{collargskey}{alias} % \begin{macro}{\collargsAlias} % \collargsSet{% alias/.code 2 args=\collargsAlias{#1}{#2}% } \def\collargsAlias#1#2{% \csdef{collargs@#1}{\collargs@@@#2}% } % \end{macro} % \end{collargskey} % % % % \subsubsection{The central loop} % \label{sec:code:collargs:central-loop} % % The central loop is where we grab the next \meta{token} from the argument % specification and execute the corresponding argument type or modifier % handler, |\collargs@|\meta{token}. The central loop consumes the argument % type \meta{token}; the handler will see the remainder of the argument % specification (which starts with the arguments to the argument type, if any, % e.g.\ by |()| of |d()|), followed by a dot, and then the tokens list from % which the arguments are to be collected. It is the responsibility of handler % to preserve the rest of the argument specification and reexecute the central % loop once it is finished. % % \begin{macro}{\collargs@} % Each argument is processed in a group to allow for local settings. This % group is closed by |\collargs@appendarg|. \def\collargs@{% \begingroup \collargs@@@ } % \end{macro} % % \begin{macro}{\collargs@@@} % This macro is where modifier handlers reenter the central loop --- we don't % want modifers to open a group, because their settings should remain in % effect until the next argument. Furthermore, modifiers do not trigger % category code fixes. \def\collargs@@@#1{% \collargs@in@{#1}{&+!>.}% \ifcollargs@in@ \expandafter\collargs@@@iii \else \expandafter\collargs@@@i \fi #1% } \def\collargs@@@i#1.{% % Fix the category code of the next argument token, if necessary, and then % proceed with the main loop. \collargs@fix{\collargs@@@ii#1.}% } % Reset the fix request and set the last verbatim conditionals to the current % state. \def\collargs@@@ii{% \global\collargs@fix@requestedfalse \global\let\ifcollargs@last@verbatim\ifcollargs@verbatim \global\let\ifcollargs@last@verbatimbraces\ifcollargs@verbatimbraces \collargs@@@iii } % Call the modifier or argument type handler denoted by the first token of the % remainder of the argument specification. \def\collargs@@@iii#1{% \ifcsname collargs@#1\endcsname \csname collargs@#1\expandafter\endcsname \else % We throw an error if the token refers to no argument type or modifier. \collargs@error@badtype{#1}% \fi } % Throwing an error stops the processing of the argument specification, and % closes the group opened in |\collargs@i|. \def\collargs@error@badtype#1#2.{% \PackageError{collargs}{Unknown xparse argument type or modifier "#1" for "\expandafter\string\collargs@caller\space"}{}% \endgroup } % \end{macro} % % {\catcode`\&=11 % \begin{macro}{\collargs@&} % We extend the |xparse| syntax with modifier |&|, which applies the given % options to the following (and only the following) argument. If |&| is % followed by another |&|, the options are expected to occur in the raw % format, like the options given to |\CollectArgumentsRaw|. Otherwise, the % options should take the form of a keylist, which will be processed by % |\collargsSet|. In any case, the options should be given within the % argument specification, immediately following the (single or double) |&|. \csdef{collargs@&}{% \futurelet\collargs@temp\collargs@amp@i } \def\collargs@amp@i{% % In \hologo{ConTeXt}, |&| has character code ``other'' in the text. %\ifx\collargs@temp&% %\expandafter\ifx\detokenize{&}\collargs@temp \expandafter\collargs@amp@raw \else \expandafter\collargs@amp@set \fi } \def\collargs@amp@raw#1#2{% \collargs@verbatim@wrap{#2}% \collargs@@@ } \def\collargs@amp@set#1{% \collargs@verbatim@wrap{\collargsSet{#1}}% \collargs@@@ } % \end{macro}} % % \begin{macro}[noindex]{\collargs@+} % \MyIndex{collargs@+}{\texttt{\textbackslash collargs@+}}{main} % This modifier makes the next argument long, i.e.\ accept paragraph tokens. \csdef{collargs@+}{% \collargs@longtrue \collargs@@@ } \newif\ifcollargs@long % \end{macro} % % \begin{macro}{\collargs@>} % We can simply ignore the processor modifier. (This, |xparse|'s processor, % should not be confused with CollArgs's processors, which are set using % keys |append preprocessor| etc.) \csdef{collargs@>}#1{\collargs@@@} % \end{macro} % % \begin{macro}[noindex]{\collargs@!} % \MyIndex{collargs@!}{\texttt{\textbackslash collargs@\exclamationmark}}{main} % Should we accept spaces before an optional argument following a mandatory % argument (\pkg{xparse} manual, \S1.1)? By default, yes. This modifier is % only applicable to types |d| and |t|, and derived types, but, unlike % |xparse|, we don't bother to enforce this; when used with other types, |!| % simply has no effect. \csdef{collargs@!}{% \collargs@grabspacesfalse \collargs@@@ } % \end{macro} % % \begin{macro}{\collargsArgs} % This token register is where we store the collected argument tokens. All % assignments to this register are global, because it needs to survive the % groups opened for individual arguments. \newtoks\collargsArgs % \end{macro} % % \begin{macro}{\collargsArg} % An auxiliary, but publicly available token register, used for processing % the argument, and by some argument type handlers. \newtoks\collargsArg % \end{macro} % % \begin{macro}{\collargs@.} % This fake argument type is used to signal the end of the argument list. % Note that this really counts as an extension of the |xparse| argument % specification. \csdef{collargs@.}{% % Close the group opened in |\collargs@|. \endgroup % Close the main |\CollectArguments| group, fix the category code of the next % token if necessary, and execute the next-code, followed by the collected % arguments in braces. Any over-grabbed spaces are reinserted into the input % stream, non-verbatim. \expanded{% \endgroup \noexpand\collargs@fix{% \expandonce\collargs@next \ifcase\collargsReturn\space {\the\collargsArgs}% \or \the\collargsArgs \fi \collargs@spaces }% }% } % \end{macro} % % \subsubsection{Auxiliary macros} % \label{sec:code:collargs:aux} % % \begin{macro}{\collargs@appendarg} % This macro is used by the argument type % handlers to append the collected argument to the storage % (|\collargsArgs|). \long\def\collargs@appendarg#1{% % Temporarily store the collected argument into a token register. The % processors will manipulate the contents of this register. \collargsArg={#1}% % This will clear the double-fix conditional, and potentially request a % normal, single fix. We can do this here because this macro is only called % when something is actually collected. For details, see % section~\ref{sec:code:collargs:fix}. \ifcollargs@double@fix \collargs@cancel@double@fix \fi % Process the argument with user-definable preprocessors, the wrapper defined % by the argument type, and user-definable postprocessors. \collargs@preprocess@arg \ifcollargsNoDelimiters \else \collargs@process@arg \fi \collargs@postprocess@arg % Append the processed argument, preceded by any grabbed spaces (in the % correct mode), to the storage. \xtoksapp\collargsArgs{\collargs@grabbed@spaces\the\collargsArg}% % Initialize the space-grabber. \collargs@init@grabspaces % Once the argument was appended to the list, we can close its group, opened % by |\collargs@|. \endgroup } % \end{macro} % % \begin{macro}{\collargs@wrap} % This macro is used by argument type handlers to declare their delimiter % wrap, like square brackets around the optional argument of type |o|. It % uses |\collargs@addwrap|, defined in section~\ref{sec:code:collargs:keys}, % but adds to |\collargs@process@arg|, which holds the delimiter wrapper % defined by the argument type handler. Note that this macro \emph{appends} % a wrapper, so multiple wrappers are allowed --- this is used by type |e| % handler. \def\collargs@wrap#1{% \appto\collargs@process@arg{% \long\def\collargs@temp##1{#1}% \expandafter\expandafter\expandafter\collargsArg \expandafter\expandafter\expandafter{% \expandafter\collargs@temp\expandafter{\the\collargsArg}% }% }% } \def\collargs@process@arg{} % \end{macro} % % \begin{macro}{\collargs@defcollector,\collargs@defusecollector,\collargs@letusecollector} % These macros streamline the usage of % the ``caller'' control sequence. They are like a |\def|, but should not % be given the control sequence to define, as they will automatically % define the control sequence residing in |\collargs@caller|; the usage is % thus |\collargs@defcollector{}|. For example, if % |\collargs@caller| holds |\foo|, |\collargs@defcollector#1{(#1)}| is % equivalent to |\def\foo#1{(#1)}|. Macro |\collargs@defcollector| will % only define the caller control sequence to be the collector, while % |\collargs@defusecollector| will also immediately execute it. \def\collargs@defcollector#1#{% \ifcollargs@long\long\fi \expandafter\def\collargs@caller#1% } \def\collargs@defusecollector#1#{% \afterassignment\collargs@caller \ifcollargs@long\long\fi \expandafter\def\collargs@caller#1% } \def\collargs@letusecollector#1{% \expandafter\let\collargs@caller#1% \collargs@caller } \newif\ifcollargs@grabspaces \collargs@grabspacestrue % \end{macro} % % \begin{macro}{\collargs@init@grabspaces} % The space-grabber macro % |\collargs@grabspaces| should be initialized by executing this macro. If % |\collargs@grabspaces| is called twice without an intermediate % initialization, it will assume it is in the same position in the input % stream and simply bail out. \def\collargs@init@grabspaces{% \gdef\collargs@gs@state{0}% \gdef\collargs@spaces{}% \gdef\collargs@otherspaces{}% } % \end{macro} % % \begin{macro}{\collargs@grabspaces} % This auxiliary macro grabs any following % spaces, and then executes the next-code given as the sole argument. The % spaces will be stored into two macros, |\collargs@spaces| and % |\collargs@otherspaces|, which store the spaces in the non-verbatim and the % verbatim form. With the double storage, we can grab the spaces in the % verbatim mode and use them non-verbatim, or vice versa. The macro takes a % single argument, the code to execute after maybe grabbing the spaces. % \def\collargs@grabspaces#1{% \edef\collargs@gs@next{\unexpanded{#1}}% \ifnum\collargs@gs@state=0 \gdef\collargs@gs@state{1}% \expandafter\collargs@gs@i \else \expandafter\collargs@gs@next \fi } \def\collargs@gs@i{% \futurelet\collargs@temp\collargs@gs@g } % We check for grouping characters even in the verbatim mode, because we might % be in the partial verbatim. \def\collargs@gs@g{% \ifcat\noexpand\collargs@temp\bgroup \expandafter\collargs@gs@next \else \ifcat\noexpand\collargs@temp\egroup \expandafter\expandafter\expandafter\collargs@gs@next \else \expandafter\expandafter\expandafter\collargs@gs@ii \fi \fi } \def\collargs@gs@ii{% \ifcollargs@verbatim \expandafter\collargs@gos@iii \else \expandafter\collargs@gs@iii \fi } % This works because the character code of a space token is always 32. \def\collargs@gs@iii{% \expandafter\ifx\space\collargs@temp \expandafter\collargs@gs@iv \else \expandafter\collargs@gs@next \fi } \expandafter\def\expandafter\collargs@gs@iv\space{% \gappto\collargs@spaces{ }% \xappto\collargs@otherspaces{\collargs@otherspace}% \collargs@gs@i } % We need the space of category 12 above. \begingroup\catcode`\ =12\relax\gdef\collargs@otherspace{ }\endgroup \def\collargs@gos@iii#1{% % Macro |\collargs@cc| recalls the ``outside'' category code of character % |#1|; see section~\ref{sec:code:collargs:verbatim}. \ifnum\collargs@cc{#1}=10 % We have a space. \expandafter\collargs@gos@iv \else \ifnum\collargs@cc{#1}=5 % We have a newline. \expandafter\expandafter\expandafter\collargs@gos@v \else \expandafter\expandafter\expandafter\collargs@gs@next \fi \fi #1% } \def\collargs@gos@iv#1{% \gappto\collargs@otherspaces{#1}% % No matter how many verbatim spaces we collect, they equal a single % non-verbatim space. \gdef\collargs@spaces{ }% \collargs@gs@i } \def\collargs@gos@v{% % Only add the first newline. \ifnum\collargs@gs@state=2 \expandafter\collargs@gs@next \else \expandafter\collargs@gs@vi \fi } \def\collargs@gs@vi#1{% \gdef\collargs@gs@state{2}% \gappto\collargs@otherspaces{#1}% \gdef\collargs@spaces{ }% \collargs@gs@i } % \end{macro} % % \begin{macro}{\collargs@maybegrabspaces} % This macro grabs any following spaces, but it will do so only when % conditional |\ifcollargs@grabspaces|, which can be \emph{un}set by modifier % |!|, is in effect. The macro is used by handlers for types |d| and |t|. \def\collargs@maybegrabspaces{% \ifcollargs@grabspaces \expandafter\collargs@grabspaces \else \expandafter\@firstofone \fi } % \end{macro} % % \begin{macro}{\collargs@grabbed@spaces} % This macro expands to either the verbatim % or the non-verbatim variant of the grabbed spaces, depending on the % verbatim mode in effect at the time of expansion. \def\collargs@grabbed@spaces{% \ifcollargs@verbatim \collargs@otherspaces \else \collargs@spaces \fi } % \end{macro} % % \begin{macro}{\collargs@reinsert@spaces} % Inserts the grabbed spaces back into the % input stream, but with the category code appropriate for the verbatim mode % then in effect. After the insertion, the space-grabber is initialized and % the given next-code is executed in front of the inserted spaces. \def\collargs@reinsert@spaces#1{% \expanded{% \unexpanded{% \collargs@init@grabspaces #1% }% \collargs@grabbed@spaces }% } % \end{macro} % % \begin{macro}{\collargs@ifnextcat} % An adaptation of |\pgf@keys@utilifnextchar| % which checks whether the \emph{category} code of the next non-space % character matches the category code of |#1|. \long\def\collargs@ifnextcat#1#2#3{% \let\pgf@keys@utilreserved@d=#1% \def\pgf@keys@utilreserved@a{#2}% \def\pgf@keys@utilreserved@b{#3}% \futurelet\pgf@keys@utillet@token\collargs@ifncat} \def\collargs@ifncat{% \ifx\pgf@keys@utillet@token\pgf@keys@utilsptoken \let\pgf@keys@utilreserved@c\collargsxifnch \else \ifcat\noexpand\pgf@keys@utillet@token\pgf@keys@utilreserved@d \let\pgf@keys@utilreserved@c\pgf@keys@utilreserved@a \else \let\pgf@keys@utilreserved@c\pgf@keys@utilreserved@b \fi \fi \pgf@keys@utilreserved@c} {% \def\:{\collargs@xifncat} \expandafter\gdef\: {\futurelet\pgf@keys@utillet@token\collargs@ifncat} } % \end{macro} % % \begin{macro}{\collargs@forrange} % This macro executes macro |\collargs@do| for every integer from |#1| and % |#2|, both inclusive. |\collargs@do| should take a single parameter, the % current number. \def\collargs@forrange#1#2{% \expanded{% \noexpand\collargs@forrange@i{\number#1}{\number#2}% }% } \def\collargs@forrange@i#1#2{% \ifnum#1>#2 % \expandafter\@gobble \else \expandafter\@firstofone \fi {% \collargs@do{#1}% \expandafter\collargs@forrange@i\expandafter{\number\numexpr#1+1\relax}{#2}% }% } % \end{macro} % % \begin{macro}{\collargs@forranges} % This macro executes macro |\collargs@do| for every integer falling into the % ranges specified in |#1|. The ranges should be given as a comma-separated % list of |from-to| items, e.g.\ |1-5,10-11|. \def\collargs@forranges{\forcsvlist\collarg@forrange@i} \def\collarg@forrange@i#1{\collarg@forrange@ii#1-} \def\collarg@forrange@ii#1-#2-{\collargs@forrange{#1}{#2}} % \end{macro} % % \begin{macro}{\collargs@percentchar} % This macro holds the percent character of category 12. \begingroup \catcode`\%=12 \gdef\collargs@percentchar{%} \endgroup % \end{macro} % % % \subsubsection{The handlers} % \label{sec:code:collargs:handlers} % % \begin{macro}{\collargs@l} % We will first define the handler for the very funky argument type |l|, % which corresponds to \hologo{TeX}'s |\def\foo#1#{...}|, which grabs (into % |#1|) everything up to the first opening brace --- not because this type is % important or even recommended to use, but because the definition of the % handler is very simple, at least for the non-verbatim case. % \def\collargs@l#1.{% % Any pre-grabbed spaces in fact belong into the argument. \collargs@reinsert@spaces{\collargs@l@i#1.}% } \def\collargs@l@i{% % We request a correction of the category code of the delimiting brace if the % verbatim mode changes for the next argument; for details, see % section~\ref{sec:code:collargs:fix}. \global\collargs@fix@requestedtrue % Most handlers will branch into the verbatim and the non-verbatim part using % conditional |\ifcollargs@verbatim|. This handler is a bit special, because % it needs to distinguish verbatim and non-verbatim \emph{braces}, and % braces are verbatim only in the full verbatim mode, i.e.\ when % |\ifcollargs@verbatimbraces| is true. \ifcollargs@verbatimbraces \expandafter\collargs@l@verb \else \expandafter\collargs@l@ii \fi } % We grab the rest of the argument specification (|#1|), to be reinserted into % the token stream when we reexecute the central loop. \def\collargs@l@ii#1.{% % In the non-verbatim mode, we merely have to define and execute the % collector macro. The parameter text |##1##| (note the doubled hashes), % which will put everything up to the first opening brace into the first % argument, looks funky, but that's all. \collargs@defusecollector##1##{% % We append the collected argument, |##1|, to |\collargsArgs|, the token % register holding the collected argument tokens. \collargs@appendarg{##1}% % Back to the central loop, with the rest of the argument specification % reinserted. \collargs@#1.% }% } \def\collargs@l@verb#1.{% % In the verbatim branch, we need to grab everything up to the first opening % brace of category code 12, so we want to define the collector with % parameter text |##1{|, with the opening brace of category 12. We have % stored this token in macro |\collargs@other@bgroup|, which we now need to % expand. \expandafter\collargs@defusecollector \expandafter##\expandafter1\collargs@other@bgroup{% % Appending the argument works the same as in the non-verbatim case. \collargs@appendarg{##1}% % Reexecuting the central loop macro is a bit more involved, as we need to % reinsert the verbatim opening brace (contrary to the regular brace above, % the verbatim brace is consumed by the collector macro) back into the % token stream, behind the reinserted argument specification. \expanded{% \noexpand\collargs@\unexpanded{#1.}% \collargs@other@bgroup }% }% } % \end{macro} % % \begin{macro}{\collargs@u} % Another weird type --- |u|\meta{tokens} reads everything up to the given % \meta{tokens}, i.e.\ this is \hologo{TeX}'s % |\def\foo#1|\meta{tokens}|{...}| --- but again, simple enough to allow us % to showcase solutions to two recurring problems. % % We start by branching into the verbatim mode (full or partial) or the % non-verbatim mode. \def\collargs@u{% \ifcollargs@verbatim \expandafter\collargs@u@verb \else \expandafter\collargs@u@i \fi } % To deal with the verbatim mode, we only need to convert the above % \meta{tokens} (i.e.\ the argument of |u| in the argument specification) to % category 12, i.e.\ we have to |\detokenize| them. Then, we may proceed as in % the non-verbatim branch, |\collargs@u@ii|. \def\collargs@u@verb#1{% % The |\string| here is a temporary solution to a problem with spaces. Our % verbatim mode has them of category ``other'', but |\detokenize| produces a % space of category ``space'' behind control words. ^^A todo \expandafter\collargs@u@i\expandafter{\detokenize\expandafter{\string#1}}% } % We then reinsert any pre-grabbed spaces into the stream, but we take care not % to destroy the braces around our delimiter in the argument specification. \def\collargs@u@i#1#2.{% \collargs@reinsert@spaces{\collargs@u@ii{#1}#2.}% } \def\collargs@u@ii#1#2.{% % |#1| contains the delimiter tokens, so |##1| below will receive everything % in the token stream up to these. But we have a problem: if we defined the % collector as for the non-verbatim |l|, and the delimiter happened to be % preceded by a single brace group, we would lose the braces. For example, % if the delimiter was |-| and we received |{foo}-|, we would collect |foo-|. % We solve this problem by inserting |\collargs@empty| (with an empty % definition) into the input stream (at the end of this macro) --- this way, % the delimiter can never be preceded by a single brace group --- and then % expanding it away before appending to storage (within the argument of % |\collargs@defusecollector|). \collargs@defusecollector##1#1{% % Define the wrapper which will add the delimiter tokens (|#1|) after the % collected argument. The wrapper will be applied during argument % processing in |\collargs@appendarg| (sandwiched between used-definable % pre- and post-processors). \collargs@wrap{####1#1}% % Expand the first token in |##1|, which we know to be |\collargs@empty|, % with empty expansion. \expandafter\collargs@appendarg\expandafter{##1}% \collargs@#2.% }% % Insert |\collargs@empty| into the input stream, in front of the ``real'' % argument tokens. \collargs@empty } \def\collargs@empty{} % \end{macro} % % \begin{macro}{\collargs@r} % Finally, a real argument type: required delimited argument. \def\collargs@r{% \ifcollargs@verbatim \expandafter\collargs@r@verb \else \expandafter\collargs@r@i \fi } \def\collargs@r@verb#1#2{% \expandafter\collargs@r@i\detokenize{#1#2}% } \def\collargs@r@i#1#2#3.{% % We will need to use the |\collargs@empty| trick from type |u|, but with an % additional twist: we need to insert it \emph{after} the opening delimiter % |#1|. To do this, we consume the opening delimiter by the ``outer'' % collector below --- we need to use the collector so that we get a nice % error message when the opening delimiter is not present --- and have this % collector define the ``inner'' collector in the spirit of type |u|. % % The outer collector has no parameters, it just requires the presence of the % opening delimiter. \collargs@defcollector#1{% % The inner collector will grab everything up to the closing delimiter. \collargs@defusecollector####1#2{% % Append the collected argument |####1| to the list, wrapping it into the % delimiters (|#1| and |#2|), but not before expanding its first token, % which we know to be |\collargs@empty|. \collargs@wrap{#1########1#2}% \expandafter\collargs@appendarg\expandafter{####1}% \collargs@#3.% }% \collargs@empty }% % Another complication: our delimited argument may be preceded by spaces. To % replicate the argument tokens faithfully, we need to collect them before % trying to grab the argument itself. \collargs@grabspaces\collargs@caller } % \end{macro} % % \begin{macro}{\collargs@R} % Discard the default and execute |r|. \def\collargs@R#1#2#3{\collargs@r#1#2} % \end{macro} % % \begin{macro}{\collargs@d} % Optional delimited argument. Very similar to |r|. \def\collargs@d{% \ifcollargs@verbatim \expandafter\collargs@d@verb \else \expandafter\collargs@d@i \fi } \def\collargs@d@verb#1#2{% \expandafter\collargs@d@i\detokenize{#1#2}% } \def\collargs@d@i#1#2#3.{% % This macro will be executed when the optional argument is not present. It % simply closes the argument's group and reexecutes the central loop. \def\collargs@d@noopt{% \global\collargs@fix@requestedtrue \endgroup \collargs@#3.% }% % The collector(s) are exactly as for |r|. \collargs@defcollector#1{% \collargs@defusecollector####1#2{% \collargs@wrap{#1########1#2}% \expandafter\collargs@appendarg\expandafter{####1}% \collargs@#3.% }% \collargs@empty }% % This macro will check, in conjunction with |\futurelet| below, whether the % optional argument is present or not. \def\collargs@d@ii{% \ifx#1\collargs@temp \expandafter\collargs@caller \else \expandafter\collargs@d@noopt \fi }% % Whether spaces are allowed in front of this type of argument depends on the % presence of modifier |!|. \collargs@maybegrabspaces{\futurelet\collargs@temp\collargs@d@ii}% } % \end{macro} % % \begin{macro}{\collargs@D} % Discard the default and execute |d|. \def\collargs@D#1#2#3{\collargs@d#1#2} % \end{macro} % % \begin{macro}{\collargs@o} % |o| is just |d| with delimiters |[| and |]|. \def\collargs@o{\collargs@d[]} % \end{macro} % % \begin{macro}{\collargs@O} % |O| is just |d| with delimiters |[| and |]| and the discarded default. \def\collargs@O#1{\collargs@d[]} % \end{macro} % % \begin{macro}{\collargs@t} % An optional token. Similar to |d|. \def\collargs@t{% \ifcollargs@verbatim \expandafter\collargs@t@verb \else \expandafter\collargs@t@i \fi } \def\collargs@t@space{ } \def\collargs@t@verb#1{% \let\collargs@t@space\collargs@otherspace \expandafter\collargs@t@i\expandafter{\detokenize{#1}}% } \def\collargs@t@i#1{% \expandafter\ifx\space#1% \expandafter\collargs@t@s \else \expandafter\collargs@t@I\expandafter#1% \fi } \def\collargs@t@s#1.{% \collargs@grabspaces{% \ifcollargs@grabspaces \collargs@appendarg{}% \else \expanded{% \noexpand\collargs@init@grabspaces \noexpand\collargs@appendarg{\collargs@grabbed@spaces}% }% \fi \collargs@#1.% }% } \def\collargs@t@I#1#2.{% \def\collargs@t@noopt{% \global\collargs@fix@requestedtrue \endgroup \collargs@#2.% }% \def\collargs@t@opt##1{% \collargs@appendarg{#1}% \collargs@#2.% }% \def\collargs@t@ii{% \ifx#1\collargs@temp \expandafter\collargs@t@opt \else \expandafter\collargs@t@noopt \fi }% \collargs@maybegrabspaces{\futurelet\collargs@temp\collargs@t@ii}% } \def\collargs@t@opt@space{% \expanded{\noexpand\collargs@t@opt{\space}\expandafter}\romannumeral-0% }% % \end{macro} % % \begin{macro}{\collargs@s} % The optional star is just a special case of |t|. \def\collargs@s{\collargs@t*} % \end{macro} % % \begin{macro}{\collargs@m} % Mandatory argument. Interestingly, here's where things get complicated, % because we have to take care of several \hologo{TeX} quirks. \def\collargs@m{% \ifcollargs@verbatim \expandafter\collargs@m@verb \else \expandafter\collargs@m@i \fi } % The non-verbatim mode. First, collect any spaces in front of the argument. \def\collargs@m@i#1.{% \collargs@grabspaces{\collargs@m@checkforgroup#1.}% } % Is the argument in braces or not? \def\collargs@m@checkforgroup#1.{% \edef\collargs@action{\unexpanded{\collargs@m@checkforgroup@i#1.}}% \futurelet\collargs@token\collargs@action } \def\collargs@m@checkforgroup@i{% \ifcat\noexpand\collargs@token\bgroup \expandafter\collargs@m@group \else \expandafter\collargs@m@token \fi } % The argument is given in braces, so we put them back around it % (|\collargs@wrap|) when appending to the storage. \def\collargs@m@group#1.{% \collargs@defusecollector##1{% \collargs@wrap{{####1}}% \collargs@appendarg{##1}% \collargs@#1.% }% } % The argument is a single token, we append it to the storage as is. \def\collargs@m@token#1.{% \collargs@defusecollector##1{% \collargs@appendarg{##1}% \collargs@#1.% }% } % The verbatim mode. Again, we first collect any spaces in front of the % argument. \def\collargs@m@verb#1.{% \collargs@grabspaces{\collargs@m@verb@checkforgroup#1.}% } % We want to check whether we're dealing with a braced argument. We're in the % verbatim mode, but are braces verbatim as well? In other words, are we in % |verbatim| or |verb| mode? In the latter case, braces are regular, so we % redirect to the regular mode. \def\collargs@m@verb@checkforgroup{% \ifcollargs@verbatimbraces \expandafter\collargs@m@verb@checkforgroup@i \else \expandafter\collargs@m@checkforgroup \fi } % Is the argument in verbatim braces? \def\collargs@m@verb@checkforgroup@i#1.{% \def\collargs@m@verb@checkforgroup@ii{\collargs@m@verb@checkforgroup@iii#1.}% \futurelet\collargs@temp\collargs@m@verb@checkforgroup@ii } \def\collargs@m@verb@checkforgroup@iii#1.{% \expandafter\ifx\collargs@other@bgroup\collargs@temp % Yes, the argument is in (verbatim) braces. \expandafter\collargs@m@verb@group \else % We need to manually check whether the following token is a (verbatim) % closing brace, and throw an error if it is. \expandafter\ifx\collargs@other@egroup\collargs@temp \expandafter\expandafter\expandafter\collargs@m@verb@egrouperror \else % The argument is a single token. \expandafter\expandafter\expandafter\collargs@m@v@token \fi \fi #1.% } \def\collargs@m@verb@egrouperror#1.{% \PackageError{collargs}{% Argument of \expandafter\string\collargs@caller\space has an extra \iffalse{\else\string}}{}% } % A single-token verbatim argument. \def\collargs@m@v@token#1.#2{% % Is it a control sequence? (Macro |\collargs@cc| recalls the ``outside'' % category code of character |#1|; see % section~\ref{sec:code:collargs:verbatim}.) \ifnum\collargs@cc{#2}=0 \expandafter\collargs@m@v@token@cs \else \expandafter\collargs@m@token \fi #1.#2% } % Is it a one-character control sequence? \def\collargs@m@v@token@cs#1.#2#3{% \ifnum\collargs@cc{#3}=11 \expandafter\collargs@m@v@token@cs@letter \else \expandafter\collargs@m@v@token@cs@nonletter \fi #1.#2#3% } % Store |\|. \def\collargs@m@v@token@cs@nonletter#1.#2#3{% \collargs@appendarg{#2#3}% \collargs@#1.% } % Store |\| to a temporary register, we'll parse the control sequence name now. \def\collargs@m@v@token@cs@letter#1.#2{% \collargsArg{#2}% \def\collargs@tempa{#1}% \collargs@m@v@token@cs@letter@i } % Append a letter to the control sequence. \def\collargs@m@v@token@cs@letter@i#1{% \ifnum\collargs@cc{#1}=11 \toksapp\collargsArg{#1}% \expandafter\collargs@m@v@token@cs@letter@i \else % Finish, returning the non-letter to the input stream. \expandafter\collargs@m@v@token@cs@letter@ii\expandafter#1% \fi } % Store the verbatim control sequence. \def\collargs@m@v@token@cs@letter@ii{% \expanded{% \unexpanded{% \expandafter\collargs@appendarg\expandafter{\the\collargsArg}% }% \noexpand\collargs@\expandonce\collargs@tempa.% }% } % The verbatim mandatory argument is delimited by verbatim braces. We have to % use the heavy machinery adapted from |cprotect|. \def\collargs@m@verb@group#1.#2{% \let\collargs@begintag\collargs@other@bgroup \let\collargs@endtag\collargs@other@egroup \def\collargs@tagarg{}% \def\collargs@commandatend{\collargs@m@verb@group@i#1.}% \collargs@readContent } % This macro appends the result given by the heavy machinery, waiting for us in % macro |\collargsArg|, to |\collargsArgs|, but not before dressing % it up (via |\collargs@wrap|) in a pair of verbatim braces. \def\collargs@m@verb@group@i{% \edef\collargs@temp{% \collargs@other@bgroup\unexpanded{##1}\collargs@other@egroup}% \expandafter\collargs@wrap\expandafter{\collargs@temp}% \expandafter\collargs@appendarg\expandafter{\the\collargsArg}% \collargs@ } % \end{macro} % % \begin{macro}{\collargs@g} % An optional group: same as |m|, but we simply bail out if we don't find the % group character. \def\collargs@g{% \def\collargs@m@token{% \global\collargs@fix@requestedtrue \endgroup \collargs@ }% \let\collargs@m@v@token\collargs@m@token \collargs@m } % \end{macro} % % \begin{macro}{\collargs@G} % Discard the default and execute |g|. \def\collargs@G#1{\collargs@g} % \end{macro} % % \begin{macro}{\collargs@v} % Verbatim argument. The code is executed in the group, deploying % |\collargsVerbatim|. The grouping characters are always set to braces, to % mimick |xparse| perfectly. \def\collargs@v#1.{% \begingroup \collargsBraces{{}}% \collargsVerbatim \collargs@grabspaces{\collargs@v@i#1.}% } \def\collargs@v@i#1.#2{% \expandafter\ifx\collargs@other@bgroup#2% % If the first token we see is an opening brace, use the |cprotect| % adaptation to grab the group. \let\collargs@begintag\collargs@other@bgroup \let\collargs@endtag\collargs@other@egroup \def\collargs@tagarg{}% \def\collargs@commandatend{% \edef\collargs@temp{% \collargs@other@bgroup\unexpanded{####1}\collargs@other@egroup}% \expandafter\collargs@wrap\expandafter{\collargs@temp}% \expandafter\collargs@appendarg\expandafter{\the\collargsArg}% \endgroup \collargs@#1.% }% \expandafter\collargs@readContent \else % Otherwise, the verbatim argument is delimited by two identical characters % (|#2|). \collargs@defcollector##1#2{% \collargs@wrap{#2####1#2}% \collargs@appendarg{##1}% \endgroup \collargs@#1.% }% \expandafter\collargs@caller \fi } % \end{macro} % % \begin{macro}{\collargs@b} % Environments. Here's where all hell breaks loose. We survive by adapting % some code from Bruno Le Floch's |cprotect|. We first define the % environment-related keys, then provide the handler code, and finish with % the adaptation of |cprotect|'s environment-grabbing code. % % The argument type |b| token may be followed by a braced environment name % (in the argument specification). \def\collargs@b{% \collargs@ifnextcat\bgroup\collargs@bg\collargs@bi } \def\collargs@bg#1{% \edef\collargs@b@envname{#1}% \collargs@bi } \def\collargs@bi#1.{% % Convert the environment name to verbatim if necessary. \ifcollargs@verbatim \edef\collargs@b@envname{\detokenize\expandafter{\collargs@b@envname}}% \fi % This is a format-specific macro which sets up |\collargs@begintag| and % |\collargs@endtag|. \collargs@bi@defCPTbeginend \edef\collargs@tagarg{% \ifcollargs@verbatimbraces \else \ifcollargsIgnoreOtherTags \collargs@b@envname \fi \fi }% % Run this after collecting the body. \def\collargs@commandatend{% % In \hologo{LaTeX}, we might, depending on the verbatim mode, need to % check whether the environment name is correct. %\collargs@bii % In \hologo{plainTeX} and \hologo{ConTeXt}, we can skip directly to % |\collargs@biii|. %\collargs@biii #1.% }% % Collect the environment body, but first, put any grabbed spaces back into % the input stream. \collargs@reinsert@spaces\collargs@readContent } %<*latex> % In \hologo{LaTeX} in the regular and the partial verbatim mode, we search for % |\begin|\slash|\end| --- as we cannot search for braces --- either as control % sequences in the regular mode, or as strings in the partial verbatim % mode. (After search, we will have to check whether the argument of % |\begin|\slash|\end| matches our environment name.) In the full verbatim % mode, we can search for the entire string |\begin|\slash|\end|\marg{name}. \def\collargs@bi@defCPTbeginend{% \edef\collargs@begintag{% \ifcollargs@verbatim \expandafter\string \else \expandafter\noexpand \fi \begin \ifcollargs@verbatimbraces \collargs@other@bgroup\collargs@b@envname\collargs@other@egroup \fi }% \edef\collargs@endtag{% \ifcollargs@verbatim \expandafter\string \else \expandafter\noexpand \fi \end \ifcollargs@verbatimbraces \collargs@other@bgroup\collargs@b@envname\collargs@other@egroup \fi }% } % %<*plain,context> % We can search for the entire |\|\meta{name}\slash|\end|\meta{name} (in % \hologo{TeX}) or |\start|\meta{name}\slash|\stop|\meta{name} (in % \hologo{ConTeXt}), either as a control sequence (in the regular mode), or as % a string (in the verbatim modes). \def\collargs@bi@defCPTbeginend{% \edef\collargs@begintag{% \ifcollargs@verbatim \expandafter\expandafter\expandafter\string \else \expandafter\expandafter\expandafter\noexpand \fi \csname %start% \collargs@b@envname \endcsname }% \edef\collargs@endtag{% \ifcollargs@verbatim \expandafter\expandafter\expandafter\string \else \expandafter\expandafter\expandafter\noexpand \fi \csname %end% %stop% \collargs@b@envname \endcsname }% } % %<*latex> % Check whether we're in front of the (braced) environment name (in % \hologo{LaTeX}), and consume it. \def\collargs@bii{% \ifcollargs@verbatimbraces \expandafter\collargs@biii \else \ifcollargsIgnoreOtherTags % We shouldn't check the name in this case, because it was already % checked, and consumed. \expandafter\expandafter\expandafter\collargs@biii \else \expandafter\expandafter\expandafter\collargs@b@checkend \fi \fi } \def\collargs@b@checkend#1.{% \collargs@grabspaces{\collargs@b@checkend@i#1.}% } \def\collargs@b@checkend@i#1.#2{% \def\collargs@temp{#2}% \ifx\collargs@temp\collargs@b@envname \else \collargs@b@checkend@error \fi \collargs@biii#1.% } \def\collargs@b@checkend@error{% \PackageError{collargs}{Environment "\collargs@b@envname" ended as "\collargs@temp"}{}% } % % This macro stores the collected body. \def\collargs@biii{% % Define the wrapper macro (|\collargs@temp|). \collargs@b@def@wrapper % Execute |\collargs@appendarg| to append the body to the list. Expand the % wrapper in |\collargs@temp| first and the body in |\collargsArg| next. \expandafter\collargs@appendarg\expandafter{\the\collargsArg}% % Reexecute the central loop. \collargs@ } \def\collargs@b@def@wrapper{% %\edef\collargs@temp{{\collargs@b@envname}}% \edef\collargs@temp{% % Was the begin-tag requested? \ifcollargsBeginTag % |\collargs@begintag| is already adapted to the format and the verbatim mode. \expandonce\collargs@begintag % Add the braced environment name in \hologo{LaTeX} in the regular and % partial verbatim mode. % %<*latex> \ifcollargs@verbatimbraces\else\collargs@temp\fi % \fi % This is the body. ####1% % Rinse and repeat for the end-tag. \ifcollargsEndTag \expandonce\collargs@endtag %<*latex> \ifcollargs@verbatimbraces\else\collargs@temp\fi % \fi }% \expandafter\collargs@wrap\expandafter{\collargs@temp}% } % \end{macro} % % \begin{macro}{\collargs@readContent} % This macro, which is an adaptation of % \texttt{cprotect}'s environment-grabbing code, collects some delimited % text, leaving the result in |\collargsArg|. Before calling it, one must % define the following macros: |\collargs@begintag| and |\collargs@endtag| % are the content delimiters; |\collargs@tagarg|, if non-empty, is the token % or grouped text which must follow a delimiter to be taken into account; % |\collargs@commandatend| is the command that will be executed once the % content is collected. \def\collargs@readContent{% % Define macro which will search for the first begin-tag. \ifcollargs@long\long\fi \collargs@CPT@def\collargs@gobbleOneB\collargs@begintag{% % Assign the collected tokens into a register. The first token in |##1| % will be |\collargs@empty|, so we expand to get rid of it. \toks0\expandafter{##1}% % |cprotect| simply grabs the token following the |\collargs@begintag| with % a parameter. We can't do this, because we need the code to work in the % non-verbatim mode, as well, and we might stumble upon a brace there. So % we take a peek. \futurelet\collargs@temp\collargs@gobbleOneB@i }% % Define macro which will search for the first end-tag. We make it long if % so required (by |+|). \ifcollargs@long\long\fi \collargs@CPT@def\collargs@gobbleUntilE\collargs@endtag{% % Expand |\collargs@empty| at the start of |##1|. \expandafter\toksapp\expandafter0\expandafter{##1}% \collargs@gobbleUntilE@i }% % Initialize. \collargs@begins=0\relax \collargsArg{}% \toks0{}% % We will call |\collargs@gobbleUntilE| via the caller control sequence. \collargs@letusecollector\collargs@gobbleUntilE % We insert |\collargs@empty| to avoid the potential debracing problem. \collargs@empty } % How many begin-tags do we have opened? \newcount\collargs@begins % An auxiliary macro which |\def|s |#1| so that it will grab everything up % until |#2|. Additional parameters may be present before the definition. \def\collargs@CPT@def#1#2{% \expandafter\def\expandafter#1% \expandafter##\expandafter1#2% } % A quark quard. \def\collargs@qend{\collargs@qend} % This macro will collect the ``environment'', leaving the result in % |\collargsArg|. It expects |\collargs@begintag|, |\collargs@endtag| and % |\collargs@commandatend| to be set. \def\collargs@gobbleOneB@i{% \def\collargs@begins@increment{1}% \ifx\collargs@qend\collargs@temp % We have reached the fake begin-tag. Note that we found the end-tag. \def\collargs@begins@increment{-1}% % Gobble the quark guard. \expandafter\collargs@gobbleOneB@v \else % Append the real begin-tag to the temporary tokens. \etoksapp0{\expandonce\collargs@begintag}% \expandafter\collargs@gobbleOneB@ii \fi }% % Do we have to check the tag argument (i.e.\ the environment name after |\begin|)? \def\collargs@gobbleOneB@ii{% \expandafter\ifx\expandafter\relax\collargs@tagarg\relax \expandafter\collargs@gobbleOneB@vi \else % Yup, so let's (carefully) collect the tag argument. \expandafter\collargs@gobbleOneB@iii \fi } \def\collargs@gobbleOneB@iii{% \collargs@grabspaces{% \collargs@letusecollector\collargs@gobbleOneB@iv }% } \def\collargs@gobbleOneB@iv#1{% \def\collargs@temp{#1}% \ifx\collargs@temp\collargs@tagarg % This is the tag argument we've been waiting for! \else % Nope, this |\begin| belongs to someone else. \def\collargs@begins@increment{0}% \fi % Whatever the result was, we have to append the gobbled group to the % temporary toks. \etoksapp0{\collargs@grabbed@spaces\unexpanded{{#1}}}% \collargs@init@grabspaces \collargs@gobbleOneB@vi } \def\collargs@gobbleOneB@v#1{\collargs@gobbleOneB@vi} \def\collargs@gobbleOneB@vi{% % Store. \etoksapp\collargsArg{\the\toks0}% % Advance the begin-tag counter. \advance\collargs@begins\collargs@begins@increment\relax % Find more begin-tags, unless this was the final one. \ifnum\collargs@begins@increment=-1 \else \expandafter\collargs@gobbleOneB\expandafter\collargs@empty \fi } \def\collargs@gobbleUntilE@i{% % Do we have to check the tag argument (i.e.\ the environment name after |\end|)? \expandafter\ifx\expandafter\relax\collargs@tagarg\relax \expandafter\collargs@gobbleUntilE@iv \else % Yup, so let's (carefully) collect the tag argument. \expandafter\collargs@gobbleUntilE@ii \fi } \def\collargs@gobbleUntilE@ii{% \collargs@grabspaces{% \collargs@letusecollector\collargs@gobbleUntilE@iii }% } \def\collargs@gobbleUntilE@iii#1{% \etoksapp0{\collargs@grabbed@spaces}% \collargs@init@grabspaces \def\collargs@tempa{#1}% \ifx\collargs@tempa\collargs@tagarg % This is the tag argument we've been waiting for! \expandafter\collargs@gobbleUntilE@iv \else % Nope, this |\end| belongs to someone else. Insert the end tag plus the % tag argument, and collect until the next |\end|. \expandafter\toksapp\expandafter0\expandafter{\collargs@endtag{#1}}% \expandafter\collargs@letusecollector\expandafter\collargs@gobbleUntilE \fi } \def\collargs@gobbleUntilE@iv{% % Invoke |\collargs@gobbleOneB| with the collected material, plus a fake % begin-tag and a quark guard. \ifcollargsIgnoreNesting \expandafter\collargsArg\expandafter{\the\toks0}% \expandafter\collargs@commandatend \else \expandafter\collargs@gobbleUntilE@v \fi } \def\collargs@gobbleUntilE@v{% \expanded{% \noexpand\collargs@letusecollector\noexpand\collargs@gobbleOneB \noexpand\collargs@empty \the\toks0 % Add a fake begin-tag and a quark guard. \expandonce\collargs@begintag \noexpand\collargs@qend }% \ifnum\collargs@begins<0 \expandafter\collargs@commandatend \else \etoksapp\collargsArg{% \expandonce\collargs@endtag \expandafter\ifx\expandafter\relax\collargs@tagarg\relax\else{% \expandonce\collargs@tagarg}\fi }% \toks0={}% \expandafter\collargs@letusecollector\expandafter\collargs@gobbleUntilE \expandafter\collargs@empty \fi } % \end{macro} % % \begin{macro}{\collargs@e} % Embellishments. Each embellishment counts as an argument, in the sense % that we will execute |\collargs@appendarg|, with all the processors, for % each embellishment separately. \def\collargs@e{% % We open an extra group, because |\collargs@appendarg| will close a group % for each embellishment. \global\collargs@fix@requestedtrue \begingroup \ifcollargs@verbatim \expandafter\collargs@e@verbatim \else \expandafter\collargs@e@i \fi } % Detokenize the embellishment tokens in the verbatim mode. \def\collargs@e@verbatim#1{% \expandafter\collargs@e@i\expandafter{\detokenize{#1}}% } % Ungroup the embellishment tokens, separating them from the rest of the % argument specification by a dot. \def\collargs@e@i#1{\collargs@e@ii#1.} % We now have embellishment tokens in |#1| and the rest of the argument % specification in |#2|. Let's grab spaces first. \def\collargs@e@ii#1.#2.{% \collargs@grabspaces{\collargs@e@iii#1.#2.}% } % What's the argument token? \def\collargs@e@iii#1.#2.{% \def\collargs@e@iv{\collargs@e@v#1.#2.}% \futurelet\collargs@temp\collargs@e@iv } % If it is a open or close group character, we surely don't have an % embellishment. \def\collargs@e@v{% \ifcat\noexpand\collargs@temp\bgroup\relax \let\collargs@marshal\collargs@e@z \else \ifcat\noexpand\collargs@temp\egroup\relax \let\collargs@marshal\collargs@e@z \else \let\collargs@marshal\collargs@e@vi \fi \fi \collargs@marshal } % We borrow the ``Does |#1| occur within |#2|?'' macro from |pgfutil-common|, % but we fix it by executing |\collargs@in@@| in a braced group. This will % prevent an |&| in an argument to function as an alignment character; the % minor price to pay is that we assign the conditional globally. \newif\ifcollargs@in@ \def\collargs@in@#1#2{% \def\collargs@in@@##1#1##2##3\collargs@in@@{% \ifx\collargs@in@##2\global\collargs@in@false\else\global\collargs@in@true\fi }% {\collargs@in@@#2#1\collargs@in@\collargs@in@@}% } % Let' see whether the following token, now |#3|, is an embellishment token. \def\collargs@e@vi#1.#2.#3{% \collargs@in@{#3}{#1}% \ifcollargs@in@ \expandafter\collargs@e@vii \else \expandafter\collargs@e@z \fi #1.#2.#3% } % |#3| is the current embellishment token. We'll collect its argument using % |\collargs@m|, but to do that, we have to (locally) redefine % |\collargs@appendarg| and |\collargs@|, which get called by |\collargs@m|. \def\collargs@e@vii#1.#2.#3{% % We'll have to execute the original |\collargs@appendarg| later, so let's % remember it. The temporary |\collargs@appendarg| simply stores the % collected argument into |\collargsArg| --- we'll do the processing etc.\ % later. \let\collargs@real@appendarg\collargs@appendarg \def\collargs@appendarg##1{\collargsArg{##1}}% % Once |\collargs@m| is done, it will call the redefined |\collargs@| and % thereby get us back into this handler. \def\collargs@{\collargs@e@viii#1.#3}% \collargs@m#2.% } % The parameters here are as follows. |#1| are the embellishment tokens, and % |#2| is the current embellishment token; these get here via our local % redefinition of |\collargs@| in |\collargs@e@vii|. |#3| are the rest of the % argument specification, which is put behind control sequence |\collargs@| by % the |m| handler. \def\collargs@e@viii#1.#2#3.{% % Our wrapper puts the current embellishment token in front of the collected % embellishment argument. Note that if the embellishment argument was in % braces, |\collargs@m| has already set one wrapper (which will apply first). \collargs@wrap{#2##1}% % We need to get rid of the current embellishment from embellishments, not to % catch the same embellishment twice. \def\collargs@e@ix##1#2{\collargs@e@x##1}% \collargs@e@ix#1.#3.% } % When this is executed, the input stream starts with the (remaining) % embellishment tokens, followed by a dot, then the rest of the argument % specification, also followed by a dot. \def\collargs@e@x{% % Process the argument and append it to the storage. \expandafter\collargs@real@appendarg\expandafter{\the\collargsArg}% % |\collargs@real@appendarg| has closed a group, so we open it again, and % start looking for another embellishment token in the input stream. \begingroup \collargs@e@ii } % The first argument token in not an embellishment token. We finish by % consuming the list of embellishment tokens, closing the two groups opened by % this handler, and reexecuting the central loop. \def\collargs@e@z#1.{\endgroup\endgroup\collargs@} % \end{macro} % % \begin{macro}{\collargs@E} % Discard the defaults and execute |e|. \def\collargs@E#1#2{\collargs@e{#1}} % \end{macro} % % % \subsubsection{The verbatim modes} % \label{sec:code:collargs:verbatim} % % \begin{macro}{\collargsVerbatim,\collargsVerb,\collargsNoVerbatim} % These macros set the two verbatim-related % conditionals, |\ifcollargs@verbatim| and |\ifcollargs@verbatimbraces|, % and then call |\collargs@make@verbatim| to effect the requested % category code changes (among other things). A group should be opened % prior to executing either of them. After execution, they are % redefined to minimize the effort needed to enter into another mode in % an embedded group. Below, we first define all the possible transitions. \let\collargs@NoVerbatimAfterNoVerbatim\relax \def\collargs@VerbAfterNoVerbatim{% \collargs@verbatimtrue \collargs@verbatimbracesfalse \collargs@make@verbatim \collargs@after{Verb}% } \def\collargs@VerbatimAfterNoVerbatim{% \collargs@verbatimtrue \collargs@verbatimbracestrue \collargs@make@verbatim \collargs@after{Verbatim}% } \def\collargs@NoVerbatimAfterVerb{% \collargs@verbatimfalse \collargs@verbatimbracesfalse \collargs@make@other@groups \collargs@make@no@verbatim \collargs@after{NoVerbatim}% } \def\collargs@VerbAfterVerb{% \collargs@make@other@groups } \def\collargs@VerbatimAfterVerb{% \collargs@verbatimbracestrue \collargs@make@other@groups % Process the lists of grouping characters, created by % |\collargs@make@verbatim|, making these characters of category ``other''. \def\collargs@do##1{\catcode##1=12 }% \collargs@bgroups \collargs@egroups \collargs@after{Verbatim}% }% \let\collargs@NoVerbatimAfterVerbatim\collargs@NoVerbatimAfterVerb \def\collargs@VerbAfterVerbatim{% \collargs@verbatimbracesfalse \collargs@make@other@groups % Process the lists of grouping characters, created by % |\collargs@make@verbatim|, making these characters be of their normal % category. \def\collargs@do##1{\catcode##1=1 }% \collargs@bgroups \def\collargs@do##1{\catcode##1=2 }% \collargs@egroups \collargs@after{Verb}% }% \let\collargs@VerbatimAfterVerbatim\collargs@VerbAfterVerb % This macro expects |#1| to be the mode just entered (|Verbatim|, |Verb| or % |NoVerbatim|), and points macros |\collargsVerbatim|, |\collargsVerb| and % |\collargsNoVerbatim| to the appropriate transition macro. \def\collargs@after#1{% \letcs\collargsVerbatim{collargs@VerbatimAfter#1}% \letcs\collargsVerb{collargs@VerbAfter#1}% \letcs\collargsNoVerbatim{collargs@NoVerbatimAfter#1}% } % The first transition is always from the non-verbatim mode. \collargs@after{NoVerbatim} % \end{macro} % % \begin{macro}{\collargs@bgroups,\collargs@egroups} % Initialize the lists of the current grouping characters used in the % redefinitions of macros |\collargsVerbatim| and |\collargsVerb| above. % Each entry is of form |\collargs@do|\marg{character code}. These lists % will be populated by |\collargs@make@verbatim|. They may be local, as they % only used within the group opened for a verbatim environment. \def\collargs@bgroups{}% \def\collargs@egroups{}% % \end{macro} % % \begin{macro}{\collargs@cc} % This macro recalls the category code of character |#1|. In % \hologo{LuaTeX}, we simply look up the category code in the original % category code table; in other engines, we have stored the original category % code into |\collargs@cc@|\meta{character code} by % |\collargs@make@verbatim|. (Note that |#1| is a character, not a number.) \ifdefined\luatexversion \def\collargs@cc#1{% \directlua{tex.sprint(tex.getcatcode(\collargs@catcodetable@original, \the\numexpr\expandafter`\csname#1\endcsname\relax))}% } \else \def\collargs@cc#1{% \ifcsname collargs@cc@\the\numexpr\expandafter`\csname#1\endcsname\endcsname \csname collargs@cc@\the\numexpr\expandafter`\csname#1\endcsname\endcsname \else 12% \fi } \fi % \end{macro} % % % \begin{macro}{\collargs@other@bgroup,\collargs@other@egroup,\collargsBraces} % Macros |\collargs@other@bgroup| and % |\collargs@other@egroup| hold the characters of category code ``other'' % which will play the role of grouping characters in the full verbatim mode. % They are usually defined when entering a verbatim mode in % |\collargs@make@verbatim|, but may be also set by the user via % |\collargsBraces| (it is not even necessary to select characters which % indeed have the grouping function in the outside category code regime). % The setting process is indirect: executing |\collargsBraces| merely sets % |\collargs@make@other@groups|, which gets executed by the subsequent % |\collargsVerbatim|, |\collargsVerb| or |\collargsNoVerbatim| (either % directly or via |\collargs@make@verbatim|). \def\collargsBraces#1{% \expandafter\collargs@braces@i\detokenize{#1}\relax } \def\collargs@braces@i#1#2#3\relax{% \def\collargs@make@other@groups{% \def\collargs@other@bgroup{#1}% \def\collargs@other@egroup{#2}% }% } \def\collargs@make@other@groups{} % \end{macro} % % \begin{macro}{\collargs@catcodetable@verbatim,\catcodetable@atletter,\collargs@catcodetable@initex} % We declare several new catcode tables in \hologo{LuaTeX}, the most % important one being |\collargs@catcodetable@verbatim|, where all characters % have category code 12. We only need the other two tables in some formats: % |\collargs@catcodetable@atletter| holds the catcode in effect at the time % of loading the package, and |\collargs@catcodetable@initex| is the % \hologo{iniTeX} table. \ifdefined\luatexversion %<*latex,context> \newcatcodetable\collargs@catcodetable@verbatim %\let\collargs@catcodetable@atletter\catcodetable@atletter %\newcatcodetable\collargs@catcodetable@atletter % %<*plain> \ifdefined\collargs@catcodetable@verbatim\else \chardef\collargs@catcodetable@verbatim=4242 \fi \chardef\collargs@catcodetable@atletter=% \number\numexpr\collargs@catcodetable@verbatim+1\relax \chardef\collargs@catcodetable@initex=% \number\numexpr\collargs@catcodetable@verbatim+2\relax \initcatcodetable\collargs@catcodetable@initex % %\savecatcodetable\collargs@catcodetable@atletter \begingroup \@firstofone{% %\catcodetable\catcodetable@initex %\catcodetable\collargs@catcodetable@initex %\catcodetable\inicatcodes \catcode`\\=12 \catcode13=12 \catcode0=12 \catcode32=12 \catcode`\%=12 \catcode127=12 \def\collargs@do#1{\catcode#1=12 }% \collargs@forrange{`\a}{`\z}% \collargs@forrange{`\A}{`\Z}% \savecatcodetable\collargs@catcodetable@verbatim \endgroup }% \fi % \end{macro} % % \begin{collargskey}{verbatim ranges} % \begin{macro}{\collargsVerbatimRanges,\collargs@verbatim@ranges} % This key and macro set the character ranges to which the verbatim mode % will apply (in \hologo{pdfTeX} and \hologo{XeTeX}), or which will be % inspected for grouping and comment characters (in \hologo{LuaTeX}). In % \hologo{pdfTeX}, the default value |0-255| should really remain % unchanged. \collargsSet{ verbatim ranges/.store in=\collargs@verbatim@ranges, } \def\collargsVerbatimRanges#1{\def\collargs@verbatim@ranges{#1}} \def\collargs@verbatim@ranges{0-255} % \end{macro} % \end{collargskey} % % \begin{macro}{\collargs@make@verbatim} % This macro changes the category code of all characters to ``other'' --- % except the grouping characters in the partial verbatim mode. While doing % that, it also stores (unless we're in \hologo{LuaTeX}) the current category % codes into |\collargs@cc@|\meta{character code} (easily recallable by % |\collargs@cc|), redefines the ``primary'' grouping characters % |\collargs@make@other@bgroup| and |\collargs@make@other@egroup| if % necessary, and ``remembers'' the grouping characters (storing them into % |\collargs@bgroups| and |\collargs@egroups|) and the comment characters % (storing them into |\collargs@comments|). % % In \hologo{LuaTeX}, we can use catcode tables, so we change the category % codes by switching to category code table % |\collargs@catcodetable@verbatim|. In other engines, we have to change the % codes manually. In order to offer some flexibility in \hologo{XeTeX}, we % perform the change for characters in |verbatim ranges|. \ifdefined\luatexversion \def\collargs@make@verbatim{% \directlua{% for from, to in string.gmatch( "\luaescapestring{\collargs@verbatim@ranges}", "(\collargs@percentchar d+)-(\collargs@percentchar d+)" ) do for char = tex.round(from), tex.round(to) do catcode = tex.catcode[char] % For category codes 1, 2 and 14, we have to call macros % |\collargs@make@verbatim@bgroup|, |\collargs@make@verbatim@egroup| % and |\collargs@make@verbatim@comment|, same as for engines other % than \hologo{LuaTeX}. if catcode == 1 then tex.sprint( \number\collargs@catcodetable@atletter, "\noexpand\\collargs@make@verbatim@bgroup{" .. char .. "}") elseif catcode == 2 then tex.sprint( \number\collargs@catcodetable@atletter, "\noexpand\\collargs@make@verbatim@egroup{" .. char .. "}") elseif catcode == 14 then tex.sprint( \number\collargs@catcodetable@atletter, "\noexpand\\collargs@make@verbatim@comment{" .. char .. "}") end end end }% \edef\collargs@catcodetable@original{\the\catcodetable}% \catcodetable\collargs@catcodetable@verbatim % Even in \hologo{LuaTeX}, we switch between the verbatim braces regimes by % hand. \ifcollargs@verbatimbraces \else \def\collargs@do##1{\catcode##1=1\relax}% \collargs@bgroups \def\collargs@do##1{\catcode##1=2\relax}% \collargs@egroups \fi } \else % The non-\hologo{LuaTeX} version: \def\collargs@make@verbatim{% \ifdefempty\collargs@make@other@groups{}{% % The user has executed |\collargsBraces|. We first apply that setting % by executing macro |\collargs@make@other@groups|, and then disable our % automatic setting of the primary grouping characters. \collargs@make@other@groups \def\collargs@make@other@groups{}% \let\collargs@make@other@bgroup\@gobble \let\collargs@make@other@egroup\@gobble }% % Initialize the list of current comment characters. Each entry is of % form |\collargs@do|\marg{character code}. The definition must be global, % because the macro will be used only once we exit the current group (by % |\collargs@fix@cc@from@other@comment|, if at all). \gdef\collargs@comments{}% \let\collargs@do\collargs@make@verbatim@char \expandafter\collargs@forranges\expandafter{\collargs@verbatim@ranges}% } \def\collargs@make@verbatim@char#1{% % Store the current category code of the current character. \ifnum\catcode#1=12 \else \csedef{collargs@cc@#1}{\the\catcode#1}% \fi \ifnum\catcode#1=1 \collargs@make@verbatim@bgroup{#1}% \else \ifnum\catcode#1=2 \collargs@make@verbatim@egroup{#1}% \else \ifnum\catcode#1=14 \collargs@make@verbatim@comment{#1}% \fi % Change the category code of the current character (including the % comment characters). \ifnum\catcode#1=12 \else \catcode#1=12\relax \fi \fi \fi } \fi % \end{macro} % % \begin{macro}{\collargs@make@verbatim@bgroup} % This macro changes the category of % the opening group character to ``other'', but only in the full verbatim % mode. Next, it populates |\collargs@bgroups|, to facilitate the potential % transition into the other verbatim mode. Finally, it executes % |\collargs@make@other@bgroup|, which stores the ``other'' variant of the % current character into |\collargs@other@bgroup|, and automatically disables % itself, so that it is only executed for the first encountered opening group % character --- unless it was already |\relax|ed at the top of % |\collargs@make@verbatim| as a consequence of the user executing % |\collargsBraces|. \def\collargs@make@verbatim@bgroup#1{% \ifcollargs@verbatimbraces \catcode#1=12\relax \fi \appto\collargs@bgroups{\collargs@do{#1}}% \collargs@make@other@bgroup{#1}% } \def\collargs@make@other@bgroup#1{% \collargs@make@char\collargs@other@bgroup{#1}{12}% \let\collargs@make@other@bgroup\@gobble } % \end{macro} % % \begin{macro}{\collargs@make@verbatim@egroup} % Ditto for the closing group character. \def\collargs@make@verbatim@egroup#1{% \ifcollargs@verbatimbraces \catcode#1=12\relax \fi \appto\collargs@egroups{\collargs@do{#1}}% \collargs@make@other@egroup{#1}% } \def\collargs@make@other@egroup#1{% \collargs@make@char\collargs@other@egroup{#1}{12}% \let\collargs@make@other@egroup\@gobble } % \end{macro} % % \begin{macro}{\collargs@make@verbatim@comment} % This macro populates % |\collargs@make@comments@other|. \def\collargs@make@verbatim@comment#1{% \gappto\collargs@comments{\collargs@do{#1}}% } % \end{macro} % % \begin{macro}{\collargs@make@no@verbatim} % This macro switches back to the non-verbatim mode: in \hologo{LuaTeX}, by % switching to the original catcode table; in other engines, by recalling the % stored category codes. \ifdefined\luatexversion \def\collargs@make@no@verbatim{% \catcodetable\collargs@catcodetable@original\relax }% \else \def\collargs@make@no@verbatim{% \let\collargs@do\collargs@make@no@verbatim@char \expandafter\collargs@forranges\expandafter{\collargs@verbatim@ranges}% } \fi \def\collargs@make@no@verbatim@char#1{% % The original category code of a characted was stored into % |\collargs@cc@|\meta{character code} by |\collargs@make@verbatim|. (We don't % use |\collargs@cc|, because we have a number.) \ifcsname collargs@cc@#1\endcsname \catcode#1=\csname collargs@cc@#1\endcsname\relax % We don't have to restore category code 12. \fi } % \end{macro} % % % \subsubsection{Transition between the verbatim and the non-verbatim mode} % \label{sec:code:collargs:fix} % % At the transition from verbatim to non-verbatim mode, and vice versa, we % sometimes have to fix the category code of the next argument token. This % happens when we have an optional argument type in one mode followed by an % argument type in another mode, but the optional argument is absent, or when % an optional, but absent, verbatim argument is the last argument in the % specification. The problem arises because the presence of optional arguments % is determined by looking ahead in the input stream; when the argument is % absent, this means that we have fixed the category code of the next token. % CollArgs addresses this issue by noting the situations where a token receives % the wrong category code, and then does its best to replace that token with % the same character of the appropriate category code. % % \begin{macro}{\ifcollargs@fix@requested} % This conditional is set, globally, % by the optional argument handlers when the argument is in fact absent, % and reset in the central loop after applying the fix if necessary. \newif\ifcollargs@fix@requested % \end{macro} % % \begin{macro}{\collargs@fix} % This macro selects the fixer appropriate to the transition between the % previous verbatim mode (determined by |\ifcollargs@last@verbatim| and % |\ifcollargs@last@verbatimbraces|) and the current verbatim mode (which % is determined by macros |\ifcollargs@verbatim| and % |\ifcollargs@verbatimbraces|); if the category code fix was not % requested (for this, we check |\ifcollargs@fix@requested|), the % macro simply executes the next-code given as the sole argument. % The name of the fixer macro has the form % |\collargs@fix@|\meta{last mode}|to|\meta{current mode}, where % the modes are given by mnemonic codes: |V| = full verbatim, |v| = % partial verbatim, and |N| = non-verbatim. \long\def\collargs@fix#1{% % Going through |\edef| + |\unexpanded| avoids doubling the hashes. \edef\collargs@fix@next{\unexpanded{#1}}% \ifcollargs@fix@requested \letcs\collargs@action{collargs@fix@% \ifcollargs@last@verbatim \ifcollargs@last@verbatimbraces V\else v\fi \else N% \fi to% \ifcollargs@verbatim \ifcollargs@verbatimbraces V\else v\fi \else N% \fi }% \else \let\collargs@action\collargs@fix@next \fi \collargs@action } % \end{macro} % % \begin{macro}{\collargs@fix@NtoN,\collargs@fix@vtov,\collargs@fix@VtoV} % \indentmacrocode % Nothing to do, continue with the next-code. \def\collargs@fix@NtoN{\collargs@fix@next} \let\collargs@fix@vtov\collargs@fix@NtoN \let\collargs@fix@VtoV\collargs@fix@NtoN % \end{macro} % % \begin{macro}{\collargs@fix@Ntov} % We do nothing for the group tokens; for other tokens, we redirect to % |\collargs@fix@NtoV|. \def\collargs@fix@Ntov{% \futurelet\collargs@temp\collargs@fix@cc@to@other@ii } \def\collargs@fix@cc@to@other@ii{% \ifcat\noexpand\collargs@temp\bgroup \let\collargs@action\collargs@fix@next \else \ifcat\noexpand\collargs@temp\egroup \let\collargs@action\collargs@fix@next \else \let\collargs@action\collargs@fix@NtoV \fi \fi \collargs@action } % \end{macro} % % \begin{macro}{\collargs@fix@NtoV} % The only complication here is that we might be in front of a control % sequence that was a result of a previous fix in the other direction. \def\collargs@fix@NtoV{% \ifcollargs@double@fix \ifcollargs@in@second@fix \expandafter\expandafter\expandafter\collargs@fix@NtoV@secondfix \else \expandafter\expandafter\expandafter\collargs@fix@NtoV@onemore \fi \else \expandafter\collargs@fix@NtoV@singlefix \fi } % This is the usual situation of a single fix. We just use |\string| on % the next token here (but note that some situations can't be saved: noone % can bring a comment back to life, or distinguish a newline and a space) \def\collargs@fix@NtoV@singlefix{% \expandafter\collargs@fix@next\string } % If this is the first fix of two, we know |#1| is a control sequence, so it is % safe to grab it. \def\collargs@fix@NtoV@onemore#1{% \collargs@do@one@more@fix{% \expandafter\collargs@fix@next\string#1% }% } % If this is the second fix of the two, we have to check whether the next token % is a control sequence, and if it is, we need to remember it. Afterwards, we % redirect to the single-fix. \def\collargs@fix@NtoV@secondfix{% \if\noexpand\collargs@temp\relax \expandafter\collargs@fix@NtoV@secondfix@i \else \expandafter\collargs@fix@NtoV@singlefix \fi } \def\collargs@fix@NtoV@secondfix@i#1{% \gdef\collargs@double@fix@cs@ii{#1}% \collargs@fix@NtoV@singlefix#1% } % \end{macro} % % \begin{macro}{\collargs@fix@vtoN} % Do nothing for the grouping tokens, redirect to |\collargs@fix@VtoN| for % other tokens. \def\collargs@fix@vtoN{% \futurelet\collargs@token\collargs@fix@vtoN@i } \def\collargs@fix@vtoN@i{% \ifcat\noexpand\collargs@token\bgroup \expandafter\collargs@fix@next \else \ifcat\noexpand\collargs@token\egroup \expandafter\expandafter\expandafter\collargs@fix@next \else \expandafter\expandafter\expandafter\collargs@fix@VtoN \fi \fi } % \end{macro} % % \begin{macro}{\collargs@fix@vtoV} % Redirect group tokens to |\collargs@fix@NtoV|, and do nothing for other % tokens. \def\collargs@fix@vtoV{% \futurelet\collargs@token\collargs@fix@vtoV@i } \def\collargs@fix@vtoV@i{% \ifcat\noexpand\collargs@token\bgroup \expandafter\collargs@fix@NtoV \else \ifcat\noexpand\collargs@token\egroup \expandafter\expandafter\expandafter\collargs@fix@NtoV \else \expandafter\expandafter\expandafter\collargs@fix@next \fi \fi } % \end{macro} % % \begin{macro}{\collargs@fix@Vtov} % Redirect group tokens to |\collargs@fix@VtoN|, and do nothing for other % tokens. |#1| is surely of category 12, so we can safely grab it. \def\collargs@fix@catcode@of@braces@fromverbatim#1{% \ifnum\catcode`#1=1 \expandafter\collargs@fix@VtoN \expandafter#1% \else \ifnum\catcode`#1=2 \expandafter\expandafter\expandafter\collargs@fix@cc@VtoN \expandafter\expandafter\expandafter#1% \else \expandafter\expandafter\expandafter\collargs@fix@next \fi \fi } % \end{macro} % % \begin{macro}{\collargs@fix@VtoN} % This is the only complicated part. Control sequences and comments (but not % grouping characters!) require special attention. We're fine to grab the % token right away, as we know it is of category 12. \def\collargs@fix@VtoN#1{% \ifnum\catcode`#1=0 \expandafter\collargs@fix@VtoN@escape \else \ifnum\catcode`#1=14 \expandafter\expandafter\expandafter\collargs@fix@VtoN@comment \else \expandafter\expandafter\expandafter\collargs@fix@VtoN@token \fi \fi #1% } % \end{macro} % % \begin{macro}{\collargs@fix@VtoN@token} % We create a new character with the current % category code behing the next-code. This works even for grouping % characters. \def\collargs@fix@VtoN@token#1{% \collargs@insert@char\collargs@fix@next{`#1}{\the\catcode`#1}% } % \end{macro} % % \begin{macro}{\collargs@fix@VtoN@comment} % This macro defines a macro which will, % when placed at a comment character, remove the tokens until the end of the % line. The code is adapted from the TeX.SE answer at % \url(https://){tex.stackexchange.com/a/10454/16819} by Bruno Le Floch. \def\collargs@defcommentstripper#1#2{% % We chuck a parameter into the following definition, to grab the (verbatim) % comment character. This is why this macro must be executed precisely % before the (verbatim) comment character. \def#1##1{% \begingroup% \escapechar=`\\% \catcode\endlinechar=\active% % We assign the ``other'' category code to comment characters. Without % this, comment characters behind the first one make trouble: there would % be no |^||^M| at the end of the line, so the comment stripper would % gobble the following line as well; in fact, it would gobble all % subsequent lines containing a comment character. We also make sure to % change the category code of \emph{all} comment characters, even if there % is usually just one. \def\collargs@do####1{\catcode####1=12 }% \collargs@comments \csname\string#1\endcsname% }% \begingroup% \escapechar=`\\% \lccode`\~=\endlinechar% \lowercase{% \expandafter\endgroup \expandafter\def\csname\string#1\endcsname##1~% }{% % I have removed |\space| from the end of the following line. We don't % want it for our application. \endgroup#2% }% } \collargs@defcommentstripper\collargs@fix@VtoN@comment{% \collargs@fix@next } % We don't need the generator any more. \let\collargs@defcommentstripper\relax % \end{macro} % % \begin{macro}{\collargs@fix@VtoN@escape} % An escape character of category code 12 % is the most challenging --- and we won't get things completely right --- as % we have swim further down the input stream to create a control sequence. % This macro will throw away the verbatim escape character |#1|. \def\collargs@fix@VtoN@escape#1{% \ifcollargs@double@fix % We need to do things in a special way if we're in the double-fix % situation triggered by the previous fixing of a control sequence % (probably this very one). In that case, we can't collect it in the usual % way because the entire control sequence is spelled out in verbatim. \expandafter\collargs@fix@VtoN@escape@d \else % This here is the usual situation where the escape character was tokenized % verbatim, but the control sequence name itself will be collected (right % away) in the non-verbatim regime. \expandafter\collargs@fix@VtoN@escape@i \fi } \def\collargs@fix@VtoN@escape@i{% % The sole character forming a control symbol name may be of any category. % Temporarily redefining the category codes of the craziest characters allows % |\collargs@fix@VtoN@escape@ii| to simply grab the following % character. \begingroup \catcode`\\=12 \catcode`\{=12 \catcode`\}=12 \catcode`\ =12 \collargs@fix@VtoN@escape@ii } % The argument is the first character of the control sequence name. \def\collargs@fix@VtoN@escape@ii#1{% \endgroup \def\collargs@csname{#1}% % Only if |#1| is a letter may the control sequence name continue. \ifnum\catcode`#1=11 \expandafter\collargs@fix@VtoN@escape@iii \else % In the case of a control space, we have to throw away the following % spaces. \ifnum\catcode`#1=10 \expandafter\expandafter\expandafter\collargs@fix@VtoN@escape@s \else % We have a control symbol. That means that we haven't peeked ahead and % can thus skip |\collargs@fix@VtoN@escape@z|. \expandafter\expandafter\expandafter\collargs@fix@VtoN@escape@z@i \fi \fi } % We still have to collect the rest of the control sequence name. Braces have % their usual meaning again, so we have to check for them explicitly (and bail % out if we stumble upon them). \def\collargs@fix@VtoN@escape@iii{% \futurelet\collargs@temp\collargs@fix@VtoN@escape@iv } \def\collargs@fix@VtoN@escape@iv{% \ifcat\noexpand\collargs@temp\bgroup \let\collargs@action\collargs@fix@VtoN@escape@z \else \ifcat\noexpand\collargs@temp\egroup \let\collargs@action\collargs@fix@VtoN@escape@z \else \expandafter\ifx\space\collargs@temp \let\collargs@action\collargs@fix@VtoN@escape@s \else \let\collargs@action\collargs@fix@VtoN@escape@v \fi \fi \fi \collargs@action } % If we have a letter, store it and loop back, otherwise finish. \def\collargs@fix@VtoN@escape@v#1{% \ifcat\noexpand#1a% \appto\collargs@csname{#1}% \expandafter\collargs@fix@VtoN@escape@iii \else \expandafter\collargs@fix@VtoN@escape@z\expandafter#1% \fi } % Throw away the following spaces. \def\collargs@fix@VtoN@escape@s{% \futurelet\collargs@temp\collargs@fix@VtoN@escape@s@i } \def\collargs@fix@VtoN@escape@s@i{% \expandafter\ifx\space\collargs@temp \expandafter\collargs@fix@VtoN@escape@s@ii \else \expandafter\collargs@fix@VtoN@escape@z \fi } \def\collargs@fix@VtoN@escape@s@ii{% \expandafter\collargs@fix@VtoN@escape@z\romannumeral-0% } % Once we have collected the control sequence name into |\collargs@csname|, we % will create the control sequence behind the next-code. However, we have two % complications. The minor one is that |\csname| defines an unexisting control % sequence to mean |\relax|, so we have to check whether the control sequence % we will create is defined, and if not, ``undefine'' it in advance. % ^^A todo: do nicer \def\collargs@fix@VtoN@escape@z@i{% \collargs@fix@VtoN@escape@z@maybe@undefine@cs@begin \collargs@fix@VtoN@escape@z@ii }% \def\collargs@fix@VtoN@escape@z@maybe@undefine@cs@begin{% \ifcsname\collargs@csname\endcsname \@tempswatrue \else \@tempswafalse \fi } \def\collargs@fix@VtoN@escape@z@maybe@undefine@cs@end{% \if@tempswa \else \cslet{\collargs@csname}\collargs@undefined \fi } \def\collargs@fix@VtoN@escape@z@ii{% \expandafter\collargs@fix@VtoN@escape@z@maybe@undefine@cs@end \expandafter\collargs@fix@next\csname\collargs@csname\endcsname } % The second complication is much greater, but it only applies to control words % and spaces, and that's why control symbols went directly to the macro above. % Control words and spaces will only get there via a detour through the % following macro. % % The problem is that collecting the control word\slash space name peeked ahead % in the stream, so the character following the control sequence (name) is % already tokenized. We will (at least partially) address this by requesting a % ``double-fix'': until the control sequence we're about to create is consumed % into some argument, each category code fix will fix two ``tokens'' rather % than one. \def\collargs@fix@VtoN@escape@z{% \collargs@if@one@more@fix{% % Some previous fixing has requested a double fix, so let's do it. % Afterwards, redirect to the control symbol code % |\collargs@fix@VtoN@escape@z@i|. It will surely use the correct % |\collargs@csname| because we do the second fix in a group. \collargs@do@one@more@fix\collargs@fix@VtoN@escape@z@i }{% % Remember the collected control sequence. It will be used in % |\collargs@cancel@double@fix|. \collargs@fix@VtoN@escape@z@maybe@undefine@cs@begin \xdef\collargs@double@fix@cs@i{\expandonce{\csname\collargs@csname\endcsname}}% \collargs@fix@VtoN@escape@z@maybe@undefine@cs@end % Request the double-fix. \global\collargs@double@fixtrue % The complication is addressed, redirect to the control symbol finish. \collargs@fix@VtoN@escape@z@ii }% } % When we have to ``redo'' a control sequence, because it was ping-ponged back % into the verbatim mode, we cannot collect it by % |\collargs@fix@VtoN@escape@i|, because it is spelled out entirely in % verbatim. However, we have seen this control sequence before, and remembered % it, so we'll simply grab it. Another complication is that we might be either % at the ``first'' control sequence, whose fixing created all these double-fix % trouble, or at the ``second'' control sequence, if the first one was % immediately followed by another one. But we have remembered both of them: % the first one in |\collargs@fix@VtoN@escape@z|, the second one in % |\collargs@fix@NtoV@secondfix|. \def\collargs@fix@VtoN@escape@d{% \ifcollargs@in@second@fix \expandafter\collargs@fix@VtoN@escape@d@i \expandafter\collargs@double@fix@cs@ii \else \expandafter\collargs@fix@VtoN@escape@d@i \expandafter\collargs@double@fix@cs@i \fi } % We have the contents of either |\collargs@double@fix@cs@i| or % |\collargs@double@fix@cs@ii| here, a control sequence in both cases. \def\collargs@fix@VtoN@escape@d@i#1{% \expandafter\expandafter\expandafter\collargs@fix@VtoN@escape@d@ii \expandafter\string#1\relax } % We have the verbatimized control sequence name in |#2| (|#1| is the escape % character). By storing it into |\collargs@csname|, we pretend we have % collected it. By defining and executing |\collargs@fix@VtoN@escape@d@iii|, % we actually gobble it from the input stream. Finally, we reroute to % |\collargs@fix@VtoN@escape@z|. \def\collargs@fix@VtoN@escape@d@ii#1#2\relax{% \def\collargs@csname{#2}% \def\collargs@fix@VtoN@escape@d@iii#2{% \collargs@fix@VtoN@escape@z }% \collargs@fix@VtoN@escape@d@iii } % This conditional signals a double-fix request. It should be always set % globally, because it is cleared by |\collargs@double@fixfalse| in a group. \newif\ifcollargs@double@fix % This conditional signals that we're currently performing the second fix. \newif\ifcollargs@in@second@fix % Inspect the two conditionals above to decide whether we have to perform % another fix: if so, execute the first argument, otherwise the second one. % This macro is called only from |\collargs@fix@VtoN@escape@z| and % |\collargs@fix@NtoV|, because these are the only two places where we might % need the second fix, ping-ponging a control sequence between the verbatim and % the non-verbatim mode. \def\collargs@if@one@more@fix{% \ifcollargs@double@fix \ifcollargs@in@second@fix \expandafter\expandafter\expandafter\@secondoftwo \else \expandafter\expandafter\expandafter\@firstoftwo \fi \else \expandafter\@secondoftwo \fi } \def\collargs@do@one@more@fix#1{% % We perform the second fix in a group, signalling that we're performing % it. \begingroup \collargs@in@second@fixtrue % Reexecute the fixing routine, at the end, close the group and execute % the given code afterwards. \collargs@fix{% \endgroup #1% }% } % This macro is called from |\collargs@appendarg| to cancel the double-fix % request. \def\collargs@cancel@double@fix{% % |\collargs@appendarg| is only executed when something was actually % consumed. We thus know that at least one of the problematic ``tokens'' is % gone, so the double fix is not necessary anymore. \global\collargs@double@fixfalse % What we have to figure out, still, is whether both problematic ``tokens'' % we consumed. If so, no more fixing is required. But if only one of them % was consumed, we need to request the normal, single, fix for the remaining % ``token''. \begingroup % This will attach the delimiters directly to the argument, so we'll see what % was actually consumed. \collargs@process@arg % We compare what was consumed when collecting the current argument with the % control word that triggered double-fixing. If they match, only the % offending control word was consumed, so we need to set the fix request to % true for the following token. \edef\collargs@temp{\the\collargsArg}% \edef\collargs@tempa{\expandafter\string\collargs@double@fix@cs@i}% \ifx\collargs@temp\collargs@tempa \global\collargs@fix@requestedtrue \fi \endgroup } % \end{macro} % % % \begin{macro}{\collargs@insert@char,\collargs@make@char} % These macros create a character of character code |#2| and category code % |#3|. The first macro inserts it into the stream behind the code in |#1|; % the second one defines the control sequence in |#1| to hold the created % character (clearly, it should not be used for categories 1 and 2). % % We use the facilities of \hologo{LuaTeX}, \hologo{XeTeX} and \hologo{LaTeX} % where possible. In the end, we only have to implement our own macros for % plain \hologo{pdfTeX}. %\ifdefined\luatexversion \def\collargs@insert@char#1#2#3{% \edef\collargs@temp{\unexpanded{#1}}% \expandafter\collargs@temp\directlua{% tex.cprint(\number#3,string.char(\number#2))}% }% \def\collargs@make@char#1#2#3{% \edef#1{\directlua{tex.cprint(\number#3,string.char(\number#2))}}% }% %<*!context> \else \ifdefined\XeTeXversion \def\collargs@insert@char#1#2#3{% \edef\collargs@temp{\unexpanded{#1}}% \expandafter\collargs@temp\Ucharcat #2 #3 }% \def\collargs@make@char#1#2#3{% \edef#1{\Ucharcat#2 #3}% }% \else %<*latex> \ExplSyntaxOn \def\collargs@insert@char#1#2#3{% \edef\collargs@temp{\unexpanded{#1}}% \expandafter\expandafter\expandafter\collargs@temp\char_generate:nn{#2}{#3}% }% \def\collargs@make@char#1#2#3{% \edef#1{\char_generate:nn{#2}{#3}}% }% \ExplSyntaxOff % %<*plain> % The implementation is inspired by |expl3|'s implementation of % |\char_generate:nn|, but our implementation is not expandable, for % simplicity. We first store an (arbitrary) character |^||^@| of category % code $n$ into control sequence |\collargs@charofcat@|$n$, for every % (implementable) category code. \begingroup \catcode`\^^@=1 \csgdef{collargs@charofcat@1}{% \noexpand\expandafter^^@\iffalse}\fi} \catcode`\^^@=2 \csgdef{collargs@charofcat@2}{\iffalse{\fi^^@} \catcode`\^^@=3 \csgdef{collargs@charofcat@3}{^^@} \catcode`\^^@=4 \csgdef{collargs@charofcat@4}{^^@} % As we have grabbed the spaces already, a remaining newline should surely be % fixed into a |\par|. \csgdef{collargs@charofcat@5}{\par} \catcode`\^^@=6 \csxdef{collargs@charofcat@6}{\unexpanded{^^@}} \catcode`\^^@=7 \csgdef{collargs@charofcat@7}{^^@} \catcode`\^^@=8 \csgdef{collargs@charofcat@8}{^^@} \csgdef{collargs@charofcat@10}{\noexpand\space} \catcode`\^^@=11 \csgdef{collargs@charofcat@11}{^^@} \catcode`\^^@=12 \csgdef{collargs@charofcat@12}{^^@} \catcode`\^^@=13 \csgdef{collargs@charofcat@13}{^^@} \endgroup \def\collargs@insert@char#1#2#3{% % Temporarily change the lowercase code of |^||^@| to the requested character % |#2|. \begingroup \lccode`\^^@=#2\relax % We'll have to close the group before executing the next-code. \def\collargs@temp{\endgroup#1}% % |\collargs@charofcat@|\meta{requested category code} is f-expanded first, % leaving us to lowercase |\expandafter\collargs@temp^||^@|. Clearly, % lowercasing |\expandafter\collargs@temp| is a no-op, but lowercasing % |^||^@| gets us the requested character of the requested category. % |\expandafter| is executed next, and this gets rid of the conditional for % category codes 1 and 2. \expandafter\lowercase\expandafter{% \expandafter\expandafter\expandafter\collargs@temp \romannumeral-`0\csname collargs@charofcat@\the\numexpr#3\relax\endcsname }% } % This macro cannot not work for category code 6 (because we assign the % result to a macro), but no matter, we only use it for category code 12 % anyway. \def\collargs@make@char#1#2#3{% \begingroup \lccode`\^^@=#2\relax % Define |\collargs@temp| to hold |^||^@| of the appropriate category. \edef\collargs@temp{% \csname collargs@charofcat@\the\numexpr#3\relax\endcsname}% % Preexpand the second |\collargs@temp| so that we lowercase % |\def\collargs@temp{^||^@}|, with |^||^@| of the appropriate category. \expandafter\lowercase\expandafter{% \expandafter\def\expandafter\collargs@temp\expandafter{\collargs@temp}% }% \expandafter\endgroup \expandafter\def\expandafter#1\expandafter{\collargs@temp}% } % \fi \fi % % \end{macro} % %\resetatcatcode %\stopmodule %\protect % \end{macrocode} % % Local Variables: % TeX-engine: luatex % TeX-master: "doc/memoize-code.tex" % TeX-auto-save: nil % End: