I have this set up:
%%% some keys
\pgfkeys{/mystuff/first color/.code=\gdef\myfirstcolor{#1}}
\pgfkeys{/mystuff/first color/.default=black}
\pgfkeys{/mystuff/last color/.code=\gdef\mylastcolor{#1}}
\pgfkeys{/mystuff/last color/.default=black}
%%% main cmd
\newcommand{\mylist}[2][]{% aaa [opts] : bbb,...
\foreach \temp in {#2}{\expandafter\mysplit\temp\relax
first is \textcolor{\myfirstcolor}{\texttt{\myfirst}},
last is \textcolor{\mylastcolor}{\texttt{\mylast}} \par}
}
%%% helper cmds
\def\mysplit#1:#2\relax{%
\myfirstandopts#1[]\relax%
\gdef\mylast{#2}
}
\def\myfirstandopts#1[#2]#3\relax{%
\gdef\myfirst{#1}
\pgfkeys{/mystuff/.cd,first color,last color,#2}
}
The idea is that I give a list and can apply some keys to each of the items:
\mylist{Hello:World,
Hello[first color=blue]:World,
Hello[last color=red]:World}
If I want to give more than one option, it does not work:
\mylist{Hello [first color=blue,last color=red]:World}
! Paragraph ended before \mysplit was complete.
The problem is in the foreach. It gets passed:
Hello [first color=blue,last color=red]:World
which are two items in the foreach list and the first one,
Hello [first color=blue
does not agree with the definition.
This is easily solved by bracing the key list:
\mylist{Hello[{first color=blue,last color=red}]:World}
However, this is not so intuitive, since normally it is not necessary to brace the whole list of keys. In this case, it is just because internally it is placed in a list separated with commas. So, I should like to add the braces automatically.
I have tried to loop over the key list and replace [
by [{
and ]
by }]
.
For this I have used the following macros:
\newcount\mycount
\def\mybracedkeys#1{\def\mybrcdkeys{}\myloop#1\relax}%% looping macro by David Carlisle
\def\myloop#1{%
\ifx\relax#1% check if \relax is reached, if so, do nothing
\else
\mycount`#1 % store the ascii code of the character in the counter
\ifnum\mycount=91% ascii code of [
\expandafter\def\expandafter\mybrcdkeys\expandafter{\mybrcdkeys [\bgroup}%
\else
\ifnum\mycount=93% ascii code of ]
\expandafter\def\expandafter\mybrcdkeys\expandafter{\mybrcdkeys \egroup]}%
\else
\expandafter\def\expandafter\mybrcdkeys\expandafter{\mybrcdkeys #1}
\fi
\fi
\expandafter\myloop\fi}
and redefined \mylist
:
\renewcommand{\mylist}[2][]{% aaa [opts] : bbb,...
\mybracedkeys{#2}
\foreach \temp in \mybrcdkeys{\expandafter\mysplit\temp\relax
first is \textcolor{\myfirstcolor}{\texttt{\myfirst}},
last is \textcolor{\mylastcolor}{\texttt{\mylast}} \par}
}
This still does not work yielding the same error, which is not because we have lost the spaces, since the same happens with:
\pgfkeys{/mystuff/.cd,\bgroup first color=blue,last color=red\egroup}
I found this explanation by @wipet in this answer:
The { and } (more exactly the tokens with catcode 1 and 2) have four different meanings in TeX:
- they are the syntactic part of macro definitions:
\def\foo...{...}
- each sequence of tokens scanned as single token list in TeX (i.e. in the parameter of a macro, inside macro definitions...) have to be balanced text by these tokens:
\macro{param{e}ter}.
- they are a part of several TeX primitive constructions, for example
\hbox...{...}, $e^{...}$, \write...{...}
.- when they are used without any context mentioned above they open and close the group.
The
\bgroup
and\egroup
are declared by\let\bgroup={ \let\egroup=}
and you can replace{
and}
by\bgroup
and\egroup
only in the third and fourth meaning mentioned above.
I guess that in this case we are in the second meaning so it can not be done.
Is there some other way to get it working?
Preferably without expl3
.
Although, after so many complications (and the spaces issue has not yet been addressed), having to brace the key list does not seem so bad.
The complete code:
\documentclass{article}
\usepackage{tikz}
\begin{document}
\section*{Bracing a list within a list}
%%% some keys
\pgfkeys{/mystuff/first color/.code=\gdef\myfirstcolor{#1}}
\pgfkeys{/mystuff/first color/.default=black}
\pgfkeys{/mystuff/last color/.code=\gdef\mylastcolor{#1}}
\pgfkeys{/mystuff/last color/.default=black}
%%% main cmd
\newcommand{\mylist}[2][]{% aaa [opts] : bbb,...
\foreach \temp in {#2}{\expandafter\mysplit\temp\relax
first is \textcolor{\myfirstcolor}{\texttt{\myfirst}},
last is \textcolor{\mylastcolor}{\texttt{\mylast}} \par}
}
%%% helper cmds
\def\mysplit#1:#2\relax{%
\myfirstandopts#1[]\relax%
\gdef\mylast{#2}
}
\def\myfirstandopts#1[#2]#3\relax{%
\gdef\myfirst{#1}
\pgfkeys{/mystuff/.cd,first color,last color,#2}
}
\begin{verbatim}
\mylist{Hello:World,
Hello[first color=blue]:World,
Hello[last color=red]:World}
\end{verbatim}
\mylist{Hello:World,
Hello[first color=blue]:World,
Hello[last color=red]:World}
\begin{verbatim}
\mylist{Hello[{first color=blue,last color=red}]:World}
\end{verbatim}
\mylist{Hello[{first color=blue,last color=red}]:World}
\newcount\mycount
\def\mybracedkeys#1{\def\mybrcdkeys{}\myloop#1\relax}%% looping macro by David Carlisle
\def\myloop#1{%
\ifx\relax#1% check if \relax is reached, if so, do nothing
\else
\mycount`#1 % store the ascii code of the character in the counter
\ifnum\mycount=91% ascii code of [
\expandafter\def\expandafter\mybrcdkeys\expandafter{\mybrcdkeys [\bgroup}%
\else
\ifnum\mycount=93% ascii code of ]
\expandafter\def\expandafter\mybrcdkeys\expandafter{\mybrcdkeys \egroup]}%
\else
\expandafter\def\expandafter\mybrcdkeys\expandafter{\mybrcdkeys #1}
\fi
\fi
\expandafter\myloop\fi}
\renewcommand{\mylist}[2][]{% aaa [opts] : bbb,...
\mybracedkeys{#2}
\foreach \temp in \mybrcdkeys{\expandafter\mysplit\temp\relax
first is \textcolor{\myfirstcolor}{\texttt{\myfirst}},
last is \textcolor{\mylastcolor}{\texttt{\mylast}} \par}
}
does not work
\verb|\mylist{Hello[first color=blue,last color=red]:World}|
does not work
\verb|\pgfkeys{/mystuff/.cd,\bgroup first color=blue,last color=red\egroup}|
%https://tex.stackexchange.com/q/1930/53956
\end{document}