2

I know about \newcommand[<arg_num>]{#1 ... #n} to define a simple macro with up to nine arguments. (More if you link multiple newcommands together)

I recently started to look at xparse for some additional features such as multiple optional arguments, as well as keyval -- which works as I want it to, as long as each option is just one keyword.

What I would ultimately like to have is a way to address certain options by name and assign values and strings like so:

\MyCommand[
    opt1="this is val1",
    opt4="this is val4",
    optN="this is valN"
]{argument1}

Or maybe even:

\MyCommand{
    arg1="this is val1",
    arg4="this is val4",
    argN="this is valN"
}

(I know that "this is val" is not LaTeX syntax, maybe {this is val}?)

I am still fairly new to LaTeX, and I am not sure how to properly search for this. So I hope someone here can point me in the right direction.

omnesia
  • 123

4 Answers4

3

You may do this kind of thing with l3keys from interface3.pdf. Here is an example:

\documentclass[a4paper,12pt]{article}
\usepackage{lmodern}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{expl3}
\usepackage{xparse}

\ExplSyntaxOn

\bool_new:N \g_my_bool
\tl_new:N \g_some_tl
\tl_new:N \g_other_tl

\keys_define:nn { mymodule }
  {
    choiceKey .choice:,
    choiceKey / firstChoice .code:n = {
      \tl_gset:Nn \g_some_tl { whatever~you~want } },
    choiceKey / secondChoice .code:n = {
       \tl_gset:Nn \g_some_tl { with~commas,~too } },
    choiceKey / thirdChoice .code:n = {
       \tl_gset:Nn \g_some_tl { blah! } },
    choiceKey .initial:n = { thirdChoice },

    booleanKey .bool_gset:N = \g_my_bool,
    % Value used when the key is passed with no value
    booleanKey .default:n = { true },
    booleanKey .initial:n = { false },

    tokenListKey .tl_gset:N = \g_other_tl,
    tokenListKey .initial:n = { some~default }
  }

\cs_new:Nn \__mymodule_print_result:
  {
    Choice~key:~``\g_some_tl'' \\
    Boolean~key:~\bool_if:NTF \g_my_bool { set } { unset } \\
    Token~list~key:~``\g_other_tl''
  }

\NewDocumentCommand \MyModuleSetup { m }
  {
    \keys_set:nn { mymodule } {#1}
  }

\NewDocumentCommand \MyModulePrintResult { }
  {
    \__mymodule_print_result:
  }

\ExplSyntaxOff

\begin{document}
\setlength{\parindent}{0pt}%
%
\MyModulePrintResult            % Work with the default (“initial”) values

\bigskip
\MyModuleSetup{
  choiceKey=secondChoice,
  booleanKey=true,              % “=true“ may be omitted because of the
                                % “booleanKey .default:n = { true }” above
  tokenListKey={Yes, you may put commas in there. Outer braces, if any, are
    automatically removed. }    % The space following the period is part of
                                % the value
  }%
\MyModulePrintResult            % Work with the values just set
\end{document}

Screenshot of the rendered example

frougon
  • 24,283
  • 1
  • 32
  • 55
  • Thank you for your answer. Werners answer fit my use-case better and is closer to the syntax I am comfortable with, that is why I accepted it. But I will definitely keep this in mind for the future and I will probably use this as I get into more advanced LaTeX. – omnesia Jun 22 '18 at 06:40
  • Okay... since your question mentioned xparse, I thought you were looking for a LaTeX3-ish way of parsing the key/value mapping. Anyway, l3keys from interface3.pdf is very convenient and powerful in my opinion; it's probably a good idea to read its documentation, or at least skim through it, when you have some time. :) [starts p. 166 in the current version] – frougon Jun 22 '18 at 07:26
1

This?

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand \kvargcommand { +m +m }
 {
  \omnesia_kvargcommand:nn { #1 } { #2 }
 }
\cs_new_protected:Npn \omnesia_kvargcommand:nn #1 #2
 {
  \group_begin:
  \keys_set:nn { omnesia / kvargcommand } { #1 }
  \cs_set:Npn \omnesia_action:nnnnnnnnn ##1 ##2 ##3 ##4 ##5 ##6 ##7 ##8 ##9 { #2 }
  \cs_set:Npn \opt ##1 { \tl_use:c { l_omnesia_arg_ \int_to_roman:n {##1} _tl } }
  \tl_set:Nx \l_tmpa_tl
   {
    \exp_not:N \omnesia_action:nnnnnnnnn
     { \exp_not:V \l_omnesia_arg_i_tl    }
     { \exp_not:V \l_omnesia_arg_ii_tl   }
     { \exp_not:V \l_omnesia_arg_iii_tl  }
     { \exp_not:V \l_omnesia_arg_iv_tl   }
     { \exp_not:V \l_omnesia_arg_v_tl    }
     { \exp_not:V \l_omnesia_arg_vi_tl   }
     { \exp_not:V \l_omnesia_arg_vii_tl  }
     { \exp_not:V \l_omnesia_arg_viii_tl }
     { \exp_not:V \l_omnesia_arg_ix_tl   }
   }
  \tl_use:N \l_tmpa_tl
  \group_end:
 }
\keys_define:nn { omnesia / kvargcommand }
 {
  opt1 .tl_set:N = \l_omnesia_arg_i_tl    ,
  opt2 .tl_set:N = \l_omnesia_arg_ii_tl   ,
  opt3 .tl_set:N = \l_omnesia_arg_iii_tl  ,
  opt4 .tl_set:N = \l_omnesia_arg_iv_tl   ,
  opt5 .tl_set:N = \l_omnesia_arg_v_tl    ,
  opt6 .tl_set:N = \l_omnesia_arg_vi_tl   ,
  opt7 .tl_set:N = \l_omnesia_arg_vii_tl  ,
  opt8 .tl_set:N = \l_omnesia_arg_viii_tl ,
  opt9 .tl_set:N = \l_omnesia_arg_ix_tl   ,
 }

\ExplSyntaxOff

\begin{document}

\kvargcommand{
  opt1 = this is val1,
  opt4 = this is val4,
  opt7 = this is val7,
  opt6 = {this is val6, that contains a comma, so it has to be enclosed in braces},
}
{do something with one [#1] and four [\opt4] and seven [\opt7], the special case six [[#6]]
 and also some empty ones by default like (\opt2) and (#3), as you can see you can use \texttt{\string\opt} or just normal arguments}

\end{document}
Manuel
  • 27,118
  • Thank you for your answer. I accepted Werners answer as it seemed simpler and sufficed for what I needed to do. – omnesia Jun 22 '18 at 06:42
  • I still don't know for sure what you wanted. If you wanted to use #1 or \opt1 as arguments. – Manuel Jun 22 '18 at 06:43
  • Maybe the mention of \newcommand[<arg_num>]{#1 ... #n} was a little misleading. I wanted to address the arguments by name, meaning e.g. \opt1. But I was also not really sure what would be possible at all. – omnesia Jun 22 '18 at 06:55
  • Okey, I will update the answer anyways. – Manuel Jun 22 '18 at 06:56
  • Now you can use \opt1 in the same way as #1, both options possible. – Manuel Jun 22 '18 at 07:01
  • Thank you for your effort, I will probably come back to this in the future as my requirements for LaTeX grow. – omnesia Jun 22 '18 at 07:05
1

You can have multiple "keywords" assigned to each option as long as entries with commas are enclosed in braces {...}. Also, you'll have to stick to alphabetic characters for the option names:

enter image description here

\documentclass{article}

\usepackage{xkeyval}

\makeatletter
% ========= KEY DEFINITIONS =========
\define@cmdkey{mycmd}[mycmd@]{argA}{}% Creates \mycmd@argA
\define@cmdkey{mycmd}[mycmd@]{argB}{}% Creates \mycmd@argB
\define@cmdkey{mycmd}[mycmd@]{argC}{}% Creates \mycmd@argC
\define@cmdkey{mycmd}[mycmd@]{argD}{}% Creates \mycmd@argD
% ========= KEY DEFAULTS =========
\setkeys{mycmd}{
  argA = ARG-A,
  argB = arg-B,
  argC = ARG-c,
  argD = A R G - D}%
\newcommand{\MyCommand}[1]{%
  \begingroup%
  \setkeys{mycmd}{#1}% Set new keys
  Argument 1/2/3/4: \mycmd@argA /\mycmd@argB /\mycmd@argC /\mycmd@argD
  \endgroup%
}
\makeatother

\begin{document}

\MyCommand{}

\MyCommand{
  argB = BbBbBb,% Change from default
  argC = CcCcC cCcCc CcCcC% Multiple words
}

\MyCommand{
  argA = {AaAaA, aAaAa},% Multiple words separated by comma
  argD = "DdDdD dDdDd"% Multiple words in quotes
}

\end{document}

Reference: How to create a command with key values?

Werner
  • 603,163
0

You can use expkv-cs and its hashing variants. With this the setup is absolutely straight forward. The resulting macro will be fully expandable. Just to mention it the following code also includes the splitting variant of expkv-cs with which the key-values are accessible as positional parameters.

\documentclass{article}

\usepackage{expkv-cs}

% splits the keys into separate arguments to be referred by #1...#9 \ekvcSplit\mymacroA { argA = ARG-A ,argB = argB ,argC = A-R-G--C ,a long key name with spaces = a long value with spaces } {Arguments: #1/#2/#3/#4}

% stores all keys into #1 (in a way they can be accessed pretty fast), so we can % access them by names. % \ekvcValue will need exactly two steps of expansion. \ekvcHash\mymacroB { argA = ARG-A ,argB = argB ,argC = A-R-G--C ,a long key name with spaces = a long value with spaces } {% Arguments: \ekvcValue{argA}{#1}/% \ekvcValue{argB}{#1}/% \ekvcValue{argC}{#1}/% \ekvcValue{a long key name with spaces}{#1}% }

\begin{document} \mymacroA{}\par \mymacroB{}\par \mymacroB{argC=ccc, a long key name with spaces = a b c} \end{document}

enter image description here

Skillmon
  • 60,462