1
\documentclass{article}
% RN. 09 Feb 2017
% BRIEF DESCRIPTION:
%  \cs_ functions are defined for a number of parameters. Evidently there is no 
%  provision of defining parameters for which arguments are optional (as there 
%  is in \NewDocumentCommands), but it seems that when a \cs_ function is called, 
%  trailing arguments are allowed to be missing, either (a) by being represented 
%  in the call through an empty pair of braces {}, or (b) with the argument missing 
%  altogether in the call. An empty pair of braces, case (a), is recognised in the
%  \cs_ function's internal code both as "blank" and as "empty", however an 
%  argument missing altogether, case (b), is not recognized as   a "-noValue-" 
%  condition as one might have expected. Is there any way to identify missing 
%  arguments in case (b)? 
%=======================
\usepackage{comment}
\usepackage[check-declarations]{expl3}
\usepackage{xparse}
%-----------------------
\ExplSyntaxOn
%  Variable Declarations:
\tl_new:N \l_rn_auxOne_tl
\tl_new:N \l_rn_auxTwo_tl
\tl_new:N \l_rn_auxThree_tl
\tl_new:N \g_rn_message_tl
\NewDocumentCommand\myTESTFunctionCalls{O{dsf134t}O{AAA}O{235711131719}}
%  RN. Thursday 9 Feb 2017
%  PARAMETERS: #1 tl,#2 tl,#3 tl 
%  BRIEF DESCRIPTION: calls a \cs function and investigates what happens if parameters 
%  are missing in calls, (a) by being represented through an empty pair of braces {}, 
%  or (b) missing altogether.
   {
\group_begin:
    \textbf{Call~1.~\textbackslash rn\_ someFunction:nnn \{\#1\}\{\#2\}\{\#3\}~~:}\\
    \rn_someFunction:nnn {#1}{#2}{#3}
    \textbf{Call~2.~\textbackslash rn\_ someFunction:nnn \{\#1\}\{\}\{\#3\}~~:}\\
    \rn_someFunction:nnn {#1}{}{#3}
    \textbf{Call~3.~\textbackslash rn\_ someFunction:nnn \{\#1\}\{\#2\}~~:}\\
    \rn_someFunction:nnn {#1}{#2}
\group_end:
  }  %  \myTESTFunctionCall

\cs_new:Npn \rn_someFunction:nnn #1#2#3
%  RN. Thursday 9 Feb 2017
%  PARAMETERS: #1 tl, #2 tl, #3 tl    
%  RETURNS: none
  {
\group_begin:
    %  processing argument #1:
    \rn_argPreprocessor:nn {#1}{\g_rn_message_tl}
    \tl_set:Nn \l_rn_auxOne_tl {#1}
    \tl_gput_left:Nn \g_rn_message_tl {argument~1~received:~}
    \tl_gput_right:Nn \g_rn_message_tl {\l_rn_auxOne_tl}
    \g_rn_message_tl \\
    %  processing argument #2:
    \rn_argPreprocessor:nn {#2}{\g_rn_message_tl}
    \tl_set:Nn \l_rn_auxTwo_tl {#2}
    \tl_gput_left:Nn \g_rn_message_tl {argument~2~received:~}
    \tl_gput_right:Nn \g_rn_message_tl {\l_rn_auxTwo_tl}
    \g_rn_message_tl \\
    %  processing argument #3:
    \rn_argPreprocessor:nn {#3}{\g_rn_message_tl}
    \tl_set:Nn \l_rn_auxThree_tl {#3}
    \tl_gput_left:Nn \g_rn_message_tl {argument~3~received:~}
    \tl_gput_right:Nn \g_rn_message_tl {\l_rn_auxThree_tl}
    \g_rn_message_tl \\
\group_end:
  }  %  \rn_someFunction:nnn

\cs_new:Npn \rn_argPreprocessor:nn #1#2
%  just a little Helper function
  {
\group_begin:
    \tl_gclear:N #2
    \IfNoValueTF {#1}
      {\tl_gput_right:Nn #2 {-NoValue-~}}
      {
            \tl_if_blank:nTF {#1}
                {\tl_gput_right:Nn #2 {\textbf{blank}}} 
            {}
        \tl_if_empty:nTF {#1} 
            {\tl_gput_right:Nn #2 {\textbf{,~empty}}}
            {}  
      }
\group_end:
      }  %  \rn_argPreprocessor:nn
 \ExplSyntaxOff
   %-----------------------
\begin{document}
    \myTESTFunctionCalls
\end{document}
  • In the last call of \rn_someFunction:nnn, the third argument is \group_end:. These are TeX macros, after all. – egreg Feb 9 '17 at 12:02
3

Your complicated code boils down to this:

\documentclass{article}
\usepackage{expl3}

\ExplSyntaxOn

\cs_new_protected:Nn \test_function:nnn
 {
  \tl_show:n { \#1=#1,~\#2=#2,~#3=#3}
 }

\group_begin:
\test_function:nnn { A } { B } { C }
\test_function:nnn { A } { B }
\group_end:

which gives, when run interactively,

> \#1=A, \#2=B, C=C.
<recently read> }

l.12 \test_function:nnn { A } { B } { C }

? 
> \#1=A, \#2=B, \group_end: =\group_end: .
<recently read> }

l.14 \group_end:

There's no difference with TeX macros, because these are TeX macros to begin with and argument parsing is as described in the TeXbook.

In your code, the last call of \rn_someFunction:nnn is

    \rn_someFunction:nnn {#1}{#2}
\group_end:

and the third argument is \group_end:.

Without \group_end:, the next token or braced group in the input stream would be taken as the “missing argument”, with possible error if the next token is invalid as argument (for instance, if it is }).

  • and I had become accustomed to naively assume that the purpose of \group_begin: \group_end: was to define the boundaries for the validity of local variables. To hear that \group_end: can be taken as the "missing argument" here suggests that Saint Knuth is lurking behind the scenes everywhere, a grey eminence. Avoiding to read the TeXbook is not an option. – Reinhard Neuwirth Feb 9 '17 at 20:35

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.