# How to set indentation within grammar environment?

I'm using mdwtools and syntax package to write BNF grammar, I wonder is there a way to set the parindent so that each production line will be center algined instead of left aligned by default?

Minimal Example

\documentclass[10pt,letterpaper]{article}
\usepackage{mdwtab}
\usepackage{syntax}

\setlength{\grammarparsep}{0.25cm}   % vertical distance between production rules
\setlength{\grammarindent}{1cm}      % horizontal indent distance

\renewcommand{\syntleft}{}          % do not display '<' associated with variable, for example <A>
\renewcommand{\syntright}{}         % do not display '>' associated with variable, for example <A>

% Note: the empty line between production rules is not optional!

\begin{document}
\begin{grammar}
<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{grammar}
\end{document}


Update (\phantom solution)

\begin{grammar}\centering
<S> $\ra$ abB

<A> $\ra$ aaBb

<B> $\ra$ bbAa

<A> $\ra$ $\lambda$
\end{grammar}

-
Do you care about the alignment of the production lines compared to one another? For example, that <A>, <B> and <C> should be below one another. –  Werner Aug 21 '11 at 0:28
@Werner: Yes, I want to them to align with each other. Thank you. –  Chan Aug 21 '11 at 0:32

You can use \centering right after \begin{grammar} to center each production in its own line:

\begin{grammar}\centering
<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B \phantom{| c | d}

<C> $\rightarrow$ D \phantom{| c | d}
\end{grammar}


Another approach; the grammar environment is defined as a list, so you could define a similar environment, to manually specify the \leftmargin length. In the following example I defined a mygrammar environment with an optional argument allowing you to control the \leftmargin:

\documentclass[10pt,letterpaper]{article}
\usepackage{mdwtab}
\usepackage{syntax}
\usepackage{lipsum} % to geberate text for the example

\setlength{\grammarparsep}{0.25cm}   % vertical distance between production rules
\setlength{\grammarindent}{1cm}      % horizontal indent distance

\renewcommand{\syntleft}{}          % do not display '<' associated with variable, for example <A>
\renewcommand{\syntright}{}         % do not display '>' associated with variable, for example <A>

% Note: the empty line between production rules is not optional!

\makeatletter
\newenvironment{mygrammar}[1][0pt]{%
\list{}{%
\labelwidth\grammarindent%
\leftmargin\dimexpr\grammarindent+#1\relax%
\itemindent\z@%
\listparindent\z@%
\parsep\grammarparsep%
}%
\let\\\@normalcr
\syntaxShortcuts\relax\relax%
\def\gr@setpar{%
\def\par{%
\parshape\@ne\@totalleftmargin\linewidth%
\@@par%
\catcode\<12%
\everypar{%
\everypar{}%
\catcode\<\active%
\gr@implitem%
}%
}%
}%
\gr@setpar%
\par%
\let\gr@leftsq$% \let\gr@rightsq$%
\def\gr@endsyntdiag]{\end{syntdiag}\gr@setpar\par}%
\def${\@ifnextchar[{\begin{syntdiag}\@gobble}\gr@leftsq}% \def${\@ifnextchar]\gr@endsyntdiag\gr@rightsq}%
}{%
\@newlistfalse%
\everypar{}%
\endlist%
}
\makeatother

\begin{document}

\begin{grammar}

<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{grammar}

\begin{mygrammar}

<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{mygrammar}

\begin{mygrammar}[3cm]

<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{mygrammar}

\begin{mygrammar}[6cm]

<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{mygrammar}

\end{document}


Here's another more automated approach: you simply provide the longest line as a mandatory argument for the newly defined Cgrammar environment, and you get the contents centered and with left alignment:

\documentclass[10pt,letterpaper]{article}
\usepackage{mdwtab}
\usepackage{syntax}
\usepackage{lipsum} % to geberate text for the example

\setlength{\grammarparsep}{0.25cm}   % vertical distance between production rules
\setlength{\grammarindent}{1cm}      % horizontal indent distance

\renewcommand{\syntleft}{}          % do not display '<' associated with variable, for example <A>
\renewcommand{\syntright}{}         % do not display '>' associated with variable, for example <A>

% Note: the empty line between production rules is not optional!

\newlength\longest
\newlength\LeftMargin
\makeatletter
\newenvironment{Cgrammar}[1]{%
\list{}{%
\settowidth\longest{#1}
\setlength\LeftMargin{\dimexpr0.5\linewidth-0.5\longest}
\labelwidth\grammarindent%
\leftmargin\dimexpr\grammarindent+\LeftMargin\relax%
\itemindent\z@%
\listparindent\z@%
\parsep\grammarparsep%
}%
\let\\\@normalcr
\syntaxShortcuts\relax\relax%
\def\gr@setpar{%
\def\par{%
\parshape\@ne\@totalleftmargin\linewidth%
\@@par%
\catcode\<12%
\everypar{%
\everypar{}%
\catcode\<\active%
\gr@implitem%
}%
}%
}%
\gr@setpar%
\par%
\let\gr@leftsq$% \let\gr@rightsq$%
\def\gr@endsyntdiag]{\end{syntdiag}\gr@setpar\par}%
\def${\@ifnextchar[{\begin{syntdiag}\@gobble}\gr@leftsq}% \def${\@ifnextchar]\gr@endsyntdiag\gr@rightsq}%
}{%
\@newlistfalse%
\everypar{}%
\endlist%
}
\makeatother

\begin{document}

\begin{grammar}

<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{grammar}

\lipsum[1]
\begin{Cgrammar}{A> $\rightarrow$ B | c | d}

<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{Cgrammar}

\lipsum[1]
\begin{Cgrammar}{<A> $\rightarrow$ B | c | d | e}

<A> $\rightarrow$ B | c | d | e

<B> $\rightarrow$ B

<C> $\rightarrow$ D
\end{Cgrammar}

\lipsum[1]
\begin{Cgrammar}{<A> $\ra$ aaBb}
<S> $\ra$ abB

<A> $\ra$ aaBb

<B> $\ra$ bbAa

<A> $\ra$ $\lambda$
\end{Cgrammar}

\end{document}

-
Thanks a lot. However, the variables A, B, and C don't align vertically with each other. Is there a work around? –  Chan Aug 21 '11 at 0:33
@Chan: see my updated answer –  Gonzalo Medina Aug 21 '11 at 0:40
Very nice solution ;) Many thanks for your effort. I really appreciate it. –  Chan Aug 21 '11 at 0:42
@Chan: see my third edit for an automated solution which only requires you to pass the longest line as an argument. –  Gonzalo Medina Aug 21 '11 at 1:01
Melinda: Awesome. I bet I will learn many useful techniques from your solutions. Thank you Sir :) –  Chan Aug 21 '11 at 1:05
show 1 more comment

You can use \centering as @Gonzalo suggested, and suppliment this with ample use of \phantom{...} to acquire the desired alignment:

\begin{grammar}\centering
<A> $\rightarrow$ B | c | d

<B> $\rightarrow$ B \phantom{| c | d}

<C> $\rightarrow$ D \phantom{| c | d}
\end{grammar}


\phantom{<stuff>} makes a box of width of <stuff>, but does not typeset anything.

For your other example, one could use a predefined length of the largest item on the right-hand side (RHS) of your production. Then, using this length, you place each RHS in a box:

\newlength{\widest}% Length of widest element
\settowidth{\widest}{aaBb}% Widest element is "aaBb"
\newcommand{\rhs}[1]{\makebox[\widest][l]{#1}}
...
\begin{grammar}\centering
<S> $\rightarrow$ \rhs{abB}

<A> $\rightarrow$ \rhs{aaBb}

<B> $\rightarrow$ \rhs{bbAa}

<A> $\rightarrow$ \rhs{$\lambda$}
\end{grammar}


The calc package also offers a \widthof{<stuff>} command that you could use and remove the declaration of a new length like I did. However, the above illustrates the concept.

-
Thanks a lot the fix. I wonder will it work with a grammar of the form. See my update. –  Chan Aug 21 '11 at 0:46
The graphic/image I've added shows that it does... –  Werner Aug 21 '11 at 0:48
In your example, there is always a letter for every production. In my case, there is a single \lambda and other productions are string of letters. So I'm not sure exactly where should I group my \phantom section? –  Chan Aug 21 '11 at 0:51
I the productions were just characters, liberal use of \phantom wherever you don't want something printed would work. However, introducing something like \lambda, which doesn't have a width that matches a regular character, requires a little more work. I've adapted my solution for such a case, placing the right-hand side elements in a box of fixed width, depending on the widest element you provide. –  Werner Aug 21 '11 at 1:17
Thanks a lot for an alternative solution ;)! –  Chan Aug 21 '11 at 3:52