5

I love the way \def can be (ab)used to parse very efficiently a string (another option would be to loop over all chars in LaTeX3 style or use LaTeX3 regexp, but it's faaaar slower, and often harder to write).

If we consider this MWE:

\documentclass[]{article}

\begin{document}

\def\firstTry#1MYSEP#2ENDOFLINE{
  The first try succeeded, I want to extract ``#2''.
}

\def\secondTry#1ENDOFLINE{
  The second try succeeded, I want to extract ``#1''.
}

\firstTry foo MYSEPHere is the line I want to extractENDOFLINE

%\firstTry This line has no separator, yet I want to run secondTry on itENDOFLINE

\end{document}

You can see that first line will be parsed correctly by \firstTry. Yes, the second commented line has no separator, so i will produce an error. Yet, I would like to also be able to do something with it (run \secondTry on it), without outputting an error message. Is it possible in TeX to "try" a parse, and if it fails to fallback on another mechanism?

1
  • 1
    You could write \firstTry text ENDOFLINE MYSEP\somemacro ENDOFLINE then test of #2 is \somemacro, and if so, use \secondTry on #1. If you're using expl3, \somemacro would be a quark Feb 20 at 12:16

3 Answers 3

6

Use appropriate delimited macros

\documentclass{article}

\def\firstTry#1EOL{\firstTryA#1SEPSEPEOL}
\def\firstTryA#1SEP#2SEP#3EOL{%
   I want to extract ``\ifx&#2&#1\else#2\fi''%
}

\begin{document}

\firstTry foo SEPHere is the line I want to extractEOL

\firstTry Here is the line I want to extractEOL

\end{document}

enter image description here

3
  • Thanks a lot. So fallback mechanism do no exist I guess.
    – tobiasBora
    Feb 20 at 12:25
  • @tobiasBora I guess you can put it in the \ifx statement if you want it.
    – campa
    Feb 20 at 12:29
  • 1
    @tobiasBora No, not built into TeX. You have to write your own. TeX just looks for whatever is in the macro parameter text, unconditionally. Feb 20 at 13:31
2

How about:

\documentclass{article}

\newcommand\UDfirstoftwo[2]{#1}
\newcommand\UDsecondoftwo[2]{#2}

\csname @ifdefinable\endcsname{\firstTry}{%
  \long\def\firstTry#1EOL{\firstTryA#1SEPSEPEOL{#1}}%
}%
\csname @ifdefinable\endcsname{\firstTryA}{%
  \long\def\firstTryA#1SEP#2SEP#3EOL#4{%
     % #3 either is empty or is the delimiter "SEP".
     \ifx\relax#3\relax\expandafter\UDfirstoftwo\else\expandafter\UDsecondoftwo\fi
     {\secondTry#4EOL}%
     {The first try succeeded, I want to extract ``\detokenize{#2}''.}%
  }%
}%
\csname @ifdefinable\endcsname{\secondTry}{%
  \long\def\secondTry#1EOL{%
    The second try succeeded, I want to extract ``\detokenize{#1}''.%
  }%
}%

\begin{document}

\ttfamily

\firstTry foo SEPHere is the line I want to extractEOL

\firstTry Here is the line I want to extractEOL

\noindent \fbox{!!! There is brace stripping:}

\firstTry{ foo }SEP{Here is the line I want to extract}EOL

\firstTry{Here is the line I want to extract}EOL

\end{document}

enter image description here


When processing things as delimited arguments, you might loose pairs of curly braces that surround the entire argument.

So in case of needing to preserve braces things might get more tricky; probably one should not think of things as "fallback" but just as "parsing and forking":

\documentclass{article}

\newcommand\UDfirstoftwo[2]{#1}%
\newcommand\UDsecondoftwo[2]{#2}%
\newcommand\UDgobble[1]{}%
\csname @ifdefinable\endcsname{\UDgobbleToSEP}{%
  \long\def\UDgobbleToSEP#1SEP{}%
}%
\csname @ifdefinable\endcsname{\UDstopromannumeral}{%
  \chardef\UDstopromannumeral=`\^^00 %
}%
\newcommand\UDCheckWhetherNull[1]{%
  \romannumeral\expandafter\UDsecondoftwo\string{\expandafter
  \UDsecondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UDsecondoftwo\string}\expandafter\UDfirstoftwo\expandafter{\expandafter
  \UDsecondoftwo\string}\expandafter\UDstopromannumeral\UDsecondoftwo}{%
  \expandafter\UDstopromannumeral\UDfirstoftwo}%
}%
\newcommand\Try{\TryPreventBraceRemoval{}}%
\csname @ifdefinable\endcsname{\TryPreventBraceRemoval}{%
  \long\def\TryPreventBraceRemoval#1EOL{%
    \expandafter\UDCheckWhetherNull\expandafter{\UDgobbleToSEP#1SEP}{%
       % There is no SEP that is not nested in braces in #1:
       \expandafter\DoMyThingWithArgIfNoSEP\expandafter{\UDgobble#1}%
    }{%
       % There is SEP that is not nested in braces in #1:
       \expandafter\DoMyThingWithArgIfSEP\expandafter{\UDgobbleToSEP#1}%
    }%
  }%
}%
\newcommand\DoMyThingWithArgIfNoSEP[1]{%
  The second try succeeded, I want to extract ``\detokenize{#1}''.%
}%
\newcommand\DoMyThingWithArgIfSEP[1]{%
  The first try succeeded, I want to extract ``\detokenize{#1}''.%
}%

\begin{document}

\ttfamily

\ttfamily

\Try foo SEPHere is the line I want to extractEOL

\Try Here is the line I want to extractEOL

\noindent \fbox{!!! There is no brace stripping:}

\Try{ foo }SEP{Here is the line I want to extract}EOL

\Try{Here is the line I want to extract}EOL

\end{document}

enter image description here

2

Perhaps slower, but definitely not harder to write.

\documentclass{article}

\ExplSyntaxOn

\NewDocumentCommand{\firstTry}{}
 {
  \peek_regex_replace_once:nnF
   { ([^\c{par}]*?) MYSEP ([^\c{par}]*?) ENDOFLINE }% search
   { The\ first\ try\ succeeded,\ I\ want\ to\ extract\ \2 }% replace
   { NO~MATCH }
 }

\ExplSyntaxOff

\begin{document}

\firstTry foo MYSEPHere is the line I want to extractENDOFLINE

\firstTry what?

\end{document}

Since the search is for (almost) arbitrary tokens, I chose to disqualify \par (that would match also an empty line).

enter image description here

You must log in to answer this question.

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