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?

  • 3
    These look like property lists to me: is every value 'some tokens to store'? – Joseph Wright Jul 7 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 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. – Phelype Oleinik Jul 7 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 at 0:34
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.

  • 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 at 20:55
  • @frougon Ouch :| Fixed. Thanks for pointing it out :-) – Phelype Oleinik Jul 8 at 21:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.