Improve list parsing command

The catoptions package has exactly the type of list processor you need. Call \menu using the syntax

\menu[<list separator>]{<list>}

The <list separator> can be anything you like, but if it is backslash (ie, \), then specify it (<list separator>) as either bslash or backslash. See examples below.

\documentclass{article}
\makeatletter
\usepackage{catoptions}[2011/12/17]

% One command (\tobiprint) replaces 7 commands by Tobi:
\robust@def*\tobiprint#1#2{\fbox{\strut#2\ifblankTF{#1}{}{~(#1)}}}

% We want to test if the <list separator> is 'bslash' or 'backslash'. For the
% test to be valid, we have to separate 'bslash' and 'backslash'. We ordinarily
% would have used comma (,) to do the splitting, but comma is also an admissible
% value for <list separator>. In fact, any character is a valid value for 
% <list separator>. Therefore, the splitter has to be a character that the user
% is unlikely to submit as the <list separator>. One such character is character
% number 1 (^^A). Hence the comma in the definition of \testlistsep is actually
% character ^^A.
% Secondly, <list separator> may have been specified by the user with spurious 
% leading and trailing spaces. Users do this to prettify their code. That is the 
% reason for calling \cpttrimspaces in \testlistsep.
\begingroup
\lccode`\,=1
\lowercase{\endgroup
  \robust@def*\testlistsep#1{%
    \xifinsetTF{,\cpttrimspaces{#1},}{,bslash,backslash,directory,location,}%
  }%
}
\newcommand\menu[2][,]{%
  \tobiprint{}{pre}%
  % \indrisloop accepts any user-specified operator. Let us call yours \tobido:
  \def\tobido##1{%
    \iflastindris
      \ifnum\indrisnr=\@ne
        \tobiprint{single}{##1}%
      \else
        \tobiprint{}{sep}\tobiprint{last}{##1}%
      \fi
    \else
      \ifnum\indrisnr=\@ne
        \tobiprint{first}{##1}%
      \else
        \tobiprint{}{sep}\tobiprint{middle}{##1}%
      \fi
    \fi
  }%
  \testlistsep{#1}{%
    \edef\alist{\detokenize{#2}}\edef\@tempa{\@backslashchar}%
  }{%
    \edef\alist{\unexpanded{#2}}\edef\@tempa{\detokenize{#1}}%
  }%
  \cptexpanded{\indrisloop*[\@tempa]}\alist\tobido
  \tobiprint{}{post}%
}
\cptrobustify\menu
\edef\cpt@parserlist{\cpt@parserlist\@backslashchar}
\makeatother
% Tests:
\begin{document}
\parindent-40pt
\menu{1,2,3,4}
\par\medskip
\menu{Single Element}
\par\medskip
\menu{A,B,C,D,E}
\par\medskip
\menu[/]{C:/Nutzer und Einstellungen/Desktop/Test}
\par\medskip
\menu[bslash]{C:\Nutzer und Einstellungen\Desktop\Test}
\end{document}

enter image description here


\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l_tobi_menu_seq
\tl_new:N \l_tobi_sep_tl
\tl_set:Nn \l_tobi_sep_tl { , } % default
\tl_const:Nx \c_tobi_backslash_tl { \cs_to_str:N \\ }

\NewDocumentCommand{\setmenuseparator}{ m }
  {
   \tl_set:Nn \l_tobi_sep_tl { #1 }
  }
\NewDocumentCommand{\menu}{ s m }
  {
   \IfBooleanTF{#1}
    {
     \group_begin:
     \tl_set_eq:NN \l_tobi_sep_tl \c_tobi_backslash_tl
     \tl_set_rescan:Nnn \l_tmpa_tl {\char_set_catcode_other:N \\ } { #2 }
     \exp_args:NV \tobi_menu_process:n \l_tmpa_tl
     \group_end:
    }
    {
     \tobi_menu_process:n { #2 }
    }
  }
\cs_new:Npn \tobi_menu_process:n #1

   \exp_args:NNV \seq_set_split:Nnn \l_tobi_menu_seq \l_tobi_sep_tl { #1 }
   \tobi_premenu:
   \prg_case_int:nnn { \seq_length:N \l_tobi_menu_seq }
     {
      { 0 } { EMPTY }
      { 1 } { \tobi_singlemenu:n { \seq_map_function:NN \l_tobi_menu_seq \use:n } }
     }
     {
      \seq_pop_left:NN \l_tobi_menu_seq \l_tmpa_tl
      \seq_pop_right:NN \l_tobi_menu_seq \l_tmpb_tl
      \tobi_firstmenu:n { \l_tmpa_tl}
      \seq_map_inline:Nn \l_tobi_menu_seq { \tobi_midmenu:n { ##1 } }
      \tobi_lastmenu:n { \l_tmpb_tl }
     }
   \tobi_postmenu:
  }
\cs_new:Npn \tobi_premenu: { \fbox{\strut pre} }
\cs_new:Npn \tobi_postmenu: { \fbox{\strut post} }
\cs_new:Npn \tobi_firstmenu:n #1 { \fbox{\strut #1~(first)} }
\cs_new:Npn \tobi_midmenu:n #1 { \fbox{\strut #1~(mid)} }
\cs_new:Npn \tobi_lastmenu:n #1 { \fbox{\strut #1~(last)} }
\cs_new:Npn \tobi_singlemenu:n #1 { \fbox{\strut #1~(single)} }
\ExplSyntaxOff

\setlength{\parskip}{1cm}
\begin{document}
\menu{1,2,3,4}

\menu{Single Element}

\menu{A,B,C,D,E}

\setmenuseparator{/}
\menu{C:/Nutzer und Einstellungen/Desktop/Test}

\menu*{C:\Nutzer und Einstellungen\Desktop\Test} 
\end{document}

You have only to give sensible definitions to the six functions \tobi_Xmenu.

EDIT: I've added support also for \ as separator; it should be called as \menu* (and doesn't need a previous \setmenuseparator).


Important change

Due to the changes made to expl3, here's a new (cleaned up) version of the code above.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\seq_new:N \l_tobi_menu_seq
\tl_new:N \l_tobi_sep_tl
\tl_set:Nn \l_tobi_sep_tl { , } % default
\tl_const:Nx \c_tobi_backslash_tl { \cs_to_str:N \\ }

\NewDocumentCommand{\setmenuseparator}{ m }
 {
  \tl_set:Nn \l_tobi_sep_tl { #1 }
 }
\NewDocumentCommand{\menu}{ s m }
 {
  \IfBooleanTF{#1}
   {
    \tobi_menu_process_rescan:n { #2 }
   }
   {
    \tobi_menu_process:n { #2 }
   }
 }

\cs_new:Npn \tobi_menu_process:n #1
 {
  \seq_set_split:NVn \l_tobi_menu_seq \l_tobi_sep_tl { #1 }
   \tobi_premenu:
   \int_case:nnF { \seq_count:N \l_tobi_menu_seq }
     {
      { 0 } { EMPTY }
      { 1 } { \tobi_singlemenu:n { \seq_map_function:NN \l_tobi_menu_seq \use:n } }
     }
     {
      \seq_pop_left:NN \l_tobi_menu_seq \l_tmpa_tl
      \seq_pop_right:NN \l_tobi_menu_seq \l_tmpb_tl
      \tobi_firstmenu:n { \l_tmpa_tl}
      \seq_map_inline:Nn \l_tobi_menu_seq { \tobi_midmenu:n { ##1 } }
      \tobi_lastmenu:n { \l_tmpb_tl }
     }
   \tobi_postmenu:
  }
\cs_new_protected:Npn \tobi_menu_process_rescan:n #1
 {
  \group_begin:
  \tl_set_eq:NN \l_tobi_sep_tl \c_tobi_backslash_tl
  \tl_set_rescan:Nnn \l_tmpa_tl {\char_set_catcode_other:N \\ } { #1 }
  \tobi_menu_process:V \l_tmpa_tl
  \group_end:
 }
\cs_generate_variant:Nn \seq_set_split:Nnn {NV}
\cs_generate_variant:Nn \tobi_menu_process:n {V}

\cs_new_protected:Npn \tobi_premenu: { \fbox{\strut pre} }
\cs_new_protected:Npn \tobi_postmenu: { \fbox{\strut post} }
\cs_new_protected:Npn \tobi_firstmenu:n #1 { \fbox{\strut #1~(first)} }
\cs_new_protected:Npn \tobi_midmenu:n #1 { \fbox{\strut #1~(mid)} }
\cs_new_protected:Npn \tobi_lastmenu:n #1 { \fbox{\strut #1~(last)} }
\cs_new_protected:Npn \tobi_singlemenu:n #1 { \fbox{\strut #1~(single)} }
\ExplSyntaxOff

\setlength{\parskip}{1cm}
\begin{document}
\menu{1,2,3,4}

\menu{Single Element}

\menu{A,B,C,D,E}

\setmenuseparator{/}
\menu{C:/Nutzer und Einstellungen/Desktop/Test}

\menu*{C:\Nutzer und Einstellungen\Desktop\Test} 
\end{document}

Tags:

Token Lists