4

Using egreg's answer here I tried to create my own advanced command with key=value-style optional argument splitting, which was successful.

Now how can I integrate the splitting of the mandatory argument, i.e. have

\mycoolcommand[...,...,...]{arg1,arg2} 

instead of

\mycoolcommand[...,...,...]{arg1}{arg2}

?

In the MWE below, the command I seek would be:

\newcoolbrace[...,...,...]{polp,root}

I usually use \SplitArgument, but I guess this will be a bit different here.

I know this might be a duplicate of How to split arguments in the context of `expl3`[, not `xparse`]? but since I am not familiar with Expl3 syntax, it is quite difficult for me to adapt the answer to my example.

MWE

\documentclass{article}



\usepackage[linguistics]{forest}

\usetikzlibrary{decorations.pathreplacing,decorations.text}



\NewDocumentCommand{\newcoolbraceaux}{mmmmm}{
    \draw[decorate,semithick,decoration={brace,amplitude=10pt,raise=#1pt}](#4)--(#5);
    \draw[decorate,semithick,decoration={raise=#2pt,text along path,text align=center,text=#3}](#4)--(#5);}

\ExplSyntaxOn
\NewDocumentCommand{\newcoolbrace}{O{}mm}
{
    \group_begin:
    \keys_set:nn { krebs/newcoolbrace } { #1 }
    \krebs_newcoolbrace:VVVnn
    \l__krebs_newcoolbrace_braise_tl
    \l__krebs_newcoolbrace_traise_tl
    \l__krebs_newcoolbrace_text_tl
    {#2}
    {#3}
    \group_end:
}
\keys_define:nn { krebs/newcoolbrace }
{
    braise .tl_set:N = \l__krebs_newcoolbrace_braise_tl,
    traise .tl_set:N = \l__krebs_newcoolbrace_traise_tl,
    text .tl_set:N = \l__krebs_newcoolbrace_text_tl,
    braise .initial:n = 36,
    traise .initial:n = 54,
    text .initial:n = ~,
}

\cs_set_eq:NN \krebs_newcoolbrace:nnnnn \newcoolbraceaux

\cs_generate_variant:Nn \krebs_newcoolbrace:nnnnn { VVV }
\ExplSyntaxOff

\begin{document}
    
\begin{forest}
    [nP[~][n
    [n][asp
    [~][asp
    [asp][pol,name=polp
    [~][pol
    [pol][vP
    [\textit{x},name=v][v′
    [v][root,name=root]]]]]]]]]
    \newcoolbrace[braise=26,traise=44,text=Unaccusative]{polp}{root}
\end{forest}
    
\end{document}
7
  • 2
    I’m not sure that \newcoolbrace[…]{x,y} is clearer than \newcoolbrace[…]{x}{y}. The two arguments are both required and they should follow a precise order, so they’re best kept separate.
    – egreg
    Commented Dec 29, 2022 at 9:43
  • 2
    You should use text={#3} instead of text=#3 or else your text can't contain a comma or equals sign (at least without an additional set of braces).
    – Skillmon
    Commented Dec 29, 2022 at 12:21
  • @Skillmon Thanks for this, I for sure need spaces and might need commas so it is really good to know! Commented Dec 29, 2022 at 13:17
  • 1
    You can change your \newcoolbrace to \NewDocumentCommand{\newcoolbrace}{O{}m} and then replace your {#2} and {#3} with {\clist_item:nn {#2}{1}} and {\clist_item:nn {#2}{2}} respectively.
    – Scott H.
    Commented Dec 29, 2022 at 19:12
  • 1
    @ScottH. seems wasteful and requires to change the underlying internal macro in addition to the document level. Imho, \SplitArgument should be preferred.
    – Skillmon
    Commented Dec 30, 2022 at 18:18

2 Answers 2

6

You could do like this:

\documentclass{article}
\usepackage[linguistics]{forest}

\usetikzlibrary{decorations.pathreplacing,decorations.text}



\NewDocumentCommand{\newcoolbraceaux}{mmm>{\SplitArgument{1}{,}}m}{%
  \newcoolbraceauxa{#1}{#2}{#3}#4%
}
\NewDocumentCommand{\newcoolbraceauxa}{mmmmm}{%
  \draw[
    decorate,
    semithick,
    decoration={brace,amplitude=10pt,raise=#1pt},
  ](#4)--(#5);%
  \draw[
    decorate,
    semithick,
    decoration={
      raise=#2pt,
      text along path,
      text align=center,
      text={#3},
    },
  ](#4)--(#5);%
}

\ExplSyntaxOn
\NewDocumentCommand{\newcoolbrace}{O{}m}
  {
    \group_begin:
    \keys_set:nn { krebs/newcoolbrace } { #1 }
    \krebs_newcoolbrace:VVVnn
    \l__krebs_newcoolbrace_braise_tl
    \l__krebs_newcoolbrace_traise_tl
    \l__krebs_newcoolbrace_text_tl
    {#2}
    \group_end:
  }
\keys_define:nn { krebs/newcoolbrace }
  {
    braise .tl_set:N = \l__krebs_newcoolbrace_braise_tl,
    traise .tl_set:N = \l__krebs_newcoolbrace_traise_tl,
    text .tl_set:N = \l__krebs_newcoolbrace_text_tl,
    braise .initial:n = 36,
    traise .initial:n = 54,
    text .initial:n = ~,
  }

\cs_set_eq:NN \krebs_newcoolbrace:nnnnn \newcoolbraceaux

\cs_generate_variant:Nn \krebs_newcoolbrace:nnnnn { VVV }
\ExplSyntaxOff

\begin{document}
    
\begin{forest}
    [nP[~][n
    [n][asp
    [~][asp
    [asp][pol,name=polp
    [~][pol
    [pol][vP
    [\textit{x},name=v][v'
    [v][root,name=root]]]]]]]]]
    \newcoolbrace[braise=26,traise=44,text=Unaccusative]{polp,root}
\end{forest}
    
\end{document}

However, I'm not sure this is better than the previous version with two mandatory arguments as the two arguments mean different things (the start node and the end node).

A different strategy could be to add them to the list of key-value pairs, with the difference that the keys start and end must be specified.

\documentclass{article}
\usepackage[linguistics]{forest}

\usetikzlibrary{decorations.pathreplacing,decorations.text}

\NewDocumentCommand{\newcoolbraceaux}{mmmmm}{%
  \draw[
    decorate,
    semithick,
    decoration={brace,amplitude=10pt,raise=#1pt},
  ](#4)--(#5);%
  \draw[
    decorate,
    semithick,
    decoration={
      raise=#2pt,
      text along path,
      text align=center,
      text={#3},
    },
  ](#4)--(#5);%
}

\ExplSyntaxOn
\NewDocumentCommand{\newcoolbrace}{m}
  {
    \group_begin:
    \keys_set:nn { krebs/newcoolbrace } { #1 }
    \bool_lazy_or:nnTF
      { \quark_if_nil_p:V \l__krebs_newcoolbrace_start_tl }
      { \quark_if_nil_p:V \l__krebs_newcoolbrace_end_tl }
      {
       \msg_error:nn { krebs/newcoolbrace } { missing-bounds }
      }
      {
        \krebs_newcoolbrace:VVVVV
          \l__krebs_newcoolbrace_braise_tl
          \l__krebs_newcoolbrace_traise_tl
          \l__krebs_newcoolbrace_text_tl
          \l__krebs_newcoolbrace_start_tl
          \l__krebs_newcoolbrace_end_tl
      }
    \group_end:
  }
\keys_define:nn { krebs/newcoolbrace }
  {
    braise .tl_set:N = \l__krebs_newcoolbrace_braise_tl,
    traise .tl_set:N = \l__krebs_newcoolbrace_traise_tl,
    text .tl_set:N = \l__krebs_newcoolbrace_text_tl,
    start .tl_set:N = \l__krebs_newcoolbrace_start_tl,
    end .tl_set:N = \l__krebs_newcoolbrace_end_tl,
    braise .initial:n = 36,
    traise .initial:n = 54,
    text .initial:n = ~,
    start .initial:n = \q_nil,
    end .initial:n = \q_nil,
  }

\cs_set_eq:NN \krebs_newcoolbrace:nnnnn \newcoolbraceaux
\cs_generate_variant:Nn \krebs_newcoolbrace:nnnnn { VVVVV }

\msg_new:nnnn { krebs/newcoolbrace } { missing-bounds }
  {
    Missing~start~or~end
  }
  {
   You~must~specify~'start=<label>'~and~'end=<label>'
  }
\ExplSyntaxOff

\begin{document}
    
\begin{forest}
    [nP[~][n
    [n][asp
    [~][asp
    [asp][pol,name=polp
    [~][pol
    [pol][vP
    [\textit{x},name=v][v'
    [v][root,name=root]]]]]]]]]
    \newcoolbrace{braise=26,traise=44,text=Unaccusative,start=polp,end=root}
\end{forest}
    
\end{document}

Now the whole argument is mandatory, because there must be both start and end. Try omitting one of them and you'll see the error message popping out.

enter image description here

1
4

This answer is more or less identical to @egreg's, namely it uses \SplitArgument, but it also uses expkv-cs for the key=value argument. Setting up your key=value interface if all you want to use it for is to forward the values to another macro is a lot easier with it.

After the definition of \newcoolbracekv with \ekvcSplitAndForward all the keys are turned into arguments for \newcoolbraceaux in the order the keys were specified.

\documentclass{article}

\usepackage[linguistics]{forest}

\usetikzlibrary{decorations.pathreplacing,decorations.text}

\NewDocumentCommand{\newcoolbraceaux}{mmmmm}{
    \draw[decorate,semithick,decoration={brace,amplitude=10pt,raise=#1pt}](#4)--(#5);
    \draw[decorate,semithick,decoration={raise=#2pt,text along path,text align=center,text={#3}}](#4)--(#5);}

\usepackage{expkv-cs}
\NewDocumentCommand\newcoolbrace { O{} >{\SplitArgument{1}{,}}m }
  {\newcoolbracekv{#1}#2}
\ekvcSplitAndForward\newcoolbracekv\newcoolbraceaux
  {
     braise = 36
    ,traise = 54
    ,text = {} % defaults to an empty value
  }

\begin{document}

\begin{forest}
    [nP[~][n
    [n][asp
    [~][asp
    [asp][pol,name=polp
    [~][pol
    [pol][vP
    [\textit{x},name=v][v′
    [v][root,name=root]]]]]]]]]
    \newcoolbrace[braise=26,traise=44,text=Unaccusative]{polp,root}
\end{forest}

\end{document}
1
  • Your package seems to simplify life quite a lot. I'm for now trying to process and assimilate the standard way and I don't want to mix things up, but after a while and when the next version is out, I'm sure I'll have fun messing around with it. Thanks ;) Commented Jan 22, 2023 at 4:52

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .