% \begin{meta-comment} % % $Id: at.dtx,v 1.3 1996/11/19 20:46:55 mdw Exp $ % % Allow @-commands % % (c) 1995 Mark Wooding % %----- Revision history ----------------------------------------------------- % % $Log: at.dtx,v $ % Revision 1.3 1996/11/19 20:46:55 mdw % Entered into RCS % % % \end{meta-comment} % % \begin{meta-comment} %% %% at package -- support for `@' commands' %% Copyright (c) 1996 Mark Wooding %% %% This program is free software; you can redistribute it and/or modify %% it under the terms of the GNU General Public License as published by %% the Free Software Foundation; either version 2 of the License, or %% (at your option) any later version. %% %% This program is distributed in the hope that it will be useful, %% but WITHOUT ANY WARRANTY; without even the implied warranty of %% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the %% GNU General Public License for more details. %% %% You should have received a copy of the GNU General Public License %% along with this program; if not, write to the Free Software %% Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. %% % \end{meta-comment} % % \begin{meta-comment} %<+package>\NeedsTeXFormat{LaTeX2e} %<+package>\ProvidesPackage{at} %<+package> [1996/05/02 1.3 @-command support (MDW)] % \end{meta-comment} % % \CheckSum{355} %% \CharacterTable %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z %% Digits \0\1\2\3\4\5\6\7\8\9 %% Exclamation \! Double quote \" Hash (number) \# %% Dollar \$ Percent \% Ampersand \& %% Acute accent \' Left paren \( Right paren \) %% Asterisk \* Plus \+ Comma \, %% Minus \- Point \. Solidus \/ %% Colon \: Semicolon \; Less than \< %% Equals \= Greater than \> Question mark \? %% Commercial at \@ Left bracket \[ Backslash \\ %% Right bracket \] Circumflex \^ Underscore \_ %% Grave accent \` Left brace \{ Vertical bar \| %% Right brace \} Tilde \~} %% % % \begin{meta-comment} % %<*driver> \input{mdwtools} \describespackage{at} \aton \atlet p=\package \atdef at{\package{at}} \atdef={\mbox{-}} \atdef-{@@@=} \atlet.=\syntax \mdwdoc % % % \end{meta-comment} % % \section{User guide} % % The @at\ package is an attempt to remove a lot of tedious typing that % ends up in \LaTeX\ documents, by expanding the number of short command % names available. The new command names begin with the `|@|' character, % rather than the conventional `|\|', so you can tell them apart. % % The package provides some general commands for defining @-commands, and % then uses them to define some fairly simple ones which will be useful to % most people. % % The rules for @-command names aren't terribly complex: % \begin{itemize} % \item If the first character of the name is a letter, then the command name % consists of all characters up to, but not including, the first % nonletter. Spaces following the command name are ignored. % \item If the first character of the name is a backslash, then the @-command % name consists of the control sequence introduced by the backslash. % \item Otherwise, the command name consists only of that first character. % Spaces following the name are not ignored, unless that character % was itself a space character. % \end{itemize} % % Usually, digits are not considered to be letters. However, the % \package{at} package will consider digits to be letters if you give it the % \textsf{digits} option in the |\usepackage| command. (Note that this % only affects the \package{at} package; it won't change the characters % allowed in normal command names.) % % \DescribeMacro{\atallowdigits} % \DescribeMacro{\atdisallowdigits} % You can enable and disable digits being considered as letters dynamically. % The |\atallowdigits| command allows digits to be used as letters; % |\atdisallowdigits| prevents this. Both declarations follow \LaTeX's % usual scoping rules. Both of these commands have corresponding % environments with the same names (without the leading `|\|', obviously). % % \subsection{Defining @-commands} % % \DescribeMacro{\newatcommand} % \DescribeMacro{\renewatcommand} % The |\newatcommand| command will define a new @-command using a syntax % similar to |\newcommand|. For example, you could define % \begin{listing} %\newatcommand c[1]{\chapter{#1}} % \end{listing} % to make @.{"@c{""}"} equivalent to @.{"\\chapter{""}"}. % % A |\renewatcommand| is also provided to redefine existing commands, should % the need arise. % % \DescribeMacro{\atdef} % For \TeX\ hackers, the |\atdef| command defines @-commands using a syntax % similar to \TeX's built-in |\def|. % % As an example, the following command makes @.{"@/""/"} write its % argument \ in italics: % \begin{listing} %\atdef/#1/{\textit{#1}} % \end{listing} % The real implementation of the |@/|\dots|/| command is a bit more % complex, and is given in the next section. % % You can use all of \TeX's features for defining the syntax of your % command. (See chapter~20 of @/The \TeX book/ for more details.) % % \DescribeMacro{\atlet} % Since |\atdef| is provided to behave similarly to |\def|, @at\ provides % |\atlet| which works similarly to |\let|. For example you can say % \begin{listing} %\atlet!=\index % \end{listing} % to allow the short |@!| to behave exactly like |\index|. % % Note that all commands defined using these commands are robust even if you % use fragile commands in their definitions. Unless you start doing very % strange things, @-commands never need |\protect|ing. % % \subsection{Predefined @-commands} % % A small number of hopefully useful commands are provided by default. % These are described in the table below: % % \bigskip \begin{center} \begin{tabular}{lp{3in}} \hline % \bf Command & \bf Meaning \\ \hline % @.{"@@"} & Typesets an `@@' character. \\ % @.{"@/""/"} & In text (LR or paragraph) mode, typesets its % argument emphasised. In maths mode, it % always chooses italics. \\ % @.{"@*""*"} & Typesets its argument \ in bold. \\ % @.{"@i{""}"} & Equivalent to `@.{"\\index{""}"}'. \\ % @.{"@I{""}"} & As for |@i|, but also writes its argument % to the document. \\ \hline % \end{tabular} \end{center} \bigskip % % Package writers should not rely on any predefined @-commands -- they're % provided for users, and users should be able to redefine them without % fear of messing anything up. (This includes the `standard' commands % provided by the @at\ package, by the way. They're provided in the vague % hope that they might be useful, and as examples.) % % \implementation % % \section{Implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \subsection{Options handling} % % We need a switch to say whether digits should be allowed. Since this % is a user thing, I'll avoid |\newif| and just define the thing by hand. % % \begin{macrocode} \def\atallowdigits{\let\ifat@digits\iftrue} \def\atdisallowdigits{\let\ifat@digits\iffalse} % \end{macrocode} % % Now define the options. % % \begin{macrocode} \DeclareOption{digits}{\atallowdigits} \DeclareOption{nodigits}{\atdisallowdigits} \ExecuteOptions{nodigits} \ProcessOptions % \end{macrocode} % % \subsection{How the commands work} % % Obviously we make the `@@' character active. It inspects the next % character (or argument, actually -- it can be enclosed in braces for % longer commands, although this is a bit futile), and builds the command % name from that. % % The |\at| command is equivalent to the active `@@' character always. % % % \subsection{Converting command names} % % We need to be able to read an @-command name, and convert it to a normal % \TeX\ control sequence. First, we declare some control sequences for % braces, which we need later. % % \begin{macrocode} \begingroup \catcode`\<1 \catcode`\>2 \catcode`\{12 \catcode`\}12 \gdef\at@lb<{> \gdef\at@rb<}> \gdef\at@spc< > \endgroup % \end{macrocode} % % I'll set up some helper routines now, to help me read the command % names. The way this works is that we |\futurelet| the token into % |\@let@token|. These routines will then sort out what to do next. % % \begin{macro}{\at@test} % % Given an |\if|\dots\ test, does its first or second argument. % % \begin{macrocode} \def\at@test#1\then{% #1\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\at@ifcat} % % Checks the category code of the current character. If it matches the % argument, it does its second argument, otherwise it does the third. % % \begin{macrocode} \def\at@ifcat#1{\at@test\ifcat#1\noexpand\@let@token\then} % \end{macrocode} % % \end{macro} % % \begin{macro}{\at@ifletter} % % This routine tests the token to see if it's a letter, and if so adds % it to the token list and does the first argument; otherwise it does the % second argument. It accepts digits as letters if the switch is turned % on. % % There's some fun later, so I'll describe this slowly. First, we compare % the category code to a letter, and if we have a match, we know we're done; % we need to pick up the letter as an argument. If the catcode is `other', % we must compare with numbers to see if it's in range. % % \begin{macrocode} \def\at@ifletter#1#2{% \at@ifcat x% {\at@ifletter@ii{#1}}% {\at@ifcat 0% {\at@ifletter@i{#1}{#2}}% {#2}% }% } % \end{macrocode} % % Right. It's `other' (so it's safe to handle as a macro argument) and we % need to know if it's a digit. This is a little tricky: I use |\if| to % compare two characters. The first character is~`1' or~`0' depending on the % `digit' switch; the second is~`1' or~`x' depending on whether it's actually % a digit. They'll only match if everything's worked out OK. % % \begin{macrocode} \def\at@ifletter@i#1#2#3{% \at@test\if% \ifat@digits1\else0\fi% \ifnum`#3<`0x\else\ifnum`#3>`9x\else1\fi\fi% \then% {\at@ifletter@ii{#1}{#3}}% {#2#3}% } % \end{macrocode} % % Right; we have the character, so add it to the list and carry on. % % \begin{macrocode} \def\at@ifletter@ii#1#2{\toks@\expandafter{\the\toks@#2}#1} % \end{macrocode} % % \end{macro} % % Now we define the command name reading routines. We have @/almost/ the % same behaviour as \TeX, although we can't support `|%|' characters for % reasons to do with \TeX's tokenising algorithm. % % \begin{macro}{\at@read@name} % % The routine which actually reads the command name works as follows: % \begin{enumerate} % \item Have a peek at the next character. If it's a left or right brace, % then use the appropriate character. % \item If the character is not a letter, just use the character (or whole % control sequence. % \item Finally, if it's a letter, keep reading letters until we find one % that wasn't. % \end{enumerate} % % First, we do some setting up and read the first character % % \begin{macrocode} \def\at@read@name#1{% \let\at@next=#1% \toks@{}% \futurelet\@let@token\at@rn@i% } % \end{macrocode} % % Next, sort out what to do, based on the category code. % % \begin{macrocode} \def\at@rn@i{% \def\@tempa{\afterassignment\at@rn@iv\let\@let@token= }% \at@ifletter% {\futurelet\@let@token\at@rn@iii}% {\at@ifcat\bgroup% {\toks@\expandafter{\at@lb}\@tempa}% {\at@ifcat\egroup% {\toks@\expandafter{\at@rb}\@tempa}% {\at@ifcat\at@spc% {\toks@{ }\@tempa}% {\at@rn@ii}% }% }% }% } % \end{macrocode} % % Most types of tokens can be fiddled using |\string|. % % \begin{macrocode} \def\at@rn@ii#1{% \toks@\expandafter{\string#1}% \at@rn@iv% } % \end{macrocode} % % We've found a letter, so we should check for another one. % % \begin{macrocode} \def\at@rn@iii{% \at@ifletter% {\futurelet\@let@token\at@rn@iii}% {\@ifnextchar.\at@rn@iv\at@rn@iv}% } % \end{macrocode} % % Finally, we need to pass the real string, as an argument, to the % macro. We make |\@let@token| relax, since it might be something which will % upset \TeX\ later, e.g., a |#| character. % % \begin{macrocode} \def\at@rn@iv{% \let\@let@token\relax% \expandafter\at@next\csname at.\the\toks@\endcsname% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\at@cmdname} % % Given a control sequence, work out which @-command it came from. % % \begin{macrocode} \def\at@cmdname#1{\expandafter\at@cmdname@i\string#1\@@foo} % \end{macrocode} % % Now extract the trailing bits. % % \begin{macrocode} \def\at@cmdname@i#1.#2\@@foo{#2} % \end{macrocode} % % \end{macro} % % \begin{macro}{\at@decode} % % The |\at@decode| macro takes an extracted @-command name, and tries to % execute the correct control sequence derived from it. % % \begin{macrocode} \def\at@decode#1{% \at@test\ifx#1\relax\then{% \PackageError{at}{Unknown @-command `@\at@cmdname#1'}{% The @-command you typed wasn't recognised, so I've ignored it. }% }{% #1% }% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\@at} % % We'd like a measure of compatibility with @p{amsmath}. The @-commands % provided by @p{amsmath} work only in maths mode, so this gives us a way of % distinguishing. If the control sequence |\Iat| is defined, and we're in % maths mode, we'll call that instead of doing our own thing. % % \begin{macrocode} \def\@at{% \def\@tempa{\at@read@name\at@decode}% \ifmmode\ifx\Iat\not@@defined\else% \let\@tempa\Iat% \fi\fi% \@tempa% } % \end{macrocode} % % \end{macro} % % % \subsection{Defining new commands} % % \begin{macro}{\at@buildcmd} % % First, we define a command to build these other commands: % % \begin{macrocode} \def\at@buildcmd#1#2{% \expandafter\def\csname\expandafter \@gobble\string#1@decode\endcsname##1{#2##1}% \edef#1{% \noexpand\at@read@name% \expandafter\noexpand% \csname\expandafter\@gobble\string#1@decode\endcsname% }% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\newatcommand} % \begin{macro}{\renewatcommand} % \begin{macro}{\provideatcommand} % \begin{macro}{\atdef} % \begin{macro}{\atshow} % % Now we define the various operations on @-commands. % % \begin{macrocode} \at@buildcmd\newatcommand\newcommand \at@buildcmd\renewatcommand\renewcommand \at@buildcmd\provideatcommand\providecommand \at@buildcmd\atdef\def \at@buildcmd\atshow\show % \end{macrocode} % % \end{macro} % \end{macro} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\atlet} % % |\atlet| is rather harder than the others, because we want to allow people % to say things like @.{"\\atlet""=@"}. The following hacking % does the trick. I'm trying very hard to duplicate |\let|'s behaviour with % respect to space tokens here, to avoid any surprises, although there % probably will be some differences. In particular, |\afterassignment| % won't work in any sensible way. % % First, we read the name of the @-command we're defining. We also open % a group, to stop messing other people up, and make `@@' into an `other' % token, so that it doesn't irritatingly look like its meaning as a control % sequence. % % \begin{macrocode} \def\atlet{% \begingroup% \@makeother\@% \at@read@name\atlet@i% } % \end{macrocode} % % Put the name into a scratch macro for later use. Now see if there's an % equals sign up ahead. If not, this will gobble any spaces in between the % @-command name and the argument. % % \begin{macrocode} \def\atlet@i#1{% \def\at@temp{#1}% \@ifnextchar=\atlet@ii{\atlet@ii=}% } % \end{macrocode} % % Now we gobble the equals sign (whatever catcode it is), and peek at the % next token up ahead using |\let| with no following space. % % \begin{macrocode} \def\atlet@ii#1{\afterassignment\atlet@iii\global\let\at@gnext=} % \end{macrocode} % % The control sequence |\at@gnext| is now |\let| to be whatever we want the % @-command to be, unless it's picked up an `@@' sign. If it has, we've % eaten the |@| token, so just read the name and pass it on. Otherwise, % we can |\let| the @-command directly to |\at@gnext|. There's some % nastiness here to make |\the\toks@| expand before we close the group and % restore its previous definition. % % \begin{macrocode} \def\atlet@iii{% \if @\noexpand\at@gnext% \expandafter\at@read@name\expandafter\atlet@iv% \else% \expandafter\endgroup% \expandafter\let\at@temp= \at@gnext% \fi% } % \end{macrocode} % % We've read the source @-command name, so just copy the definitions over. % % \begin{macrocode} \def\atlet@iv#1{% \expandafter\endgroup% \expandafter\let\at@temp=#1% } % \end{macrocode} % % \end{macro} % % % \subsection{Robustness of @-commands} % % We want all @-commands to be robust. We could leave them all being % fragile, although making robust @-commands would then be almost impossible. % There are two problems which we must face: % % \begin{itemize} % % \item The `|\@at|' command which scans the @-command name is (very) % fragile. I could have used |\DeclareRobustCommand| for it (and in % fact I did in an earlier version), but that doesn't help the other % problem at all. % % \item The `name' of the @-command may contain active characters or control % sequences, which will be expanded at the wrong time unless we do % something about it now. % % \end{itemize} % % We must also be careful not to introduce extra space characters into any % files written, because spaces are significant in @-commands. Finally, % we have a minor problem in that most auxiliary files are read in with % the `@@' character set to be a letter. % % \begin{macro}{\at} % % Following the example of \LaTeX's `short' command handling, we'll define % |\at| to decide what to do depending on what |\protect| looks like. If % we're typesetting, we just call |\@at| (above) and expect it to cope. % Otherwise we call |\at@protect|, which scoops up the |\fi| and the |\@at|, % and inserts other magic. % % \begin{macrocode} \def\at{\ifx\protect\@typeset@protect\else\at@protect\fi\@at} % \end{macrocode} % % \end{macro} % % \begin{macro}{\at@protect} % % Since we gobbled the |\fi| from the above, we must put that back. We then % need to do things which are more complicated. If |\protect| is behaving % like |\string|, then we do one sort of protection. Otherwise, we assume % that |\protect| is being like |\noexpand|. % % \begin{macrocode} \def\at@protect\fi#1{% \fi% \ifx\protect\string% \expandafter\at@protect@string% \else% \expandafter\at@protect@noexpand% \fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\at@protect@string} % % When |\protect| is |\string|, we don't need to be able to recover the % original text particularly accurately -- it's for the user to look at. % Therefore, we just output a $|@|_{11}$ and use |\string| on the next % token. This must be sufficient, since we only allow multi-token command % names if the first token is a letter (code~11). % % \begin{macrocode} \def\at@protect@string{@\string} % \end{macrocode} % % \end{macro} % % \begin{macro}{\at@protect@noexpand} % % This is a little more complex, since we're still expecting to be executed % properly at some stage. However, there's a cheeky dodge we can employ % since the |\at| command is thoroughly robustified (or at least it will be % by the time we've finished this). All |\@unexpandable@protect| does % is confer repeated robustness on a fragile command. Since our command % is robust, we don't need this and we can get away with just using a % single |\noexpand|, both for the |\@at@| command and the following token % (which we must robustify, because no-one else can do it for us -- if % anyone tries, they end up using the |@\protect| command which is rather % embarassing). % % I'll give the definition, and then examine how this expands in various % cases. % % \begin{macrocode} \def\at@protect@noexpand{\noexpand\@at@ @\noexpand} \def\@at@#1{\at} % \end{macrocode} % % A few points, before we go into the main examination of the protection. % I've inserted a $|@|_{11}$ token, which is gobbled by |\@at@| when the % thing is finally expanded fully. This prevents following space tokens % in an |\input| file from being swallowed because they follow a control % sequence. (I can't use the normal $|@|_{13}$ token, because when files % like the |.aux| file are read in, |@| is given code~11 by % |\makeatletter|.) % % \setbox0\hbox{|@at@|} % Now for a description of why this works. When |\at| is expanded, it works % out that |\protect| is either |\noexpand| or |\@unexpandable@protect|, and % becomes |\at@protect@noexpand|. Because of the |\noexpand| tokens, this % stops being expanded once it reaches $\fbox{\box0}\,|@|_{11}\,x$ (where % $x$ is the token immediately following the $|@|_{13}$ character). If this % is expanded again, for example in another |\edef|, or in a |\write| or a % |\mark|, the |\@at@| wakes up, gobbles the following |@| (whatever catcode % it is -- there may be intervening |\write| and |\input| commands) and % becomes |\at|, and the whole thing can start over again. % % \end{macro} % % % \subsection{Enabling and disabling @-commands} % % \begin{macro}{\aton} % % We define the |\aton| command to enable all of our magic. We store % the old catcode in the |\atoff| command, make `@@' active, and make it % do the stuff. % % \begin{macrocode} \def\aton{% \ifnum\catcode`\@=\active\else% \edef\atoff{\catcode`\noexpand\@\the\catcode`\@}% \catcode`\@\active% \lccode`\~`\@% \lowercase{\let~\at}% \fi% } % \end{macrocode} % % \end{macro} % % \begin{macro}{\atoff} % % The |\atoff| command makes `@@' do the stuff it's meant to. We remember % the old catcode and revert to it. This is largely unnecessary. % % \begin{macrocode} \def\atoff{\catcode`\@12} % \end{macrocode} % % \end{macro} % % \begin{macro}{\makeatother} % % Now we make our active `@@' the default outside of package files. % % \begin{macrocode} \let\makeatother\aton % \end{macrocode} % % \end{macro} % % And we must make sure that the user can use all of our nice commands. % Once the document starts, we allow @-commands. % % \begin{macrocode} \AtBeginDocument{\aton} % \end{macrocode} % % \begin{macro}{\dospecials} % \begin{macro}{\@sanitize} % % We must add the `@@' character to the various specials lists. % % \begin{macrocode} \expandafter\def\expandafter\dospecials\expandafter{\dospecials\do\@} \expandafter\def\expandafter\@sanitize\expandafter{% \@sanitize\@makeother\@} % \end{macrocode} % % \end{macro} % \end{macro} % % \subsection{Default @-commands} % % We define some trivial examples to get the user going. % % \begin{macrocode} \expandafter\chardef\csname at.@\endcsname=`\@ \atdef*#1*{\ifmmode\mathbf{#1}\else\textbf{#1}\fi} \atdef/#1/{\ifmmode\mathit{#1}\else\emph{#1}\fi} \atlet i=\index \atdef I#1{#1\index{#1}} % % \end{macrocode} % % \hfill Mark Wooding, \today % % \Finale % \endinput