% Copyright 2012-2024, 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 .
% fast(er) versions of stack access functions
\catcode`\@=11
\def\yyinitstack#1{% #1 is the name of the stack
\csname \expandafter\eatone\string#1\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 )
% 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 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\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{% this assumes that #1 is a control sequence
\expandafter\advance\csname \expandafter\eatone\string#1\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}%
}
\long\def\yypushg#1\on#2{% global push with expand: do not use this for anything other than index macros
\expandafter\global\romannumeral0\movestackpointer#2\by\@ne
\expandafter\expandafter\expandafter\xdef\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\endcsname-#2\relax
%
\expandafter\let\expandafter#3\csname \expandafter\eatone\string#1[\expandafter\the
\csname \expandafter\eatone\string#1\endcsname]\parsernamespace\endcsname
%
\expandafter\advance\csname \expandafter\eatone\string#1\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\endcsname-#2\relax
%
#3\csname \expandafter\eatone\string#1[\expandafter\the
\csname \expandafter\eatone\string#1\endcsname]\parsernamespace\endcsname\relax
%
\expandafter\advance\csname \expandafter\eatone\string#1\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}%
}
% macros to read the stack from top to bottom (i.e.~in `reverse' order)
\def\yyreadustack#1{% we do not compute anything unless the postprocessor
% detected an implicit rule
\ifnum\number\yycomputeilength{\number\yyn}>\z@
\yybreak{\expandafter\yyr@@@ust@@k\expandafter{\number\toptoks}{1}{#1}}%
\else
\yybreak{}%
\yycontinue
}
\def\yyr@@@ust@@k#1#2#3{%
\yyr@@dust@@k{#2}{#1}{#3}%
}
\def\yyreadust@ck#1#2#3{%
\expandafter\toksdef\csname$$$'#2\endcsname=#1\relax %
\expandafter\yypop\expandafter#3\expandafter\into\csname$$$'#2\endcsname%$
\ifnum\number\yycomputeilength{\number\yyn}>#2 %
\yybreak{\expandafter\yyr@@dust@@k\expandafter{\number\xincrement{#2}}{#1}{#3}}%
\else
\yybreak\relax
\yycontinue
}
\def\yyr@@dust@@k#1#2#3{%
\expandafter\yyreadust@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
}
\def\yypeekustack#1{%
\yyreadustack#1%
\movestackpointer#1\by\number\yycomputeilength{\number\yyn}\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{\yyvalx}%
\else
\yybreak{\p@tyyassignment{#1}}%
\yycontinue
}
\def\p@tyyassignment#1{%
\expandafter\advance\csname yyvsa\endcsname-\yylen
\expandafter\advance\csname yyvsa\endcsname#1\relax
%
\expandafter\expandafter\expandafter
\p@@yyassignment
\expandafter\expandafter\expandafter{%
\csname yyvsa[\expandafter\the
\csname yyvsa\endcsname]\parsernamespace\endcsname
}{%
\expandafter\advance\csname yyvsa\endcsname-#1\relax
\expandafter\advance\csname yyvsa\endcsname\yylen
}%
}
% new \bb macro implementation
\def\p@twwassignment#1{%
\expandafter\advance\csname yyvsa\endcsname-#1\relax
\expandafter\advance\csname yyvsa\endcsname\@ne
%
\expandafter\expandafter\expandafter
\p@@yyassignment
\expandafter\expandafter\expandafter{%
\csname yyvsa[\expandafter\the
\csname yyvsa\endcsname]\parsernamespace\endcsname
}{%
\expandafter\advance\csname yyvsa\endcsname#1\relax
\expandafter\advance\csname yyvsa\endcsname\m@ne
}%
}
\def\p@@yyassignment#1#2#3{%
#2\yystringempty{#3}{#1}{#3{#1}}%
}
% dysplaying stack contents (\yyparse specific)
\def\showstack#1{%
\begingroup
\let\stackcs\empty
\count\z@\csname \expandafter\eatone\string#1\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}}%
}