Expandiendo una macro en \ index para ordenar usando expl3

Aug 20 2020

Tengo un documento con requisitos marcados en texto con una macro personalizada \Req{}, que toma una lista separada por comas de identificadores del formulario [A-F]:[0-9]{4,5}([0-9]{1,2})?, genera los identificadores y llama \indexa cada uno de ellos. Necesito que el índice se envíe ordenado por la parte numérica del identificador, así que estoy intentando rellenar la parte numérica del identificador en una clave de clasificación para pasar \index{thing!sort-key@text}. Esto es realizado por \padreq:n:

\documentclass{article}
\usepackage{expl3}
\usepackage{imakeidx}
\usepackage{xparse}

\makeindex[name=a2b, columns=1]

\ExplSyntaxOn

% #1 length
% #2 padee
\cs_new:Nn \padint:nn
{ % From : https://tex.stackexchange.com/a/412238/104401
  \prg_replicate:nn { #1 - \tl_count:f { \int_to_arabic:n { #2 } } } { 0 }
  \int_to_arabic:n { #2 }
}
\cs_generate_variant:Nn \tl_count:n { f }

% takes x.y, pads to 00000x.0y
\cs_new:Nn \padreq:n
{
    % temp variables
    \seq_clear_new:N \l_padreq_tmp_seq
    \tl_clear_new:N \l_padreq_tmp_tl

    % split arg 1
    \seq_set_split:Nnn \l_padreq_tmp_seq {.} {#1}
    \seq_log:N \l_padreq_tmp_seq

    % Take the first item... 
    \seq_pop_left:NNT \l_padreq_tmp_seq \l_padreq_tmp_tl
    { % ... and pad it
      %\padint:nn{5}{\l_padreq_tmp_tl}
      \padint:nn{5}{\l_padreq_tmp_tl}
    }
    % Add the middle '.'
    .
    % Take next split item, which is optional
    \seq_pop_left:NNTF \l_padreq_tmp_seq {\l_padreq_tmp_tl}
    { % and pad it
    \padint:nn{2}{\l_padreq_tmp_tl}
    }
    { % or set a default of 00 if missing
    00
    }
}
\cs_generate_variant:Nn \padreq:n { x }

\DeclareDocumentCommand{\Req}{m}
{
  % Split the csv input
  \seq_set_from_clist:Nn \l_tmpa_seq {#1}

  % output back to the document, with formatting
  [ \seq_use:Nn \l_tmpa_seq {,~} ]

  % Index each value, creating a sort key for \index
  \seq_map_inline:Nn \l_tmpa_seq {
      % Split by colon
      % colons with expl3 https://tex.stackexchange.com/a/501346/104401
      \use:x {\exp_not:N\seq_set_split:Nnn \exp_not:N\l_tmpb_seq {\c_colon_str} {##1}}
      % Pad the 2nd item in the split sequence 
      \tl_set:Nn \l_tmpa_tl {\padreq:x{\seq_item:Nn \l_tmpb_seq 2}}
      \newline ##1~--~\l_tmpa_tl % debug, typesets correctly
      \index[a2b]{MWE!\l_tmpa_tl@##1}
  }
}

\ExplSyntaxOff

\begin{document}
Hello. \Req{C:230, A:10}
\par
World. \Req{B:101.1}

\printindex[a2b]
\end{document}

Esto produce un a2b.idx de:

\indexentry{MWE!\padreq:x {230}@C:230}{1}
\indexentry{MWE!\padreq:x {10}@A:10}{1}
\indexentry{MWE!\padreq:x {101.1}@B:101.1}{1}

mientras que, creo, makeindex lo tratará literalmente y necesita ser evaluado para el archivo idx.

El índice impreso debe estar en orden ABC, pero termina BA C.

Los síntomas son los mismos que los de esta pregunta sin respuesta, pero estoy usando expl3 Expandir macro en el argumento \ index .

Ahora hay suficientes correcciones de copiar y pegar en mi solución (relleno, dos puntos, cuerpo de explotación general) que tengo la sensación de que me estoy perdiendo algo obvio, ya que todo lo que he probado no ha hecho ninguna diferencia o lo ha roto por completo. ¿Qué estoy haciendo mal?

Respuestas

1 egreg Aug 20 2020 at 16:30

La \padreq:nfunción no se puede ampliar. Puede guardar su salida en una lista de tokens y usarla para producir la entrada de índice. Sin .embargo, no debería haber .

\documentclass{article}
\usepackage{expl3}
\usepackage{imakeidx}
\usepackage{xparse}

\makeindex[name=a2b, columns=1]

\ExplSyntaxOn

% variables and variants
\seq_new:N \l_padreq_tmp_seq
\tl_new:N \l_padreq_paddedargs_tl

\cs_generate_variant:Nn \tl_count:n { f }
\cs_generate_variant:Nn \seq_set_split:Nnn { NV }

% #1 length
% #2 padee
\cs_new:Nn \padint:nn
 { % From : https://tex.stackexchange.com/a/412238/104401
  \prg_replicate:nn { #1 - \tl_count:f { \int_to_arabic:n { #2 } } } { 0 }
  \int_to_arabic:n { #2 }
 }
\cs_generate_variant:Nn \padint:nn { ne }

% takes x.y, pads to 00000x.0y
\cs_new_protected:Nn \padreq:n
 {
  % split arg 1
  \seq_set_split:Nnn \l_padreq_tmp_seq {.} {#1}
  %\seq_log:N \l_padreq_tmp_seq

  % pad the arguments
  \tl_set:Nx \l_padreq_paddedargs_tl
   {
    % pad the first argument
    \padint:ne { 5 } { \seq_item:Nn \l_padreq_tmp_seq { 1 } }
    % Take next split item, which is optional
    \int_compare:nTF { \seq_count:N \l_padreq_tmp_seq = 1 }
     {% no second argument, add a default
      00000
     }
     {
      \padint:ne { 5 } { \seq_item:Nn \l_padreq_tmp_seq { 2 } }
     }
   }
 }
\cs_generate_variant:Nn \padreq:n { e }

\DeclareDocumentCommand{\Req}{m}
 {
  % Split the csv input
  \seq_set_from_clist:Nn \l_tmpa_seq {#1}

  % output back to the document, with formatting
  [ \seq_use:Nn \l_tmpa_seq {,~} ]

  % Index each value, creating a sort key for \index
  \seq_map_inline:Nn \l_tmpa_seq
   {
    % Split by colon
    \seq_set_split:NVn \l_tmpb_seq \c_colon_str {##1}
    % Pad the 2nd item in the split sequence
    \padreq:e { \seq_item:Nn \l_tmpb_seq { 2 } }
    \index[a2b] {\l_padreq_paddedargs_tl@##1}
   }
 }

\ExplSyntaxOff

\begin{document}
Hello. \Req{C:230, A:10}
\par
World. \Req{B:101.1}

\printindex[a2b]
\end{document}

Esto es lo que obtengo en el .idxarchivo:

\indexentry{0023000000@C:230}{1}
\indexentry{0001000000@A:10}{1}
\indexentry{0010100001@B:101.1}{1}

Si cambio A:10en A:410, consigo

1 F.Pantigny Aug 20 2020 at 16:13

El siguiente código debería hacer el trabajo.

\documentclass{article}
\usepackage{expl3}
\usepackage{imakeidx}
\usepackage{xparse}

\makeindex[name=a2b, columns=1]

\ExplSyntaxOn

% #1 length
% #2 padee
\cs_new:Nn \__aejh_padint:nn
  { % From : https://tex.stackexchange.com/a/412238/104401
    \prg_replicate:nn { #1 - \tl_count:f { \int_to_arabic:n { #2 } } } { 0 }
    \int_to_arabic:n { #2 }
  }
\cs_generate_variant:Nn \tl_count:n { f }


% Will contain the result (that is to say the key constructed on padding)
\tl_clear_new:N \l__aejh_result_tl


\cs_generate_variant:Nn \seq_set_split:Nnn { N V n } 

\DeclareDocumentCommand { \Req } { m }
  {
    \clist_map_inline:Nn { #1 }
      {
        % Split by colon : we need to use the *value* of \c_colon_str
        \seq_set_split:NVn \l_tmpa_seq \c_colon_str { ##1 } 

        % We retrieve the second item (after the colon)
        % Fortunately \seq_item:Nn is fully expandable
        \tl_set:Nx \l_tmpa_tl { \seq_item:Nn \l_tmpa_seq 2 }

        % We split on an optional dot: \l_tmpb_seq will be of length 1 or 2
        \seq_set_split:NnV \l_tmpb_seq { . } \l_tmpa_tl

        % We retrive (by poping) the first part
        \seq_pop_left:NN \l_tmpb_seq \l_tmpb_tl

        % We pad the first part. Fortunately, \__aejh_padint:nn is fully expandable.
        \tl_set:Nx \l__aejh_result_tl { \__aejh_padint:nn { 5 } { \l_tmpb_tl } }

        % We add the dot
        \tl_put_right:Nn \l__aejh_result_tl { . }

        % We add the second part, after padding
        \seq_pop_left:NNTF \l_tmpb_seq \l_tmpb_tl
          { \tl_put_right:Nx \l__aejh_result_tl { \__aejh_padint:nn 2 { \l_tmpb_tl } } }
          { \tl_put_right:Nn \l__aejh_result_tl { 00 } }

        \index [a2b] { MWE ! \l__aejh_result_tl @ ##1 }
     }
  }

\ExplSyntaxOff

\begin{document}
Hello. \Req{C:230, A:10}
\par
World. \Req{B:101.1}

\printindex[a2b]
\end{document}