Context:
I'm trying to write a macro that behaves somewhat like C's printf family of functions. For instance, the following C code:
#include <stdio.h>
int main()
{
printf("Hello %s! Today's a good day to write obscure %s macros.\n",
"World","TeX");
}
prints
Hello World! Today's a good day to write obscure TeX macros.
by replacing the first %s by "World" and the second %s by "TeX".
I'm trying to provide a similar syntax:
\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
{world,\TeX}
which should print the same sentence in TeX. Right now I'm failing because TeX is not C :)
The problem:
I can't find a way to pass the items in the second argument of \Printf to \% because the \% macro can be hidden anywhere in the middle of the text.
A simple definition for \Printf and \% would be:
\def\Printf#1#2{%
#1% What do I do with #2?
}
\def\%{% How do I define this macro?
:(%
}
\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
{world,\TeX}
\bye
The question is: how do I define \Printf and \% such that it prints the correct sentence?
Requirements (because I'm picky :P):
The strongest requirement is that I would like the
\Printffunction to be expandable, so that:\edef\tempa{\Printf{Obscure \%\space Macros.}{TeX}} \show\tempawould print:
> \tempa=macro: ->Obscure TeX Macros..It has to work inside brace groups. I tried a delimited macro approach that kind of worked, but failed miserably if the
\%appeared inside a brace pair1.I would also like engine independence.
The syntax, on the other hand, can change slightly. That is, the
\Printf{text}{replacement}. I would like to keep the\%thingy:)
What I tried:
1Here's my attempt using \% delimited macros. I like this approach better because I don't have to redefine the \% control character anywhere. The downside is that it fails miserably when the \% token appears inside a braced group. What a bold...
\input expl3-generic.tex
\ExplSyntaxOn
\cs_generate_variant:Nn \clist_item:nn { nf }
\cs_set_eq:NN \clistItem \clist_item:nf
\cs_new:Npn \Printf #1 #2
{ \__printf_step:nnn { 0 } { #2 } { #1 } }
\cs_new:Npn \__printf_step:nnn #1 #2 #3
{ \__printf_step:nnw { #1 } { #2 } #3 \% \q_nil }
\cs_new:Npn \__printf_step:nnw #1 #2 #3 \% #4
{
#3
\quark_if_nil:NF #4
{
\exp_args:Nf \__printf_percent:nn
{ \int_eval:n { #1 + 1 } } { #2 }
\exp_args:Nf \__printf_step:nnw
{ \int_eval:n { #1 + 1 } } { #2 } #4
}
}
\cs_new:Npn \__printf_percent:nn #1 #2
{ \clist_item:nn { #2 } { #1 } }
\ExplSyntaxOff
\Printf{Hello \%! Today's a good day to write obscure \%\ macros.}
{world,\TeX}
\Printf{Oh, \%, my macro doesn't work {\bf with \%\ text}. What a \%\dots}
{drat,bold,shame}
\bye
This prints:



\__tl_act:NNNnn, used internally in the kernel: one of Bruno's. You can't directly use that here, which is much the same as\tl_lower_case:n, due to the need to keep extra data on the stack. But it's fundamentally Bruno's approach ... – Joseph Wright Oct 31 '18 at 20:36f-type expansion of the replacement text. That would be doable (if for example you want to examine the tokens in the case of adding\TeX), but I'll leave as an exercise for the reader :) [Hint: see\tl_lower_case:n] – Joseph Wright Oct 31 '18 at 20:37