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

% Simple integer parsing macros to display various styles of integers
% used in ld scripts. Instead of creating a dedicated parser for this
% task, it was decided to implement it as a set of expandable
% macros. The degree of complexity of both implementations would
% probably be about the same, and a true parser approach will be more
% flexible. The choice was made in favor of a macro implementation to
% ensure expandability and speed.

\def\ldintiterator#1#2#3#4#5#6#7#8{%
                    % #1 a control sequence to insert at the end
                    % #2 first divisor
                    % #3 second divisor
                    % #4 first remainder
                    % #5 second remainder
                    % #6 read digits
                    % #7 last read digit
                    % #8 current digit
    \ifcat\noexpand#8\relax
        \yybreak{#1{#2}{#3}{#4}{#5}{#6}{#7}}%
    \else
        \yybreak{\expandafter\lditeratormodone\expandafter{\number\incrementmod{#4}{#2}}{#5}{#3}{#1}{#2}{#3}{#6#7}{#8}}%
    \yycontinue
}

\def\incrementmod#1#2{%
    \expandafter\incr@mentmod\expandafter{\number\xincrement{#1}}{#2}%
}

\def\incr@mentmod#1#2{%
    \ifnum#1=#2
        \yybreak{0}%
    \else
        \yybreak{#1}%
    \yycontinue
}

\def\decrementmod#1#2{%
    \ifnum#1=\z@
        \yybreak{\xdecrement{#2}}%
    \else
        \yybreak{\xdecrement{#1}}%
    \yycontinue
}

\def\lditeratormodone#1#2#3{%
    \expandafter\lditeratormodtwo\expandafter{\number\incrementmod{#2}{#3}}{#1}%
}

\def\lditeratormodtwo#1#2#3#4#5{%
    \ldintiterator{#3}{#4}{#5}{#2}{#1}%
}

\def\ldintegerspacingmodone#1#2#3#4#5#6#7#8#9{%
                                          % #1 control sequence to insert at the end
                                          % #2 first divisor
                                          % #3 second divisor
                                          % #4 number of digits mod #2
                                          % #5 number of digits mod #3
                                          % #6 digits read
                                          % #7 separator
                                          % #8 last digit
                                          % #9 current digit
    \ifcat\noexpand#9\relax
        \yybreak{#1{#2}{#3}{#4}{#5}{#6}{#8}}%
    \else
        \yybreak{\ldintegerspacingmodon@{#1}{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}}%
    \yycontinue
}

\def\ldintegerspacingmodon@#1#2#3#4#5#6#7#8#9{%
    \ifnum#4=\z@
        \yybreak{%
            \yystringempty{#8}{%
                \expandafter\ldintegerspacingmod@n@\expandafter{\number\decrementmod{#4}{#2}}{#1}{#2}{#3}{#5}{}{#7}{#9}%
            }{%
                \expandafter\ldintegerspacingmod@n@\expandafter{\number\decrementmod{#4}{#2}}{#1}{#2}{#3}{#5}{#6#8#7}{#7}{#9}%                
            }%
        }%
    \else
        \yybreak{%
            \expandafter\ldintegerspacingmod@n@\expandafter{\number\decrementmod{#4}{#2}}{#1}{#2}{#3}{#5}{#6#8}{#7}{#9}%
        }%
    \yycontinue
}

\def\ldintegerspacingmod@n@#1#2#3#4#5#6#7#8{%
    \ldintegerspacingmodone{#2}{#3}{#4}{#1}{#5}{#6}{#7}{#8}%
}

\def\showiteratorresults#1#2#3#4#5#6{%
    \errmessage{mod #1: #3, mod #2: #4, digits: #5, last digit: #6}%
}

\def\showseparatorresults#1#2#3#4#5#6#7{%
    \errmessage{digits after separation: \yystringempty{#1}{}{[#1]}#6.#7}%
}

\def\ldseparatemodone#1#2#3#4#5#6#7{%
    \expandafter\yystringempty\expandafter{\romannumeral-"0#7}{%
        \ldintegerspacingmodone{#1{}}{#2}{#3}{#4}{#5}{}{\ldintsep}{}#6#7\end
    }{%
        \ldseparatewithsuffix{#1}{#2}{#3}{#4}{#5}{#6}{#7}%
    }%
}

\def\ldseparatewithsuffix#1#2#3#4#5#6#7{%
    \ifcase\csname ldsuffix#7\endcsname\space
        \expandafter\ldseparatewiths@ffix\expandafter{\number\decrementmod{#4}{#2}}{#1}{#2}{#3}{#5}{#6}{#7}%
    \or
        \expandafter\ldseparatewiths@ffix\expandafter{\number\decrementmod{#5}{#3}}{#1}{#3}{#2}{#4}{#6}{#7}%
    \else
    \fi
}

\def\ldseparatewiths@ffix#1#2#3#4#5#6#7{%
    \ldintegerspacingmodone{#2{#7}}{#3}{#4}{#1}{#5}{}{\ldintsep}{}#6\end
}

\def\ldsuffixD{0}
\def\ldsuffixO{0}
\def\ldsuffixB{1}
\def\ldsuffixH{1}
\def\ldsuffixX{1}
%
\def\ldradixD{}
\def\ldradixO{8}
\def\ldradixB{01}
\def\ldradixH{16}
\def\ldradixX{16}
%
\def\ldsuffixK{0}
\def\ldsuffixM{0}
\def\ldsuffixG{0}

\let\ldintsep\relax

\def\intprefix#1{% analyzing the prefix of an integer
    \intpr@fix#1..\end
}

\def\intpr@fix#1#2#3\end{%
    \if#10%
        \if#2X% prefix 0X
            1% hex number
        \else
            \if#2.%
                4% zero
            \else
                3% octal number
            \fi
        \fi
    \else
        \if#1$%
            2% hex number
        \else
            0% decimal number (no prefix)
        \fi
    \fi
}

\def\ldsciinteger#1{%
    \ifcase\intprefix{#1}%
        % decimal number (no prefix)
        \lddecsplitws{#1}{}%
    \or % hex number (0X)
        \expandafter\ldhexsplitws\expandafter{\eattwo#1}{16}%
    \or % hex number ($)
        \expandafter\ldhexsplitws\expandafter{\eatone#1}{16}%
    \or % octal number
        \lddecsplitws{#1}{8}%
    \or % zero
        0%
    \else
    \fi
}

\def\ldhexsplitws#1#2{%
    \ldintiterator{\ldseparatemodone{\displayinteger{#2}}}{4}{4}{0}{0}{}{}#1\end
}

\def\lddecsplitws#1#2{%
    \ldintiterator{\ldseparatemodone{\displayinteger{#2}}}{3}{3}{0}{0}{}{}#1\end
}

\def\ldbasedinteger#1{%
    \ldintiterator{\ldseparatewithsuffix{\displayintegerws}}{3}{4}{0}{0}{}{}#1\end
}

\def\displayinteger#1#2#3#4#5#6#7#8{%
    {\def\ldintsep{$\,$}\hbox{\ldintfont#7#8\ldrlap{${}_{#1}$}$\yystringempty{#2}{}{\lddisplayintsuffix{#2}}$}}%
}

\let\ldrlap\rlap
\let\ldintfont\rm

\def\lddisplayintsuffix#1{%
    \expandafter\ifx\csname ldspecialsuffixdisplay#1\endcsname\relax
        \,\hbox{\tt#1}%
    \else
        \csname ldspecialsuffixdisplay#1\endcsname
    \fi
}

\def\ldspecialsuffixdisplayK{{}\cdot2^{10}}

%\def\ldspecialsuffixdisplayK{\,\hbox{\rm Kb}}

\def\displayintegerws#1#2#3#4#5#6#7{%
    {\def\ldintsep{$\,$}\hbox{\rm#6#7\rlap{${}_{\yystringempty{#1}{}{\csname ldradix#1\endcsname}}$}}}%
}

% typeseting examples in text

\def\beginldprod{%
%    \par
    \begingroup
        \b@ginldprod
}

\long\def\b@ginldprod#1\endprod{%
        \setldproduction{#1}%
    \endgroup
%    \par
}

\long\def\setldproduction#1{%
    \def\termidxrank{5}%
    \def\headeridxrank{4}%
    \def\defidxrank{3}%
    \def\texcsidxrank{5}%
    \ninepoint
    \let\returnexplicitspace\splitexplicitspace
    \let\acharswitch\texcharadjust
    \let\onecharswitch\texcsadjust
    \let\yyinputgroup\yyinputldgroup
    \expandafter\hidecs\expandafter{\ldunion} % inhibit expansion so that fewer \noexpand are necessary
    \toldparser
    \ldparserinit
    \yyparse#1\yyeof\yyeof\endparseinput\endparse
    \ifyyparsefail % revert to generic macros if parsing failed
        \yybreak{\toks0{#1}\errmessage{failed to parse: \the\toks0}}%
    \else % Stage three, process the parsed table
        \yybreak{%
            {%
                \restorecslist{ld-parser:restash}\ldunion % extract the stash and mark lhs of assignments
                \setprodtable % use \bison's parser typesetting definitions
                \the\ldcmds
                \restorecslist{ld-display}\ldunion
                \setprodtable % use \bison's parser typesetting definitions 
                \restorecs{ld-display}{\anint\bint\hexint} % ... except for integer typesetting
                \the\ldcmds
                \the\lddisplay
             }%
        }%
    \yycontinue
}

% to make it possible to write {...} without changes

\def\yyinputldgroup#1{%
    \yyinput\{#1\}%
}