TransWikia.com

Expanding a macro into index for sorting using expl3

TeX - LaTeX Asked on July 11, 2021

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:Nseq_set_split:Nnn exp_not:Nl_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?

2 Answers

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

Correct answer by egreg on July 11, 2021

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}

Answered by F. Pantigny on July 11, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP