% 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/>.

% fast(er) versions of stack access functions

\catcode`\@=11

\def\yyinitstack#1{% #1 is the name of the stack
    \csname \expandafter\eatone\string#1<count>\endcsname\m@ne
}

% stacks will be defined as pairs ( \s t a c k n a m e [n u m b e r], \s t a c k n a m e <count> )
% where \s t a c k n a m e [n u m b e r] is an associative array of control sequences
% and \s t a c k n a m e <count> is a \count register
%
% note that in the implementaion below the stack grows `the wrong
% way'; this is done for convenience, since there is no `memory read
% with increment' operation, the `usual' (say, hardware) stack
% implementations take advantage of, and our stack can grow
% unrestricted this way; also note that while the array is namespace specific,
% the stack pointer is not, so special effort is required to preserve it

\let\yyinitarray\yyinitstack % using the stack as an array only makes sense for the optimized version

\def\getstackpointer#1{% expands to the value of the current top of the stack position
    \expandafter\the\csname \expandafter\eatone\string#1<count>\endcsname
}

\def\gettopofstackcs#1{% expands to the control sequence (a member of the associative array)
                       % that holds the value of the current element at the top of the stack
    \csname \expandafter\eatone\string#1[\getstackpointer#1]\parsernamespace \endcsname
}

\def\gettopofstackcsx#1{% a version of the above to be used with \romannumeral
    0\expandafter\noexpand\csname \expandafter\eatone\string#1[\getstackpointer#1]\parsernamespace \endcsname
}

\def\gettopofstackcsxx#1{% to be used with \romannumeral, expands to the contents of the top stack element
    0\expandafter\expandafter\expandafter\space\csname \expandafter\eatone\string#1[\getstackpointer#1]\parsernamespace \endcsname
}

\def\getmidstackcs#1#2{% expands to the control sequence (a member of the associative array)
                       % that holds the value of the current element at the specified (#2) position
    \csname \expandafter\eatone\string#1[#2]\parsernamespace\endcsname
}

\def\getmidstackcsx#1#2{% a version of the above to be used with \romannumeral, safe to use with \edef
    0\expandafter\noexpand\csname \expandafter\eatone\string#1[#2]\parsernamespace\endcsname
}

\def\movestackpointer#1\by#2{% 
    \expandafter\advance\csname \expandafter\eatone\string#1<count>\endcsname#2%
}

\def\yypopstack#1\by#2{% removes the top #2 elements from the top of the stack #1
    \movestackpointer#1\by{-#2}\relax
}

%

\def\yypop#1\into#2{% pops the stack #1, stores the top element in token register #2
    #2\expandafter{\romannumeral\gettopofstackcsxx#1}%
    \movestackpointer#1\by\m@ne
}

% pushing stuff on a stack: \yypush{t o k e n s}\on\yyvs or \expandafter\yypush\number\yystate\on\yyss

\long\def\yypush#1\on#2{%
    \movestackpointer#2\by\@ne
    \expandafter\expandafter\expandafter\def\gettopofstackcs#2{#1}%
}

\long\def\yypushx#1\on#2{% push with expand
    \movestackpointer#2\by\@ne
    \expandafter\expandafter\expandafter\edef\gettopofstackcs#2{#1}%
}

% push register contents on a stack: #1 is a register, #2 is a stack

\def\yypushr#1\on#2{%
    \movestackpointer#2\by\@ne
    \expandafter\expandafter\expandafter\edef\gettopofstackcs#2{\the#1}%
}

% the first parameter is the stack, the second is the location from the top (a nonnegative number), the third is
% the control sequence that will hold the value;

\def\yyreadstack#1\at#2\to#3{%
    \expandafter\advance\csname \expandafter\eatone\string#1<count>\endcsname-#2\relax
    %
    \expandafter\let\expandafter#3\csname \expandafter\eatone\string#1[\expandafter\the
        \csname \expandafter\eatone\string#1<count>\endcsname]\parsernamespace\endcsname
    %
    \expandafter\advance\csname \expandafter\eatone\string#1<count>\endcsname#2\relax
}

% same as above but reads the value into a register

\def\yyreadstackr#1\at#2\to#3{%
    \expandafter\advance\csname \expandafter\eatone\string#1<count>\endcsname-#2\relax
    %
    #3\csname \expandafter\eatone\string#1[\expandafter\the
        \csname \expandafter\eatone\string#1<count>\endcsname]\parsernamespace\endcsname\relax
    %
    \expandafter\advance\csname \expandafter\eatone\string#1<count>\endcsname#2\relax
}

% the macros below use general purpose registers and have a chance to interfere with
% the user namespace; they have been replaced by the macros that only use expandable
% arithmetic which leads to about 3 percent slowdown. thus the original macros were left 
% in the package in case such a slowdown is undesirable

\def\yyreadvstack#1\upto#2{% assume that #2 > 0
    \tempca=#2\relax
    \tempcb\toptoks
    \advance\tempcb\@ne
    \yyreadvst@ck{#1}%
}

\def\yyreadvst@ck#1{%
    \expandafter\toksdef\csname$$'\the\tempca\endcsname=\tempcb%
    \expandafter\yypop\expandafter#1\expandafter\into\csname$$'\the\tempca\endcsname%$
    \expandafter\expandafter\expandafter\def
        \expandafter\expandafter\csname$'\the\tempca\endcsname\expandafter{\the\toks\tempcb}%$
    \ifnum\tempca=\@ne %we have read the values
        \yybreak\relax
    \else
        \advance\tempcb\@ne
        \advance\tempca\m@ne
        \yybreak{\yyreadvst@ck{#1}}%
    \yycontinue
}

% the macros below replace the two macros above to only use expandable arithmetic

\def\yyreadvstack#1\upto#2{% assume that #2 > 0
    \expandafter\yyr@@dvst@ck\expandafter{\number#2}{#1}%
}

\def\yyr@@dvst@ck#1#2{%
    \expandafter\yyr@@@vst@@k\expandafter{\number\toptoks}{#1}{#2}%
}

\def\yyr@@@vst@@k#1#2#3{%
    \yyr@@dvst@@k{#2}{#1}{#3}%
}

\def\yyreadvst@ck#1#2#3{%
    \expandafter\toksdef\csname$$'#2\endcsname=#1\relax %
    \expandafter\yypop\expandafter#3\expandafter\into\csname$$'#2\endcsname%$
    % the portion below is redundant if the \yn macro below is used and no symbolic switch
    % is implemented
    \expandafter\expandafter\expandafter\def
        \expandafter\expandafter\csname$'#2\endcsname\expandafter{\the\toks#1}%$
    \ifnum#2=\@ne %we have read the values
        \yybreak\relax
    \else
        \yybreak{\expandafter\yyr@@dvst@@k\expandafter{\number\xdecrement{#2}}{#1}{#3}}%
    \yycontinue
}

\def\yyr@@dvst@@k#1#2#3{%
    \expandafter\yyreadvst@ck\expandafter{\number\xincrement{#2}}{#1}{#3}%
}

% end of replacement macros

\def\yypeekvstack#1\upto#2{% assume that #2 > 0
    \yyreadvstack#1\upto{#2}\relax
    \movestackpointer#1\by#2\relax
}

% macros for reading the state stack (needed if using bison version 3.0 and above)

\def\yyreadsstack#1\upto#2\withprefix#3{% assume that #2 > 0
    \expandafter\yyr@@dsst@ck\expandafter{\number#2}{#1}{#3}%
}

\def\yyr@@dsst@ck#1#2#3{%
    \expandafter\yyr@@@sst@@k\expandafter{\number\toptoks}{#1}{#2}{#3}%
}

\def\yyr@@@sst@@k#1#2#3#4{%
    \yyr@@dsst@@k{#2}{#1}{#3}{#4}{}%
}

\def\yyr@@dsst@@k#1#2#3#4#5{%
    \expandafter\yyreadsst@ck\expandafter{\number\xincrement{#2}}{#1}{#3}{#4}{#5}%
}

\def\yyreadsst@ck#1#2#3#4#5{%
    \ifnum#2=\z@ %we have read the values
        \yybreak{#5}%
    \else
        \yybreak{\expandafter\yyr@@dsst@@@\expandafter{\number\gettopofstackcs#3}{#1}{#2}{#3}{#4}{#5}}%
    \yycontinue
}

\def\yyr@@dsst@@@#1#2#3#4#5#6{%
    \movestackpointer#4\by\m@ne
    \expandafter\yyr@@dsst@@k\expandafter{\number\xdecrement{#3}}{#2}{#4}{#5}{#5{#1}#6}%
}

\def\yypeeksstack#1\upto#2\withprefix#3{% assume that #2 > 0
    \yyreadsstack#1\upto{#2}\withprefix{#3}\relax
    \movestackpointer#1\by#2\relax
}

% faster array access macros

\def\fastgetelemof#1\at#2{%
    \csname #1\parsernamespace\number#2\endcsname
}

\def\fgetelemof#1\at#2{% this definition allows mixing optimized and unoptimized tables
    \expandafter\ifx\csname optopt[#1]\parsernamespace\endcsname\relax
        \getelemof{#1}\at{#2}%
    \else
        \csname #1\parsernamespace\number#2\endcsname
    \fi
}

% new stack access macros that read the stack directly (to reduce namespace pollution)
% these are here more as an example

\def\yn#1#{%
  \ifnum#1<\@ne
      \yybreak{\putyyval}%
  \else
      \yybreak{\p@tyyassignment{#1}}%
  \yycontinue
}

\def\p@tyyassignment#1{%
    \expandafter\advance\csname yyvsa<count>\endcsname-\yylen
    \expandafter\advance\csname yyvsa<count>\endcsname#1\relax
    %
    \expandafter\expandafter\expandafter
        \p@@yyassignment
        \expandafter\expandafter\expandafter{%
            \csname yyvsa[\expandafter\the
                \csname yyvsa<count>\endcsname]\parsernamespace\endcsname
        }{%
            \expandafter\advance\csname yyvsa<count>\endcsname-#1\relax
            \expandafter\advance\csname yyvsa<count>\endcsname\yylen
        }%
}

% \bb macro implementation

\def\p@twwassignment#1{%
    \expandafter\advance\csname yyvsa<count>\endcsname-#1\relax
    \expandafter\advance\csname yyvsa<count>\endcsname\@ne
    %
    \expandafter\expandafter\expandafter
        \p@@yyassignment
        \expandafter\expandafter\expandafter{%
            \csname yyvsa[\expandafter\the
                \csname yyvsa<count>\endcsname]\parsernamespace\endcsname
        }{%
            \expandafter\advance\csname yyvsa<count>\endcsname#1\relax
            \expandafter\advance\csname yyvsa<count>\endcsname\m@ne
        }%
}

\def\p@@yyassignment#1#2#3{%
    #2\yystringempty{#3}{#1}{#3{#1}}%
}

\def\putyyval#1{\edef\next{\yyval{#1}}\next}

% dysplaying stack contents (\yyparse specific)

\def\showstack#1{%
    \begingroup
        \let\stackcs\empty
        \count\z@\csname \expandafter\eatone\string#1<count>\endcsname
        \ifnum\count\z@<\z@
        \else
            \bloop
                \count\@ne\csname \expandafter\eatone\string#1[\the\count\z@]\parsernamespace\endcsname
                \count\tw@=\fgetelemof{yystos}\at{\count\@ne}%
                \edef\stackcs{\stackcs^^J \space\space\fgetelemof{yytname}\at{\count\tw@}}%
            \ifnum\count\z@>\z@
                \advance\count\z@\m@ne
            \repeat
        \fi
    \tokreturn{}{}{\def\noexpand\stackcs{\stackcs}}%
}

% saving token registers

\def\newtable@generic#1{%
    \expandafter\ifx\csname #1\endcsname\relax
        \toksa{\csname newtoks\endcsname}%
        \expandafter\the\expandafter\toksa\csname #1\endcsname
    \fi
    \csname #1\endcsname=%
}

\let\newtable\newtable@generic % TODO: right now this is done either for all tables or none of them

% macros to support `hardwired' optimized tables

\def\appendtoyy@generic#1{%
    \toksa{\or}%
    \expandafter\ifx\csname optopt[#1]\parsernamespace\endcsname\relax
        \expandafter\concat\csname#1\endcsname\toksb
        \expandafter\concat\csname#1\endcsname\toksa
    \else
        \expandafter\edef\csname #1\parsernamespace\the\tempca\endcsname{\the\toksb}%
    \fi
}

\def\appendtoyy@genericlast#1{%
    \expandafter\ifx\csname optopt[#1]\parsernamespace\endcsname\relax
        \expandafter\concat\csname#1\endcsname\toksb
    \else
        \expandafter\edef\csname #1\parsernamespace\the\tempca\endcsname{\the\toksb}%
    \fi
}

\def\beginoptimizednonnumeric#1{%
    \tempca\z@
    \def\appendtoyytname{\appendtoyy@generic{#1}}%
    \def\appendtoyytnamelast{\appendtoyy@genericlast{#1}}%
}

% list macros

\def\listx#1{% #1 is the list name
    \expandafter\ifx\csname #1[\the\csname#1<count>\endcsname]\endcsname\relax
        \csname#1<count>\endcsname\z@
        \yybreak\relax
    \else
        \yybreak{\csname #1[\the\csname#1<count>\endcsname]\endcsname
            \expandafter\advance\csname#1<count>\endcsname\@ne
            \listx{#1}}%
    \yycontinue
}

\def\newlist#1{%
    \toksa{\csname newcount\endcsname}%
    \expandafter\the\expandafter\toksa\csname#1<count>\endcsname
}

\def\initlistx#1{%
    \csname#1<count>\endcsname\z@
    \expandafter\let\csname #10\endcsname\relax
}

\def\resetlistx#1{%
    \csname#1<count>\endcsname\z@
}

\def\listxlastindex#1{%
    \the\csname#1<count>\endcsname
}

\def\addtolist#1#2{%
    \expandafter\def\csname#1[\the\csname#1<count>\endcsname]\endcsname{#2}%
    \expandafter\advance\csname#1<count>\endcsname\@ne
}

\def\addxtolist#1#2{%
    \expandafter\edef\csname#1[\the\csname#1<count>\endcsname]\endcsname{#2}%
    \expandafter\advance\csname#1<count>\endcsname\@ne
}

\def\finishlist#1{%
    \expandafter\advance\csname#1<count>\endcsname\@ne
    \expandafter\let\csname#1[\the\csname#1<count>\endcsname]\endcsname\relax
}
