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