# key={v,a,l} argument separated by commas in xparse [duplicate]

I'm trying to create a command to show the distributive property of multiplication (for school effects) using tikzmarks and xparse. So far so good, the code is a bit long and I would like to know if I can accomplish this in a more summarized way (the right way ...my code is not very neat).

The subject is like this, I have defined a command \tikzmkpd[...]{...}{...} which receives an optional argument [key=val] and two mandatory arguments separated by commas. With the mandatory arguments it works perfect, the problem I have when trying to implement [key=val]. My idea is that [key=val]works like this:

config-A={yshift={#1},end-yshift={#1,#2,#3},color={#1,#2,#3},distance={#1,#2,#3}},
config-B={yshift={#1},end-yshift={#1,#2,#3},color={#1,#2,#3},distance={#1,#2,#3}}


For now of config-A I only have written the yshift and end-yshift keys that control the spaces of the beginning and end of the arrows that I am drawing with tikz.

My problem is with the key end-yshift={#1,#2,#3} which calls the command auxiliary \endyshift{...}. By using the command \endyshift{...} directly it accepts:

\endyshift{#1}
\endyshift{#1,#2}
\endyshift{#1,#2,#3}


but if I use it in [key=val] it becomes obligatory to pass the three parameters:

end-yshift={#1}       % error
end-yshift={#1,#2}    % error
end-yshift={#1,#2,#3} % correct


I've tried {} or ,,  as "empty" arguments, but it doesn't work, besides, I think the code is pretty long and maybe it can be achieved using another strategy.

Here's my question then, can you pass key={v,a,l} where v, a and l execute different code?

This is my example document:

\documentclass{article}
\usepackage{xparse,tikz,etoolbox}
\usetikzlibrary{arrows.meta,bending,calc,tikzmark}
\setlength{\parindent}{0pt} % just for the example
\pagestyle{empty}
% short :)
\newcommand*{\TkM}[2]{\tikzmarknode{#1}{#2}}
% active : for expl3
\makeatletter
\def\@colon{:}
\ExplSyntaxOn

\keys_define:nn { tikzmkpd }
{
config-A  .code:n = { \keys_set:nn { tikzmkpd / config-A } { #1 } },
}
% config-A={yshift={#1},end-yshift={#1,#2,#3},color={#1,#2,#3},distance={#1,#2,#3}}
\keys_define:nn { tikzmkpd / config-A }
{
yshift     .dim_set:N = \l_yshift_A_dim,
yshift     .initial:n = 1.7ex,
yshift-AC  .dim_set:N = \l_yshift_AC_dim,
yshift-AC  .initial:n = 1.7ex,
yshift-AD  .initial:n = 1.7ex,
yshift-AE  .dim_set:N = \l_yshift_AE_dim,
yshift-AE  .initial:n = 1.7ex,
distan-AC  .dim_set:N = \l_distance_AC_dim,
distan-AC  .initial:n = 1.00ex,
distan-AD  .initial:n = 2.00ex,
distan-AE  .dim_set:N = \l_distance_AE_dim,
distan-AE  .initial:n = 3.00ex,
colour-AC  .tl_set:N  = \l_color_AC_tl,
colour-AC  .initial:n = red,
colour-AD  .initial:n = green,
colour-AE  .tl_set:N  = \l_color_AE_tl,
colour-AE  .initial:n = blue,
end-yshift .code:n    = \endyshift{#1},
%  yshiftall  .meta:n    = {end-yshift={#1}, yshift = #1,}, % not work
}

\NewDocumentCommand { \endyshift } { m }
{
\int_case:nnF { \clist_count:n { #1 } }
{
{1}{\EndACshift {#1}} %
{3}{\EndAEshift {#1}} %
}{}
}

\NewDocumentCommand{ \EndACshift }{m}
{
\EndAC#1
}

\cs_new_protected:Npn \EndAC #1
{
\keys_set:nn { tikzmkpd / config-A }
{
yshift-AC = #1,
}
}

{
}

\cs_new_protected:Npn \EndAD #1 #2
{
\keys_set:nn { tikzmkpd / config-A }
{
yshift-AC = #1,
}
}

\NewDocumentCommand{\EndAEshift}{>{\SplitArgument{2}{,}}m}
{
\EndAE#1
}

\cs_new_protected:Npn \EndAE #1 #2 #3
{
\keys_set:nn { tikzmkpd / config-A }
{
yshift-AC = #1,
yshift-AE = #3,
}
}

% case 1: a(c+d)  A1(B1+B2)
\cs_new_protected:Npn \_case_one:n
{
\draw[->,color=\l_color_AC_tl] ([yshift=\l_yshift_A_dim]$(pic ~ cs\@colon\csuse{A1})$)
to[out=60,in=120,distance=\l_distance_AC_dim]
([yshift=\l_yshift_AC_dim]$(pic ~ cs\@colon\csuse{B1})$);
\draw[->,,color=\l_color_AD_tl] ([yshift=\l_yshift_A_dim]$(pic ~ cs\@colon\csuse{A1})$)
([yshift=\l_yshift_AD_dim]$(pic ~ cs\@colon\csuse{B2})$);
}
% case 2: a(c+d+e) A1(B1+B2+B3)
\cs_new_protected:Npn \_case_two:n
{
\_case_one:n
\draw[->,color=\l_color_AE_tl] ([yshift=\l_yshift_A_dim]$(pic ~ cs\@colon\csuse{A1})$)
to[out=60,in=120,distance=\l_distance_AE_dim]
([yshift=\l_yshift_AE_dim]$(pic ~ cs\@colon\csuse{B3})$);
}
% case 3: (a+b)(c+d) (A1+A2)(B1+B2)
\cs_new_protected:Npn \_case_tree:n
{
\_case_one:n
\draw[->] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{A2})$)
to[out=-60,in=-120,distance=1ex] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{B1})$);
\draw[->] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{A2})$)
to[out=-60,in=-120,distance=2ex] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{B2})$);
}
% case 4: (a+b)(c+d+e) (A1+A2)(B1+B2+B3)
\cs_new_protected:Npn \_case_fourth:n
{
\_case_tree:n
\draw[->,color=\l_color_AE_tl] ([yshift=1.7ex]$(pic ~ cs\@colon\csuse{A1})$)
to[out=60,in=120,distance=3.0ex] ([yshift=1.7ex]$(pic ~ cs\@colon\csuse{B3})$);
\draw[->] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{A2})$)
to[out=-60,in=-120,distance=3ex] ([yshift=-0.5ex]$(pic ~ cs\@colon\csuse{B3})$);
}
% user command
\NewDocumentCommand{\tikzmkpd}{O{} m m}
{
\group_begin:
\IfNoValueF { #1 } { \keys_set:nn { tikzmkpd }{ #1 } }
\foreach \x [count=\n] in {#2} { \csxdef{A\n}{\x} }%  save in A<n>
\foreach \y [count=\m] in {#3} { \csxdef{B\m}{\y} }%  save in B<m>
\tikzset{>={Straight ~ Barb[length=1.5pt,round,bend]}}%
\begin{tikzpicture}[overlay,remember ~ picture]%
\bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 1 }   &&
\int_compare_p:n { \clist_count:n { #3 } = 2 } } { \_case_one:n }{  }
\bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 1 }   &&
\int_compare_p:n { \clist_count:n { #3 } = 3 } } { \_case_two:n }{  }
\bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 2 }   &&
\int_compare_p:n { \clist_count:n { #3 } = 2 } } { \_case_tree:n }{  }
\bool_if:nTF { \int_compare_p:n { \clist_count:n { #2 } = 2 }   &&
\int_compare_p:n { \clist_count:n { #3 } = 3 } } { \_case_fourth:n }{  }
\end{tikzpicture}
\group_end:
}
\ExplSyntaxOff
\makeatother
\begin{document}
\section{case 1}

$\TkM{A}{2a^2}(\TkM{B}{b}+\TkM{D}{3q})$%
\tikzmkpd[config-A={end-yshift={2ex,2ex,2ex}}]{A}{B,D}

\section{case 2}

$\TkM{a1}{2p}\left(\TkM{b1}{3q}+\TkM{c1}{4r}+\TkM{d1}{1}\right)$
\tikzmkpd{a1}{b1, c1, d1}

\section{case 3}

$(\TkM{X1}{2a^2}+\TkM{X2}{n})(\TkM{Y1}{b}+\TkM{Y2}{3q})$%
\tikzmkpd{X1,X2}{Y1,Y2}

\section{case 4}

$(\TkM{a}{x}-\TkM{b}{2})(\TkM{c}{x^2}+\TkM{d}{2x}+\TkM{e}{4})$
\tikzmkpd{a,b}{c, d, e}

\end{document}


And an example of the output that produces:

Any help is appreciated.

PS: The idea of the command is NOT to write the tikzpicture environment directly into the document.

• As far as I understand, the all that TikZ code isn't really relevant to the problem. Could you boil down the example to only the code that is necessary to understand the parameter parsing problem? – siracusa Jun 8 '19 at 9:51
• You can probably pass whatever you need as key={some,thing} by defining key with l3keys' .code:n or .tl_set:N, but then for the passed-in contents, you'll have to do the parsing work yourself (unless you delegate to l3keys again...). Wouldn't it be more natural to use hierarchical keys in the style mykey/mysubkey=... (see Sub-dividing keys in the l3keys documentation)? – frougon Jun 8 '19 at 10:33
• Unlike the others I feel you should drop xparse and etoolbox. That way you avoid mixing pgf keys with other keys. Of course, I do not fully understand what's going on here but the splitting of the arguments can be achieved with the /.list key handler. – user121799 Jun 8 '19 at 16:01
• Unfortunately I can't reduce the example code, I think the question would be misunderstood (writing using expl3 is always extensive). Going straight to pgfkeys is not an option for me, I don't know it well and it would cause me more problems (I'm new to tikz`), subdividing the keys seems a good idea to me, I'll try. – Pablo González L Jun 8 '19 at 18:18
• Reviewing well in the forum I found the answer (tex.stackexchange.com/a/423119/7832) , to the moderators I ask to leave this question as closed (duplicate) – Pablo González L Jun 9 '19 at 6:42