1

I have a document with requirements marked up in text with a custom macro \Req{}, which takes a comma separated list of identifiers of the form [A-F]:[0-9]{4,5}([0-9]{1,2})?, outputs the identifiers and calls \index for each of them. I need the index to be output ordered by the numeric part of the identifier, so am attempting to pad the numeric part of the identifier into a sort key to pass to \index{thing!sort-key@text}. This is performed by \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}

This produces an a2b.idx of:

\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}

whereas, I think, makeindex will treat it literally and it needs to be evaluated for the idx file.

Printed index should be in order A B C, but ends up B A C.

The symptoms are the same as this unanswered question, but I'm using expl3 Expanding macro in \index argument.

There are now enough copy 'n paste fixes in my solution (padding, colons, general expl3 bodging) that I get the feeling I'm missing something obvious, as everything I've tried has either made no difference or broken it completely. What am I doing wrong?

aejh
  • 107

2 Answers2

2

The \padreq:n function is not expandable. You can save its output in a token list and use it for producing the index entry. There should not be the ., however.

\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}

This is what I get in the .idx file:

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

enter image description here

enter image description here

If I change A:10 into A:410, I get

enter image description here

egreg
  • 1,121,712
1

The following code should do the job.

\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}

F. Pantigny
  • 40,250