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