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.

6
  • I think an example of how you used it might be handy: I suspect you got the catcode of @ wrong somewhere.
    – Joseph Wright
    May 4, 2011 at 15:02
  • @Joseph: I edited my question in order to include example of usage. May 4, 2011 at 15:25
  • @Dmitry: I've edited the answer to cover that case
    – Joseph Wright
    May 4, 2011 at 15:36
  • @Dmitry: The issue of category codes for @ really deserves a separate question. I suggest you post one, linking to this question for context. (We like to keep each question focussed, where possible.)
    – Joseph Wright
    May 4, 2011 at 17:45
  • @Joseph: I've just started to compose one :) Also, there will be the minimalistic example, so that you're able to see what's happening. May 4, 2011 at 17:51

1 Answer 1

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
4
  • at the top of pgfutil-common.tex there's statement \catcode`\@=11\relax. And then there's a plenty of macroses, which contain @ in the name. So, it looks unnecessary to \makeatletter. May 4, 2011 at 15:27
  • @Dmitry: My assumption was that you were using things in a document context, hence the guards.
    – Joseph Wright
    May 4, 2011 at 15:33
  • I should add that the general point is the same: if @ is a letter, you need to take care when searching for @ as an 'other' character.
    – Joseph Wright
    May 4, 2011 at 15:37
  • please read EDIT 2 section of my question. Thanks for the current help and in advance. May 4, 2011 at 17:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .