1

I have a custom environment to recreate a certain layout formatting a variable number of text entries in multiple enumerate environments. So far I have been defining multiple similarly named commands as tokens that can be set inside the environment, but my current method requires a lot of \ithenelse switches and is not very well scalable.

\newcommand{\setName}[2][]{\ifthenelse{\equal{#1}{}}{\renewcommand{\tokenName}{#2}}{\renewcommand{\tokenName}{#2 1--#1}}}
\newcommand{\setEffect}[2][]{
    \ifthenelse{\equal{#1}{}}{\renewcommand{\tokenEffect}{\item[] #2}}{}
    \ifthenelse{\equal{#1}{1}}{\renewcommand{\tokenEffectI}{\item #2}}{}
    \ifthenelse{\equal{#1}{2}}{\renewcommand{\tokenEffectII}{\item #2}}{}
    \ifthenelse{\equal{#1}{3}}{\renewcommand{\tokenEffectIII}{\item #2}}{}
    \ifthenelse{\equal{#1}{X}}{\renewcommand{\tokenEffectX}{\item[each:] #2}}{}
}
\newcommand{\setRequirement}[2][]{
    \ifthenelse{\equal{#1}{}}{\renewcommand{\tokenEffect}{\item[] #2}}{}
    \ifthenelse{\equal{#1}{1}}{\renewcommand{\tokenRequirement}{\item #2}}{}
    \ifthenelse{\equal{#1}{2}}{\renewcommand{\tokenRequirementI}{\item #2}}{}
    \ifthenelse{\equal{#1}{3}}{\renewcommand{\tokenRequirementII}{\item #2}}{}
    \ifthenelse{\equal{#1}{X}}{\renewcommand{\tokenRequirementIII}{\item #2}}{}
}

\newenvironment{Description}{
    \newcommand{\tokenName}{}
    \newcommand{\tokenEffect}{}
    \newcommand{\tokenEffectI}{}
    \newcommand{\tokenEffectII}{}
    \newcommand{\tokenEffectIII}{}
    \newcommand{\tokenEffectX}{}

    \newcommand{\tokenRequirement}{}
    \newcommand{\tokenRequirementI}{}
    \newcommand{\tokenRequirementII}{}
    \newcommand{\tokenRequirementIII}{}
}{
    \section{\tokenName}
    \paragraph{Effects}
    \begin{enumerate}
        \tokenEffect
        \tokenEffectX
        \tokenEffectI
        \tokenEffectII
        \tokenEffectIII
    \end{enumerate}

    \paragraph{Requirements}
    \begin{enumerate}
        \tokenRequirement
        \tokenRequirementI
        \tokenRequirementII
        \tokenRequirementIII
    \end{enumerate} 
}

Is there a more efficient, flexible way to replace the token command system? I tried xsavebox but could not get it to work properly with arguments in the savebox names.

2
  • If you don't call \setName from within the environment, this will yield a \section without a title. That seems weird. What do you wish to happen in case \setName is not called from within the environment? If you don't call \setEffect/\setRequirement from within the environment, this might yield empty enumerate environments which also yields error-messages. What behavior do you wish in such cases? May 30, 2019 at 23:21
  • @UlrichDiez As the environment is only intended for internal use inside my own rulebook, it was intentional to get error messages if the enumerate environments are empty so that I could see where I forgot to add the info. However I have since restructured the output to move the [] and [X] tokens outside of the enumerate environment and hidden the environment inside a check for the existance of the [1] token. I just wasn't sure it made sense to update the question with my current solution.
    – J Lossner
    May 31, 2019 at 7:29

3 Answers 3

1

I think you want something like

\makeatletter
\newcommand{\setEffect}[2][]{%
    \ifx\relax#1\relax
      \def\tokenEffect{\item[] #2}%
    \else\ifx X#1\relax
       \def\tokenEffectX{\item[each:] #2}%
    \else
       \expandafter\def\csname tokenEffect\@Roman{#1}\endcsname{\item #2}%
    \fi\fi}
5
  • I suspect that is pretty close to what I was looking for, however I now get "Missing \endcsname inserted", "Extra \endcsname" and "Lonely item" error messages for the numbered tokens. Also, is there any way to shorten the initial command definitions / their usage? (As in "for each N in 1 to 4, use \commandN")
    – J Lossner
    May 30, 2019 at 12:36
  • @JLossner you didn't provide an example document which left some things unclear, yes you could write a loop but if it is always just 4 tokens it is hard to get a loop syntax that takes less. May 30, 2019 at 12:39
  • I gave up on the example document after fiddling with it for a day or two because of all the other dependencies and decided to rewrite a simpler version of the macros. There are either 0, 2 or 4 numbered tokens, and sometimes (but not always) the blank and X token, however the amount of different numbered commands is what led me to the question to cut down on manual definitions to reduce errors when changing things around.
    – J Lossner
    May 30, 2019 at 12:45
  • @JLossner but you could still now fix the example in your question to be an example people can run to test answers, not just a fragment of code they have to look at by eye and guess an intended test usage. May 30, 2019 at 12:47
  • Actually, I figured out the rest now by building the rest of the token command name with another argument to a generic token setting function - thank you for making me aware of the \csname \endcsname functionality. The errors disappeared during rebuilding to make an example document, not sure what caused them in the first place.
    – J Lossner
    May 30, 2019 at 13:15
0

You can use a lot of \name both for defining and calling macros whose names also contain digits and/or spaces and/or the like - i elaborated on the macro \name in the discussion Define a control sequence after that a space matters.

Perhaps something like this suits your needs? :

%%----------------------------------------------------------------------
%% \name{macro} -> \macro
%% \name\string{macro} -> \string\macro
%% \name\newcommand{macro}... -> \newcommand\macro...
%% (Instead of "macro" you can also use phrases that contain digits
%%  and/or spaces and/or the like.)
%%......................................................................
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\innername{#1}}%
\newcommand\innername[2]{\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}}%
\newcommand\exchange[2]{#2#1}%
%%......................................................................
\newcommand\CheckWhetherUndefined{}%
\name\global\let\CheckWhetherUndefined={@ifundefined}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\firstoftwo[2]{#1}%
\newcommand\secondoftwo[2]{#2}%
\newcommand\CheckWhetherNull[1]{%
  \romannumeral0\expandafter\secondoftwo\string{\expandafter\secondoftwo
  \expandafter{\expandafter{\string#1}\expandafter\secondoftwo\string}%
  \expandafter\firstoftwo\expandafter{\expandafter\secondoftwo\string}%
  \expandafter\expandafter\firstoftwo{ }{}\secondoftwo}{\expandafter
  \expandafter\firstoftwo{ }{}\firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Maintaining local commands:
%%......................................................................
\newcommand\genericSet[1]{%
  \name\name{\CheckWhetherUndefined{#1}{}{re}newcommand}*{#1}%
}%
\newcommand{\setName}[2][]{%
  \CheckWhetherNull{#1}{\genericSet{tokenName}{#2}}{\genericSet{tokenName}{#2 1--#1}}%
  \ignorespaces
}%
\newcommand{\setEffect}[2][]{\genericSet{tokenEffect#1}{#2}\ignorespaces}%
\newcommand{\setRequirement}[2][]{\genericSet{tokenRequirement#1}{#2}\ignorespaces}%
% You must never define \UnDeFInED  ;-)
\newenvironment{Description}{\let\tokenName=\UnDeFInED\ignorespaces}{%
  \section{\tokenName}% <- This way you will get an undefined 
                      %    control-sequence-error in case of not 
                      %    having called \setName from within the environment.
  \paragraph{Effects}%
  \begin{enumerate}% At least one of the following should be defined!
    \CheckWhetherUndefined{tokenEffect}{}{\item[]\name{tokenEffect}}%
    \CheckWhetherUndefined{tokenEffectX}{}{\item[each:]\name{tokenEffectX}}%
    \CheckWhetherUndefined{tokenEffect1}{}{\item\name{tokenEffect1}}%
    \CheckWhetherUndefined{tokenEffect2}{}{\item\name{tokenEffect2}}%
    \CheckWhetherUndefined{tokenEffect3}{}{\item\name{tokenEffect3}}%
  \end{enumerate}%
  \paragraph{Requirements}%
  \begin{enumerate}% At least one of the following should be defined!
    \CheckWhetherUndefined{tokenRequirement}{}{\item[]\name{tokenRequirement}}%
    \CheckWhetherUndefined{tokenRequirement1}{}{\item\name{tokenRequirement1}}%
    \CheckWhetherUndefined{tokenRequirement2}{}{\item\name{tokenRequirement2}}%
    \CheckWhetherUndefined{tokenRequirement3}{}{\item\name{tokenRequirement3}}%
  \end{enumerate}%
}%

%% Usage:
% \begin{Description}
%   \setName[foo]{Bar}%
%   \setEffect{Token Effect}%
%   \setEffect[1]{Token Effect 1}%
%   \setEffect[2]{Token Effect 2}%
%   \setEffect[3]{Token Effect 3}%
%   \setEffect[X]{Token Effect X}%
%   \setRequirement{Token Requirement}%
%   \setRequirement[1]{Token Requirement 1}%
%   \setRequirement[2]{Token Requirement 2}%
%   \setRequirement[3]{Token Requirement 3}%
% \end{Description}

Additionally one can add an \if..-switch for starting and ending the enumerate-environment only in case there are items:

%%----------------------------------------------------------------------
%% \name{macro} -> \macro
%% \name\string{macro} -> \string\macro
%% \name\newcommand{macro}... -> \newcommand\macro...
%% (Instead of "macro" you can also use phrases that contain digits
%%  and/or spaces and/or the like.)
%%......................................................................
\newcommand\name{}%
\long\def\name#1#{\romannumeral0\innername{#1}}%
\newcommand\innername[2]{\expandafter\exchange\expandafter{\csname#2\endcsname}{ #1}}%
\newcommand\exchange[2]{#2#1}%
%%......................................................................
\newcommand\CheckWhetherUndefined{}%
\name\global\let\CheckWhetherUndefined={@ifundefined}%
%%----------------------------------------------------------------------
%% Check whether argument is empty:
%%......................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that argument
%%                    which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\firstoftwo[2]{#1}%
\newcommand\secondoftwo[2]{#2}%
\newcommand\CheckWhetherNull[1]{%
  \romannumeral0\expandafter\secondoftwo\string{\expandafter\secondoftwo
  \expandafter{\expandafter{\string#1}\expandafter\secondoftwo\string}%
  \expandafter\firstoftwo\expandafter{\expandafter\secondoftwo\string}%
  \expandafter\expandafter\firstoftwo{ }{}\secondoftwo}{\expandafter
  \expandafter\firstoftwo{ }{}\firstoftwo}%
}%
%%----------------------------------------------------------------------
%% Starting and ending enumerate-environment:
%%......................................................................
\newif\ifThereAlreadyIsAnItem\ThereAlreadyIsAnItemfalse
\newcommand\ProbablyStartEnumerate[1]{%
  \ifThereAlreadyIsAnItem\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {}{\ThereAlreadyIsAnItemtrue#1\begin{enumerate}}%
}%
\newcommand\ProbablyStopEnumerate{%
  \ifThereAlreadyIsAnItem\expandafter\firstoftwo\else\expandafter\secondoftwo\fi
  {\end{enumerate}\ThereAlreadyIsAnItemfalse}{}%
}%
%%----------------------------------------------------------------------
%% Maintaining local commands:
%%......................................................................
\newcommand\genericSet[1]{%
  \name\name{\CheckWhetherUndefined{#1}{}{re}newcommand}*{#1}%
}%
\newcommand{\setName}[2][]{%
  \CheckWhetherNull{#1}{\genericSet{tokenName}{#2}}{\genericSet{tokenName}{#2 1--#1}}%
  \ignorespaces
}%
\newcommand{\setEffect}[2][]{\genericSet{tokenEffect#1}{#2}\ignorespaces}%
\newcommand{\setRequirement}[2][]{\genericSet{tokenRequirement#1}{#2}\ignorespaces}%
% You must never define \UnDeFInED  ;-)
\newenvironment{Description}{%
  \ThereAlreadyIsAnItemfalse
  \let\tokenName=\UnDeFInED
  \ignorespaces
}{%
  \section{\tokenName}% <- This way you will get an undefined 
                      %    control-sequence-error in case of not 
                      %    having called \setName from within the environment.
  \CheckWhetherUndefined{tokenEffect}{}{\ProbablyStartEnumerate{\paragraph{Effects}}\item[]\name{tokenEffect}}%
  \CheckWhetherUndefined{tokenEffectX}{}{\ProbablyStartEnumerate{\paragraph{Effects}}\item[each:]\name{tokenEffectX}}%
  \CheckWhetherUndefined{tokenEffect1}{}{\ProbablyStartEnumerate{\paragraph{Effects}}\item\name{tokenEffect1}}%
  \CheckWhetherUndefined{tokenEffect2}{}{\ProbablyStartEnumerate{\paragraph{Effects}}\item\name{tokenEffect2}}%
  \CheckWhetherUndefined{tokenEffect3}{}{\ProbablyStartEnumerate{\paragraph{Effects}}\item\name{tokenEffect3}}%
  \ProbablyStopEnumerate
  \CheckWhetherUndefined{tokenRequirement}{}{\ProbablyStartEnumerate{\paragraph{Requirements}}\item[]\name{tokenRequirement}}%
  \CheckWhetherUndefined{tokenRequirement1}{}{\ProbablyStartEnumerate{\paragraph{Requirements}}\item\name{tokenRequirement1}}%
  \CheckWhetherUndefined{tokenRequirement2}{}{\ProbablyStartEnumerate{\paragraph{Requirements}}\item\name{tokenRequirement2}}%
  \CheckWhetherUndefined{tokenRequirement3}{}{\ProbablyStartEnumerate{\paragraph{Requirements}}\item\name{tokenRequirement3}}%
  \ProbablyStopEnumerate
}%

%% Usage:
% \begin{Description}
%   \setName[foo]{Bar}%
%   \setEffect{Token Effect}%
%   \setEffect[1]{Token Effect 1}%
%   \setEffect[2]{Token Effect 2}%
%   \setEffect[3]{Token Effect 3}%
%   \setEffect[X]{Token Effect X}%
%   \setRequirement{Token Requirement}%
%   \setRequirement[1]{Token Requirement 1}%
%   \setRequirement[2]{Token Requirement 2}%
%   \setRequirement[3]{Token Requirement 3}%
% \end{Description}
0

This is scalable (up to 100 effects and requirements are available).

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\prop_new:N \l_lossner_token_prop
\tl_new:N \l_lossner_token_name_tl

\NewDocumentCommand{\setName}{om}
 {
  \IfNoValueTF { #1 }
   {
    \tl_set:Nn \l_lossner_token_name_tl { #2 }
   }
   {
    \tl_set:Nn \l_lossner_token_name_tl { #2 ~ 1--#1 }
   }
 }
\NewDocumentCommand{\setEffect}{om}
 {
  \IfNoValueTF { #1 }
   {
    \prop_put:Nnn \l_lossner_token_prop { effect@ } { \item[] #2 }
   }
   {
    \str_if_eq:nnTF { #1 } { X }
     {
      \prop_put:Nnn \l_lossner_token_prop { effect@#1 } { \item[each:] #2 }
     }
     {
      \prop_put:Nnn \l_lossner_token_prop { effect@#1 } { \item #2 }
     }
   }
 }
\NewDocumentCommand{\setRequirement}{om}
 {
  \IfNoValueTF { #1 }
   {
    \prop_put:Nnn \l_lossner_token_prop { effect@ } { \item[] #2 }
   }
   {
    \prop_put:Nnn \l_lossner_token_prop { requirement@#1 } { \item #2 }
   }
 }

\NewDocumentEnvironment{Description}{}
 {}
 {
\prop_show:N \l_lossner_token_prop
  \section{\l_lossner_token_name_tl}
  \paragraph{Effects}
  \begin{enumerate}
    \lossner_token:nn {effect}{}
    \lossner_token:nn {effect}{X}
    \int_step_inline:nn {100}
     {
      \lossner_token:nn {effect}{##1}
     }
  \end{enumerate}

  \paragraph{Requirements}
    \begin{enumerate}
    \lossner_token:nn {requirement}{X}
    \int_step_inline:nn {100}
     {
      \lossner_token:nn {requirement}{##1}
     }
    \end{enumerate} 
 }

\cs_new_protected:Nn \lossner_token:nn
 {
  \prop_item:Nn \l_lossner_token_prop { #1@#2 }
 }

\ExplSyntaxOff

\begin{document}

\begin{Description}
\setName{abc}
\setEffect{Main}
\setEffect[1]{Wow}
\setRequirement[3]{Whatever}
\end{Description}

\begin{Description}
\setName[A]{abc}
\setEffect{Wow}
\setEffect[X]{Plus}
\setRequirement[1]{Whatever}
\setRequirement[2]{Whatever}
\setRequirement[3]{Whatever}
\setRequirement[4]{Whatever}
\setRequirement[5]{Whatever}
\setRequirement[6]{Whatever}
\setRequirement[7]{Whatever}
\setRequirement[8]{Whatever}
\setRequirement[9]{Whatever}
\end{Description}

\end{document}

enter image description here

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.