3

Is it possible to use the substr package to extract the name of a command, then invoke that command? For example:

\newcommand{\abc}{yay}

\csname \BeforeSubString{|}{abc|def}\endcsname

What I want to see:

yay

What I see:

Missing \endcsname inserted. [\csname \BeforeSubString{|}{abc|def}]
Extra \endcsname. [...ame \BeforeSubString{|}{abc|def}\endcsname]
  • \BeforeSubString is not expandable, so it won't work there. Do you need to use \BeforeSubString or some other command/package will do? – Phelype Oleinik Jun 27 at 1:33
  • it can be any command that achieves that goal. any package is fine. – 14jbella Jun 27 at 1:33
  • what does expandable/not expandable mean? – 14jbella Jun 27 at 1:34
  • 1
    TeX works by expanding macros (for example, expanding \abc to yay), but some operations are not expandable, which means that they need to be processed in a deeper level by TeX. See the second part of this answer for a brief description of that. If you want to read more, then see this thread too. – Phelype Oleinik Jun 27 at 1:38
  • hmm, so is it safe to say that string parsing is beyond the scope of latex? sorry I am new and don't know anything :) – 14jbella Jun 27 at 1:41
4

Here's an expandable version of \BeforeSubString which I (with all my creativity) called \ExpBeforeSubString:

\documentclass{article}

\makeatletter
\def\ExpBeforeSubString#1#2{%
  \expandafter\befsub@detoki\expandafter{\detokenize{#2}}{#1}}
\def\befsub@detoki#1#2{%
  \expandafter\befsub@detokii\expandafter{\detokenize{#2}}{#1}}
\def\befsub@detokii#1#2{%
  \rn@ifsinglechar{#1}%
    {\befsub@loop{}{#1}#2\q@rec@tail\q@rec@stop}%
    {\errmessage{`#1' should be a single, non-empty token.}}}
\def\q@rec@tail{\q@rec@tail}
\def\befsub@loop#1#2#3{%
  \befsub@iftail{#3}%
    {\befsub@return{#1}}%
    {%
      \befsub@match{#2}{#3}%
        {\befsub@return{#1}}%
        {\befsub@loop{#1#3}{#2}}%
    }%
}
\def\befsub@match#1#2{%
  \if#1#2%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}
\def\befsub@return#1#2\q@rec@stop{#1}
\def\befsub@iftail#1{%
  \ifx\q@rec@tail#1%
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi}
\edef\rn@catofamp{\the\catcode`\&}
\catcode`\&=11
\long\def\rn@gobble#1&{%
  \romannumeral-`0\rn@@gobble#1\rn@qrtail &}
\long\def\rn@@gobble#1#2&{%
  \ifx\rn@qrtail#1%
    \expandafter\rn@@gobble@end
  \else
    \expandafter\rn@de@tail
  \fi#2}
\def\rn@@gobble@end{ }
\long\def\rn@de@tail#1\rn@qrtail{ #1}
\long\def\rn@ifsinglechar#1{%
  \rn@ifempty{#1}%
    {\@secondoftwo}%
    {%
      \if\relax\expandafter\rn@gobble\detokenize{#1}&\relax
        \expandafter\@firstoftwo
      \else
        \expandafter\@secondoftwo
      \fi
    }%
}
\catcode`\&=\rn@catofamp
\long\def\rn@ifempty#1{%
  \if\relax\detokenize{#1}\relax
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
}
\makeatother

\begin{document}

\newcommand{\abc}{yay}
\csname \ExpBeforeSubString{|}{abc|def}\endcsname

\end{document}

Now using

\newcommand{\abc}{yay}
\csname \ExpBeforeSubString{|}{abc|def}\endcsname

yields yay. Yay!

Compare the length of code with the original (non expandable) version:

\makeatletter
\newcommand*\BeforeSubString[2]{%
  \su@ExpandTwoArgs{#1}{#2}\su@BeforeSubString
}
\newcommand*\su@BeforeSubString[2]{%
  \def\su@rest##1#1##2\@nil{##1}%
  \IfSubStringInString{#1}{#2}{\su@rest#2\@nil}{#2}%
}
\newcommand\su@ExpandTwoArgs[3]{%
  \protected@edef\su@SubString{#1}%
  \protected@edef\su@String{#2}%
  \expandafter\expandafter\expandafter#3%
  \expandafter\expandafter\expandafter{%
    \expandafter\su@SubString\expandafter
  }\expandafter{\su@String}%
}
\newcommand*\IfSubStringInString[2]{%
  \su@ExpandTwoArgs{#1}{#2}\su@IfSubStringInString
}
\newcommand*\su@IfSubStringInString[2]{%
  \def\su@compare##1#1##2\@nil{%
    \def\su@param{##2}%
    \ifx\su@param\@empty
      \expandafter\@secondoftwo
    \else
      \expandafter\@firstoftwo
    \fi
  }%
  \su@compare#2\@nnil#1\@nil
}
\makeatother

Or a version with xstring which allows you to store the match in a temporary macro:

\documentclass{article}

\usepackage{xstring}

\begin{document}

\newcommand{\abc}{yay}
\StrBefore{abc|def}{|}[\tempmacro]
\csname\tempmacro\endcsname

\end{document}
  • omg thank you so much!! I thought I was just asking a simple question, i guess latex is much less powerful than I expected! – 14jbella Jun 27 at 2:31
  • sorry i am not allowed to vote up but if i could i would – 14jbella Jun 27 at 2:32
  • @14jbella It's not that complicated, compared to other pieces of code out there. The first code has a bunch of security features to guarantee it will not explode, plus the extra complication for expandable code. You are facing this extra complication because you need/want expandability here, but you can always use the xstring version, which just requires an additional step. And I disagree, TeX can be quite powerful! Of course there are some things it simply can't do, but all languages have their limitations. (cont'd) – Phelype Oleinik Jun 27 at 2:59
  • @14jbella If you want to give a try at programming in LaTeX, I suggest you take a look at expl3. It's an attempt at providing a consistent programming API for LaTeX, which allows you do write some complicated code with relatively less effort. As I said, TeX can be quite powerful, you just have to get used to its oddities. Once you do, it's a quite fun procrastination tool :-) (P.S.: Don't worry about votes. However, you can mark the question as “solved” by clicking the check mark below it, if you want to.) – Phelype Oleinik Jun 27 at 3:02
  • 1
    The are three things a man must do before his life is done; write two lines in expl3 and make the buggers run. (Stolen from The Devil's DP Dictionary, more or less). – John Kormylo Jun 27 at 4:13

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.