4

For example, I defined a macro \test with one parameter. Latex takes the first character following \test as its parameter by default. I want latex to throw an error if \test is called without any parameter explicitly. Namely, I should always write \test{a}, not \test a. How to achieve this?

\documentclass{amsart}
\newcommand{\test}[1]{test #1 test}
\begin{document}
    \test abcdefg
\end{document}
0

2 Answers 2

4

Please note that this behaviour of grabbing a single non-space non-brace token if you don't provide braces is standard TeX behaviour and no error on the LaTeX side. TeX grabs a brace-balanced parameter, if you don't start with an opening brace the single token is brace-balanced. While LaTeX officially says the braces around arguments should always be provided, it is quite common to omit them for single tokens (for instance I never used \newcommand{\foo}{} but always \newcommand\foo{}).

Be it as it may, you can explicitly test for a following opening brace using \@ifnextchar\bgroup and throw an error if you don't find it:

\documentclass{amsart}

\makeatletter
\@ifdefinable\test{\protected\def\test
  {%
    \@ifnextchar\bgroup
      {\@test}%
      {\PackageError{gaoqiang}{Missing braced argument}{}}%
  }}
\newcommand\@test[1]{test #1 test}
\makeatother

\begin{document}
    \test abcdefg
\end{document}

Note that \@ifnextchar isn't expandable, hence we need to define the macro protected in some way (either \DeclareRobustCmd, \NewDocumentCommand, or as done here using \protected\def; I've added \@ifdefinable to make sure \test is a new command).

1
  • Perhaps add that as the LaTeX syntax only requires a balanced text at the TeX level, \test abc is acting 'correctly', see for example common usage \frac12 or similar.
    – Joseph Wright
    Feb 23 at 7:47
3

In case you don't wish to specify the error message yourself but are happy with some low level TeX error message "! Use of \test doesn't match its definition.", you can use #{-notation:

\documentclass{amsart}
\makeatletter
\@ifdefinable\test{\long\def\test#{\test@brace}}%
\newcommand{\test@brace}[1]{test #1 test}%
\makeatother
\begin{document}
    \test {Nice}
    \test Problem
\end{document}


In case you wish to specify the error-message yourself, things might turn out more tricky.

In TeX jargon, in the context of macro programming, the terms "argument" and "parameter" don't denote the same.

In TeX jargon, in the context of macro programming, some teminology (like the word "parameter") focusses on the time when a macro is defined, and some teminology (like the word "argument") focusses on the time when a macro-token, i.e., a control-sequence-token which currently represents an instance of the macro, is expanded.

In TeX jargon, in the context of macro programming, "parameters" are components of a ⟨definition⟩ of a macro. They occur in the ⟨definition⟩'s ⟨parameter text⟩ and in the ⟨definition⟩'s ⟨balanced text⟩. They denote that at the time of expanding a control-sequence-token which currently represents an instance of the macro (as the corresponding ⟨definition⟩ currently is assigned to it) additional constellations of tokens are needed which form the "arguments" of that control-sequence-token/of that macro-token.

In TeX jargon, in the context of macro programming, an "argument" is a constellation of tokens which at the time of expanding a macro-token is grabbed from the token-stream for replacing all instances of a parameter (that parameter might be #1 or might be #2 or...) that occur in the ⟨balanced text⟩ of the ⟨definition⟩ currently assigned to that macro-token.
The rules for grabbing from the token-stream the constellation of tokens that form the arguments (delimiters of delimited arguments, undelimited arguments) come from the ⟨parameter text⟩ of the ⟨definition⟩ currently assigned to the macro-token.

In the sense of having different terminology for focussing on different aspects, in this answer

  • the phrase "⟨replacement text⟩" (in angles) refers to the ⟨balanced text⟩ of the ⟨definition⟩ of a macro, i.e., to a component of the ⟨definition⟩ of a macro. ⟨replacement text⟩ refers to defining a macro. It may contain parameters #1, #2, etc as denoted in the ⟨definition⟩'s ⟨parameter text⟩.

  • the phrase "replacement text" (without angles) refers to the tokens by which in the stage of expanding a macro-token that macro-token and the tokens forming its argument-delimiters and arguments are replaced and where parameters #1, #2, etc of the ⟨replacement text⟩ of the ⟨definition⟩ currently assigned to the macro-token are replaced by those tokens that form the macro-token's corresponding arguments.


\test as defined in your example has an undelimited parameter #1 both in the ⟨parameter text⟩ and in the ⟨replacement text⟩ of the ⟨definition⟩ that comes into being in the course of carrying out \newcommand.

Therefore, at the time of expanding a macro-token \test, i.e., at the time when in the token-stream replacing a macro-token \test and the tokens forming its argument-delimiters and arguments, tokens that are to form a so-called undelimited argument are grabbed/removed from the token stream for replacing all instances of #1 that occur within the ⟨replacement text⟩ of the ⟨definition⟩ currently assigned to the macro-token \test.

Undelimited arguments can be denoted in two ways:

  1. Either there is just a single non-\outer token which neither is an explicit character token of category 1 (begin group) or nor is an explicit character token of category 2 (end group). In this case that token is the undelimited argument.

  2. Or there is a (possibly empty) constellation of non-\outer tokens where explicit character tokens of category 1 (begin group) and explicit character tokens of category 2 (end group), if present, are balanced and which is surrounded by an explicit character token of category 1 (begin group) and a matching explicit character token of category 2 (end group). In this case, these two surrounding character tokens denote/delimit the constellation of tokens that forms the so-called undelimited argument, but these two surrounding/delimiting character tokens themselves are not components of the argument in the sense of making it into that set of tokens which forms the replacement text that replaces the macro-token where the argument belongs to and the tokens that delimit/form the arguments of the macro-token where the argument belongs to.
    These two surrounding/delimiting character tokens are stripped off in the course of expanding the macro-token where the argument belongs to and hereby grabbing from the token-stream the tokens that delimit/form the argument.


The reference documentation for the LaTeX 2ε programming environment "expl3", "The LaTeX3 Interfaces", says:

It is important to distinguish two aspects of a token: its "shape" (for lack of a better word), which affects the matching of delimited arguments and the comparison of token lists containing this token, and its "meaning", which affects whether the token expands or what operation it performs. One can have tokens of different shapes with the same meaning, but not the converse.

Basically you ask for detecting whether a macro-token's subsequent undelimited argument is one which is enclosed by an explicit character token of category 1 (begin group), {1, and a matching explicit character token of category 2 (end group), }2.

A method is known to me for after grabbing an undelimited macro argument—be it provided as a single token not enclosed by an explicit character token of category 1 (begin group) and a matching explicit character token of category 2 (end group), be it provided as a constellation of tokens enclosed by an explicit character token of category 1 (begin group) and a matching explicit character token of category 2 (end group)—detecting whether the first token of that argument is an explicit character token of category 1 (begin group):

\makeatletter
% Compile with latex and look at the messages on the console/in the .log-file.
%
%%=============================================================================
%% Check whether argument's first token is an explicit character token 
%% of category 1 (begin group):
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has a leading
%%                        explicit catcode-1-character-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked does not have a
%%                        leading explicit catcode-1-character-token>}%
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00 }%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%

\newcommand\macro[1]{%
  The first token of the argument of \string\macro\space is 
  \UD@CheckWhetherBrace{#1}{indeed}{not}
  an explicit character token of category 1(begin group).
}%
\makeatother

\message{%
  ^^J%
  \macro{no braces}%
}%
\message{%
  ^^J%
  \macro{braces {in} the middle}%
}%
\message{%
  ^^J%
  \macro{{braces} as leading tokens}%
}%
\message{%
  ^^J%
  \macro{{everything in braces}}%
}%

\stop

Messages on the console and in the .log-file:

The first token of the argument of \macro is not an explicit character token of
 category 1(begin group). 

The first token of the argument of \macro is not an explicit character token of
 category 1(begin group). 

The first token of the argument of \macro is indeed an explicit character token
 of category 1(begin group). 

The first token of the argument of \macro is indeed an explicit character token
 of category 1(begin group)

But this is not what you are after because this does not test whether the tokens forming an undelimited argument are enclosed in curly braces/are enclosed by an explicit character token of category 1 (begin group) and a matching explicit character token of category 2 (end group). This does test whether the first one of the tokens forming the argument is a curly left brace/is an explicit character token of category 1 (begin group).

Neither do I know a perfectly reliable method for before grabbing an undelimited macro argument detecting whether that undelimited macro argument is provided as a single token not enclosed by an explicit character token of category 1 (begin group) and a matching explicit character token of category 2 (end group), nor do I know a perfectly reliable method for before grabbing an undelimited macro argument detecting whether that undelimited macro argument is provided as a constellation of tokens enclosed by an explicit character token of category 1 (begin group) and a matching explicit character token of category 2 (end group) that will be stripped off in the course of gathering the tokens that form the argument.

For detecting whether a macro-token is trailed by tokens that form an undelimited macro argument of the kind "constellaton of tokens enclosed by an explicit character token of category 1 (begin group) and a matching explicit character token of category 2 (end group)", you'd need some facility which would allow to look ahead at the "shape" of the next non-space-token that in the token-stream follows the macro-token in question.

The bad news is: In TeX-engines (other than LuaTeX) there are no facilities for looking ahead at the "shape" of the next token that in the token-stream follows a macro-token.

The good news is: You can have TeX do an "educated guess" by instead of having TeX look ahead at the "shape" of the next token that in the token-stream follows a macro-token having TeX look ahead at the meaning of the next token that in the token-stream follows a macro-token, using a combination of \futurelet and \ifx. Those who implemented the LaTeX 2ε-macro \@ifnextchar used \futurelet for writing an intriguing loop for looking ahead at the meaning of the next non-space-token that in the token-stream follows a macro-token:

\documentclass{amsart}
\makeatletter
\newcommand{\test}{\@ifnextchar\bgroup{\test@brace}{\test@nobrace}}%
\newcommand\test@brace[1]{The argument is formed by the following tokens: \texttt{\detokenize{#1}}.}%
\newcommand\test@nobrace{\PackageError{gaoqiang}{Missing braced argument}{}}%
\makeatother

\begin{document}

\hrule\bigskip

\noindent
Here the educates guess works out and you get the expected text:

\smallskip

\noindent
\verb|\test{UNDELIMITED ARGUMENT ENCLOSED BY BRACES}|:\\
\test{UNDELIMITED ARGUMENT ENCLOSED BY BRACES}

\bigskip\hrule\bigskip

\noindent
Here the educates guess works out and you get the expected error message:

\smallskip

\noindent
\verb|\test THIS YIELDS AN ERROR.|:\\
\test THIS YIELDS AN ERROR.

\bigskip\hrule\bigskip

\noindent
Here the educates guess does \textbf{not} work out---although with the following
there is no \verb|{|, you  do not get an error-message:

\smallskip

\noindent
\verb|\test\bgroup THERE SHOULD BE AN ERROR MESSAGE BUT THERE IS NONE|:\\
\test\bgroup THERE SHOULD BE AN ERROR MESSAGE BUT THERE IS NONE

\bigskip\hrule\bigskip

\noindent
Besides this, the way in which \verb|\@ifnextchar| is implemented in edge cases
lets you create surprising results\dots

\makeatletter

\smallskip

\noindent
\verb|\test \reserved@d THERE SHOULD BE AN ERROR MESSAGE BUT THERE IS NONE.|:\\
\test \reserved@d THERE SHOULD BE AN ERROR MESSAGE BUT THERE IS NONE.

\smallskip

\noindent
\dots and surprising error-messages:

\smallskip
\noindent
\verb|\test \@sptoken THE ERROR-MESSAGE DIFFERS FROM ``Missing braced argument''.|:\\
\test \@sptoken THE ERROR-MESSAGE DIFFERS FROM ``Missing braced argument''.

\bigskip\hrule

\end{document}

enter image description here

Excerpt from the messages on the console/terminal and .log-file:

[...]
! Package gaoqiang Error: Missing braced argument.

See the gaoqiang package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              
                                                  
l.30 \test T
            HIS YIELDS AN ERROR.
? 
[...]
! Use of \reserved@c doesn't match its definition.
<recently read> \@sptoken 
                          
l.66 \test \@sptoken
                     THE ERROR-MESSAGE DIFFERS FROM ``Missing braced argumen...

? 
[...]

Besides not being one hundred percent reliable, the educated guess by via \@ifnextchar/\futurelet checking the meaning instead of the shape has another drawback: \futurelet is an assignment. Assignments are not carried out in the stage of expansion. They are carried out in later stages of processing. Therefore having TeX "look ahead" at the meaning of the following token does not work out in contexts where only expansion is done. Such contexs are, e.g.,

  • expansion of tokens in the course of gathering the ⟨balanced text⟩ and ⟨right brace⟩ of a ⟨definition⟩ in terms of \edef or \xdef.
  • expansion of tokens after \csname in the course of gathering a sequence of explicit character-tokens that forms the name of a ⟨control sequence token⟩ and the matching \endcsname.
  • expansion while scanning for the { of a ⟨general text⟩.
  • expansion of the ⟨general text⟩ of \write or \message.
  • expansion while gathering the first token and probably subsequent tokens of a ⟨number⟩.
  • expansion while scanning for ⟨one optional space⟩, e.g., with some kinds of ⟨number⟩.
  • expansion while scanning for an ⟨internal quantity⟩ for \the.
  • ...

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