Created
September 12, 2018 10:19
-
-
Save jfbu/fd49cdec4267cf335cd770c0131d9ccc to your computer and use it in GitHub Desktop.
CheckWhetherBlank.tex
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
% Time-stamp: <12-09-2018 12:18:27 CEST> | |
% cf https://tex.stackexchange.com/a/449979/4686 | |
% Rules of the game: | |
% ------------------ | |
% 1. \CheckFoo{A}{B}{C} expands in two steps to either B or C | |
% depending on whether A (not expanded in any way) verifies Foo | |
% 2. No token is forbidden from A, which is most general allowed | |
% TeX macro argument. | |
% 3. No e-TeX extensions (\detokenize etc...), no requirement on | |
% \escapechar etc... | |
\catcode`@ 11 | |
\long\def\@firstofone#1{#1} | |
\long\def\@firstoftwo#1#2{#1} | |
\long\def\@secondoftwo#1#2{#2} | |
\long\def\@gobble#1{} | |
\long\def\@gobbletwo#1#2{} | |
\long\def\@gobblethree#1#2#3{} | |
\long\def\@gobblefive#1#2#3#4#5{} | |
\long\def\JFB@stop@firstoftwo#1#2{\z@#1} | |
\long\def\JFB@stop@secondoftwo#1#2{\z@#2} | |
\long\def\JFB@stop@firstofthree#1#2#3{\z@#1} | |
\long\def\JFB@stop@secondofthree#1#2#3{\z@#2} | |
\long\def\JFB@stop@thirdofthree#1#2#3{\z@#3} | |
\long\def\JFB@stop@thirdoffour#1#2#3#4{\z@#3} | |
%%%%%%%%%%%%%%%% | |
% \JFB@CheckWhetherBrace | |
% (branches according to whether #1 starts or not with explicit | |
% character token of catcode 1) | |
\long\def\JFB@CheckWhetherBrace#1{% | |
\romannumeral | |
\expandafter\@gobble\expandafter | |
{\expandafter{\string#1.}\expandafter\JFB@stop@thirdoffour\string}% | |
\JFB@stop@secondoftwo | |
}% | |
testing checkwhetherbrace | |
\JFB@CheckWhetherBrace{cccc{aaaa}bbbb}{WRONG}{OK} | |
\JFB@CheckWhetherBrace{{aaaa}bbbb}{OK}{WRONG} | |
%%%%%%%%%%%%%%%% | |
% \JFB@CheckWhetherNull | |
% (branches according to whether #1 is or not empty) | |
\long\def\JFB@CheckWhetherNull#1{% | |
\romannumeral | |
\expandafter\expandafter\expandafter\@gobbletwo | |
\expandafter\string\expandafter{\expandafter{% | |
\expandafter{\string#1}\expandafter\@gobble\string}% | |
\iffalse}\JFB@stop@thirdoffour\fi | |
\JFB@stop@secondoftwo | |
}% | |
testing checkwhethernull | |
\JFB@CheckWhetherNull{}{OK}{WRONG} | |
\JFB@CheckWhetherNull{{}}{WRONG}{OK} | |
\JFB@CheckWhetherNull{ }{WRONG}{OK} | |
\JFB@CheckWhetherNull{ {}}{WRONG}{OK} | |
\JFB@CheckWhetherNull{{} }{WRONG}{OK} | |
\JFB@CheckWhetherNull{{ }}{WRONG}{OK} | |
\JFB@CheckWhetherNull{a}{WRONG}{OK} | |
\JFB@CheckWhetherNull{\error}{WRONG}{OK} | |
%%%%%%%%%%%%%%%% | |
% \JFB@CheckWhetherLeadingSpace | |
% (branches according to whether #1 starts or not | |
% with character code 32 space token) | |
% as in the other macros we could simplify with "delimiter" tokens | |
% but this would limit generality as they would be forbidden | |
% from argument | |
\long\def\JFB@CheckWhetherLeadingSpace#1{% | |
\romannumeral | |
\expandafter\JFB@CheckWhetherLeadingSpace@a\string{{}#1. }% | |
}% | |
\long\def\JFB@CheckWhetherLeadingSpace@a#1#2 {% | |
\JFB@CheckWhetherNull{#2}% | |
{\expandafter\JFB@stop@secondofthree}% | |
{\expandafter\JFB@stop@thirdofthree}% | |
\expandafter{\string}% | |
} | |
testing checkwhetherleadingspace | |
\JFB@CheckWhetherLeadingSpace{}{WRONG}{OK} | |
\JFB@CheckWhetherLeadingSpace{{}}{WRONG}{OK} | |
\JFB@CheckWhetherLeadingSpace{ }{OK}{WRONG} | |
\JFB@CheckWhetherLeadingSpace{ {}}{OK}{WRONG} | |
\JFB@CheckWhetherLeadingSpace{{} }{WRONG}{OK} | |
\JFB@CheckWhetherLeadingSpace{{ }}{WRONG}{OK} | |
\JFB@CheckWhetherLeadingSpace{a}{WRONG}{OK} | |
\JFB@CheckWhetherLeadingSpace{\error}{WRONG}{OK} | |
\JFB@CheckWhetherLeadingSpace{ \error}{OK}{WRONG} | |
%%%%%%%%%%%%%%%% | |
% \JFB@CheckWhetherOnlySpaces | |
% (branches according to whether #1 consists only of space tokens, | |
% possibly none) | |
% NOTICE: etoolbox \ifblank is (non-expandable) analog of this | |
\long\def\JFB@CheckWhetherOnlySpaces#1{% | |
\romannumeral | |
\expandafter\JFB@CheckWhetherOnlySpaces@a\string{#1{}}% | |
}% | |
\long\def\JFB@CheckWhetherOnlySpaces@a#1#2{% | |
\JFB@CheckWhetherNull{#2}% | |
{\expandafter\JFB@CheckWhetherOnlySpaces@b}% | |
{\expandafter\JFB@stop@thirdofthree}% | |
\expandafter{\string}% | |
} | |
\long\def\JFB@CheckWhetherOnlySpaces@b#1{% | |
\expandafter\JFB@CheckWhetherNull\expandafter{\@gobble#1}% | |
\JFB@stop@firstoftwo\JFB@stop@secondoftwo | |
} | |
testing checkwhetheronlyspaces | |
\JFB@CheckWhetherOnlySpaces{}{OK}{WRONG} | |
\JFB@CheckWhetherOnlySpaces{{}}{WRONG}{OK} | |
\JFB@CheckWhetherOnlySpaces{ }{OK}{WRONG} | |
\JFB@CheckWhetherOnlySpaces{ {}}{WRONG}{OK} | |
\JFB@CheckWhetherOnlySpaces{{} }{WRONG}{OK} | |
\JFB@CheckWhetherOnlySpaces{{ }}{WRONG}{OK} | |
\JFB@CheckWhetherOnlySpaces{ { }}{WRONG}{OK} | |
\JFB@CheckWhetherOnlySpaces{a}{WRONG}{OK} | |
\JFB@CheckWhetherOnlySpaces{\error}{WRONG}{OK} | |
\JFB@CheckWhetherOnlySpaces{ \error}{WRONG}{OK} | |
\edef\foo{\noexpand\JFB@CheckWhetherOnlySpaces{\space\space\space}{OK}{WRONG}} | |
\foo | |
\edef\foo{\noexpand\JFB@CheckWhetherOnlySpaces{\space\space\space{}}{WRONG}{OK}} | |
\foo | |
%%%%%%%%%%%%%%%% | |
% \JFB@CheckWhetherBlank | |
% (branches according to whether #1 consists only of space tokens, | |
% intermixed with balanced (possibly nested of course) catcode1/2 | |
% pairs) | |
% example of "blank": " { { } } {} " | |
% (attention that a non-expanded \space token inside cause the | |
% argument not to be considered blank) | |
% NOTICE: etoolbox \ifblank is (non-expandable) analog of | |
% the CheckWhetherOnlySpaces, not of this CheckWhetherBlank | |
% This is implemented at some "high" level using OnlySpaces, Brace and | |
% LeadingSpace as helpers. It could possibly be worthwile to achieve a | |
% "lower" level approach, but this is not easy! Here the same #1 is tested | |
% possibly three times to start with: only spaces ? leading catcode-1 | |
% explicit character token ? leading space ?. | |
% This is the only recursively implemented macro in this file, and the | |
% main point is to avoid a fixed point of recursion causing infinite | |
% loop... | |
% To avoid nesting \romannumeral's a sub-macro is used, and it is | |
% simple iterated execution (not recursion on say two parts PartA and | |
% PartB of original argument #1, as I had been thinking of doing | |
% initially; but finally we avoid all complications of fetching such | |
% parts of a completely general argument which we can not delimite by | |
% some sacrificed token). | |
\def\JFB@CheckWhetherBlank{\romannumeral\JFB@checkwhetherblank}% | |
\long\def\JFB@checkwhetherblank#1{% | |
\JFB@CheckWhetherOnlySpaces{#1}% | |
\JFB@checkwhetherblank@stop@T | |
{}% | |
\JFB@CheckWhetherBrace{#1}% | |
{}% | |
{\JFB@CheckWhetherLeadingSpace{#1}% | |
{}% | |
\JFB@checkwhetherblank@stop@F | |
}% | |
\expandafter\JFB@checkwhetherblank\expandafter{\@firstofone#1}% | |
}% | |
\long\def\JFB@checkwhetherblank@stop@T#1% | |
\JFB@checkwhetherblank\expandafter#2#3#4{\z@#3} | |
\long\def\JFB@checkwhetherblank@stop@F#1% | |
\JFB@checkwhetherblank\expandafter#2#3#4{\z@#4} | |
testing checkwhetherblank | |
1 \JFB@CheckWhetherBlank{}{OK}{WRONG} | |
2 \JFB@CheckWhetherBlank{{}}{OK}{WRONG} | |
3 \JFB@CheckWhetherBlank{{ }}{OK}{WRONG} | |
4 \JFB@CheckWhetherBlank{{}{}{}}{OK}{WRONG} | |
5 \JFB@CheckWhetherBlank{{} { { } }{}}{OK}{WRONG} | |
6 \JFB@CheckWhetherBlank{ }{OK}{WRONG} | |
7 \JFB@CheckWhetherBlank{ {}}{OK}{WRONG} | |
8 \JFB@CheckWhetherBlank{{} }{OK}{WRONG} | |
9 \JFB@CheckWhetherBlank{{ }}{OK}{WRONG} | |
10 \JFB@CheckWhetherBlank{ { }}{OK}{WRONG} | |
11 \JFB@CheckWhetherBlank{a}{WRONG}{OK} | |
12 \JFB@CheckWhetherBlank{\error}{WRONG}{OK} | |
13 \JFB@CheckWhetherBlank{ \error}{WRONG}{OK} | |
\edef\foo{\noexpand\JFB@CheckWhetherBlank{\space\space\space}{OK}{WRONG}} | |
14 \foo | |
\edef\foo{\noexpand\JFB@CheckWhetherBlank{\space\space\space{\space\space\space{\space\space\space}\space\space\space}\space\space\space}{OK}{WRONG}} | |
15 \foo | |
%" {{ { } } {} } " | |
\edef\foo{\space\space\space{{\space\space{\space\space}\space\space}\space\space{}\space}\space\space} | |
%\tracingmacros1 | |
16 \expandafter\JFB@CheckWhetherBlank\expandafter{\foo}{OK}{WRONG} | |
\bye | |
% Annex | |
% some tests showing inserting an opening brace via \iftrue technique | |
% is faster than using \gobble\string technique | |
% 0.08215pt | |
% macro:->\ZAZA {\relax } | |
% 0.09035pt | |
% macro:->\ZAZA {\relax } | |
% 0.07764pt | |
% macro:->\ZAZA {\relax } | |
% 0.09068pt | |
% macro:->\ZAZA {\relax } | |
% \tt | |
% \input xintkernel.sty | |
% \def\@firstofone#1{#1} | |
% \def\ZAZA{\noexpand\ZAZA} | |
% \def\foo#1{% | |
% \expandafter\expandafter\expandafter\ZAZA | |
% \iftrue\expandafter{\else}\fi} | |
% \pdfresettimer | |
% \romannumeral\xintreplicate{100000}{\edef\bar{% | |
% \expandafter\foo\string{\relax}}}% | |
% \the\dimexpr\pdfelapsedtime sp\relax | |
% \meaning\bar | |
% \def\foo#1{% | |
% \expandafter\expandafter\expandafter\ZAZA | |
% \expandafter\expandafter\expandafter{\expandafter\@gobble\string}} | |
% \pdfresettimer | |
% \romannumeral\xintreplicate{100000}{\edef\bar{% | |
% \expandafter\foo\string{\relax}}}% | |
% \the\dimexpr\pdfelapsedtime sp\relax | |
% \meaning\bar | |
% \def\foo#1{% | |
% \expandafter\expandafter\expandafter\ZAZA | |
% \iftrue\expandafter{\else}\fi} | |
% \pdfresettimer | |
% \romannumeral\xintreplicate{100000}{\edef\bar{% | |
% \expandafter\foo\string{\relax}}}% | |
% \the\dimexpr\pdfelapsedtime sp\relax | |
% \meaning\bar | |
% \def\foo#1{% | |
% \expandafter\expandafter\expandafter\ZAZA | |
% \expandafter\expandafter\expandafter{\expandafter\@gobble\string}} | |
% \pdfresettimer | |
% \romannumeral\xintreplicate{100000}{\edef\bar{% | |
% \expandafter\foo\string{\relax}}}% | |
% \the\dimexpr\pdfelapsedtime sp\relax | |
% \meaning\bar |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment