To answer my own question, using only expandable macros and avoiding the use of \prg_new_conditional:Npnn
the following example just works fine and is flexible in the way that all used tests can be adapted to specific problems and do not rely on predefined code like \__int_to_roman:w
suggested by egreg.
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\bool_new:N \true_bool
\bool_new:N \false_bool
\bool_gset_true:N \true_bool
\bool_gset_false:N \false_bool
\prg_new_conditional:Npnn \is_digit:N #1 { TF }
{
\bool_if:nTF
{
\token_if_eq_charcode_p:NN 0 #1 ||
\token_if_eq_charcode_p:NN 1 #1 ||
\token_if_eq_charcode_p:NN 2 #1 ||
\token_if_eq_charcode_p:NN 3 #1 ||
\token_if_eq_charcode_p:NN 4 #1 ||
\token_if_eq_charcode_p:NN 5 #1 ||
\token_if_eq_charcode_p:NN 6 #1 ||
\token_if_eq_charcode_p:NN 7 #1 ||
\token_if_eq_charcode_p:NN 8 #1 ||
\token_if_eq_charcode_p:NN 9 #1
}{
\prg_return_true:
}{
\prg_return_false:
}
}
\cs_new:Npn \is_integer_p:n #1
{
\tl_if_empty:nTF { #1 }
{
% We are done if the token list is empty
\bool_if_p:n { \true_bool }
}{
\exp_args:Nf \is_digit:NTF { \tl_head:n { #1 } }
{
\exp_args:Nf \is_integer_p:n { \tl_tail:n { #1 } }
}{
\bool_if_p:n { \false_bool }
}
}
}
\bool_if:nTF { \is_integer_p:n { 1234 } } { true } { false }
\ExplSyntaxOff
\end{document}
Interestingly, trying the same with \prg_new_conditional:Npnn
doesn't seem to be possible. The following throws errors, although I don't understand why. I would assume that both implementations result in equivalent code, but apparently this isn't true.
\prg_new_conditional:Npnn \is_integer_prg:n #1 { p }
{
\tl_if_empty:nTF { #1 }
{
% We are done if the token list is empty
\prg_return_true:
}{
\exp_args:Nf \is_digit:NTF { \tl_head:n { #1 } }
{
\exp_args:Nf \is_integer_prg_p:n { \tl_tail:n { #1 } }
}{
\prg_return_false:
}
}
}
Maybe somebody can clarify, why the \prg_new_conditional:Npnn
solution doesn't work.
Edit: Eventually I figured out how to do the trick with \prg_new_conditional:Npnn
. The problem in my previous attempt was that a predicate conditional is no legal "return value" of \prg_new_conditional:Npnn
. This means that
\prg_new_conditional:Npnn \foo: { p }
{
\int_compare_p:n { 1 = 2 }
}
fails, while
\prg_new_conditional:Npnn \foo: { p }
{
\int_compare:nTF { 1 = 2 } { \prg_return_true: } { \prg_return_false: }
}
works, which of course makes sense in hindsight. So the above attempt can be corrected to the following working example:
\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\prg_new_conditional:Npnn \is_digit:N #1 { TF }
{
\bool_if:nTF
{
\token_if_eq_charcode_p:NN 0 #1 ||
\token_if_eq_charcode_p:NN 1 #1 ||
\token_if_eq_charcode_p:NN 2 #1 ||
\token_if_eq_charcode_p:NN 3 #1 ||
\token_if_eq_charcode_p:NN 4 #1 ||
\token_if_eq_charcode_p:NN 5 #1 ||
\token_if_eq_charcode_p:NN 6 #1 ||
\token_if_eq_charcode_p:NN 7 #1 ||
\token_if_eq_charcode_p:NN 8 #1 ||
\token_if_eq_charcode_p:NN 9 #1
}{
\prg_return_true:
}{
\prg_return_false:
}
}
\prg_new_conditional:Npnn \is_integer:n #1 { p, TF }
{
\tl_if_empty:nTF { #1 }
{
% We are done if the token list is empty
\prg_return_true:
}{
\exp_args:Nf \is_digit:NTF { \tl_head:n { #1 } }
{
\exp_args:Nf \is_integer:nTF { \tl_tail:n { #1 } }
{
\prg_return_true:
}{
\prg_return_false:
}
}{
\prg_return_false:
}
}
}
\bool_if:nTF { \is_integer_p:n { 1234 } } { true } { false },~
\bool_if:nTF { \is_integer_p:n { 12ab } } { true } { false }
\ExplSyntaxOff
\end{document}
As expected, this results in the text true, false
in the document. Of course, as pointed out by Manuel, the use of \exp_args:Nf
can be avoided by defining appropriate variants of \is_digit:NTF
and \is_integer:NTF
. Marking as solved here.