Problems testing for a leap year using expl3

conditionalsexpl3latex3macros

For a recent project I tried to become acquainted with LaTeX3 but unfortunately, I have some difficulties and cannot find the issue, which is why I'm asking for a helpful hint.

In the project, I want to check whether a given year of a date is a leap year. That's why I defined an \isLeapYear macro calling a LaTeX3 function under the hood.

The common algorithm to check whether a given year is a leap year is the following (from Wikipedia):

Algorithm for leap years, © Wikipedia

Taking this, I wrote the following code. Remember, that I'm just starting to learn LaTeX3, and to be honest, it's quite challenging even though I am more or less proficient in several programming languages.

\prg_new_conditional:Npnn \is_leapyear:n #1 { p, T, F, TF }
{
    \int_set:Nn \l_tmpa_int { \curriculum_modulo:nn {#1} {4} }
    \tl_if_eq:nnTF {\l_tmpa_int} {0} {
        \int_set:Nn \l_tmpa_int { \curriculum_modulo:nn {#1} {100} }
        \tl_if_eq:nnTF {\l_tmpa_int} {0} {
            \int_set:Nn \l_tmpa_int { \curriculum_modulo:nn {#1} {400} }
            \tl_if_eq:nnTF {\l_tmpa_int} {0} {\prg_return_true}{\prg_return_false}
        }{\prg_return_true}
    } {\prg_return_false}
}

\newcommand{\isLeapYear}[1]
{
    \bool_if:nTF { \is_leapyear_p:n {#1} } {leap~year} {normal~year}
}

When running this code \isLeapYear{2024} a Missing number, treated as zero. error occurs.

Here is the exact error message.

! Missing number, treated as zero.
<to be read again> 
                   \__bool_=_0:
l.25     \isLeapYear{2024}
                          
A number should have been here; I inserted `0'.
(If you can't figure out why I needed to see a number,
look up `weird error' in the index to The TeXbook.)

Is there a major issue in my approach or am I missing something? I look forward to any helpful hints that increase my knowledge of LaTeX3 and take me further.

MWE

\documentclass{article}

\ExplSyntaxOn

% https://www.alanshawn.com/latex3-tutorial/#implementing-modulo-operation
\cs_set:Npn \curriculum_modulo:nn #1#2 {
    \int_set:Nn \l_tmpa_int { \int_div_truncate:nn {#1}{#2} }
    \int_eval:n { (#1) - \l_tmpa_int * (#2) }
}

\prg_new_conditional:Npnn \is_leapyear:n #1 { p, T, F, TF }
{
    \int_set:Nn \l_tmpa_int { \curriculum_modulo:nn {#1} {4} }
    \tl_if_eq:nnTF {\l_tmpa_int} {0} {
        \int_set:Nn \l_tmpa_int { \curriculum_modulo:nn {#1} {100} }
        \tl_if_eq:nnTF {\l_tmpa_int} {0} {
            \int_set:Nn \l_tmpa_int { \curriculum_modulo:nn {#1} {400} }
            \tl_if_eq:nnTF {\l_tmpa_int} {0} {\prg_return_true}{\prg_return_false}
        }{\prg_return_true}
    } {\prg_return_false}
}

\newcommand{\isLeapYear}[1]
{
    \bool_if:nTF { \is_leapyear_p:n {#1} } {leap~year} {normal~year}
}

\ExplSyntaxOff

\begin{document}
    \isLeapYear{2024}
\end{document}

Best Answer

\bool_if:n(TF) requires that all operations in its first (n) argument be expandable (marked with ★ in interface3). You were using \int_set:Nn (assignments are never expandable) and \tl_if_eq:nnTF, which are not expandable. You can easily rewrite that code using \int_mod:nn instead of \curriculum_modulo:nn, and \int_compare:nNnTF instead of \int_set:Nn+\tl_if_eq:nnTF.

\int_compare:nNnTF expects <integer expressions> as (1st and 3rd) arguments, so instead of feeding it an int variable, you can feed it a (mandatorily) expandable expression that evaluates to an integer. If you look at the documentation of \int_mod:nn in interface3 (currently in section 20.1 Integer expressions), you'll see it is expandable, and evaluates to an integer, so you can safely use it anywhere LaTeX expects an integer.

\documentclass{article}

\ExplSyntaxOn

\prg_new_conditional:Npnn \is_leapyear:n #1 { p, T, F, TF }
  {
    \int_compare:nNnTF { \int_mod:nn {#1} { 4 } } = { 0 }
      {
        \int_compare:nNnTF { \int_mod:nn {#1} { 100 } } = { 0 }
          {
            \int_compare:nNnTF { \int_mod:nn {#1} { 400 } } = { 0 }
              { \prg_return_true: }
              { \prg_return_false: }
          }
          { \prg_return_true: }
      }
      { \prg_return_false: }
}

\newcommand{\isLeapYear}[1]
  {
    \bool_if:nTF { \is_leapyear_p:n {#1} } {leap~year} {normal~year}
  }

\ExplSyntaxOff

\begin{document}
    \isLeapYear{2024}
\end{document}
Related Question