% Copyright 2012-2020, Alexander Shibakov
% This file is part of SPLinT
%
% SPLinT is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% SPLinT is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with SPLinT.  If not, see <http://www.gnu.org/licenses/>.

% indexing macros

% set up the list of unindexable terms

\ifx\unindexable\UNDEFINED
    \def\unindexable{{$\TeXx$}{$\TeXa$}{$\TeXb$}{$\TeXf$}{$\TeXao$}{$\TeXfo$}}%
\else
    \expandafter\def\expandafter\unindexable\expandafter
        {\unindexable{$\TeXx$}{$\TeXa$}{$\TeXb$}{$\TeXf$}{$\TeXao$}{$\TeXfo$}}%
\fi

\def\TeXx{\hbox{\let\_\UL\tt\TeX\_}}
\def\TeXa{\hbox{\tt\TeX\rm(a)}}
\def\TeXb{\hbox{\tt\TeX\rm(b)}}
\def\TeXf{\hbox{\tt\TeX\rm(f)}}
\def\TeXao{\hbox{\tt\TeX\rm(ao)}}
\def\TeXfo{\hbox{\tt\TeX\rm(fo)}}

% macros to determine if the current term is indexable

\newif\ifindexverbose

\def\yyifindexable#1{%
    {%
        \def\n@xt{#1}%
        \expandafter\s@tindexable\unindexable\relax\end % the \relax is to preserve the braces
                                                        % in the last list element
    }%
}

\def\s@tindexable#1#2\end{%
    {%
        \def\next{#1}%
        \ifx\next\n@xt % match found, stop
            \yybreak{\aftergroup\s@t@ndexable}%
        \else
            \yybreak{%
                \yystringempty{#2}{% unindexable sequence list has been exhausted, stop
                    \aftergroup\s@t@nd@xable
                }{% no match yet, continue
                    \aftergroup\s@tindexable
                }%
            }%
        \yycontinue
    }%
    #2\end
}

\def\s@t@ndexable#1\end{\aftergroup\yysecondoftwo}

\def\s@t@nd@xable#1\end{\aftergroup\yyfirstoftwo}

% \Cee\ index entry (produced by \CWEAVE\ and typeset directly from the file)

\def\I#1, #2.{%
    \makeoneindexentry{#1}{#1}{#2}{\setxreflistplain}{\filterxrefsplain}%
}

% other language index entries (produced by \GI, \FI, and \HI macros)

%     the standard reference format (no page number references)

\def\Ji#1#2, #3.{% #1 is the `visible key', #2 and #3 are similar to #1 and #2 in \I above
    \makeoneindexentry{#1}{#2}{#3}{\setxreflistplain}{\filterxrefsplain}%
}%

%     the `fine' reference format

\def\Fi#1#2, #3.{% #1 is the `visible key', #2 and #3 are similar to #1 and #2 in \I above
    \makeoneindexentry{#1}{#2}{#3}{\setxreflist}{\filterxrefs}%
}%

% special index entries: redirects, etc. (generated by bindx.pl)

\def\Jk#1#2, #3.{% 
    \makeoneindexentry{#1}{#2}{#3}{\yyid}{\eatone}%
}%

\def\makeoneindexentry#1#2#3#4#5{% #1 is the `visible key'
                                 % #2 is the term (including typesetting)
                                 % #3 is the list of references
                                 % #4 is the command to process #3
                                 % #5 is the command to filter and process #3
    \yyifindexable{#1}{\oneindexentry{#2}{#3}{#4}}{%
        \ifindexverbose{\toksa{#1}\message{filtering term: \the\toksa}}\fi
        \oneindexentryfiltered{#2}{#3}{#5}%
    }%
}

% insert the original control sequence name after the `graphic' version (\thisnamex
% is set by yytexlex.sty macros

\def\defypostambleshowcs{{ \rm(}\hbox{\sixpoint\tt\char`\\}\.{\thisnamex}{\rm)}}%

% let the author set the format for a cross reference list.

\def\setxreflistplain#1{%
    \ifacro\pdfnote#1.\else#1.\fi
}

\def\oneindexentry#1#2#3{%
    \checkforninecs#1\9\end{%
        \the\toksg\toksg{}% set the index section header
        \let\defypreamble\empty
        \let\defypostamble\defypostambleshowcs
        \hangindent\inxhangindent\noindent\strut#1:%
            \hskip0pt plus2em\penalty1000\hskip0pt plus-2em\relax\kern\inxaiskip
        #3{#2}%
        \par
    }{%
        \toksg{#1}%
    }%
}

\def\filterxrefsplain#1{\setxrefsplain{\f@lterdefsplain}{#1}}

\def\filterxrefs#1{\setxrefs{\f@lterdefs}{#1}}

\def\oneindexentryfiltered#1#2#3{%
    \the\toksg\toksg{}% set the index section header
    \let\defypreamble\empty
    \let\defypostamble\defypostambleshowcs
    \hangindent\inxhangindent\noindent\strut#1:%
        \hskip0pt plus2em\penalty1000\hskip0pt plus-2em\relax\kern\inxaiskip
    #3{#2}%
    \par
}

\def\checkforninecs#1\9#2\end{%
    \yystringempty{#2}{\yyfirstoftwo}{\yysecondoftwo}%
}

\def\filterdefs#1#2{%
    #1{}{}, #2, \[]{}% the last empty braces are not necessary but this way
                     % both \f@lterdefs and \f@lterdefsplain can be defined
}

\def\f@lterdefsplain#1#2#3, \[#4]{%
    \yystringempty{#4}{ {#1}{#2}}{%
        \yystringempty{#1}{%
            \f@lterdefsplain{\[#4]}{#2#3}%
        }{%
            \f@lterdefsplain{#1, \[#4]}{#2#3}%
        }%
    }%
}

\def\f@lterdefs#1#2#3, \[#4]#5{%
    \yystringempty{#4}{ {#1}{#2}}{%
        \yystringempty{#1}{%
            \f@lterdefs{\[#4]{#5}}{#2#3}%
        }{%
            \f@lterdefs{#1, \[#4]{#5}}{#2#3}%
        }%
    }%
}

\def\setxrefs#1#2{% typeset filtered references (with page references)
    \expandafter\s@txrefs\romannumeral0\filterdefs{#1}{#2}%
}

\def\s@txrefs#1#2{%
    \yystringempty{#1}{%
        several refs.%
    }{%
        \setxreflist{#1}%
        \yystringempty{#2}{}{, other refs.}%
    }%
}

\def\setxrefsplain#1#2{% typeset filtered references (without page references)
    \expandafter\s@txrefsplain\romannumeral0\filterdefs{#1}{#2}%
}

\def\s@txrefsplain#1#2{%
    \yystringempty{#1}{%
        several refs.%
    }{%
        \ifacro\pdfnote#1.\else#1.\fi
        \yystringempty{#2}{}{, other refs.}%
    }%
}

\def\setxreflist#1{%
    \yystringempty{#1}{}{%
        \grabfinexrefs{}, #1, {}{}%
    }%
}

\def\grabfinexrefs#1, #2#3#{% collect (and process) fine references (i.e. references like lnnnr{p1, p2, ... }
    \yystringempty{#2#3}{% this is the last reference, clean up (the last empty group is left unchanged)
        \expandafter\relax\eatacomma#1%
    }{%
        \ifcat\noexpand#20% this is a bare number
            \yybreak{\consumeonexref{{}{}{#1}}{#2#3}}%
        \else % this is a qualified number
            \yybreak{\stripxrefdelims{{#2}{#1}}{}#3}%
        \yycontinue
    }%
}

\def\eatacomma, {}

% get the right delimeter; we assume that at least one token is present

\def\stripxrefdelims#1#2#3{%
    \ifnum`#3<`0
        \yybreak{\consumeonexref{{#3}#1}{#2}}% this is a delimeter
    \else
        \ifnum`#3>`9
            \yybreak@{\consumeonexref{{#3}#1}{#2}}% this is a delimeter
        \else % `0<=`#4<=`9
            \yybreak@{\stripxrefdelims{#1}{#2#3}}% keep looking for the next digit
        \fi
    \yycontinue
}

\ifx\consumeonexref\UNDEFINED
    \def\consumeonexref#1#2#3{% #1 is the accumulated references
                              % #2 is the section number
                              % #3 is the list of pages
        \addnewpair#1{#2}%
    }
\fi

% ignore paging information

\def\addnewpair#1#2#3#4{% #1 is the right delimeter
                        % #2 is the left delimeter
                        % #3 is the list of references
                        % #4 is the section number
    \grabfinexrefs{#3, #2\compoundlink{#4}{#4}#1}%
}


\def\compoundlink#1#2{%
    \ifacro
        \pdflink{#1}{#2}%
    \else
        #2%
    \fi
}

\def\pagelink#1{%
    \ifacro
        \pdfpagelink{#1}% use the section number
    \else
        #1%
    \fi
}

% indexing macros for grammar terms

\def\termidstring#1{% processed name in italics
    \numberstocharsandspaces#1\end
    \let\optstrextra\optstrextraesc
    \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
    \def\idxentry{{\it\the\toksa}}%
}%

\def\termvstring#1{% processed name in typewriter style
    \numberstochars#1\end
    \let\optstrextra\optstrextraesc
    \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
    \def\idxentry{{\tt\def\_{\char`\_}\the\toksa}}%
}%

\def\termttstring#1{% straightforward typewriter text
    \numberstocharsandspaces#1\end
    \def\idxentry{{\tt\the\toksa}}%
}%

\def\termhostidstring#1{% processed name in italics (using the host name parser)
    \numberstocharsandspaces#1\end
    \let\optstrextra\optstrextraesc
    {%
        \expandafter\let\expandafter\tosmallparser
            \csname to\stripbrackets\hostparsernamespace parser\endcsname
        \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
        \aftergroup\toksa
        \expandafter
    }\expandafter{\the\toksa}%
    \def\idxentry{{\it\the\toksa}}%
}%

\def\termhostvstring#1{% processed name in typewriter style (using the host name parser)
    \numberstocharsandspaces#1\end
    \let\optstrextra\optstrextraesc
    {%
        \expandafter\let\expandafter\tosmallparser
            \csname to\stripbrackets\hostparsernamespace parser\endcsname
        \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
        \aftergroup\toksa
        \expandafter
    }\expandafter{\the\toksa}%
    \def\idxentry{{\tt\def\_{\char`\_}\the\toksa}}%
}%

\def\termostring#1{% options (e.g. \flex\ and \bison\)
    \numberstocharsandspaces#1\end
    \def\idxentry{{$\langle$\bf\the\toksa$\rangle$}}%
}%

\def\termfsrestring#1{% flex regular expression definition names
    \numberstocharsandspaces#1\end
    \let\optstrextra\optstrextraesc
    \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
    \def\idxentry{{\def\_{\char`\_}\flexrendisplay{\the\toksa}}}%
}%

\def\termfsopstring#1{% flex option names
    \numberstocharsandspaces#1\end
    \let\optstrextra\optstrextraesc
    \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
    \def\idxentry{{\def\_{\char`\_}\hbox{\tt$\langle$\the\toksa$\rangle_{\rm f}$}}}%
}%

\def\termstring#1{%
    \numberstocharsandspaces#1\end
    \let\optstrextra\optstrextraesc
    \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
    \def\idxentry{{\tt"\the\toksa"}}%
}%

\def\termexception#1{% special names
    \numberstocharsandspaces#1\end
    \toksc\toksa
    \expandafter\nameproc\expandafter{\the\toksa}\with\parsebin
    \ifyyparsefail
        \expandafter\ifx\csname\prettynamecs\hostparsernamespace{\the\toksa}\endcsname\relax
            \errmessage{The name \the\toksc\space is exceptional but is not defined.}%
        \else
            \def\idxentry{{\it\csname\prettynamecs\hostparsernamespace{\the\toksa}\endcsname{#1}}}%
        \fi
    \else
        \errmessage{The name \the\toksc\space is not exceptional.}%
    \fi
}%

% \TeX\ conrol sequence output
\def\texcsstring#1{%
    \numberstocharsandspaces#1\end
    \def\idxentry{%
        \let\texnspace\hostparsernamespace
        \def\getcescape{% an \ seen is really an \, and will not go through C string processing
            \def\next{escape}%
            \switchon\next\in\currentstate
        }%
        \termindexfalse\expandafter\inlineTeXx\expandafter{\expandafter/\the\toksa}%
    }%
}%

\expandafter\def\csname acharswitch:index\endcsname{% correct reserved \TeX\ characters a la CWEB verbatim
    %$\%\\ % unaffected
    %\#\   % these never appear
    _{%
        \yybyte\expandafter{\csname \the\yybyte\endcsname}%
        \expandafter\yycp@\expandafter`\the\yybyte\relax
        \mkpurebyte
        \yyreturn
     }
}%

\expandafter\setspecialcharsfrom\csname acharswitch:index\endcsname

\def\texlexerindex{% now that all character codes are 12
    \let\default\yygetchar
    \let\next\yycp@
    \ifnum\yycp@>"3F %
        \ifnum\yycp@<"5B % an uppercase letter or @
                \def\next{letter}%
        \fi
    \fi
    \ifnum\yycp@>"60 %
        \ifnum\yycp@<"7B % 
                \def\next{letter}%
        \fi
    \fi
    \switchonwithtype\next\in\currentstate
}%

\def\indexseparator#1#2{% generic separator
    \vskip.5\baselineskip
    \centerline{\dinkus}%
    \vskip.5\baselineskip
}

\def\indexseparator#1#2{%
    \vskip.3\baselineskip
    \centerline{\csname index domain translation [#1]\endcsname}%
    \vskip.3\baselineskip
}

\expandafter\def\csname index domain translation [F]\endcsname{{\sc FLEX INDEX}}
\expandafter\def\csname index domain translation [T]\endcsname{{\sc\TeX\ INDEX}}

\def\indexsection#1{%
    \vskip.3\baselineskip
    \penalty-1000
    \hbox to \hsize{\strut\ssfbn #1\ \cdotfill}%
    \penalty10000
    \vskip.2\baselineskip
}

\def\9#1{%
    \indexs@ction#1\end
}

\def\indexs@ction#1#2\end{
    \indexsection{\uppercase{#1}}%
}

\def\otherlangindexseparator{%
  \enddoublecols
  \vskip.8\baselineskip
  \centerline{B{\sc ISON}, F{\sc LEX, AND} \TeX\ {\sc INDICES}}%
  \vskip.5\baselineskip
  \begindoublecols
}

% general index entries (generated by bindx.pl)

\def\GI#1#2#3#4.{% raw index entries (standard format, with no page references)
    {%
        \edef\hostparsernamespace{\yysecondoftwo#1}%
        \edef\currentrulecontext{\yyfirstoftwo#1}%
        \toksa{}\numberstocharsandspaces#3\end
        \edef\indexkeyseq{\the\toksa}%        
        \toksa{}#2{#3}%
        \expandafter\Ji\expandafter{\indexkeyseq}{\idxentry}#4.%
    }%
}%

\def\FI#1#2#3#4.{% raw index entries (fine format)
    {%
        \edef\hostparsernamespace{\yysecondoftwo#1}%
        \edef\currentrulecontext{\yyfirstoftwo#1}%
        \toksa{}\numberstocharsandspaces#3\end
        \edef\indexkeyseq{\the\toksa}%        
        \toksa{}#2{#3}%
        \expandafter\Fi\expandafter{\indexkeyseq}{\idxentry}#4.%
    }%
}%

\def\HI#1#2#3#4{% special raw index entries
    {%
        \def\hostparsernamespace{#2}%
        \let\defypreamble\empty
        \let\defypostamble\empty
        \toksa{}\numberstocharsandspaces#4\end
        \edef\indexkeyseq{\the\toksa}%
        \toksa{}#3{#4}%
        \expandafter\Jk\expandafter{\indexkeyseq}{\idxentry}, see  
            {\tt\hbox{\sixpoint\tt\char`\\}\indexkeyseq}.%
    }%
}%

% reference styles (ordinary terms are set in roman face)

\def\[#1]{{\it#1}} % term definitions (such as lhs in productions)
\def\(#1){$\underline{#1}$} % declarations (such as token declarations), underlined index item
\def\(#1){{\bf #1}} % declarations, an alternative to the above
\def\e#1e{#1{\sevenpoint$^\circ\!$}} % terms in examples
\def\f#1f{{\it#1\/\kern.2ex}${}^\circ\!$} % lhs in examples (italic correction does not seem to be enough)
\def\g#1g{$\underline{#1}^\circ\!$} % declarations in examples
\def\g#1g{{\bf #1}$^\circ\!$} % declarations in examples, an alternative to the above

\def\inxhangindent{1em}
\def\inxaiskip{.5em}
\def\inxicgap{5pt}

\def\inxmod{% new indexing macro
    \write\cont{} % ensure that the contents file isn't empty
         \write\cont{\catcode `\noexpand\@=12\relax}   % \makeatother
    \closeout\cont % the contents information has been fully gathered
    \message{Index:}
    \medskip
    \eightpoint\raggedright
    \fnotesstart=2
    \fnotesspan=1
    \noofcolumns=3
    \icgap=\inxicgap%
    \linecount=3
    \setmcparams
    \dsskip=0pt%
    \adjskip=0pt plus 9pt%
    % \TeX\ conrol sequence output
    \expandafter\let\expandafter\acharswitch\csname acharswitch:index\endcsname
    \let\texlexer\texlexerindex
    \let\*=\lapstar
    \begindoublecols
    \readindex
    \otherlangindexseparator
    \readgindex
}

\newread\trygindex

\def\readgindex{%
    \openin\trygindex=\jobname.gdy
    \ifeof\trygindex
    \else
        \closein\trygindex
        \input \jobname.gdy
    \fi
}
