% 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/>.
% the sequences in this file have the eventual goal of implementing macros that count and compare token sequences
% (as either parameters or contents of token registers) in expandable manner.
% currently, a new sequence is prepared first, consisting of `markers'
% that indicate whether the token in the coresponding position of the
% original sequence is a potential blank space or brace (closing or
% opening);
% the macros then make the determination about the last possible blank
% on whether this is a true space or not; the goal is to make this
% macro independent of the value of \escapechar
%
% as of now the implementation is far from refined although an expandable conting macro
% can be easily produced
\catcode`\@=11
\input ../../tex/yycommon.sty
\input ../../tex/yymisc.sty
%%%%%%%%%%%%% the material before this point should be included from appropriate files
% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence
\def\yypreparsetokensequenc@#1#2#3{%
\yystringempty{#2}{#1{#3}}{\yypreparsetokensequen@@{#1}{#2}{#3}}%
}
\def\yypreparsetokensequen@@#1#2#3{% remaining sequence is nonempty
\yystartsinbrace{#2}{\yydealwithbracedgroup{#1}{#2}{#3}}{\yypreparsetokensequ@n@@{#1}{#2}{#3}}%
}
\def\yydealwithbracedgroup#1#2#3{% the first token of the remaining sequence is a brace
\iffalse{\fi\yydealwithbracedgro@p#2}{#1}{#3}%
}
\def\yydealwithbracedgro@p#1{%
\yypreparsetokensequenc@{\yyrepackagesequence}{#1}{}%
}
% #1 -- parsed sequence
% this is a sequence to `propagate expansion' into the next parameter.
% the same can be achieved by packaging the whole sequence with a
% \csname ... \endcsname pair and using a simple \expandafter
\def\yyrepackagesequence#1{%
\yyrepackagesequenc@{}#1\end
}
% #1 -- `packaged' sequence (\expandafter\expandafter\expandafter ? ...)
% #2 -- the next category 12 character or \end
\def\yyrepackagesequenc@#1#2{%
\ifx#2\end
\yybreak{\yyrepackagesequ@nc@{#1\expandafter\expandafter\expandafter}}%
\else
\yybreak{\yyrepackagesequenc@{#1\expandafter\expandafter\expandafter#2}}%
\yycontinue
}
% #1 -- `packaged' sequence (\expandafter\expandafter\expandafter ? ...)
% this macro is followed by the remainder of the original sequence with a so far
% unmatched right brace, the `call stack' and the parsed sequence.
\def\yyrepackagesequ@nc@#1{%
\expandafter\expandafter\expandafter\yyrepackagesequ@nc@swap#1{\expandafter\eatone\string}%
}
% #1 -- parsed sequence without packaging
\def\yyrepackagesequ@nc@swap#1#{%
\yyrepackagesequ@nc@sw@p{#1}%
}
% #1 -- parsed `inner' sequence
% #2 -- remainder of the original sequence
% #3 -- `call stack'
% #4 -- parsed sequence so far
\def\yyrepackagesequ@nc@sw@p#1#2#3#4{%
\yypreparsetokensequenc@{#3}{#2}{#4[#1]}%
}
% `braced group' thread ends here
% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence
\def\yypreparsetokensequ@n@@#1#2#3{% the remaining group in #2 is nonempty and does not start with a brace
\yystartsinspace{#2}{\yyconsumetruespace{#1}{#2}{#3}}{\yypreparsetokenseq@@n@@{#1}{#2}{#3}}%
}
\def\yyconsumetruespace#1#2#3{%
\expandafter\yyconsumetruespac@swap\expandafter{\eatonespace#2}{#1}{#3.}%
}
\def\yyconsumetruespac@swap#1#2#3{%
\yypreparsetokensequenc@{#2}{#1}{#3}%
}
% `group starting with a true (character code 32, category code 10) space' thread ends here
% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence
\def\yypreparsetokenseq@@n@@#1#2#3{% a nonempty group, that does not start with a brace or a true space
\yymatchblankspace{#2}{\yyrescanblankspace{#2}{#1}{#3}}{\yypreparsetokens@q@@n@@{#1}{#2}{#3}}%
}
% #1 -- remaining sequence
% #2 -- `call stack'
% #3 -- `parsed' sequence
\def\yyrescanblankspace#1#2#3{%
\expandafter\expandafter\expandafter
\yyrescanblankspac@swap
\expandafter\expandafter\expandafter{\expandafter\yynormalizeblankspac@\meaning#1}{#2}{#3*}%
}
\def\yyrescanblankspac@swap#1#2#3{%
\yystartsinspace{#1}{%
\expandafter\yyrescanblankspac@sw@p\expandafter{\eatonespace#1}{#2}{#3}%
}{%
\expandafter\yyrescanblankspac@sw@p\expandafter{\eatone#1}{#2}{#3}%
}%
}
\def\yyrescanblankspac@sw@p#1#2#3{%
\yypreparsetokensequenc@{#2}{#1}{#3}%
}
% `group starting with a blank space' ends here
% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence
\def\yypreparsetokens@q@@n@@#1#2#3{% nonempty group starting with a non blank, non brace token
\expandafter\yypreparsetokens@q@@n@@swap\expandafter{\eatone#2}{#1}{#30}%
}
\def\yypreparsetokens@q@@n@@swap#1#2#3{%
\yypreparsetokensequenc@{#2}{#1}{#3}%
}
% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- `parsed' sequence
\def\yydebracesequence#1#2#3{%
\yybracesleft#3[\end{#1}{#2}%
}
% #1 -- tokens preceding the brace
% #2 -- tokens following the brace
% #3 -- `call stack'
% #4 -- remaining sequence
\def\yybracesleft#1[#2\end#3#4{%
\if9#29%
\yybreak{\yyfirstoftwo}%
\else
\yybreak{\yysecondoftwo}%
\yycontinue{#3{#4}}{\yydebracesequenc@swap{#4}{#3}{#1#2}}%
}
\def\yydebracesequenc@swap#1#2#3{%
\expandafter\expandafter\expandafter
\yydebracesequenc@sw@p
\expandafter\expandafter\expandafter{\yygrabprebrace#1}{#2}{#3}%
}
\def\yydebracesequenc@sw@p#1#2#3{%
\yydebracesequ@nc@sw@p#3\end{#1}{#2}%
}
\def\yydebracesequ@nc@sw@p#1[\end#2#3{%
\yydebracesequence{#3}{#2}{#1}%
}
\def\yygrabprebrace#1#{%
\yygrabprebrac@{#1}%
}
\def\yygrabprebrac@#1#2{#1#2}
% the `debracing group' ends here
% string comparison macros below are woefully inefficient and can be replaced by a much
% shorter and faster version; they are easy to analyze, though;
% #1 -- string of category code 12 or 10 characters
% #2 -- string of category code 12 or 10 characters
\def\yycomparesimplestrings#1#2{%
\yystringempty{#1}{%
\yystringempty{#2}{\yyfirstoftwo}{\yysecondoftwo}%
}{\yycomparesimplestrings@{#1}{#2}}%
}
\def\yycomparesimplestrings@#1#2{% the first string is nonempty
\yystringempty{#2}{\yysecondoftwo}{\yycomparesimplestrings@@{#1}{#2}}%
}
\def\yycomparesimplestrings@@#1#2{% both strings are nonempty
\yystartsinspace{#1}{%
\yystartsinspace{#2}{\yyabsorbfirstspace{#1}{#2}}{\yysecondoftwo}%
}{%
\yystartsinspace{#2}{\yysecondoftwo}{\yyabsorbfirstnonspace{#1}{#2}}%
}
}
\def\yyabsorbfirstspace#1#2{%
\expandafter\yyabsorbfirstspac@swap\expandafter{\eatonespace#1}{#2}%
}
\def\yyabsorbfirstspac@swap#1#2{%
\expandafter\yyabsorbfirst@swap\expandafter{\eatonespace#2}{#1}%
}
\def\yyabsorbfirstnonspace#1#2{%
\expandafter\yyabsorbfirstnonspac@swap\expandafter{\eatone#1}{#2}%
}
\def\yyabsorbfirstnonspac@swap#1#2{%
\expandafter\yyabsorbfirst@swap\expandafter{\eatone#2}{#1}%
}
\def\yyabsorbfirst@swap#1#2{%
\yycomparesimplestrings{#2}{#1}%
}
% `compare strings of category code 12' thread ends here
% #1 -- a balanced sequence of tokens
% #2 -- a `parsed version' of the same sequence
%
% #1 should be such that \eatone#1 does not lead to an immediate error
\def\yycomparetailsignatures#1#2{%
\yypreparsetokensequenc@{\yycomparesimplestrings{#2}}{#1}{}%
}
% #1 -- `call stack'
% #2 -- remaining sequence
% #3 -- remaining parsed sequence
% #4 -- resolved sequence
\def\yyresolvespaces#1#2#3#4{%
\yystringempty{#2}{#1{#4}}{\yyresolveoneblank#3\end{#2}{#4}{#1}}%
}
\def\yyresolveoneblank#1{%
\if#1*%
\expandafter\yyfirstoftwo
\else
\expandafter\yysecondoftwo
\fi
{\yyresolveonebl@nk}{%
\ifx#1.%
\expandafter\yyfirstoftwo
\else
\expandafter\yysecondoftwo
\fi
{\yyresolveonebl@nk}%
{\yyconsumebothnonblanks}%
}%
}
% #1 -- the remainder of the parsed sequence
% #2 -- remaining sequence
% #3 -- resolved sequence
\def\yyresolveonebl@nk#1\end#2#3{%
\expandafter\yycomparetailsignatures\expandafter{\eatone#2}{#1}%
{\expandafter\yyresolvespac@sswap\expandafter{\eatone#2}{#1}{#3+}}%
{\expandafter\yyresolveonebl@nklight\expandafter{\eatone#2}{#1}{#3-}}%
}
% #1 -- remaining sequence
% #2 -- remaining parsed sequence
% #3 -- resolved sequence
% #4 -- `call stack'
\def\yyresolvespac@sswap#1#2#3#4{%
\yyresolvespaces{#4}{#1}{#2}{#3}%
}
\def\yyconsumebothnonblanks#1\end#2#3{%
\expandafter\yyconsumebothnonblanksswap\expandafter{\eatone#2}{#1}{#30}%
}
\def\yyconsumebothnonblanksswap#1#2#3#4{%
\yyresolvespaces{#4}{#1}{#2}{#3}%
}
% #1 -- remaining sequence
% #2 -- the remainder of the parsed sequence
% #3 -- resolved sequence
\def\yyresolveonebl@nklight#1#2#3{%
\yyresolveonebl@nklighttest#2\end{#1}{#3}%
}
\def\yyresolveonebl@nklighttest#1#2\end#3#4{%
\yycomparetailsignatures{#3}{#2}%
{%
\if#1*%
\expandafter\yyfirstoftwo
\else
\expandafter\yysecondoftwo
\fi
{\yyresolvespac@sswap{#3}{#2}{#4+}}%
{\yyresolvespac@sswap{#3}{#2}{#40}}%
}%
{\yyresolveonebl@nklight{#3}{#2}{#4-}}%
}
% `space resolution code' ends here
% #1 -- remaining parsed sequence
% #2 -- analysed sequence
\def\yyanalysetokens@#1#2{%
\yystringempty{#1}{{#2}}%
{\yyanalysetok@ns@#1\end{#2}}%
}
\def\yyanalysetok@ns@#1#2\end{%
\ifx#1.%
\expandafter\yyfirstoftwo
\else
\expandafter\yysecondoftwo
\fi
{\yygrabablank{#2}}%
{%
\ifx#1[% not a space, an opening brace
\expandafter\yyfirstoftwo
\else
\expandafter\yysecondoftwo
\fi
{%
\yydisableobrace{#2}%
}{%
\ifx#1]% not a space, a closing brace
\expandafter\yyfirstoftwo
\else
\expandafter\yysecondoftwo
\fi
{%
\yydisablecbrace{#2}%
}{% neither space nor brace
\yygrabtokenraw{#2}%
}%
}%
}%
}
% #1 -- remaining parsed sequence
% #2 -- analysed sequence
% #3 -- next token
\def\yygrabtokenraw#1#2#3{%
\expandafter\yyanalysetokens@swap\expandafter{\meaning#3}{#1}{#2}%
}
\def\yyanalysetokens@swap#1#2#3{%
\yyanalysetokens@{#2}{#3t#1e}%
}
\def\yygrabablank#1#2 {%
\yyanalysetokens@{#1}{#2s0e}%
}
% #1 -- remaining parsed sequence
% #2 -- analysed sequence
\def\yydisablecbrace#1#2{%
\yydisablecbrac@{}#1\relax#2\end
}
\def\yydisablecbrac@#1#2{%
\ifx#2\end
\yybreak{\yydisablecbrac@@{#1\expandafter\expandafter\expandafter}}%
\else
\yybreak{\yydisablecbrac@{#1\expandafter\expandafter\expandafter#2}}%
\yycontinue
}
\def\yydisablecbrac@@#1{%
\expandafter\expandafter\expandafter
\yydisablecbrace@@@#1\end
\expandafter\expandafter\expandafter
{\iffalse}\fi\string
}
\def\yydisablecbrace@@@#1\relax#2\end#3{%
\yystartsinspace{#3}%
{\expandafter\yyanalysetok@nsswap\expandafter{\eatonespace#3}{#1}{#2c1e}}%
{\expandafter\yyanalysetok@nsswap\expandafter{\eatone#3}{#1}{#2c2e}}%
}
\def\yyanalysetok@nsswap#1#2#3{%
\iffalse{\fi\yyanalysetokens@{#2}{#3}#1}%
}
% #1 -- remaining parsed sequence
% #2 -- analysed sequence
\def\yydisableobrace#1#2{%
\yydisableobrac@{}#1\relax#2\end
}
\def\yydisableobrac@#1#2{%
\ifx#2\end
\yybreak{\yydisableobrac@@{#1\expandafter\expandafter\expandafter}}%
\else
\yybreak{\yydisableobrac@{#1\expandafter\expandafter\expandafter#2}}%
\yycontinue
}
\def\yydisableobrac@@#1{%
\expandafter\expandafter\expandafter
\yydisableobrace@@@#1\end
\expandafter\expandafter\expandafter
{\iffalse}\fi\string
}
\def\yydisableobrace@@@#1\relax#2\end#3{%
\yystartsinspace{#3}%
{\expandafter\yyanalysetok@nsswap\expandafter{\eatonespace#3}{#1}{#2o1e}}%
{\expandafter\yyanalysetok@nsswap\expandafter{\eatone#3}{#1}{#2o2e}}%
}
% \ifx1\expandafter\eatone\string\11% check if \escapechar is nontrivial
\uccode`\ =`\-
\uppercase{\def\dotspace{ }}
\toksa\expandafter\expandafter\expandafter{\expandafter\meaning\dotspace}
\toksb{-}
\toksc{#2}
\toksd\toksa
\yyreplacestring\toksb\in\toksa\with\toksc
\toksc{}
\yyreplacestring\toksb\in\toksd\with\toksc
\expandafter\def\expandafter\yymatchblankspac@\expandafter#\expandafter1\the\toksd{%
\yystringempty{#1}{\expandafter\yysecondofthree\expandafter{\string}}%
{\expandafter\yythirdofthree\expandafter{\string}}%
}
\edef\yymatchblankspace#1{% is it \catcode 10 token?
\noexpand\iffalse{\noexpand\fi
\noexpand\expandafter
\noexpand\yymatchblankspac@
\noexpand\meaning#1\the\toksd}%
}
% the idea behind the sequence below is that a leading character of category code 10
% is replaced either by a character of category code 10 and charachter code 32 or a character
% of category code 12 and character code other than 32
% note that while it is tempting to replace the definition below by something that ends in
% ... blank space #2{ ... with the hope of absorbing the result of \meaning in one step,
% this will not give the desired result in case of an active character,
% say, `~' that had been \let to the normal blank space
\expandafter\def\expandafter\yynormalizeblankspac@\expandafter#\expandafter1\the\toksd{}
%% test code begins here
\tracingmacros=3
\tracingonline=3
\catcode`\ =13\relax%
\def\actspace{ }%
\catcode`\ =10\relax%
\catcode`\.=13\relax%
\def\actdotspace{.}%
\catcode`\.=12\relax%
\edef\makefunkydotspace{\let\expandafter\noexpand\actdotspace= \dotspace}
\edef\makefunkyspace{\let\expandafter\noexpand\actspace= \space}
\makefunkyspace
\makefunkydotspace
\def\identity#1{#1}
%\def\tempseq{\space\dotspace\space\space\dotspace\expandafter\noexpand\actspace\expandafter\noexpand\actdotspace
% \end\noexpand\fi\noexpand\else\noexpand\iffalse}
%\def\tempseq{\space\dotspace e}
%\edef\mypars@{%
% \noexpand\yypreparsetokensequenc@{\noexpand\identity}%
% {\tempseq}%
% {}%
%}
%\edef\myparse{\mypars@}
%\toksa\expandafter{\myparse}
%\showthe\toksa
%\edef\myresolve{\noexpand\yyresolvespaces{\errmessage}%
% {\tempseq}%
% {\the\toksa}{}
%}
%\show\myresolve
%\myresolve
\catcode`\<=1
\catcode`\>=2
\uccode`\<=32
\uccode`\>=32
\uppercase{\edef\temptest{<> \space\space\dotspace\expandafter\noexpand\actspace\expandafter\noexpand\actdotspace{{} {{}{{ u o l k kk
\end\noexpand\fi\noexpand\else\noexpand\iffalse{}} }}}}}
%\uppercase{\edef\temptest{\dotspace E <>}}
\show\temptest
\def\displaypreparse#1{%
\expandafter\errmessage\expandafter{\romannumeral-1\yypreparsetokensequenc@{\yyanalysetokens@}{#1}{}{}#1}%
}
%\def\displaydebraceparse#1{%
% \yypreparsetokensequenc@{\yydebracesequence{\displayseq}{#1}}{#1}{}%
%}
%\def\displayseq#1{\toksa{#1}\showthe\toksa}
\expandafter\displaypreparse\expandafter{\temptest}
\errmessage{stop!}
%\expandafter\displaydebraceparse\expandafter{\temptest}
\end