Skip to content

Instantly share code, notes, and snippets.

@jfbu
Created September 12, 2018 10:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jfbu/fd49cdec4267cf335cd770c0131d9ccc to your computer and use it in GitHub Desktop.
Save jfbu/fd49cdec4267cf335cd770c0131d9ccc to your computer and use it in GitHub Desktop.
CheckWhetherBlank.tex
% 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