diff options
Diffstat (limited to 'macros/latex/contrib/amsrefs/rkeyval.dtx')
-rw-r--r-- | macros/latex/contrib/amsrefs/rkeyval.dtx | 1127 |
1 files changed, 1127 insertions, 0 deletions
diff --git a/macros/latex/contrib/amsrefs/rkeyval.dtx b/macros/latex/contrib/amsrefs/rkeyval.dtx new file mode 100644 index 0000000000..271c0694a8 --- /dev/null +++ b/macros/latex/contrib/amsrefs/rkeyval.dtx @@ -0,0 +1,1127 @@ +% \def\filename{rkeyval.dtx} +% \def\fileversion{2.00} +% \def\filedate{2004/06/28} +% +% \iffalse meta-comment +% +% American Mathematical Society +% Technical Support +% Publications Technical Group +% 201 Charles Street +% Providence, RI 02904 +% USA +% tel: (401) 455-4080 +% (800) 321-4267 (USA and Canada only) +% fax: (401) 331-3842 +% email: tech-support@ams.org +% +% Copyright 2001, 2010 American Mathematical Society. +% +% This work may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3c +% of this license or (at your option) any later version. +% The latest version of this license is in +% http://www.latex-project.org/lppl.txt +% and version 1.3c or later is part of all distributions of LaTeX +% version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is the American Mathematical +% Society. +% +% \fi +% +% \iffalse +%<*driver> +\documentclass[draft,oneside]{amsdtx} +\pagestyle{myheadings} + +\makeatletter +\DeclareRobustCommand{\fld}{\@category@index{field}} +\makeatother + +\DoNotIndex{\@emptytoks,\@temptokenb} + +\CodelineIndex + +\begin{document} +\title{The \pkg{rkeyval} package:\protect\linebreak[1] + Syntactically restricted key-value scanning} +\author{Michael Downes and David M. Jones\\American Mathematical Society} +\date{Version \fileversion, \filedate} +\DocInput{rkeyval.dtx} +\PrintIndex +\end{document} +%</driver> +% \fi +% +% \MakeShortVerb{\|} +% +% \maketitle +% \markboth{The \protect\pkg{rkeyval} package} +% {Version \protect\fileversion, \protect\filedate} +% +% \tableofcontents +% +% \section{Introduction} +% +% The \pkg{rkeyval} package provides functions for scanning key-value +% notation similar to the kind of scanning supported by the standard +% \pkg{keyval} package. However, the syntax is more restrictive in +% order to make some improved error-checking possible. In particular, +% if a comma is omitted between two instances of |key={value}| +% form, the \cn{RestrictedSetKeys} command will spot the missing +% comma and issue a suitable error message (and it will be given at +% the point where the missing comma is detected, before reading any +% further in the \tex/ file). The standard \cn{setkeys} command, by +% contrast, will append the second key name to the value of the first +% key and discard the second value, without any notification to the +% user that anything has gone wrong. But that is partly because the +% standard \cn{setkeys} command allows implied values and does not +% require braces around explicit values (except when necessary to +% hide material that has a syntactic resemblance to a key-value +% pair). With \cn{RestrictedSetKeys} the value must always be present +% and it must be enclosed in braces. +% +%^^A Maybe commas should be optional. +% +% Further restrictions of the \cn{RestrictedSetKeys} command and its +% companion commands reduce memory consumption in certain ways. +% Defining a key creates only one control sequence, a container for +% holding the value. Processing of key values is normally limited to +% storing a value given by the user; any additional processing must +% be supplied separately by the programmer. +% +% Generally speaking, the error-checking done by +% \cn{RestrictedSetKeys} is better for applications where all the +% keys are expected to have textual values, while \cn{setkeys} is +% better when one wants to silently recover as far as possible from +% syntactic errors, instead of notifying the user of the errors; or +% when keys have nontrivial default values (i.e., not empty) or other +% kinds of special processing. +% +% \begin{verbatim} +% \RestrictedSetKeys{setup-code}{group}{code}{key={val}, ...} +%\end{verbatim} +% Normally \cn{RestrictedSetKeys} simply carries out the following +% assignment for each key-value pair: +% \begin{verbatim} +% \def\group'key{val} +%\end{verbatim} +% The first argument is normally empty, but the exact nature of the +% warnings given and other aspects of the processing can be affected +% by putting extra setup code there. The \pkg{amsrefs} package uses +% this to implement a copying operation where field name and value +% are written out immediately to another file instead of being stored +% in the usual way. +% +% Some examples for defining the key names associated with a given +% group. This defines ``title'' as a recognized key for the \fn{bib} +% group: +% \begin{verbatim} +% \DefineSimpleKey{bib}{title} +%\end{verbatim} +% If a key is defined with \cs{DefineSimpleKey}, the result of using +% the same key more than once in a single entry will be an error +% message. +% +% This defines ``title'' to be a repeatable key: +% \begin{verbatim} +% \DefineSupersedingKey{bib}{title} +%\end{verbatim} +% If it occurs more than once, the last value supersedes the earlier +% ones, instead of getting an error. This variation is not needed for +% simple usage, but in more complicated situations where key values +% are combined from multiple sources, it may be useful. +% +% This defines ``author'' to be a repeatable key, with each value +% being appended to a list of values: +% \begin{verbatim} +% \DefineAdditiveKey{bib}{author}{\name} +%\end{verbatim} +% The third argument specifies a wrapper function that should be +% applied to each item in the list. I.e., suppose that two author +% names are given: +% \begin{verbatim} +% author={Smith, J. Q.}, +% author={Taylor, X. Y.}, +%\end{verbatim} +% Then they will be stored in the form +% \begin{verbatim} +% \name{Smith, J. Q.}\name{Taylor, X. Y.} +%\end{verbatim} +% +% This defines ``transition'' to be a dummy key with a value that is +% superficially nonempty but effectly empty: +% \begin{verbatim} +% \DefineDummyKey{bib}{transition} +%\end{verbatim} +% Defining a dummy key like this can be useful in dealing with +% certain boundary situations that sometimes arise. +% +% \StopEventually{} +% +% \section{Implementation} +% Standard declaration of package name and date. +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e} +\ProvidesPackage{rkeyval}[2004/05/05 v1.08] +% \end{macrocode} +% +% \begin{macro}{\@xp} +% \begin{macro}{\@nx} +% \begin{macrocode} +\let\@xp\expandafter +\let\@nx\noexpand +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@gobblethree} +% \begin{macro}{\@nilgobble} +% Not in the \latex/ kernel yet. +% \begin{macrocode} +\long\def\@gobblethree#1#2#3{} +\long\def\@nilgobble#1\@nil{} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@emptytoks} +% Using \cs{@ifundefined} here avoids problems with really old +% versions of \latex/ that choke on \cs{newtoks} if it is written +% directly in the false branch of a conditional. +% \begin{macrocode} +\@ifundefined{@emptytoks}{\csname newtoks\endcsname\@emptytoks}{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@temptokenb} +% \begin{macrocode} +\@ifundefined{@temptokenb}{\csname newtoks\endcsname\@temptokenb}{} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@append} +% \begin{macrocode} +\def\@append#1#2#3{\@xp\def\@xp#2\@xp{#2#1{#3}}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\star@} +% Test for a trailing option marked by a star. Usage: +% \begin{verbatim} +% \newcommand{\blub}[1]{\star@{\blubaux{#1}}{default}} +%\end{verbatim} +% Arg 1 of \cs{star@} is the code to +% be run, arg 2 is the default value of the option (could be empty). +% If arg 1 is \ncn{moo}, this test discards a star and expands to +% \ncn{moo} if a star is found, or expands to |\moo{#2}| if +% not. As the example shows, arg 1 need not be a single token. +% \begin{macrocode} +\def\star@#1#2{% + \def\star@a##1{#1}% + \def\star@b{#1{#2}}% + \futurelet\@let@token\star@test +} + +\def\star@test{\ifx*\@let@token \let\star@b\star@a\fi \star@b} +% \end{macrocode} +% +% Please note: If there is a space before the star, then the star is +% not treated as an option char. +% +% Why use a star? Since it's already part of standard \latex/ +% command syntax, it's unlikely to suffer from catcode changes. +% +% Why not just put the star at the beginning in the usual way? It +% seemed to me that the lack of a trailing option +% feature was a deficiency in current \latex/ and could be given an +% experimental implementation in a package like this without any +% adverse effect on existing documents. +% \end{macro} +% +% Ensure non-weird catcode for relevant characters. +% \begin{macrocode} +\@ifundefined{NormalCatcodes}{\RequirePackage{pcatcode}\relax}{} +\PushCatcodes\NormalCatcodes +% \end{macrocode} +% +% \begin{macro}{\extract@group} +% Extracts ``group'' from |\group'field|. +% \begin{macrocode} +\def\extract@group#1{% + \@xp\extract@group@a\string#1\@nil +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\extract@group@a} +% \begin{macrocode} +\def\extract@group@a#1#2'{#2\@nilgobble} +% \end{macrocode} +% \end{macro} +% +% \section{Data structures} +% +% The result of scanning the key/value pairs is an assignment statement +% for \cs{rsk@toks}. For example, consider the entry +% \begin{verbatim} +% \bib{LelekZ1962}{article}{ +% author={Lelek, A.}, +% author={Zaremba, D.}, +% title={Dimensions of irreducible ...}, +% journal={Fund. Math.}, +% date={1962/63}, +% } +%\end{verbatim} +% The scanned result is to assign +% \begin{verbatim} +% \global\rsk@toks{% +% \set:bib'author{Lelek, A.}{}% +% \set:bib'author{Zaremba, D.}{}% +% \set:bib'title{Dimensions of irreducible ...}{}% +% \set:bib'journal{Fund. Math.}{}% +% \set:bib'date{1962/63}{}% +% } +%\end{verbatim} +% The extra empty arguments on each line are for auxiliary +% properties (see below). +% What happens thereafter with \cs{rsk@toks} depends on the code in +% the last arg of \cs{RestrictedSetKeys}. +% +% \section{Auxiliary properties} +% +% Unfortunately, the previous section isn't the entire story. In +% addition to the values of each field, we need to store a set of +% auxiliary properties associated with those values. Note that +% properties are explicitly associated with \emph{values}, not with +% keys, because each value of an additive key could have different +% properties. +% +% All such extra data will be stored in a special field named +% ``aux'', with embedded tags to indicate which field each piece of +% the \fld{aux} field is associated with. The extra bits can be +% extracted on demand using standard techniques, and the primary +% value of each field is not burdened with any attachments, so that +% comparisons or scanning of the field contents can remain as +% simple as possible. +% +% Thus in practice there is at least one bit of auxiliary information +% in every bib item, and our previous example would have the title +% language indicated: +% \begin{verbatim} +% \DSK@def\bib'title{Eine Bemerkung zur Darstellung von Polynomen +% \"{u}ber Verb\"{a}nden}% +% \@append\bib'title\bib'aux{\selectlanguage{german}}% +%\end{verbatim} +% +% \begin{macro}{\set@property} +% \begin{macrocode} +\def\set@property#1{% + \begingroup + \edef\@tempa{\extract@group#1}% + \edef\@tempa{% + \@nx\@append\@nx#1\@xp\@nx\csname \@tempa,aux\endcsname + }% + \@xp\endgroup + \@tempa +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\get@property} +% \begin{macrocode} +% \get@property\destination\bib'title +\def\get@property#1#2{% + \get@nth@property#1#2\m@ne +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\get@nth@property} +% \begin{macrocode} +% \get@nth@property\destination\bib'title N +\def\get@nth@property#1#2#3{% + \begingroup + \edef\@tempa{\extract@group#2}% + \@tempcnta#3\relax + \@tempcntb\z@ + \@xp\scan@properties\@xp#2\csname \@tempa,aux\endcsname + \edef\@tempa{\def\@nx#1{\@tempa}}% + \@xp\endgroup + \@tempa +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\scan@properties} +% \begin{macrocode} +\def\scan@properties#1#2{% + \begingroup + \def\@tempa{#1}% + \let\@tempc\@empty + \@xp\find@property #2 \@nil\@nil + \edef\@tempa{\def\@nx\@tempa{\@tempc}}% + \@xp\endgroup + \@tempa +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\find@property} +% \begin{macrocode} +\def\find@property#1#2{% + \ifx\@nil#1% + \else + \def\@tempb{#1}% + \ifx\@tempa\@tempb + \ifnum\@tempcnta<\z@ + \def\@tempc{#2}% + \else + \advance\@tempcntb\@ne + \ifnum\@tempcntb=\@tempcnta + \def\@tempc{#2}% + \fi + \fi + \fi + \@xp\find@property + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\reset@property} +% \begin{macrocode} +\def\reset@property#1#2{% + \reset@nth@property#1\m@ne{#2}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\reset@nth@property} +% \begin{macrocode} +% \reset@nth@property\bib'title N VALUE +\def\reset@nth@property#1#2#3{% + \begingroup + \edef\@tempa{\extract@group#1}% + \@tempcnta#2\relax + \@temptokena{#3}% + \toks@\@emptytoks + \@tempcntb\z@ + \@xp\reset@scan\@xp#1\csname \@tempa,aux\endcsname + \edef\@tempa{% + \def\@xp\@nx\csname \@tempa,aux\endcsname{\the\toks@}% + }% + \@xp\endgroup + \@tempa +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\reset@scan} +% \begin{macrocode} +\def\reset@scan#1#2{% + \begingroup + \def\@tempa{#1}% + \@xp\reset@scan@a #2 \@nil\@nil + \edef\@tempa{\toks@{\the\toks@}}% + \@xp\endgroup + \@tempa +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\find@property} +% \begin{macrocode} +\def\reset@scan@a#1#2{% + \ifx\@nil#1% + \else + \def\@tempb{#1}% + \ifx\@tempa\@tempb + \ifnum\@tempcnta<\z@ + \@temptokenb\@temptokena + \else + \advance\@tempcntb\@ne + \ifnum\@tempcntb=\@tempcnta + \@temptokenb\@temptokena + \fi + \fi + \else + \@temptokenb{#2}% + \fi + \edef\@tempb{% + \toks@{\the\toks@ \@nx#1{\the\@temptokenb}}% + }% + \@tempb + \@xp\reset@scan@a + \fi +} +% \end{macrocode} +% \end{macro} +% +% \section{Some machinery for finite state automata} +% +% Coincidentally I needed to write two finite state automaton parsers +% for two related packages, so after writing them separately I spent +% some time analyzing the code fragments they shared in common and +% abstracted them so that the cs names could be shared. +% +% \begin{macro}{\fsa@l} +% FSA lookahead. +% \begin{macrocode} +\def\fsa@l{\futurelet\@let@token\fsa@t} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\fsa@b} +% FSA bypass a token. Don't delete the space at the end! +% \begin{macrocode} +\def\fsa@b{\afterassignment\fsa@l \let\@let@token= } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\fsa@c} +% FSA copy a token (not space, bgroup, egroup). +% \begin{macrocode} +\def\fsa@c#1{\aftergroup#1\fsa@l} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\fsa@n} +% FSA next action. This is just a placeholder definition. +% \begin{macrocode} +\let\fsa@n\@empty +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\fsa@t} +% FSA test. This is just a placeholder definition. +% \begin{macrocode} +\let\fsa@t\@empty +% \end{macrocode} +% \end{macro} +% +% \section{Now some of the real work} +% +% \begin{macro}{\rsk@toks} +% \begin{macrocode} +\newtoks\rsk@toks +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rkvIfEmpty} +% Beginning here. +% \begin{macrocode} +\def\rkvIfEmpty#1#2{% + \@xp\ifx\csname#1'#2\endcsname\@empty + \@xp\@firstoftwo + \else + \@xp\@secondoftwo + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rkvIfAdditive} +% \begin{macrocode} +\def\rkvIfAdditive#1{% + \@xp\let\@xp\@let@token \csname \rkv@setter#1\endcsname + \afterassignment\@nilgobble + \@xp\let\@xp\@let@token \@let@token \@empty\@empty\@nil + \ifx\@let@token\DSK@append + \@xp\@firstoftwo + \else + \@xp\@secondoftwo + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rkv@setter} +% It irritates me that I can't embed the \ncn{csname} and +% \ncn{endcsname} in here. +% \begin{macrocode} +\def\rkv@setter#1{set:\@xp\@gobble\string#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rkv@DSAK} +% Define a simple, superseding, or additive key. +% \begin{macrocode} +\def\rkv@DSAK#1#2{% + \addto@group@reset#1{\let#1\@empty}% + \edef\@tempa{\def\csname \rkv@setter#1\endcsname}% + \@tempa{#2#1}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rkv@DDK} +% This function is used for a dummy key whose value (expansion) +% should be empty but that should appear non-empty to +% \cs{rkvIfEmpty}. +% \begin{macrocode} +\def\rkv@DDK#1{% + \addto@group@reset#1{\def#1{\@empty}}% + \@xp\let\csname \rkv@setter#1\endcsname\@gobble +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DSK@def} +% \begin{macrocode} +\def\DSK@def#1{% + \ifx#1\@empty\else + \PackageWarningNoLine{rkeyval}% + {Key \string#1 should not be repeated}% + \fi + \DSK@redef#1% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DSK@redef} +% We weed out empty field values for consistency with +% \cs{DSK@append}. +% \begin{macrocode} +\def\DSK@redef#1#2{% + \@ifempty{#2}{\@gobble}{% + \def#1{#2}% + \set@property#1 + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\init@group@reset} +% \begin{macrocode} +\def\init@group@reset#1{% + \begingroup + \edef\@tempb{\@xp\@nx\csname #1@reset\endcsname}% + \@xp\ifx\@tempb\relax + \@xp\xdef\@tempb{\let \csname #1,aux\endcsname\@nx\@empty} + \fi + \endgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\addto@group@reset} +% \begin{macrocode} +\def\addto@group@reset#1{% + \begingroup + \edef\@tempa{\extract@group#1}% + \init@group@reset\@tempa + \edef\@tempa{% + \@nx\g@addto@macro\@xp\@nx\csname\@tempa @reset\endcsname + }% + \@xp\endgroup + \@tempa +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DefineSimpleKey} +% \begin{macrocode} +\newcommand{\DefineSimpleKey}[2]{% + \@xp\rkv@DSAK + \csname #1'#2\endcsname + {\DSK@def}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DefineSupersedingKey} +% \begin{macrocode} +\newcommand{\DefineSupersedingKey}[2]{% + \@xp\rkv@DSAK + \csname #1'#2\endcsname + {\DSK@redef}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DefineAdditiveKey} +% \begin{macrocode} +\newcommand{\DefineAdditiveKey}[3]{% + \@xp\rkv@DSAK + \csname #1'#2\endcsname + {\DSK@append#3}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DSK@append} +% We weed out empty field values (e.g., |editor={}| or +% \verb*|editor={ }|) because otherwise an additive field could +% end up with a value like |\name{}| which appears non-empty to +% \cs{rkvIfEmpty} but produces no output on the page. +% \begin{macrocode} +\def\DSK@append#1#2#3{% + \@ifempty{#3}{\@gobble}{% + \@append#1#2{#3}% + \set@property#2 + }% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\DefineDummyKey} +% \begin{macrocode} +\newcommand{\DefineDummyKey}[2]{% + \@xp\rkv@DDK \csname #1'#2\endcsname +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\RestrictedSetKeys} +% \begin{macrocode} +\newcommand{\RestrictedSetKeys}[3]{% + \global\rsk@toks\@xp{\csname #2@reset\endcsname}% + \def\rsk@finish{#3}% + \gdef\rsk@set{\@xp\rsk@set@a\csname#2'}% + #1\relax + \begingroup + \rsk@changecase + \aftergroup\rsk@set +% \end{macrocode} +% Start by removing the opening brace. +% \begin{macrocode} + \let\fsa@t\rsk@z + \fsa@l +} +% \end{macrocode} +% \end{macro} +% +% The aftergroup tokens end up looking like this: +% \begin{verbatim} +% \lowercase{\rsk@set FIELDNAME\endcsname} +% --> \@xp\rsk@set@a\csname bib'fieldname\endcsname +% --> \rsk@set@a\bib'abcdef +%\end{verbatim} +% +% \begin{macro}{\rsk@unknown@key} +% \begin{macrocode} +\def\rsk@unknown@key#1{% + \PackageWarning{rkeyval}{Unknown key: \string#1}% + \@xp\def\csname\rkv@setter#1\endcsname {\DSK@redef#1}% +} +% \end{macrocode} +% \end{macro} +% +% \section{The state machine} +% +%\begin{verbatim} +% State 0: Skip opening brace (\rsk@z). +% space -> 0 +% { -> 2 +% other -> error "Missing open brace" +% +% State 1: Skip comma (\rsk@a). +% space -> 1 +% \par -> 1 +% comma -> 2 +% @ -> read optional arg; 1 +% } -> 6 +% other -> error "Missing comma"; 2 +% +% State 2: Find field name (\rsk@b). +% space -> 2 +% \par -> 2 +% comma -> 2 +% letter -> 3 +% { -> error "Missing key name"; 4 +% } -> 6 +% other -> error "Invalid key name character"; 2 +% +% State 3: Scan field name (\rsk@c). +% letter -> 3 +% comma -> error "Invalid key name character"; 3 +% equal -> 4 +% other punct -> 3 +% space -> 4 +% { -> error "Missing equal sign"; 4 +% } -> error "Missing equal sign"; 4 +% other -> error "Invalid key name character"; 3 +% +% State 4: Skip equals (\rsk@d). +% space -> 4 +% equal -> 4 +% { -> 5 +% other -> error "Missing { for value of current key"; 5 +% +% State 5: Read field value (\rsk@set@a). +% any -> 1 +% +% State 6: Done (\rsk@end). +%\end{verbatim} +% +% \begin{macro}{\rsk@z} +% State 0: Skip opening brace. +% \begin{macrocode} +\def\rsk@z{% + \ifx\bgroup\@let@token + \let\fsa@t\rsk@b + \let\fsa@n\fsa@b + \else + \ifx\@sptoken\@let@token + \let\fsa@n\fsa@b + \else + \rsk@errf + \fi + \fi + \fsa@n +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@a} +% State 1: Skip comma. +% \begin{macrocode} +\def\rsk@a{% + \ifx\@let@token\@sptoken + \let\fsa@n\fsa@b + \else + \ifx\@let@token\par + \let\fsa@n\fsa@b + \else + \ifx,\@let@token + \endgroup + \let\fsa@t\rsk@b + \let\fsa@n\fsa@b + \else + \ifx\egroup\@let@token + \endgroup + \let\fsa@n\rsk@end + \else + \endgroup + \let\fsa@n\rsk@erraa + \fi + \fi + \fi + \fi + \fsa@n +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@b} +% State 2: Find field name. +% +% Allow \cs{par} here to permit a blank line after +% the end of one key-val pair and the start of the next (perhaps to +% break up a long list into sections). +% \begin{macrocode} +\def\rsk@b{% + \ifcat\@nx\@let@token A% + \let\fsa@t\rsk@c + \let\fsa@n\fsa@c + \else + \ifx\@sptoken\@let@token + \let\fsa@n\fsa@b + \else + \rsk@bb + \fi + \fi + \fsa@n +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@bb} +% \begin{macrocode} +\def\rsk@bb{% + \ifx,\@let@token + \let\fsa@n\fsa@b + \else + \ifx\bgroup\@let@token + \let\fsa@n\rsk@errb + \else + \ifx\egroup\@let@token + \let\fsa@n\rsk@end + \else + \ifx\par\@let@token + \let\fsa@n\fsa@b + \else + \let\fsa@n\rsk@errc + \fi + \fi + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@c} +% State 3: Scan field name. +% \begin{macrocode} +\def\rsk@c{% + \ifcat\@nx\@let@token A% + \let\fsa@n\fsa@c + \else + \ifx\@sptoken\@let@token + \let\fsa@t\rsk@d + \let\fsa@n\fsa@b + \else + \ifx=\@let@token + \let\saw@equal T% + \let\fsa@t\rsk@d + \let\fsa@n\fsa@b + \else + \rsk@cb + \fi + \fi + \fi + \fsa@n +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@cb} +% \begin{macrocode} +\def\rsk@cb{% + \ifx,\@let@token + \let\fsa@n\rsk@errc + \else + \ifcat\@nx\@let@token .% + \let\fsa@n\fsa@c + \else + \ifx\bgroup\@let@token + \let\fsa@n\rsk@noequal + \else + \ifx\egroup\@let@token + \let\fsa@n\rsk@noequal + \else + \let\fsa@n\rsk@errc + \fi + \fi + \fi + \fi +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\saw@equal} +% \begin{macrocode} +\let\saw@equal=F +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@d} +% State 4: Skip equals. +% +% If no equal sign ever came +% along, then give a warning about it and set \cs{saw@equal} to true +% so that when \cs{rsk@noequal} cycles through again it will take the +% other branch. +% \begin{macrocode} +\def\rsk@d{% + \ifx\bgroup\@let@token + \ifx\saw@equal T% + \aftergroup\endcsname + \rsk@endcase + \let\fsa@n\endgroup + \else + \let\saw@equal T% + \let\fsa@n\rsk@noequal + \fi + \else + \ifx\@sptoken\@let@token + \let\fsa@n\fsa@b + \else + \ifx=\@let@token + \let\saw@equal T% + \let\fsa@n\fsa@b + \else + \let\fsa@n\rsk@erre + \fi + \fi + \fi + \fsa@n +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@casesensitive} +% \begin{macrocode} +\def\rsk@casesensitive{% + \let\rsk@changecase\@empty + \let\rsk@endcase\@empty +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@startlc} +% \begin{macrocode} +\def\rsk@startlc{\aftergroup\lowercase\aftergroup{\iffalse}\fi} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@endlc} +% \begin{macrocode} +\def\rsk@endlc{\iffalse{\fi\aftergroup}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@lowercase} +% \begin{macrocode} +\def\rsk@lowercase{% + \let\rsk@changecase\rsk@startlc + \let\rsk@endcase\rsk@endlc +} +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +\rsk@lowercase +% \end{macrocode} +% +% \begin{macro}{\rsk@resume} +% Here we get improved reporting of error context by changing +% end-of-line to be different from normal space. If we don't find a +% comma on the current line, assume it is an error. +% \begin{macrocode} +\def\rsk@resume{% + \begingroup + \rsk@changecase + \aftergroup\rsk@set + \let\fsa@t\rsk@a + \begingroup + \catcode\endlinechar=\active + \lccode`\~=\endlinechar + \lowercase{\let~\par}% + \fsa@l +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@set@a} +% State 5: Read field value. +% \begin{macrocode} +\def\rsk@set@a#1#2{% + \star@{\rsk@set@b#1{#2}}{}% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@set@b} +% \begin{macrocode} +\def\rsk@set@b#1#2#3{% + \@xp\ifx \csname\rkv@setter#1\endcsname \relax + \rsk@unknown@key#1% + \fi + \edef\@tempa{\@xp\@nx\csname \rkv@setter#1\endcsname}% + \toks@\@xp{\@tempa{#2}{#3}}% + \edef\@tempa{% + \global\rsk@toks{\the\rsk@toks \the\toks@}% + }% + \@tempa + \rsk@resume +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@end} +% State 6: Done. +% +% Lets see, now why did I add this? +% \begin{macrocode} +\def\rsk@end{% + \global\let\rsk@set\rsk@terminate + \rsk@endcase + \endgroup + \endcsname + \afterassignment\rsk@finish + \toks@\bgroup +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@terminate} +% \begin{macrocode} +\def\rsk@terminate{\@xp\@gobble\csname} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\NoCommaWarning} +% \begin{macrocode} +\def\NoCommaWarning{\PackageWarning{rkeyval}{Missing comma}}% +% \end{macrocode} +% \end{macro} +% +%% % \begin{macro}{\NoCommaError} +%% % \begin{macrocode} +%% \def\NoCommaError{\rsk@err{Missing comma}\@ehc} +%% % \end{macrocode} +%% % \end{macro} +% +% \begin{macro}{\rsk@nocomma} +% \begin{macrocode} +\def\rsk@nocomma{\NoCommaWarning} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@err} +% \begin{macrocode} +\def\rsk@err{\PackageError{rkeyval}} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@errf} +% \begin{macrocode} +\def\rsk@errf{\rsk@err{Missing open brace}\@ehc\rsk@b} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@erraa} +% \begin{macrocode} +\long\def\rsk@erraa{\rsk@nocomma \let\fsa@t\rsk@b \fsa@l} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@errb} +% \begin{macrocode} +\def\rsk@errb{\rsk@err{Missing key name}\@ehc\rsk@d} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@errc} +% \begin{macrocode} +\def\rsk@errc{\rsk@err{Invalid key name character}\@ehc\fsa@b} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\rsk@noequal} +% \begin{macrocode} +\def\rsk@noequal{\rsk@err{Missing equal sign}\@ehc\rsk@d} +% \end{macrocode} +% \end{macro} +% \begin{macro}{\rsk@erre} +% In this case we guess that the value is without braces but probably +% terminated with a comma. We want to scan up to the comma in order +% to get back in synch. +% \begin{macrocode} +\def\rsk@erre#1,{% + \rsk@err{Missing open brace for key value}\@ehc + \iffalse{\fi + \endgroup + \endcsname + \rsk@endcase }{#1},% +} +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +\PopCatcodes +% \end{macrocode} +% +% The usual \cs{endinput} to ensure that random garbage at the end of +% the file doesn't get copied by \fn{docstrip}. +% \begin{macrocode} +\endinput +% \end{macrocode} +% +% \CheckSum{871} +% \Finale |