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.

I am trying to define a macro with flexible arguments: 3 mandatory and 2 optional ones - like this:

\macro{<enter code here>}[arg2] arg3 = arg4 | arg5;

So args 2 and 5 are optional, as is the |.

I got quiet close to solving this with ideas found here and here.

Now I am stuck somewhere in TeX's stomach. Maybe someone can shed some light on the problem - I am a total newbie and I think, there must be plenty of possibilities. I have tried a lot but am not familiar enough with \expandafter, \futurelet, \xdef & friends. I am stuck here:

\makeatother
\def\@pop#1{}
\def\@split#1;{\def\@@tmp{#1|}\expandafter\@@split\@@tmp\@end@token}
\def\@@split#1|{\@ifnextchar\@end@token{{#1}{}\@pop}{\@@@split{#1}}}
\def\@@@split#1#2|{{#1}{#2}\@pop}

\def\display#1{\@ifnextchar[{\@display{#1}}{\@display{#1}[1]}}
\def\@display#1[#2]#3={\def\@tmp{\@@display{#1}{#2}{#3}}\expandafter\@tmp\@split}

\def\@@display#1#2#3#4#5{*#1*#2*#3*#4*#5*}
\makeatletter

\display{aaa}[bbb] AAA = BBB | CCC;

the output is supposed to look like this:

*aaa*bbb*AAA*BBB*CCC*

but instead I get

/tmp/problem.tex:56: Missing control sequence inserted.
&lt;inserted text&gt;
                \inaccessible 
l.42 \display{aaa} AAA = BBB;

...am using a href="http://gummi.midnightcoding.org/"gummi/a ...good stuff IMHO!

share|improve this question
add comment

5 Answers

up vote 8 down vote accepted

A solution using LaTeX2e just for \@ifnextchar:

\documentclass{article}
\makeatletter
\def\display#1{%
  \@ifnextchar[%]
    {\display@aux@i{#1}}
    {\display@aux@i{#1}[]}%
}
\def\display@aux@i#1[#2]#3=#4;{%
  \display@aux@ii{#1}{#2}{#3}#4||\@nil
}
\def\display@aux@ii#1#2#3#4|#5|#6\@nil{%
  *#1*#2*#3*#4*#5*
}
\makeatother
\begin{document}
\noindent
\display{aaa}[bbb] AAA = BBB | CCC; \\
\display{aaa}[bbb] AAA = BBB ;
\end{document}

Alternative xparse-based solution:

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\DeclareDocumentCommand \display 
  { m o u { = } > { \SplitArgument { 1 } { | } } u { ; } } {
  * #1 * #2 * #3 * \display_aux:nn #4 *  
}
\cs_new:Npn \display_aux:nn #1#2 { #1 * #2 }
\ExplSyntaxOff
\begin{document}
\noindent
\display{aaa}[bbb] AAA = BBB | CCC; \\
\display{aaa}[bbb] AAA = BBB ;
\end{document}
share|improve this answer
    
I felt that there would be an easy solution but I certainly didn't expect it to be THAT simple and I didn't expect it to turn up here THAT fast. Have you got some kind of "TEX-goggles" to see this? I tried hard to wrap my head around that ... amazed :-) ...thanks A LOT! –  Patrick2000 Jan 23 '11 at 13:47
1  
@Patrick. There are some well-known techniques for parsing optional material, which both Herbert and I have used. In the case of the [...], the key is to use \@ifnextchar, which you already knew. For the second optional argument, as the ; has to be there it is best to grab everything then re-parse the last argument looking for a |. By using two of these and an appropriate end point you can avoid needing to test for | explicitly. The method is to use #4|#5|#6\@nil (or similar), with #6 as a dummy argument that is thrown away. –  Joseph Wright Jan 23 '11 at 13:54
    
Thank you for your explanations. I have a small follow-up question concerning \@ifnextchar: I understand that is not in core (knuth) tex bit in some latex ... addon? I am not sure if i looked in the right place because it is not easy to find with find | grep. Do you know by heart in which file the definition is ? And maybe you could give me a rough idea of how it works... ? :-) just curios –  Patrick2000 Jan 23 '11 at 14:41
    
@Patrick. That would count as 'a separate question' :-). It's defined in latex.ltx. –  Joseph Wright Jan 23 '11 at 14:51
add comment

the same, only the other way round :-)

\documentclass{minimal}

\makeatletter
\def\display#1{\@ifnextchar[{\display@i{#1}}{\display@i{#1}[]}}
\def\display@i#1[#2]#3;{\display@ii#3||\@nil{#1}[#2]}
\def\display@ii#1=#2|#3|#4\@nil#5[#6]{*#5*#6*#1*#2*#3*}

\def\blub{\@ifnextchar[\blub@i{\blub@i[]}}
\def\blub@i[#1]#2;{\blub@ii[#1]#2++\@nil}
\def\blub@ii[#1]#2+#3+#4\@nil{blub[#1] & #2 & #3}
\makeatother


\makeatother

\begin{document}

\display{aaa}[bbb] AAA = BBB|CCC;

\display{aaa}[bbb] AAA = BBB;

\end{document}
share|improve this answer
    
All right ... nice compilation of parsing techniques now. –  Patrick2000 Jan 23 '11 at 14:15
    
Thanks as well for this one! –  Patrick2000 Jan 23 '11 at 14:41
    
Another Patrick - welcome :) –  topskip Jan 24 '11 at 13:42
add comment

You have already received valid LaTeX solution, but for someone else who wants to do something similar in ConTeXt, I am including a ConTeXt solution. This is essential similar to LaTeX solutions using \@ifnextchar, but use a higher level macro \dodoubleargument that checks for two optional arguments (ConTeXt also provides other variants of \do...argument: single, double, triple, quadruple, quintuple, sixtuple, and seventuple).

\def\Macro#1%
  {\dodoubleargument\doMacro[#1]}

\def\doMacro[#1][#2]#3=#4;%
  {\dostartMacro{#1}{#2}{#3}#4||\dostopMacro}

\def\dostartMacro#1#2#3#4|#5|#6\dostopMacro
  {*#1*#2*#3*#4*#5*}

\starttext
\startlines
\Macro{aaa} AAA = BBB ;
\Macro{aaa} AAA = BBB | CCC ;
\Macro{aaa}[bbb] AAA = BBB ;
\Macro{aaa}[bbb] AAA = BBB | CCC; 
\stoplines
\stoptext
share|improve this answer
add comment

Here's yet another LaTeX solution not unlike this related answer of mine; the new feature is that it's completely free of explicit calls of \@ifnextchar. (This also makes \makeatletter / \makeatother dispensable, so maybe it's a bit more accessible for the end user.)

\documentclass{minimal}
\newcommand\display[1]{*#1*\displayi}
\newcommand\displayi[1][]{#1*\displayii}
\def\displayii#1=#2;{#1*\displayiii#2||;}
\def\displayiii#1|#2|#3;{#1*#2}
\begin{document}
\display{aaa} AAA = BBB | CCC; \par
\display{aaa}[bbb] AAA = BBB | CCC; \par
\display{aaa}[bbb] AAA = BBB ;
\end{document}

Note that I used the fact that the arguments #1 to #5 occur in their natural order in the replacement of \display; this could be adjusted if needed.

share|improve this answer
    
Well, it uses \@ifnextchar within \displayi, although you don't see it :-) –  Joseph Wright Jan 24 '11 at 12:49
    
\newcommand is a high level LaTeX macro and it uses more than an internal \@ifnextchar ... –  Herbert Jan 24 '11 at 13:06
    
@Joseph, @Herbert: I know, and in the end, \futurelet will be used. I'll try and improve the wording. –  Hendrik Vogt Jan 24 '11 at 13:18
    
@Herbert: I think using \newcommand if possible is better because it does more, e.g. testing if the macro is already defined. This advantage is of course somewhat lost as I use \def later on, anyway. –  Hendrik Vogt Jan 24 '11 at 13:19
    
I like this one because it beats my blub() by 12 chars, parsing 5 (not only 3) args and avoids \makeatother. Who cares about "plain tex" ? :-) I have been digging into the sources a bit now... –  Patrick2000 Jan 24 '11 at 21:12
show 1 more comment

I learned a lot - now let me show off this pure TeX version, using \futurelet: (if you can shorten it, feel free to edit)

\documentclass{article}

\def\b{\ba[]}
\def\ba[#1]#2;{\bar{#1}#2++\nil}
\def\bar#1#2+#3+#4\nil{blub[#1]&#2&#3}
\def\blu{\ifx\next[\let\f\ba\else\let\f\b\fi\f}
\def\blub{\futurelet\next\blu}

\begin{document}
\begin{tabular}{lll}
\blub foo;                  \\
\blub[bla] bar;                     \\
\blub foo + bar;            \\
\blub[bla] foo + bar;               \\
\end{tabular}
\end{document}

Oh ... i was just wondering how remote images can NOT be disabled ... alt text

share|improve this answer
    
@Patrick: I do not really know what you want to achieve with your code, but the one added to my answer does the same. –  Herbert Jan 23 '11 at 19:34
1  
@Herbert: This was just a "research project" whose goal was to solve the problem in "pure tex". I mean the tex core application binary macro engine, written in c most probably (want to study it...), without any "latex or other tex .sty libraries loaded. A solution using only tex "primitives". (quoting Donald Knuth in "The Texbook", page 9: "About 300 of TEX’s control sequences are called primitive; these are the low-level atomic operations that are not decomposable into simpler functions.") –  Patrick2000 Jan 23 '11 at 20:05
    
@Patrick. TeX82 is written in Pascal, using the WEB system. Knuth never expected you just to use primitives, and indeed much of 'plain TeX' is macros, not primitives. Things like \@ifnextchar are ultimately defined in primitives, of course, as LaTeX is a macro package on top of TeX (the engine) and not a modified binary. –  Joseph Wright Jan 23 '11 at 21:36
    
the tex/latex source is a bit scary as you find more than a dozen unpopular file extensions like ".sty" ".ltx" and such stuff in the archive. and i had a tough time locating my "binary" i was looking for. thought it would be part of texlife. finally found branches/stable/source/src/texk/web2c/pdftexdir/pdftex.web in the pdftex project - foundry.supelec.fr/gf/project/pdftex. so tex is written in web is written in c (originally pascal). huh. i'm glad i finally took the time to find this out :-) –  Patrick2000 Jan 24 '11 at 21:18
    
pdftex.web is derived from tex.ac.uk/CTAN/systems/knuth/dist/tex/tex.web. and web is converted to c these days with "web2c" ... but in tex82 is used to be converted to pascal (correct?) and what was the name of that system again? ... anyway ... the last thing i wonder now, is... –  Patrick2000 Jan 24 '11 at 21:25
show 3 more comments

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.