6

Having been writing small lib for TikZ, I tried to use this macro in order to search for @. Code compiled, but the result was false, despite that I was sure it was in tested string. So, I decided to look into an implementation of this macro, but it breaks my brain by self-recursive usage of pgfutil@in@ and pgfutil@in@@. Could someone explain how that manages to work? And, furthermore, if it can be changed to make search for @ applicable?

% pgfutil@in@

\newif\ifpgfutil@in@

% Usage:
% \pgfutil@in@{one}{three two one}
% \ifpgfutil@in@
%   -> will be true!
% \else
% \fi
%
% \pgfutil@in@{,}{1234,456567}
% \ifpgfutil@in@
%   -> will be true!
% \else
% \fi
\def\pgfutil@in@#1#2{%
 \def\pgfutil@in@@##1#1##2##3\pgfutil@in@@{%
  \ifx\pgfutil@in@##2\pgfutil@in@false\else\pgfutil@in@true\fi}%
 \pgfutil@in@@#2#1\pgfutil@in@\pgfutil@in@@}

I expect that there must be some way to tweak it by means of catcode...

EDIT:

Usage example:

\edef \pgf@marshal{\noexpand \pgfutil@in@{@}{\tikz@temp}}%
\pgf@marshal%

EDIT 2:

I discovered that using \pgfutil@in@{@}{\tikz@temp} from document context with set \makeatletter I don't encounter any problems even with searching for @ directly.

Having investigated into how TikZ includes libraries, I found that it saves category code for @, sets it to 11, includes library, then restores the category code for @. So it's ok to define macroses with @ in the name.

But before executing \pgfutil@in@{@}{\tikz@temp} (now inside some macros in my library) I do:

\c@pgf@counta = \catcode`\@%
\showthe \c@pgf@counta%

and see that pdflatex outputs 12. Hmmm... I guess this is due to that macroses in my library are just read and saved for later use. They're executed inside the document environment, when the category code for @ is already 12. Made such a suggestion I tried to put \makeatletter just before the \begin{document} and it worked like a charm!

So, is there a way to somehow alter the category code for @ just in place where it should be 11 by the time of execution?

Another suggestion is that by the time the macros is read the category code for @ is 11, but when being executed, the expanded value of \tikz@temp contains @ of category code 12. So, they will never match, unless I do \makeatletter at the document context.

1 Answers1

7

The macros are pretty simple and use standard TeX argument delimiting. The fact that the same macro names are reused makes the code a little less clear, but does not alter the effect. To make life easier, we could write the same concept out using some additional markers:

\def\pgfutil@in@#1#2{%
  \def\pgfutil@in@@##1#1##2##3\pgfutil@stop{%
    \ifx\pgfutil@marker##2\pgfutil@in@false\else\pgfutil@in@true\fi
   }%
 \pgfutil@in@@#2#1\pgfutil@marker\pgfutil@stop
}
\def\pgfutil@marker{\pgfutil@marker}

The idea is then that the auxiliary function \pgfutil@in@@ is defined so that its argument has to contain #1, and will be stopped by \pgfutil@stop (\pgfutil@in@@ in the original).

\pgfutil@in@@ is then used so that it will always be followed by at least one #1, which will be immediately followed by a marker (\pgfutil@in@ in the original, \pgfutil@marker in my version). If #1 is not in #2, then ##1 will be #2, ##2 will be the marker and ##3 will be empty. The \ifx test will be TRUE, and the flag is set FALSE. On the other hand, if #1 is in #2, then ##1 will be the part of #2 before the first #1, ##2 will not be the marker and ##3 will tidy up. The \ifx will be FALSE and so the flag will be set TRUE.

This all relies on the category code of #1 matching the occurrence in #2. We are talking about @, and so it's possible to do something like

\makeatletter
\def\myfunction#1{%
  \pgfutil@in@{@}{#1}%
  ...
}
\makeatother

and to get the wrong result. That's because the hard-coded @ is a 'letter' while any in #1 will (probably) be 'other'. That can be avoided using a bit of \lowercase trickery.

\makeatletter
\begingroup
\lccode`\*=`\@\relax
\lowercase{%
  \endgroup
  \def\myfunction#1{%
    \pgfutil@in@{*}{#1}%
    ...
  } 
}
\makeatother

where the idea is that * acts as a substitute for @: it has category code 'other' but is turned into a @ by the \lowercase.


For the edited question, the correct approach would be

\begingroup
\lccode`\*=`\@\relax
\lowercase{%
  \endgroup
  \edef\pgf@marshal{\noexpand\pgfutil@in@{*}{\tikz@temp}}%
}
\pgf@marshal
Joseph Wright
  • 259,911
  • 34
  • 706
  • 1,036