12

Suppose a given macro is not already \defined \long, but I want to call it with \pars in the argument. Is there any way to turn off TeX's error checking for one call? What I have been doing is roughly

\def\short#1{#1}
%\short{a
%
%b}
    %% The commented code above causes an error
\def\aslong{\def\hiddenpar{\par}}
\aslong\short{a\hiddenpar b}

This works, in the sense that it produces the desired expansion, but it can hardly be said to be elegant. I suppose that one could use some cat-code monkeying to achieve a slightly more elegant call syntax, but everything I can think of would become more brittle.

5
  • 1
    you can use \endgraf rather than \hiddenpar which is there for exactly that reason. (at least when \par has its normal meaning) Jun 14, 2015 at 23:53
  • @DavidCarlisle, fair enough, although it still seems inelegant. Nonetheless, if that's the best possible (i.e., if there is no way to turn off TeX's error checking), then I will accept it if you post it as an answer.
    – LSpice
    Jun 15, 2015 at 0:00
  • Can you define a parallel version of the \short macro that's "long"?
    – Mico
    Jun 15, 2015 at 0:03
  • the only other way is to extract the definition of \short and redefine another macro that is \long, but that can be tricky if you have delimited macros etc Jun 15, 2015 at 0:03
  • @Mico (and DavidCarlisle), indeed, I am trying to use macros whose definition is not in my control, so that it is not trivial to make an identical \long'd version.
    – LSpice
    Jun 15, 2015 at 1:22

4 Answers 4

13

You can not turn off the error check however plain (and latex) have \endgraf which is \let (rather than \def) to the primitive \par and is there for exactly this reason so you can use

\short{a\endgraf b}

the only other alternative would be to define a \long version of the command, which is trivial if copying the source is a possibility, but can be tricky if just the macro is available, especially if it has delimited arguments. If etex is available, basically you can use \scantokens to re-parse a \long\def that can be constructed from the output from \meaning\short.

1
  • Wow, as an old Perl programmer, that \scantokens trick makes me think of all the goodness of source filters in that language. I'll swallow my æsthetic taste and stick with \endgraf!
    – LSpice
    Jun 15, 2015 at 1:23
6

If you have a simple macro, say with two undelimited arguments, it's possible to patch it for becoming \long without e-TeX:

\def\foo#1#2{#1-#2}

\begingroup\toks0=\expandafter{\foo{#1}{#2}}
\edef\x{\endgroup\long\def\noexpand\foo##1##2{\the\toks0 }}\x

\foo{a\par b}{c\par d}

{\tt\meaning\foo}

(see https://tex.stackexchange.com/a/39980/4427 for an explanation of the trick).

enter image description here

If the macro has delimited arguments or you don't want to bother knowing how many arguments it takes, \scantokens is your friend, but requires an e-TeX enabled engine, such as pdfTeX, XeTeX or LuaTeX.

\def\makelong#1{%
  \expandafter\getparts\meaning#1\relax
  \domakelong#1%
}

\begingroup
\catcode`M=12 \catcode`A=12 \catcode`C=12 \catcode`R=12 \catcode`O=12
\lowercase{\endgroup\def\getparts#1MACRO:#2->#3\relax}{%
  \def\prefixes{#1}%
  \def\parameters{#2}%
  \def\replacement{#3}%
}

\def\domakelong#1{%
  \edef\temp{\long\prefixes\def\noexpand#1\parameters{\replacement}}%
  \scantokens\expandafter{\temp}%
}

\protected\def\short#1{==#1==}
\def\nonlong#1+#2-{(#1)--(#2)}

\makelong\short
\makelong\nonlong

\short{a\par b}

\nonlong X\par Y+Z\par W-

{\tt\meaning\short}

{\tt\meaning\nonlong}

\bye

enter image description here

3
  • Thank you! In fact, your answer that you link makes me wonder if this is a duplicate. Do you think so?
    – LSpice
    Jun 15, 2015 at 19:13
  • @LSpice They are related
    – egreg
    Jun 15, 2015 at 19:15
  • \scantokens-technique relies on the same catcode-régime being in effect both at the time of defining the original variant and at the time of re-tokenizing things in terms of \scantokens. You may also need to take care of \newlinechar, \endlinechar and \escapechar. Aug 5, 2020 at 23:34
5

If you are using LuaTeX then you can set \suppresslongerror to a positive value. For example

\def\short#1{#1}

\suppresslongerror=1
\short{a

b}
\suppresslongerror=0

or

\long\def\forcelong#1{\suppresslongerror=1 #1\suppresslongerror=0 }

\forcelong{\short{a

b}}
1

Off the cuff I can only offer a \romannumeral0-expansion-driven recursive routine \ReplaceEveryPar:

\ReplaceEveryPar{⟨replacement text⟩}%
                {⟨arbitrary token-sequence where to replace \par⟩}%

Each control word token \par of the ⟨arbitrary token-sequence where to replace \par⟩ will be replaced by ⟨replacement text⟩.

You obtain the result after two expansion-steps, i.e., in expansion-contexts you get the result after "hitting" \ReplaceEveryPar by two \expandafter.

As a side-effect the routine does replace matching pairs of explicit character tokens of catcode 1 and 2 by matching pairs of curly braces of catcode 1 and 2.

I suppose this won't be a problem in most situations as usually the curly braces are the only characters of category code 1 / 2...

In some situations you can use that routine for replacing every token \par, e.g., by \csname par\endcsname when having a token-sequence which forms the call to a short macro with \par occurring in the short macro's arguments:

With

\def\removemacrophrase#1>{}%

\def\MyShortMacro#1#2{%
  \ifvmode\noindent\else\hfil\break\fi
  \def\tempa{#1}%
  Short macro's first argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
  \hfil\break
  \def\tempa{#2}%
  Short macro's second argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
}%

the sequence

\ReplaceEveryPar{\csname par\endcsname}{%
  \MyShortMacro{A\par B}{C\par D}%
}%

yields

\MyShortMacro{A\csname par\endcsname B}{C\csname par\endcsname D}%

, which in turn yields

  \ifvmode\noindent\else\hfil\break\fi
  \def\tempa{A\csname par\endcsname B}%
  Short macro's first argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
  \hfil\break
  \def\tempa{C\csname par\endcsname D}%
  Short macro's second argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%

Above I wrote "in some situations" because there are situations where things get tricky:

E.g., with (short) macros whose arguments are delimited by the control word token \par replacing that control word token should take place with the arguments but not with the argument-delimiters.

Things get even worse in case \par was let equal to { at the time of using #\par-notation for definig a macro whose last argument is delimited by a control-word-token \par which will get reinserted into the token-stream behind the replacement-text of that macro.

\catcode`\@=11
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\long\def\UD@firstoftwo#1#2{#1}%
\long\def\UD@secondoftwo#1#2{#2}%
\long\def\UD@PassFirstToSecond#1#2{#2{#1}}%
\long\def\UD@Exchange#1#2{#2#1}%
\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@CheckWhetherNull{<Argument which is to be checked>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is empty>}%
%%                     {<Tokens to be delivered in case that argument
%%                       which is to be checked is not empty>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\long\def\UD@CheckWhetherNull#1{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\long\def\UD@CheckWhetherBrace#1{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument's first token is an explicit
%% space token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is a
%%                               space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               a space-token>}%
\long\def\UD@CheckWhetherLeadingSpace#1{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%
%% The `\romannumeral0-expansion-driven loop in terms of 
%% \UD@ExtractFirstArgLoop has 
%%  ( <amount of non-brace-nested \UD@Seldom not occurring as
%%    the very first token within \UD@ExtractFirstArg's 
%%    argument> + 2 )
%% iterations.
%% This implies: Usage of \UD@SelDOm within \UD@ExtractFirstArg's 
%% argument is _not_ forbidden. 
%%.............................................................................
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\long\def\UD@ExtractFirstArg#1{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\long\def\UD@ExtractFirstArgLoop#1{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% Check Whether argument which must consist of a single token is the token 
%% \par
%%-----------------------------------------------------------------------------
\long\def\UD@CheckWhetherpar#1#2#3{%
  \UD@@Checkkwhetherparfork#1{#2}\par{#3}\par\par\par
}%
\long\def\UD@@Checkkwhetherparfork#1\par#2#3\par\par\par{#2}%
%%=============================================================================
%% \ReplaceEveryPar{<replacement text>}{<argument where to replace \par>}%
%%
%%   Each control word token \par of the <argument where to replace \par>
%%   will be replaced by <replacement text>.
%%
%%   You obtain the result after two expansion-steps, i.e., 
%%   in expansion-contexts you get the result after "hitting" 
%%   \ReplaceEveryPar by two \expandafter.
%%   
%%   As a side-effect, the routine does replace matching pairs of explicit
%%   character tokens of catcode 1 and 2 by matching pairs of curly braces
%%   of catcode 1 and 2.
%%   I suppose this won't be a problem in most situations as usually the
%%   curly braces are the only characters of category code 1 / 2...
%%-----------------------------------------------------------------------------
\long\def\ReplaceEveryPar#1#2{%
   \romannumeral0\UD@ReplaceEveryParLoop{#2}{}{#1}%
}%
\long\def\UD@ReplaceEveryParLoop#1#2#3{%
  % #1 = remaining arbitrary token-sequence where to replace \par
  % #2 = token-sequence with replacement applied constructed so far
  % #3 = replacement text
  \UD@CheckWhetherNull{#1}{ #2}{%
    \expandafter\UD@ReplaceEveryParLoop
    \romannumeral0%
    \UD@CheckWhetherLeadingSpace{#1}{%
       \expandafter{\UD@removespace#1}{#2 }%
    }{%
      \expandafter\UD@PassFirstToSecond\expandafter{%
        \romannumeral0%
        \UD@CheckWhetherBrace{#1}{%
          \expandafter\UD@PassFirstToSecond
          \expandafter{%
            \romannumeral0%
            \expandafter\UD@ReplaceEveryParLoop
            \romannumeral0%
            \UD@ExtractFirstArgLoop{#1\UD@SelDOm}{}{#3}%
          }{ #2}%
        }{%
          \expandafter\UD@CheckWhetherpar
          \romannumeral0\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{ #2#3}{%
            \expandafter\UD@Exchange
            \romannumeral0\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{ #2}%
          }%
        }%
      }{%
        \expandafter{\UD@firstoftwo{}#1}%
      }%
    }%
    {#3}%
  }%
}%
\catcode`\@=12
%%=============================================================================

\tt\frenchspacing

\ReplaceEveryPar{\Rap}{%
  \def\macro#1{text\par #1 text{\par}}%
}%

\string\macro: \meaning\macro

\bigskip

\def\removemacrophrase#1>{}%

\def\MyShortMacro#1#2{%
  \ifvmode\noindent\else\hfil\break\fi
  \def\tempa{#1}%
  Short macro's first argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
  \hfil\break
  \def\tempa{#2}%
  Short macro's second argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
}%

\ReplaceEveryPar{\csname par\endcsname}{%
  \MyShortMacro{A\par B}{C\par D}%
}%

\bye

enter image description here

2
  • I appreciate the obviously huge amount of work you put into this, but @DavidCarlisle's answer of using \endgraf accomplished what I needed and can be understood at a glance, so I'll stick with that for now, and keep this one in my back pocket. Thanks!
    – LSpice
    Aug 3, 2020 at 19:17
  • 1
    @LSpice Never mind. I just put some pieces of code together which I use all the time. That took me only a few minutes. ;-) Aug 3, 2020 at 20:22

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.