% \iffalse meta-comment % % Copyright (C) 2011 by Jesse A. Tov % ---------------------------------------------------- % % This file may be distributed and/or modified under the % conditions of the LaTeX Project Public License, either version 1.2 % 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.2 or later is part of all distributions of LaTeX % version 1999/12/01 or later. % % ------------------------------------------------------------------ % This is a LaTeX package to make it easy to refer to nested labels % using both an outer number (such as a theorem number) and an inner % number (such as an item in an enumeration). % ------------------------------------------------------------------ % % *** The package file: %\NeedsTeXFormat{LaTeX2e}[1999/12/01] %\ProvidesPackage{plstx} % [2011/03/26 v0.2 (grammars for programming language syntax)] % % *** The driver file: %\NeedsTeXFormat{LaTeX2e} % % *** date, version, and stuff: %\fi %\ProvidesFile{plstx} % [2011/03/26 v0.2 (grammars for programming language syntax)] % \changes{v0.2}{2011/03/30}{Included listproc.sty} % \changes{v0.1}{2011/03/26}{Initial documented release} % % \CheckSum{505} % \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 \~} % % \iffalse % %<*driver> \documentclass{ltxdoc} \usepackage{plstx} \relax \usepackage{hypdoc} \EnableCrossrefs \CodelineIndex \RecordChanges \begin{document} \DocInput{plstx.dtx} \PrintChanges \setcounter{IndexColumns}{2} \PrintIndex \end{document} % % \fi % % \GetFileInfo{plstx} % % \DoNotIndex{\newcommand,\newenvironment,\def,\relax,\do,\@gobble} % \DoNotIndex{\if,\ifx,\else,\fi,\providecommand,\let,\global,\ignorespaces} % \DoNotIndex{\@undefined,\expandafter,\@for,\@ifnextchar,\addtolength} % \DoNotIndex{\aftergroup,\begin,\dp,\ht,\wd,\end,\ifdim} % \DoNotIndex{\@firstoftwo,\@secondoftwo,\@notfound,\@tester} % \DoNotIndex{\addtocounter,\advance,\edef,\empty,\gdef,\ifnum} % \DoNotIndex{\long,\newcounter,\renewcommand,\setcounter,\the,\toksdef} % \DoNotIndex{\value,\xdef,\\,\begingroup,\endgroup} % % {\catcode`\|=0 \catcode`\\=12 % |gdef|bslash{\}} % \makeatletter\relax % % \newcommand{\usemacro}[2][altusage]{\relax % \texttt{\bslash#2}\relax % \indexmacro[#1]{#2}\relax % } % \newcommand{\defmacro}[2][usage]{\relax % \hypertarget{macro:#2}{\usemacro[#1]{#2}}\relax % } % \newcommand{\indexmacro}[2][altusage]{\relax % \index{#2=\string\verb!*+\bslash#2+\string|#1}\relax\iffalse!\fi % } % \newcommand{\useenviron}[2][altusage]{\relax % \texttt{#2}\relax % \indexenviron[#1]{#2}\relax % } % \newcommand{\defenviron}[2][usage]{\relax % \hypertarget{environ:#2}{\useenviron[#1]{#2}}\relax % } % \newcommand{\indexenviron}[2][altusage]{\relax % \index{#2={\string\ttfamily\space#2} (environment)\string|#1}\relax % \index{environments:>#2={\string\ttfamily\space#2}\string|#1}\relax % } % \newcommand{\useoption}[2][altusage]{\relax % \texttt{#2}\relax % \indexoption[#1]{#2}\relax % } % \newcommand{\seeoption}[2][altusage]{\relax % \hyperlink{option:#2}{\texttt{#2}}\relax % \indexoption[#1]{#2}\relax % } % \newcommand{\defoption}[2][usage]{\relax % \hypertarget{option:#2}{\useoption[#1]{#2}}\relax % } % \newcommand{\indexoption}[2][altusage]{\relax % \index{#2={\string\ttfamily\space#2} (configuation option)\string|#1}\relax % \index{configuation options:>#2={\string\ttfamily\space#2}\string|#1}\relax % } % \newcommand{\useother}[2][altusage]{\relax % \texttt{#2}\relax % \indexother[#1]{#2}\relax % } % \newcommand{\defother}[2][usage]{\relax % \useother[#1]{#2}\relax % } % \newcommand{\indexother}[2][usage]{\relax % \index{#2=\string\verb!*+#2+\string|#1}\relax\iffalse!\fi % } % \newcommand{\altusage}[1]{\emph{(#1)}} % % { % \makeatletter % \global\let\doc@old@tabular\tabular % \global\def\doctabular{\begingroup\catcode`\|=12\relax\doc@tabular} % \global\def\doc@tabular#1{\endgroup\doc@old@tabular{#1}} % } % \let\enddoctabular\endtabular % % { % \catcode`\|=12\relax % \newenvironment{decl} % {\leavevmode\trivlist\item % \begin{tabular}{|l|l|}\hline\ignorespaces} % {\\\hline\end{tabular}\endtrivlist} % \global\let\decl\decl % \global\let\enddecl\enddecl % } % % \newcounter{macrosenv} % \newenvironment{macros}[1] % {\setcounter{macrosenv}{0} % \@for\@each@macro:=#1\do{ % \addtocounter{macrosenv}{1} % \expandafter\macro\expandafter{\csname\@each@macro\endcsname} % }} % {\@whilenum\value{macrosenv}>0\do{ % \addtocounter{macrosenv}{-1} % \endmacro % }} % % \title{The \textsf{plstx} package} % \author{Jesse A. Tov \\ \texttt{tov@ccs.neu.edu}} % \date{This document % corresponds to \textsf{\filename}~\fileversion, dated \filedate.} % % \maketitle % % \tableofcontents % % \section{Introduction} % \label{sec:intro} % % The purpose of this package is to provide a facility for typesetting % grammars for programming language syntax, like this: % % \begin{quote} % \parskip=0pt % \begin{plstx} % *(type variables): \alpha [\in] \mathit{TVar} \\ % *(variables): x [\in] \mathit{Var} \\ % (types): \tau ::= \alpha | \tau_1 \to \tau_2 | \forall\alpha.\tau \\ % (terms): e ::= x | e_1\,e_2 | \lambda x\colon\tau. e % | \Lambda\alpha.e | e[\tau] \\ % \end{plstx} % \end{quote} % Using the \useenviron{plstx} environment, I coded that like this: % \begin{verbatim} % \begin{plstx} % *(type variables): \alpha [\in] \mathit{TVar} \\ % *(variables): x [\in] \mathit{Var} \\ % (types): \tau ::= \alpha | \tau_1 \to \tau_2 | \forall\alpha.\tau \\ % (terms): e ::= x | e_1\,e_2 | \lambda x\colon\tau. e % | \Lambda\alpha.e | e[\tau] \\ % \end{plstx} % \end{verbatim} % % The |plstx| environment allows redefining much of its behavior. For % example, if we prefer $\longrightarrow$ to $::=$ in our grammars, we % can change the ``is one of'' symbol. Perhaps we also want to change % the formatting for the descriptions on the right. % \indexmacro{plstxset} % \indexoption{is one of} % \indexoption{label style} % \begin{verbatim} % \plstxset{ % is one of=\longrightarrow, % label style=\textsf % } % \end{verbatim} % Then we get: % { % \plstxset{ % is one of=\longrightarrow, % label style=\textsf % } % \begin{quote} % \parskip=0pt % \begin{plstx} % *(type variables): \alpha [\in] \mathit{TVar} \\ % *(variables): x [\in] \mathit{Var} \\ % (types): \tau ::= \alpha | \tau_1 \to \tau_2 | \forall\alpha.\tau \\ % (terms): e ::= x | e_1\,e_2 | \lambda x\colon\tau. e % | \Lambda\alpha.e | e[\tau] \\ % \end{plstx} % \end{quote} % } % % The environment also handles breaking lines when all the productions % won't fit on one line, like this: % \begin{center} % \begin{minipage}{0.5\linewidth} % \parskip=0pt % \begin{plstx} % *(type variables): \alpha [\in] \mathit{TVar} \\ % *(variables): x [\in] \mathit{Var} \\ % (types): \tau ::= \alpha | \tau_1 \to \tau_2 | \forall\alpha.\tau \\ % (terms): e ::= x | e_1\,e_2 | \lambda x\colon\tau. e % | \Lambda\alpha.e | e[\tau] \\ % \end{plstx} % \end{minipage} % \end{center} % % \subsection{Requirements} % % The \textsf{plstx} package depends on three other packages. Two are a % standard part of the \LaTeX{} distribution: \textsf{keyval} and % \textsf{calc}. The third, \textsf{listproc}, is non-standard, and may % be obtained at \url{http://www.ccs.neu.edu/~tov/code/latex/}. % % \section{Command Reference} % % \begin{decl} % \defmacro{plstxset} \marg{plstx-options} % \end{decl} % Takes a comma-separated list of keys and values, in the style of % \textsf{keyval}: % \begin{quote} % \begin{plstx} % \relax: \meta{plstx-options} ::= % \meta{key}_1 \,\texttt=\, \meta{value}_1\texttt, \ldots \texttt, % \meta{key}_k \,\texttt=\, \meta{value}_k [\texttt,] % \\ % \end{plstx} % \end{quote} % The options available are described in \S\ref{sec:options}. % % \begin{decl} % |\begin{|\defenviron{plstx}|}| \oarg{plstx-options} \\ % | |\meta{plstx-cmd} \ldots \\ % |\end{plstx}| % \end{decl} % The |plstx| environment takes an optional argument, which is a list of % options as keys and values, as described in \S\ref{sec:options}. % These are the same options that may be provided to % \usemacro{plstxset}. % % The available commands are: % \begin{plstx}[rhs style=,one per line] % : \meta{plstx-cmd} % ::= \meta{label-text}\texttt: \meta{nonterm} \defother{::=} % \meta{rhs} \texttt{\bslash\bslash} % | \defother{*} \meta{label-text}\texttt: \meta{nonterm} % \texttt[\meta{sep}\texttt] % \meta{rhs} \texttt{\bslash\bslash} % | \defmacro{set} \marg{plstx-options} % | \defmacro{intertext} \marg{text} % | \defother{[}\meta{dimen}\texttt] % \\ % \intertext{where} % : \meta{sep} % ::= \meta{is-one-of} % | \meta{is-one-of} \texttt, \meta{continue} \\ % : \meta{rhs} % ::= \meta{production} % | \meta{production} {\texttt|} \meta{rhs} \\ % \end{plstx} % If a command starts with |*|, |\set|, |\intertext|, or |[|, then it is % taken to be one of those four commands---otherwise, it is treated as % the first case, which handles normal nonterminal item. We'll consider % the available commands in order: % % \vspace{-1.5pc} % \begin{quotation} % \begin{decl} % \meta{label-text}\texttt: \meta{nonterm} \defother{::=} % \meta{rhs} \texttt{\bslash\bslash} % \end{decl} % A normal nonterminal item % consists of a label \meta{label-text} (which is set on the right, in % text mode by default); a non-terminal being % defined \meta{nonterm} (which is set on the left, in math mode by % default); a separator (option \seeoption{is one of}, % default $::=$, and written as |::=| in the % command even if it has been configured to appear otherwise), and % a right-hand side \meta{rhs}, which is a % sequence of productions separated by \verb+|+, each set in math mode % by default. The nonterminal and label are set first, and then % productions from the right-hand side are added one at a time until % there's no more space remaining, at which point it may add % continuation lines. % \begin{decl} % \defother{*} \meta{label-text}\texttt: \meta{nonterm} % \texttt[\meta{sep}\texttt] % \meta{rhs} \texttt{\bslash\bslash} % \end{decl} % A special nonterminal item starts with |*|, after which the % syntax is the same as a normal nonterminal, with one exception. Rather % than write |::=| for the ``is one of'' separator, it expects a % separator for use in just that case to appear in square brackets. For % example, to get $\alpha \in \mathit{TVar}$ in the example from % \S\ref{sec:intro}, I wrote % |\alpha [\in] \mathit{TVar}|. Optionally, the square brackets may % contain a second item, after a comma, which indicates the separator to % use for continuation lines if the right-hand side wraps. Writing a % special |*| nonterminal item with separator % |[::=,\vert]| is equivalent to writing a normal nonterminal. % \begin{decl} % \defmacro{set} \marg{plstx-options} % \end{decl} % This allows changing the options in the middle of a grammar, using the % options described in \S\ref{sec:options}. Changes % made by |\set| last only until the end of the current % |plstx| environment. % \begin{decl} % \defmacro{intertext} \marg{text} % \end{decl} % Escapes from the normal grammar typesetting to allow % including arbitrary text between grammar items. (This is similar to % \textsf{amsmath}'s |\intertext| command.) % \begin{decl} % \defother{[}\meta{dimen}\texttt] % \end{decl} % Inserts \meta{dimen} vertical space. % \end{quotation} % % \emph{Note: The grammar for \meta{plstx-cmd} above was written like % this:} % \begin{verbatim} % \begin{plstx}[rhs style=,one per line] % : \meta{plstx-cmd} % ::= \meta{label-text}\texttt: \meta{nonterm} \defother{::=} % \meta{rhs} \texttt{\bslash\bslash} % | \defother{*} \meta{label-text}\texttt: \meta{nonterm} % \texttt[\meta{sep}\texttt] % \meta{rhs} \texttt{\bslash\bslash} % | \defmacro{set} \marg{plstx-options} % | \defmacro{intertext} \marg{text} % | \defother{[}\meta{dimen}\texttt] % | \oarg{dimen} % \\ % \intertext{where} % : \meta{sep} % ::= \meta{is-one-of} % | \meta{is-one-of} \texttt, \meta{continue} \\ % : \meta{rhs} % ::= \meta{production} % | \meta{production} {\defother|} \meta{rhs} \\ % \end{plstx} % \end{verbatim} % % \subsection{Configuration Options} % \label{sec:options} % % In this section, we document the configuration options that may be % passed to \usemacro{plstxset}, \usemacro{set}, or environment % \useenviron{plstx}. % % \newcommand\singlequotearg[1]{`#1'} % \newcommand\fmtoptitemlow[3]{\relax % {\normalfont\mbox{}\quad % \makebox[.25\paperwidth] % {#1\relax % \ifx\boolean#2\relax % \hfill\emph{(boolean)}\relax % \else % |=|\meta{#2}\hfill % \fi}\relax % \quad#3}\relax % } % \newcommand\fmtoptitem[4][\singlequotearg]{\relax % \fmtoptitemlow{\defoption{#2}}{#3}{\emph{default:} #1{#4}}\relax % } % \newcommand\optitem[4][\singlequotearg]{\relax % \item[{\fmtoptitem[#1]{#2}{#3}{#4}}] % \leavevmode\par % } % \newcommand\seeoptitem[3]{\relax % \item[{\fmtoptitemlow{\texttt{#1}}{#2}{\emph{see} \seeoption{#3}}}] % } % \newcommand\optitems[1]{\relax % \item[{\begin{minipage}{\linewidth}\relax % \setlength{\parindent}{0pt}#1\relax % \end{minipage}\relax}]\relax % \leavevmode\par % } % \newcommand\suboptitem[4][\singlequotearg]{\relax % \fmtoptitem[#1]{#2}{#3}{#4}\ignorespaces % } % % \begin{description} % \optitems{ % \suboptitem{align continue}{cs}{\usemacro{plstx@right}} \\ % \suboptitem[]{continue center}\boolean{false} \\ % \suboptitem[]{continue left}\boolean{false} \\ % \suboptitem[]{continue right}\boolean{true} % } % To configure the horizontal alignment of the continuation separator % (see \seeoption{continue}). The default is to right align it. % It's possible to specify different alignment using one of the % boolean options, or supply a command to format the continuation % separator using |align continue|. % \optitems{ % \suboptitem{align is one of}{cs}{\usemacro{plstx@center}} \\ % \suboptitem[]{is one of center}\boolean{true} \\ % \suboptitem[]{is one of left}\boolean{false} \\ % \suboptitem[]{is one of right}\boolean{false} % } % To configure the horizontal alignment of the ``is one of'' separator % (see \seeoption{is one of}). The default is to center it. % \optitems{ % \suboptitem{align nonterm}{cs}{\usemacro{plstx@center}} \\ % \suboptitem[]{nonterm center}\boolean{true} \\ % \suboptitem[]{nonterm left}\boolean{false} \\ % \suboptitem[]{nonterm right}\boolean{false} % } % To configure the horizontal alignment of each nonterminal. % The default is to center them. % \optitem{continue}{text}{\usemacro{vert}} % The ``is one of'' separator for continuation lines in normal grammar items. % When the right-hand side spills onto additional lines, this is used % in the separator column for each additional line. % To change this for just one item, use the |*| command to get a special % grammar item. % The value of |continue| is set in math mode. % \seeoptitem{continue center}\boolean{align continue} % \seeoptitem{continue left}\boolean{align continue} % \seeoptitem{continue right}\boolean{align continue} % \optitems{ % \suboptitem{gutter}{dimen}{|4pt|}\\ % \suboptitem{gutter left}{dimen}{|4pt|}\\ % \suboptitem{gutter right}{dimen}{|4pt|}\\[4pt] % \suboptitem{gutter left text}{text}{\usemacro{kern}|4pt|}\\ % \suboptitem{gutter right text}{text}{\usemacro{kern}|4pt|}\\ % \suboptitem{gutter text}{text}{\usemacro{kern}|4pt|} % } % These options are for specifying the \emph{gutters}, which are the % space to the left and right of the ``is one of'' separator. The % |text| versions of the options set exactly what will be placed to % the left or right (or both) of the separator, whereas the non-|text| % versions allow supplying a length to be kerned. For example, each % of these pairs is equivalent: % % \begin{doctabular}{l@{\quad$\equiv$\quad}l} % |gutter left=|\meta{dimen} & |gutter left text=\kern|\meta{dimen} \\ % |gutter right=|\meta{dimen} & |gutter right text=\kern|\meta{dimen} \\ % |gutter=|\meta{dimen} & |gutter left=|\meta{dimen}|,gutter right=|\meta{dimen} % \end{doctabular} % \optitem{is one of}{text}{|::=|} % The separator for normal grammar items. To change this for just % one item, use the |*| command to get a special grammar item. % The value of |is one of| is set in math mode. % \seeoptitem{is one of center}\boolean{align is one of} % \seeoptitem{is one of left}\boolean{align is one of} % \seeoptitem{is one of right}\boolean{align is one of} % \optitems{ % \suboptitem{label skip}{dimen}{|1pc|} \\ % \suboptitem{label skip text}{text}{\usemacro{kern}|1pc|} % } % This specifies the space to the left of the label, which separates % the label from the right-hand side. % Option |label skip text| takes the exact text to put % to the left of (non-empty) labels, whereas |label skip| merely % needs a length. The latter is defined in terms of the former: % |label skip=|\meta{dimen} $\equiv$ |label skip text=\kern|\meta{dimen}. % \optitem{label style}{cs}{\usemacro{emph}} % Command used to style grammar labels. Providing this key with no % value sets the option to empty. % \seeoptitem{left margin}{dimen}{margin} % \optitem[]{many per line}{\boolean}{true} % Set as many right-hand side productions as will fit on each line % before wrapping. % This option does not take a value; the opposite % option is \seeoption{one per line}. % \optitems{ % \suboptitem{margin}{dimen}{|0pt|} \\ % \suboptitem{left margin}{dimen}{|0pt|} \\ % \suboptitem{right margin}{dimen}{|0pt|} % } % Sets the margin on one or both sides of the grammar. % This margin applies % only to items (normal and special), not to \usemacro{intertext}. % If no value is supplied, the margin is set to |1em|. % \seeoptitem{nonterm center}\boolean{align nonterm} % \seeoptitem{nonterm left}\boolean{align nonterm} % \seeoptitem{nonterm right}\boolean{align nonterm} % \optitem{nonterm style}{cs}{\usemacro{ensuremath}} % Commands used to style nonterminals. By default, nonterminals are % set in math mode using |\ensuremath|. % Providing this key with no value sets the option to empty. % \optitem[]{one per line}\boolean{false} % Set only one right-hand side production on each line, regardless of % space. % This option does not take a value; the opposite % option is \seeoption{many per line}. % \optitem{or}{text}{\usemacro{vert}} % Used to separate productions in a right-hand side. Set in math % mode. % \optitems{ % \suboptitem{or skip}{dimen}{|4pt|} \\ % \suboptitem{or skip text}{text}{\usemacro{kern}|4pt|} % } % This specifies the space around the production separator (option % \seeoption{or}). Option |or skip text| takes the exact text to put % on each side of the production separator, whereas |or skip| merely % needs a length. The latter is defined in terms of the former: % |or skip=|\meta{dimen} $\equiv$ |or skip text=\kern|\meta{dimen}. % \optitem{rhs style}{cs}{\usemacro{ensuremath}} % Commands used to style each right-hand side production. % By default, productions are % set in math mode using |\ensuremath|. % Providing this key with no value sets the option to empty. % \seeoptitem{right margin}{dimen}{margin} % \iffalse % \fi % \end{description} % % \StopEventually{} % % \section{Implementation} % % We begin by requiring packages: % \begin{macrocode} \RequirePackage{keyval} \RequirePackage{calc} \RequirePackage{listproc} % \end{macrocode} % Set up the configuration options for \textsf{keyval}: % \begin{macrocode} \define@key{plstx}{align continue}{\def\plstx@align@continue{#1}} \define@key{plstx}{align is one of}{\def\plstx@align@isoneof{#1}} \define@key{plstx}{align nonterm}{\def\plstx@align@nonterm{#1}} \define@key{plstx}{continue center}[]{\def\plstx@align@continue{\plstx@center}} \define@key{plstx}{continue left}[]{\def\plstx@align@continue{\plstx@left}} \define@key{plstx}{continue right}[]{\def\plstx@align@continue{\plstx@right}} \define@key{plstx}{continue}[]{\def\plstx@continue{#1}} \define@key{plstx}{gutter}{% \def\plstx@gutter@left{\kern#1}% \def\plstx@gutter@right{\kern#1}} \define@key{plstx}{gutter left text}{\def\plstx@gutter@left{#1}} \define@key{plstx}{gutter left}{\def\plstx@gutter@left{\kern#1}} \define@key{plstx}{gutter right text}{\def\plstx@gutter@right{#1}} \define@key{plstx}{gutter right}{\def\plstx@gutter@right{\kern#1}} \define@key{plstx}{gutter text}{% \def\plstx@gutter@left{#1}% \def\plstx@gutter@right{#1}} \define@key{plstx}{is one of center}[]{\def\plstx@align@isoneof{\plstx@center}} \define@key{plstx}{is one of left}[]{\def\plstx@align@isoneof{\plstx@left}} \define@key{plstx}{is one of right}[]{\def\plstx@align@isoneof{\plstx@right}} \define@key{plstx}{is one of}{\def\plstx@isoneof{#1}} \define@key{plstx}{label skip text}{\def\plstx@labelskip{#1}} \define@key{plstx}{label skip}{\def\plstx@labelskip{\kern#1}} \define@key{plstx}{label style}[]{\def\plstx@label@style{#1}} \define@key{plstx}{left margin}[1em]{\def\plstx@margin@left{\kern#1}} \define@key{plstx}{many per line}[]{\let\plstx@one@per@line\@secondoftwo} \define@key{plstx}{margin}[1em]{% \def\plstx@margin@left{\kern#1}% \def\plstx@margin@right{\kern#1}} \define@key{plstx}{nonterm center}[]{\def\plstx@align@nonterm{\plstx@center}} \define@key{plstx}{nonterm left}[]{\def\plstx@align@nonterm{\plstx@left}} \define@key{plstx}{nonterm right}[]{\def\plstx@align@nonterm{\plstx@right}} \define@key{plstx}{nonterm style}[]{\def\plstx@nonterm@style{#1}} \define@key{plstx}{one per line}[]{\let\plstx@one@per@line\@firstoftwo} \define@key{plstx}{or skip text}{\def\plstx@orskip{#1}} \define@key{plstx}{or skip}{\def\plstx@orskip{\kern#1}} \define@key{plstx}{or}{\def\plstx@or{#1}} \define@key{plstx}{rhs style}[]{\def\plstx@rhs@style{#1}} \define@key{plstx}{right margin}[1em]{\def\plstx@margin@right{\kern#1}} % \end{macrocode} % \begin{macros}{plstx@set,plstxset} % To set configuration options, we delegate to \usemacro{setkeys} from % the \textsf{keyval} package. % \begin{macrocode} \newcommand*\plstx@set{\setkeys{plstx}} \let\plstxset\plstx@set\relax % \end{macrocode} % \end{macros} % Set the initial options: % \begin{macrocode} \plstx@set{ continue = \vert, continue right, gutter = 4pt, is one of = {::=}, is one of center, label skip = 1pc, label style = \emph, many per line, margin = 0pt, nonterm center, nonterm style = \ensuremath, or = \vert, or skip = 4pt, rhs style = \ensuremath, } % \end{macrocode} % \begin{macros}{plstx@left,plstx@right,plstx@center} % Helper commands for aligning text: % \begin{macrocode} \def\plstx@left#1{#1\hfill} \def\plstx@right#1{\hfill#1} \def\plstx@center#1{\hfill#1\hfill} % \end{macrocode} % \end{macros} % \begin{macros}{plstx@parseRHS,plstx@parseRHS@loop} % The right-hand side is provided by the user delimited by \verb+|+. We % need to break it into productions, carefully, in order to line break % it as necessary. Command |\plstx@parseRHS| breaks |#1| into % productions and stores them as a list in |#2| It does this by calling % |\plstx@parseRHS@loop|, which uses \TeX's argument pattern matching % to find each \verb+|+. % \begin{macrocode} \newcommand\plstx@parseRHS[2]{% \let#1=\empty \plstx@parseRHS@loop#2|\plstx@parseRHS@stop\plstx@parseRHS@loop{#1}% } \def\plstx@parseRHS@loop#1|#2\plstx@parseRHS@loop#3{% \SnocTo{#1}{#3}% \ifx#2\plstx@parseRHS@stop \let\plstx@parseRHS@kont=\relax \else \def\plstx@parseRHS@kont{% \plstx@parseRHS@loop#2\plstx@parseRHS@loop{#3}% }% \fi \plstx@parseRHS@kont } % \end{macrocode} % \end{macros} % \begin{macros}{plstx@additem} % The |plstx| environment accumulates grammar items in a list, so that % it can measure all of them before it chooses the widths of various % parts. This macro adds an item to the accumulating list of items. % \begin{macrocode} \newcommand\plstx@additem[1]{% \SnocTo{#1}{\plstx@items}% } % \end{macrocode} % \end{macros} % \begin{macros}{plstx@dispatch} % This macro is used inside the |plstx| environment to figure out which % \meta{plstx-command} comes next. It takes one argument, and then % dispatches to the handler for the correct command. It has to deal % with an additional case not mentioned in the user documentation: it % detects the control sequences |\end| and |\endplstx| to detect when % the environment is ending. If nothing matches, it dispatches to the % normal item parser |\plstx@parseprod|. % \begin{macrocode} \def\plstx@dispatch#1{% \ifx#1\end \let\plstx@dispatch@kont\end \else\ifx#1\endplstx \let\plstx@dispatch@kont\endplstx \else\ifx#1\intertext \let\plstx@dispatch@kont\plstx@intertext \else\ifx#1[% \let\plstx@dispatch@kont\plstx@vskip \else\ifx#1\set \let\plstx@dispatch@kont\plstx@set@later \else\ifx#1*% \let\plstx@dispatch@kont\plstx@other \else \def\plstx@dispatch@kont{\plstx@parseprod#1}% \fi\fi\fi\fi\fi\fi \plstx@dispatch@kont } % \end{macrocode} % \end{macros} % \begin{macros}{plstx@parseprod} % This is the command handler for normal productions. Productions are % stored in the item list as % \marginpar{\hfill\useother{::=}} % \begin{quote} % \usemacro{plstx@production}\marg{label-text}\marg{nonterm}\marg{is-one-of}\marg{continue}{\marg{rhs}} % \end{quote} % It then calls back to \usemacro{plstx@dispatch} to have it figure out % the next command. % \begin{macrocode} \def\plstx@parseprod#1:#2::=#3\\{% \plstx@additem{\plstx@production{#1}{#2}{\plstx@isoneof}{\plstx@continue}{#3}}% \plstx@dispatch% } % \end{macrocode} % \end{macros} % \begin{macros}{plstx@other} % The command handler for special grammar items. Almost all the % complexity is about figuring out whether the separator(s) in the % \marginpar{\hfill\useother{*}} % square brackets are a single separator to use for both ``is one of'' % and ``continuation'' separators, or two with a comma in between. % \begin{macrocode} \def\plstx@other#1:#2[#3]#4\\{% \let\plstx@other@isoneof\plstx@isoneof \let\plstx@other@continue\plstx@continue \def\plstx@other@todo##1{% \def\plstx@other@isoneof{##1}% \def\plstx@other@continue{##1}% \def\plstx@other@todo####1{% \def\plstx@other@continue{####1}% }% }% \@for\plstx@each:=#3\do{% \expandafter\plstx@other@todo\expandafter{\plstx@each}% }% \def\plstx@other@addthis##1##2{% \plstx@additem{\plstx@production{#1}{#2}{##1}{##2}{#4}}% }% \expandafter\expandafter\expandafter\plstx@other@addthis \expandafter\expandafter \expandafter{\expandafter\plstx@other@isoneof\expandafter}% \expandafter{\plstx@other@continue}% \let\plstx@other@isoneof\@undefined \let\plstx@other@continue\@undefined \let\plstx@other@todo\@undefined \let\plstx@other@addthis\@undefined \plstx@dispatch } % \end{macrocode} % \end{macros} % \begin{macros}{intertext,plstx@intertext} % Intertext is added to the item list as % \begin{quote} % \usemacro{plstx@intertext}\marg{text} % \end{quote} % \begin{macrocode} \def\plstx@intertext#1{% \plstx@additem{\plstx@intertext{#1}}% \plstx@dispatch% } % \end{macrocode} % \end{macros} % \begin{macros}{plstx@vskip} % To add vertical space, we add % \begin{quote} % \usemacro{plstx@later}|{|\usemacro{vskip}\meta{dimen}|}| % \marginpar{\hfill\useother{[}\meta{dimen}\texttt{]}} % \end{quote} % to the list of items. % \begin{macrocode} \def\plstx@vskip#1]{\plstx@additem{\plstx@later{\vskip#1}}\plstx@dispatch} % \end{macrocode} % \end{macros} % \begin{macros}{set,plstx@set@later} % For |\set|, we add \usemacro{plstx@set}\marg{plstx-options} directly % to the list of grammar items. % \begin{macrocode} \def\plstx@set@later#1{\plstx@additem{\plstx@set{#1}}\plstx@dispatch} % \end{macrocode} % \end{macros} % \begin{macros}{plstx@box@a,plstx@box@b,plstx@box@c} % We require three boxes: |box@a| is used for labels, |box@b| for the % nonterminal and productions, and |box@c| as a temporary box as needed. % \begin{macrocode} \newsavebox\plstx@box@a \newsavebox\plstx@box@b \newsavebox\plstx@box@c % \end{macrocode} % \end{macros} % \begin{macros}{plstx@maxnt,plstx@maxisoneof,plstx@availwd} % We use two dimension registers for calculating the maximum width of % the nonterminals and the maximum width of the ``is one of'' and % ``continue'' separators. The third dimension register, % |\plstx@availwd|, is used to keep track of remaining available width % when line breaking the right-hand side. % \begin{macrocode} \newlength\plstx@maxnt \newlength\plstx@maxisoneof \newlength\plstx@availwd % \end{macrocode} % \end{macros} % \begin{environment}{plstx} % The main |plstx| environment. % \begin{macrocode} \newenvironment{plstx}[1][] {% \begingroup % \end{macrocode} % Make sure that \verb+|+ is recognizable as the production separator: % \begin{macrocode} \catcode`\|=12\relax \plstx@set{#1}% % \end{macrocode} % Initialize the list of items as empty. Then call |\plstx@dispatch| to % read in the commands in the grammar. % \begin{macrocode} \let\plstx@items\empty \plstx@dispatch } {% \ifx\plstx@items\empty \PackageWarning{plstx}{grammar must have at least one production}% \else % \end{macrocode} % For both passes through the list of items, we'll just evaluate the % list, so we make |\listitem| a no-op. % \begin{macrocode} \def\plstx@listitem@noop##1{##1\let\listitem\plstx@listitem@noop}% \plstx@listitem@noop\relax% % \end{macrocode} % We're going to compute the width of the widest nonterminal and widest % ``is one of.'' We do this by defining |\plstx@production| to measure % each nonterminal and ``is one of.'' The other grammar item callbacks % are defined to do nothing for now. % \begin{macrocode} \setlength{\plstx@maxnt}{0pt}% \setlength{\plstx@maxisoneof}{0pt}% \def\plstx@production##1##2##3##4##5{% \setlength {\global\plstx@maxnt} {\maxof{\plstx@maxnt}{\widthof{\plstx@nonterm@style{##2}}}}% \setlength {\global\plstx@maxisoneof} {\maxof{\plstx@maxisoneof} {\maxof{\widthof{${##3}$}} {\widthof{${##4}$}}}}% }% \def\plstx@intertext##1{}% \def\plstx@later##1{}% {\plstx@items}% % \end{macrocode} % Now |\plstx@maxnt| is the widest nonterminal. % % For the second pass, we actually output each item. We're going to % wrap the whole thing in a \usemacro{trivlist}, so we'll precede each % line with |\item|. We redefine the grammar item callbacks: % \begin{macrocode} \def\plstx@production##1##2##3##4##5{% % \end{macrocode} % The initial available width is the |\linewidth|. We then add the % label to |box@a|, and if the resulting box has non-zero width, we % prepend |\plstx@labelskip| to it. Then, in either case, we postpend % the right margin to it. We update the available width to account for % the size of the label and any space around it. % \begin{macrocode} \setlength{\plstx@availwd}{\linewidth}% \sbox\plstx@box@a{\plstx@label@style{##1}}% \ifdim\wd\plstx@box@a>0pt \sbox\plstx@box@a{\plstx@labelskip\usebox\plstx@box@a}% \fi \sbox\plstx@box@a{\usebox\plstx@box@a\plstx@margin@right}% \addtolength{\plstx@availwd}{-\wd\plstx@box@a}% % \end{macrocode} % Now we begin with the nonterminal. In |box@b|, we add the left margin, % the nonterminal in a box of size |\plstx@maxnt| (formatted and aligned % according to the options), the left gutter, the ``is one of'' % separator, and finally the right gutter. % \begin{macrocode} \sbox\plstx@box@b{% \plstx@margin@left \makebox[\plstx@maxnt] {\plstx@align@nonterm{\plstx@nonterm@style{##2}}}% \plstx@gutter@left \makebox[\plstx@maxisoneof]{\plstx@align@isoneof{${##3}$}}% \plstx@gutter@right }% % \end{macrocode} % Parse the right-hand side into a list of productions. % We take the first production out of the list, postpend it to % |box@b|, and update the available width. % \begin{macrocode} \plstx@parseRHS\plstx@rhsOut{##5}% \LopTo\plstx@rhsOut\plstx@rhsFirst \sbox\plstx@box@b {\usebox\plstx@box@b \plstx@rhs@style{\plstx@rhsFirst}}% \addtolength{\plstx@availwd}{-\wd\plstx@box@b}% % \end{macrocode} % Now iterate over the remaining productions. % \begin{macrocode} \@forList\plstx@each:=\plstx@rhsOut\do{% % \end{macrocode} % Place the next production in |box@c| along with the production % separator. If option \seeoption{one per line} is set, then we don't % need to check, but otherwise, we check whether |box@c| will exceed the % available space. % \begin{macrocode} \sbox\plstx@box@c {\plstx@orskip${\plstx@or}$\plstx@orskip \plstx@rhs@style{\plstx@each}}% \plstx@one@per@line {\iftrue} {\ifdim\wd\plstx@box@c>\plstx@availwd}% % \end{macrocode} % In this case, either |box@c| won't fit or we're in one-per-line mode. % So we stick |box@a| and |box@b| together and output them. % Then, to start the next line, we reinitialize |box@a| with the right % margin and |box@b| with the ``continue'' separator and the current % production. % \begin{macrocode} \item\makebox[\linewidth] {\strut\usebox\plstx@box@b\hfill\usebox\plstx@box@a} \setlength{\plstx@availwd}{\linewidth}% \sbox\plstx@box@a{\plstx@margin@right}% \sbox\plstx@box@b{% \plstx@margin@left \makebox[\plstx@maxnt]{}% \plstx@gutter@left \makebox[\plstx@maxisoneof]{\plstx@align@continue{${##4}$}}% \plstx@gutter@right \plstx@rhs@style{\plstx@each}% }% \addtolength{\plstx@availwd}{-\wd\plstx@box@b}% \else % \end{macrocode} % Otherwise, we add |box@c| to |box@b| and update the available width. % \begin{macrocode} \addtolength{\plstx@availwd}{-\wd\plstx@box@c}% \sbox\plstx@box@b{\usebox\plstx@box@b\usebox\plstx@box@c}% \fi }% end \do % \end{macrocode} % When we've iterated through all the productions, we flush |box@b| if % it isn't empty: % \begin{macrocode} \ifdim\wd\plstx@box@b>0pt \item\makebox[\linewidth] {\strut\usebox\plstx@box@b\hfill\usebox\plstx@box@a} \fi }% % \end{macrocode} % That's the end of the main grammar item callback. % % For |\intertext|, we merely drop the text in a fresh |\item|. For % items delayed with |\plstx@later|, we evaluate them as is. % \begin{macrocode} \def\plstx@intertext##1{% \item\strut\ignorespaces##1% }% \def\plstx@later##1{##1}% % \end{macrocode} % Finally, we evaluate the list of grammar items in a |\trivlist|: % \begin{macrocode} \trivlist{\plstx@items}\endtrivlist \fi \endgroup } % \end{macrocode} % \end{environment} % % \Finale \endinput