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
\bgroupand\egroupare declared by\let\bgroup={ \let\egroup=}and you can replace{and}by\bgroupand\egrouponly 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}


\edef? There could be keys liketext=\textbf{some text}. – Raoul Kessels Jan 31 '23 at 22:56\protected \expandafter\def \expandafter\textbf \expandafter{\csname textbf \endcsname}. We are using this: 1.\protected\defmacros are not expanded during\edef. 2. LaTeX defines its internal\textbfas\textbf space. – wipet Feb 02 '23 at 12:16