2

Say I want to define a command \defineaspect that takes a key/value option list to associate values with keys that are not known in advance. For example, to associate certain aspects with a list of persons, a use of that command might look like

\defineaspect{forename}{
    .print = {},
    sherlock = Sherlock,
    jim = James
}
\defineaspect{surname}{
    .print = \scshape,
    sherlock = Holmes,
    jim = Moriarty
}

sherlock and jim should then be treated kind of like primary keys in a database table with the various aspects being further columns in that table. Additionally, some actually predefined keys might occur in this list too, like .print in the example.

I had a glance at some of the packages for key/value processing, but as far as I can see having a lot of non-predefined keys is not a typical use case for these packages.

Is there a key/value package that provides easy handling of such keys? Or should I better use a custom parser that splits the list at , first and then parses each key/value pair separately?

5
  • 3
    These look like property lists to me: is every value 'some tokens to store'?
    – Joseph Wright
    Jul 7, 2019 at 8:48
  • 1
    Can't the parsing be easily done with l3keys using unknown .code:n = { ... } for the keys that aren't know in advance, and normal handling for the other keys?
    – frougon
    Jul 7, 2019 at 13:32
  • 2
    As Joseph said, something like \NewDocumentCommand \defineaspect { mm } { \prop_gset_from_keyval:cn { g_siracusa_#1_aspect_prop } {#2} } should do what you want. Jul 7, 2019 at 14:12
  • @frougon Thanks, this approach also seems to work. I misunderstood the documentation in that the unknown key were only meant for a custom error message.
    – siracusa
    Jul 8, 2019 at 0:34
  • There is no need to implement a parser splitting at , and parsing a key=value pair, \keyval_parse:NNn and \ekvparse (from expkv) do exactly that for you so that you can provide two macros which should be called each element of your key=value list (one if no value was given for an element, one if a value was given).
    – Skillmon
    Dec 6, 2020 at 13:37

1 Answer 1

3

You can exploit expl3's key-value system and the prop data type. To turn a key-value list into an expl3 property list you can use \prop_set_from_keyval:Nn. The function will get each key = value pair in #2 and add to the prop list in #1. A sample document in this case would be:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand \defineaspect { mm }
  {
    \prop_new:c { g__siracusa_#1_aspect_prop }
    \prop_gset_from_keyval:cn { g__siracusa_#1_aspect_prop } {#2}
  }
%
\tl_new:N \l__siracusa_tmpa_tl
\NewDocumentCommand \getaspect { mm }
  {
    \prop_get:cnNTF { g__siracusa_#1_aspect_prop } {#2} \l__siracusa_tmpa_tl
      { \tl_use:N \l__siracusa_tmpa_tl }
      { Aspect~#2~does~not~exist~in~#1! }
  }
\NewDocumentCommand \fullname { m }
  {
    { \getaspect { forename } { .print } \getaspect { forename } {#1} ~ }
    { \getaspect { surname  } { .print } \getaspect { surname  } {#1} }
  }
\ExplSyntaxOff
\defineaspect{forename}{
    .print = {},
    sherlock = Sherlock,
    jim = James
}
\defineaspect{surname}{
    .print = \scshape,
    sherlock = Holmes,
    jim = Moriarty
}
\begin{document}
Did you miss me? --- \fullname{jim}
\end{document}

However this does a one-to-one translation of the input to the data structure. If you need a finer-grained control of that process, say, to differentiate simple keys from key from "special" keys, like .print, then you could use \keyval_parse:NNn.

\keyval_parse:NNn takes two functions and a key-value list as argument. \keyval_parse:NNn iterates through the key-value list in #3 and uses function #1 or #2 in each item of the list depending if the item has only a key or a key and a value.

A reimplementation of the \defineaspect above would become:

\prop_new:N \l__siracusa_tmpa_prop
\NewDocumentCommand \defineaspect { mm }
  {
    \prop_clear:N \l__siracusa_tmpa_prop
    \keyval_parse:NNn \__siracusa_aspect:n \__siracusa_aspect:nn {#2}
    \prop_new:c { g__siracusa_#1_aspect_prop }
    \prop_gset_eq:cN { g__siracusa_#1_aspect_prop } \l__siracusa_tmpa_prop
  }
\cs_new_protected:Npn \__siracusa_aspect:n #1
  { \__kernel_msg_error:nnn { kernel } { prop-keyval } {#1} }
\cs_new_protected:Npn \__siracusa_aspect:nn #1#2
  { \prop_put:Nnn \l__siracusa_tmpa_prop {#1} {#2} }

The error message in the definition of \__siracusa_aspect:n is there only to mimick the behaviour of \prop_set_from_keyval:Nn, but of course you can change that to the appropriate action when only a key is given.

2
  • 1
    Thanks for mentioning \keyval_parse:NNn. I believe the second code sample would appreciate \prop_gset_eq:cN and \prop_put:Nnn, wouldn't it? ;-)
    – frougon
    Jul 8, 2019 at 20:55
  • @frougon Ouch :| Fixed. Thanks for pointing it out :-) Jul 8, 2019 at 21:01

You must log in to answer this question.

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