4

Assume a scenario where some macro triggers error-messages.

The error-messages shall also reveal the name of the macro which did trigger the error-message.

One could do something like this:

\newcommand\MYCOMMAND{%
  % ...
  \GenericError{%
    \space\space\space\@spaces\@spaces\@spaces
  }{%
    A point is reached where carrying out macro
    \string\MYCOMMAND\space triggers an error-message...
  }{%
    See the documentation for more information.%
  }{%
    More explanation.%
  }%
  % ...
}%

The problem is:

If you do something like \let\MYSAVEDCOMMAND=\MYCOMMAND, then the error-message with \MYSAVEDCOMMAND will also contain the phrase \MYCOMMAND although here the phrase should be \MYSAVEDCOMMAND.
This is because the phrase is "hardcoded" inside the definition-text.

If you had syntax where #0 denotes the result of applying \string to the macro-token (control-sequence-token/active character-token) that currently is expanded, then you could do without such "hardcoding":

\newcommand\MYCOMMAND{%
  % ...
  \GenericError{%
    \space\space\space\@spaces\@spaces\@spaces
  }{%
    A point is reached where carrying out macro
    #0 triggers an error-message...
  }{%
    See the documentation for more information.%
  }{%
    More explanation.%
  }%
  % ...
}%

My questions are:

Would it be hard to implement and add this feature to the TeX program?

(As you can see from the compiler-generated error-messages with macros that process delimited arguments and hereby encounter unbalanced curly right braces, TeX seems to already keep track of names of macro tokens:

The example

\def\foo#1delimiter{definition text, argument: #1}
\foo bla}delimiter
\bye

delivers:

! Argument of \foo has an extra }.

)

Are there drawbacks or pitfalls or problems with this #0-feature?

Would code written for compiling with current TeX engines be incompatible to engines where such a #0-feature was available?

5
  • 1
    Interesting question. One minor drawback I can think of is that this would expose internal names in macros where you pass the body of another macro as argument, e.g. in expl3 various \*_map_inline: functions. – siracusa Sep 3 '19 at 2:50
  • It is not clear to me what "body of a macro" is. Do you mean parameter-text+definition-text? In scenarios with macro-mechanisms where a user-level macro triggers an expansion-cascade with internal macros the user-level-macro "could pass its name" via parameter #0 as macro-argument to internal macros. In the internal macros that name would be available as one of #1.. #9 for (generic) error-messages where only the name of the user-level macro occurs... Of course there would still be the possibility of error-messages triggered by the compiler at the time of processing internal macros... – Ulrich Diez Sep 3 '19 at 8:14
  • What I mean are scenarios where you pass an "inline function" as an argument to a macro, i.e. material that is internally used as the replacement text of a temporary macro definition. For example, expl3 has a function \tl_map_inline:nn which maps a function over each token of a token list. The function definition here is given "inline", e.g. just \textit{#1}. In such cases the internal macro name could be exposed, which is conceptionally unpleasant but no big problem at all. – siracusa Sep 3 '19 at 21:28
  • @siracusa I am not that familiar with expl3 but if I got you right, it might boil down to scenarios like this: \newtoks\mytoks\mytoks{my definition-text with parameter #1.}\expandafter\def\expandafter\internalscratchmacro\expandafter#\expandafter1\expandafter{\the\mytoks}\show\internalscratchmacro\csname stop\endcsname\bye. In such a situation passing #0 into \mytoks might make it possible to reveal that the macro defined from the token-register has the name \internalscratchmacro. – Ulrich Diez Sep 3 '19 at 23:17
  • Exactly. Not sure if there are scenarios where this might be actually useful. For the improved error message scenario you described you'd probably want to present the user the name of the calling macro, not the internal one. – siracusa Sep 3 '19 at 23:28
6

How hard is it to implement:

When the error

! Argument of \foo has an extra }.

is generated, TeX is still in the process of scanning the parameters. During this process, the name which has been used to call the macro is still available. (in warning_index if you want to look into the source) The situation changes after all parameters have been read: Then TeX starts a new input level and reads the macro body, inserting parameters in the process. Here warning_index is no longer set to the macro name.

But while the error message example doesn't work out, something also does: For every input level, TeX stores a name, in case of macro expansion this is the csname. So the name is available.

For example in LuaTeX (should be similar in other engines but I prefer C over Pascal), a variant of your #0 can be implemented by the patch: (This code replaces #0 with the macro token, not with the result of applying \string, so you have to add \string yourself. That could be combined without significantly more work, but this is more flexible. E.g. it allows you to use \csstring instead)

diff --git a/source/texk/web2c/luatexdir/tex/scanning.c b/source/texk/web2c/luatexdir/tex/scanning.c
index 8f7e1d591..5064c5401 100644
--- a/source/texk/web2c/luatexdir/tex/scanning.c
+++ b/source/texk/web2c/luatexdir/tex/scanning.c
@@ -2183,7 +2183,7 @@ halfword scan_toks(boolean macro_def, boolean xpand)
                 else
                     get_token();
                 if (cur_cmd != mac_param_cmd) {
-                    if ((cur_tok <= zero_token) || (cur_tok > t)) {
+                    if ((cur_tok < zero_token) || (cur_tok > t)) {
                         print_err("Illegal parameter number in definition of ");
                         sprint_cs(warning_index);
                         help3(
diff --git a/source/texk/web2c/luatexdir/tex/textoken.c b/source/texk/web2c/luatexdir/tex/textoken.c
index d6c168906..3de298753 100644
--- a/source/texk/web2c/luatexdir/tex/textoken.c
+++ b/source/texk/web2c/luatexdir/tex/textoken.c
@@ -2018,8 +2018,15 @@ static boolean get_next_tokenlist(void)
                 break;
             case out_param_cmd:
                 /*tex Insert macro parameter and |goto restart|. */
-                begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
-                return false;
+                if (cur_chr == 0) {
+                    cur_cs = iname;
+                    cur_cmd = call_cmd;
+                    cur_chr = istart;
+                    return true;
+                } else {
+                    begin_token_list(param_stack[param_start + cur_chr - 1], parameter);
+                    return false;
+                }
                 break;
         }
     }

About compatibility: This change only affects code which uses #0. As can be seen in the code above changing scan_toks, using #0 in normal TeX will always lead to an error Illegal parameter number. So it is compatible in the sense that every TeX document which does not produce an error message in a normal engine will still work without change in behavior in the modified one.

So I think the biggest problem is that it is of limited use: For error messages TeX already shows some context, normally including the macro name. So it doesn't add a lot of information while adding (a bit of) complexity to one of TeX's most central routines (get_next_tokenlist) and making TeX's macro expansion rules more complicated.

2
  • 1
    Thank you very much for your detailed answer. Not applying \string brings more flexibility. But it also brings the flexibility of easily creating infinite recursive loops via \def\macro{#0}. So perhaps the benefit would be "overruled" by the new pitfalls... (I don't know what I was thinking. I shouldn't have brought up the question.) – Ulrich Diez Sep 3 '19 at 19:40
  • @UlrichDiez I don't think that's a big concern; after all one easily write \def\macro{\macro} right now. This #0 feature seems like the sort of thing that Knuth might have added in the early days of TeX if enough people had asked for it, but the thinking at the time was that not many users would use macros much. :-) – ShreevatsaR Sep 3 '19 at 22:53

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.