% Copyright 2012-2022, 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/>.

% assigning symbolic names to production terms (this is only a demonstration)
% no attempt is made to make this code modular or namespace safe;
% the approach is not very elegant and can stand a lot of improvement;
% it was decided against such optimization to present a more explicit demonstration
% of the procedures

\def\makesymbols{%
 \ifsymdemo
     \restorecslist{symbols}\yyunion
     \toksa{}\toksb{}\toksc{}\toksd{}%
     %\showthe\newsymswitch
     \the\table
     \global\newsymswitch\newsymswitch
 \else
     \newsymswitch{}% otherwise \yysymswitch is trying to expand the wrong names (\namepair is not present to do \csname ... )
 \fi
}

% symbolic reference name parser

\newtoks\newsymswitch
\newread\symnames

\newwrite\symbolicswitch
\newif\ifsymdemo

\symdemotrue

\ifbootstrapmode
   \symdemofalse
\fi

\ifsymdemo
   \def\inamespace{[intermediate]}%
   \let\parsernamespace\inamespace
   \let\hostparsernamespace\mainnamespace % purely for testing reasons
   \pinittoks{}%
   \input cweb/gyytab.tex % this should be the parser that will be used later
                                           % in this case it is just an example
   \edef\tointermediateparser{%
       \let\noexpand\parsernamespace\noexpand\inamespace % switch to the new namespace
       \the\pinittoks % restore all the tables, tokens and constants, and stacks
       \let\noexpand\getcurrentparser\noexpand\tointermediateparser
   }%
   \settokens % this simply assigns values to tokens where the name of each token is taken out of yytname ...
   \input bo.tok % this will set up token equivalences in the namespace above ...
                 % those are the values gleaned during the bootstrapping stage.
                 % in the general case, one needs to run a bootsrapping (or similar)
                 % parser to extract the token information.
   \optimizeall % this is necessary for correct rule listing in the output stage:
                % otherwise \fgetelemof will use the current value of the \yy... token registers which
                % will hold the values of the full parser that is loaded next
   \newsymswitch{}%
   \listrules                                % ... to be used while listing the rules
   % note that we do not bother to set up a lexer for this parser (even though we already have one and the
   % \optimizeall macro above will create a set of associative tables for it---this is merely an unwanted
   % sideffect); after the rules have been listed, the intemediate parser is no longer needed.
   %\showthe\newsymswitch
   \def\fullnamespace{[full]}% this is the parser that parses the bison grammar from a raw
                             % bison file; it can play a role of the bootstrap parser for
                             % the grammar above, as well, since its input is a complete
                             % bison file; note that it cannot be a bootstrap parser for itself
                             % because it will reject any input that does not form a complete
                             % bison file; this is why a special grammar was created that includes
                             % only a small subset of the full set of productions (%token rules only) to
                             % serve as a bootstrap grammar.
   %\tomainparser % this will set the value of \setflexstates, so that, if the lexer initialization
                  % below is omitted, the \setflexstates macro can still be used to correctly set the
                  % lexer states
   \let\parsernamespace\fullnamespace
   \pinittoks{}% %%
   \input cweb/fyytab.tex
   \input cweb/ltab.tex % we reinitialize the lexer out of necessity:
                                         % \collecttokennames needs to know how to switch
                                         % between two lexer/parser environments so both states
                                         % have to be preserved; if one is sure that
                                         % grammar_declarations will not used in the productions
                                         % part of the file, this can simply be disabled
   \settokens
   \setflexstates % the main lexer can be reused in this case; the states still need to be set up
   \input bo.tok % set up the tokens for the bison grammar parser
   \newparserstate
   \newlexerstate
   \newlexerstateextra
   %
   \setnulstack{yyirulestack}%
   %
   \edef\tofullparser{%
       \let\noexpand\parsernamespace\noexpand\fullnamespace % switch to the new namespace
       \the\pinittoks % restore all the tables, tokens and constants, and stacks
       \let\noexpand\getcurrentparser\noexpand\tofullparser
   }%
   \optimizeall
   \toksa{\input bg.y}% start building the parsing command
                      % this has to be done carefully, since all the characters input
                      % from the parser file have to be `harmless', so their categories
                      % have to be reset; in order for the parser to be able to stop,
                      % appropriate command sequences would have to be inserted at the end
   \toksb{\yyeof\yyeof\endparseinput\endparse
       \let\yyinput\yyinputold
       \undoascii
       \ifyyparsefail
           \errmessage{could not process symbols}%
       \else
           \tointermediateparser % the parsing is finished, so we have two very long strings:
                                 %  o the rules of the hosted parser ([intermediate])
                                 %  o the rules of the same parser just parsed
                                 % these two strings are used to associate the symbolic names
                                 % (which only exist in the second list) to the rule numbers and
                                 % the relevant terms;
                                 % switching the parse namespace above is done so that the term
                                 % indices are looked up in the appropriate table
           %\showthe\table
           \makesymbols % list all the rules deriving explicit non-terminals
           %\showthe\newsymswitch
           \setexplicitinlinerules\newsymswitch % process implicit non-terminals resulting from inline actions
           %\showthe\newsymswitch
           \makesymrefs\newsymswitch % create the switch to be used by the parser (\yyparse)
       \fi
   }%
   % build the command to create the symbolic name switch
   \toksc{\tofullparser\basicparserinit\bisonparserinit\bisonparserdatainit
       \let\yyinputold\yyinput
       \let\yyinput\yyinputtrivial % a demo of a stripped down, slightly faster input routine
       \doascii{11}\expandafter\yyparse}%
   \edef\next{\the\toksc\the\toksa\relax\the\toksb}\next % \relax is to stop \TeX\ from trying to expand the file name further
                                                         % it is not flagged as a bad character  because it is part of the epilogue
   \immediate\openout\symbolicswitch=\jobname.sns
   %
   \edef\next{\setsncommands{\def\noexpand\symswitch\hashletter1{\harmlesscomment^^J\noexpand\ifcase\hashletter1\relax
                                                                          \harmlesscomment^^J\the\setsncommands
                                                                          \noexpand\else\harmlesscomment^^J%
                                                                          \noexpand\fi\harmlesscomment^^J%
                                                                          }\harmlesscomment^^J^^J}}\next
   \edef\next{\unsetsncommands{\def\noexpand\symswitchoff\hashletter1{\harmlesscomment^^J\noexpand\ifcase\hashletter1\relax
                                                                          \harmlesscomment^^J\the\unsetsncommands
                                                                          \noexpand\else\harmlesscomment^^J%
                                                                          \noexpand\fi\harmlesscomment^^J%
                                                                          }\harmlesscomment^^J^^J}}\next
   {\newlinechar=`\^^J \immediate\write\symbolicswitch{\the\setsncommands\the\unsetsncommands}}%
   \immediate\closeout\symbolicswitch
   \tomainparser % go back to the main parser
%
%   \flex\ parser test
%
   {%
       \toflexreparser
       \basicparserinit
       \flexreparserinit
       \flexreparserdatainit
       \yyBEGIN{SECT2}%
       \flin@ruletrue
       % special status of `\yl' in \CWEB\ makes the following workaround necessary
       % if the code is unsed inside a \CWEB\ file
       %|@t}\expandafter\yyparse\space [a-b]*(c|d|e)?\yyeof\yyeof\endparseinput\endparse{@>|
       \expandafter\yyparse\space [a-b]*(c|d|e)?\yyeof\yyeof\endparseinput\endparse
       \ifyyparsefail\else
           \ferrmessage{done processing flex}%
       \fi
   }%
%
   {%
       \def\flnamespace{[flex]}
       \let\parsernamespace\flnamespace
       \toflexparser
       \basicparserinit
       %\flexparserinit
       \flexparserdatainit
       \yylessusedtrue % TODO: put it in the \genericparser command
       \let\f@nishparse\finishparse
       \def\finishparse{\endinput\message{end of input}\relax\let\cleanupcs\f@nishparse}%
       \def\next{%
           \catcode`\\=12
           \catcode`\^^J=12
           \catcode`\%=12
           \catcode`\^^M=12
           \catcode`\{=12
           \catcode`\}=12
           \catcode`\#=12
           \catcode`\_=12
           %\yydebugmost
           \expandafter\yyparse\input cweb/lo.l \cleanupcs\yyeof\yyeof\endparseinput\endparse % note that the space after the file name is necessary
           \ifyyparsefail
               \errmessage{stopped}%
           \fi
           \yydebugnone
           \basicparserinit
           \expandafter\yyparse\input cweb/fil.l \cleanupcs\yyeof\yyeof\endparseinput\endparse
           % note that the space after the file name is necessary
       }\expandafter\next % to lock the \catcode of the brace
   }%
\fi