5

This MWE is boiled down so much the usefulness of the code is lost. However, the code nevertheless still exhibits the problem I'm encountering.

\documentclass{article}
\usepackage{pgffor}
\usepackage{etoolbox}

\makeatletter
\newcommand\aetest[1]{%%
  \foreach \myl  in {#1}
    {%%
      \expandafter\ae@parse@line\myl
    }%% 
}

%% read the next character and                
%% branch off if its "*"                      
\def\ae@parse@line{%%
  \@ifnextchar*
  {\s@ae@parse@line}
  {\@ae@parse@line[]}}

%% what to do if there is no "*"              
\def\@ae@parse@line[#1]#2#3{%%
  \fbox{Case #2}\hspace*{0.5em}%%
  \def\ae@tmp{#3}%%
  \typeout{==[base case][#2]==>\expandonce\ae@tmp}%%
  \@ae@parse@content#3\@nil
}

%% If there's  "*" the command now            
%% expects three arguments with the argument  
%% #3 dressed up like an optional argument.   
%% After processing, pass arguments back to   
%% "\@ae@parse@line"                          
\def\s@ae@parse@line*#1#2[#3]{%%
  \def\ae@tmp{#2}%%
  \typeout{==[star case]___==>\expandonce\ae@tmp}%%
  \@ae@parse@line[#3]{#1}{#2}}

%% the "content" argument                     
%% may start with "[" which                   
%% signals that the "content"                 
%% should go in a `minipage`.                 
\def\@ae@parse@content{%%
  \@ifnextchar[%%]
  {\@ae@parse@mp}
  {\@@ae@parse@content}}

%% formatting for "non-minipage" case         
\def\@@ae@parse@content#1\@nil{%%
  \textbf{#1}\par}

%% formatting for "minipage" case             
\def\@ae@parse@mp[#1]#2\@nil{%%
  \fbox{\begin{minipage}[t]{#1}
    #2
  \end{minipage}}\par}

\makeatother

\begin{document}

\aetest{
  {A}{formatted correctly},
 *{B} {formatted correctly}[],
 *{C} {[4in] spurious space, and is \textbf{not} correct formatted}[],
 *{D}{[4in] no spurious space and correctly formatted}[],
  {E} {[2in] spurious space but still correctly formatted},
  {F}{[3in] no spurious space, formatted correctly},
  {G} {done as expected}}

\end{document}

producing

enter image description here

What I'm curious about is why does case C not get correctly formatted, but case E does. It seems to me that, if they both suffer from the same spurious space, then I would expect them to be handled similarly.

Looking at the log file shows that in cases B and C the space is parsed as part of the argument and so the braces are not lost. But why do the braces then disappear is cases E and G?

Here's the relevant portion of the log file. The ==> points to the content of the passed argument and shows the spurious space and the braces.

==[base case][A]==>formatted correctly
==[star case]___==> {formatted correctly}
==[base case][B]==> {formatted correctly}
==[star case]___==> {[4in] spurious space, and is \textbf {not} correct formatted}
==[base case][C]==> {[4in] spurious space, and is \textbf {not} correct formatted}
==[star case]___==>[4in] no spurious space and correctly formatted
==[base case][D]==>[4in] no spurious space and correctly formatted
==[base case][E]==>[2in] spurious space but still correctly formatted
==[base case][F]==>[3in] no spurious space, formatted correctly
==[base case][G]==>done as expected
3
  • 2
    Argument #2 in \s@ae@parse@line is delimited (by [), so TeX doesn't skip spaces when looking for it. And, in case C, the initial space prevents the brace stripping.
    – egreg
    Commented May 21, 2014 at 20:42
  • @egreg I figured it had to be something simple like that. I'm sure I could figure this out if I thought about it for a bit, but, off the cuff, do you know a way to trim leading white space so that I don't have to worry about this from the user end? I was thinking of defining a macro \def\something #1\@nil{\def\noleadingwhitespace{#1}}, but that seems like it would interrupt the argument flow and create further headaches.
    – A.Ellett
    Commented May 21, 2014 at 20:46
  • BTW, there is another unwanted space after #2 in the minipage. At the end of a paragraph TeX only removes one space. Thus if #2 ends with a space it is kept. Commented May 22, 2014 at 3:21

2 Answers 2

7

Argument #2 in \s@ae@parse@line is delimited (by [), so TeX doesn't skip spaces when looking for it. And, in case C, the initial space prevents the brace stripping.

Use two steps:

%% If there's  "*" the command now
%% expects three arguments with the argument
%% #3 dressed up like an optional argument.
%% After processing, pass arguments back to
%% "\@ae@parse@line"
\def\s@ae@parse@line*#1#2{%
  \s@ae@parse@line@aux{#1}{#2}%
}
\def\s@ae@parse@line@aux#1#2[#3]{%
  \def\ae@tmp{#2}%%
  \typeout{==[star case]___==>\expandonce\ae@tmp}%%
  \@ae@parse@line[#3]{#1}{#2}}

In this way argument #2 will lose the braces correctly and you won't have problems of spaces because TeX ignores them when looking for undelimited arguments.

I didn't change anything else, so the image doesn't tell the truth. ;-)

Anyway, you'll be getting the same if you leave a space before the trailing optional argument like in

 *{C} {[4in] spurious space, and is \textbf{not} correct formatted} []% <-- a space!

enter image description here

5
  • I've seen that sort of thing in code before and always wondered why the arguments would be broken up that way over multiple macros. Not only have you solved this problem, but you've also explained something that's kind of nagged me when reading other's code. --- You've killed two birds with one stone. Thank you.
    – A.Ellett
    Commented May 21, 2014 at 20:51
  • @A.Ellett There's still the problem of killing a trailing space. Complicated syntax with trailing optional arguments is to be avoided.
    – egreg
    Commented May 21, 2014 at 20:53
  • Your latest update about *{C} {[4in] stuff} [] confuses me a bit. Of course I see it failing, But when I write, {E} {[2in] stufff} , so that there's a trailing space there, things work as expected. I would have thought \s@ae@parse@line would have read in #1 and #2 properly stripped before handing things over \s@ae@parse@line@aux.
    – A.Ellett
    Commented May 21, 2014 at 21:00
  • Is this really about optional arguments, or is just about delimited arguments?
    – A.Ellett
    Commented May 21, 2014 at 21:01
  • Scatch that! I see what's going on now. I'm not really worried about a space creeping in between the last brace and [. But it seems that before I hand things to \s@ae@parse@line@aux, I could save #1 and #2 to temporary macros.
    – A.Ellett
    Commented May 21, 2014 at 21:06
1

Following @egreg 's lead, here's what I came up with to handle noxious, spurious white space creeping in:

\def\s@ae@parse@line*#1#2{%%
  \def\ae@tmp@a{#1}%%
  \def\ae@tmp@b{#2}%%
  \s@ae@parse@line@aux}

\def\s@ae@parse@line@aux{%%
  \@ifnextchar[%]
  {\@s@ae@parse@line@aux}
  {\@s@ae@parse@line@aux[]}}

\def\@s@ae@parse@line@aux[#1]{%%
  \def\ae@tmp@c{#1}%%
  \edef\ae@tmp{[\expandonce\ae@tmp@c]{\expandonce\ae@tmp@a}{\expandonce\ae@tmp@b}}%%
  \expandafter\@ae@parse@line\ae@tmp}

As egreg suggested, I handle the optional argument by defining an auxiliary macro. But as pointed out, white space could still interfere with the expect parsing if it creeps in before the [. So, I handle this by saving the first two arguments to macros, then I use two auxiliary macros. The first tests whether there's a following [ (\@ifnextchar very conveniently gobbles up whitespace for me). Then this third argument is also saved to a macro. I then build the arguments for \@ae@parse@line using expansion tricks.

3
  • \@ifnextchar will happily destroy all spaces also when the trailing optional argument doesn't follow. This may be a problem depending on what usage of the macro you have in mind.
    – egreg
    Commented May 21, 2014 at 21:29
  • @egreg You make me laugh! I love the way things can sneak up on you in LaTeX and bite you where you least expect. That said, I think I'd be less worried about \@ifnextchar destroying spaces since that usually happens after macros generally. (Assuming that it's not going to gobble things like \ or \hspace*{...}).
    – A.Ellett
    Commented May 21, 2014 at 21:40
  • Just space tokens. Spaces don't disappear after regular macros with regular arguments. Commands like \linebreak do some trick for avoiding spaces disappear.
    – egreg
    Commented May 21, 2014 at 21:57

You must log in to answer this question.

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