Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

define \tl_if_integer:n #959

Closed
bastien-roucaries opened this issue Jul 5, 2021 · 24 comments
Closed

define \tl_if_integer:n #959

bastien-roucaries opened this issue Jul 5, 2021 · 24 comments

Comments

@bastien-roucaries
Copy link

Hi,

It will nice to test wether a token list is an integer or not, without resorting to use internal __int_to_roman:w:

\prg_new_conditional:Npnn \tl_if_integer:n #1 { p, T, F, TF }
{
  \tl_if_blank:oTF { \__int_to_roman:w -0#1 }
   {
    \prg_return_true:
   }
   {
    \prg_return_false:
   }
}
@bastien-roucaries
Copy link
Author

Better here:

\prg_new_conditional:Npnn \tl_if_integer:n #1 { p, T, F, TF }
{
  \tl_if_blank:nTF{#1}
    {
      \prg_return_false:
    }
    {
      \tl_if_blank:oTF { \__int_to_roman:w -0#1 }
      {
        \prg_return_true:
      }
      {
        \prg_return_false:
      }
    }
}

@zauguin
Copy link
Member

zauguin commented Jul 5, 2021

The name seems odd: It doesn't actually check for an integer (e.g. -5 is an integer but returns false, +7 returns false too). It seems more like a test if only digits are present.

@bastien-roucaries
Copy link
Author

@zauguin Ok for the name but the function is useful, and match the old tick of tex book to check if digit. I agree that integer could be implemented using regex but it will be slow

@bastien-roucaries
Copy link
Author

Improved with your comments:

\prg_new_conditional:Npnn \tl_if_digit:n #1 { p, T, F, TF }
{
  \tl_if_blank:nTF{#1}
    {
      \prg_return_false:
    }
    {
      \tl_if_blank:oTF { \__int_to_roman:w -0#1 }
      {
        \prg_return_true:
      }
      {
        \prg_return_false:
      }
    }
  }
\prg_generate_conditional_variant:Nnn \tl_if_digit:n { o } { p, T, F, TF }
\prg_new_conditional:Npnn \__tl_if_integer:n #1 { p, T, F, TF }
{
    \exp_args:No\str_if_eq:onTF{\tl_head:n{#1}}{+}
    {
      \exp_args:No\tl_if_digit:oTF{\tl_tail:n{#1}}
      {
        \prg_return_true:
      }
      {
        \prg_return_false:
      }
    }
    {
      \exp_args:No\str_if_eq:onTF{\tl_head:n{#1}}{-}
      {
        \exp_args:No\tl_if_digit:oTF{\tl_tail:n{#1}}
        {
          \prg_return_true:
        }
        {
          \prg_return_false:
        }
      }
      {
        \prg_return_false:
      }
    }
}
\prg_new_conditional:Npnn \tl_if_integer:n #1 { p, T, F, TF }
{
  % fast path
  \tl_if_digit:nTF{#1}
  {
    \prg_return_true:
  }
  {
    % slow path
    \__tl_if_integer:nTF{#1}
    {
      \prg_return_true:
    }
    {
      \prg_return_false:
    }
  }
}

@PhelypeOleinik
Copy link
Member

@bastien-roucaries TeX accepts multiple signs in front of a number, so your test is usually enough but not always. Should this be implemented, it could allow multiple signs like this:

\ExplSyntaxOn
\prg_new_conditional:Npnn \str_if_integer:n #1 { p, T, F, TF }
  { \exp_after:wN \__str_if_integer_sign:N \tl_to_str:n {#1} \scan_stop: }
\cs_new:Npn \__str_if_integer_sign:N #1
  {
    \if:w $
        \if_meaning:w - #1 F \fi:
        \if_meaning:w + #1 F \fi: $
      \exp_after:wN \__str_if_integer_digits:w \exp_after:wN #1
    \else:
      \exp_after:wN \__str_if_integer_sign:N
    \fi:
  }
\cs_new:Npn \__str_if_integer_digits:w #1 \scan_stop:
  {
    \tl_if_blank:nTF {#1}
      { \prg_return_false: }
      {
        \tl_if_blank:oTF { \__int_to_roman:w -0#1 }
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }

\cs_new:Npn \test #1
  { \typeout { #1: \str_if_integer:nTF {#1} { INT } { NOT } } }

\test {   }
\test { ~ }
\test { 1 }
\test { - }
\test { -1 }
\test { +1 }
\test { +-+-+-1 }

\stop

The test is str to prevent expansion of tokens in the argument. Both mine and your implementation do not expand the sign, but do expand the digits with \romannumeral. A tl version would probably have to expand everything as needed.

@blefloch
Copy link
Member

blefloch commented Jul 6, 2021

I agree it would make sense to provide \str_if_integer:nTF, but it is not yet clear what the allowed integers should be. Presumably spaces should be allowed between signs, but not within an integer since TeX disallows it?

Code based on \romannumeral breaks for long strings of digits, with a "number too big" error. Presumably we need a slower process that only grabs a few digits at a time, but then it gets annoying to detect spaces between digits.

An alternative/addition would be \str_to_integer:nF that sanitizes the string if it looks "enough" like an integer (we could decide to be somewhat lax (but well documented)) and otherwise leaves its second argument in the input stream.

@bastien-roucaries
Copy link
Author

@blefloch What is the documented limit of \romannumeral ?

@PhelypeOleinik
Copy link
Member

@bastien-roucaries The biggest integer TeX can handle: 2³¹−1 = 2147483647.

@bastien-roucaries
Copy link
Author

@PhelypeOleinik It seems that @blefloch means that +-+-+-+-+-+-+-+-+-+-+-+-+000000000000000000000000000000000000000000000000000000000000000 could break \romannumeral...

So what is the max string size ?

@PhelypeOleinik
Copy link
Member

@bastien-roucaries \romannumeral only breaks with “Number too big” for a nonzero number. TeX can take an arbitrarily long string of zeros as long as the resulting integer is within the [−2³¹−1, 2³¹−1] range.

@eg9
Copy link
Contributor

eg9 commented Jul 9, 2021

@bastien-roucaries @blefloch I'd not forget integers in hexadecimal or octal format. And

-`a

is an integer too.

@u-fischer
Copy link
Member

there is an internal test for digits in l3bitset \__bitset_test_digits:nTF. It doesn't handle signs as this was unneeded but it should handle integer expressions.

@davidcarlisle
Copy link
Member

as @eg9 just hinted, what is the actual use case here? Is it a user level document check for a string of digits, or a check that the string is a valid input to an expl3 integer function. The latter seems more useful in an expl3 context, and basically means that nothing is left after \numexpr#1 so the slow check for literal strings of digits would not be needed.

@bastien-roucaries
Copy link
Author

@davidcarlisle This bug is about the first case, but i will like to have the second use

@FrankMittelbach
Copy link
Member

I don't think that I'm in favor of extending the L3 programming layer with random extensions that are (possibly) useful in special situations but not have clear regularly needed use cases. To me this is one such example. If we start with that, where do we end? How many special "is this tl some X" should we support then? My vote on this is no; not for the core language.

@bastien-roucaries
Copy link
Author

@FrankMittelbach Knowing f we can do some computation or not on TL is uselful. The other could be done using regex, but with the pitfall that it does not work in expand context (and I need this test to work in expand context)

@FrankMittelbach
Copy link
Member

No doubt. But is it useful enough to warrant its inclusion into the core language (or only to be provided as part of some package code when actually needed)? That's the question for me here and so far I haven't seen really arguments that favor its inclusion.

You can ask the same question for "is it a dimension", "is it as skip", " is it just letters", "does it contain non-ascii characters", "is it a date format", and, and, and ... the possibilities are quite open ended and for most of them you can construct a use case or two. But 99% of the time they will just sit there and take up space. And while space is not a premium as it was in the past it still adds up to the complexity and maintainability of the core system. So again, is this functionality with wide and repeated use? If yes, and I hear convincing arguments for that it could be considered a candidate for inclusion. If not, it should be implemented as part of the code that needs it.

@bastien-roucaries
Copy link
Author

@FrankMittelbach The problem is specific to biblatex here. We need to test if some field is an integer (think about pages number) and so do some aithmetic on it, in a expandable context.
Ok ti is specific but the tricky part is the expandable stuff.
May be this kind of test should only be documented. Reading the doc I do not know how to do, and moreover biblatex core thinks it is not possible to test (expand comptabible) if a token if an integer.

May be the solution is to render regex expandable (I think it will be nice)

@FrankMittelbach
Copy link
Member

Don't get me wrong, I'm not at all against that we help you on the biblatex side to make this work, and it may well be that on the core we are missing some functionality that should be in core to support that. My point is that the core should restrict itself to needs that are "general" and to provide the basic general foundation to write special code, but not provide all kind of extensions that are used very seldom if at all (e.g in this case when biblatex is not used). Otherwise we will end up with a very bloated set of commands eventually.

Making things work expandably usually comes with a heavy pricetag, either in limited functionality or loss of speed or both. So I doubt it is a good idea to try to make regex expandable, but I let @blefloch comment on that.

But without knowning your use case in more detail: somewhere your fields are being set up and that is not done expandably as it will require assignments, so at that stage you could determine if you have an integer or something else and record that fact which could then be expandably used (not sure that helps or is feasible, but it would avoid testing the field over and over again).

@bastien-roucaries
Copy link
Author

Any advance for this. I need this time in order to detect and improve parsing of id. Exapndable is needed due to hyperref

@bastien-roucaries
Copy link
Author

They are also a few use case here https://tex.stackexchange.com/questions/7180/testing-for-number

@u-fischer
Copy link
Member

Any advance for this. I need this time in order to detect and improve parsing of id. Exapndable is needed due to hyperref

then why don't you define yourself a private function? There were above a number of suggestion which seems to work for you.

@josephwright
Copy link
Member

Aside: I think if added should be in the int module as something like \int_validate:n(TF)

@josephwright
Copy link
Member

I think I will close at the moment: if there is a pressing general use case and it can't be covered using public tools, we can revisit

@josephwright josephwright closed this as not planned Won't fix, can't repro, duplicate, stale May 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants