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.

  • 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? – Ulrich Diez May 30 '19 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 '19 at 07:29

3 Answers3

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}
David Carlisle
  • 757,742
  • 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 '19 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. – David Carlisle May 30 '19 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 '19 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. – David Carlisle May 30 '19 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 '19 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}
Ulrich Diez
  • 28,770
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

egreg
  • 1,121,712