Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.

share|improve this question
    
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 '11 at 15:02
    
@Joseph: I edited my question in order to include example of usage. –  Dmitrii F. Volosnykh May 4 '11 at 15:25
    
@Dmitry: I've edited the answer to cover that case –  Joseph Wright May 4 '11 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 '11 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. –  Dmitrii F. Volosnykh May 4 '11 at 17:51

1 Answer 1

up vote 6 down vote accepted

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
share|improve this answer
    
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. –  Dmitrii F. Volosnykh May 4 '11 at 15:27
    
@Dmitry: My assumption was that you were using things in a document context, hence the guards. –  Joseph Wright May 4 '11 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 '11 at 15:37
    
please read EDIT 2 section of my question. Thanks for the current help and in advance. –  Dmitrii F. Volosnykh May 4 '11 at 17:09
    
Great answer, Joseph –  Matthew Leingang May 4 '11 at 17:10

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.