5

As an attempt to practice with expl3, I am trying to parse numerical date string to French. There is a \mymodule_display_date_year_month_day:n which receives a string of form yyyy-mm-dd (or yyyy-mm or mm-dd, which is not done yet), and tries to convert it to French text. However, it cannot successfully parse the yyyy-mm-dd string and pass the three integers to \mymodule_display_date_year_month_day_french:nnn.

Here is what I've tried so far. Where does it go wrong?

\documentclass{article}

\usepackage[french,english]{babel}

\begin{document}

\selectlanguage{french}

\ExplSyntaxOn

\int_new:N \l__mymodule_year_int
\int_new:N \l__mymodule_month_int
\int_new:N \l__mymodule_day_int

\cs_new:Nn \mymodule_text_superscript:n { \textsuperscript { #1 } }


\cs_new_protected:Nn \mymodule_display_date_year_month_day:n
  {
    \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
      {
        \__mymodule_parse_yyyymmdd:www #1 \q_stop
        \exp_args:Nx \cs_if_exist_use:cTF { mymodule_display_date_year_month_day_ \languagename :nnn }
          {
            { \l__mymodule_year_int }
            { \l__mymodule_month_int }
            { \l__mymodule_day_int }
          }
          { #1 }
      }
      {
        \regex_match:nnTF { \A \d{4}-\d{1,2} \Z } { #1 }
          {
            %%
          }
          {
            \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
              {
                %%
              }
              { ?? }
          }
      }
  }

\cs_new:Npn \__mymodule_parse_yyyymmdd:www #1-#2-#3 \q_stop
  {
    \int_set:Nn \l__mymodule_year_int  { #1 }
    \int_set:Nn \l__mymodule_month_int { #2 }
    \int_set:Nn \l__mymodule_day_int   { #3 }
  }

\cs_new:Nn \mymodule_display_date_year_month_day_french:nnn
  {
    \mymodule_display_date_month_day_french:nn { #2 } { #3 }
    \c_space_tl
    #1
  }

\cs_new:Nn \mymodule_display_date_year_month_french:nn
  {
    \mymodule_display_date_month_french:n { #2 }
    \c_space_tl
    #1
  }

\cs_new_protected:Nn \mymodule_display_date_month_day_french:nn
  {
    \mymodule_display_date_day_french:n { #2 }
    \c_space_tl
    \mymodule_display_date_month_french:n { #1 }
  }

\cs_new:Nn \mymodule_display_date_month_french:n
  {
    \int_case:nnF { #1 }
    {
      { 1 }  { janvier   }
      { 2 }  { février   }
      { 3 }  { mars      }
      { 4 }  { avril     }
      { 5 }  { mai       }
      { 6 }  { juin      }
      { 7 }  { juillet   }
      { 8 }  { août      }
      { 9 }  { septembre }
      { 10 } { octobre   }
      { 11 } { novembre  }
      { 12 } { décembre  }
    } { #1 }
  }

\cs_new:Nn \mymodule_display_date_day_french:n
  {
    \int_case:nnF { #1 }
      {
        { 1 } { 1 \mymodule_text_superscript:n { er } }
      } { #1 }
  }



\mymodule_display_date_year_month_day:n {2021-06-03}



\ExplSyntaxOff
\end{document}
3
  • Unrelated, but make sure to use \cs_new_protected:Npn when your macros contain other non-expandable macros. Also, variables (like integers) should start with their scope, so \l__mymodule_year_int instead of \__mymodule_year_int Feb 5, 2022 at 18:51
  • @PhelypeOleinik May I ask how can one usually find out if there are non-expandable macros involved?
    – Jinwen
    Feb 5, 2022 at 18:52
  • 1
    If you look at the documentation in interface3, macros marked with a ★ or a ☆ are expandable, and everything else is not. If your macro contains any non-expandable macro, it is also non-expandable. If you are using some non-expl3 macro, you can find out experimentally by doing \edef\x{<macro>}\show\x. If what shows in the terminal is what you expect, the macro is (likely) expandable. If it explodes into an error or shows gibberish, it's not expandable Feb 5, 2022 at 18:55

3 Answers 3

6

You were getting an error:

! Missing number, treated as zero.
<to be read again> 
                   j

because your code was doing:

\mymodule_display_date_year_month_day_french:nnn
  \l__mymodule_year_int \l__mymodule_month_int \l__mymodule_day_int

with this definition of \mymodule_display_date_year_month_day_french:nnn:

\cs_new_protected:Nn \mymodule_display_date_year_month_day_french:nnn
  {
    \mymodule_display_date_month_day_french:nn { #2 } { #3 }
    \c_space_tl
    #1
  }

This would just “use” #1, which is \l__mymodule_year_int, which is equivalent to just typing:

using an \l__mymodule_year_int causes an error

You cannot use an integer variable by just writing it out. You must precede it with \int_use:N. So one solution would be to write \int_use:N #1 in that definition (and the one for the day). A better solution is to use the V-type expansion to pass the Value of the integer variables to the macro, so inside the macro you don't have to worry about what type of data you passed to it:

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage[french,english]{babel}

\begin{document}

\selectlanguage{french}

\ExplSyntaxOn

\int_new:N \l__mymodule_year_int
\int_new:N \l__mymodule_month_int
\int_new:N \l__mymodule_day_int

\cs_new_protected:Nn \mymodule_text_superscript:n { \textsuperscript { #1 } }


\cs_new_protected:Nn \mymodule_display_date_year_month_day:n
  {
    \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
      {
        \__mymodule_parse_yyyymmdd:www #1 \s_stop
        \cs_if_exist_use:cTF { mymodule_display_date_year_month_day_ \languagename :VVV }
          {
            \l__mymodule_year_int
            \l__mymodule_month_int
            \l__mymodule_day_int
          }
          { #1 }
      }
      {
        \regex_match:nnTF { \A \d{4}-\d{1,2} \Z } { #1 }
          {
            %%
          }
          {
            \regex_match:nnTF { \A \d{4}-\d{1,2}-\d{1,2} \Z } { #1 }
              {
                %%
              }
              { ?? }
          }
      }
  }

\cs_new_protected:Npn \__mymodule_parse_yyyymmdd:www #1-#2-#3 \s_stop
  {
    \int_set:Nn \l__mymodule_year_int  { #1 }
    \int_set:Nn \l__mymodule_month_int { #2 }
    \int_set:Nn \l__mymodule_day_int   { #3 }
  }

\cs_new_protected:Nn \mymodule_display_date_year_month_day_french:nnn
  {
    \mymodule_display_date_month_day_french:nn { #2 } { #3 }
    \c_space_tl
    #1
  }
\cs_generate_variant:Nn \mymodule_display_date_year_month_day_french:nnn { VVV }

\cs_new_protected:Nn \mymodule_display_date_year_month_french:nn
  {
    \mymodule_display_date_month_french:n { #2 }
    \c_space_tl
    #1
  }

\cs_new_protected:Nn \mymodule_display_date_month_day_french:nn
  {
    \mymodule_display_date_day_french:n { #2 }
    \c_space_tl
    \mymodule_display_date_month_french:n { #1 }
  }

\cs_new_protected:Nn \mymodule_display_date_month_french:n
  {
    \int_case:nnF { #1 }
    {
      { 1 }  { janvier   }
      { 2 }  { février   }
      { 3 }  { mars      }
      { 4 }  { avril     }
      { 5 }  { mai       }
      { 6 }  { juin      }
      { 7 }  { juillet   }
      { 8 }  { août      }
      { 9 }  { septembre }
      { 10 } { octobre   }
      { 11 } { novembre  }
      { 12 } { décembre  }
    } { #1 }
  }

\cs_new_protected:Nn \mymodule_display_date_day_french:n
  {
    \int_case:nnF { #1 }
      {
        { 1 } { 1 \mymodule_text_superscript:n { er } }
      } { #1 }
  }



\mymodule_display_date_year_month_day:n {2021-06-03}



\ExplSyntaxOff
\end{document}
11
  • Phelype, may I take the opportunity to mention that a conditional/predicate to test whether "something" is an integer is sorely missed. :-)
    – gusbrs
    Feb 5, 2022 at 19:22
  • Let us continue this discussion in chat.
    – gusbrs
    Feb 5, 2022 at 21:39
  • @gusbrs If "integer" denotes something that already forms or expands to a valid TeX-<number>-quantity, then a 100% reliable test seems to be a hard problem: A sequence of tokens may form an arbitrary expansion-based algorithm. Checking if that yields a <number> subsumes checking if an arbitrary expansion-based algorithm terminates at all. (Might be an infinite loop or the like.) This reminds me of the halting-problem. Feb 6, 2022 at 0:58
  • @UlrichDiez I was hoping for something to guard against the sensitivity of integer expressions. But take a look at the discussion in chat, Phelype provided some good insight, and also some interesting links on related discussion upstream.
    – gusbrs
    Feb 6, 2022 at 1:14
  • @gusbrs I follow the discussion about checking for valid TeX-<number> quantity as defined in the TeXbook for more than 20 years. No one came up with a 100% foolproof solution so far. Feb 6, 2022 at 1:18
1

We should not forget that we have much more simple programming language: low level TeX:

\def\monthdef#1 #2 {\expandafter\def\csname m:#1\endcsname{#2}}

\monthdef 1   janvier   
\monthdef 2   février   
\monthdef 3   mars      
\monthdef 4   avril     
\monthdef 5   mai       
\monthdef 6   juin      
\monthdef 7   juillet   
\monthdef 8   août      
\monthdef 9   septembre 
\monthdef 10  octobre   
\monthdef 11  novembre  
\monthdef 12  décembre  

\catcode`\_=11

\newcount\tmpnum
\def\display_date_year_month_day #1{\display_dateA #1;}
\def\display_dateA #1-#2-#3;{\tmpnum=#3\relax \the\tmpnum \
   \tmpnum=#2\relax \csname m:\the\tmpnum\endcsname \
   #1%
}

\display_date_year_month_day {2021-06-03}

\bye
1

You can speed up your code. You can check correctness of the input with

\regex_match:nnTF { \A \d{4}-\d{1,2}(|-\d{1,2}) \Z } { #1 }

so the last group might be missing.

After that you can split the input via \seq_set_split:Nnn and call a generic date function to which you also pass the current language (expanded).

The next step is to use the localized version. If the day is missing, the third argument will be 0 and you can check for it in the localized function.

\documentclass{article}

\usepackage[T1]{fontenc}
\usepackage[french,english]{babel}

\ExplSyntaxOn

% for testing
\NewDocumentCommand{\displaydate}{m}
  {
    \mymodule_display_date_year_month_day:n { #1 }
  }

\seq_new:N \l__mymodule_date_seq

\cs_new_protected:Nn \mymodule_text_superscript:n { \textsuperscript { #1 } }

\cs_new_protected:Nn \mymodule_display_date_year_month_day:n
  {
    \regex_match:nnTF { \A \d{4}-\d{1,2}(|-\d{1,2}) \Z } { #1 }
      {
        \seq_set_split:Nnn \l__mymodule_date_seq { - } { #1 }
        \mymodule_display_date_year_month_day:Veee
          \languagename
          { \seq_item:Nn \l__mymodule_date_seq { 1 } } % year
          { \seq_item:Nn \l__mymodule_date_seq { 2 } } % month
          { \seq_item:Nn \l__mymodule_date_seq { 3 } } % day
      }
      {
       ???#1???
      }
  }

\cs_new_protected:Nn \mymodule_display_date_year_month_day:nnnn
  {
    \use:c { __mymodule_display_date_#1:nnn } { #2 } { #3 } { \int_eval:n { 0#4 } }
  }
\cs_generate_variant:Nn \mymodule_display_date_year_month_day:nnnn { Veee }

\cs_new_protected:Nn \__mymodule_display_date_english:nnn
  {
    % month
    \int_case:nn { #2 }
      {
        { 1 }  { January   }
        { 2 }  { February  }
        { 3 }  { March     }
        { 4 }  { April     }
        { 5 }  { May       }
        { 6 }  { June      }
        { 7 }  { July      }
        { 8 }  { August    }
        { 9 }  { September }
        { 10 } { October   }
        { 11 } { November  }
        { 12 } { December  }
      }
    % day
    \int_compare:nF { #3 == 0 } { \nobreakspace #3 }
    % year
    ,\nobreakspace #1
  }

\cs_new_protected:Nn \__mymodule_display_date_french:nnn
  {
    % day
    \int_compare:nF { #3 == 0 } { \mymodule_display_date_day_french:n { #3 } \nobreakspace }
    % month
    \int_case:nn { #2 }
      {
        { 1 }  { janvier   }
        { 2 }  { février   }
        { 3 }  { mars      }
        { 4 }  { avril     }
        { 5 }  { mai       }
        { 6 }  { juin      }
        { 7 }  { juillet   }
        { 8 }  { août      }
        { 9 }  { septembre }
        { 10 } { octobre   }
        { 11 } { novembre  }
        { 12 } { décembre  }
      }
    \nobreakspace #1
  }

\cs_new_protected:Nn \mymodule_display_date_day_french:n
  {
    \int_case:nnF { #1 }
      {
        { 1 } { 1 \mymodule_text_superscript:n { er } }
      }
      { #1 }
  }

\ExplSyntaxOff


\begin{document}

\displaydate{abdarrr}

\displaydate{2021-06-03}

\displaydate{2021-06-01}

\displaydate{2021-06}

\selectlanguage{french}

\displaydate{2021-06-03}

\displaydate{2021-06-01}

\displaydate{2021-06}


\end{document}

enter image description here

2
  • Thank you, I will try to optimize my code with this method. By the way, may I ask the difference between \nobreakspace and \c_space_tl?
    – Jinwen
    Feb 10, 2022 at 17:11
  • @Jinwen You don't want to break a line between day, month and year; \nobreakspace is equivalent to a tie ~ in the document (but here we can't use ~ because it would just mean a space).
    – egreg
    Feb 10, 2022 at 17:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .