diff options
Diffstat (limited to 'Master/texmf-dist/source/latex/mdwtools/mdwtab.dtx')
-rw-r--r-- | Master/texmf-dist/source/latex/mdwtools/mdwtab.dtx | 5441 |
1 files changed, 5441 insertions, 0 deletions
diff --git a/Master/texmf-dist/source/latex/mdwtools/mdwtab.dtx b/Master/texmf-dist/source/latex/mdwtools/mdwtab.dtx new file mode 100644 index 00000000000..84d67f07242 --- /dev/null +++ b/Master/texmf-dist/source/latex/mdwtools/mdwtab.dtx @@ -0,0 +1,5441 @@ +% \begin{meta-comment} +% +% $Id: mdwtab.dtx,v 1.8 1996/12/09 23:20:42 mdw Exp $ +% +% Another rewrite of the tabular environment, and maths alignments +% +% (c) 1996 Mark Wooding +% +%----- Revision history ----------------------------------------------------- +% +% $Log: mdwtab.dtx,v $ +% Revision 1.8 1996/12/09 23:20:42 mdw +% (\tab@setstrut): Fixed so that it uses \dimen\tw@ for the strut depth, +% as advertised. +% +% Revision 1.7 1996/11/29 21:59:16 mdw +% Fixed a little formatting mistake in a syntax diagram, and switched over +% to the new syntax diagram commands on the grounds that they're slightly +% less messy. Maybe. +% +% Revision 1.6 1996/11/19 20:54:33 mdw +% Entered into RCS +% +% +% \end{meta-comment} +% +% \begin{meta-comment} <general public licence> +%% +%% mdwtab package -- another rewrite of the tabular environment, etc. +%% 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 preambles> +%<+mdwtab>\NeedsTeXFormat{LaTeX2e} +%<+mdwtab>\ProvidesPackage{mdwtab} +%<+mdwtab> [1998/04/28 1.9 Table typesetting with style] +%<+mathenv>\NeedsTeXFormat{LaTeX2e} +%<+mathenv>\ProvidesPackage{mathenv} +%<+mathenv> [1998/04/28 1.9 Various maths environments] +% \end{meta-comment} +% +% \CheckSum{2758} +%% \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{mdwtab} +\describespackage{mathenv} +\addcontents{lot}{\listoftables} +\mdwdoc +%</driver> +% +% \end{meta-comment} +% +%^^A------------------------------------------------------------------------- +% \renewcommand{\tabstyle}{\small} +% +% \section{User guide} +% +% +% The \package{mdwtab} package contains a reimplementation of the standard +% \LaTeX\ \env{tabular} and \env{array} environments. This is not just an +% upgraded version: it's a complete rewrite. It has several advantages over +% the official \package{array} package (not raw \LaTeX's, which is even less +% nice), and it's more-or-less compatible. Most of these are rather +% technical, I'll admit. +% +% \begin{itemize} +% +% \item The newcolumn system is properly and perfectly integrated into the +% system. There are now \emph{no} `primitive' column types -- all the +% standard types are created as user-defined columns. +% +% \item You can define entirely different table-like environments using the +% equipment here. It's still hard work, although less so than before. +% I'll do an example of this some time. +% +% \item Construction of the preamble is generally much tidier. I've used +% token registers rather than |\edef|, and it's all done very nicely. +% +% \item Fine spacing before and after rules (described by DEK as `a mark of +% quality') is now utterly trivial, since the preamble-generator will +% store the appropriate information. +% +% \item You can use \env{array} in LR and paragraph modes without having +% to surround it with `|$|' signs. +% +% \item Usually you don't want tables in the middle of paragraphs. For these +% cases, I've provided a simpler way to position the table +% horizontally. +% +% \item Footnotes work properly inside \env{tabular} environments (hoorah!). +% You can `catch' footnotes using the \env{minipage} environment if +% you like. (It uses an internal version of the \package{footnote} +% package to handle footnotes, which doesn't provide extra goodies like +% the \env{footnote} environment; you'll need to load the full package +% explicitly to get them.) +% +% \item Standard \LaTeX\ tabular environments have a problem with lining up +% ruled tables. The |\firsthline| command given in the \textit{\LaTeX\ +% Companion} helps a bit, but it's not really good enough, and besides, +% it doesn't \emph{actually} line the text up right after all. The +% \package{mdwtab} package does the job properly to begin with, so you +% don't need to worry. +% +% \end{itemize} +% +% I've tested the following packages with \package{mdwtab}, and they all +% work. Some of the contortions required to make them work weren't pleasant, +% but you don't need to know about them. By a strange coincidence, all the +% packages were written by David Carlisle. Anyway, here's the list: +% \begin{itemize} +% \item The quite nice \package{dcolumn} package. +% \item The more useful \package{delarray} package. +% \item The rather spiffy \package{hhline} package. +% \item The truly wonderful \package{tabularx} package. +% \item The utterly magnificent \package{longtable} package. +% \end{itemize} +% +% Note that I've looked at \package{supertabular} as well: it won't work, so +% use \package{longtable} instead, 'cos it's much better. +% +% +% \subsection{The downside} +% +% There's no such thing as a free lunch. The \package{mdwtab} environment +% is not 100\% compatible with the \env{tabular} environment found in +% \LaTeXe\ or the \package{array} package. +% +% The differences between \package{mdwtab} and \LaTeXe's \env{tabular} +% environment are as follows: +% +% \begin{itemize} \synshorts \let\`=\lq +% +% \item The vertical spacing in \env{array} environments is different to +% that in \env{tabular} environments. This produces more attractive +% results in most mathematical uses of \env{array}s, in the author's +% opinion. The spacing can be modified by playing with length +% parameters. +% +% \item The presence of horizontal and vertical rules will alter the spacing +% of the table (so a pair of columns separated by a `|' is wider than +% a pair with no separation by "\\arrayrulewidth". This does mean that +% horizontal and vertical rules match up properly -- the usual \LaTeX\ +% environment makes the horizontal rules stop just short of the edge +% of the table, making an ugly mess (check out the \textit{\LaTeX\ +% book} if you don't believe me -- page~62 provides a good example). +% The \package{array} package handles rules in the same way as +% \package{mdwtab}. +% +% \setbox0=\hbox{\footnotesize`\\def\\xcs{\\tabskip=\\fill}'} +% \setbox2=\hbox{\footnotesize`...@{\\span\\xcs}...'} +% \item In common with the \package{array} package, there are some +% restrictions on the use of the "\\extracolsep" command in preambles: +% you may use at most one "\\extracolsep" command in each `@' or `!' +% expression. Also, you can't say +% \begin{listing} +%\newcommand{\xcs}{\extracolsep{\fill}} +% \end{listing} +% and then expect something like `...@{\\xcs}...' to actually work -- +% the "\\extracolsep" mustn't be hidden inside any other +% commands. Because things like `@' expressions aren't expanded at +% the time, "\\extracolsep" has to be searched and processed +% \`by hand'.\footnote{^^A +% All \cs{extracolsep} does is modify the \cs{tabskip} glue, so +% if you were an evil \TeX\ hacker like me, you could just say +% \unhbox0\ and put \unhbox2\ in your preamble. That'd work nicely. +% It also works with the \package{array} package.} +% +% \item Control sequences (commands) in a table's preamble aren't expanded +% before the preamble is read. In fact, commands in the preamble are +% considered to be column types, and their names are entirely +% independent of normal \LaTeX\ commands. No column types of this +% nature have yet been defined\footnote{^^A +% There used to be an internal \cs{@magic} type used by +% \env{eqnarray}, but you're not supposed to know about that. +% Besides, it's not there any more.} +% but the possibility's always there. Use the "\\newcolumntype" or +% "\\coldef" commands to define new column types. +% +% \item The preamble parsing works in a completely different way. There is +% a certain amount of compatibility provided, although it's heavily +% geared towards keeping \package{longtable} happy and probably won't +% work with other packages. +% +% \item Obscure constructs which were allowed by the old preamble parser but +% violate the syntax shown in the next section (e.g., `|@{}|' to +% suppress the "\\doublerulesep" space between two vertical rules, +% described in \textit{The \LaTeX\ Companion} as \`a misuse of the +% `@{...}' qualifier') are now properly outlawed. You will be given +% an error message if you attempt to use such a construction. +% +% \item The `*' forms (which repeat column types) are now expanded at a +% different time. Previously, preambles like `c@*{4}{{:}@}{--}c' +% were considered valid (the example would expand to +% `c@{:}@{:}@{:}@{:}@{--}c'), because `*'s were expanded before the +% preamble was actually parsed. In the new system, `*' is treated +% just like any other preamble character (it just has a rather odd +% action), and preambles like this will result in an error (and +% probably a rather confusing one). +% +% \end{itemize} +% +% There are also several incompatibilities between \package{mdwtab} and +% \package{array}: +% +% \begin{itemize} \synshorts \let\`=\lq +% +% \item Because of the way "\\newcolumntype" works in the \package{array} +% package, a horrid construction like +% \begin{listing} +%\newcolumntype{x}{{:}} +%\begin{tabular}{|c!xc|} +% \end{listing} +% is considered to be valid, and is interpreted as `|c!{:}c|'. My +% reading of pages~54 and~55 of the \textit{\LaTeX\ book} tells me +% that this sort of thing is forbidden in normal \LaTeX\ commands. +% The \package{mdwtab} preamble parser now treats column type letters +% much more like commands with the result that the hacking above won't +% work any more. The construction above would actually be interpreted +% as `|c!{x}c|' (i.e., the `x' column type wouldn't be expanded to +% `{:}' because the parser noticed that it was the argument to the +% `!' modifier\footnote{^^A +% This is a direct result of the way \TeX\ treats undelimited +% arguments. See chapters~5 and~20 of \textit{The \TeX book} for +% more information about how grouping affects argument reading.}). +% +% \item Most of the points above, particularly those relating to the +% handling of the preamble, also apply to the \package{array} package. +% it's not such an advance over the \LaTeXe\ version as everyone said +% it was. +% +% \end{itemize} +% +% +% \subsection{Syntax} +% +% \DescribeEnv{tabular} +% \DescribeEnv{tabular*} +% \DescribeEnv{array} +% So that everyone knows where I stand, here's a complete syntax for my +% version of the \env{tabular} environment, and friends +% +% \begin{grammar} +% +% <tabular-env> ::= \[[ +% "\\begin" +% \begin{stack} +% "{tabular}" \\ "{tabular*}" "{" <length> "}" \\ +% "{array}" \\ "{smarray}" +% \end{stack} +% \[ "[" <position-arg> "]" \] +% "{" <preamble> "}" <text> +% "\\end" +% \( "{tabular}" \\ "{tabular*}" \\ "{array}" \\ "{smarray}" \) +% \]] +% +% <position-arg> ::= (see below) +% +% <preamble> ::= \[[ +% <first-column> +% \[ \< <column> \> \] +% \]] +% +% <first-column> ::= \[[ \[ <rule> \] <column> \]] +% +% <column> ::= \[[ +% \[ <spacing> \] \[ \< <user-pre-text> \> \] <column-type> +% \[ \< <user-post-text> \> \] \[ <spacing> \] \[ <rule> \] +% \]] +% +% <spacing> ::= \[[ "@" "{" <text> "}" \]] +% +% <user-pre-text> ::= \[[ ">" "{" <text> "}" \]] +% +% <column-type> ::= \[[ +% \begin{stack} +% \[ "T" \\ "M" \] \( "l" \\ "c" \\ "r" \) \\ +% \( "p" \\ "m" \\ "b" \) "{" <length> "}" \\ +% "#" "{" <raw-pre-text> "}" "{" <raw-post-text> "}" +% \end{stack} +% \]] +% +% <user-post-text> ::= \[[ "<" "{" <text> "}" \]] +% +% <rule> ::= \[[ \( "|" \\ "!" "{" <text> "}" \) \]] +% +% \end{grammar} +% +% If you examine the above very carefully, you'll notice a slight deviation +% from the original -- an |@|-expression \emph{following} a rule is +% considered to be part of the \emph{next} column, not the current one. This +% is, I think, an almost insignificant change, and essential for some of the +% new features. You'll also notice the new |#| column type form, which +% allows you to define new real column types instead of just modifying +% existing ones. It's not intended for direct use in preambles -- it's +% there mainly for the benefit of people who know what they're doing and +% insist on using |\newcolumntype| anyway. +%% +% The actual column types are shown in table~\ref{tbl:columns}. +% +% \begin{table} +% \begin{tabular}[C]{| >{\synshorts} c | m{3in} |} \hlx{hv[1]} +% +% \multicolumn{2}{|c|}{\bf Column types} \\ \hlx{v[1]hv} +% \bf Name & \bf Meaning \\ \hlx{vhv.} +% "l" & Left aligned text (\env{tabular}) or +% equation (\env{array}). \\ \hlx{.} +% "c" & Centred text (\env{tabular}) or +% equation (\env{array}). \\ \hlx{.} +% "r" & Right aligned text (\env{tabular}) or +% equation (\env{array}). \\ \hlx{vhv.} +% "Ml", "Mc" and "Mr" & Left, centre and right aligned +% equations.* \\ \hlx{.} +% "Tl", "Tc" and "Tr" & Left, centre and right aligned +% text.* \\ \hlx{vhv.} +% "p{"<width>"}" & Top aligned paragraph with the given +% width. \\ \hlx{.} +% "m{"<width>"}" & Vertically centred paragraph with +% the given width. \\ \hlx{.} +% "b{"<width>"}" & Bottom aligned paragraph with the +% given width. \\ \hlx{vhv.} +% "#{"<pre>"}{"<post>"}" & User defined column type: +% \<pre> is inserted before the +% cell entry, \<post> is inserted +% afterwards.* \\ \hlx{vhhv[1]} +% +% \multicolumn{2}{|c|}{\bf Other modifier characters} \\ \hlx{v[1]hv} +% \bf Name & \bf Meaning \\ \hlx{vhv.} +% "|" & Inserts a vertical rule between +% columns. \\ \hlx{.} +% "!{"<text>"}" & Inserts \<text> between columns, +% treating it as a vertical rule. \\ \hlx{vhv.} +% "@{"<text>"}" & Inserts \<text> instead of the +% usual intercolumn space. \\ \hlx{vhv.} +% ">{"<text>"}" & Inserts \<text> just before the +% actual column entry. \\ \hlx{.} +% "<{"<text>"}" & Inserts \<text> just after the +% actual column entry. \\ \hlx{vhv.} +% "*{"<count>"}{"<chars>"}" & Inserts \<count> +% copies of the \<chars> into the +% preamble. \\ \hlx{vhs} +% +% \multicolumn{2}{@{}l}{* This column type is a new feature} +% \end{tabular} +% +% \caption{\package{array} and \package{tabular} column types and modifiers} +% \label{tbl:columns} +% \end{table} +% +% Now that's sorted everything out, there shouldn't be any arguments at all +% about what a column means. +% +% The lowercase \<position-arg>s \lit{t}, \lit{c} and \lit{b} do exactly +% what they did before: control the vertical positioning of the table. The +% uppercase ones control the \emph{horizontal} positioning -- this is how you +% create \emph{unboxed} tables. You can only create unboxed tables in +% paragraph mode. +% +% Note that unboxed tables still can't be broken across pages. Use +% the \package{longtable} package for this, because it already does an +% excellent job. +% +% \DescribeMacro{\tabpause} +% One thing you can to with unboxed tables, however, is to `interrupt' them, +% do some normal typesetting, and then continue. This is achieved by the +% |\tabpause| command: its argument is written out in paragraph mode, and +% the table is continued after the argument finishes. +% Note that it isn't a real argument as far as commands like |\verb| are +% concerned -- they'll work inside |\tabpause| without any problems. +% +% \DescribeMacro{\vline} +% The |\vline| command draws a vertical rule the height of the current table +% cell (unless the current cell is being typeset in paragraph mode -- it +% only works in the simple LR-mode table cells, or in \lit{@} or \lit{!} +% modifiers). It's now been given an optional argument which gives the +% width of the rule to draw: +% +% { \let\tabstyle=\relax +% \begin{demo}{An example of \cmd\vline} +%\large +%\begin{tabular} +% {| c !{\vline[2pt]} c | c |} +% \hlx{hv} +% \bf A & \it B & \sf C \\ +% \hlx{vhv} +% \bf D & \it E & \sf F \\ +% \hlx{vh} +%\end{tabular} +% \end{demo} +% } +% +% \DescribeMacro{smarray} +% You've probably noticed that there's an unfamiliar environment mentioned +% in the syntax shown above. The \env{smarray} environment produces a +% `small' array, with script size cells rather than the normal full text +% size cells. I've seen examples of this sort of construction\footnote{^^A +% There's a nasty use of \env{smallmatrix} in the |testmath.tex| file which +% comes with the \package{amslatex} distribution. It's actually there to +% simulate a `smallcases' environment, which the \package{mathenv} package +% includes, based around \env{smarray}.} +% being implemented by totally unsuitable commands. Someone may find it +% handy. +% +% +% \subsection{An updated \cs{cline} command} +% +% \DescribeMacro{\cline} +% The standard \LaTeX\ |\cline| command has been updated. As well as just +% passing a range of columns to draw lines through, you can now pass a comma +% separated list of column numbers and ranges: +% +% \begin{grammar} +% <cline-cmd> ::= \[[ +% "\\cline" "{" \< <number> \[ "-" <number> \] \\ "," \> "}" +% \]] +% \end{grammar} +% +% The positioning of the horizontal lines has also been improved a bit, so +% that they meet up with the vertical lines properly. Displays like the one +% in the example below don't look good unless this has been done properly. +% +% {\let\tabstyle\relax +% \begin{demo}[w]{A \cs{cline} example} +%\newcommand{\mc}{\multicolumn{1}} +%\begin{tabular}[C]{|c|c|c|c|} \cline{2,4} +% \mc{c|}{one} & two & three & four \\ \hline +% five & six & seven & \mc{c}{eight} \\ \cline{1,3} +%\end{tabular} +% \end{demo} +% } +% +% \subsection{Spacing control} +% +% One of the most irritating things about \LaTeX's tables is that there isn't +% enough space around horizontal rules. Donald Knuth, in \textit{The +% \TeX book}, describes addition of some extra vertical space here as `a mark +% of quality', and since \TeX\ was designed to produce `beautiful documents' +% it seems a shame that \LaTeX\ doesn't allow this to be done nicely. Well, +% it does now. +% +% \DescribeMacro{\vgap} +% The extra vertical space is added using a command |\vgap|, with the +% following syntax: +% +% \begin{grammar} +% +% <vgap-cmd> ::= \[[ +% "\\vgap" \[ "[" <which-cols> "]" \] "{" <length> "}" +% \]] +% +% <which-cols> ::= \[[ \< <number> \[ "-" <number> \] \\ "," \> \]] +% +% \end{grammar} +% +% This command must appear either immediately after the beginning of the +% table or immediately after the |\\| which ends a row. (Actually, there are +% other commands which also have this requirement -- you can specify a +% collection of them wherever you're allowed to give any one.) It adds some +% vertical space (the amount is given by the \<length>) to the table, +% making sure that the vertical rules of the table are extended correctly. +% +% The |\vgap| command relies on information stored while your table preamble +% is being examined. However, it's possible that you might not want some +% of the rules drawn (e.g., if you've used |\multicolumn|). The optional +% \<which-cols> argument allows you to specify which rules are \emph{not} +% to be drawn. You can specify either single column numbers or ranges. The +% rule at the very left hand side is given the number~0; the rules at the +% end of column~$n$ are numbered~$n$. It's easy really. +% +% \DescribeMacro{\hlx} +% Using |\vgap| is all very well, but it's a bit cumbersome, and takes up a +% lot of typing, especially when combined with |\hline| commands. The |\hlx| +% command tries to tidy things. +% +% The syntax is simple: +% \begin{grammar} +% +% <hlx-cmd> ::= \[[ +% "\\hlx" "{" +% \begin{rep} +% \begin{stack} +% "h" \\ +% \tok{"v["<which-cols>"]["<length>"]"} \\ +% \tok{"s["<length>"]"} \\ +% \tok{"c{"<which-cols>"}"} \\ +% "b" \\ +% \tok{"/["<number>"]"} \\ +% "." +% \end{stack} +% \end{rep} +% "}" +% \]] +% +% \end{grammar} +% The argument works a bit like a table preamble, really. Each letter is a +% command. The following are supported: +% +% \begin{description} +% +% \item [\lit*{h}] Works just like |\hline|. If you put two adjacent to each +% other, a gap will be put between them. +% +% \item [\lit*{v[}\<which-cols>\lit*{][}\<length>\lit*{]}] Works +% like \syntax{"\\vgap["<which-cols>"]{"<length>"}"}. If the +% \<length> is omitted, the value of |\doublerulesep| is used. +% This usually looks right. +% +% \item [\lit*{s[}\<length>\lit*{]}] Leaves a vertical gap with the +% given size. If you omit the \<length> then |\doublerulesep| is +% used. This is usually right. +% +% \item [\lit*{c\char`\{}\<which-cols>\lit*{\char`\}}] Works just like +% |\cline|. +% +% \item [\lit*{b}] Inserts a backspace the width of a rule. This is useful +% when doing \package{longtable}s. +% +% \item [\lit*{/[}\<number>\lit*{]}] Allows a page break in a table. Don't +% use this except in a \env{longtable} environment. The \<number> +% works exactly the same as it does in the |\pagebreak| command, +% except that the default is 0, which just permits a break without +% forcing it. +% +% \item [\lit*{.}] (That's a dot) Starts the next row of the table. No +% more characters may follow the dot, and no |\hline|, |\hlx|, |\vgap| +% or |\multicolumn| commands may be used after it. You don't have to +% include it, and most of the time it's totally useless. It can be +% handy for some macros, though. I used it in (and in fact added it +% especially for) the table of column types. +% +% \end{description} +% +% An example of the use of |\hlx| is given, so you can see what's going on. +% +% \begin{figure} +% \let\tabstyle\relax +% \begin{demo}[w]{Beautiful table example} +%\newcommand{\zerowidth}[1]{\hbox to 0pt{\hss#1\hss}} +%\setlength{\tabcolsep}{1.5em} +%\begin{tabular}[C]{| r | c | r |} \hlx{hv[1,2]} +% \multicolumn{3}{|c|}{\bf AT\&T Common Stock} \\ \hlx{v[1,2]hv} +% \multicolumn{1}{|c|}{\zerowidth{\bf Year}} & +% \multicolumn{1}{c|}{\zerowidth{\bf Price}} & +% \multicolumn{1}{c|}{\zerowidth{\bf Dividend}} \\ \hlx{vhv} +% 1971 & 41--54 & \$2.60 \\ +% 2 & 41--54 & 2.70 \\ +% 3 & 46--55 & 2.87 \\ +% 4 & 40--53 & 3.24 \\ +% 5 & 45--52 & 3.40 \\ +% 6 & 51--59 & .95\rlap{*} \\ \hlx{vhs} +% \multicolumn{3}{@{}l}{* (first quarter only)} +%\end{tabular} +% \end{demo} +% \end{figure} +% +% +% \subsection{Creating beautiful long tables} +% +% You can use the |\vgap| and |\hlx| commands with David Carlisle's +% stunning \package{longtable} package. However, there are some things you +% should be away of to ensure that your tables always come out looking +% lovely. +% +% The \package{longtable} package will break a table at an |\hline| command, +% leaving a rule at the bottom of the page and another at the top of the +% next page. This means that a constructions like |\hlx{vhv}| will be +% broken into something like |\hlx{vh}| at the bottom of the page and +% |\hlx{hv}| at the top of the next. You need to design the table headers +% and footers with this in mind. +% +% However, there appears to be a slight problem:\footnote +% {You might very well call it a bug. I couldn't possibly comment.} +% if the footer starts with an |\hline|, and a page is broken at an |\hline|, +% then you get an extra thick rule at the bottom of the page. This is a bit +% of a problem, because if the rule isn't there in the footer and you get +% a break between two rows \emph{without} a rule between them, then the page +% looks very odd. +% +% If you want to do ruled longtables, I'd recommend that you proceed as +% follows: +% \begin{itemize} +% \item End header sections with an |\hlx{vh}|. +% \item Begin footer sections with an |\hlx{bh}|. +% \item Begin the main table with |\hlx{v}|. +% \item Insert |\hlx{vhv}| commands in the main table body as usual. +% \end{itemize} +% If \package{longtable} gets modified appropriately, the use of the \lit{b} +% command won't be necessary. +% +% Here's an example of the sort of thing you'd type. +% +% \begin{listinglist} \listingsize +% \verb"\begin{longtable}[c]{|c|l|} \hlx{hv}" \\ +% \verb"\bf Heading & \bf Also heading \\ \hlx{vh}" \\ +% \verb"\endhead" \\ +% \verb"\hlx{bh}" \\ +% \verb"\endfoot" \\ +% \verb"\hlx{v}" \\ +% \verb"First main & table line \\ \hlx{vhv}" \\ +% \verb"Lots of text & like this \\ \hlx{vhv}" \\ +% \null\quad\vdots \\ +% \verb"Lots of text & like this \\ \hlx{vhv}" \\ +% \verb"Last main & table line \\ \hlx{vh}" \\ +% \verb"\end{longtable}" +% \end{listinglist} +% +% +% \subsection{Rules and vertical positioning} +% +% In the \LaTeXe\ and \package{array.sty} versions of \env{tabular}, you run +% into problems if you try to use ruled tables together with the \lit{[t]} or +% \lit{[b]} position specifiers -- the top or bottom rule ends up being +% nicely lined up with the text baseline, giving you an effect which is +% nothing like the one you expected. The \textit{\LaTeX\ Companion} gives +% two commands |\firsthline| and |\lasthline| which are supposed to help with +% this problem. (These commands have since migrated into the \package{array} +% package.) Unfortunately, |\firsthline| doesn't do its job properly -- +% it gets the text position wrong by exactly the width of the table rules. +% +% The \package{mdwtab} package makes all of this automatic. It gets the +% baseline positions exactly right, whether or not you use rules. Earlier +% versions of this package required that you play with a length parameter +% called |\rulefudge|; this is no longer necessary (or even possible -- the +% length parameter no longer exists). The package now correctly compensates +% for all sorts of rules and |\vgap|s at the top and bottom of a table and +% it gets the positioning right all by itself. You've never had it so good. +% +% +% \subsection{User serviceable parts} +% +% There are a lot of parameters which you can modify in order to make arrays +% and tables look nicer. They are all listed in table~\ref{tbl:config}. +% +% \begin{table} +% \begin{tabular}[C]{| l | m{3in} |} \hlx{hv} +% \bf Parameter & \bf Meaning \\ \hlx{vhv} +% |\tabstyle| & A command executed at the beginning of +% a \env{tabular} or \env{tabular$*$} +% environment. By default does nothing. +% Change using |\renewcommand|. \\ \hlx{vhv} +% |\extrarowheight| & A length added to the height of every +% row, used to stop table rules +% overprinting ascenders. Default 0\,pt. +% Usage is deprecated now: use |\hlx| +% instead. \\ \hlx{vhv} +% |\tabextrasep| & Extra space added between rows in a +% \env{tabular} or \env{tabular$*$} +% environment (added \emph{before} any +% following |\hline|). Default 0\,pt. \\ +% |\arrayextrasep| & Analogous to |\tabextrasep|, but for +% \env{array} environments. Default +% 1\,jot (3\,pt). \\ +% |\smarrayextrasep| & Analogous to |\tabextrasep|, but for +% \env{smarray} environments. Default +% 1\,pt. \\ \hlx{vhv} +% |\tabcolsep| & Space added by default on each side of +% a table cell (unless suppressed by an +% \lit{@}-expression) in \env{tabular} +% environments. Default is defined by +% your document class. \\ +% |\arraycolsep| & Analogous to |\tabcolsep|, but for +% \env{array} environments. Default is +% defined by your document class. \\ +% |\smarraycolsep| & Analogous to |\tabcolsep|, but for +% \env{smarray} environments. Default +% is 3\,pt. \\ \hlx{vhv} +% |\arrayrulewidth| & The width of horizontal and vertical +% rules in tables. \\ +% |\doublerulesep| & Space added between two adjacent +% vertical or horizontal rules. Also +% used by |\hlx{v}|. \\ \hlx{vhv} +% |\arraystretch| & Command containing a factor to +% multiply the default row height. +% Default is defined by your document +% class (usually 1). \\ \hlx{vh} +% \end{tabular} +% +% \caption{Parameters for configuring table environments} +% \label{tbl:config} +% +% \end{table} +% +% +% \subsection{Defining column types} +% +% \DescribeMacro{\newcolumntype} +% The easy way to define new column types is using |\newcolumntype|. It +% works in more or less the same way as |\newcommand|: +% +% \begin{grammar} +% +% <new-col-type-cmd> ::= \[[ +% "\\newcolumntype" +% "{" <column-name> "}" +% \[ "[" <num-args> "]" \] +% \[ "[" <default-arg> "]" \] +% "{" <first-column> \[ \< <column> \> \] "}" +% \]] +% +% \end{grammar} +% +% (The \env{array.sty} implementation doesn't accept the \<default-arg> +% argument. I've no idea why not, 'cos it was very easy to implement.) +% +% \DescribeMacro{\colset} +% This implementation allows you to define lots of different sets of columns. +% You can change the current set using the |\colset| declaration: +% \begin{grammar} +% <colset-cmd> ::= \[[ "\\colset" "{" <set-name> "}" \]] +% \end{grammar} +% This leaves a problem, though: at any particular moment, the current +% column set could be anything, since other macros and packages can change +% it. +% +% \DescribeMacro{\colpush} +% \DescribeMacro{\colpop} +% What actually happens is that a stack of column sets is maintained. The +% |\colset| command just replaces the item at the top of the stack. The +% command |\colpush| pushes its argument onto the top of the stack, making +% it the new current set. The corresponding |\colpop| macro (which doesn't +% take any arguments) removes the top item from the stack, reinstating the +% previous current column set. +% +% \begin{grammar} +% <colpush-cmd> ::= \[[ "\\colpush" "{" <set-name> "}" \]] +% <colpop-cmd> ::= \[[ "\\colpop" \]] +% \end{grammar} +% +% The macros which manipulate the column set stack work \emph{locally}. +% The contents of the stack are saved when you open a new group. +% +% To make sure everyone behaves themselves properly, these are the rules for +% using the column set stack: +% +% \begin{itemize} +% +% \item Packages defining column types must ensure that they preserve the +% current column set. Either they must push their own column type +% and pop it off when they're finished defining columns, or they must +% avoid changing the stack at all, and use the optional arguments to +% |\coldef| and |\collet|. +% +% \item Packages must not assume that any particular column set is current +% unless they have made sure of it themselves. +% +% \item Packages must ensure that they pop exactly as much as they push. +% There isn't much policing of this (perhaps there should be more), +% so authors are encouraged to behave responsibly. +% +% \item Packages must change the current column set (using |\colset|) when +% they start up their table environment. This will be restored when +% the environment closes. +% +% \end{itemize} +% +% \DescribeMacro{\coldef} +% |\newcolumntype| is probably enough for most purposes. However, Real +% \TeX nicians, and people writing new table-generating environments, require +% something lower-level. +% +% \begin{grammar} +% <coldef-cmd> ::= \[[ +% "\\coldef" +% \[ "[" <set-name> "]" \] +% <col-name> <arg-template> "{" <replacement-text> "}" +% \]] +% \end{grammar} +% +% Note that this defines a column type in the current colset. It works +% almost exactly the same way as \TeX's primitive |\def|. There is a +% potential gotcha here: a |\tab@mkpream| token is inserted at the end of +% your replacement text. If you need to read an optional argument or +% something, you'll need to gobble this token before you carry on. The +% |\@firstoftwo| macro could be handy here: +% \begin{listing} +%\coldef x{\@firstoftwo{\@ifnextchar[\@xcolumn@i\@xcolumn@ii}}} +% \end{listing} +% This isn't a terribly pretty state of affairs, and I ought to do something +% about it. I've not seen any use for an optional argument yet, though. +% Note that if you do gobble the |\tab@mkpream|, it's your responsibility to +% insert another one at the very end of your macro's expansion (so that +% further preamble characters can be read). +% +% The replacement text is inserted directly. It's normal to insert preamble +% elements here. There are several to choose from: +% +% \begin{description} +% +% \item [Column items] provide the main `meat' of a column. You insert a +% column element by saying +% \syntax{"\\tabcoltype{"<pre-text>"}{"<post-text>"}"}. +% The user's text gets inserted between these two. (So do user pre- +% and post-texts. Bear this in mind.) +% +% \item [User pre-text items] work like the \lit{>} preamble command. You +% use the \syntax{"\\tabuserpretype{"<text>"}"} command to insert it. +% User pre-texts are written in \emph{reverse} order between the +% pre-text of the column item and the text from the table cell. +% +% \item [User post-text items] work like the \lit{<} preamble command. You +% use the \syntax{"\\tabuserposttype{"<text>"}"} command to insert it. +% Like user pre-texts, user post-texts are written in reverse order, +% between the table cell text and the column item post-text. +% +% \item [Space items] work like the \lit{@} preamble command. They're +% inserted with the \syntax{"\\tabspctype{"<text>"}"} command. +% +% \item [Rule items] work like the `\verb"|"' and \lit{!} commands. You +% insert them with the \syntax{"\\tabruletype{"<text>"}"} command. +% Note that the text is inserted by |\vgap| too, so it should contain +% things which adjust their vertical size nicely. If you really need +% to, you can test |\iftab@vgap| to see if you're in a |\vgap|. +% +% \end{description} +% +% \DescribeMacro{\collet} +% As well as defining columns, you can copy definitions (rather like |\let| +% allows you to copy macros). The syntax is like this: +% +% \begin{grammar} +% +% <collet-cmd> ::= \[[ +% \[ "[" <set-name> "]" \] <col-name> \[ "=" \] \[ "[" <set-name> "]" \] +% <col-name> +% \]] +% +% \end{grammar} +% +% (In other words, you can copy defintions from other column sets.) +% +% +% \subsection{Defining new table-generating environments} +% +% Quite a few routines are provided specifically to help you to define new +% environments which do alignment in a nice way. +% +% \subsubsection{Reading preambles} +% +% The main tricky bit in doing table-like environments is parsing preambles. +% No longer. +% +% \DescribeMacro{\tab@readpreamble} +% \DescribeMacro{\tab@doreadpream} +% The main parser routine is called |\tab@doreadpream|. Given a user +% preamble string as an argument, it will build an |\halign| preamble to +% return to you. However, the preamble produced won't be complete. This is +% because you can actually make multiple calls to |\tab@doreadpream| with +% bits of user preambles. The |\newcolumntype| system uses this mechanism, +% as does the \lit{*} (repeating) modifier. When there really is no more +% preamble to read, you need to \emph{commit} the heldover tokens to the +% output. The |\tab@readpreamble| routine will do this for you -- given a +% user preamble, it builds a complete output from it. +% +% A token register |\tab@preamble| is used to store the generated preamble. +% Before starting, you must iniitialise this token list to whatever you want. +% There's another token register, |\tab@shortline|, which is used to store +% tokens used by |\vgap|. For each column in the table, the list contains +% an |\omit| (to override the standard preamble) and an |\hfil| space taking +% up most of the column. Finally, for each rule item in the user preamble, +% the shortline list contains an entry of the form: +% \begin{quote} \synshorts +% "\\tab@ckr{"<column-number>"}{"<rule-text>"}" +% \end{quote} +% This is used to decide whether to print the rule or an empty thing of the +% same width. You probably ought to know that the very first column does +% \emph{not} have a leading |\omit| -- this is supplied by |\vgap| so that +% it can then look for optional arguments. +% +% \DescribeMacro{\tab@initread} +% As well as initialising |\tab@preamble| and emptying |\tab@shortline|, +% there are several other operations required to initialise a preamble read. +% These are all performed by the |\tab@initread| macro, although you may want +% to change some of the values for your specific application. For reference, +% the actions performed are: +% \begin{itemize} +% \item initialising the parser state by setting $|\tab@state| = +% |\tab@startstate|$; +% \item clearing the token lists |\tab@preamble| and |\tab@shortlist|; +% \item initialising the macros |\tab@tabtext|, |\tab@midtext|, and +% |\tab@multicol| to their default values of `|&|', +% `|\ignorespaces#\unskip|' and the empty token list respectively.^^A +% \footnote{^^A +% These are macros rather than token lists to avoid hogging all +% the token list registers. Actually, the package only allocates +% two, although it does use almost all of the temporary registers as +% well. Also, there's a lie: \cs{unskip} is too hamfisted to remove +% trailing spaces properly; I really use a macro called +% \cs{@maybe@unskip}} +% \item clearing the internal token list registers |\tab@pretext|, +% |tab@userpretext| and |\tab@posttext|; +% \item clearing the column counter |\tab@columns| to zero; +% \item clearing the action performed when a new column is started (by making +% the |\tab@looped| macro equal to |\relax|; this is used to make +% |\multicolumn| macro raise an error if you try to do more than one +% column); and +% \item setting up some other switches used by the parser (|\iftab@rule|, +% |\iftab@initrule| and |\iftab@firstcol|, all of which are set to be +% |true|). +% \end{itemize} +% +% The macro |\tab@multicol| is used by the |\multicolumn| command to insert +% any necessary items (e.g., struts) before the actual column text. If you +% set this to something non-empty, you should probably consider adding a +% call to the macro to the beginning of |\tab@preamble|. +% +% When parsing is finally done, the count register |\tab@columns| contains +% the number of columns in the alignment. Don't corrupt this value, because +% it's used for handling |\hline| commands. +% +% \subsubsection{Starting new lines} +% +% The other messy bit required by table environments is the newline command +% |\\|. There are nasty complications involved with starting new lines, some +% of which can be handled by this package, and some on which I can only give +% advice. +% +% \DescribeMacro{\tab@cr} +% The optional arguments and star-forms etc. can be read fairly painlessly +% using the |\tab@cr| command: +% +% \begin{grammar} +% <tabcr-cmd> ::= \[[ +% "\\tab@cr" <command> "{" <non-star-text> "}" "{" <star-text> "}" +% \]] +% \end{grammar} +% +% This will call your \<command> with two arguments. The first is the +% contents of the optional argument, or `|\z@|' if there wasn't one. The +% second is either \<star-text> or \<non-star-text> depending on +% whether the user wrote the $*$-form or not. +% +% Somewhere in your \<command>, you'll have to use the |\cr| primitive to +% end the table row. After you've done this, you \emph{must} ensure that you +% don't do anything that gets past \TeX's mouth without protecting it -- +% otherwise |\hline| and co.\ won't work. I usually wrap things up in a +% |\noalign| to protect them, although there are other methods. Maybe. +% +% You might like to have a look at the \env{eqnarray} implementation provided +% to see how all this gets put into practice. +% +% +% \subsection{The \env{mathenv} package alignment environments} +% +% The \env{mathenv} package provides several environments for aligning +% equations in various ways. They're mainly provided as a demonstration of +% the table handling macros in \package{mdwtab}, so don't expect great +% things. If you want truly beautiful mathematics, use +% \package{amsmath}.\footnote{^^A +% Particularly since nice commands like \cmd\over\ are being reactivated +% in a later release of \package{amsmath}.} +% However, the various environments do nest in an approximately useful way. +% I also think that the \env{matrix} and \env{script} environments provided +% here give better results than their \package{amsmath} equivalents, and +% they are certainly more versatile. +% +% \subsubsection{The new \env{eqnarray} environment} +% +% \DescribeEnv{eqnarray} +% \DescribeEnv{eqnarray*} +% As an example of the new column defining features, and because the original +% isn't terribly good, I've included a rewritten version of the +% \env{eqnarray} environment. The new implementation closes the gap between +% \env{eqnarray} and \AmSTeX\ alignment features. It's in a separate, +% package called \package{mathenv}, to avoid wasting your memory. +% +% \begin{grammar} +% +% <eqnarray-env> ::= \[[ +% <begin-eqnarray> \< <row> \\ "\\\\" \> <end-eqnarray> +% \]] +% +% <begin-eqnarray> ::= \[[ +% "\\begin" \( "{eqnarray}" \\ "{eqnarray*}" \) +% \[ "[" \< <eqa-column> \> "]" \] +% \]] +% +% <eqa-column> ::= \[[ +% \[ "q" \\ ":" \] +% \[ \< ">" "{" <pre-text> "}" \> \] +% \begin{stack} +% \[ "T" \] \( "r" \\ "c" \\ "l" \) \\ +% "L" \\ +% "x" +% \end{stack} +% \[ \< "<" "{" <post-text> "}" \> \] +% \]] +% +% <end-eqnarray> ::= \[[ +% "\\end" \begin{stack} "{eqnarray}" \\ "{eqnarray*}" \end{stack} +% \]] +% +% \end{grammar} +% +% Descriptions of the various column types are given in +% table~\ref{tbl:eqnarray}. +% +% \begin{table} +% \begin{tabular}[C]{| >{\synshorts} c | m{3in} |} \hlx{hv[1]} +% +% \multicolumn{2}{|c|}{\bf Column types} \\ \hlx{v[1]hv} +% \bf Name & \bf Meaning \\ \hlx{vhv.} +% "l" & Left aligned piece of equation. \\ \hlx{.} +% "c" & Centred piece of equation. \\ \hlx{.} +% "x" & Centred or flush-left whole equation +% (depending on \textsf{fleqn} option). \\ \hlx{.} +% "r" & Right aligned piece of equation. \\ \hlx{vhv.} +% "L" & Left aligned piece of equation whose +% width is considered to be 2\,em. \\ \hlx{vhv.} +% "Tl", "Tc" and "Tr" & Left, centre and right aligned +% text. \\ \hlx{vhhv[1]} +% +% \multicolumn{2}{|c|}{\bf Other modifier characters} \\ \hlx{v[1]hv} +% \bf Name & \bf Meaning \\ \hlx{vhv.} +% ":" & Leaves a big gap between equations. +% By default, the `chunks' separated by +% \lit{:}s are equally spaced on the +% line. \\ \hlx{.} +% "q" & Inserts 1\,em of space \\ \hlx{vhv.} +% ">{"<text>"}" & Inserts \<text> just before the +% actual column entry. \\ \hlx{.} +% "<{"<text>"}" & Inserts \<text> just after the +% actual column entry. \\ \hlx{vhv.} +% "*{"<count>"}{"<chars>"}" & Inserts \<count> +% copies of the \<chars> into the +% preamble. \\ \hlx{vh} +% \end{tabular} +% +% \caption{\package{eqnarray} column types and modifiers} +% \label{tbl:eqnarray} +% \end{table} +% +% The default preamble, if you don't supply one of your own, is \lit{rcl}. +% Most of the time, \lit{rl} is sufficient, although compatibility is more +% important to me. +% +% By default, there is no space between columns, which makes formul\ae\ in an +% \env{eqnarray} environment look just like formul\ae\ typeset on their own, +% except that things get aligned in columns. This is where the default +% \env{eqnarray} falls down: it leaves |\arraycolsep| space between each +% column making the thing look horrible. +% +% An example would be good here, I think. This one's from exercise 22.9 of +% the \textit{\TeX book}. +% +% \begin{demo}[w]{Simultaneous equations} +%\begin{eqnarray}[*3{rc}rl] +% 10w & + & 3x & + & 3y & + & 18z & = 1 \\ +% 6w & - & 17x & & & - & 5z & = 2 +%\end{eqnarray} +% \end{demo} +% +% Choosing a more up-to-date example, here's some examples from the +% \textit{\LaTeX\ Companion}. +% +% \begin{demo}[w]{Lots of equations} +%\begin{eqnarray}[rl:rl:lq] +% V_i &= v_i - q_i v_j, & X_i &= x_i - q_i x_j, & +% U_i = u_i, \qquad \mbox{for $i \ne j$} \\ +% V_j &= v_j, & X_j &= x_j & +% U_j u_j + \sum_{i \ne j} q_i u_i. \label{eq:A} +%\end{eqnarray} +% \end{demo} +% +% \begin{figure} +% \begin{demo}[w]{Plain text column and \cs{tabpause}} +%\begin{eqnarray}[rlqqTl] +% x &= y & by (\ref{eq:A}) \\ +% x' &= y' & by definition \\ +%\tabpause{and} +% x + x' &= y + y' & by Axiom~1 +%\end{eqnarray} +% \end{demo} +% \end{figure} +% +% The new features also mean that you don't need to mess about with +% |\lefteqn| any more. This is handled by the \lit{L} column type: +% +% \begin{demo}{Splitting example} +%\begin{eqnarray*}[Ll] +% w+x+y+z = \\ +% & a+b+c+d+e+ \\ +% & f+g+h+i+j +%\end{eqnarray*} +% \end{demo} +% +% Finally, just to prove that the spacing's right at last, here's another one +% from the \textit{Companion}. +% +% \begin{demo}{Spacing demonstration} +%\begin{equation} +% x^2 + y^2 = z^2 +%\end{equation} +%\begin{eqnarray}[rl] +% x^2 + y^2 &= z^2 \\ +% y^2 &< z^2 +%\end{eqnarray} +% \end{demo} +% +% Well, that was easy enough. Now on to numbering. As you've noticed, the +% equations above are numbered. You can use the \env{eqnarray$*$} +% environment to turn off the numbering in the whole environment, or say +% |\nonumber| on a line to suppress numbering of that one in particular. +% +% \DescribeMacro{\eqnumber} +% More excitingly, you can say |\eqnumber| to enable numbering for a +% particular equation, or \syntax{"\\eqnumber["<text>"]"} to choose what to +% show instead of the line number. This works for both starred and unstarred +% versions of the environment. Now |\nonumber| becomes merely a synonym for +% `|\eqnumber[]|'. +% +% A note for cheats: you can use the sparkly new \env{eqnarray} for simple +% equations by specifying \lit{x} as the column description. Who needs +% \AmSTeX?\ |;-)| +% +% \DescribeEnv{eqlines} +% \DescribeEnv{eqlines*} +% In fact, there's a separate environment \env{eqlines}, which is equivalent +% to \env{eqnarray} with a single \lit{x} column; the result is that you can +% insert a collection of displayed equations separated by |\\| commands. If +% you don't like numbering, use \env{eqlines$*$} insead. +% +% \subsubsection{The \env{eqnalign} environment} +% +% \DescribeEnv{eqnalign} +% There's a new environment, \env{eqnalign}, which does almost the same +% thing as \env{eqnarray} but not quite. It doesn't do equation numbers, +% and it wraps its contents up in a box. The result of this is that: +% +% \begin{itemize} +% +% \item You can use \env{eqnalign} for just a part of a formula. +% The \env{eqnarray} environment must take up the whole display. +% +% \item You can use \env{eqnalign} within \env{eqnarray} for extra fine +% alignment of subsidiary bits. +% +% \item You can break off from doing an \env{eqnarray} using the |\tabpause| +% command. You can't use |\tabpause| inside +% \env{eqnalign}.\footnote{^^A +% Well, technically speaking there's nothing to stop you. However, +% the results won't be pretty.} +% +% \end{itemize} +% +% The \env{eqnalign} environment works like this: +% +% \begin{grammar} +% +% <eqnalign-env> ::= \[[ +% <begin-eqnalign> <contents> <end-eqnalign> +% \]] +% +% <begin-eqnalign> ::= \[[ +% "\\begin" "{eqnalign}" +% \[ "[" \< <eqa-column> \> "]" \] +% \[ "[" \( "t" \\ "c" \\ "b" \) "]" \] +% \]] +% +% <end-eqnalign> ::= \[[ "\\end" "{eqnalign}" \]] +% +% \end{grammar} +% +% As the syntax suggests, the preamble for the \env{eqnalign} environment +% works exactly the same way as for \env{eqnarray}. Example time: another +% one from the \textit{\TeX book}. +% +% \begin{figure} +% \begin{demo}[w]{Example of \env{eqnalign}} +%\[ +% \left\{ \begin{eqnalign}[rl] +% \alpha &= f(z) \\ \beta &= f(z^2) \\ +% \gamma &= f(z^3) +% \end{eqnalign} \right\} +% \qquad +% \left\{ \begin{eqnalign}[rl] +% x &= \alpha^2 - \beta \\ y &= 2\gamma +% \end{eqnalign} \right\}. +%\] +% \end{demo} +% \end{figure} +% +% \DescribeMacro{\multicolumn} +% The |\multicolumn| command works correctly in both the \env{eqnarray} and +% \env{eqnalign} environments, although you should bear in mind that you +% should give \env{eqnarray} column types, not \env{array} ones. +% +% \subsubsection{A note on spacing in alignment environments} +% +% Most of the time, equations in \env{eqnarray} and \env{eqnalign} +% environments will be beautiful. However, there are some things you should +% bear in mind when you produce beautiful equations. +% +% The main problem with spacing is making sure that binary relations and +% binary operators have the correct amount of space on each side of them. +% The alignment environments insert `hidden' objects at the ends of table +% cells to assist with the spacing: \lit{l} column types have a hidden object +% on the left, \lit{r} types have a hidden object on the right, and \lit{c} +% types have a hidden object on \emph{both} ends. These hidden objects add +% the correct space when there's a binary operator or relation next to them. +% If some other sort of object is lurking there, no space is added. So far, +% so good. +% +% The only problem comes when you have something like this: +% +% \begin{demo}{How not to do an \env{eqnarray}} +%\begin{eqnarray*}[rcl] +% x + y & = & 12 \\ +% 2x - 5y & = & -6 +%\end{eqnarray*} +% \end{demo} +% +% The `$-$' sign in the second equation has been treated as a binary operator +% when really it should be a unary prefix operator, but \TeX\ isn't clever +% enough to know the difference. (Can you see the difference in the spacing +% between $-6$~and~${}-6$?) There are two possible solutions to the +% problem. You could wrap the `|-6|' up in a group (`|{-6}|'), or just the +% $-$ sign (`|{-}6|'). A better plan, though, is to get rid of the middle +% column altogether: +% +% \begin{demo}{How to do an \env{eqnarray}} +%\begin{eqnarray*}[rl] +% x + y & = 12 \\ +% 2x - 5y & = -6 +%\end{eqnarray*} +% \end{demo} +% +% Since the things in the middle column were the same width, it's not +% actually doing any good. Also, now that \TeX\ can see that the thing on +% the left of the `$-$' sign is a relation (the `$=$' sign), it will space +% the formula correctly. +% +% In this case, it might be even better to add some extra columns, and line +% up the $x$ and $y$ terms in the left hand side: +% +% \begin{demo}{Extra beautiful \env{eqnarray}} +%\begin{eqnarray*}[rrl] +% x + & y & = 12 \\ +% 2x - & 5y & = -6 +%\end{eqnarray*} +% \end{demo} +% +% ^^A Some hacking now to display box sizes. +% +% { +% \catcode`p=12 \catcode`t=12 +% \gdef\magni#1pt{#1} +% } +% +% \newcommand{\widthof}[1]{^^A +% \settowidth{\dimen0 }{#1}^^A +% \expandafter\magni\the\dimen0\,pt^^A +% } +% +% ^^A The text below makes an assumption which looks correct to me (I asked +% ^^A TeX, and it agreed with me), although in case anything changes, I want +% ^^A to be informed. +% +% \sbox0{$+$} \sbox2{$-$} \ifdim\wd0=\wd2\else% +% \errmessage{Assertion failed: `+' and `-' are different widths!} +% \fi +% +% There's no need to put the `$+$' and `$-$' operators in their own column +% here, because they're both \widthof{$+$} wide, even though they don't +% look it. +% +% \subsubsection{Configuring the alignment environments} +% +% There are a collection of parameters you can use to make the equation +% alignment environments (\env{eqnarray} and \env{eqnalign}) look the way +% you like them. These are all shown in table~\ref{tbl:eqnparms}. +% +% \begin{table} +% \begin{tabular}[C]{| l | p{3in} |} \hlx{hv} +% \bf Parameter & \bf Use \\ \hlx{vhv} +% |\eqaopenskip| & Length put on the left of an +% \env{eqnarray} environment. By +% default, this is |\@centering| (to +% centre the alignment) or |\mathindent| +% (to left align) depending on whether +% you're using the \textsf{fleqn} +% document class option. \\ +% |\eqacloseskip| & Length put on the right of an +% \env{eqnarray} environment. By +% default, this is |\@centering|, to +% align the environment correctly. \\ \hlx{vhv} +% |\eqacolskip| & Space added by the \lit{:} column +% modifier. This should be a rubber +% length, although it only stretches in +% \env{eqnarray}, not in \env{eqnalign}. +% The default value is 1\smallf1/2\,em +% with 1000\,pt of stretch. \\ +% |\eqainskip| & Space added at each side of a normal +% column. By default this is 0\,pt. \\ \hlx{vhv} +% |\eqastyle| & The maths style used in the alignment. +% By default, this is |\textstyle|, +% and you probably won't want to change +% it. \\ \hlx{vh} +% \end{tabular} +% +% \caption{Parameters for the \env{eqnarray} and \env{eqnalign} environments} +% \label{tbl:eqnparms} +% \end{table} +% +% +% \subsection{Other multiline equations} +% +% Sometimes there's no sensible alignment point for splitting equations. The +% normal thing to do under these circumstances is to put the first line way +% over to the left of the page, and the last line over to the right. (If +% there are more lines, I imagine we put them in the middle.) +% +% \DescribeEnv{spliteqn} +% \DescribeEnv{spliteqn*} +% The \env{spliteqn} environment allows you to do such splitting of +% equations. Rather than tediously describe it, I'll just give an example, +% because it's really easy. The $*$-version works the same, except it +% doesn't put an equation number in. +% +% \begin{figure} +% \begin{demo}[w]{A split equation} +%\begin{spliteqn} +% \sum_{1\le j\le n} +% \frac {1} { (x_j - x_1) \ldots (x_j - x_{j-1}) +% (x - x_j) (x_j - x_{j+1}) \ldots (x_j - x_n) } +% \\ +% = \frac {1} { (x - x_1) \ldots (x - x_n) }. +%\end{spliteqn} +% \end{demo} +% \end{figure} +% +% \DescribeEnv{subsplit} +% If you have a very badly behaved equation, you might want to split a part +% of it (say, a bit of a fraction), particularly if you're doing things in +% narrow columns. +% +% \begin{figure} +% \begin{demo}[w]{A \env{subsplit} environment} +%\begin{equation} +% \frac{ +% \begin{subsplit} +% q^{\frac{1}{2} n(n+1)}(ea; q^2)_\infty (eq/a; q^2)_\infty \\ +% (caq/e; q^2)_\infty (cq^2/ae; q^2)_\infty +% \end{subsplit} +% }{ +% (e; q)_\infty (cq/e; q)_\infty +% } +%\end{equation} +% \end{demo} +% \end{figure} +% +% \subsection{Matrices} +% +% Also included in the \package{mathenv} package is a collection of things +% for typesetting matrices. The standard \env{array} doesn't (in my opinion) +% provide the right sort of spacing for matrices. \PlainTeX\ provides some +% quite nice matrix handling macros, but they don't work in the appropriate +% \LaTeX\ way. +% +% \textbf{Warning:} These definitions will make old versions of +% \package{plain.sty} unhappy; newer versions correctly restore the +% Plain~\TeX\ macros |\matrix| and |\pmatrix|. +% +% \DescribeEnv{matrix} +% The simple way to do matrices is with the \env{matrix} environment. +% +% \begin{grammar} +% +% <matrix-env> ::= \[[ <begin-matrix> <contents> <end-matrix> \]] +% +% <begin-matrix> ::= \[[ "\\begin{matrix}" \[ "[" <matrix-cols> "]" \] \]] +% +% <matrix-cols> ::= \[[ +% \< \[ "[" \] \[ "T" \] \( "l" \\ "c" \\ "r" \) \> +% \]] +% +% <end-matrix> ::= \[[ "\\end{stack}" \]] +% +% \end{grammar} +% +% The \lit{l}, \lit{c} and \lit{r} columns are fairly obvious -- they align +% their contents in the appropriate way. The \lit{[} character is more +% complicated. It means `repeat the remaining column types forever', so a +% preamble of \lit{cc[lr} means `two centred columns, then alternating left- +% and right-aligned columns for as often as needed'. The default preamble, +% if you don't specify one, is \lit{[c} -- `any number of centred columns'. +% +% \DescribeMacro{\multicolumn} +% The |\multicolumn| command works correctly in matrices, although you should +% bear in mind that you should give \env{matrix} column types, not +% \env{array} ones. +% +% \DescribeEnv{pmatrix} +% The standard \env{matrix} environment doesn't put any delimiters around the +% matrix. You can use the standard |\left| and |\right| commands, although +% this is a bit nasty. The \env{pmatrix} environment will put parentheses +% around the matrix it creates; it's otherwise exactly the same as +% \env{matrix}. +% +% \DescribeEnv{dmatrix} +% A \env{dmatrix} environment is also provided. It takes two extra +% arguments: the left and right delimiter characters (without |\left| or +% |\right|). +% +% \begin{figure} +% \begin{demo}[w]{Various \env{matrix} environments} +%\[ \begin{matrix} 1 & 0 \\ 0 & -1 \end{matrix} \quad +% \begin{pmatrix} +% \cos\theta & \sin\theta \\ +% -\sin\theta & \cos\theta +% \end{pmatrix} \quad +% \begin{dmatrix}[] 0 & -i \\ i & 0 \end{dmatrix} +%\] +% \end{demo} +% \end{figure} +% +% \DescribeEnv{smatrix} +% Normal matrices always come out the same size; they don't change size +% according to the surrounding context (unfortunately). However, it can be +% occasionally useful to put matrices in running text, so you can talk about +% $A$ being $\bigl( \begin{smatrix} a & b \\ b & c \end{smatrix} \bigr)$ +% being its own transpose (i.e., $A = A^T$). This is accomplished using the +% \env{smatrix} (the `s' stands for `small' -- I thought that `smallmatrix' +% was too big to type inline). As well as inline text, the \env{smatrix} +% can be useful in displays, if the matrix is deep in a subformula. I can't +% think of any examples offhand, though. +% +% \DescribeEnv{spmatrix} +% \DescribeEnv{sdmatrix} +% The \env{smatrix} environment doesn't supply any delimiters, like +% \env{matrix}. There are \env{spmatrix} and \env{sdmatrix} environments +% which do, though. Note that delimiters have a tendency to get too big and +% mess up the line spacing -- I had to use explicitly |\big| delimiters +% in the above example. +% +% \DescribeEnv{pmatrix*} +% \DescribeEnv{spmatrix*} +% \DescribeEnv{sdmatrix*} +% All the small matrix environments have starred versions, which are more +% suitable for use in displays, since they have more space between the rows. +% They're intended for typesetting really big matrices in displays. +% +% \DescribeMacro{\ddots} +% \DescribeMacro{\vdots} +% The standard |\vdots| and |\ddots| commands don't produce anything at all +% nice in small matrices, so this package redefines them so that they scale +% properly to smaller sizes. +% +% \DescribeEnv{genmatrix} +% Actually, all these environments are special cases of one: \env{genmatrix}. +% This takes oodles of arguments: +% \begin{quote} \synshorts +% "\\begin{genmatrix}{"<matrix-style>"}{"<outer-style>"}" \\ +% \null \qquad "{"<spacing>"}{"<left-delim>"}{"<right-delim>"}" \\ +% \null \quad\vdots \\ +% "\\end{genmatrix}" +% \end{quote} +% The two `style' arguments should be things like |\textstyle| or +% |\scriptstyle|; the first, \<matrix-style>, is the style to use for the +% matrix elements, and the second, \<outer-style>, is the style to assume +% for the surrounding text (this affects the spacing within the matrix; it +% should usually be the same as \<matrix-style>). The \<spacing> is inserted +% between the matrix and the delimiters, on each side of the matrix. It's +% usually `|\,|' in full-size matrices, and blank for small ones. The +% delimiters are inserted around the matrices, and sized appropriately. +% +% \DescribeEnv{newmatrix} +% You can create your own matrix environments if you like, using the +% |\newmatrix| command. It takes two arguments, although they're a bit +% odd. The first is the name of the environment, and the second contains +% the arguments to pass to \env{genmatrix}. For example, the \env{pmatrix} +% environment was defined by saying +% +% \begin{listing} +%\newmatrix{pmatrix}{{\textstyle}{\textstyle}{\,}{(}{)}} +% \end{listing} +% +% If you don't pass all three arguments, then you end up requiring the +% user to specify the remaining ones. This is how \env{dmatrix} works. +% +% \DescribeEnv{script} +% Finally, although it's not really a matrix, stacked super- and subscripts +% follow much the same sorts of spacing rules. The \env{script} environment +% allows you to do this sort of thing very easily. It essentially provides +% a `matrix' with the right sort of spacing. The default preamble string is +% \lit{c}, giving you centred scripts, although you can say +% |\begin{script}[l]| for left-aligned scripts, which is better if the +% script is being placed to the right of its operator. If you're really +% odd, you can have more than one column. +% +% \begin{demo}{Example of \env{script}} +%\[ \mathop{{\sum}'}_{x \in A} +% f(x) +% \stackrel{\mathrm{def}}{=} +% \sum_{\begin{script} +% x \in A \\ x \ne 0 +% \end{script}} f(x) +%\] +% \end{demo} +% +% +% \subsection{Other \package{mathenv} environments} +% +% The \package{mathenv} package contains some other environments which may +% be useful, based on the enhanced \env{tabular} and \env{array} +% environments. +% +% \DescribeEnv{cases} +% The \env{cases} environment lets you say things like the following: +% +% \begin{demo}[w]{Example of \env{cases}} +%\[ P_{r-j} = \begin{cases} +% 0 & if $r-j$ is odd \\ +% r!\,(-1)^{(r-j)/2} & if $r-j$ is even +% \end{cases} +%\] +% \end{demo} +% +% The spacing required for this is a bit messy, so providing an environment +% for it is quite handy. +% +% \DescribeEnv{smcases} +% The \env{smcases} environment works the same way as \env{cases}, but with +% scriptsize lettering. +% +% \implementation +% +% +%^^A------------------------------------------------------------------------- +% \section{Implementation of table handling} +% +% +% Here we go. It starts horrid and gets worse. However, it does stay nicer +% than the original, IMHO. +% +% \begin{macrocode} +%<*mdwtab> +% \end{macrocode} +% +% +% \subsection{Registers, switches and things} +% +% We need lots of these. It's great fun. +% +% The two count registers are simple enough: +% +% \begin{description} +% \item [\cs{tab@state}] contains the current parser state. Since we +% probably won't be parsing preambles recursively, this is a global +% variable. +% \item [\cs{tab@columns}] contains the number of the current column. +% \item [\cs{tab@hlstate}] contains the state required for hline management. +% \end{description} +% +% \begin{macrocode} +\newcount\tab@state +\newcount\tab@columns +% \end{macrocode} +% +% We need \emph{lots} of token registers. Fortunately, most of them are only +% used during parsing. We'll use \PlainTeX's scratch tokens for this. Note +% that |\toks\tw@| isn't used here. It, and |\toks@|, are free for use by +% column commands. +% +% \begin{macrocode} +\newtoks\tab@preamble +\newtoks\tab@shortline +\toksdef\tab@pretext 4 +\toksdef\tab@posttext 6 +\toksdef\tab@userpretext 8 +% \end{macrocode} +% +% The dimens are fairly straightforward. The inclusion of |\col@sep| is a +% sacrifice to compatibility -- judicious use of |\let| in \package{array} +% would have saved a register. +% +% \begin{macrocode} +\newdimen\extrarowheight +\newdimen\tabextrasep +\newdimen\arrayextrasep +\newdimen\smarraycolsep +\newdimen\smarrayextrasep +\newdimen\tab@width +\newdimen\col@sep +\newdimen\tab@endheight +% \end{macrocode} +% +% Some skip registers too. Phew. +% +% \begin{macrocode} +\newskip\tab@leftskip +\newskip\tab@rightskip +% \end{macrocode} +% +% And some switches. The first three are for the parser. +% +% \begin{macrocode} +\newif\iftab@firstcol +\newif\iftab@initrule +\newif\iftab@rule +\newif\iftab@vgap +% \end{macrocode} +% +% Now assign some default values to new dimen parameters. These definitions +% are essentially the equivalent of an |\openup 1\jot| in \env{array}, but +% not in \env{tabular}. This looks nice, I think. +% +% \begin{macrocode} +\tabextrasep\z@ +\arrayextrasep\jot +\smarraycolsep\thr@@\p@ +\smarrayextrasep\z@ +% \end{macrocode} +% +% Set some things up for alien table environments. +% +% \begin{macrocode} +\let\tab@extrasep\tabextrasep +\let\tab@penalty\relax +% \end{macrocode} +% +% +% \subsection{Some little details} +% +% \begin{macro}{\@maybe@unskip} +% +% This macro solves a little problem. In an alignment (and in other places) +% it's desirable to suppress trailing space. The usual method, to say +% |\unskip|, is a little hamfisted, because it removes perfectly reasonable +% aligning spaces like |\hfil|s. While as a package writer I can deal with +% this sort of thing by saying |\kern\z@| in appropriate places, it can +% annoy users who are trying to use |\hfill| to override alignment in funny +% places. +% +% My current solution seems to be acceptable. I'll remove the natural width +% of the last glue item, so that it can still stretch and shrink if +% necessary. The implementation makes use of the fact that multiplying +% a \<skip> by a \<number> kills off the stretch. (Bug fix: don't do this +% when we're in vertical mode.) +% +% \begin{macrocode} +\def\@maybe@unskip{\ifhmode\hskip\m@ne\lastskip\relax\fi} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\q@delim} +% +% Finally, for the sake of niceness, here's a delimiter token I can use +% for various things. It's a `quark', for what it's worth (i.e., it expands +% to itself) although I'm not really sure why this is a good thing. As far +% as I'm concerned, it's important that it has a unique meaning (i.e., that +% it won't be |\ifx|-equal to other things, or something undefined) and that +% it won't be used where I don't expect it to be used. \TeX\ will loop +% horridly if it tries to expand this, so I don't think that quarks are +% wonderfully clever thing to use. (Maybe it should really expand to +% something like `\syntax{<quark>"."}', which will rapdly fill \TeX's memory +% if it gets accidentally expanded. Still, I'll leave it as it is until +% such time as I understand the idea more.) +% +% \begin{macrocode} +\def\q@delim{\q@delim} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Parser states} +% +% Now we start on the parser. It's really simple, deep down. We progress +% from state to state, extracing tokens from the preamble and building +% command names from them. Each command calls one of the element-building +% routines, which works out which state it should be in. We go through each +% of the states in between (see later) doing default things for the ones we +% missed out. +% +% Anyway, here's some symbolic names for the states. It makes my life +% easier. +% +% \begin{macrocode} +\chardef\tab@startstate 0 +\chardef\tab@loopstate 1 +\chardef\tab@rulestate 1 +\chardef\tab@prespcstate 2 +\chardef\tab@prestate 3 +\chardef\tab@colstate 4 +\chardef\tab@poststate 5 +\chardef\tab@postspcstate 6 +\chardef\tab@limitstate 7 +% \end{macrocode} +% +% +% \subsection{Adding things to token lists} +% +% Define some macros for adding stuff to the beginning and end of token +% lists. This is really easy, actually. Here we go. +% +% \begin{macrocode} +\def\tab@append#1#2{#1\expandafter{\the#1#2}} +\def\tab@prepend#1#2{% + \toks@{#2}#1\expandafter{\the\expandafter\toks@\the#1}% +} +% \end{macrocode}% +% +% +% \subsection{Committing a column to the preamble} +% +% Each time we pass the `rule' state, we `commit' the tokens we've gathered +% so far to the main preamble token list. This is how we do it. Note the +% icky use of |\expandafter|. +% +% \begin{macrocode} +\def\tab@commit{% +% \end{macrocode} +% +% If this isn't the first column, then we need to put in a column separator. +% +% \begin{macrocode} + \iftab@firstcol\else% + \expandafter\tab@append\expandafter\tab@preamble% + \expandafter{\tab@tabtext}% + \fi% +% \end{macrocode} +% +% Now we spill the token registers into the main list in a funny order (which +% is why we're doing it in this strange way in the first place. +% +% \begin{macrocode} + \toks@\expandafter{\tab@midtext}% + \tab@preamble\expandafter{% + \the\expandafter\tab@preamble% + \the\expandafter\tab@pretext% + \the\expandafter\tab@userpretext% + \the\expandafter\toks@% + \the\tab@posttext% + }% +% \end{macrocode} +% +% Now reset token lists and things for the next go round. +% +% \begin{macrocode} + \tab@firstcolfalse% + \tab@pretext{}% + \tab@userpretext{}% + \tab@posttext{}% +} +% \end{macrocode} +% +% +% \subsection{Playing with parser states} +% +% \begin{macro}{\tab@setstate} +% +% This is how we set new states. The algorithm is fairly simple, really. +% +% ^^A Let's see how good my TeX really is... ;-) +% ^^A Actually, it doesn't seem to have worked out too badly. Maybe I should +% ^^A write a package to do this automatically. It's rather tricky, though. +% +% \def\qq{\mbox{\quad}} +% \sbox{0}{\itshape\textunderscore}\def\_{\usebox{0}} +% +% \begin{quote} +% {\bf while} $\it tab\_state \ne s$ {\bf do} \\ +% \qq $\mathit{tab\_state = tab\_state}+1$; \\ +% \qq {\bf if} $\it tab\_state = tab\_limitState$ {\bf then} +% $\it tab\_state=tab\_loopState$; \\ +% \qq {\bf if} $\it tab\_state = tab\_preSpcState$ {\bf then} \\ +% \qq \qq {\bf if} $\it tab\_initRule$ {\bf then} \\ +% \qq \qq \qq $\it tab\_initRule = {\bf false}$; \\ +% \qq \qq {\bf else} \\ +% \qq \qq \qq {\bf if} $\it tab\_inMultiCol$ {\bf then moan}; \\ +% \qq \qq \qq $\it commit$; \\ +% \qq \qq \qq $\it append(tab\_shortLine,\hbox{`|&\omit|')}$; \\ +% \qq \qq {\bf end\,if}; \\ +% \qq {\bf end\,if}; \\ +% \qq {\bf if} $\it tab\_state \ne s$ {\bf then} +% $\it do\_default(tab\_state)$; \\ +% {\bf end\,while}; +% \end{quote} +% +% First we decide if there's anything to do. If so, we call another macro to +% do it for us. +% +% \begin{macrocode} +\def\tab@setstate#1{% + \ifnum#1=\tab@state\else% + \def\@tempa{\tab@setstate@i{#1}}% + \@tempa% + \fi% +} +% \end{macrocode} +% +% This is where the fun is. First we bump the state by one, and loop back +% if we fall off the end. +% +% \begin{macrocode} +\def\tab@setstate@i#1{% + \global\advance\tab@state\@ne% + \ifnum\tab@state>\tab@limitstate% + \global\tab@state\tab@loopstate% + \fi% +% \end{macrocode} +% +% Now, if we've just passed the ruleoff state, we commit the current text +% \emph{unless} this was the strange initial rule at the very beginning. We +% provide a little hook here so that |\multicolumn| can moan if you try and +% give more than one column there. We also add another tab/omit pair to the +% list we use for |\vgap|. +% +% \begin{macrocode} + \ifnum\tab@state=\tab@prespcstate% + \iftab@initrule% + \tab@initrulefalse% + \else% + \tab@looped% + \tab@commit% + \tab@append\tab@shortline{&\omit}% + \fi% + \fi% +% \end{macrocode} +% +% Now we decide whether to go round again. If not, we do the default thing +% for this state. This is mainly here so that we can put the |\tabcolsep| or +% whatever in if the user didn't give an \lit{@} expression. +% +% \begin{macrocode} + \ifnum#1=\tab@state% + \let\@tempa\relax% + \else% + \csname tab@default@\number\tab@state\endcsname% + \fi% + \@tempa% +} +% \end{macrocode} +% +% \end{macro} +% +% Now we set up the default actions for the various states. +% +% In state~2 (pre-space) we add in the default gap if either we didn't have +% an \lit{@} expression in the post-space state or there was an explicit +% intervening rule. +% +% \begin{macrocode} +\@namedef{tab@default@2}{% + \iftab@rule% + \tab@append\tab@pretext{\hskip\col@sep}% + \fi% +} +% \end{macrocode} +% +% If the user omits the column type, we insert an `l'-type column and moan +% a lot. +% +% \begin{macrocode} +\@namedef{tab@default@4}{% + \tab@err@misscol% + \tab@append\tab@pretext{\tab@bgroup\relax}% + \tab@append\tab@posttext{\relax\tab@egroup\hfil}% + \tab@append\tab@shortline{\hfil}% + \advance\tab@columns\@ne% +} +% \end{macrocode} +% +% Finally we deal with the post-space state. We set a marker so that we +% put in the default space in the pre-space state later too. +% +% \begin{macrocode} +\@namedef{tab@default@6}{% + \tab@append\tab@posttext{\hskip\col@sep}% + \tab@ruletrue% +} +% \end{macrocode} +% +% +% \subsection{Declaring token types} +% +% \begin{macro}{\tab@extracol} +% +% Before we start, we need to handle |\extracolsep|. This is a right pain, +% because the original version of \env{tabular} worked on total expansion, +% which is a Bad Thing. On the other hand, turning |\extracolsep| into a +% |\tabskip| is also a major pain. +% +% \begin{macrocode} +\def\tab@extracol#1#2{\tab@extracol@i#1#2\extracolsep{}\extracolsep\end} +\def\tab@extracol@i#1#2\extracolsep#3#4\extracolsep#5\end{% + \ifx @#3@% + \def\@tempa{#1{#2}}% + \else% + \def\@tempa{#1{#2\tabskip#3\relax#4}}% + \fi% + \@tempa% +} +% \end{macrocode} +% +% \end{macro} +% +% This is where we do the work for inserting preamble elements. +% +% \begin{macro}{\tabruletype} +% +% Inserting rules is interesting, because we have to decide where to put +% them. If this is the funny initial rule, it goes in the pre-text list, +% otherwise it goes in the post-text list. We work out what to do first +% thing: +% +% \begin{macrocode} +\def\tabruletype#1{\tab@extracol\tabruletype@i{#1}}% +\def\tabruletype@i#1{% + \iftab@initrule% + \let\tab@tok\tab@pretext% + \else% + \let\tab@tok\tab@posttext% + \fi% +% \end{macrocode} +% +% Now if we're already in the rule state, we must have just done a rule. +% This means we must put in the |\doublerulesep| space, both here and in the +% shortline list. Otherwise we just stick the rule in. +% +% This is complicated, because |\vgap| needs to be able to remove some bits +% of rule. We pass each one to a macro |\tab@ckr|, together with the column +% number, which is carefully bumped at the right times, and this macro will +% vet the rules and output the appropriate ones. There's lots of extreme +% |\expandafter| nastiness as a result. Amazingly, this actually works. +% +% \begin{macrocode} + \ifnum\tab@state=\tab@rulestate% + \tab@append\tab@tok{\hskip\doublerulesep\begingroup#1\endgroup}% + \expandafter\tab@append\expandafter\tab@shortline\expandafter{% + \expandafter\hskip\expandafter\doublerulesep% + \expandafter\tab@ckr\expandafter{\the\tab@columns}% + {\begingroup#1\endgroup}% + }% + \else% + \tab@setstate\tab@rulestate% + \tab@append\tab@tok{\begingroup#1\endgroup}% + \expandafter\tab@append\expandafter\tab@shortline\expandafter{% + \expandafter\tab@ckr\expandafter{\the\tab@columns}% + {\begingroup#1\endgroup}% + }% + \fi% +% \end{macrocode} +% +% Finally, we say there was a rule here, so that default space gets put in +% after this. Otherwise we lose lots of generality. +% +% \begin{macrocode} + \tab@ruletrue% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tabspctype} +% +% We need to work out which space-state we should be in. Then we just put +% the text in. Easy, really. +% +% \begin{macrocode} +\def\tabspctype#1{\tab@extracol\tabspctype@i{#1}}% +\def\tabspctype@i#1{% + \tab@rulefalse% + \ifnum\tab@state>\tab@prespcstate% + \tab@setstate\tab@postspcstate% + \let\tab@tok\tab@posttext% + \else% + \tab@setstate\tab@prespcstate% + \let\tab@tok\tab@pretext% + \fi% + \tab@append\tab@tok{\begingroup#1\endgroup}% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tabcoltype} +% +% If we're already in the column state, we bump the state and loop round +% again, to get all the appropriate default behaviour. We bump the column +% counter, and add the bits of text we were given to appropriate token lists. +% We also add the |\hfil| glue to the shortline list, to space out the rules +% properly. +% +% \begin{macrocode} +\def\tabcoltype#1#2{% + \ifnum\tab@state=\tab@colstate% + \global\advance\tab@state\@ne% + \fi% + \advance\tab@columns\@ne% + \tab@setstate\tab@colstate% + \tab@append\tab@pretext{#1}% + \tab@append\tab@posttext{#2}% + \tab@append\tab@shortline{\hfil}% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tabuserpretype} +% \begin{macro}{\tabuserposttype} +% +% These are both utterly trivial. +% +% \begin{macrocode} +\def\tabuserpretype#1{% + \tab@setstate\tab@prestate% + \tab@prepend\tab@userpretext{#1}% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tabuserposttype#1{% + \tab@setstate\tab@poststate% + \tab@prepend\tab@posttext{#1}% +} +% \end{macrocode} +% +% \end{macro} +% \end{macro} +% +% +% \subsection{The colset stack} +% +% Let's start with something fairly easy. We'll keep a stack of column sets +% so that users don't get confused by package authors changing the current +% column set. This is fairly easy, really. +% +% \begin{macro}{\tab@push} +% \begin{macro}{\tab@pop} +% \begin{macro}{\tab@head} +% +% These are the stack management routines. The only important thing to note +% is that |\tab@head| must take place \emph{only} in \TeX's mouth, so we can +% use it in |\csname|\dots|\endcsname| constructions. +% +% \begin{macrocode} +\def\tab@push#1#2{% + \toks@{{#2}}% + \expandafter\def\expandafter#1\expandafter{\the\expandafter\toks@#1}% +} +\def\tab@pop#1{\expandafter\def\expandafter#1\expandafter{\@gobble#1}} +\def\tab@head#1{\expandafter\tab@head@i#1\relax} +\def\tab@head@i#1#2\relax{#1} +% \end{macrocode} +% +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\colset} +% \begin{macro}{\colpush} +% \begin{macro}{\colpop} +% +% Now we can define the user macros. +% +% \begin{macrocode} +\def\tab@colstack{{tabular}} +\def\colset{\colpop\colpush} +\def\colpush{\tab@push\tab@colstack} +\def\colpop{\tab@pop\tab@colstack} +% \end{macrocode} +% +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\tab@colset} +% +% Now we define a shortcut for reading the top item off the stack. +% +% \begin{macrocode} +\def\tab@colset{\tab@head\tab@colstack} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{The main parser routine} +% +% \begin{macro}{\tab@initread} +% +% This macro sets up lots of variables to their normal states prior to +% parsing a preamble. Some things may need changing, but not many. +% +% \begin{macrocode} +\def\tab@initread{% +% \end{macrocode} +% +% First, reset the parser state to the start state. +% +% \begin{macrocode} + \global\tab@state\tab@startstate% +% \end{macrocode} +% +% We clear the token lists to sensible values, mostly. The midtext macro +% contains what to put in the very middle of each template -- |\multicolumn| +% will insert its argument here. +% +% \begin{macrocode} + \tab@preamble{}% + \tab@shortline{}% + \def\tab@tabtext{&}% + \def\tab@midtext{\ignorespaces####\@maybe@unskip}% + \tab@pretext{}% + \tab@userpretext{}% + \tab@posttext{}% + \let\tab@multicol\@empty% + \def\tab@startpause{\penalty\postdisplaypenalty\medskip}% + \def\tab@endpause{\penalty\predisplaypenalty\medskip}% +% \end{macrocode} +% +% Finally, reset the column counter, don't raise errors when we loop, and set +% some parser flags to their appropriate values. +% +% \begin{macrocode} + \tab@columns\z@% + \let\tab@looped\relax% + \tab@ruletrue% + \tab@initruletrue% + \tab@firstcoltrue% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@readpreamble} +% +% This is the main macro for preamble handling. Actually, all it does is +% gobble its argument's leading brace and call another macro, but it does it +% with style. +% +% \begin{macrocode} +\def\tab@readpreamble#1{% + \tab@doreadpream{#1}% + \iftab@initrule\global\tab@state\tab@prespcstate\fi% + \tab@setstate\tab@rulestate% + \tab@commit% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@doreadpream} +% +% The preamble is in an argument. Previous versions used a nasty trick using +% |\let| and |\afterassignment|. Now we use an explicit end token, to allow +% dodgy column type handlers to scoop up the remaining preamble tokens +% and process them. Not that anyone would want to do that, oh no (see +% the \lit{[} type in the \env{eqnarray} environment |;-)|). +% +% \begin{macrocode} +\def\tab@doreadpream#1{\tab@mkpreamble#1\q@delim} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@mkpreamble} +% +% This is the main parser routine. It takes each token in turn, scrutinises +% it carefully, and does the appropriate thing with it. +% +% The preamble was given as an argument to |\tab@doreadpream|, and that has +% helpfully stripped off the initial |{| character. We need to pick off the +% next token (whatever it is) so we can examine it. We'll use |\futurelet| +% so we can detect groups and things in funny places. +% +% \begin{macrocode} +\def\tab@mkpreamble{\futurelet\@let@token\tab@mkpreamble@i} +% \end{macrocode} +% +% If we find a space token, we'll go off and do something a bit special, +% since spaces are sort of hard to handle. Otherwise we'll do it in the old +% fashioned way. +% +% \begin{macrocode} +\def\tab@mkpreamble@i{% + \ifx\@let@token\@sptoken% + \expandafter\tab@mkpreamble@spc% + \else% + \expandafter\tab@mkpreamble@ii% + \fi% +} +% \end{macrocode} +% +% If we find a |\@@endpreamble| token, that's it and we're finished. We just +% gobble it and return. Otherwise, if it's an open group character, we'll +% complain because someone's probably tried to put an argument in the wrong +% place. Finally, if none of the other things apply, we'll deal with the +% character below. +% +% \begin{macrocode} +\def\tab@mkpreamble@ii{% + \ifx\@let@token\q@delim% + \def\@tempa{\let\@let@token}% + \else% + \ifcat\bgroup\noexpand\@let@token% + \tab@err@oddgroup% + \def\@tempa##1{\tab@mkpreamble}% + \else% + \let\@tempa\tab@mkpreamble@iii% + \fi% + \fi% + \@tempa% +} +% \end{macrocode} +% +% Handle a character. This involves checking to see if it's actually +% defined, and then doing it. Doing things this way means we won't get +% stranded in mid-preamble unless a package author has blown it. +% +% \begin{macrocode} +\def\tab@mkpreamble@iii#1{% + \@ifundefined{\tab@colset!col.\string#1}{% + \tab@err@undef{#1}\tab@mkpreamble% + }{% + \@nameuse{\tab@colset!col.\string#1}% + }% +} +% \end{macrocode} +% +% If we get given a space character, we'll look up the command name as +% before. If no-one's defined the column type we'll just skip it silently, +% which lets users do pretty formatting if they like. +% +% \begin{macrocode} +\@namedef{tab@mkpreamble@spc} {% + \@ifundefined{\tab@colset!col. }{% + \tab@mkpreamble% + }{% + \@nameuse{\tab@colset!col. }% + }% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\coldef} +% +% Here's how to define column types the nice way. Some dexterity is required +% to make everything work right, but it's simple really. +% +% \begin{macrocode} +\def\coldef{\@ifnextchar[\coldef@i{\coldef@i[\tab@colset]}} +\def\coldef@i[#1]#2#3#{\coldef@ii[#1]{#2}{#3}} +\def\coldef@ii[#1]#2#3#4{% + \expandafter\def\csname#1!col.\string#2\endcsname#3{% + #4\tab@mkpreamble% + }% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\collet} +% +% We'd like to let people copy column types from other places. This is how +% to do it. +% +% \begin{macrocode} +\def\collet{\@ifnextchar[\collet@i{\collet@i[\tab@colset]}} +\def\collet@i[#1]#2{% + \@ifnextchar=% + {\collet@ii[#1]{#2}}% + {\collet@ii[#1]{#2}=}% +} +\def\collet@ii[#1]#2={% + \@ifnextchar[% + {\collet@iii[#1]{#2}}% + {\collet@iii[#1]{#2}[\tab@colset]}% +} +\def\collet@iii[#1]#2[#3]#4{% + \expandafter\let\csname#1!col.\string#2\expandafter\endcsname% + \csname#3!col.\string#4\endcsname% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\newcolumntype} +% +% We just bundle the text off to |\newcommand| and expect it to cope. It +% ought to. The column type code inserts the user's tokens directly, rather +% than calling |\tab@doreadpream| recursively. The magic control sequence +% is the one looked up by the parser. +% +% There's some additional magic here for compatiblity with the obscure way +% that \package{array} works. +% +% \begin{macrocode} +\def\newcolumntype#1{\@ifnextchar[{\nct@i{#1}}{\nct@i#1[0]}} +\def\nct@i#1[#2]{\@ifnextchar[{\nct@ii{#1}[#2]}{\nct@iii{#1}{[#2]}}} +\def\nct@ii#1[#2][#3]{\nct@iii{#1}{[#2][#3]}} +\def\nct@iii#1#2#3{% + \expandafter\let\csname\tab@colset!col.\string#1\endcsname\relax% + \expandafter\newcommand\csname\tab@colset!col.\string#1\endcsname#2{% + \tab@deepmagic{#1}% + \tab@mkpreamble% + #3% + }% +} +% \end{macrocode} +% +% Now for some hacking for compatibility with \package{tabularx}. +% +% \begin{macrocode} +\def\newcol@#1[#2]{\nct@iii{#1}{[#2]}} +% \end{macrocode} +% +% And now some more. This is seriously deep magic. Hence the name. +% +% \begin{macrocode} +\def\tab@deepmagic#1{% + \csname NC@rewrite@\string#1\endcsname\NC@find\tab@@magic@@% +} +\def\NC@find#1\tab@@magic@@{} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Standard column types} +% +% First, make sure we're setting up the right columns. This also sets the +% default for the user. Other packages must not use the |\colset| command +% for defining columns -- they should use the stack operations defined above. +% +% \begin{macrocode} +\colset{tabular} +% \end{macrocode} +% +% Now do the simple alignment types. These are fairly simple. The +% mysterious kern in the \lit{l} type is to stop the |\col@sep| glue from +% vanishing due to the |\unskip| inserted by the standard |\tab@midtext| if +% the column contains no text. (Thanks for spotting this bug go to that +% nice Mr~Carlisle.) +% +% \begin{macrocode} +\coldef l{\tabcoltype{\kern\z@\tab@bgroup}{\tab@egroup\hfil}} +\coldef c{\tabcoltype{\hfil\tab@bgroup}{\tab@egroup\hfil}} +\coldef r{\tabcoltype{\hfil\tab@bgroup}{\tab@egroup}} +% \end{macrocode} +% +% Some extensions now. These are explicitly teextual or mathematical +% columns. Can be useful if you're providing column types for other people. +% I've inserted a kern here for exactly the same reason as for the \lit{l} +% column type above. +% +% \begin{macrocode} +\coldef T#1{\tab@aligncol{#1}{\tab@btext}{\tab@etext}} +\coldef M#1{\tab@aligncol{#1}{\tab@bmaths}{\tab@emaths}} +\def\tab@aligncol#1#2#3{% + \if#1l\tabcoltype{\kern\z@#2}{#3\hfil}\fi% + \if#1c\tabcoltype{\hfil#2}{#3\hfil}\fi% + \if#1r\tabcoltype{\hfil#2}{#3}\fi% +} +% \end{macrocode} +% +% Now for the default rules. +% +% \begin{macrocode} +\coldef |{\tabruletype{\vrule\@width\arrayrulewidth}} +\coldef !#1{\tabruletype{#1}} +% \end{macrocode} +% +% Deal with \lit{@} expressions. +% +% \begin{macrocode} +\coldef @#1{\tabspctype{#1}} +% \end{macrocode} +% +% And the paragraph types. I've added things to handle footnotes here. +% +% \begin{macrocode} +\coldef p#1{\tabcoltype% + {\savenotes\vtop\tab@bpar{#1}}% + {\tab@epar\spewnotes\hfil}} +\coldef m#1{\tabcoltype% + {\savenotes$\vcenter\tab@bpar{#1}}% + {\tab@epar$\spewnotes\hfil}} +\coldef b#1{\tabcoltype% + {\savenotes\vbox\tab@bpar{#1}}% + {\tab@epar\spewnotes\hfil}} +% \end{macrocode} +% +% Phew. Only a few more left now. The user text ones. +% +% \begin{macrocode} +\coldef >#1{\tabuserpretype{#1}} +\coldef <#1{\tabuserposttype{#1}} +% \end{macrocode} +% +% The strange column type. +% +% \begin{macrocode} +\coldef ##1#2{\tabcoltype{#1}{#2}} +% \end{macrocode} +% +% And \lit{*}, which repeats a preamble spec. This is really easy, and not +% at all like the original one. +% +% \begin{macrocode} +\coldef *#1#2{% + \count@#1% + \loop\ifnum\count@>0\relax% + \tab@doreadpream{#2}% + \advance\count@\m@ne% + \repeat% +} +% \end{macrocode} +% +% +% \subsection{Paragraph handling} +% +% First of all, starting new paragraphs: the vbox token is already there, and +% we have the width as an argument. +% +% \begin{macro}{\tab@bpar} +% +% There are some gymnastics to do here to support lists which form the +% complete text of the parbox. One of the odd things I'll do here is to +% not insert a strut on the first line: instead, I'll put the text into a +% box register so that I can inspect it later. So that I have access to +% the height of the first line, I'll use a |\vtop| -- I can get at the +% final depth by using |\prevdepth|, so this seems to be the most general +% solution. +% +% \begin{macrocode} +\def\tab@bpar#1{% + \bgroup% + \hsize#1\relax% + \@arrayparboxrestore% + \setbox\z@\vtop\bgroup + \global\@minipagetrue% + \everypar{% + \global\@minipagefalse% + \everypar{}% + }% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@epar} +% +% To end the paragraph, close the box. That sounds easy, doesn't it? +% I need to space out the top and bottom of the box so that it looks as if +% struts have been applied. +% +% \begin{macrocode} +\def\tab@epar{% +% \end{macrocode} +% +% Anyway, I should end the current paragraph if I'm still in horizontal +% mode. A simple |\par| will do this nicely. I'll also remove any trailing +% vertical glue (which may be left there by a list environment), because +% things will look very strange otherwise. +% +% \begin{macrocode} + \ifhmode\@maybe@unskip\par\fi% + \unskip% +% \end{macrocode} +% +% Now I'll look at the depth of the last box: if it's less deep than my +% special strut, I'll cunningly backpedal by a bit, and add a box with the +% appropriate depth. Since this will lie on the previous baseline, it won't +% alter the effective height of the box. There's a snag here. |\prevdepth| +% may be wrong for example if the last thing inserted was a rule, or the +% box is just empty. Check for this specially. (Thanks to Rowland for +% spotting this.) +% +% \begin{macrocode} + \ifdim\prevdepth>-\@m\p@\ifdim\prevdepth<\dp\@arstrutbox% + \kern-\prevdepth% + \nointerlineskip% + \vtop to\dp\@arstrutbox{}% + \fi\fi% +% \end{macrocode} +% +% I've finished the bottom of the box now: I'll close it, and start work on +% the top again. +% +% \begin{macrocode} + \egroup% +% \end{macrocode} +% +% For top-alignment to work, the first item in the box must be another box. +% (This is why I couldn't just set |\prevdepth| at the beginning.) If the +% box isn't high enough, I'll add a box of the right height and then kern +% backwards so that the `real' first box ends up in the right place. +% +% \begin{macrocode} + \ifdim\ht\z@<\ht\@arstrutbox% + \vbox to\ht\@arstrutbox{}% + \kern-\ht\z@% + \fi% + \unvbox\z@% + \egroup% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Gentle persuasion} +% +% To persuade \package{longtable} to work, we emulate some features of +% the \package{array} way of doing things. It's a shame, but we have to do +% it, because \package{longtable} came first. +% +% Note the horribleness with the grouping here. In order to get everything +% expanded at the right time, |\@preamble| just replaces itself with the (not +% expanded!) preamble string, using |\the|. This means that the preamble +% string must be visible in the group just above us. Now, +% \package{longtable} (and \package{array} for that matter) does +% |\@mkpreamble| immediately after opening a new group. So all we need to do +% is close that group, do our stuff, and reopen the group again. (Evil +% laughter\dots) +% +% \begin{macrocode} +\def\@mkpream#1{% + \endgroup% + \colset{tabular}% + \tab@initread% + \def\tab@multicol{\@arstrut}% + \tab@preamble{\tab@multicol}% + \def\tab@midtext{\ignorespaces\@sharp\@sharp\@maybe@unskip}% + \tab@readpreamble{#1}% + \gdef\@preamble{\the\tab@preamble}% + \let\tab@bgroup\begingroup% + \let\tab@egroup\endgroup% + \begingroup% +} +% \end{macrocode} +% +% +% \subsection{Debugging} +% +% This macro just parses a preamble and displays it on the terminal. It +% means I can see whether the thing's working. +% +% \begin{macrocode} +\def\showpream#1{% + \tab@initread% + \tab@readpreamble{#1}% + \showthe\tab@preamble% + \showthe\tab@shortline% +} +% \end{macrocode} +% +% A quick macro for showing column types. +% +% \begin{macrocode} +\def\showcol#1{% + \expandafter\show\csname\tab@colset!col.\string#1\endcsname% +} +% \end{macrocode} +% +% +% \subsection{The \env{tabular} and \env{array} environments} +% +% This is where we define the actual environments which users play with. +% +% \subsubsection{The environment routines} +% +% The real work is done in the |\@array| macro later. We just set up lots +% (and I mean \emph{lots}) of parameters first, and then call |\@array|. +% +% \begin{macro}{\tab@array} +% +% The |\tab@array| macro does most of the common array things. +% +% \begin{macrocode} +\def\tab@array{% + \tab@width\z@% + \let\tab@bgroup\tab@bmaths% + \let\tab@egroup\tab@emaths% + \@tabarray% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@btext} +% \begin{macro}{\tab@bmaths} +% \begin{macro}{\tab@etext} +% \begin{macro}{\tab@emaths} +% +% These macros contain appropriate things to use when typesetting +% text or maths macros. They're all trivial. They're here only for +% later modification by funny things like the \env{smarray} environment. +% +% \begin{macrocode} +\def\tab@btext{\begingroup} +\def\tab@bmaths{$} +\def\tab@etext{\endgroup} +\def\tab@emaths{\m@th$} +% \end{macrocode} +% +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{environment}{array} +% +% Now for the \env{array} environment. The `|$|' signs act as a group, so we +% don't need to do extra grouping this time. Closing the environment is +% easy. +% +% \begin{macrocode} +\def\array{% + \col@sep\arraycolsep% + \let\tab@extrasep\arrayextrasep% + \tab@normalstrut% + \tab@array% +} +\def\endarray{% + \crcr% + \egroup% + \tab@right% + \tab@restorehlstate% +} +% \end{macrocode} +% +% \end{environment} +% +% \begin{environment}{smarray} +% +% Now for something a little different. The \env{smarray} environment +% gives you an array with lots of small text. +% +% \begin{macrocode} +\def\smarray{% + \extrarowheight\z@% + \col@sep\smarraycolsep% + \let\tab@extrasep\smarrayextrasep% + \def\tab@bmaths{$\scriptstyle}% + \def\tab@btext{\begingroup\scriptsize}% + \setbox\z@\hbox{\scriptsize\strut}% + \dimen@\ht\z@\dimen\tw@\dp\z@\tab@setstrut% + \tab@array% +} +\let\endsmarray\endarray +% \end{macrocode} +% +% \end{environment} +% +% \begin{macro}{\tabstyle} +% +% This is a little hook that document designers can use to modify the +% appearance of tables throughout a document. For example, I've set it to +% make the text size |\small| in all tables in this document. Macro writers +% shouldn't try to use it as a hook for their own evilness, though. I've +% used |\providecommand| to avoid nobbling an existing definition. +% +% \begin{macrocode} +\providecommand\tabstyle{} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\@tabular} +% +% The two \env{tabular} environments share lots of common code, so we +% separate that out. (This needs to be done better.) All we really do here +% is set up the |\tab@bgroup| and |\tab@egroup| to localise things properly, +% and then go. +% +% \begin{macrocode} +\def\@tabular#1{% + \tabstyle% + \tab@width#1% + \let\tab@bgroup\tab@btext% + \let\tab@egroup\tab@etext% + \col@sep\tabcolsep% + \let\tab@extrasep\tabextrasep% + \tab@normalstrut% + \@tabarray% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{environment}{tabular} +% \begin{environment}{tabular*} +% +% These environments just call a macro which does all the common stuff. +% +% \begin{macrocode} +\def\tabular{\@tabular\z@} +\expandafter\let\csname tabular*\endcsname\@tabular +\let\endtabular\endarray +\expandafter\let\csname endtabular*\endcsname\endarray +% \end{macrocode} +% +% \end{environment} +% \end{environment} +% +% \subsubsection{Setting the strut height} +% +% \begin{macro}{\tab@setstrut} +% +% We use a magical strut, called |\@arstrut|, which keeps the table from +% collapsing around our heads. This is where we set it up. +% +% It bases the array strut size on the given values of |\dimen@| and +% |\dimen\tw@|, amended by various appropriate fiddle values added in by +% various people. +% +% \begin{macrocode} +\def\tab@setstrut{% + \setbox\@arstrutbox\hbox{% + \vrule% + \@height\arraystretch\dimen@% + \@depth\arraystretch\dimen\tw@% + \@width\z@% + }% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@normalstrut} +% +% This sets the strut the normal way, from the size of |\strutbox|. +% +% \begin{macrocode} +\def\tab@normalstrut{% + \dimen@\ht\strutbox\advance\dimen@\extrarowheight% + \dimen\tw@\dp\strutbox% + \tab@setstrut% +} +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{Setting up the alignment} +% +% The following bits are mainly for other packages to hook themselves onto. +% +% \begin{macrocode} +\let\@arrayleft\relax% +\let\@arrayright\relax% +% \end{macrocode} +% +% \begin{macrocode} +\def\@tabarray{% + \let\@arrayleft\relax% + \let\@arrayright\relax% + \@ifnextchar[\@array{\@array[c]}% +} +% \end{macrocode} +% +% \begin{macro}{\@array} +% +% The |\@array| macro does most of the real work for the environments. The +% first job is to set up the row strut, which keeps the table rows at the +% right height. We just take the normal strut box, and extend its height by +% the |\extrarowheight| length parameter. +% +% \begin{macrocode} +\def\@array[#1]#2{% +% \end{macrocode} +% +% Sort out the hline state variable. We'll store the old value in a +% control sequence to avoid wasting any more count registers. +% +% \begin{macrocode} + \edef\tab@restorehlstate{% + \global\tab@endheight\the\tab@endheight% + \gdef\noexpand\tab@hlstate{\tab@hlstate}% + }% + \def\tab@hlstate{n}% +% \end{macrocode} +% +% Now we read the preamble. All the clever things we've already done are +% terribly useful here. +% +% The |\tab@setcr| sets up |\\| to be a newline even if users have changed it +% using something like |\raggedright|. +% +% \begin{macrocode} + \colset{tabular}% + \tab@initread% + \def\tab@midtext{\tab@setcr\ignorespaces####\@maybe@unskip}% + \def\tab@multicol{\@arstrut\tab@startrow}% + \tab@preamble{\tab@multicol\tabskip\z@skip}% + \tab@readpreamble{#2}% +% \end{macrocode} +% +% Set up the default tabskip glue. This is easy: there isn't any. +% +% \begin{macrocode} + \tab@leftskip\z@skip% + \tab@rightskip\z@skip% +% \end{macrocode} +% +% Now set up the positioning of the table. This is put into a separate macro +% because it's rather complicated. +% +% \begin{macrocode} + \tab@setposn{#1}% +% \end{macrocode} +% +% Now work out how to start the alignment. +% +% \begin{macrocode} + \ifdim\tab@width=\z@% + \def\tab@halign{}% + \else% + \def\tab@halign{to\tab@width}% + \fi% +% \end{macrocode} +% +% Finally, do all the normal things we need to do before an alignment. Note +% that we define |\tabularnewline| first, then set |\\| from that (using +% |\tab@setcr|). Since |\\| is reset in the |\tab@midtext| of every table +% cell, it becomes secondary to |\tabularnewline|. Doing things this way +% avoids the problems with declarations like |\raggedright| which redefine +% |\\| in their own (usually rather strange) way, so you don't need to mess +% about with things like the |\PreserveBackslash| command given in the +% \textit{\LaTeX\ Companion}. +% +% \begin{macrocode} + \lineskip\z@\baselineskip\z@% + \m@th% + \def\tabularnewline{\tab@arraycr\tab@penalty}% + \tab@setcr% + \let\par\@empty% + \everycr{}\tabskip\tab@leftskip% + \tab@left\halign\tab@halign\expandafter\bgroup% + \the\tab@preamble\tabskip\tab@rightskip\cr% +} +% \end{macrocode} +% +% \end{macro} +% +% You've no doubt noticed the |\tab@left| and |\tab@right| macros above. +% These are set up here and elsewhere to allow other things to gain control +% at various points of the table (they include and take the place of the +% |\@arrayleft| and |\@arrayright| hooks in \package{array}, put in for +% \package{delarray}'s use. +% +% \subsubsection{Positioning the table} +% +% \begin{macro}{\tab@setposn} +% +% This macro sets everything up for the table's positioning. It's rather +% long, but not all that complicated. Honest. +% +% First, we set up some defaults (for centring). If anything goes wrong, we +% just do the centring things. +% +% \begin{macrocode} +\def\tab@setposn#1{% + \def\tab@left{% + \savenotes% + \leavevmode\hbox\bgroup$\@arrayleft\vcenter\bgroup% + }% + \def\tab@right{% + \egroup% + \m@th\@arrayright$\egroup% + \spewnotes% + }% + \global\tab@endheight\z@% +% \end{macrocode} +% +% For the standard positioning things, we just do appropriate boxing things. +% Note that the dollar signs are important, since \package{delarray} might +% want to put its delimiters in here. +% +% The |\if@tempswa| switch it used to decide if we're doing an unboxed +% tabular. We'll set it if we find an unbox-type position code, and then +% check that everything's OK for this. +% +% \begin{macrocode} + \@tempswafalse% + \let\tab@penalty\relax% + \if#1t% + \def\tab@left{% + \savenotes% + \leavevmode\setbox\z@\hbox\bgroup$\@arrayleft\vtop\bgroup% + }% + \def\tab@right{% + \egroup% + \m@th\@arrayright$\egroup% + \tab@raisebase% + \spewnotes% + }% + \gdef\tab@hlstate{t}% + \global\tab@endheight\ht\@arstrutbox% + \else\if#1b% + \def\tab@left{% + \savenotes% + \leavevmode\setbox\z@\hbox\bgroup$\@arrayleft\vbox\bgroup% + }% + \def\tab@right{% + \egroup% + \m@th\@arrayright$\egroup% + \tab@lowerbase% + \spewnotes% + }% + \gdef\tab@hlstate{b}% + \else% + \if#1L\@tempswatrue\fi% + \if#1C\@tempswatrue\fi% + \if#1R\@tempswatrue\fi% + \fi\fi% +% \end{macrocode} +% +% Now for some tests to make sure we're allowed to do the unboxing. We text +% for |\@arrayleft| being defined, because people trying to hook us won't +% understand unboxed tabulars. +% +% \begin{macrocode} + \if@tempswa\ifhmode% + \ifinner\tab@err@unbrh\@tempswafalse\else\par\fi% + \fi\fi% + \if@tempswa\ifmmode\tab@err@unbmm\@tempswafalse\fi\fi% + \if@tempswa\ifx\@arrayleft\relax\else% + \tab@err@unbext\@tempswafalse% + \fi\fi% +% \end{macrocode} +% +% Finally, if we're still doing an unboxed alignment, we need to sort out the +% spacing. We know that no-one's tried to hook on to the environment, so we +% clear |\tab@left| and |\tab@right|. +% +% \begin{macrocode} + \if@tempswa% + \def\tab@left{\vskip\parskip\medskip}% + \def\tab@right{\par\@endpetrue\global\@ignoretrue}% +% \end{macrocode} +% +% Now we need to sort out the alignment. The only way we can do this is by +% playing with tabskip glue. There are two possiblities: +% +% \begin{itemize} +% +% \item If this is a straight \env{tabular} or an \env{array}, we just use +% infinite glue. This is reasonable, I think. +% +% \item If we have a width for the table, we calculate the fixed values of +% glue on either side. This is fairly easy, and forces the table to +% the required width. +% +% \end{itemize} +% +% First, set up the left and right glues to represent the prevailing +% margins set up by \env{list} environments. I think this is the right +% thing to do. +% +% \begin{macrocode} + \tab@leftskip\@totalleftmargin% + \tab@rightskip\hsize% + \advance\tab@rightskip-\linewidth% + \advance\tab@rightskip-\@totalleftmargin% +% \end{macrocode} +% +% First of all, deal with the simple case. I'm using 10000\,fill glue here, +% in an attempt to suppress |\extracolsep| glue from making the table the +% wrong width. It can always use filll glue if it really needs to, though. +% +% \begin{macrocode} + \ifdim\tab@width=\z@% + \if#1L\else\advance\tab@leftskip\z@\@plus10000fill\fi% + \if#1R\else\advance\tab@rightskip\z@\@plus10000fill\fi% +% \end{macrocode} +% +% Now for the fun bit. This isn't too hard really. The extra space I must +% add around the table adds up to $|\linewidth| - |\tab@width|$. I just +% need to add this onto the appropriate sides of the table. +% +% \begin{macrocode} + \else% + \dimen@\linewidth% + \advance\dimen@-\tab@width% + \if#1L\advance\tab@rightskip\dimen@\fi% + \if#1R\advance\tab@leftskip\dimen@\fi% + \if#1C% + \advance\tab@leftskip.5\dimen@% + \advance\tab@rightskip.5\dimen@% + \fi% + \fi% +% \end{macrocode} +% +% Don't allow page breaks. David Carlisle's wonderful \env{longtable} +% package does page breaks far better than I could possibly do here, and +% we're compatible with it (wahey!). +% +% \begin{macrocode} + \def\tab@penalty{\penalty\@M}% +% \end{macrocode} +% +% Finally, set the new width of the table, and leave. +% +% \begin{macrocode} + \tab@width\hsize% + \fi% +} +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{Handling tops and bottoms} +% +% This is how the tops and bottoms of tables are made to line up with the +% text on the same line, in the presence of arbitrary rules and space. The +% old method, based on the way the \package{array} package worked, wasn't +% terribly good. This new version copes much better with almost anything +% that gets thrown at it. +% +% I'll keep a state in a macro (|\tab@hlstate|), which tells me what I'm +% meant to be doing. The possible values are \lit{n}, which means I don't +% have to do anything, \lit{t}, which means that I'm meant to be handling +% top-aligned tables, and \lit{b}, which means that I'm meant to be lining +% up the bottom. There are several other `substates' which have various +% magic meanings. +% +% \begin{macrocode} +\def\tab@hlstate{n} +% \end{macrocode} +% +% When all's said and done, I extract the box containing the table, and +% play with the height and depth to try and make it correct. +% +% \begin{macro}{\tab@addruleheight} +% +% This macro is called by `inter-row' things to add their height to our +% dimen register. +% +% Only do this if the state indicates that it's sensible. +% +% \begin{macrocode} +\def\tab@addruleheight#1{% + \if\tab@hlstate n\else% + \global\advance\tab@endheight#1\relax% + \fi% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@startrow} +% +% This is called at the start of a row, from within the array preamble. +% Currently, this assumes that the rows aren't bigger than their struts: +% this is reasonable, although slightly limiting, and it could be done better +% if I was willing to rip the alignment apart and put it back together +% again. +% +% \begin{macrocode} +\def\tab@startrow{% + \if\tab@hlstate t% + \gdef\tab@hlstate{n}% + \else\if\tab@hlstate b% + \global\tab@endheight\dp\@arstrutbox% + \fi\fi% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@raisebase} +% +% This macro is called at the end of it all, to set the height and depth +% of the box correctly. It sets the height to |\tab@endheight|, and the +% depth to everything else. The box is in |\box|~0 currently. +% +% \begin{macrocode} +\def\tab@raisebase{% + \global\advance\tab@endheight-\ht\z@% + \raise\tab@endheight\box\z@% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@lowerbase} +% +% And, for symmetry's sake, here's how to set the bottom properly instead. +% +% \begin{macrocode} +\def\tab@lowerbase{% + \global\advance\tab@endheight-\dp\z@% + \lower\tab@endheight\box\z@% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Breaking tables into bits} +% +% Unboxed tables have a wonderful advantage over boxed ones: you can stop +% halfway through and do something else for a bit. Here's how: +% +% \begin{macro}{\tabpause} +% +% I'd like to avoid forbidding catcode changes here. I'll use |\doafter| +% now I've got it, to ensure that colour handling and things occur +% \emph{inside} the |\noalign| (otherwise they'll mess up the alignment +% very seriously). +% +% We have to be careful here to ensure that everything works correctly within +% lists. (The \package{amsmath} package had this problem in its +% |\intertext| macro, so I'm not alone here.) +% +% \begin{macrocode} +\def\tabpause#{% + \noalign{\ifnum0=`}\fi% + \@parboxrestore% + \tab@startpause% + \vskip-\parskip% + \parshape\@ne\@totalleftmargin\linewidth% + \noindent% + \doafter\tabpause@i% +} +\def\tabpause@i{% + \nobreak% + \tab@endpause% + \ifnum0=`{\fi}% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{The wonderful world of \cmd\multicolumn} +% +% \begin{macro}{\multicolumn} +% +% This is actually fantasitcally easy. Watch and learn. Make sure you +% notice the |\long|s here: remember that some table cells can contain +% paragraphs, so it seems sensible to allow |\par| into the argument. +% (As far as I know, most other |\multicolumn| commands don't do this, +% which seems a little silly. Then again, I forgot to do it the first +% time around.) +% +% \begin{macrocode} +\long\def\multicolumn#1#2#3{% + \multispan{#1}% + \begingroup% + \tab@multicol% + \tab@initread% + \tab@preamble{}% + \long\def\tab@midtext{#3}% + \let\tab@looped\tab@err@multi% + \tab@readpreamble{#2}% + \the\tab@preamble% + \endgroup% + \ignorespaces% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Interlude: range lists} +% +% For processing arguments to |\vgap| and |\cline|, we need to be able to +% do things with lists of column ranges. To save space, and to make my +% fingers do less typing, here's some routines which do range handling. +% +% \begin{macro}{\ranges} +% +% Given a macro name and a comma separated list of ranges and simple numbers, +% this macro will call the macro giving it each range in the list in turn. +% Single numbers~$n$ will be turned into ranges $n$--$n$. +% +% The first job is to read the macro to do (which may already have some +% arguments attached to it). We'll also start a group to make sure that +% our changes to temp registers don't affect anyone else. +% +% There's a space before the delimiting |\q@delim| to stop numbers being +% parsed to far and expanding our quark (which will stop \TeX\ dead in its +% tracks). Since we use |\@ifnextchar| to look ahead, spaces in range lists +% are perfectly all right. +% +% \begin{macrocode} +\def\ranges#1#2{% + \gdef\ranges@temp{#1}% + \begingroup% + \ranges@i#2 \q@delim% +} +% \end{macrocode} +% +% +% We're at the beginning of the list. We expect either the closing marker +% (if this is an empty list) or a number, which we can scoop up into a +% scratch register. +% +% \begin{macrocode} +\def\ranges@i{% + \@ifnextchar\q@delim\ranges@done{\afterassignment\ranges@ii\count@}% +} +% \end{macrocode} +% +% We've read the first number in the range. If there's another number, we'll +% expect a `|-|' sign to be next. If there is no `|-|', call the user's code +% with the number duplicated and then do the rest of the list. +% +% \begin{macrocode} +\def\ranges@ii{% + \@ifnextchar-\ranges@iii{\ranges@do\count@\count@\ranges@v}% +} +% \end{macrocode} +% +% Now we strip the `|-|' off and read the other number into a temporary +% register. +% +% \begin{macrocode} +\def\ranges@iii-{\afterassignment\ranges@iv\@tempcnta} +% \end{macrocode} +% +% We have both ends of the range now, so call the user's code, passing it +% both ends of the range. +% +% \begin{macrocode} +\def\ranges@iv{\ranges@do\count@\@tempcnta\ranges@v} +% \end{macrocode} +% +% We've finished doing an item now. If we have a `|,|' next, then start +% over with the next item. Otherwise, if we're at the end of the list, +% we can end happily. Finally, if we're totally confused, raise an +% error. +% +% \begin{macrocode} +\def\ranges@v{% + \@ifnextchar,% + \ranges@vi% + {% + \@ifnextchar\q@delim% + \ranges@done% + {\tab@err@range\ranges@vi,}% + }% +} +% \end{macrocode} +% +% We had a comma, so gobble it, read the next number, and go round again. +% +% \begin{macrocode} +\def\ranges@vi,{\afterassignment\ranges@ii\count@} +% \end{macrocode} +% +% Here's how we call the user's code, now. We close the group, so that the +% user's code doesn't have to do global things to remember its results, and +% we expand the two range ends from their count registers. We also ensure +% that the range is the right way round. +% +% \begin{macrocode} +\def\ranges@do#1#2{% + \ifnum#1>#2\else% + \expandafter\endgroup% + \expandafter\ranges@temp% + \expandafter{% + \the\expandafter#1% + \expandafter}% + \expandafter{% + \the#2% + }% + \begingroup% + \fi% +} +% \end{macrocode} +% +% And finishing the scan is really easy. We close the group after gobbling +% the close token. +% +% \begin{macrocode} +\def\ranges@done\q@delim{\endgroup} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\ifinrange} +% +% Something a little more useful, now. |\ifinrange| takes four arguments: +% a number, a range list (as above), and two token lists which I'll call +% \emph{then} and \emph{else}. If the number is in the list, I'll do +% \emph{then}, otherwise I'll do \emph{else}. +% +% \begin{macrocode} +\def\ifinrange#1#2{% + \@tempswafalse% + \count@#1% + \ranges\ifinrange@i{#2}% + \if@tempswa% + \expandafter\@firstoftwo% + \else% + \expandafter\@secondoftwo% + \fi% +} +\def\ifinrange@i#1#2{% + \ifnum\count@<#1 \else\ifnum\count@>#2 \else\@tempswatrue\fi\fi% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Horizontal rules OK} +% +% This is where all the gubbins for |\vgap| and friends is kept, lest it +% contaminate fairly clean bits of code found elsewhere. +% +% \subsubsection{Drawing horizontal rules} +% +% \begin{macro}{\hline} +% +% Note the funny use of |\noalign| to allow \TeX\ stomach ops like +% |\futurelet| without starting a new table row. This lets us see if there's +% another |\hline| coming up, so we can see if we need to insert extra +% vertical space. +% +% \begin{macrocode} +\def\hline{% + \tab@dohline% + \noalign{\ifnum0=`}\fi% + \tab@penalty% + \futurelet\@let@token\hline@i% +} +% \end{macrocode} +% +% We check here for another |\hline| command, and insert glue if there is. +% This looks terrible, though, and |\hlx{hvh}| is much nicer. Still\dots +% +% \begin{macrocode} +\def\hline@i{% + \ifx\@let@token\hline% + \vskip\doublerulesep% + \tab@addruleheight\doublerulesep% + \fi% + \ifnum0=`{\fi}% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@dohline} +% +% This is where hlines actually get drawn. +% Drawing lines is more awkward than it used to be, particularly in unboxed +% tables. It used to be a case simply of saying |\noalign{\hrule}|. +% However, since unboxed tables are actually much wider than they look, this +% would make the rules stretch right across the page and look generally +% horrible. +% +% The solution is simple: we basically do a dirty big |\cline|. +% +% \begin{macrocode} +\def\tab@dohline{% + \multispan{\tab@columns}% + \leaders\hrule\@height\arrayrulewidth\hfil% + \tab@addruleheight\arrayrulewidth% + \cr% +} +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{Vertical rules} +% +% I couldn't fit these in anywhere else, so they'll have to go here. I'll +% provide a new optional argument which specifies the width of the rule; this +% gets rid of the problem described in the \emph{Companion}, where to get +% an unusually wide vertical rule, you have to play with things like +% \syntax{"\\vrule width" <dimen>} which really isn't too nice. +% +% \begin{macro}{\vline} +% +% The new |\vline| has an optional argument which gives the width of the +% rule. The |\relax| stops \TeX\ trying to parse a \<rule-specification> for +% too long, in case someone says something like `|\vline depthcharges|' or +% something equally unlikely. +% +% \begin{macrocode} +\renewcommand\vline[1][\arrayrulewidth]{\vrule\@width#1\relax} +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{Drawing bits of lines} +% +% Just for a bit of fun, here's an extended version of |\cline| which takes +% a list of columns to draw lines under, rather than just a single range. +% +% \begin{macro}{\cline} +% +% Not a single line of code written yet, and we already have a dilemma on +% our hands. Multiple consecutive |\cline| commands are meant to draw +% on the same vertical bit of table. But horizontal lines are meant to have +% thickness now. Oh, well [sigh], we'll skip back on it after all. +% +% Now the problem remains how best to do the job. The way I see it, there +% are three possibilities: +% +% \begin{itemize} +% +% \item We can start a table row, and then for each column of the table +% (as recorded in |\tab@columns|) we look to see if that column is +% listed in the range list and if so draw the rule. This requires +% lots of scanning of the range list. +% +% \item We can take each range in the list, and draw rules appropriately, +% just like the old |\cline| used to do, and starting a new table row +% for each. +% +% \item We can start a table row, and then for each range remember where we +% stopped drawing the last row, move to the start of the new one, and +% draw it. If we start moving backwards, we close the current row +% and open a new one. +% +% \end{itemize} +% +% The last option looks the most efficient, and the most difficult. This +% is therefore what I shall do |;-)|. +% +% The first thing to do is to add in a little negative space, and start a +% table row (omitting the first item). Then scan the range list, and finally +% close the table row and add some negative space again. +% +% We need a global count register to keep track of where we are. Mixing +% local and global assignments causes all sorts of tragedy, so I shall hijack +% |\tab@state|. +% +% \begin{macrocode} +\def\cline#1{% + \noalign{\kern-.5\arrayrulewidth\tab@penalty}% + \omit% + \global\tab@state\@ne% + \ranges\cline@i{#1}% + \cr% + \noalign{\kern-.5\arrayrulewidth\tab@penalty}% +} +% \end{macrocode} +% +% Now for the tricky bit. When we're given a range, we look to see if the +% first number is less than |\tab@state|. If so, we quickly close the +% current row, kern backwards and start again with an |\omit| and reset +% |\tab@state| to 1, and try again. +% +% \begin{macrocode} +\def\cline@i#1#2{% + \ifnum#1<\tab@state\relax% + \tab@@cr% + \noalign{\kern-\arrayrulewidth\tab@penalty}% + \omit% + \global\tab@state\@ne% + \fi% +% \end{macrocode} +% +% We are now either at or in front of the column position required. If +% we're too far back, we must |\hfil&\omit| our way over to the correct% +% column. +% +% \begin{macrocode} + \@whilenum\tab@state<#1\do{% + \hfil\tab@@tab@omit% + \global\advance\tab@state\@ne% + }% +% \end{macrocode} +% +% We've found the start correctly. We must deal with a tiny problem now: +% if this is not the first table cell, the left hand vertical rule is in the +% column to the left, so our horizontal rule won't match up properly. So +% we skip back by a bit to compensate. If there isn't actually a vertical +% rule to line up with, no-one will notice, because the rules are so thin. +% This adds a little touch of quality to the whole thing, which is after all +% the point of this whole exercise. +% +% \begin{macrocode} + \ifnum\tab@state>\@ne% + \kern-\arrayrulewidth% + \fi% +% \end{macrocode} +% +% Now we must stretch this table cell to the correct width. +% +% \begin{macrocode} + \@whilenum\tab@state<#2\do{% + \tab@@span@omit% + \global\advance\tab@state\@ne% + }% +% \end{macrocode} +% +% We're ready. Draw the rule. Note that this is |\hfill| glue, just in case +% we start putting in |\hfil| glue when we step onto the next cell. +% +% \begin{macrocode} + \leaders\hrule\@height\arrayrulewidth\hfill% +} +% \end{macrocode} +% +% Some alignment primitives are hidden inside macros so they don't get seen +% at the wrong time. This is what they look like: +% +% \begin{macrocode} +\def\tab@@cr{\cr} +\def\tab@@tab@omit{&\omit} +\def\tab@@span@omit{\span\omit} +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{Drawing short table rows} +% +% Before I start on a description of more code, I think I'll briefly discuss +% my reasons for leaving the |\vgap| command in its current state. There's a +% reasonable case for introducing an interface between |\vgap| and +% |\multicolumn|, to avoid all the tedious messing about with column +% ranges. There are good reasons why I'm not going to do this: +% +% \begin{itemize} +% +% \item It's very difficult to do: it requires either postprocessing of +% the table or delaying processing of each row until I know exactly +% what's in it; a |\multicolumn| in a row should be able to affect +% a |\vgap| before the row, which gets very nasty. This package is +% probably far too large already, and adding more complexity and +% running the risk of exhausting \TeX's frustratingly finite capacity +% for the sake of relieving the user of a fairly trivial job doesn't +% seem worthwhile. +% +% \item Perhaps more importantly, there are perfectly valid occasions when +% it's useful to have the current vgap behaviour. For example, the +% \texttt{MIX} word layout diagrams found in \emph{The Art of +% Computer Programming} use the little `stub lines' to show where +% data items cross byte boundaries: +% +% ^^A This actually looks terrifyingly similar to the original. +% ^^A The leading @{} is there to stop the table looking off-centre, +% ^^A because there's no left hand rule telling you where the table +% ^^A starts, like there is on the right, just the \tabcolsep glue. +% +% \begingroup +% \newcommand{\wide}[2]{\multicolumn{#1}{c|}{\ttfamily #2}} +% \begin{tabular}[C]{@{} r @{\qquad} | Mc | *{5}{c|}} \hlx{c{2-7} v} +% empty & - & 1 & 0 & 0 & 0 & 0 \\ \hlx{v c{2-7} v} +% occupied & + & \wide{2}{LINK} & \wide{3}{KEY} \\ \hlx{v c{2-7}} +% \end{tabular} +% \endgroup +% +% \end{itemize} +% +% That's my excuses out of the way; now I'll press on with the actual +% programming. +% +% \begin{macro}{\tab@checkrule} +% +% We have a range list in |\tab@xcols| and a number as an argument. If we +% find the number in the list, wejust space out the following group, +% otherwise we let it be. +% +% \begin{macrocode} +\def\tab@checkrule#1{% + \count@#1\relax% + \expandafter\ifinrange% + \expandafter\count@% + \expandafter{\tab@xcols}% + {\tab@checkrule@i}% + {}% +} +\def\tab@checkrule@i#1{\setbox\z@\hbox{#1}\hb@xt@\wd\z@{}} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\vgap} +% +% We must tread carefully here. A single misplaced stomach operation can +% cause error messages. We therefore start with an |\omit| so we can search +% for optional arguments. +% +% So that |\hlx| can get control after |\vgap| has finished, we provide a +% hook called |\vgap@after| which is expanded after |\vgap| has finished. +% Here we make it work like |\@empty|, which expands to nothing. (Note that +% |\relax| will start a new table row, so we can't use that.) There are +% some penalty items here to stick the |\vgap| row to the text row and +% |\hline| that are adjacent to it. The \package{longtable} package will +% split an |\hline| in half, so this is the correct thing to do. +% +% \begin{macrocode} +\def\vgap{% + \noalign{\nobreak}% + \omit% + \global\let\vgap@after\@empty% + \iffalse{\fi\ifnum0=`}\fi% + \@ifnextchar[\vgap@i\vgap@simple% +} +% \end{macrocode} +% +% We set up two different sorts of |\vgap| -- a simple one which allows all +% rules to be passed through, and a specific one which carefully vets each +% one (and is therefore slower). We decide which to so based on the presence +% of an optional argument. +% +% The optional argument handler just passes its argument to an interface +% routine which is used by |\hlx|. +% +% \begin{macrocode} +\def\vgap@i[#1]{\vgap@spec{#1}} +% \end{macrocode} +% +% Now we handle specified columns. Since we're in an omitted table cell, we +% must set things up globally. Assign the column spec to a macro, and set up +% vetting by the routine above. Then just go and do the job. +% +% \begin{macrocode} +\def\vgap@spec#1#2{% + \gdef\tab@xcols{#1}% + \global\let\tab@ckr\tab@checkrule% + \vgap@do{#2}% +} +% \end{macrocode} +% +% Handle all columns. Just gobble the column number for each rule, and let +% the drawing pass unharmed. Easy. +% +% \begin{macrocode} +\def\vgap@simple#1{% + \global\let\tab@ckr\@gobble% + \vgap@do{#1}% +} +% \end{macrocode} +% +% This is where stuff actually gets done. We set the |\vgap| flag on while +% we do the short row. Then just expand the token list we built while +% scanning the preamble. +% +% Note that the flag is cleared at the end of the last column, to allow other +% funny things like |\noalign| and |\omit| before a new row is started. +% +% \begin{macrocode} +\def\vgap@do#1{% + \ifnum0=`{}\fi% + \global\tab@vgaptrue% + \the\tab@shortline% + \vrule\@height#1\@width\z@% + \global\tab@vgapfalse + \tab@addruleheight{#1}% + \cr% + \noalign{\nobreak}% + \vgap@after% +} +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{Prettifying syntax} +% +% \begin{macro}{\hlx} +% +% This is like a poor cousin to the preamble parser. The whole loop is +% carefully written to take place \emph{only} in \TeX's mouth, so the +% alignment handling bits half way down the gullet don't see any of this. +% +% First, pass the string to another routine. +% +% \begin{macrocode} +\def\hlx#1{\hlx@loop#1\q@delim} +% \end{macrocode} +% +% Now peel off a token, and dispatch using |\csname|. We handle +% undefinedness of the command in a fairly messy way, although it probably +% works. Maybe. +% +% \begin{macrocode} +\def\hlx@loop#1{% + \ifx#1\q@delim\else% + \@ifundefined{hlx@cmd@\string#1}{% + \expandafter\hlx@loop% + }{% + \csname hlx@cmd@\string#1\expandafter\endcsname% + }% + \fi% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlxdef} +% +% New |\hlx| commands can be defined using |\hlxdef|. This is a simple +% abbreviation. +% +% \begin{macrocode} +\def\hlxdef#1{\@namedef{hlx@cmd@#1}} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlx h} +% +% Handle an \lit{h} character. Just do an |\hline| and return to the loop. +% We look ahead to see if there's another \lit{h} coming up, and if so +% insert two |\hline| commands. This strange (and inefficient) behaviour +% keeps packages which redefine |\hline| happy. +% +% \begin{macrocode} +\hlxdef h#1{% + \noalign{% + \ifx#1h% + \def\@tempa{\hline\hline\hlx@loop}% + \else% + \def\@tempa{\hline\hlx@loop#1}% + \fi% + \expandafter + }% + \@tempa% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlx b} +% +% The \lit{b} character does a nifty backspace, for \package{longtable}'s +% benefit. +% +% \begin{macrocode} +\hlxdef b{\noalign{\kern-\arrayrulewidth}\hlx@loop} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlx /} +% +% The `"/"' character allows a page break at the current position. +% +% \begin{macrocode} +\hlxdef /{% + \noalign{\ifnum0=`}\fi% + \@ifnextchar[\hlx@cmd@break@i{\hlx@cmd@break@i[0]}% +} +\def\hlx@cmd@break@i[#1]{\ifnum0=`{\fi}\pagebreak[0]\hlx@loop} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlx v} +% +% Handle a \lit{v} character. This is rather like the |\vgap| code above, +% although there are syntactic differences. +% +% \begin{macrocode} +\hlxdef v{% + \noalign{\nobreak}% + \omit% + \iffalse{\fi\ifnum0=`}\fi% + \global\let\vgap@after\hlx@loop% + \@ifnextchar[\hlx@vgap@i{\hlx@vgap@ii\vgap@simple}% +} +\def\hlx@vgap@i[#1]{% + \ifx!#1!% + \def\@tempa{\hlx@vgap@ii\vgap@simple}% + \else% + \def\@tempa{\hlx@vgap@ii{\vgap@spec{#1}}}% + \fi% + \@tempa% +} +\def\hlx@vgap@ii#1{% + \@ifnextchar[{\hlx@vgap@iii{#1}}{\hlx@vgap@iii{#1}[\doublerulesep]}% +} +\def\hlx@vgap@iii#1[#2]{#1{#2}} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlx s} +% +% Allow the user to leave a small gap using the \lit{s} command. +% +% \begin{macrocode} +\hlxdef s{% + \noalign{\ifnum0=`}\fi% + \nobreak% + \@ifnextchar[\hlx@space@i{\hlx@space@i[\doublerulesep]}% +} +\def\hlx@space@i[#1]{% + \vskip#1% + \tab@addruleheight{#1}% + \ifnum0=`{\fi}% + \hlx@loop% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlx c} +% +% We might as well allow a \lit{c} command to do a |\cline|. +% +% \begin{macrocode} +\hlxdef c#1{\cline{#1}\hlx@loop} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\hlx .} +% +% The \lit{.} character forces a start of the new column. There's a little +% problem here. Since the \lit{.} character starts the next column, we need +% to gobble any spaces following the |\hlx| command before the cell contents +% actually starts. Unfortunately, |\ignorespaces| will start the column for +% us, so we can't put it in always. We'll handle it here, then. We'll take +% the rest of the `preamble' string, and warn if it's not empty. Then we'll +% |\ignorespaces| -- this will start the column for us, so we don't need to +% |\relax| any more. +% +% \begin{macrocode} +\hlxdef .#1\q@delim{% + \ifx @#1@\else% + \PackageWarning{mdwtab}{% + Ignoring \protect\hlx\space command characters following a + `.'\MessageBreak command% + }% + \fi% + \ignorespaces% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Starting new table rows} +% +% We take a break from careful mouthery at last, and start playing with +% newlines. The standard one allows pagebreaks in unboxed tables, which +% isn't really too desirable. +% +% Anyway, we'll try to make this macro rather more reusable than the standard +% one. Here goes. +% +% \begin{macro}{\@arraycr} +% +% We pass lots of information to a main parser macro, and expect it to cope. +% +% \begin{macrocode} +\def\@arraycr{\tab@arraycr{}} +\def\tab@arraycr#1{\tab@cr{\tab@tabcr{#1}}{}{}} +% \end{macrocode} +% +% Now to actually do the work. |\tab@cr| passes us the skip size, and the +% appropriate one of the two arguments given above (both of which are empty) +% depending on the presence of the $*$. +% +% \begin{macrocode} +\def\tab@tabcr#1#2{% +% \end{macrocode} +% +% If the total height I need to add between rows (from the optional argument +% and the `extrasep' parameter) is greater than zero, I'll handle this by +% extending the strut slightly. I'm not actually sure whether this is the +% right thing to do, to be honest, although it's easier than trying to +% to an automatic |\vgap|, because I need to know which columns to skip. +% If the space is less than zero, I'll just insert the vertical space with +% in a |\noalign|. +% +% First, to calculate how much space needs adding. +% +% \begin{macrocode} + \dimen@#2% + \advance\dimen@\tab@extrasep% +% \end{macrocode} +% +% If the height is greater than zero, I need to play with the strut. I must +% bear in mind that the current table cell (which I'm still in, remember) +% may be in vertical mode, and I may or may not be in a paragraph. +% +% If I am in vertical mode, I'll backpedal to the previous box and put the +% strut in an hbox superimposed on the previous baseline. Otherwise, I can +% just put the strut at the end of the text. (This works in either LR +% or paragraph mode as long as I'm not between paragraphs.) Again, Rowland's +% empty cell bug strikes. (See |\tab@epar| for details.) +% +% \begin{macrocode} + \ifdim\dimen@>\z@% + \ifvmode% + \unskip\ifdim\prevdepth>-\@m\p@\kern-\prevdepth\fi% + \nointerlineskip\expandafter\hbox% + \else% + \@maybe@unskip\expandafter\@firstofone% + \fi% + {\advance\dimen@\dp\@arstrutbox\vrule\@depth\dimen@\@width\z@}% + \fi% +% \end{macrocode} +% +% This table cell works as a group (which is annoying here). I'll copy the +% interrow gap into a global register so that I can use it in the |\noalign|. +% +% \begin{macrocode} + \global\dimen\@ne\dimen@% + \cr% + \noalign{% + #1% + \ifdim\dimen\@ne<\z@\vskip\dimen\@ne\relax\fi% + }% + \@gobble% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@setcr} +% +% To set the |\\| command correctly in each table cell, we make it a part of +% the preamble (in |\tab@midtext|) to call this routine. It's easy -- just +% saves the preamble from being huge. +% +% \begin{macrocode} +\def\tab@setcr{\let\\\tabularnewline} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\tab@cr} +% +% Now we do the parsing work. This is fun. Note the revenge of the funny +% braces here. Nothing to worry about, honest. The tricky bit is to keep +% track of which arguments are which. (Thanks to David Carlisle for pointing +% out that I'd missed out the |\relax| here.) +% +% \begin{macrocode} +\def\tab@cr#1#2#3{% + \relax% + \iffalse{\fi\ifnum0=`}\fi% + \@ifstar{\tab@cr@i{#1}{#3}}{\tab@cr@i{#1}{#2}}% +} +\def\tab@cr@i#1#2{% + \@ifnextchar[{\tab@cr@ii{#1}{#2}}{\tab@cr@ii{#1}{#2}[\z@]}% +} +\def\tab@cr@ii#1#2[#3]{% + \ifnum0=`{}\fi% + #1{#3}{#2}% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Gratuitous grotesquery} +% +% So far we've had an easy-ish ride (or should that be \emph{queasy}?). Now +% for something unexplainably evil. We convince \LaTeX\ that it's loaded the +% \package{array} package, so that packages which need it think they've got +% it. +% +% The bogus date is the same as the date for the \package{array} package I've +% got here -- this will raise a warning if Frank updates his package which +% should filter back to me telling me that there's something I need to +% know about. +% +% The messing with |\xdef| and the funny parsing ought to insert the current +% \package{mdwtab} version and date into the fake \package{array} version +% string, giving a visible clue to the user that this isn't the real +% \package{array} package. +% +% \begin{macrocode} +\begingroup +\catcode`.=11 +\def\@tempa#1 #2 #3\@@{#1 #2} +\xdef\ver@array.sty + {1995/11/19 [mdwtab.sty \expandafter\@tempa\ver@mdwtab.sty\@@]} +\endgroup +% \end{macrocode} +% +% +% \subsection{Error messages} +% +% I've put all the error messages together, where I can find them, translate +% them or whatever. +% +% First, some token-space saving (which also saves my fingers): +% +% \begin{macrocode} +\def\tab@error{\PackageError{mdwtab}} +% \end{macrocode} +% +% Now do the error messages. +% +% \begin{macrocode} +\def\tab@err@misscol{% + \tab@error{Missing column type}{% + I'm lost. I was expecting something describing^^J% + the type of the current column, but you seem to^^J% + have missed it out. I've inserted a type `l'^^J% + column here in the hope that this makes sense.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tab@err@oddgroup{% + \tab@error{Misplaced group in table preamble}{% + I've found an open brace character in your preamble^^J% + when I was expecting a specifier character. I'm^^J% + going to gobble the whole group and carry on as if^^J% + I'd never seen it.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tab@err@undef#1{% + \tab@error{Unknown `\tab@colset' preamble character `\string#1'}{% + I don't understand what you meant by typing this^^J% + character. Anyway, I'll ignore it this time around.^^J% + Just don't you do it again.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tab@err@unbrh{% + \tab@error{Can't use unboxed tabular in LR mode}{% + You've asked for a tabular or array environment with^^J% + `L', `C' or `R' as the position specifier, but you're^^J% + in LR (restricted horizontal) mode, so it won't work.^^J% + I'll assume you really meant `c' and soldier on.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tab@err@unbmm{% + \tab@error{Can't use unboxed tabular in maths mode}{% + You've asked for a tabular or array environment with^^J% + `L', `C' or `R' as the position specifier, but you're^^J% + in maths mode, so it won't work. I'll pretend that^^J% + you really typed `c', and that this is all a bad dream.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tab@err@unbext{% + \tab@error{Can't extend unboxed tabulars}{% + You're trying to use kludgy extensions (e.g.,^^J% + `delarray') on an array or tabular with `L', `C'^^J% + or `R' as the position specifier. I'll assume you^^J% + subconsciously wanted a `c' type all along.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tab@err@multi{% + \tab@error{More than one column in a \protect\multicolumn}{% + You've put more than one column into a \string\multicolumn^^J% + descriptor. It won't work. I have no idea what^^J% + will happen, although it won't be pleasant. Hold^^J% + on tight now...% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\tab@err@range{% + \tab@error{Expected `,' or `<end>' in range list}{% + I was expecting either the end of the range list,^^J% + or a comma, followed by another range. I've^^J% + inserted a comma to try and get me back on track.^^J% + Good luck.% + }% +} +% \end{macrocode} +% +% That's it. No more. Move along please. +% +% \begin{macrocode} +%</mdwtab> +% \end{macrocode} +% +% +%^^A------------------------------------------------------------------------- +% \section{Implementation of \package{mathenv}} +% +% +% This is in a separate package, mainly to avoid wasting people's memory. +% +% \begin{macrocode} +%<*mathenv> +% \end{macrocode} +% +% +% \subsection{Options handling} +% +% We need to be able to cope with \textsf{fleqn} and \textsf{leqno} options. +% This will adjust our magic modified \env{eqnarray} environment +% appropriately. +% +% \begin{macrocode} +\newif\if@fleqn +\newif\if@leqno +\DeclareOption{fleqn}{\@fleqntrue} +\DeclareOption{leqno}{\@leqnotrue} +\ProcessOptions +% \end{macrocode} +% +% We use the \package{mdwtab} package for all its nice table handling things. +% (Oh, and to inflict it on users who want to do nice equations and don't +% care about our tables.) +% +% \begin{macrocode} +\RequirePackage{mdwtab} +% \end{macrocode} +% +% +% \subsection{Some useful registers} +% +% The old \LaTeX\ version puts the equation numbers in by keeping a count of +% where it is in the alignment. Since I don't know how may columns there are +% going to be, I'll just use a switch in the preamble to tell me to stop +% tabbing. +% +% \begin{macrocode} +\newif\if@eqalast +% \end{macrocode} +% +% Now define some useful length parameters. First allocate them: +% +% \begin{macrocode} +\newskip\eqaopenskip +\newskip\eqacloseskip +\newskip\eqacolskip +\newskip\eqainskip +\newskip\splitleft +\newskip\splitright +% \end{macrocode} +% +% Now assign some default values. Users can play with these if they really +% want although I can't see the point myself. +% +% \begin{macrocode} +\AtBeginDocument{% + \eqacloseskip\@centering% + \eqacolskip1.5em\@plus\@m\p@ + \eqainskip\z@% + \if@fleqn% + \eqaopenskip\mathindent% + \splitleft\mathindent\relax% + \splitright\mathindent\@minus\mathindent\relax% + \else% + \eqaopenskip\@centering% + \splitleft2.5em\@minus2.5em% + \splitright\splitleft% + \fi% + \relax% +} +% \end{macrocode} +% +% +% \subsection{A little display handling} +% +% I'm probably going a little far here, and invading territory already +% claimed by the \package{amsmath} stuff (and done a good deal better than +% I can be bothered to do), but just for completeness, this is how we handle +% attempts to put displays inside other displays without screwing up the +% spacing. +% +% \begin{macro}{\dsp@startouter} +% +% This is how we start an outermost display. It's fairly easy really. We +% make |\dsp@start| start an inner display, and make |\dsp@end| close the +% outer display. +% +% \begin{macrocode} +\def\dsp@startouter{% + \let\dsp@end\dsp@endouter% + $$% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\dsp@endouter} +% +% Ending the outer display is utterly trivial. +% +% \begin{macrocode} +\def\dsp@endouter{$$} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\dsp@startinner} +% +% Starting inner displays is done in a vbox (actually I choose |\vbox| or +% |\vtop| depending on the setting of \textsf{leqno} to put the equation +% number the right way round). +% +% \begin{macrocode} +\def\dsp@startinner{% + \let\dsp@end\dsp@endinner% + \if@fleqn\kern-\mathindent\fi% + \if@leqno\vtop\else\vtop\fi\bgroup% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\dsp@endinner} +% +% Ending an inner display is also really easy. +% +% \begin{macrocode} +\def\dsp@endinner{\egroup} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\dsp@start} +% +% This is what other bits of code uses to start displays. It's one of the +% start macros up above, and outer by default. +% +% \begin{macrocode} +\def\dsp@start{% + \ifmmode% + \ifinner\mth@err@mdsp\fi% + \expandafter\dsp@startinner% + \else% + \ifhmode\ifinner\mth@err@hdsp\fi\fi% + \expandafter\dsp@startouter% + \fi% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\dsp@tabpause} +% +% This sets up the correct pre- and postambles for the |\tabpause| macro in +% maths displays. This is fairly simple stuff. +% +% \begin{macrocode} +\def\dsp@tabpause{% + \def\tab@startpause% + {\penalty\postdisplaypenalty\vskip\belowdisplayskip}% + \def\tab@endpause% + {\penalty\predisplaypenalty\vskip\abovedisplayskip}% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{The \env{eqnarray} environment} +% +% We allow the user to play with the style if this is really wanted. I dunno +% why, really. Maybe someone wants very small alignments. +% +% \begin{macrocode} +\let\eqastyle\displaystyle +% \end{macrocode} +% +% \subsubsection{The main environments} +% +% \begin{environment}{eqnarray} +% \begin{environment}{eqnarray*} +% +% We define the toplevel commands here. They just add in default arguments +% and then call |\@eqnarray| with a preamble string. We handle equation +% numbers by setting up a default (|\eqa@defnumber|) which is put into +% the final column. At the beginning of each row, we globally |\let| +% |\eqa@number| equal to |\eqa@defnumber|. The |\eqnumber| macro just +% changes |\eqa@number| as required. Since |\eqa@number| is changed globally +% we must save it in this environment. +% +% First, we must sort out the optional arguments and things. This is really +% easy. The only difference between the starred and non-starred environments +% is the default definition of |\eqa@defnumber|. +% +% \begin{macrocode} +\def\eqnarray{% + \eqnarray@i\eqa@eqcount% +} +\@namedef{eqnarray*}{\eqnarray@i{}} +\def\eqnarray@i#1{\@ifnextchar[{\eqnarray@ii{#1}}{\eqnarray@ii{#1}[rcl]}} +% \end{macrocode} +% +% Right. Now for the real work. The first argument is the default numbering +% tokens; the second is the preamble string. +% +% \begin{macrocode} +\def\eqnarray@ii#1[#2]{% +% \end{macrocode} +% +% Set up the equation counter and labels correctly. +% +% \medskip\par\noindent|\begin{rant}|\par +% The hacking with |\@currentlabel| is here because (in the author's opinion) +% \LaTeX's |\refstepcounter| macro is broken. It's currently defined as +% \begin{listing} +%\def\refstepcounter#1{% +% \stepcounter{#1}% +% \protected@edef\@currentlabel% +% {\csname p@#1\endcsname\csname the#1\endcsname}% +%} +% \end{listing} +% which means that the current label gets `frozen' as soon as you do the +% counter step. By redefining the macro as +% \begin{listing} +%\def\refstepcounter#1{% +% \stepcounter{#1}% +% \edef\@currentlabel{% +% \expandafter\noexpand\csname p@#1\endcsname% +% \expandafter\noexpand\csname the#1\endcsname% +% }% +%} +% \end{listing} +% these sorts of problems would be avoided, without any loss of functionality +% or compatibility that I can see. +% \par\noindent|\end{rant}|\par +% +% \begin{macrocode} + \stepcounter{equation}% + \def\@currentlabel{\p@equation\theequation}% +% \end{macrocode} +% +% The next step is to set up the numbering. I must save the old numbering +% so I can restore it later (once in the alignment, I must assign these +% things globally). +% +% \begin{macrocode} + \let\eqa@oldnumber\eqa@number% + \def\eqa@defnumber{#1}% + \global\let\eqa@number\eqa@defnumber% +% \end{macrocode} +% +% The |\if@eqalastfalse| switch is false everywhere except when we're in the +% final column. +% +% \begin{macrocode} + \@eqalastfalse% +% \end{macrocode} +% +% Remove the |\mathsurround| kerning, since it will look very odd inside +% the display. We have our own spacing parameters for configuring these +% things, so |\mathsurround| is unnecessary. +% +% \begin{macrocode} + \m@th% +% \end{macrocode} +% +% Time to parse the preamble string now. I must choose the correct column +% set, initialise the preamble parser and set up the various macros. The% +% extra `|@{\tabskip\eqacloseskip}|' item sets up the tabskip glue to centre +% the alignment properly. +% +% \begin{macrocode} + \colset{eqnarray}% + \tab@initread% + \def\tab@tabtext{&\tabskip\z@skip}% + \tab@preamble{\tabskip\z@skip}% + \tab@readpreamble{#2@{\tabskip\eqacloseskip}}% + \dsp@tabpause% +% \end{macrocode} +% +% Now for some final setting up. The column separation is set from the +% user's parameter, the |\everycr| tokens are cleared, and I set up the +% newline command appropriately. +% +% \begin{macrocode} + \col@sep.5\eqainskip% + \everycr{}% + \let\\\@eqncr% +% \end{macrocode} +% +% Now start a maths display and do the alignment. Set up the left hand +% tabskip glue to centre the alignment, and do the actual alignment. +% The preamble used is mainly that generated from the user's string, although +% the stuff at the end is how we set up the equation number -- it repeats +% appropriately so we can always find it. +% +% \begin{macrocode} + \dsp@start% + \tabskip\eqaopenskip% + \halign to\displaywidth\expandafter\bgroup% + \the\tab@preamble% + &&\eqa@lastcol\hb@xt@\z@{\hss##}\tabskip\z@\cr% +} +% \end{macrocode} +% +% Now for the end of the environment. This is really easy. Set the final +% equation number, close the |\halign|, tidy up the equation counter (it's +% been stepped once too many times) and close the display. +% +% \begin{macrocode} +\def\endeqnarray{% + \eqa@eqnum% + \egroup% + \dsp@end% + \global\let\eqa@number\eqa@oldnumber% + \global\@ignoretrue% + \global\advance\c@equation\m@ne% +} +\expandafter\let\csname endeqnarray*\endcsname\endeqnarray +% \end{macrocode} +% +% \end{environment} +% \end{environment} +% +% Now we can define the column types. +% +% \begin{macrocode} +\colpush{eqnarray} +% \end{macrocode} +% +% Note the positioning of ord atoms in the stuff below. This will space out +% relations and binops correctly when they occur at the edges of columns, and +% won't affect ord atoms at the edges, because ords pack closely. +% +% First the easy onces. Just stick |\hfil| in the right places and +% everything will be all right. +% +% \begin{macrocode} +\coldef r{\tabcoltype{\hfil$\eqastyle}{{}$}} +\coldef c{\tabcoltype{\hfil$\eqastyle{}}{{}$\hfil}} +\coldef l{\tabcoltype{$\eqastyle{}}{$\hfil}} +\coldef x{\tabcoltype{\if@fleqn\else\hfil\fi$\eqastyle}{$\hfil}} +% \end{macrocode} +% +% Now for the textual ones. This is also fairly easy. +% +% \begin{macrocode} +\collet T [tabular]T +% \end{macrocode} +% +% Sort of split types of equations. I mustn't use |\rlap| here, or +% everything goes wrong -- |\\| doesn't get noticed by \TeX\ in the same way +% as |\cr| does. +% +% \begin{macrocode} +\coldef L{\tabcoltype{\hb@xt@2em\bgroup$\eqastyle}{$\hss\egroup}} +% \end{macrocode} +% +% The \lit{:} column type is fairly simple. +% +% \begin{macrocode} +\coldef :{\tabspctype{\tabskip\eqacolskip}} +\coldef q{\tabspctype{\quad}} +% \end{macrocode} +% +% The other column types just insert given text in an appropriate way. +% +% \begin{macrocode} +\collet > [tabular]> +\collet < [tabular]< +\collet * [tabular]* +\collet @ [tabular]@ +% \end{macrocode} +% +% Finally, the magical `|\magic|' column type, which sets the equation +% number. We set up the |\tabskip| glue properly, tab on, and set the flag +% which marks the final column. The |\eqa@lastcol| command is there to +% raise an error if the user tabs over to this column. I'll temporarily +% redefine it to |\@eqalasttrue| when I enter this column legitimately. +% The extra magical bits here will make the final column repeat, so that we +% can find it if necessary. Well is this column type named. +% +% That's it. We can return to normal now. +% +% \begin{macrocode} +\colpop +% \end{macrocode} +% +% \subsubsection{Newline codes} +% +% Newline sequences (|\\|) get turned into calls of |\@eqncr|. The job is +% fairly simple, really. +% +% \begin{macrocode} +\def\@eqncr{\tab@cr\eqacr@i\interdisplaylinepenalty\@M}% +\def\eqacr@i#1#2{% + \eqa@eqnum% + \noalign{\penalty#2\vskip\jot\vskip#1}% +} +% \end{macrocode} +% +% \subsubsection{Setting equation numbers} +% +% \begin{macro}{\eqa@eqpos} +% +% Before we start, we need to generalise the flush-left number handling bits. +% The macro |\eqa@eqpos| will put its argument in the right place. +% +% \begin{macrocode} +\if@leqno + \def\eqa@eqpos#1{% + \hb@xt@.01\p@{}\rlap{\normalfont\normalcolor\hskip-\displaywidth#1}% + } +\else + \def\eqa@eqpos#1{\normalfont\normalcolor#1} +\fi +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\eqa@eqnum} +% +% Here we typeset an equation number in roughly the right place. First I'll +% redefine |\eqa@lastcol| so that it tells me I'm in the right place, and +% start a loop to find that place. +% +% \begin{macrocode} +\def\eqa@eqnum{% + \global\let\eqa@lastcol\@eqalasttrue% + \eqa@eqnum@i% +} +% \end{macrocode} +% +% Now for the loop. The |\relax| here is absolutely vital -- it starts the +% table column, inserting useful tokens like `|\eqa@lastcol|' which tell +% me where I am in the alignment. Then, if I've reached the end, I can +% typeset the equation number; otherwise I go off into another macro and +% step on to the next column. +% +% \begin{macrocode} +\def\eqa@eqnum@i{% + \relax% + \if@eqalast% + \expandafter\eqa@eqnum@ii% + \else% + \expandafter\eqa@eqnum@iii% + \fi% +} +\def\eqa@eqnum@ii{% + \eqa@eqpos\eqa@number% + \global\let\eqa@number\eqa@defnumber% + \global\let\eqa@lastcol\eqa@@lastcol% + \cr% +} +\def\eqa@eqnum@iii{&\eqa@eqnum@i} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\eqa@lastcol} +% +% This is used as a marker for the final column in an \env{eqnarray} +% environment. By default it informs the user that they've been very +% silly and swallows the contents of the column. I'll redefine it to +% something more useful at appropriate times, and then turn it back again. +% +% \begin{macrocode} +\def\eqa@@lastcol{\mth@err@number\setbox\z@} +\let\eqa@lastcol\eqa@@lastcol +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{Numbering control} +% +% \begin{macro}{\eqnumber} +% +% The |\eqnumber| command sets the equation number on the current equation. +% This is really easy, actually. +% +% \begin{macrocode} +\newcommand\eqnumber[1][\eqa@eqcount]{\gdef\eqa@number{#1}} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\eqa@eqcount} +% +% This is how a standard equation number is set, stepping the counter and +% all. It's really easy and obvious. +% +% \begin{macrocode} +\def\eqa@eqcount{(\theequation)\global\advance\c@equation\@ne} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\nonumber} +% +% The \LaTeX\ |\nonumber| command could be defined by saying +% \begin{listing} +%\renewcommand{\nonumber}{\eqnumber[]} +% \end{listing} +% but I'll be slightly more efficient and redefine |\eqa@number| directly. +% +% \begin{macrocode} +\def\nonumber{\global\let\eqa@number\@empty} +% \end{macrocode} +% +% \end{macro} +% +% \subsubsection{The \env{eqnalign} environment} +% +% As a sort of companion to \env{eqnarray}, here's an environment which does +% similar things inside a box, rather than taking up the whole display width. +% It uses the same column types that we've already created, so there should +% be no problems. +% +% \begin{environment}{eqnalign} +% +% First, sort out some simple things like optional arguments. +% +% \begin{macrocode} +\def\eqnalign{\@ifnextchar[\eqnalign@i{\eqnalign@i[rcl]}} +\def\eqnalign@i[#1]{% + \@ifnextchar[{\eqnalign@ii{#1}}{\eqnalign@ii{#1}[c]}% +} +% \end{macrocode} +% +% Now we actually do the environment. This is fairly easy, actually. +% +% \begin{macrocode} +\def\eqnalign@ii#1[#2]{% + \let\\\eqn@cr% + \colset{eqnarray}% + \tab@initread% + \def\tab@tabtext{&\tabskip\z@skip}% + \tabskip\z@skip% + \col@sep.5\eqainskip% + \tab@readpreamble{#1}% + \everycr{}% + \if#2t\vtop\else% + \if#2b\vbox\else% + \vcenter% + \fi% + \fi% + \bgroup% + \halign\expandafter\bgroup\the\tab@preamble\cr% +} +% \end{macrocode} +% +% Finishing the environment is even simpler. +% +% \begin{macrocode} +\def\endeqnalign{% + \crcr% + \egroup% + \egroup% +} +% \end{macrocode} +% +% \end{environment} +% +% \begin{macro}{\eqn@cr} +% +% Newlines are really easy here. +% +% \begin{macrocode} +\def\eqn@cr{\tab@cr\eqn@cr@i{}{}} +\def\eqn@cr@i#1{\cr\noalign{\vskip\jot\vskip#1}\@gobble} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Simple multiline equations} +% +% As a sort of example and abbreviation, here's a multiline display +% environment which just centres everything. +% +% \begin{environment}{eqlines} +% +% We just get |\eqnarray| to do everything for us. This is really easy. +% +% \begin{macrocode} +\def\eqlines{\eqnarray[x]} +\let\endeqlines\endeqnarray +% \end{macrocode} +% +% \end{environment} +% +% \begin{environment}{eqlines*} +% +% There's a $*$ version which omits numbers. This is easy too. Lots of +% hacking with expansion here to try and reduce the number of tokens being +% used. Is it worth it? +% +% \begin{macrocode} +\expandafter\edef\csname eqlines*\endcsname{% + \expandafter\noexpand\csname eqnarray*\endcsname[x]% +} +\expandafter\let\csname endeqlines*\expandafter\endcsname + \csname endeqnarray*\endcsname +% \end{macrocode} +% +% \end{environment} +% +% +% \subsection{Split equations} +% +% Based on an idea from \textit{The \TeX book}, we provide some simple +% environments for doing split equations. These's plenty of scope for +% improvement here, though. +% +% \begin{environment}{spliteqn} +% \begin{environment}{spliteqn*} +% +% The only difference between these two is that the $*$-version doesn't put +% in an equation number by default (although this behaviour can be +% changed by |\eqnumber|). +% +% The fun here mainly concerns putting in the equation number at the right +% place -- for |leqno| users, we need to put the number on the first line; +% otherwise we put it on the last line. +% +% The way we handle this is to have two macros, |\\| (which clearly does +% all the user line breaks) and |\seq@lastcr| which is used at the end of +% the environment to wrap everything up. The |\seq@eqnocr| macro puts an +% equation number on the current line and then does a normal |\\|. It also +% resets |\\| and |\seq@lastcr| so that they don't try to put another +% equation number in. This must be done globally, although anyone who tries +% to nest maths displays will get what they deserve. +% +% For the non-$*$ environment, then, we need to step the equation counter, +% and set |\\| to |\seq@cr| or |\seq@eqnocr| as appropriate for the setting +% of the |leqno| flag -- |\seq@lastcr| always gets set to put an equation +% number in (because it will be reset if the number actually gets done +% earlier -- this catches stupid users trying to put a single row into +% a split environment). +% +% \begin{macrocode} +\def\spliteqn{% + \let\eqa@oldnumber\eqa@number% + \global\let\eqa@number\eqa@eqcount% + \spliteqn@i% +} +% \end{macrocode} +% +% For the $*$ variant, we don't need to bother with equation numbering, so +% this is really easy. +% +% \begin{macrocode} +\@namedef{spliteqn*}{% + \let\eqa@oldnumber\eqa@number% + \gdef\eqa@number{}% + \spliteqn@i% +} +% \end{macrocode} +% +% Ending the environments is easy. Most of the stuff here will be described +% later. +% +% \begin{macrocode} +\def\endspliteqn{% + \hfilneg\seq@lastcr% + \egroup% + \dsp@end% + \global\let\eqa@number\eqa@oldnumber% + \global\advance\c@equation\m@ne% +} +\expandafter\let\csname endspliteqn*\endcsname\endspliteqn +% \end{macrocode} +% +% \end{environment} +% \end{environment} +% +% \begin{macro}{\spliteqn@i} +% +% Here we handle the full display splits. Start a maths display, and make +% each row of the alignment take up the full display width. +% +% The macro |\seq@dosplit| does most of the real work for us -- setting up +% the alignment and so forth. The template column is interesting. There +% are two items glue on both sides of the actual text: +% +% \begin{itemize} +% +% \item Some glue which can shrink. This keeps the display from the edges +% of the page unless we get a really wide item. +% +% \item An |\hfil| to do the alignment. By default, this centres the +% equations. On the first line, however, we put a leading |\hfilneg| +% which cancels the first |\hfil|, making the first row left aligned. +% Similarly, at the end, we put an |\hfilneg| after the last equation +% to right align the last line. +% +% \end{itemize} +% +% We pass this information on as an argument. It's easy really. +% +% \begin{macrocode} +\def\spliteqn@i{% +% \end{macrocode} +% +% First, set up equation numbering properly. See my rant about +% |\refstepcounter| above. +% +% \begin{macrocode} + \stepcounter{equation}% + \def\@currentlabel{\p@equation\theequation}% +% \end{macrocode} +% +% Right; now to sort out the numbering and newline handling. If the number's +% meant to be on the first line (for \textsf{leqno} users), then it gets +% typeset on the first like; otherwise we just do a normal newline on +% all lines except the first. Once |\seq@eqnocr| has done its stuff, it +% redefines all the newline handling not to insert another number. +% +% \begin{macrocode} + \if@leqno% + \global\let\seq@docr\seq@eqnocr% + \else% + \global\let\seq@docr\seq@cr% + \fi% + \global\let\seq@lastcr\seq@eqnocr% +% \end{macrocode} +% +% For my next trick, I'll do some display handling -- start a (possibly +% nested) maths display, set up the |\tabpause| macro appropriately, and +% set the newline command to do the right thing. +% +% \begin{macrocode} + \dsp@start% + \dsp@tabpause% + \def\\{\seq@docr}% +% \end{macrocode} +% +% Finally, call another macro to do the remaining bits of setting up. +% +% \begin{macrocode} + \seq@dosplit% + {\hb@xt@\displaywidth{% + \hskip\splitleft\hfil$\displaystyle##$% + \hfil\hskip\splitright}}% + {\hfilneg}% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{environment}{subsplit} +% +% For doing splits in the middle of equations, we provide a similar +% environment. Here, we make |\\| just start a new line. We also use +% a |\vcenter| rather than a full maths display. The glue items are also +% a bit different: we use plain double-quads on each side of the item, and +% we need to remove them by hand at the extremeties of the environment. +% +% \begin{macrocode} +\def\subsplit{% + \let\\\seq@cr% + \vcenter\bgroup% + \seq@dosplit{\hfil\qquad$##$\qquad\hfil}{\hfilneg\hskip-2em}% +} +% \end{macrocode} +% +% Ending the environment is fairly easy. We remove the final glue item, +% and close the alignment and the vbox. +% +% \begin{macrocode} +\def\endsubsplit{% + \hfilneg\hskip-2em\cr% + \egroup\egroup% +} +% \end{macrocode} +% +% \end{environment} +% +% \begin{macro}{\seq@dosplit} +% +% Here we do most of the real work. Actually, since the preamble is passed +% in as an argument, most of the work is already done. The only thing to +% really note is the template for subsequent columns. To stop users putting +% in extra columns (which is where we put the equation number) we raise an +% error and discard the input in a scratch box register. This template is +% repeated infinitely so as to allow us to put the equation number in nicely. +% However, the final negative glue item won't work properly, so the equation +% will look awful. +% +% \begin{macrocode} +\def\seq@dosplit#1#2{% + \halign\bgroup% + #1&&\mth@err@number\setbox\z@\hbox{##}\cr% + #2\relax% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\seq@eqnocr} +% +% Here's how we set equation numbers. Since the column provided raises +% errors as soon as a token finds its way into it, we start with a |&\omit|. +% Then we just put the equation number in a zero-width box. Finally, we +% reset the newline commands to avoid putting in more than one equation +% number, and do normal newline things. +% +% \begin{macrocode} +\def\seq@eqnocr{% + &\omit% + \hb@xt@\z@{\hss\eqa@eqpos\eqa@number}% + \global\let\seq@docr\seq@cr% + \global\let\seq@lastcr\seq@cr% + \seq@cr% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\seq@cr} +% +% Newlines are very easy. We add a |\jot| of extra space, since this is +% a nice thing to do. +% +% \begin{macrocode} +\def\seq@cr{\tab@cr\seq@cr@i\interdisplaylinepenalty\@M} +\def\seq@cr@i#1#2{\cr\noalign{\penalty#2\vskip\jot\vskip#1}} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Matrix handling} +% +% There's been a complete and total overhaul of the spacing calculations +% for matrices here. The vertical spacing now bears an uncanny similarity +% to the rules \TeX\ uses to space out |\atop|-like fractions, the difference +% being that you can have more than one column in a matrix. This has the +% interesting side-effect that we get an \package{amsmath}-style +% sub/superscript environment almost free of charge with the matrix handling +% (it just ends up being a script-size single-column matrix). +% +% What is rather gratifying is that our \env{matrix} environment looks +% rather nicer than \package{amsmath}'s (which is based directly on +% \env{array}, giving it nasty restrictions on the numbers of columns and +% so on); in particular, the version here gives the `correct' result for +% Knuth's exercise~18.42 (which states categorically that a |\smallskip| +% should be placed between the rows of the big matrix). +% +% The reason the interrow space doesn't come out in the AMS version is +% that \env{array} inserts extra vertical space by extending the depth of +% the final row using a strut: the big matrix already extends deeper than +% this, so the strut doesn't make any difference. If the space was added +% by |\hlx{s[\smallskipamount]}| instead of the |\\| command, things would +% be different. +% +% \begin{figure} +% +% ^^A This is essentially what amsmath (version 1.2b) does. The real +% ^^A implementation requires a counter MaxMatrixCols, and has fewer braces: +% ^^A that's all the difference. Oh, and I turn off \arrayextrasep here, +% ^^A since amsmath doesn't expect it to be there (accurate emulation, see?) +% ^^A and I've used \hspace instead of \hskip since everything else is +% ^^A `proper' LaTeX stuff. +% +% \newenvironment{ams-pmatrix}{^^A +% \setlength{\arrayextrasep}{0pt}^^A +% \left(^^A +% \hspace{-\arraycolsep}^^A +% \begin{array}{*{10}{c}}^^A +% }{^^A +% \end{array}^^A +% \hspace{-\arraycolsep}^^A +% \right)^^A +% } +% +% \begin{demo}{Exercise 18.42 from \emph{The \TeX book}} +%\newcommand{\domatrix}[1]{ +% \def\mat##1 +% {\begin{#1}##1\end{#1}} +% \[ \begin{#1} +% \mat{a & b \\ c & d} & +% \mat{e & f \\ g & h} +% \\[\smallskipamount] +% 0 & +% \mat{i & j \\ k & l} +% \end{#1} +% \] +%} +%\domatrix{pmatrix} +%\domatrix{ams-pmatrix} +% \end{demo} +% +% \end{figure} +% +% \begin{environment}{genmatrix} +% +% The first job is to store my maths style and font away, because I'll be +% needing it lots later. +% +% \begin{macrocode} +\def\genmatrix#1#2#3#4#5{% + \let\mat@style#1% + \ifx#2\scriptstyle% + \let\mat@font\scriptfont% + \else\ifx#2\scriptscriptstyle% + \let\mat@font\scriptscriptfont% + \else% + \let\mat@font\textfont% + \fi\fi% +% \end{macrocode} +% +% Now to cope with inserted text. This is easy. +% +% \begin{macrocode} + \ifx\mat@style\scriptstyle% + \let\mat@textsize\scriptsize% + \else\ifx\mat@style\scriptscriptstyle% + \let\mat@textsize\scriptscriptsize% + \else% + \let\mat@textsize\relax% + \fi\fi% +% \end{macrocode} +% +% Now for some fun. I'll remember how to start and end the matrix in a +% couple of macros |\mat@left| and |\mat@right|. I haven't yet worked out +% exactly what needs to be in |\mat@right| yet, though, so I'll build that +% up in a scratch token list while I'm making my mind up. +% +% Initially, I want to open a group (to trap the style changes), set the +% maths style (to get the right spacing), insert the left delimiter, insert +% some spacing around the matrix, and start a centred box. The ending just +% closes all the groups and delimiters I opened. +% +% \begin{macrocode} + \def\mat@left{\bgroup\mat@style\left#4#3\vcenter\bgroup}% + \toks@{\egroup#3\right#5\egroup}% +% \end{macrocode} +% +% Now comes a slightly trickier bit. If the maths style is script or +% scriptscript, then I need to raise the box by a little bit to make it look +% really good. The right amount is somewhere around \smallf 3/4\,pt, I +% think, so that's what I'll use. +% +% \begin{macrocode} + \@tempswatrue% + \ifx\mat@style\displaystyle\else\ifx\mat@style\textstyle\else% + \@tempswafalse% + \setbox\z@\hbox\bgroup$% + \toks@\expandafter{\the\toks@$\m@th\egroup\raise.75\p@\box\z@}% + \fi\fi% +% \end{macrocode} +% +% If I'm not in maths mode right now, then I should enter maths mode, and +% remember to leave it later. +% +% \begin{macrocode} + \if@tempswa\ifmmode\else% + $\m@th% + \toks@\expandafter{\the\toks@$}% + \fi\fi% +% \end{macrocode} +% +% Now I've sorted out how to end the environment properly, so I can set up +% the macro, using |\edef|. +% +% \begin{macrocode} + \edef\mat@right{\the\toks@}% +% \end{macrocode} +% +% Now see if there's an optional argument. If not, create lots of centred +% columns. +% +% \begin{macrocode} + \@ifnextchar[\genmatrix@i{\genmatrix@i[[c]}% +} +% \end{macrocode} +% +% Now to sort out everything else. +% +% \begin{macrocode} +\def\genmatrix@i[#1]{% +% \end{macrocode} +% +% Some initial setting up: choose the correct column set, and set up some +% variables for reading the preamble. +% +% \begin{macrocode} + \colset{matrix}% + \tab@initread% +% \end{macrocode} +% +% Now comes some of the tricky stuff. The space between columns should be +% 12\,mu (by trial and error). We put the space in a box so we can measure +% it in the correct mathstyle. +% +% \begin{macrocode} + \setbox\z@\hbox{$\mat@style\mskip12mu$}% + \edef\tab@tabtext{&\kern\the\wd\z@}% + \tab@readpreamble{#1}% +% \end{macrocode} +% +% Now we need to decide how to space out the rows. The code here is based +% on the information in appendix~G of \emph{The \TeX book}: I think it'd be +% nice if my matrices were spaced out in the same way as normal fractions +% (particularly |\choose|y things). The standard |\baselineskip| and +% |\lineskip| parameters come in really handy here. +% +% The parameters vary according to the size of the text, so I need to see +% if we have scriptsize or less, or not. The tricky |\if| sorts this out. +% +% \begin{macrocode} + \if1\ifx\mat@style\scriptstyle1\else% + \ifx\mat@style\scriptscriptstyle1\else0\fi\fi% + \baselineskip\fontdimen10\mat@font\tw@% + \advance\baselineskip\fontdimen12\mat@font\tw@% + \lineskip\thr@@\fontdimen8\mat@font\thr@@% + \else% + \baselineskip\fontdimen8\mat@font\tw@% + \advance\baselineskip\fontdimen11\mat@font\tw@% + \lineskip7\fontdimen8\mat@font\thr@@% + \fi% + \lineskiplimit\lineskip% +% \end{macrocode} +% +% Now actually set up for the alignment. Assign |\\| to the correct value. +% Set up the |\tabskip|. Do the appropriate |\mat@left| thing set up above. +% And then start the alignment. +% +% \begin{macrocode} + \let\\\mat@cr% + \tabskip\z@skip% + \col@sep\z@% + \mat@left% + \halign\expandafter\bgroup\the\tab@preamble\tabskip\z@skip\cr% +% \end{macrocode} +% +% Now for a little hack to make the spacing consistent between matrices of +% the same height. This comes directly from \PlainTeX. This appears to +% make the spacing \emph{exactly} the same as the \TeX\ primites, oddly +% enough. +% +% \begin{macrocode} + \ifx\mat@font\textfont% + \omit$\mat@style\mathstrut$\cr\noalign{\kern-\baselineskip}% + \fi% +} +% \end{macrocode} +% +% Finishing the environment is really easy. We do the spacing hack again +% at the bottom, close the alignment and then tidy whatever we started in +% |\mat@left|. +% +% \begin{macrocode} +\def\endgenmatrix{% + \crcr% + \ifx\mat@font\textfont% + \omit$\mat@style\mathstrut$\cr\noalign{\kern-\baselineskip}% + \fi% + \egroup% + \mat@right% +} +% \end{macrocode} +% +% \end{environment} +% +% \begin{macro}{\mat@cr} +% +% Newlines are really easy. The $*$-form means nothing here, so we ignore +% it. +% +% \begin{macrocode} +\def\mat@cr{\tab@cr\mat@cr@i{}{}} +\def\mat@cr@i#1{\cr\noalign{\vskip#1}\@gobble} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\newmatrix} +% +% This is how we define new matrix environments. It's simple fun with +% |\csname| and |\expandafter|. +% +% \begin{macrocode} +\def\newmatrix#1#2{% + \@namedef{#1}{\genmatrix#2}% + \expandafter\let\csname end#1\endcsname\endgenmatrix% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{environment}{matrix} +% \begin{environment}{pmatrix} +% \begin{environment}{dmatrix} +% \begin{environment}{smatrix} +% \begin{environment}{spmatrix} +% \begin{environment}{sdmatrix} +% \begin{environment}{smatrix*} +% \begin{environment}{spmatrix*} +% \begin{environment}{sdmatrix*} +% +% Now we define all the other environments we promised. This is easy. +% +% \begin{macrocode} +\newmatrix{matrix}{{\textstyle}{\textstyle}{\,}{.}{.}} +\newmatrix{pmatrix}{{\textstyle}{\textstyle}{\,}{(}{)}} +\newmatrix{dmatrix}{{\textstyle}{\textstyle}{\,}} +\newmatrix{smatrix}{{\scriptstyle}{\scriptstyle}{}{.}{.}} +\newmatrix{spmatrix}{{\scriptstyle}{\scriptstyle}{}{(}{)}} +\newmatrix{sdmatrix}{{\scriptstyle}{\scriptstyle}{}} +\newmatrix{smatrix*}{{\scriptstyle}{\textstyle}{}{.}{.}} +\newmatrix{spmatrix*}{{\scriptstyle}{\textstyle}{}{(}{)}} +\newmatrix{sdmatrix*}{{\scriptstyle}{\textstyle}{}} +% \end{macrocode} +% +% \end{environment} +% \end{environment} +% \end{environment} +% \end{environment} +% \end{environment} +% \end{environment} +% \end{environment} +% \end{environment} +% \end{environment} +% +% \begin{environment}{script} +% +% Now for superscripts and subscripts. This is fairly easy, because I +% took so much care over the matrix handling. +% +% \begin{macrocode} +\def\script{% + \let\mat@style\scriptstyle% + \def\mat@left{\vcenter\bgroup}% + \def\mat@right{\egroup}% + \let\mat@font\scriptfont% + \let\mat@textsize\scriptsize% + \@ifnextchar[\genmatrix@i{\genmatrix@i[c]}% +} +\let\endscript\endgenmatrix +% \end{macrocode} +% +% \end{environment} +% +% Now define the column types. +% +% \begin{macrocode} +\colpush{matrix} +\coldef l{\tabcoltype{\kern\z@$\mat@style}{\m@th$\hfil}} +\coldef c{\tabcoltype{\hfil$\mat@style}{\m@th$\hfil}} +\coldef r{\tabcoltype{\hfil$\mat@style}{\m@th$}} +\coldef T#1{\tab@aligncol{#1}{\begingroup\mat@textsize}{\endgroup}} +% \end{macrocode} +% +% The repeating type is more awkward. Things will go wrong if this is +% given before the first column, so we must do a whole repeat by hand. We +% can tell if we haven't contributed a column yet, since |\tab@column| will +% be zero. Otherwise, we fiddle the parser state to start a new column, and +% insert the |&| character to make \TeX\ repeat the preamble. +% +% \begin{macrocode} +\coldef {[}{% + \@firstoftwo{% + \ifnum\tab@columns=\z@% + \def\@tempa##1\q@delim{% + \tab@mkpreamble##1[##1\q@delim% + }% + \expandafter\@tempa% + \else% + \tab@setstate\tab@prestate% + \tab@append\tab@preamble{&}% + \expandafter\tab@mkpreamble% + \fi% + }% +} +% \end{macrocode} +% +% We're done defining columns now. +% +% \begin{macrocode} +\colpop +% \end{macrocode} +% +% +% \subsection{Dots\dots} +% +% Nothing whatsoever to do with alignments, although vertical and diagonal +% dots in small matrices look really silly. The following hacky definitions +% work rather better. +% +% \begin{macro}{\mdw@dots} +% +% First of all, here's some definitions common to both of the dots macros. +% The macro takes as an argument the actual code to draw the dots, passing +% it the scaled size of a point in the scratch register |\dimen@|; the +% register |\box 0| is set to contain a dot of the appropriate size. +% +% \begin{macrocode} +\def\mdw@dots#1{\ensuremath{\mathpalette\mdw@dots@i{#1}}} +\def\mdw@dots@i#1#2{% + \setbox\z@\hbox{$#1\mskip1.8mu$}% + \dimen@\wd\z@% + \setbox\z@\hbox{$#1.$}% + #2% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\vdots} +% +% I'll start with the easy one. This is a simple translation of the original +% implementation. +% +% \begin{macrocode} +\def\vdots{% + \mdw@dots{\vbox{% + \baselineskip4\dimen@% + \lineskiplimit\z@% + \kern6\dimen@% + \copy\z@\copy\z@\box\z@% + }}% +} +% \end{macrocode} +% +% \end{macro} +% +% \begin{macro}{\ddots} +% +% And I'll end with the other easy one\dots +% +% \begin{macrocode} +\def\ddots{% + \mdw@dots{\mathinner{% + \mkern1mu% + \raise7\dimen@\vbox{\kern7\dimen@\copy\z@}% + \mkern2mu% + \raise4\dimen@\copy\z@% + \mkern2mu% + \raise\dimen@\box\z@% + \mkern1mu% + }}% +} +% \end{macrocode} +% +% \end{macro} +% +% +% \subsection{Lucky dip} +% +% Time to round off with some trivial environments, just to show how easy +% this stuff is. +% +% \begin{environment}{cases} +% \begin{environment}{smcases} +% +% These are totally and utterly trivial. +% +% \begin{macrocode} +\def\cases{\left\{\,\array{@{}lTl@{}}} +\def\endcases{\endarray\,\right.} +\def\smcases{\left\{\smarray{@{}lTl@{}}} +\def\endsmcases{\endsmarray\,\right.} +% \end{macrocode} +% +% \end{environment} +% \end{environment} +% +% \subsection{Error messages} +% +% Some token saving: +% +% \begin{macrocode} +\def\mth@error{\PackageError{mathenv}} +% \end{macrocode} +% +% Now for the error messages. +% +% \begin{macrocode} +\def\mth@err@number{% + \mth@error{Too many `&' characters found}{% + You've put too many `&' characters in an alignment^^J% + environment (like `eqnarray' or `spliteqn') and wandered^^J% + into trouble. I've gobbled the contents of that column^^J% + and hopefully I can recover fairly easily.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\mth@err@mdsp{% + \mth@error{Can't do displays in nondisplay maths mode}{% + You're trying to start a display environment, but you're^^J% + in nondisplay maths mode. The display will appear but^^J% + don't blame me when it looks horrible.% + }% +} +% \end{macrocode} +% +% \begin{macrocode} +\def\mth@err@hdsp{% + \mth@error{Can't do displays in LR mode}{% + You're trying to start a display environment, but you're^^J% + in LR (restricted horizontal) mode. Everything will go^^J% + totally wrong, so your best bet is to type `X', fix the^^J% + mistake and start again.% + }% +} +% \end{macrocode} +% +% \vskip\parskip\vbox{ ^^A The best way I could find of keeping this lot +% ^^A together, I'm afraid. +% That's all there is. Byebye. +% +% \begin{macrocode} +%</mathenv> +% \end{macrocode} +% \nopagebreak +% +% \hfill Mark Wooding, \today +% } +% +% \Finale +% +\endinput |