在新环境定义中使用令牌列表

在新环境定义中使用令牌列表

我正在尝试定义一个新环境来格式化我正在开展的项目的一些用户场景。下面的 MWE 显示了正在发生的事情。似乎第一次使用会\\清空令牌列表。

如果将这两行注释掉:

\the\scgiventoks \\
When & \@when \\

然后出现“Then”短语,所以我知道标记列表已正确填充。有什么想法会导致标记列表被清空,或者如何解决这个问题?我正在用 pdflatex 处理文件。

\documentclass[letterpaper,12pt]{article}
\usepackage[utf8]{inputenc}

\title{Test Tokens in Environment}

% Counter for scenarios in this story
\newcounter{scenariocnt}
\newtoks{\scgiventoks} \scgiventoks={}
\newtoks{\scthentoks} \scthentoks={}

\makeatletter
\newenvironment{scenario}[1][no title provided]{%
  % Token registers for givens and thens
  \scgiventoks={}
  \scthentoks={}
  \def\given##1{\scgiventoks=\expandafter{\the\scgiventoks \@formatgiven{##1}}\ignorespaces}%
  \def\when##1{\gdef\@when{##1}\ignorespaces}%
  \def\@when{\@latex@error{No \noexpand\when provided.}\@ehc}%
  \def\then##1{\scthentoks=\expandafter{\the\scthentoks \@formatthen{##1}}\ignorespaces}%

  \stepcounter{scenariocnt}%
  \subsection{Scenario \thescenariocnt : #1}%
  \begin{tabular}{rp{5.5in}}%
  \ignorespaces
}{%
  \gdef\@given{Given}
  \gdef\@then{Then}
  \let\argrowsep=\empty
  % if the next two lines are commented out, then the Then tokens get rendered
  % it seems that the presence of a \\ empties the token lists.
  \the\scgiventoks \\
  When & \@when \\
  \let\argrowsep=\empty
  \the\scthentoks \\
  \end{tabular}%
}%

\newcommand{\@formatgiven}[1]{%
  \argrowsep % first time empty, second time a \\
  \@given & #1
  \gdef\argrowsep{\\}% next time
  \gdef\@given{And}% next time
}

\newcommand{\@formatthen}[1]{%
  \argrowsep % first time empty, second time a \\
  \@then & #1
  \gdef\argrowsep{\\}% next time
  \gdef\@then{And}% next time
}
\makeatother

\begin{document}

\section{Minimum Working Example}
I'm trying to create a new environment to allow a scenario to be defined.

The environment can contain exactly one \verb+\when+ macro, and any number of \verb+\given+ or
\verb+\then+ macros. Since the macros can be given in any order, the environment uses token lists
to collect the \emph{given} and \emph{then} invocations, and the closing block of the environment
outputs them in the desired format.

The output I want from the environment should look like this:
\vspace{1em}

\begin{tabular}{rp{5.5in}}
  Given & the account balance is \$100.00 \\
    And & the card is valid \\
    And & the machine contains enough money \\
  When  & the Account Holder requests \$20.00 \\
  Then  & the ATM should dispense \$20.00 \\
   And  & the account balance should be \$80.00 \\
   And  & the card should be returned \\
\end{tabular}

\begin{scenario}[Account has sufficient funds]
  \when{the Account Holder requests \$20.00}

  \given{the account balance is \$100.00}
  \then{the ATM should dispense \$20.00}
  \given{the card is valid}
  \then{the account balance should be \$80.00}
  \given{the machine contains enough money}
  \then{the card should be returned}
\end{scenario}
\end{document}

答案1

由于您对标记列表寄存器的分配发生在表单元中,因此它们应该是全局的:

\documentclass[letterpaper,12pt]{article}
\usepackage[utf8]{inputenc}

% Counter for scenarios in this story
\newcounter{scenariocnt}
\newtoks{\scgiventoks}
\newtoks{\scthentoks}

\makeatletter
\newenvironment{scenario}[1][no title provided]{%
  % Token registers for givens and thens
  \global\scgiventoks={}%
  \global\scthentoks={}%
  \def\given##1{%
    \global\scgiventoks=\expandafter{\the\scgiventoks \@formatgiven{##1}}\ignorespaces
  }%
  \def\when##1{\gdef\@when{##1}\ignorespaces}%
  \def\@when{\@latex@error{No \noexpand\when provided.}\@ehc}%
  \def\then##1{%
    \global\scthentoks=\expandafter{\the\scthentoks \@formatthen{##1}}\ignorespaces
  }%
  \stepcounter{scenariocnt}%
  \subsection{Scenario \thescenariocnt : #1}%
  \begin{tabular}{rp{4in}}%
  \ignorespaces
}{%
  \gdef\@given{Given}%
  \gdef\@then{Then}%
  \let\argrowsep=\empty
  % if the next two lines are commented out, then the Then tokens get rendered
  % it seems that the presence of a \\ empties the token lists.
  \the\scgiventoks \\%
  When & \@when \\%
  \let\argrowsep=\empty
  \the\scthentoks \\%
  \end{tabular}%
}%

\newcommand{\@formatgiven}[1]{%
  \argrowsep % first time empty, second time a \\
  \@given & #1%
  \gdef\argrowsep{\\}% next time
  \gdef\@given{And}% next time
}

\newcommand{\@formatthen}[1]{%
  \argrowsep % first time empty, second time a \\
  \@then & #1%
  \gdef\argrowsep{\\}% next time
  \gdef\@then{And}% next time
}
\makeatother

\begin{document}

\title{Test Tokens in Environment}

\section{Minimum Working Example}
I'm trying to create a new environment to allow a scenario to be defined.

The environment can contain exactly one \verb+\when+ macro, and any number of \verb+\given+ or
\verb+\then+ macros. Since the macros can be given in any order, the environment uses token lists
to collect the \emph{given} and \emph{then} invocations, and the closing block of the environment
outputs them in the desired format.

The output I want from the environment should look like this:
\vspace{1em}

\noindent
\begin{tabular}{rp{4in}}
  Given & the account balance is \$100.00 \\
    And & the card is valid \\
    And & the machine contains enough money \\
  When  & the Account Holder requests \$20.00 \\
  Then  & the ATM should dispense \$20.00 \\
   And  & the account balance should be \$80.00 \\
   And  & the card should be returned \\
\end{tabular}

\begin{scenario}[Account has sufficient funds]
  \when{the Account Holder requests \$20.00}
  \given{the account balance is \$100.00}
  \then{the ATM should dispense \$20.00}
  \given{the card is valid}
  \then{the account balance should be \$80.00}
  \given{the machine contains enough money}
  \then{the card should be returned}
\end{scenario}
\end{document}

在此处输入图片描述

您可能会喜欢expl3相同的实现。无需全局分配,因为表体是在启动表格之前构建的。

\documentclass[letterpaper,12pt]{article}
\usepackage[utf8]{inputenc}
\usepackage{xparse}

% Counter for scenarios in this story
\newcounter{scenariocnt}

\ExplSyntaxOn
\NewDocumentEnvironment{scenario}{O{no title provided}}
 {
  \stepcounter{scenariocnt}
  \subsection{Scenario~\thescenariocnt :~#1}
  \cs_set_eq:NN \given \scenario_given:n
  \cs_set_eq:NN \then \scenario_then:n
  \cs_set_eq:NN \when \scenario_when:n
 }
 {
  \bool_if:NF \l_scenario_when_bool
   {
    \msg_error:nn { scenario } { missing-when }
   }
  \begin{tabular}{rp{4in}}
  \l_scenario_given_tl
  \l_scenario_when_tl
  \l_scenario_then_tl
  \end{tabular}
 }

\tl_const:Nn \c_scenario_given_tl { Given }
\tl_const:Nn \c_scenario_and_tl   { And }
\tl_const:Nn \c_scenario_then_tl  { Then }
\tl_const:Nn \c_scenario_when_tl  { When }

\bool_new:N \l_scenario_given_bool
\bool_new:N \l_scenario_then_bool

\tl_new:N \l_scenario_given_tl
\tl_new:N \l_scenario_then_tl
\tl_new:N \l_scenario_when_tl

\msg_new:nnnn { scenario } { missing-when }
 {
  Missing~\exp_not:N\when
 }
 {
  You~need~one~\exp_not:N\when command
 }

\cs_new_protected:Nn \scenario_given:n
 {
  \bool_if:NTF \l_scenario_given_bool
   {
    \tl_put_right:Nn \l_scenario_given_tl { \c_scenario_and_tl & #1 \\ }
   }
   {
    \tl_put_right:Nn \l_scenario_given_tl { \c_scenario_given_tl & #1 \\ }
    \bool_set_true:N \l_scenario_given_bool
   }
 }

\cs_new_protected:Nn \scenario_then:n
 {
  \bool_if:NTF \l_scenario_then_bool
   {
    \tl_put_right:Nn \l_scenario_then_tl { \c_scenario_and_tl & #1 \\ }
   }
   {
    \tl_put_right:Nn \l_scenario_then_tl { \c_scenario_then_tl & #1 \\ }
    \bool_set_true:N \l_scenario_then_bool
   }
 }

\cs_new_protected:Nn \scenario_when:n
 {
  \tl_put_right:Nn \l_scenario_when_tl { \c_scenario_when_tl & #1 \\ }
  \bool_set_true:N \l_scenario_when_bool
 }
\ExplSyntaxOff

\begin{document}

\title{Test Tokens in Environment}

\section{Minimum Working Example}
I'm trying to create a new environment to allow a scenario to be defined.

The environment can contain exactly one \verb+\when+ macro, and any number of \verb+\given+ or
\verb+\then+ macros. Since the macros can be given in any order, the environment uses token lists
to collect the \emph{given} and \emph{then} invocations, and the closing block of the environment
outputs them in the desired format.

The output I want from the environment should look like this:
\vspace{1em}

\noindent
\begin{tabular}{rp{4in}}
  Given & the account balance is \$100.00 \\
    And & the card is valid \\
    And & the machine contains enough money \\
  When  & the Account Holder requests \$20.00 \\
  Then  & the ATM should dispense \$20.00 \\
   And  & the account balance should be \$80.00 \\
   And  & the card should be returned \\
\end{tabular}

\begin{scenario}[Account has sufficient funds]
  \when{the Account Holder requests \$20.00}
  \given{the account balance is \$100.00}
  \then{the ATM should dispense \$20.00}
  \given{the card is valid}
  \then{the account balance should be \$80.00}
  \given{the machine contains enough money}
  \then{the card should be returned}
\end{scenario}
\end{document}

答案2

您可以在列表清空之前将标记保存到宏中,然后使用该宏。

例如,

  \xdef\tempa{\the\scthentoks}%
  \the\scgiventoks \\
  When & \@when \\
  \let\argrowsep=\empty
  \tempa\\

已保存的 toks

完整代码:

\documentclass[letterpaper,12pt]{article}
\usepackage[utf8]{inputenc}

\title{Test Tokens in Environment}

% Counter for scenarios in this story
\newcounter{scenariocnt}
\newtoks{\scgiventoks} \scgiventoks={}
\newtoks{\scthentoks} \scthentoks={}

\makeatletter
\newenvironment{scenario}[1][no title provided]{%
  % Token registers for givens and thens
  \scgiventoks={}%
  \scthentoks={}%
  \def\given##1{\scgiventoks=\expandafter{\the\scgiventoks \@formatgiven{##1}}\ignorespaces}%
  \def\when##1{\gdef\@when{##1}\ignorespaces}%
  \def\@when{\@latex@error{No \noexpand\when provided.}\@ehc}%
  \def\then##1{\scthentoks=\expandafter{\the\scthentoks \@formatthen{##1}}\ignorespaces}%
  \stepcounter{scenariocnt}%
  \subsection{Scenario \thescenariocnt : #1}%
  \begin{tabular}{rp{5.5in}}%
  \ignorespaces
}{%
  \gdef\@given{Given}
  \gdef\@then{Then}
  \let\argrowsep=\empty
  % if the next two lines are commented out, then the Then tokens get rendered
  % it seems that the presence of a \\ empties the token lists.
  \xdef\tempa{\the\scthentoks}%
  \the\scgiventoks \\
  When & \@when \\
  \let\argrowsep=\empty
  \tempa\\
  \end{tabular}%
}%

\newcommand{\@formatgiven}[1]{%
  \argrowsep % first time empty, second time a \\
  \@given & #1
  \gdef\argrowsep{\\}% next time
  \gdef\@given{And}% next time
}

\newcommand{\@formatthen}[1]{%
  \argrowsep % first time empty, second time a \\
  \@then & #1
  \gdef\argrowsep{\\}% next time
  \gdef\@then{And}% next time
}
\makeatother

\begin{document}

\section{Minimum Working Example}

\begin{scenario}[Account has sufficient funds]
  \when{the Account Holder requests \$20.00}

  \given{the account balance is \$100.00}
  \then{the ATM should dispense \$20.00}
  \given{the card is valid}
  \then{the account balance should be \$80.00}
  \given{the machine contains enough money}
  \then{the card should be returned}
\end{scenario}
\end{document}

相关内容