# String parsing macro fails within custom cite command of biblatex?

I am trying to use the same string parsing macro as in String parsing macro fails within a minipage environment?; but this time in context of biblatex - and I'm having similar problems, I guess.

Here is an MWE, partially borrowing code from Biblatex: ifx inside DeclareFieldFormat:

\documentclass{article}

\def\parseMyNumHelper num0#1\relax{\edef\MyNum{#1}}
\def\parseMyNum#1{\edef\temp{#1}%
\expandafter\parseMyNumHelper\temp\relax}

\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@misc{num01,
title = {Title1},
eprint = {MR001},
eprinttype = {mrnumber}}
@misc{num02,
title = {Title},
eprint = {002},
eprinttype = {mrnumber}}
@misc{num03,
title = {Title},
eprint = {MR003 (aa)},
eprinttype = {mrnumber}}
@misc{num04,
title = {Title},
eprint = {004 (bb)},
eprinttype = {mrnumber}}
\end{filecontents}
\usepackage{biblatex}

\DeclareFieldFormat{myentrykey}{%
\parseMyNum{#1} %
\textbf{Testing \MyNum: \textit{#1}}%
}

\makeatletter
\newbibmacro*{myentrycite}{% custom
\printfield[myentrykey]{entrykey}
} % end \newbibmacro*{
\makeatother

\DeclareCiteCommand{\myentrycite}
{\usebibmacro{prenote}}
{\usebibmacro{citeindex}%
\printtext{\usebibmacro{myentrycite}}}
{}
{\usebibmacro{postnote}}

\begin{document}

\def\testvar{num01}
\typeout{ a== \testvar, }
\typeout{ -- \meaning\parseMyNumHelper}
\typeout{ -- \meaning\parseMyNum}
\parseMyNum{\testvar}
\typeout{ b== \testvar, - \temp, - \MyNum}

\myentrycite{num03}

\end{document}


Here, as a result of the cite command, I'd like to print the last numeral of the citation entry key.

If you run this code once with pdflatex, things look reasonable:

 a== num01,
-- macro:num0#1\relax ->\edef \MyNum {#1}
-- macro:#1->\edef \temp {#1}\expandafter \parseMyNumHelper \temp \relax
b== num01, - num01, - 1


However, if you then run bibtex, and pdflatex again, at this second run you get:

 a== num01,
-- macro:num0#1\relax ->\edef \MyNum {#1}
-- macro:#1->\edef \temp {#1}\expandafter \parseMyNumHelper \temp \relax
b== num01, - num01, - 1
! Use of \parseMyNumHelper doesn't match its definition.
\temp ->n
um03
l.59   \myentrycite{num03}

? i
insert>\typeout{ \meaning\parseMyNumHelper }
macro:num0#1\relax ->\edef \MyNum {#1}
...


Here is the strange thing - I expect num03 to arrive to the macro, and it does; and yet, macro still crashes on it? And even more strange - if we continue to the end, the following output is generated:

... and what I can see is that the parsing "almost" succeeded (I get "Testing X" as I expected) - except the last validly set number is returned....

Well, in String parsing macro/minipage... , the problem was minipage not having the correct argument, and therefore interfering with the next of the \parse commands - in Biblatex: ifx inside DeclareFieldFormat, it is mentioned:

This is happening because you are using the \ifx test, which will only be true if the category codes of M and R are 'letter'. biblatex is passing the argument as a detokenized string, so the test fails.

... and I'm not really sure if that is related to this..

But in any case, I'd appreciate a lot if anyone can explain why does this "string parsing" macro crash here - and how can I get it to work as intended..

Cheers!

-

The problem is that the string you want to pass to \parseMyNumHelper consists of tokens of category code 12. Thus you have to use category code 12 letters as delimiters; the easiest way is

\expandafter\def\expandafter\parseMyNumHelper \detokenize{num}0#1\relax{\def\MyNum{#1}}


With \detokenize we change the category code of the characters num to 12.

There are other ways:

\begingroup\lccode!=n \lccode?=u \lccode/=m
\lowercase{\endgroup\def\parseMyNumHelper !?/0#1\relax}{\def\MyNum{#1}}


Or, with my regexpatch package,

\usepackage{regexpatch}
\def\parseMyNumHelper num0#1\relax{\def\MyNum{#1}}
\xpatchparametertext{\parseMyNumHelper}{num}{\cO n \cO u \cO m}{}{}


Of course, your \testvar test won't work, unless you change it to

\edef\testvar{\detokenize{num01}}
\typeout{ a== \testvar, }
\typeout{ -- \meaning\parseMyNumHelper}
\typeout{ -- \meaning\parseMyNum}
\parseMyNum{\testvar}
\typeout{ b== \testvar, - \temp, - \MyNum}


## Easier way

You want to remove four tokens:

\def\parseMyNumHelper #1#2#3#4#5\relax{\def\MyNum{#5}}


No category code problem.

-
Fantastic - many thanks for that explanation @egreg! Incidentally, after messing around with this problem, I also came to the conclusion that I should use #1#2#3#4#5, which seems to work fine (posted below). Your answer helps me remember why I just can't call "string.split()" here, as I am sometimes used to :) Many thanks again - cheers! –  sdaau May 23 '12 at 18:37
Just a note re: \detokenize: I tried using it in many cases, and I think I failed; possibly because I don't really understand it: this: "\def\aa{test}\typeout{ = \aa, \detokenize{\aa} = }" produces "= test, \aa ="; I would have thought instead, that the \aa would have been either \ a a or t e s t. I should look up more on this command, eventually.. Cheers! –  sdaau May 23 '12 at 18:50

If you want to use epl3 you can parse the string by the module l3str.

The following code works with the newest version of expl3.

\usepackage{xparse,l3str}
\ExplSyntaxOn
\NewDocumentCommand \parseMyNum { m }
{
\tl_set:Nx \l_tmpa_tl {#1}
\cs_gset:Npn \MyNum { \str_substr:Nnn \l_tmpa_tl  {4 } { \tl_count:N \l_tmpa_tl } }
}
\ExplSyntaxOff


However if you use an older one you can use \tl_length:N instead of \tl_count:N

\usepackage{xparse,l3str}
\ExplSyntaxOn
\NewDocumentCommand \parseMyNum { m }
{
\tl_set:Nx \l_tmpa_tl {#1}
\cs_gset:Npn \MyNum { \str_substr:Nnn \l_tmpa_tl  {4 } { \tl_length:N \l_tmpa_tl } }
}
\ExplSyntaxOff


Here the complete code:

\documentclass{article}
\usepackage{xparse,l3str}
\ExplSyntaxOn
\NewDocumentCommand \parseMyNum { m }
{
\tl_set:Nx \l_tmpa_tl {#1}
\cs_gset:Npn \MyNum { \str_substr:Nnn \l_tmpa_tl  {4 } { \tl_count:N \l_tmpa_tl } }
}
\ExplSyntaxOff

\usepackage{filecontents}
\begin{filecontents}{\jobname.bib}
@misc{num01,
title = {Title1},
eprint = {MR001},
eprinttype = {mrnumber}}
@misc{num02,
title = {Title},
eprint = {002},
eprinttype = {mrnumber}}
@misc{num03,
title = {Title},
eprint = {MR003 (aa)},
eprinttype = {mrnumber}}
@misc{num04,
title = {Title},
eprint = {004 (bb)},
eprinttype = {mrnumber}}
\end{filecontents}
\usepackage{biblatex}

\DeclareFieldFormat{myentrykey}{%
\parseMyNum{#1}%
\textbf{Testing \MyNum: \textit{#1}}%
}

\makeatletter
\newbibmacro*{myentrycite}{% custom
\printfield[myentrykey]{entrykey}
} % end \newbibmacro*{
\makeatother

\DeclareCiteCommand{\myentrycite}
{\usebibmacro{prenote}}
{\usebibmacro{citeindex}%
\printtext{\usebibmacro{myentrycite}}}
{}
{\usebibmacro{postnote}}

\begin{document}

\def\testvar{num01}
\typeout{ a== \testvar, }
\typeout{ -- \meaning\parseMyNum}
\parseMyNum{\testvar}
\typeout{ b== \testvar, - \MyNum}

\myentrycite{num03}

\end{document}

-
the saving of the input in a single token list isn't necessary. But I like to handle inputs in this way. ;-) –  Marco Daniel May 23 '12 at 17:19
Many thanks for that, @MarcoDaniel - looks like a great example! However, it will also demand I start on learning some new syntax, which I'd rather avoid.. And I'd also hope to learn at least an approximate reason of why the problem appears as it does... Thanks again - cheers! –  sdaau May 23 '12 at 17:23
@MarcoDaniel The \str_substr:nnn function (in very recent commits, it is renamed to \str_range:nnn) also accepts negative arguments, counting from the right. So the \tl_count:N \l_tmpa_tl can be replaced by -1. –  Bruno Le Floch Jan 20 '13 at 18:16

Ok, I did get somewhere, finally, so I'm just posting this as a possible workaround; however, I'd still like to know better what is going on.

Becuase in my case, I work strictly with 5-letter words, and with extracting the last one - after a lot of messing with \detokenize and a bunch of other commands I only guess at, I arrived at the simplest way - simply define a macro with 5 input arguments, and supposedly the characters of the string would split themselves automatically into locations. It's a somewhat disappointing workaround, as it works in just a limited ammount of cases (if you deal with short, fixed-length strings). But at least (and somewhat surprisingly for me, after agonizing over this example for a while), it does seem to work.

Here is a snippet of just the functions which are changed in respect to the OP:

...
% notes:
% a1: cannot call this directly, still needs a wrapper!
% (though only from \parseMyNum{}: "Runaway argument? ... ")
% a2: the typeout here also returns cruft! comment it out!
%
\def\parseMyNumHelperB#1#2#3#4#5{% (a1)
% \typeout{ 01 #1 #2 #3 #4 #5}% (a2)
#5% output only 5-th character
}

% b1: this expandafter..relax stanza must be present for proper parsing!
%
\def\parseMyNumHelper#1{%
\edef\MyNum{\expandafter\parseMyNumHelperB#1\relax} % (b1)
\typeout{ == MyNum \MyNum ==} %
}

% c1: with newcommand, so we can use braces to pass variables around
% c2: here we cannot use braces anymore to pass the variable
%
\newcommand*\parseMyNum[1]{% (c1)
\long\edef\temp{#1}%
\parseMyNumHelper\temp%    (c2)
}

....

% d1: directly with #1, used to pass only one char; that
% required storage in a variable (d2) - not anymore
%
\DeclareFieldFormat{myentrykey}{\relax%
%\edef\eetmp{#1} % (d2)
\parseMyNum{#1} %  (d1)
\textbf{Testing \MyNum: \textit{#1}}\relax%
}

...


Why should this work under biblatex, and the other code not, I really have no idea - but at least it works:

... and the terminal log also looks better:

 a== num01,
-- macro:#1->\edef \MyNum {\expandafter \parseMyNumHelperB #1\relax } \typeout
{ == MyNum \MyNum ==}
-- macro:#1->\long \edef \temp {#1}\parseMyNumHelper \temp
== MyNum 1\relax ==
b== num01, - num01, - 1\relax
== MyNum 3\relax ==


Well, I hope may help someone - and/or maybe inspire someone to come up with an explanation for this problem :) ..
Cheers!