%\filename{SynChart.tex}
%\edited{by stolfi on Fri Jan 10 15:29:46 1986}

% TeX macros for syntax charts
% Adapted from Mike Plass's SAIL-TEX macros (SynChart.TEX)
% New version - using full power of \halign

\def\macrosection{\begingroup\catcode`\ =12\XXmacrosection}
\def\XXmacrosection#1{\message{#1}\endgroup}

\macrosection{ fonts...}

\font\CIRCF=circlew10 \relax % A circle font
\font\ARRF=amtt10 \relax
\font\BARRF=amtt10 scaled \magstep2 \relax

% The user should also define the fonts \TerminalFont, \KeywordFont,
% and \NonTerminalFont.

\macrosection{ space and linebreak control...}

% These macros control how spaces and line breaks are to be treated:

\def\IgnoreLinebreaks{\catcode'015=9\endlinechar=-1}
\def\IgnoreWhiteSpace{\catcode'040=9\catcode'011=9\IgnoreLinebreaks}
\def\DontIgnoreWhiteSpace{\catcode'040=10\catcode'015=5%
\catcode'011=10\endlinechar='015}

\IgnoreWhiteSpace % so the macro definitions may be freely formatted.

% Because spaces are ignored, we can't write \ifnum \x=0 \Error...\fi
% since TeX will try to expand \Error until it sees a non-digit.
% To avoid this, we write \ifnum \x=0 \then ... \fi, where

\let\then=\relax

\macrosection{ counting...}

\def\inc#1{\advance #1 by 1\relax}
\def\dec#1{\advance #1 by -1\relax}

\macrosection{ dimensions...}

% Dimensions

\newdimen\Wid % Line width
\newdimen\Eps % Line half-width
\newdimen\Unit % Width of a quarter circle (EXCLUDING brush thickness)
\newdimen\UPlus % \Unit plus \Eps

\newcount\AltSpacing % Minimum distance between alternatives,

% Circle size selection

\def\SmallCircles{
\chardef\Ichar='010
\chardef\IIchar='013
\chardef\IIIchar='012
\chardef\IVchar='011
\ComputeDimensions
}
\def\BigCircles{
\chardef\Ichar='014
\chardef\IIchar='017
\chardef\IIIchar='016
\chardef\IVchar='015
\ComputeDimensions
}

\def\ComputeDimensions{
\Wid=0.75pt
\Eps=0.5\Wid
\setbox0\hbox{\CIRCF \Ichar}
\Unit=0.5\wd0
\UPlus=\Unit \advance \UPlus by \Eps
}

% Useful spacing macros:

\def\BackU{\kern -\Unit}
\def\BackUU{\kern -2\Unit}
\def\ForwU{\kern \Unit}
\def\ForwUU{\kern 2\Unit}

\def\DnU{\lower \Unit}
\def\UpU{\raise \Unit}

\def\BackE{\kern -\Eps}
\def\BackEE{\kern -\Wid}
\def\ForwE{\kern \Eps}
\def\ForwEE{\kern \Wid}

\def\DnE{\lower \Eps}
\def\DnEE{\lower \Wid}
\def\UpE{\raise \Eps}
\def\UpEE{\raise \Wid}

% A note on spacing:

% The plan is to space things so that the CENTERS of horizontal lines
% (including the tops and bottoms of symbol boxes) are separated by
% integer multiples of 2\Unit. This would be easier if every
% chart-building macro (\Terminal, \Alternatives, \Arrow, \Empty, etc.)
% returned its result in a box just big enough to contain it, except
% that lines along the top and bottom be only half inside (i.e., the ink
% would extend \Eps outside the box on both sides).

% Unfortunately, it is hard to enforce this on \Fil and related macros.
% Instead, the chart-building macros construct boxes that are just big
% enough to contain the diagram, with no extra space around it and with no
% ink extending out of the bounding box. Therefore, the size of such boxes
% is k*2\Unit + \Wid, and in a vertical list they should be separated by
% \AltSpacing*\Unit-\Wid worth of glue.

\macrosection{ quarter circles...}

% \I, \II, \III, \IV are the quarter circles in the first, second, third
% and fourth quadrants, numbered counterclockwise starting with the
% upper-right one.

% Each macro produces a box of width =\Unit,
% height+depth = \Unit+\Wid, with the
% quarter circle stretching between two opposite corners.

% Actually, the endpoints of the PATH are on the corners of a square of
% side \Unit concentric with the box; The box is \Eps taller and deeper to
% account for the thicknes of the brush.

% The height and depth are such that the horizontal end of the arc is on
% the baseline. So, \I and \II have height=\Eps and depth=\UPlus, whereas
% \III and \IV have height = \UPlus, depth = \Eps.

\def\I{\vbox to \Eps{
\BackU\ForwE
\hbox to\Unit{\ForwU\BackE\CIRCF \Ichar \hss}
\vss
\hrule height -\Unit depth \UPlus width 0pt
}}

\def\II{\vbox to \Eps{
\BackU\ForwE
\hbox to \Unit{\BackE\CIRCF \IIchar \hss}
\vss
\hrule height -\Unit depth \UPlus width 0pt
}}

\def\III{\vbox to\UPlus{
\ForwE
\hbox to \Unit{\BackE\CIRCF \IIIchar \hss}
\vss
\hrule height 0pt depth \Eps width 0pt
}}

\def\IV{\vbox to \UPlus{
\ForwE
\hbox to \Unit{\BackE\ForwU\CIRCF \IVchar \hss}
\vss
\hrule height 0pt depth \Eps width 0pt
}}

% End caps for Terminal boxes:

% These macros return boxes with depth=height=\UPlus, width=\Unit,
% containing a semicircular stroke that begins and ends at two adjacent
% corners and touches the middle of the opposite side. The ink protrudes
% \Wid distance out left and right, but not
% top and bottom.

\def\LeftRound{\vbox to \UPlus{\CIRCF
\BackU\ForwE
\hbox to \Unit{\BackE\IIchar\hss}\nointerlineskip
\vss
\hbox to \Unit {\BackE\IIIchar\hss}\nointerlineskip
\hrule height -2\Unit depth\UPlus width 0pt
}}

\def\RightRound{\vbox to \UPlus{\CIRCF
\BackU\ForwE
\hbox to \Unit{\ForwU\BackE\Ichar\hss}\nointerlineskip
\vss
\hbox to \Unit{\ForwU\BackE\IVchar\hss}\nointerlineskip
\hrule height -2\Unit depth\UPlus width 0pt
}}

% End caps for Nonterminal boxes:

\def\LeftSquare
{\vrule height\UPlus depth\UPlus width \Wid \BackE}
\def\RightSquare
{\BackE \vrule height\UPlus depth\UPlus width \Wid}

\macrosection{ arrows...}

% \LeftGoingArrow, \RightGoingArrow, \BigLeftGoingArrow, and
% \BigRightGoingArrow always produce arrows pointing in the stated
% direction.

% Arrows have height=depth=\Eps, i.e. the arrow tips stick out of the
% enclosing boxes.

% These arrows use the "<" and ">" characters of font cmtt10

\newdimen\ArrowLength
\ArrowLength=6.5pt

\def\LeftGoingArrow{\MakeArrow{\ARRF\lower3.1pt\hbox{<}\hss}}
\def\RightGoingArrow{\MakeArrow{\hss\ARRF\lower3.17pt\hbox{>}\kern-1pt}}

\def\BigLeftGoingArrow{\MakeArrow{\kern-1pt\BARRF\lower4.25pt\hbox{<}\hss}}
\def\BigRightGoingArrow{\MakeArrow{\hss\BARRF\lower4.3pt\hbox{>}\kern-2pt}}

\def\DownGoingArrow{\hbox to 0pt{\hss\BARRF\char'024\kern-1pt\hss}}

\def\MakeArrow#1{{
\LineRule width \ArrowLength
\kern-\ArrowLength
\setbox0\hbox to \ArrowLength{#1} \ht0=0pt \dp0=0pt
\box0
}}

% \InputLeftArrow, \InputRightArrow, \OutputLeftArrow, and
% \OutputRightArrow define the stems that get put on each terminal or
% nonterminal box. The appropriate "real" arrow is put on the entry side
% of each box, and the corresponding "dummy" one is put on the exit side to
% balance the whole thing. This means the centers of vertically stacked
% symbol boxes will be aligned.

\def\DontOmitArrows{
\def\InputLeftArrow{\LeftGoingArrow}
\def\OutputLeftArrow{\LineRule width \ArrowLength}
\def\InputRightArrow{\RightGoingArrow}
\def\OutputRightArrow{\LineRule width \ArrowLength}
}

% If the charts get too wide, the following macro can be used to
% supress all right-going arrows in symbol boxes.

\def\OmitRightArrows{
\def\InputLeftArrow{\LeftGoingArrow}
\def\OutputLeftArrow{\LineRule width \ArrowLength}
\def\InputRightArrow{\LineRule width 2.5\Wid}
\def\OutputRightArrow{\LineRule width 2.5\Wid}
}

% In extreme cases, supress all arrows:

\def\OmitArrows{
\def\InputLeftArrow{}
\def\InputRightArrow{}
\def\OutputLeftArrow{}
\def\OutputRightArrow{}
}

% The internal macros \LeftToRight, \RightToLeft, and \SwitchDirection
% control the way arrows are pasted on symbol boxes, and the meaning of
% the \Arrow and \BigArrow user macros:

\def\LeftToRight
{\def\ArrowOnLeftSide{\InputRightArrow}
\def\ArrowOnRightSide{\OutputRightArrow}
\def\Arrow{\RightGoingArrow\Empty\ignorespaces}
\def\BigArrow{\BigRightGoingArrow\Empty\ignorespaces}
\def\SwitchDirection{\RightToLeft}}

\def\RightToLeft
{\def\ArrowOnLeftSide{\OutputLeftArrow}
\def\ArrowOnRightSide{\InputLeftArrow}
\def\Arrow{\LeftGoingArrow\Empty\ignorespaces}
\def\BigArrow{\BigLeftGoingArrow\Empty\ignorespaces}
\def\SwitchDirection{\LeftToRight}}

\macrosection{ symbol boxes...}

% The next macros make terminal and nonterminal boxes:

\def\SquareBox#1{\ArrowOnLeftSide\LeftSquare
\Sandwich{#1}
\RightSquare\ArrowOnRightSide}

\def\RoundBox#1{\ArrowOnLeftSide\LeftRound
\Sandwich{#1}
\RightRound\ArrowOnRightSide}

% The following macros will produce a round or square box containing
% the given argument, vertically centered. The user can control the
% vertical positioning of the box contents by including the appropriate
% strut. For example, \Terminal{+\OpStrut} produces a round box with the
% plus sign at the exact center (in most fonts).

\def\NonTerminal#1{
\SquareBox{\NonTerminalFont\ #1\ }
\ignorespaces}

\def\Terminal#1{
\RoundBox{\kern -0.5\Unit \TerminalFont #1\kern-0.5\Unit}
\ignorespaces}

\def\Keyword#1{
\RoundBox{\kern-0.25\Unit\KeywordFont #1\kern-0.25\Unit}
\ignorespaces}

% \Sandwich puts its argument between two horizontal lines
% exactly 2\Unit apart, and centers the result vertically.
% The \halign is there to center the argument horzly if it happens to have
% negative width (as may be the case for thin terminal symbols).

% The resulting box has height=depth=\UPlus

\def\Sandwich#1{\DnU\vbox
{\HorzLine\BackE\ForwU
\vbox to 0pt{\vss
\halign{\hfil##\hfil\cr\null\cr\hbox{#1}\cr}
\vss\null
}
\ForwU\BackE
\HorzLine
}}

\macrosection{ random macros...}

% Some useful formatting primitives used internally:

\def\LineRule{\vrule height \Eps depth \Eps} % Horz line in horz mode
\def\HorzLine{\hrule height \Eps depth \Eps} % Horz line in vert mode
\def\VertLine#1{\BackE \vrule #1 width \Wid \BackE} % Vert line in horz mode

% A strut with height and width like those of box 0:

\def\BZStrut{\vrule height \ht0 depth \dp0 width 0pt}

\def\LinetoTop{\VertLine{ depth -\Unit}}
\def\LinetoBot{\VertLine{ height -\Unit}}
\def\FilVertLine{\BackE \vrule width \Wid \BackE}

% These macros are meant to be used by the User:

% \Strut is a strut for text with ascenders and descenders;
% \OpStrut is a strut that is good for centering math operators
% \CapStrut is a strut for all-uppercase text

\def\Strut{{\setbox0\hbox{Bg}\BZStrut}}
\def\OpStrut{{\setbox0\hbox{(}\BZStrut}}
\def\CapStrut{{\setbox0\hbox{B}\BZStrut}}

% Say things like \HLine 10pt plus 20pt to get stretchable lines
% in horizontal mode

\def\HLine{\leaders\HorzLine\hskip}

% Use \Empty in empty alternatives, etc:

\def\Empty{\LineRule width 0pt\ignorespaces} % For empty alternatives
\def\ULine{\LineRule width \Unit\ignorespaces} % A short horizontal line
\def\UULine{\LineRule width 2\Unit\ignorespaces} % Double that

\def\Fil{\HLine 0pt plus 1fil\ignorespaces} % A stretchable horz line
\def\Fill{\HLine 0pt plus 1fill\ignorespaces} % A more stretchable one
\def\FilNeg{\HLine 0pt plus -1fil\ignorespaces} % Cancels a \Fil

\macrosection{ chart sections...}

% Use \Define to start a section of the diagram:

\def\LeftHandSide#1{
\setbox0\hbox{\NonTerminalFont
\vrule height \UPlus depth \UPlus width 0pt
#1}
\dimen0=\wd0
\leavevmode
\vbox{\box0\kern -\Wid \hbox{\LineRule width \dimen0}}
}

\newbox\LineBox
\newif\ifgroundlevel % True only at the top level in a \Define

\def\Define#1{\par
\message{ #1...}
\groundleveltrue
\vfil\penalty-50\vfilneg
\vskip \AltSpacing\Unit
\vskip \AltSpacing\Unit
\vskip -\Wid
\setbox\LineBox\hbox\bgroup
\LeftHandSide{#1}
\UULine
\ignorespaces
}

% And use \EndDef to end it:

\def\EndDef{
\ifgroundlevel \else \InvalidUseOfNewLine \fi
\egroup % Close the \LineBox
\hbox to \hsize{
\unhbox\LineBox
\DefaultFil
\BigRightGoingArrow
}
}

% \DefaultFil is a stretchable line that is (almost) infinitely
% stiffer than \Fil. It is the default left/right filling in
% \Define, \Alternatives, etc. but is overriden by user's \Fil.

\def\DefaultFil{\leaders\HorzLine\hskip 0pt plus 1000pt}

\macrosection{ line breaks...}

% The minimum line length for \NewLine and \OptionalLines:

\newdimen\MinLineWidth

% If a section gets too wide, \NewLine will continue the chart on a new line:

\def\NewLine{\OptionalLines{}}

% In general, \OptionalLines{\Line{Foo}\Line{Bar}...}
% is equivalent to (but nicer than) the sequence
% \NewLine
% \Alternatives{\Middle{Foo}\Lower{}}\NewLine
% \Alternatives{\Middle{Bar}\Lower{}}\NewLine
% ...

\def\OptionalLines#1{
\ifgroundlevel \else \InvalidUseOfNewLine \fi
% Close the \LineBox, print it, and print the
% connector to the next line
\BreakCurrentLine

% Typeset optional lines:
\begingroup
\groundlevelfalse
\def\Line{\DoOptionalLine}
% Process the lines:
\ignorespaces #1
\endgroup

% Finally, begin the next line:
\BeginNewLine
\ignorespaces
}

% These internal procedures are acalled by
% \NewLine and \OptionalLines:

\def\BreakCurrentLine{
\DefaultFil\ULine\I\LinetoBot\ForwU
\egroup % Close the \LineBox
\halign{##\cr

% Insert a horizontal strut to ensure minimum line length
\vrule height0pt depth0pt width\MinLineWidth \cr

% Insert the line:
\unhbox\LineBox\cr

% Insert the connector to the next line:
\dimen0=\AltSpacing\Unit \advance\dimen0 by -\Eps
\advance\dimen0 by \Unit
\vrule height \dimen0 depth \dimen0 width 0pt
\ForwUU\ForwUU\LinetoBot\II
\Fil\BigLeftGoingArrow\Fil
\IV\LinetoTop\ForwU\cr
}
}

\def\DoOptionalLine#1{
% Typeset the optional line:
\setbox0\hbox{\ForwUU\ForwUU\FilVertLine
\III
\ULine#1\DefaultFil\ULine\I\LinetoBot\ForwU
}

\halign{##\cr

% Insert horizontal strut to guarantee minimum line width:
\vrule height0pt depth0pt width\MinLineWidth \cr

% Now insert the stuff in the line:
\unhbox0 \cr

% Now the connector to the next line:
\dimen0=\AltSpacing\Unit \advance\dimen0 by -\Eps
\advance\dimen0 by \Unit
\dimen2=\dimen0 \advance\dimen2 by \Unit
\vrule height \dimen0 depth \dimen2 width 0pt
\ForwUU\ForwUU\FilVertLine\II
\Fil\BigLeftGoingArrow\Fil
\IV\LinetoTop\ForwU\cr

}
\ignorespaces
}

\def\BeginNewLine{
\setbox\LineBox\hbox\bgroup
\ForwUU\ForwUU\LinetoTop\III\ULine
}

\macrosection{ alternatives...}

% Both \Alternatives and \Repeat work in roughly the same fashion, so this
% description applies to them both. The single argument is composed of the
% subcomponents, each one enclosed in braces and preceded by a control
% sequence. These control sequences get defined as local macros, and grab
% the subcomponents as parameters.

% Actually, this happens twice: the first time the argument is interpreted,
% the local macros just ignore their parameter, and the second time
% through, the real work is done. The first pass is used to count the
% subcomponents, since the last one has to be treated specially, and also
% does some validation of the arguments.

% If the structure of the macro isn't tricky enough for you, you can try to
% understand the way the result is built up out of boxes. Here is the
% basic idea: the alternatives are piled up as a \halign, each preceded by
% a left connector and a \Fil, and followed by a \Fil and the right
% connector. The midlines of the vertical wires are right on the edges of
% the \halign.

% The height+depth of the upper alternatives, (and the height of the middle
% one) are added and stored in a local variable.
% The result of the \halign is \vtop ped and raised by that amount.

% The \halign is needed since each row has to be expanded to the maximum
% width of everything in the middle. It also implies that empty alternatives
% have their height or depth (which is usually \Eps) augmented by an extra
% \Unit, to accomodate the connectors.

% To get the right vertical spacing, some entries may have an extra
% are preceded by a strut whose height is
% (\AltSpacing)*\Unit plus the `natural' height of the current entry,
% and minus the amount of blank space already inserted at the bottom
% of the previous one. The latter is nonero only if the previous alternative
% has a `natural depth' that is too small to accomodate the side connectors,
% as for example in \Upper{\Empty},
% \Upper{\Repeat{\Upper{...}\Middle{\Empty}}}, or omitted \Middle.
% is inserted between consecultive alternatives. Also, between
% every two rows of the \halign we insert a \kern -\Wid, to
% compensate for the thickness of lines and ensure seamless connections.

% Since each row of an \halign is a separate group, some tricks are
% required to propagate information from one row to the next and
% yet to prevent nested \Alternatives from interacting in undesired
% ways. Those valueas are normally stored in the global registers
% \AltCtr, \ChartHeight, and \PrevSpace.
% While typesetting one branch (which may involve
% recursive calls to % \Alternatives) those values are locally assigned
% to scratch registers, and then re-assigned to the global
% ones after the stuff in that branch has been made into
% a box.

% These reagisters should be assigned with \global:

\newcount\AltCtr % Alternative counter
\newdimen\ChartHeight % Height of current chart above its baseline
\newdimen\PrevSpace % `Actual' minus `natural' depth of previous alt.

% These should be defined only locally:

\newcount\AltNum % Number of alternatives in current chart

\newcount\SaveAltCtr
\newdimen\SaveChartHeight
\newdimen\SavePrevSpace

\def\SaveData{
\SaveAltCtr=\AltCtr
\SaveChartHeight=\ChartHeight
\SavePrevSpace=\PrevSpace
}

\def\RestoreData{
\global\AltCtr=\SaveAltCtr
\global\ChartHeight=\SaveChartHeight
\global\PrevSpace=\SavePrevSpace
}

\newif\ifupper % Flags that tell the type of previous
\newif\iflower % alternative

\def\Alternatives#1{
\begingroup

\groundlevelfalse

% First, count alternatives:
\AltNum=0 \upperfalse \lowerfalse
\def\Upper##1{\iflower \BadUseOfUpper \fi
\inc\AltNum \uppertrue \lowerfalse}
\def\Middle##1{\iflower \BadUseOfMiddle \fi
\inc\AltNum\upperfalse \lowertrue}
\def\Lower##1{\inc\AltNum\upperfalse \lowertrue}
#1

% Now typeset them:
\global\AltCtr=\AltNum
\global\ChartHeight=0pt
\global\PrevSpace=\AltSpacing\Unit
\upperfalse \lowerfalse

\def\Upper{\UpperAlternative}
\def\Middle{\MiddleAlternative}
\def\Lower{\MaybeOmittedMiddleAlternative\LowerAlternative}

\setbox0\vtop{\null\halign{##\cr
\ignorespaces#1
\MaybeOmittedMiddleAlternative
\Empty\cr
}}
\hbox{\ForwU\raise\ChartHeight \box0\ForwU}

\endgroup
\ignorespaces}

\macrosection{ repeat...}

\def\Repeat#1{
\begingroup

\groundlevelfalse

% First count the alternatives:
\AltNum=0 \upperfalse \lowerfalse
\def\Upper##1{\iflower \BadUseOfUpper \fi
\inc\AltNum\uppertrue \lowerfalse}
\def\Middle##1{\iflower \BadUseOfMiddle \fi
\inc\AltNum\upperfalse \lowertrue}
\def\Lower##1{\iflower \else \BadUseOfLower \fi
\inc\AltNum\upperfalse \lowertrue}
#1
\iflower \else \NoMiddle \fi

% Now typeset them:
\global\AltCtr=\AltNum
\global\ChartHeight=0pt
\global\PrevSpace=\AltSpacing\Unit
\upperfalse \lowerfalse

\def\Upper{\SwitchDirection\UpperAlternative}
\def\Middle{\RepeatBody}
\def\Lower{\SwitchDirection\LowerAlternative}

\setbox0\vtop{\null\halign{##\cr
\ignorespaces#1
\Empty\cr
}}
\hbox{\raise\ChartHeight \box0}

\endgroup
\ignorespaces}

\macrosection{ internals of the above...}

\def\AltStrut{
% Assumes \box0 tightly contains the current alternative,
% minus its side connectors, and \PrevSpace contains
% the actual minus natural depth of the previous alternative.

% Produces a vertical strut that ensures \AltSpacing\Unit-\Wid
% whitespace between this and the previous alternative.

\ifdim \PrevSpace<\AltSpacing\Unit \then
\dimen0=\AltSpacing\Unit \advance\dimen0 by -\PrevSpace
\advance\dimen0 by \ht0
\vrule height \dimen0 depth 0pt width 0pt
\fi
}

\def\UpperAlternative#1{
\global\dec\AltCtr

% Typeset the argument into \box0, without connectors and vert space.
% Beware of recursive calls.
\SaveData \setbox0\hbox{\Empty\ignorespaces#1} \RestoreData

% Now add top spacer, add connecting arcs, and compute bottom space
% added because of them:
\dimen2=\dp0 % Save `natural' depth
\setbox0\hbox{\II\DefaultFil \AltStrut \unhbox0\relax\DefaultFil\I}
\global\PrevSpace=\dp0
\global\advance\PrevSpace by -\dimen2

% Update total chart height:
\global\advance \ChartHeight by \ht0
\global\advance \ChartHeight by \dp0

% Now add side rails, and pass it to the \halign:
\ifupper \FilVertLine \else \LinetoBot \fi
\unhbox0\relax
\ifupper \FilVertLine \else \LinetoBot \fi
\cr

% Backspace to account for line thickness:
\noalign{\kern -\Wid}
\global\advance \ChartHeight by -\Wid

\uppertrue\lowerfalse\ignorespaces
}

\def\MiddleAlternative#1{
\global\dec\AltCtr

% Typeset the argument into \box0, without connectors and vert space:
\SaveData \setbox0\hbox{\Empty\ignorespaces#1} \RestoreData

% Now add top spacer, add connecting arcs, and compute bottom space
% added because of them:
\dimen2=\dp0 % Save `natural' depth
\setbox0\hbox{
\ifupper \BackU\IV \fi
\ifnum \AltCtr>0 \then \BackU\I \fi
\BackU\UULine
\DefaultFil \AltStrut \unhbox0\relax \DefaultFil
\UULine\BackU
\ifupper \III\BackU \fi
\ifnum \AltCtr>0 \then \II\BackU \fi
}

\global\PrevSpace=\dp0
\global\advance\PrevSpace by -\dimen2

% Update total chart height:
\global\advance\ChartHeight by \ht0

% Now add side rails, and pass it to the \halign:
\ifupper \LinetoTop \fi
\ifnum \AltCtr>0 \then \LinetoBot \fi
\unhbox0 \relax
\ifupper \LinetoTop \fi
\ifnum \AltCtr>0 \then \LinetoBot \fi
\cr

% Backspace to account for line thickness:
\noalign{\kern -\Wid}
\upperfalse\lowertrue\ignorespaces
}

\def\MaybeOmittedMiddleAlternative{
\let\next=\relax
\iflower \else \let\next=\OmittedMiddleAlternative \fi
\next
}

\def\OmittedMiddleAlternative{

\setbox0\hbox{
\vrule height \Eps depth\Eps width 0pt
\ifupper \BackU\IV\LinetoTop \fi
\ifnum \AltCtr>0 \then \BackU\I\LinetoBot \fi
\ForwU

\ifupper
% Put a strut to ensure correct spacing relative to previous
% alternative. Try to distribute the extra space equally on both
% sides of the baseline:
\dimen0=\AltSpacing\Unit
\advance\dimen0 by -\PrevSpace
\advance\dimen0 by -2\Unit % height+depth of self
\dimen0 =0.5\dimen0
\ifdim \dimen0<0pt \then \dimen0=0pt \fi
% Round \dimen0 to a multiple of \Unit:
\advance\dimen0 by 0.5\Unit
\divide\dimen0 by \Unit \multiply \dimen0 by \Unit
% Now put the qppropriate strut:
\advance\dimen0 by \Unit
\vrule height \dimen0 depth 0pt width 0pt
\fi

\hfil

\ForwU
\ifupper \LinetoTop\III\BackU \fi
\ifnum \AltCtr>0 \then \LinetoBot\II\BackU \fi
}

% Update white space at bottom of previous alternative:
\global\advance\PrevSpace by \ht0
\global\advance\PrevSpace by \dp0
\global\advance\PrevSpace by -\Wid

% Update total chart height
\global\advance\ChartHeight by \ht0

% Now pass the stuff to the \halign:
\unhbox0\relax
\cr
% Backspace to compensate for line thickness:
\noalign{\kern -\Wid}
}

\def\RepeatBody#1{
\global\dec\AltCtr

% Typeset the argument into \box0, without connectors and vert space:
\SaveData \setbox0\hbox{\Empty\ignorespaces#1} \RestoreData

% Now add top spacer, add connecting arcs, and compute bottom space
% added because of them:
\dimen2=\dp0 % Save `natural' depth
\setbox0\hbox{
\ifupper \III\BackU \fi
\ifnum \AltCtr>0 \then \II\BackU\fi
\ULine \DefaultFil \AltStrut \unhbox0\relax \DefaultFil \ULine
\ifupper \BackU\IV \fi
\ifnum \AltCtr>0 \then \BackU\I \fi
}

\global\PrevSpace=\dp0
\global\advance\PrevSpace by -\dimen2

% Update total chart height:
\global\advance\ChartHeight by \ht0

% Now add side rails, and pass it to the \halign:
\ifupper \LinetoTop \fi
\ifnum \AltCtr>0 \then \LinetoBot \fi
\unhbox0
\ifupper \LinetoTop \fi
\ifnum \AltCtr>0 \then \LinetoBot \fi
\cr
% Backspace to compensate for line thickness:
\noalign{\kern -\Wid}
\upperfalse\lowertrue\ignorespaces
}

\def\LowerAlternative#1{
\global\dec\AltCtr

% Typeset the argument into \box0, without connectors and vert space:
\SaveData \setbox0\hbox{\Empty\ignorespaces#1} \RestoreData

% Now add top spacer, add connecting arcs, and compute bottom space
% added because of them:
\dimen2=\dp0 % Save `natural' depth
\setbox0\hbox{\III\DefaultFil\AltStrut \unhbox0\relax \DefaultFil\IV}
\global\PrevSpace=\dp0
\global\advance\PrevSpace by -\dimen2

% Now add side rails, and pass it to the \halign:
\ifnum \AltCtr=0 \then \LinetoTop \else \FilVertLine \fi
\unhbox0 \relax
\ifnum \AltCtr=0 \then \LinetoTop \else \FilVertLine \fi
\cr
% Backspace to compensate for line thickness:
\noalign{\kern -\Wid}
\upperfalse\lowertrue\ignorespaces
}

\macrosection{ horz alternatives...}

% Horizontal alternatives work in much the same way as the others, but is a
% little simple because we don't have to worry about a \Middle.
% We use a \halign with three rows (one for the stuff and
% two for the top and bottom connections), and keep track of the maximum
% height among the branches. The result of the \halign is \vtop ped and
% then raised by that amount (plus the height of the top connectors).

% The same tricks used to handle \AltCtr and \ChartHeight in the vertical
% case are also used here.

\def\HorzAlternatives#1{
\begingroup

\groundlevelfalse

% First, count the alternatives:
\AltNum=0
\def\Alternative##1{\inc\AltNum}
#1
\ifnum \AltNum<2 \then \NeedTwoOrMoreAlternatives \fi

% Now typeset them:
\global\AltCtr=\AltNum
\global\ChartHeight=0pt
\def\Alternative{\DoHorzAlternative}
\setbox0\vtop{\null
\halign{##&&##\cr
% Now generate the top row of the \halign
\global\count1=\AltNum
\TopHorzAlternativeConnectors
\cr \noalign{\kern -\Wid}
% Now the middle row:
\ignorespaces#1\cr\noalign{\kern -\Wid}
% Now the bottom connectors:
\global\count1=\AltNum
\BotHorzAlternativeConnectors\cr
}}
% Account for top connector height:
\global\advance\ChartHeight by \Unit
\hbox{\ForwU\raise\ChartHeight\box0 \ForwU}
\endgroup
\ignorespaces}

\def\TopHorzAlternativeConnectors{
& \ifnum \count1=\AltNum \then \II \fi
\ifnum \count1>1 \then \Fil \fi
&
\ifnum \count1>1 \then
\I \ifnum \count1>2 \then \BackU\ULine \fi
\let\next=\TopHorzAlternativeConnectors
\else
\let\next=\relax
\fi
\global\advance\count1 by -1\relax \next
}

\def\HorzAltStrut{
% Assumes \box0 is a tight box enclosing the alternative, without
% its connecting arcs. Produces a strut that ensures \AltSpacing\Unit
% whitespace between the alternative and the top and bottom railing.
% Assumes the top and bottom connectors already include one unit
% worth of whitespace, to accomaodate the circular arcs.

\dimen0=\ht0 \advance\dimen0 by \AltSpacing\Unit
\advance\dimen0 by -\Unit
\dimen2=\dp0 \advance\dimen2 by \AltSpacing\Unit
\advance\dimen2 by -\Unit
\vrule height \dimen0 depth \dimen2 width 0pt
}

\def\DoHorzAlternative#1{
&

% First, typeset the alternative without connectors and struts into
% \box0. Beware of recursive calls:
\SaveAltCtr=\AltCtr \SaveChartHeight=\ChartHeight
\setbox0\hbox{\Empty\ignorespaces#1}
\global\AltCtr=\SaveAltCtr \global\ChartHeight=\SaveChartHeight

% Now add connectors and vertical spacers:
\setbox0\hbox{
\ifnum \AltCtr=\AltNum \then \BackU\IV\BackU\UULine \else \III \fi
\HorzAltStrut \unhbox0
\ifnum \AltCtr>1 \then \I \else \UULine\BackU\II\BackU \fi
}

% Now update the maximum height:
\ifdim \ht0>\ChartHeight \then \global\ChartHeight=\ht0 \fi

% Finally, add vertical connections and pass it to the \halign:
\LinetoTop \unhbox0 \LinetoBot
&
\hfil
\global\dec\AltCtr
}

\def\BotHorzAlternativeConnectors{
&
\ifnum \count1=\AltNum \then
\hfil
\else
\Fil \ifnum \count1=1 \then \IV \fi
\fi
&
\ifnum \count1>1 \then
\III \let\next=\BotHorzAlternativeConnectors
\ifnum \count1<\AltNum \then\BackU\ULine \fi
\else
\let\next=\relax
\fi
\global\advance\count1 by -1\relax \next
}

\macrosection{ syntax chart...}

\def\SyntaxChart{
\par
\lineskip0pt
\baselineskip-1pt
\parfillskip 0pt plus 10000pt % stiffer than the \Define-\EndDef glue
\LeftToRight
\IgnoreLinebreaks
}

\macrosection{ defaults...}

\AltSpacing=2
\MinLineWidth=0pt
\DontOmitArrows
\SmallCircles
\DontIgnoreWhiteSpace % so the user can space significantly

\macrosection{ done.}

% END OF FILE