1

I'm using sides to write a play. To add a character you do:

\begin{castpage}
\cast{Bob SquarePants}{A sponge}
\end{castpage}

and then you do

\repl{Bob SquarePants} Plop!

\stage{\chara{Bob} do a flip.}

\chara being a shortcut to small cap a string

What I would like to do is override \cast to do what it does and declare a new command/alias using a value passed in parameter. Like so: \cast{Bob SquarePants}{A sponge}{bob} and the new command created \bob with the code being something like \bob{\chara Bob}.

The goal being to replace \stage{\chara{Bob} do a flip.} by \stage{\bob do a flip.}

I tried to do :

\let\oldCast\cast
\renewcommand{\cast}[3]{
    \oldCast{#1}{#2}
    \expandafter\newcommand\csname #3\endcsname{\chara ##1}
}

But I get the error Illegal parameter number in definition of \bob when I write \cast{Bob SquarePants}{A sponge}{bob}

EDIT:

Here is a MWE (commented what I'd like to achieve):

\documentclass{sides}
\usepackage[T1]{fontenc}
\usepackage{libertine}
\def \stage{\stagedir}

\title{MWE}
\author{Foo}
\begin{document}
\maketitle

\newcommand*{\newcast}[3]{%
  \cast{#1}{#2}%
  \expandafter\newcommand\csname #3\endcsname{\chara{#1}}%
}

\begin{castpage}
\newcast{Bob SquarePants}{A sponge}{bob}
\newcast{Patrick Star}{A starfish}{pat}
\end{castpage}

\newact
\newscene

\stage{Opens on a black stage.}
\repl{Bob SquarePants} I will do a flip!
\stage{\chara{Bob} do a flip.}%\stage{\bob do a flip}
\repl{Patrick Star} Amazing!
\stage{\chara{Bob} smiles and \chara{Patrick} applauds.} %\stage{\bob smiles and \pat applauds.}

\end{document}

The point of the command being to save some typing and not adding the same code for each new character.

0

2 Answers 2

1

Welcome to TeX.SE. Problems in your macro:

  1. You are adding unwanted spaces. Please refer to What is the use of percent signs (%) at the end of lines?

  2. If you write \cast{Bob SquarePants}{A sponge}{bob}, when \cast is expanded, the whole will be replaced with:

    \oldCast{Bob SquarePants}{A sponge}
    \expandafter\newcommand\csname bob\endcsname{\chara #1}
    

    You see, the parameter tokens have been replaced by their values, and the ## has been replaced by a single #. The problem is that after \expandafter is expanded, the last line will become:

    \newcommand\bob{\chara #1}
    

    In other words, it defines a macro that takes no argument but contains #1 in its replacement text. This explains the error you obtained: “Illegal parameter number in definition of \bob.” \bob takes 0 arguments, but it is trying to use its first argument, hence an illegal parameter number.

  3. Your definition of the character-specific new command is done using \newcommand, therefore it is local and will be forgotten after \end{castpage} if you use \cast inside \begin{castpage} ... \end{castpage} (environments define a TeX group).

What you need to fix point 2 is to use a single # and add braces around the #1, so that after one expansion step of \cast{Bob SquarePants}{A sponge}{bob}, one obtains:

\oldCast{Bob SquarePants}{A sponge}%
\expandafter\newcommand\csname bob\endcsname{\chara{bob}}%

So, your definition could be:

\let\oldcast\cast
\renewcommand*{\cast}[3]{%
  \oldCast{#1}{#2}%
  \expandafter\newcommand\csname #3\endcsname{\chara{#1}}%
}

Note that in some cases (most notably commands defined with \newcommand and taking optional arguments, or commands defined with \DeclareRobustCommand), the \let\oldcast\cast won't work the way you want. In such cases, the letltxmacro package can be very useful.

Also, since you are changing the syntax and semantics of \cast, it may be wiser to name your function differently—say, \newcast:

\newcommand*{\newcast}[3]{%
  \cast{#1}{#2}%
  \expandafter\newcommand\csname #3\endcsname{\chara{#1}}%
}

Now to problem number 3. We need to do the dynamic command definition performed inside \newcast global. This can be done with \gdef. In order to be über-clean, we can additionally use \newcommand to make sure the command (such as \bob) doesn't already exist. Indeed, \gdef would overwrite an existing command without notice. Thus, we can do the following:

\newcommand*{\newcast}[3]{%
  \cast{#1}{#2}%
  % Print an error if the command named after #3 is already defined
  \expandafter\newcommand\csname #3\endcsname{}%
  \expandafter\gdef\csname #3\endcsname{\chara{#1}\ }% there is a control space
}

I added a control space after \chara{#1} because when you write for instance \bob do a flip., from TeX's point of view, there is no space token after \bob: the space character that follows in the input stream is eaten as soon as \bob is turned into a control sequence token (this is during the TeX processing stage known as tokenization).

Finally, the same thing can also be done using \csgdef from the etoolbox package (this is syntactic sugar and behaves the same):

\csgdef{#3}{\chara{#1}\ }

Full code:

\documentclass{sides}
\usepackage{etoolbox}

\newcommand*{\stage}{\stagedir}

\title{MWE}
\author{Foo}

\begin{document}
\maketitle

\newcommand*{\newcast}[3]{%
  \cast{#1}{#2}%
  % Print an error if the command named after #3 is already defined
  \expandafter\newcommand\csname #3\endcsname{}%
  \csgdef{#3}{\chara{#1}\ }% there is a control space before the brace
}

\begin{castpage}
\newcast{Bob SquarePants}{A sponge}{bob}
\newcast{Patrick Star}{A starfish}{pat}
\end{castpage}

\newact
\newscene
\show\bob
\stage{Opens on a black stage.}
\repl{Bob SquarePants} I will do a flip!
\stage{\bob do a flip.}
\repl{Patrick Star} Amazing!
\stage{\bob smiles and \chara{Patrick} applauds.}

\end{document}

enter image description here

6
  • Thanks for your answer I added a MWE as requested. May 12, 2020 at 14:57
  • Thanks. Indeed, that was quite important since you are using \newcast inside a group. The dynamic definition has to be global, then; please see the updated answer. I removed \usepackage[T1]{fontenc} and \usepackage{libertine} from the MWE because they are not relevant to the question; feel free to readd them on your side.
    – frougon
    May 12, 2020 at 15:27
  • Thanks, it works just fine! I just added added a parameter because I just want the firstname \newcommand*{\newcast}[4]{% \cast{#1\ #2}{#3}% \expandafter\newcommand\csname #4\endcsname{}% \csgdef{#4}{\chara{#1}\ } } May 12, 2020 at 16:04
  • Right, glad to hear that. Note that the reason why I used a control space instead of a space token (i.e., {\chara{#1}\ } instead of {\chara{#1} }) is that a control space always adds an interword glue (space) to the horizontal list, whereas a space token can add a glue item of varying width depending on what precedes (for instance, in English, after a sentence-ending period, the space is larger than an interword space). Normally, this only matters in corner cases where punctuation precedes the space; now, you know the difference and can choose appropriately.
    – frougon
    May 12, 2020 at 16:09
  • In the end I use {\chara{#1}\xspace} from the package xspace because I noticed that \bob, foo gave the result Bob , foo instead of Bob, foo. xspace is able to determine is he should put a space or not ctan.org/pkg/xspace May 12, 2020 at 18:14
0

The command \cast takes one argument and does nothing at all with the first argument except typesetting it as if it were a section title. What seems to be a second argument is just the text of the “section”.

If you type

\begin{castpage}
\cast{Bob SquarePants} A sponge
\end{castpage}

there would be no difference at all.

The command \chara just typesets its argument in small caps. Whether you have used \cast{Patrick Star}{A starfish} or not, typing \chara{Patrick} will do its work as defined.

There is no connection whatsoever between the argument of \chara and anything that appears in castpage.

You can simply do \newcommand{\bob}{\chara{Bob}} in the document preamble and use

\stagedir{\bob\ do a flip}

Here's a way to avoid code duplication. You input character data with a key-value syntax.

\documentclass{sides}
\usepackage{libertine}
\usepackage{xparse,xspace}

\ExplSyntaxOn
\keys_define:nn { misterjack/cast }
 {
  shortname .tl_set:N = \l__misterjack_cast_shortname_tl,
  fullname  .tl_set:N = \l__misterjack_cast_fullname_tl,
  char      .code:n = \misterjack_cast_full:nV { #1 } \l__misterjack_cast_fullname_tl,
  nick      .code:n = \misterjack_cast_short:nV { #1 } \l__misterjack_cast_shortname_tl,
  desc      .tl_set:N = \l__misterjack_cast_desc_tl,
 }
\cs_new_protected:Nn \misterjack_cast_full:nn
 {
  \cs_new:cpn { #1 } { \repl { #2 } \xspace }
 }
\cs_generate_variant:Nn \misterjack_cast_full:nn { nV }
\cs_new_protected:Nn \misterjack_cast_short:nn
 {
  \cs_new:cpn { #1 } { \chara { #2 } \xspace }
 }
\cs_generate_variant:Nn \misterjack_cast_short:nn { nV }

\NewDocumentCommand{\Cast}{m}
 {
  \group_begin:
  \keys_set:nn { misterjack/cast } { #1 }
  \cast{\l__misterjack_cast_fullname_tl} \l__misterjack_cast_desc_tl
  \group_end:
 }
\ExplSyntaxOff

\begin{document}

\begin{castpage}
\Cast{
  fullname=Bob SquarePants,
  desc=A sponge,
  shortname=Bob,
  char=Bob,
  nick=bob,
}
\Cast{
  fullname=Patrick Star,
  desc=A starfish,
  shortname=Patrick,
  char=Pat,
  nick=pat,
}
\Cast{
  fullname=Mr.~Krab,
  desc=A crab,
  shortname=Mr.~Krab,
  char=Krab,
  nick=krab,
}
\end{castpage}

\Bob Plop!

\stagedir{\bob do a flip.}

\stagedir{\krab grumbles.}

\end{document}

The strings for the abbreviated input are arbitrary (but should contain only ASCII characters).

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .