Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

Consider the following macro definitions:

\def\mymacro#1{\mymacroii#1\nil}
\def\mymacroii#1#2\nil{%
  \ifx\\#1\\%
    empty%
  \else
    #1%
  \fi
}%
\mymacro{foo}% -> f
\mymacro{}
\bye

This is supposed to extract the first character of the parameter to \mymacro. This works, but only if the argument is not empty.

How must I change my macros, so that the call \mymacro{} works as expected, i.e. outputs "empty"? Note, that is is only a simplified version. In reality I have several checks in \mymacroii for the first character, if it is a number, an opening brace, and so on.

This question is for sure related to Why does TeX remove braces around delimited arguments?, but I couldn't apply those informations to my case.

share|improve this question
    
Have you looked at the various token tests in expl3 (we have tests for opening brace, ... plus a very robust 'first token' function)? –  Joseph Wright Jan 10 at 9:51
    
@JosephWright No, I haven't. This code is supposed to be used in my pst-optexp package, which doesn't use expl3. I don't want to add that dependencies for now, especially because I haven't worked with it at all. –  Christoph Jan 10 at 9:53
1  
I meant 'you could see how we've tackled this and use the same ideas': ther has been lots of testing of a number of approaches to these 'low level' problems. –  Joseph Wright Jan 10 at 9:54
    
@JosephWright Thanks for the suggestion. I started from some ideas in the pstricks package. I had a look at the expl3 files, but couldn't get a good starting point. Where could I start for investigating on these specific tests? –  Christoph Jan 10 at 10:30

2 Answers 2

up vote 6 down vote accepted
\def\mymacro#1{\mymacroii#1{empty}\nil}
\def\mymacroii#1#2\nil{%
    #1%
}%

\mymacro{foo}% -> f

\mymacro{}
\bye

In your version, in the empty case, the inner call is

\mymacroii\nil

so #1 is \nil and then it starts looking for the next \nil (which isn't there) to make #2 and things go wrong.

share|improve this answer
    
Thanks. Unfortunately, my example was too minimal. In reality I have several different checks for the first argument in \mymacroii, it #1 is a number, a (, a < etc. I added this information to my question. –  Christoph Jan 10 at 9:51
    
@Christoph Can't you just do your tests sequentially? –  Joseph Wright Jan 10 at 9:52
    
@Christoph well you can add those to mine, the point is structuring that way means that #1 in your actual macro is never empty so you can write the tests knowing there is something there. –  David Carlisle Jan 10 at 9:53
    
@DavidCarlisle Thanks, it seems to work now. I'll test this solution in my package, before accepting the answer and closing the question. –  Christoph Jan 10 at 10:28

Just check for emptyness first.

\long\def\firstoftwo#1#2{#1}
\long\def\secondoftwo#1#2{#2}
\def\mymacro#1{%
  \ifx\\#1\\%
    \expandafter\firstoftwo
  \else
    \expandafter\secondoftwo
  \fi
  {empty}%
  {\mymacroii#1\nil}%
}
\def\mymacroii#1#2\nil{#1}

Just to get the flavor with LaTeX3, here's a version that allows you to define a function that examines the first token of the argument provided it was not empty in the first place (in this case it would output empty). It works also in expanded definitions, as shown below.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\mymacro}{ m }
 {
  \tl_if_empty:nTF { #1 }
   { empty }
   { \christoph_examine_first:f { \tl_head:n { #1 } } }
 }
\cs_new:Npn \christoph_examine_first:n #1
 {
  Whatever~you~want~with~#1~which~is~one~token
 }
\cs_generate_variant:Nn \christoph_examine_first:n { f }
\ExplSyntaxOff
\begin{document}

\mymacro{foo}

\mymacro{}

\edef\x{\mymacro{foo}\mymacro{}}\x

\end{document}
share|improve this answer
    
Thanks for your solution. I used David's answer, which fits better to the rest of my code. –  Christoph Jan 11 at 13:54

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.