10

When I tried to compile

\begingroup
\def\iden#1{#1}
\catcode`\,=13
\catcode`\==13
\iden{\endgroup
  \pgfkeys{/my family/.is family}
  \pgfkeys{/my family/my keys/.cd,
    keya/.code=\def\mywidth{##1},
    keyb/.code=\def\mydefault{##1}%
  }%
}

I got the error

{\edef}
\pgfkeyscurrentkey ->/my family/my keys/.cd, keya/.code
{undefined}
! Undefined control sequence.
\pgfkeyscurrentkey ->/my family/my keys/.cd,

Please how can I get over this? pgfkeys' Chief Executive (to use Knuth's term) doesn't seem to be able to inherently handle active parsers.

Ahmed Musa
  • 11,742
  • It can't deal also with active =. Try with \usepackage[turkish]{babel}. – egreg Apr 09 '12 at 22:54
  • @egreg: You're right. Active = is even a bigger problem; see http://www.latex-project.org/cgi-bin/ltxbugs2html?pr=babel/3523. Also, \pgfkeys{/my family/my keys/keya={x}} strips the braces around x. Furthermore, I find that its leading and trailing space trimmer (\pgfkeys@spdef) is an exact replica of keyval package's \KV@@sp@def, thereby inheriting the known deficiencies of \KV@@sp@def. In order to be usable for general purposes, pgfkeys needs to be more robust. Or there supplements (ie, add-ons) that attend to the need for more resilience? – Ahmed Musa Apr 10 '12 at 02:12

2 Answers2

9

Original Answer

In general, as long as delimited argument matching is used to parse the value list, redefining catcodes of list syntax characters will be a problem (same for LaTeX lists).

One could redefine \pgfkeys@@set to re-catcode , and = like this:

\makeatletter
\long\def\pgfkeys@@set#1#2{%
  \let\pgfkeysdefaultpath\pgfkeys@root%
  {\catcode`\,=12\catcode`\==12
  \scantokens{\def\mytmp{#2}}%
  \expandafter
  }%
  \expandafter\pgfkeys@parse\mytmp,\pgfkeys@mainstop%
  \def\pgfkeysdefaultpath{#1}}
\makeatother

I'm not getting much of a test result as your MWE doesn't produce any output, but at least I'm getting no errors ;-)

Don't know whether this was the kind of general robust solution you're looking for though...

Edit

Please see the comments below on why the above is not a good idea. \scantokens will de-activate all instances of , and = in its argument, also in the values of keys where they need to be active to fulfill the purpose they were originally made active for.

Following the comment of Joseph Wright below, here is a "solution" referring to the kvsetkeys package (Which was, obviously, an enormous tedium to make. Kudos to Heiko for this achievement!):

\usepackage{kvsetkeys}
\usepackage{tikz}

\makeatletter
\long\def\pgfkeys@@set#1#2{%
  \let\pgfkeysdefaultpath\pgfkeys@root%
  \kv@normalize{#2}%
  \expandafter\pgfkeys@parse\kv@list,\pgfkeys@mainstop%
  \def\pgfkeysdefaultpath{#1}}
\makeatother

Again, I'm getting no errors :-)

I'd be interested to hear what the drawbacks of this approach are.

  • 3
    I would avoid \scantokens in general: you don't know what other changes may apply. I'd prefer the approach in kvsetkeys where a search-and-replace alters only the catcode of 'delimiting' = and ,, leaving for example tokens 'hidden' within braces unchanged. (We use the same approach in l3keys for LaTeX3.) – Joseph Wright Apr 10 '12 at 16:46
  • @JosephWright Thanks for the comment. I'll try to update my answer maybe this evening. – Stephan Lehmke Apr 10 '12 at 16:52
  • @AhmedMusa It seems you know much more about these matters than I do. Should I delete my answer? – Stephan Lehmke Apr 10 '12 at 18:08
  • As Joseph has noted, \scantokens is not recommended here since, even if the token list in question was scannable, rescanning will introduce unintended changes to the token list. If, for instance, the user wanted genuinely active = in the token list (which he might do, for example, by hiding such active = in brace groups), rescanning will infringe that requirement and some other things. Joseph suggests list normalization by Heiko’s method. That is what ltxkeys too adopted. – Ahmed Musa Apr 10 '12 at 18:10
  • There is also the relatively more expensive scheme of selective sanitization (SS) by Uwe Kern, as used in xkeyval package. A limitation of SS concerning \bgroup token was addressed by ltxtools-base package. – Ahmed Musa Apr 10 '12 at 18:11
  • @Stephan Lehmke: No, please keep your answer. If you remove it our comments will have to go. I think this issue is important for pgfkeys since, because of tikz, pgfkeys has emerged as the most favoured key-value parser. There might be a need for xpgfkeys, which would provide additional functionality. – Ahmed Musa Apr 10 '12 at 18:19
6

EDIT (2012/04/21)

I needed to correct and extend some lines in the package, but the site's limit of 30000 characters was exceeded. So I have moved the package to CTAN as pgfkeyx.

Examples

\documentclass{article}
\usepackage{pgfkeys}
\usepackage{pgfkeyx}
\makeatletter

\begingroup
\def\iden#1{#1}
\catcode`\/=13
\catcode`\,=13
\catcode`\==13
\iden{\endgroup
  \pgfkeys{%
    % The spaces here are just examples of how they are removed by
    % normalization:
    / my family / .is family,
    /my family/my keys/.cd,
    keya / .code = \def\keyavalue{#1},
    keya/.default = {keya-default},
    keyb / .code = \def\myvalueb{#1},
    keyb /.default=keyb-default,
    .normal code=\def\x#1{#1},
    keyc/.code=\pgfqkeysalso\pgfkeyscurrentpath{#1},
    keyc/.value required,
    keyd/.is if with 2 callbacks =
      {true}{\def\x##1{##1-True-#1}}{\def\x##1{##1-False-#1}},
  }%
}

\pgfkeys{%
  /my family/my keys/.cd,
  keya={x},
  keyb=aa,
  keyc = {keya=aa, keyb=bb},
}

\pgfkeys{%
  /my family/my keys/.cd,
  keya/.restore default,
  .restore key defaults={keya,keyb}
}

\makeatother

\begin{document}
\pgfkeys{%
  /my family/my keys/keyd=true,
}
\ifkeyd
`keyd' is true.
\fi
%\show\x
\end{document}
Ahmed Musa
  • 11,742
  • I'm really interested by your solution :) – cjorssen Apr 11 '12 at 11:23
  • Apart from the question what the tikz team would prefer, I don't think anything is keeping you from making a package which patches pgfkeys and which could then be recommended here to users getting issues with active = or ,. – Stephan Lehmke Apr 13 '12 at 20:37
  • @StephanLehmke: Here you go. I have edited my answer to include a rather incomplete patch. See comments within the code. I have included a provision for resetting keys to their default values, something Ben User wanted some weeks ago. – Ahmed Musa Apr 13 '12 at 22:25
  • Wow, that's an impressive patch! I hope it will come in handy. – Stephan Lehmke Apr 13 '12 at 22:40
  • Ahmed Musa, @cjorssen is from the TikZ\PGF team :-). I think this is indeed a candidate for a contribution for the CVS version. – percusse Apr 13 '12 at 23:38
  • 2
    @AhmedMusa Thanks for this patch. I added a link in the bug tracker to your answer (this has been an open bug since 2008!). We need to remove all etex features to include it in pgf code since it is the policy of pgf/tikz for now (this might change in the future). – cjorssen Apr 14 '12 at 10:43
  • 2
    @cjorssen: OK. Before I used eTeX features for the patch I confirmed from Joseph Wright that eTeX is now available on more than 99% of all TeX distributions. The earlier that policy changes the better for contributors to tikz. Life without eTeX is awkward. – Ahmed Musa Apr 14 '12 at 18:04
  • @AhmedMusa the patch looks promising. Just one question in a direction which you mentioned in some other comment already: pgfkeys requires time. I am wondering if this search-and-replace-scheme has significant additional performance impact? I know that pgfkeys is used heavily in tikz and all its contributions. – Christian Feuersänger Apr 21 '12 at 21:44
  • @AhmedMusa: Please have a look at this question: http://meta.tex.stackexchange.com/questions/1721/which-questions-led-to-the-creation-of-a-new-package and http://meta.tex.stackexchange.com/questions/1181/who-are-the-package-maintainers-here – Marco Daniel Apr 22 '12 at 10:36
  • @ChristianFeuersänger: You have a case, but please remember that (1) the leading and trailing (L/T) space trimmer \pgfkeys@spdef of pgfkeys package also consumes time and it is called at least twice by the package for each key-value pair, whether or not L/T spaces actually exist. Normalization trims L/T spaces around blocks of key-value pairs instead of around each pair, and hence it is cheaper. – Ahmed Musa Apr 22 '12 at 10:42
  • (2) the need for \pgfkeys@spdef is completely removed by normalization and normalization does nothing when no L/T spaces exist; (3) when no active parsers (/,=) are present in the key-value pairs, normalization is actually fast: it stops searching as soon as it can't find any active parser, and it doesn't replace what it hasn't found. I am still looking into the normalization scheme - perhaps an optimization of the replacement scheme is possible. – Ahmed Musa Apr 22 '12 at 10:42
  • @AhmedMusa sounds good to me, thanks for the details. – Christian Feuersänger Apr 22 '12 at 14:39