programming add tag
निरंजन
I am trying to re-implement a package with a command `\foo` which I am guessing has 2 APIs (I am not very sure, maybe there are other ways of achieving it). The user-side API of the command is like this:

### Case 1
---

```
\foo
\baz abc def::% Marks the end of the argument, coded with u{::} in xparse.
\foobaz ghi jkl::
\bazfoo mno pqr::
\oof
```

This is the default behavior. The same behavior is repeated with the following, but with a value `abc` to a key named `foo`. We can interpret that the initial value of the key foo is `abc`.

### Case 2
---

```
\foo[foo=abc]
\baz abc def::
\foobaz ghi jkl::
\bazfoo mno pqr::
\oof
```

### Case 3
---

```
\foo[foo=def]
abc[ghi/mno] def[ghi/jkl/pqr]
\oof
```

I have coded the simple-most case as follows:

```
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{ \foo }{ }{%
  \begingroup
  \NewDocumentCommand{ \baz }{ u{::} }{%
    \noindent{##1}\par
  }%
  \NewDocumentCommand{ \foobaz }{ u{::} }{%
    \noindent{##1}\par
  }%
  \NewDocumentCommand{ \bazfoo }{ u{::} }{%
    \noindent{##1}\par
  }%
}
\NewDocumentCommand{ \oof }{ }{%
  \endgroup
}

\begin{document}
\foo
\baz abc def::
\foobaz ghi jkl::
\bazfoo mno pqr::
\oof

\foo
\baz abc def::
\foobaz ghi jkl::
\bazfoo mno pqr::
\oof
\end{document}
```

![Screenshot_2022-06-26_12-01-55.png](/image?hash=9a36f63ad8e7298142d8623a3aeaf4e69ad978f1b557d41a67e7ecd7d0a669d2)

For developing keys, I will use my favorite pair of packages `expkv-opt,expkv-def`. The following should give us case 2.

```
\documentclass{article}
\usepackage{xparse}
\usepackage{expkv-def,expkv-opt}
\newif\ifabc
\def\abc{abc}
\def\pqr{pqr}
\ekvdefinekeys{foo}{
  choice  foo    = {%
    abc          = {%
      \abctrue
      \edef\fookey{\abc}%
    },%
    pqr          = {%
      \abcfalse
      \edef\fookey{\pqr}%
    }%
  }
}
\ekvoProcessLocalOptions{foo}
\NewDocumentCommand{ \foo }{ O{foo=abc} }{%
  \begingroup
  \ekvset{foo}{#1}%
  \ifx\fookey\abc
    \NewDocumentCommand{ \baz }{ u{::} }{%
      \noindent{##1}\par
    }%
    \NewDocumentCommand{ \foobaz }{ u{::} }{%
      \noindent{##1}\par
    }%
    \NewDocumentCommand{ \bazfoo }{ u{::} }{%
      \noindent{##1}\par
    }%
  \else
    % The code for `def' value of foo should come here.
  \fi
}
\NewDocumentCommand{ \oof }{ }{%
  \endgroup
}

\begin{document}
\section*{Case: 1}
\foo
\baz abc def::
\foobaz ghi jkl::
\bazfoo mno pqr::
\noindent The current value of \texttt{foo} is:
\texttt{\fookey}
\oof

\section*{Case: 2}
\foo[foo=abc]
\baz abc def::
\foobaz ghi jkl::
\bazfoo mno pqr::
\noindent The current value of \texttt{foo} is:
\texttt{\fookey}
\oof

\section*{Case: 3}
\foo[foo=pqr]
abc[ghi/mno] def[ghi/jkl/pqr]
\noindent The current value of \texttt{foo} is:
\texttt{\fookey}
\oof
\end{document}
```

![Screenshot_2022-06-26_17-32-04.png](/image?hash=1ce6f3baa001f91cb866956756865fd81f71e6b58e0d79135f99f7201cc81bb4)

So I think in order to equate the output of case 3 with the rest of the two; I will have to have an alternate argument structure for `\foo`. Is there any other way to get it?
Top Answer
frougon
The input syntax is very weird. As I wrote in the comments, please first think if it *really* isn't possible to support an input syntax that is more common in the LaTeX world.

I provide the following code to make it clear how the special parsing can be triggered when we detect the special case `foo=def` in the options of `\foo`. Consider it as code golf!

Note: I used `l3keys` because I am familiar with it and not with `expkv`; I have nothing particular against the latter. :)

```
\documentclass{article}

\ExplSyntaxOn

\tl_new:N \l_mypkg_fookey_tl % optional here because of the \keys_define:nn

\keys_define:nn { mypkg / foo }
  {
    foo .tl_set:N = \l_mypkg_fookey_tl, % simply store the value
    foo .value_required:n = true,
    foo .initial:n = abc,
  }

\NewExpandableDocumentCommand \useFooKey { }
  {
    \tl_use:N \l_mypkg_fookey_tl
  }

\NewDocumentCommand \foo { O{ } } % no options by default (different from yours)
  {
    \mypkg_foo:n {#1}
  }

\cs_new_protected:Npn \mypkg_foo:n #1
  {
    \group_begin:
    \keys_set:nn { mypkg / foo } {#1}

    \tl_if_eq:NnT \l_mypkg_fookey_tl { def } % special case for this value
      {
        \__mypkg_parse_special:w
      }
  }

% Common formatting code for all cases
\cs_new_protected:Npn \__mypkg_format_line:n #1
  {
    \par \noindent { #1 \par }
  }

% Works well for ~ (catcode 10 here) because of a special rule regarding
% characters whose catcode is 10 (TeXbook p. 47, dangerous bend number 4).
\cs_new_protected:Npn \__mypkg_parse_special:w #1[#2/#3]~#4[#5/#6]
  {
    \__mypkg_format_line:n { #1~ #4 }
    \__mypkg_format_line:n { #2~ #5 }
    \__mypkg_format_line:n { #3~ #6 }
  }

\use:e % special trickery because the ':' we want in the delimiter must have
  {    % catcode 12 (after \ExplSyntaxOn, ':' has catcode 11)
    \exp_not:n { \cs_new_protected:Npn \__mypkg_parse_simple_delimited:w #1 }
    \c_colon_str \c_colon_str
  }
  {
    \__mypkg_format_line:n {#1}
  }

\cs_new_eq:NN \baz    \__mypkg_parse_simple_delimited:w
\cs_new_eq:NN \foobaz \__mypkg_parse_simple_delimited:w
\cs_new_eq:NN \bazfoo \__mypkg_parse_simple_delimited:w
\cs_new_eq:NN \oof    \group_end:

\ExplSyntaxOff

\begin{document}

\section*{Case: 1}
\foo
\baz abc def::
\foobaz ghi jkl::
\bazfoo mno pqr::
\noindent The current value of \texttt{foo} is: \useFooKey
\oof

\section*{Case: 2}
\foo[foo=abc]% the space token would do nothing in vertical mode, but...
\baz abc def::
\foobaz ghi jkl::
\bazfoo mno pqr::
\noindent The current value of \texttt{foo} is: \useFooKey
\oof

\section*{Case: 3}
\foo[foo=def]% simpler with the %, otherwise we could use e.g. \tl_trim_spaces:n
abc[ghi/mno] def[jkl/pqr]
\noindent The current value of \texttt{foo} is: \useFooKey
\oof

\end{document}
```

![image.png](/image?hash=bf7b0876299a6b1ff7e2acb002d459c5d2d2b5f9febac893a3e72cad62dead5e)

Regarding this part:  
  
```  
\NewExpandableDocumentCommand \useFooKey { }
  {
    \tl_use:N \l_mypkg_fookey_tl
  }  
```  
  
I used `\NewExpandableDocumentCommand` in case you want to use `\useFooKey` in expansion-only contexts (inside `\edef`, `\write`, `\csname`, `\numexpr`... similarly, expandable material works fine inside the argument of `siunitx` macros like `\num`, and there are many more places like that). But this is not a choice **I** can make because I don't know what kind of tokens `\l_mypkg_fookey_tl` will legitimately contain.  All I know is that `\tl_use:N` is expandable, thus works in expansion-only contexts (and can even be omitted because `tl` vars are simple non-`\protected` macros).

*I can't know if the material resulting from the expansion of `\l_mypkg_fookey_tl` is designed to be used in  expansion-only contexts: only you, the API designer, can know.*
  
For instance, if `\l_mypkg_fookey_tl` is supposed to contain non-expandable code fragments (e.g., containing `\def`, `\setcounter` or `\addtocounter` calls, markup like `\section{...}`...), probably the `\NewExpandableDocumentCommand` should be replaced with `\NewDocumentCommand` in order to prevent the expansion of `\useFooKey` in certain kinds of expansion-only contexts (inside `\edef`, `\xdef`, `\expanded`, `\message`, `\errmessage`, `\special`, `\mark`, `\marks`; when writing the token list for `\write` to a file; when looking ahead in an alignment for `\noalign` or `\omit`; plus everything that is based on one of these mechanisms of the TeX engine).  
  
Also, I hesitated between “purity” and efficiency here: the `\NewExpandableDocumentCommand` clearly marks `\useFooKey` as belonging to the document-level layer, however we don't need the (formerly from `xparse`, now documented in [usrguide3.pdf](http://mirrors.ctan.org/macros/latex-dev/base/usrguide3.pdf)) heavy `\NewExpandableDocumentCommand` machinery here, since `\useFooKey` is a trivial macro that takes no argument. The simple fact of using `\NewExpandableDocumentCommand` probably makes `\useFooKey` a bit slow.  
  
So, an alternative which I didn't put in the code because I wanted to keep it short and readable, but is at least as good in my opinion, is:  
  
```  
\cs_new:Npn \mypkg_use_foo_key:
  {
    \tl_use:N \l_mypkg_fookey_tl
  }

\cs_new_eq:NN \useFooKey \mypkg_use_foo_key:
```  
  
or, more generically:  
  
```  
\cs_new:Npn \mypkg_use_key:n #1
  {
    \tl_use:c { l_mypkg_#1key_tl }
  }

\cs_new_eq:NN \useKey \mypkg_use_key:n
```  
  
(in which case you would input `\useKey{foo}` instead of `\useFooKey`).  
  
Again, the `\cs_new:Npn` used here was chosen under the assumption that `\useFooKey` or `\useKey{foo}` should expand in expansion-only contexts; if `\useFooKey` or `\useKey{foo}` should be expanded only when TeX performs its full expansion + execution interleaved process (typically, when typesetting), use `\cs_new_protected:Npn` or `\cs_new_protected:Nn` instead.

Enter question or answer id or url (and optionally further answer ids/urls from the same question) from

Separate each id/url with a space. No need to list your own answers; they will be imported automatically.