X-Git-Url: https://git.distorted.org.uk/~mdw/sod/blobdiff_plain/82567ea97e4f9374e8a3d586ea49ad9bf3dff3dc..83f56c86d40ed056b2f8d01ec4c8148335cc4c86:/doc/sod.sty

diff --git a/doc/sod.sty b/doc/sod.sty
index 69b4d3d..592ff5c 100644
--- a/doc/sod.sty
+++ b/doc/sod.sty
@@ -73,7 +73,8 @@
 %% Diagram settings.
 \tikzset{
   every picture/.style={>=stealth, thick},
-  lit/.style={font=\sffamily}
+  lit/.style={font=\sffamily},
+  dashed/.style={dash pattern=on 2.19pt off 2.19pt}
 }
 
 %% Metavariables are italics without decoration.
@@ -106,8 +107,8 @@
 
 %% Intercept grammar typesetting and replace the vertical bar with the
 %% maths-font version.
-\let\@@grammar\grammar
-\def\grammar{\def\textbar{\hbox{$|$}}\@@grammar}
+\let\@@syn@shorts\syn@shorts
+\def\syn@shorts{\def\textbar{\hbox{$|$}}\@@syn@shorts}
 
 %% Collect super- and subscripts.  (Note that underscores are active for the
 %% most part.)  When we're done, end maths mode if we entered it
@@ -189,8 +190,17 @@
 %%%--------------------------------------------------------------------------
 %%% Environment for setting programs.
 
+%% Save `\kill' so that `longtable' won't clobber it.
+\let\prog@@kill\kill
+
 %% Main guts of `prog' and `nprog'.
-\def\@prog{\let\prog@@cr\@tabcr\let\@tabcr\@progcr\codeface\tabbing}
+\def\@prog{%
+  \let\prog@@cr\@tabcr%
+  \let\@tabcr\@progcr%
+  \let\kill\prog@@kill%
+  \codeface%
+  \tabbing%
+}
 
 %% Newlines: called by `\\' within a `prog'.
 %%
@@ -237,6 +247,41 @@
 %% Show a backslash by the right-hand margin; for multiline macros etc.
 \def\macsl{\`\textbackslash\hskip\leftmargin}
 
+%% \maplist{THING}{{ITEM}...}: Invoke THING{ITEM} for each ITEM in turn.
+\def\maplist#1#2{\map@i{#1}#2\q@}
+\def\map@i#1{\def\next@{\map@ii{#1}}\futurelet\ch@\next@}
+\def\map@ii#1{\ifx\ch@\q@\expandafter\@gobble%
+  \else\def\next@{\map@iii{#1}}\expandafter\next@\fi}
+\def\map@iii#1#2{#1{#2}\map@i{#1}}
+
+%% \crossproduct{THING}{{LIST}...} where each LIST is {ITEM}...
+%% For each possible way of selecting one ITEM from each LIST, in order,
+%% invoke THING{{ITEM}...}
+\toksdef\cprod@new=0
+\toksdef\cprod@old=2
+\toksdef\cprod@head=4
+\toksdef\cprod@tail=6
+\def\crossproduct#1#2{%
+  \cprod@new{{}}%
+  \maplist{\cprod@f{#1}}{#2}%
+  \cprod@head{#1}%
+  \edef\next@{\noexpand\maplist{\the\cprod@head}{\the\cprod@new}}
+  \next@%
+}
+\def\cprod@f#1#2{%
+  \cprod@old\cprod@new\cprod@new{}%
+  \maplist\cprod@g{#2}%
+}
+\def\cprod@g#1{%
+  \cprod@head{#1}%
+  \expandafter\maplist\expandafter\cprod@h\expandafter{\the\cprod@old}%
+}
+\def\cprod@h#1{%
+  \cprod@tail{#1}%
+  \cprod@new\expandafter{\the\expandafter\cprod@new\expandafter{%
+      \the\expandafter\cprod@tail\the\cprod@head}}%
+}
+
 %%%--------------------------------------------------------------------------
 %%% Machinery for describing functions, etc.
 
@@ -261,7 +306,14 @@
 \def\@descname@i[#1]#2{%
   \expandafter\let\expandafter\@tempa\csname cat!#2\endcsname%
   \expandafter\let\expandafter\@tempb\csname modcat/#1\endcsname%
-  \ifx\@tempa\relax\@tempb{#2}\else\@tempa\@tempb\fi}
+  \ifx\@tempa\relax\PackageError{sod}{unknown description category `#2'}%
+    {I don't know what `#2' means as a thing you might try to\MessageBreak
+     document.  Maybe you mistyped it, or forgot to say\MessageBreak
+     `\protect\definedescribecategory' for it.  For now I'm going\MessageBreak
+     just use the literal string `#2' and hope that doesn't look\MessageBreak
+     too terrible.}%
+    \@tempb{#2}%
+  \else\@tempa\@tempb\fi}
 
 %% Call a modifier method.
 \def\@mod@dispatch#1#2{\csname #1/#2\endcsname}
@@ -274,10 +326,10 @@
   \endcsname%
 }
 
-%% Modifier methods for the default empty modifier.
-\@namedef{modcat/}#1{#1}
-\@namedef{modlabel/}#1{#1}
-\@namedef{modindex/}#1{#1@\noexpand\code{#1}}
+%% Modifier methods for the default `plain' modifier.
+\@namedef{modcat/plain}#1{#1}
+\@namedef{modlabel/plain}#1{#1}
+\@namedef{modindex/plain}#1{#1@\noexpand\code{#1}}
 
 %% Modifier methods for `setf'.  The name text prefixes the relevant word
 %% with `setf-'.
@@ -288,28 +340,29 @@
 %% Modifier methods for words with *earmuffs*.  Sort into the index without
 %% the earmuffs.
 \@namedef{modcat/muffs}#1{#1}
-\@namedef{modlabel/muffs}#1{*#1*}
-\@namedef{modindex/muffs}#1{#1@\noexpand\code{*#1*}}
+\@namedef{modlabel/muffs}#1{#1}
+\@namedef{modindex/muffs}#1{\@unmuff#1@\noexpand\code{#1}}
+\def\@unmuff*#1*{#1}
 
 %% Modifier methods for :keywords.  Sort into the index without the `:'.
 \@namedef{modcat/kwd}#1{#1}
-\@namedef{modlabel/kwd}#1{:#1}
-\@namedef{modindex/kwd}#1{#1@\noexpand\code{:#1}}
+\@namedef{modlabel/kwd}#1{#1}
+\@namedef{modindex/kwd}#1{\@gobble#1@\noexpand\code{#1}}
 
 %% Category-kind methods for plain categories.
 \@namedef{descargs/plain}#1{#1{}}
-\@namedef{desclabel/plain}#1#2#3{#1:\@mod@dispatch{modlabel}{#2}{#3}}
+\@namedef{desclabel/plain}#1#2#3{#2:\@mod@dispatch{modlabel}{#1}{#3}}
 \@namedef{descindex/plain}#1#2#3{%
-  \@mod@dispatch{modindex}{#2}{#3}!%
-  \protect\describecategoryname[#2]{#1}%
+  \@mod@dispatch{modindex}{#1}{#3}!%
+  \protect\describecategoryname[#1]{#2}%
 }
 
 %% Category-kind methods for `method' categories.  Collect an extra argument
 %% listing the specializers: include them in the label, and typeset them in
 %% the index.
-\def\@fmtspecs#1#2{%
-  \if!#2!\else\count@\z@\toks@{}#1\@fmtspecs@i#2,\q@,\fi}
-\def\@fmtspecs@i#1,{%
+\def\fmtspecs#1#2{%
+  \if!#2!\else\count@\z@\toks@{}#1\fmtspecs@i#2,\q@,\fi}
+\def\fmtspecs@i#1,{%
   \def\@tempa{#1}%
   \ifx\@tempa\q@%
     \ifcase\count@(no args?!)%
@@ -322,16 +375,16 @@
     \ifnum\count@>\@ne, \fi%
     \the\toks@%
     \toks@{\code{#1}}\advance\count@\@ne%
-    \expandafter\@fmtspecs@i%
+    \expandafter\fmtspecs@i%
   \fi%
 }
 \@namedef{descargs/method}#1#2{#1{{#2}}}
 \@namedef{desclabel/method}#1#2#3#4%
-  {#1:\@mod@dispatch{modlabel}{#2}{#3}(#4)}
+  {#2:\@mod@dispatch{modlabel}{#1}{#4}(#3)}
 \@namedef{descindex/method}#1#2#3#4{%
-  \@mod@dispatch{modindex}{#2}{#3}!%
-  \protect\describecategoryname[#2]{#1}%
-  \protect\@fmtspecs{ specialized at }{#4}%
+  \@mod@dispatch{modindex}{#1}{#4}!%
+  \protect\describecategoryname[#1]{#2}%
+  \protect\fmtspecs{ specialized at }{#3}%
 }
 
 %% Some magic strings.
@@ -366,7 +419,7 @@
 \def\parse@dhd@cb#1#2#3{%  {NEXT}{CAT}{{...}...}
   \@ifnextchar[{\parse@dhd@cc{#1}{#2}{#3}}{\parse@dhd@cd{#1}{#2}{#3}}}
 \def\parse@dhd@cc#1#2#3[#4]#5{%  {NEXT}{CAT}{{...}...}[NAME]{SYNOPSIS}
-  #1{}{#2}{#3}{#4}{#5}}
+  #1{plain}{#2}{#3}{#4}{#5}}
 \def\parse@dhd@cd#1#2#3#4{%  {NEXT}{CAT}{{...}...}{SYNOPSIS}
   \parse@dhd@ce{#1}{#2}{#3}{#4}#4 \q@}
 \def\parse@dhd@ce#1#2#3#4#5 #6\q@{%
@@ -374,42 +427,44 @@
   \def\temp@{#5}%
   \ifx\@setf\temp@\def\next@{\parse@dhd@csetf{#1}{#2}{#3}{#4}#6 \q@}%
   \else\def\temp@##1##2*##3\q@{\def\temp@{##1##3}}\temp@#5*\q@%
-    \ifx\temp@\@starstar\def\next@{\parse@dhd@cmuffs{#1}{#2}{#3}{#4}#5}%
+    \ifx\temp@\@starstar\def\next@{#1{muffs}{#2}{#3}{#5}{#4}}%
     \else\def\temp@##1##2\q@{\def\temp@{##1}}\temp@#5\q@%
-      \if:\temp@\def\next@{\parse@dhd@ckwd{#1}{#2}{#3}{#4}#5\q@}%
-      \else\def\next@{#1{}{#2}{#3}{#5}{#4}}\fi\fi\fi%
+      \if:\temp@\def\next@{#1{kwd}{#2}{#3}{#5}{#4}}%
+      \else\def\next@{#1{plain}{#2}{#3}{#5}{#4}}\fi\fi\fi%
   \next@%
 }
 \def\parse@dhd@csetf#1#2#3#4(#5 #6\q@{%
   % {NEXT}{CAT}{{...}...}{SYNOPSIS}(NAME [ARGS...])\q@
   #1{setf}{#2}{#3}{#5}{#4}}
-\def\parse@dhd@cmuffs#1#2#3#4*#5*{%
-  % {NEXT}{CAT}{{...}...}{SYNOPSIS}*NAME*
-  #1{muffs}{#2}{#3}{#5}{#4}}
-\def\parse@dhd@ckwd#1#2#3#4:#5\q@{%
-  % {NEXT}{CAT}{{...}...}{SYNOPSIS}:NAME\q@
-  #1{kwd}{#2}{#3}{#5}{#4}}
 
 %% \dhead[MOD]{CAT}{...}...[NAME]{SYNOPSIS}
+%% \dhead*[MOD]{CAT}{...}...[NAME]{SYNOPSIS}
 %%
 %% Typeset a description head.  Use this within the first argument of
 %% `describe*'; see `describe' for the details.
+%%
+%% With `*', don't set labels or add items to the index.
 \newif\if@dheadfirst
-\def\dhead{\parse@dhd\dhead@}
+\newif\if@dheadindex
+\def\dhead{\@ifstar%
+  {\parse@dhd{\global\@dheadindexfalse\dhead@}}%
+  {\parse@dhd{\global\@dheadindextrue\dhead@}}}
 \def\dhead@#1#2#3#4#5{%  {MOD}{CAT}{{...}...}{NAME}{SYNOPSIS}
   \if@dheadfirst\global\@dheadfirstfalse\else\relax\\*[\smallskipamount]\fi%
-  \phantomsection%
-  {\let\protect\@empty\let\@uscore\relax%
-   \edef\temp@{\@desc@dispatch{desclabel}{#2}{#2}{#1}{#4}#3}%
-   \def\@uscore{_\@gobble}\expandafter\message\expandafter{\temp@}%
-   \def\@uscore{-\@gobble}\expandafter\label\expandafter{\temp@}}%
-  {\begingroup\lccode`\~=`\_\lowercase{\endgroup\def~{_}}%
-   \protected@edef\@tempa##1{%
-     \noexpand\index{\@desc@dispatch{descindex}{#2}{#2}{#1}{#4}#3##1}}%
-   \toks@\expandafter{\@tempa{|)}}%
-   \toks\tw@\expandafter{\after@desc}%
-   \xdef\after@desc{\the\toks\tw@\the\toks@}%
-   \@tempa{|(}}%
+  \if@dheadindex%
+    \phantomsection%
+    {\let\protect\@empty\let\@uscore\relax%
+     \edef\temp@{\@desc@dispatch{desclabel}{#2}{#1}{#2}#3{#4}}%
+     \def\@uscore{_\@gobble}\expandafter\message\expandafter{\temp@}%
+     \def\@uscore{-\@gobble}\expandafter\label\expandafter{\temp@}}%
+    {\begingroup\lccode`\~=`\_\lowercase{\endgroup\def~{_}}%
+     \protected@edef\@tempa##1{%
+       \noexpand\index{\@desc@dispatch{descindex}{#2}{#1}{#2}#3{#4}##1}}%
+     \toks@\expandafter{\@tempa{|)}}%
+     \toks\tw@\expandafter{\after@desc}%
+     \xdef\after@desc{\the\toks\tw@\the\toks@}%
+     \@tempa{|(}}%
+  \fi%
   \rlap{\hb@xt@\linewidth{\hfil\normalfont\bfseries
       [\describecategoryname[#1]{#2}]}}%
   #5%
@@ -455,12 +510,14 @@
 %%
 %%   * `setf (NAME ...) ...': selects NAME, and defaults MOD to `setf'.
 %%
-%%   * `*NAME*': selects NAME, without the earmuffs, and defaults MOD to
-%%     `muffs'.
+%%   * `*NAME*': defaults MOD to `muffs'.
 %%
-%%   * `:NAME': selects NAME, withtout the colon, and defaults MOD to `kwd'.
+%%   * `:NAME': defaults MOD to `kwd'.
 \def\describe{\parse@dhd\desc@}
-\def\desc@#1#2#3#4#5{\desc@begin{\dhead@{#1}{#2}{#3}{#4}{#5}}}
+\def\desc@#1#2#3#4#5{%
+  \global\@dheadindextrue%
+  \desc@begin{\dhead@{#1}{#2}{#3}{#4}{#5}}%
+}
 \let\enddescribe\desc@end
 
 %% \begin{describe*}
@@ -474,8 +531,29 @@
 \@namedef{describe*}#1{\desc@begin{#1}}
 \expandafter\let\csname enddescribe*\endcsname\desc@end
 
-%% \descref{CAT}{...}...{LABEL}[TEXT]
-%% \descref*{CAT}{...}...{LABEL}
+%% \parse@dlbl{NEXT}[MOD]{CAT}{...}...{LABEL}
+%%
+%% Parse a description label, and call
+%%
+%%      NEXT{MOD}{CAT}{{...}...}{LABEL}
+%%
+%% This handles defaulting the MOD correctly, based on the LABEL text.  See
+%% `\descref' for the details.
+\def\parse@dlbl#1{\@ifnextchar[{\parse@dlbl@a{#1}}{\parse@dlbl@c{#1}}}
+\def\parse@dlbl@a#1[#2]#3{\@desc@dispatch{descargs}{#3}{#1{#2}{#3}}}
+\def\parse@dlbl@c#1#2%
+  {\@desc@dispatch{descargs}{#2}{\parse@dlbl@cb{#1}{#2}}}
+\def\parse@dlbl@cb#1#2#3#4{%
+  \def\temp@##1##2*##3\q@{\def\temp@{##1##3}}\temp@#4*\q@%
+    \ifx\temp@\@starstar\def\next@{#1{muffs}{#2}{#3}{#4}}%
+    \else\def\temp@##1##2\q@{\def\temp@{##1}}\temp@#4\q@%
+      \if:\temp@\def\next@{#1{kwd}{#2}{#3}{#4}}
+      \else\def\next@{#1{plain}{#2}{#3}{#4}}\fi\fi%
+  \next@%
+}
+
+%% \descref[MOD]{CAT}{...}...{LABEL}[TEXT]
+%% \descref*[MOD]{CAT}{...}...{LABEL}
 %%
 %% Typesets a cross-reference to a described thing.  The CAT names the
 %% category of thing being described, and the LABEL names the specific thing.
@@ -483,35 +561,79 @@
 %% The {...}... are any additional arguments required by the category's kind
 %% (e.g., method specializers).
 %%
-%% The precise rules for how the LABEL matches the name in the description
-%% depend on the description's modifier:
+%% The MOD is the modifier to apply, similar (but subtly different from) to
+%% the `describe' environment.  If omitted, it will usually default to
+%% `plain', but in the absence of a NAME, some kinds of synopses are
+%% recognized specially:
+%%
+%%   * `*NAME*': defaults MOD to `muffs'.
 %%
-%%   * `plain': the LABEL is the same as the NAME.
-%%   * `setf': the LABEL should be `setf/NAME'.
-%%   * `muffs': the LABEL should be `*NAME*', i.e., with the earmuffs
-%%     restored.
-%%   * `kwd: the LABEL should be `:NAME', i.e., with the colon restored.
+%%   * `:NAME': defaults MOD to `kwd'.
+%%
+%% (`setf' is /not/ specially detected here.  Write an explicit `setf'
+%% modifier if necessary, because it's no more typing.)
 %%
 %% Usually a page-number cross-reference is included, so as to help readers
 %% of a dead-tree copy; this is suppressed by the `*' version.
 \def\descref{\@ifstar%
-  {\descref@i{}\@gobble{}}%
-  {\descref@i{ (}{\noexpand\autopageref}{)}}}
-\def\descref@i#1#2#3#4#5{\@ifnextchar@preserve[%
-  {\descref@ii{#1}{#2}{#3}{#4}{#5}}%
-  {\descref@iii{#1}{#2}{#3}{#4}{#5}{}}}
-\def\descref@ii#1#2#3#4#5[#6]{\descref@iii{#1}{#2}{#3}{#4}{#5}{ #6}}
-\def\descref@iii#1#2#3#4#5#6{%
+  {\parse@dlbl{\descref@i\relax\@gobble\relax}}%
+  {\parse@dlbl{\descref@i{ (}\autopageref)}}}
+\def\descref@i#1#2#3#4#5#6#7{\@ifnextchar@preserve[%
+  % {PGA}{PGB}{PGC}{MOD}{CAT}{{...}...}{LABEL}
+  {\descref@ii{#1}{#2}{#3}{#4}{#5}{#6}{#7}}%
+  {\descref@iii{#1}{#2}{#3}{#4}{#5}{#6}{#7}{}}}
+\def\descref@ii#1#2#3#4#5#6#7[#8]%
+  % {PGA}{PGB}{PGC}{MOD}{CAT}{{...}...}{LABEL}[AFTER]
+  {\descref@iii{#1}{#2}{#3}{#4}{#5}{#6}{#7}{ #8}}
+\def\descref@iii#1#2#3#4#5#6#7#8{%
+  % {PGA}{PGB}{PGC}{MOD}{CAT}{{...}...}{LABEL}{AFTER}
+  \begingroup%
+    \let\protect\@empty\def\@uscore{-\@gobble}%
+    \edef\temp@{\@desc@dispatch{desclabel}{#5}{#4}{#5}#6{#7}}%
+    \edef\next@##1##2##3{\endgroup%
+      \noexpand\hyperref[\temp@]{##1}##2{\temp@}##3}%
+  \next@{\code{#7}}{#8#1#2}{#3}%
+}
+
+%% \descindex[MOD]{CAT}{...}...{LABEL}[SUFFIX]
+%%
+%% Set a label and index entry here, as if for a description.  The CAT names
+%% the category of thing being described, and the LABEL names the specific
+%% thing, as for `\descref'.  The {...}... are any additional arguments
+%% required by the category's kind (e.g., method specializers).  The MOD is
+%% the modifier to apply; see `\descref' for the details.
+%%
+%% The SUFFIX is appended to the index-entry text; by default it is empty.
+%% Useful values are `|(' and `|)' to set ranges.
+\def\descindex{\parse@dlbl\descindex@i}
+\def\descindex@i#1#2#3#4{\@ifnextchar[%
+  {\descindex@ii{#1}{#2}{#3}{#4}}%
+  {\descindex@ii{#1}{#2}{#3}{#4}[]}}
+\def\descindex@ii#1#2#3#4[#5]{%
+  {\begingroup\lccode`\~=`\_\lowercase{\endgroup\def~{_}}%
+   \protected@edef\@tempa{%
+     \noexpand\index{\@desc@dispatch{descindex}{#2}{#1}{#2}#3{#4}#5}}%
+   \@tempa}%
+}
+
+%% \desclabel[MOD]{CAT}{...}...{LABEL}[INDEX-SUFFIX]
+%%
+%% Set a label and index entry here, as if for a description.  The CAT names
+%% the category of thing being described, and the LABEL names the specific
+%% thing, as for `\descref'.  The {...}... are any additional arguments
+%% required by the category's kind (e.g., method specializers).  The MOD is
+%% the modifier to apply; see `\descref' for the details.
+%%
+%% This will also add an index entry, as for `\descindex'; the INDEX-SUFFIX
+%% argument has the same effect as its SUFFIX argument.
+\def\desclabel{\parse@dlbl\desclabel@i}
+\def\desclabel@i#1#2#3#4{%
   \begingroup%
-    \let\protect\@empty%
-    \def\@uscore{-\@gobble}%
-    \edef\@tempa##1{%
-      \endgroup%
-      \noexpand\hyperref[#4:#5]%
-      ##1%
-      #2{#4:#5}%
-    }%
-  \@tempa{{\code{#5}}#6#1}#3%
+    \let\protect\@empty\def\@uscore{-\@gobble}%
+    \edef\@tempa{\@desc@dispatch{desclabel}{#2}{#1}{#2}#3{#4}}%
+    \phantomsection\label{\@tempa}%
+  \endgroup%
+  \descindex@i{#1}{#2}{#3}{#4}%
 }
 
 %% Description categories.
