LATEX-L Archives

Mailing list for the LaTeX3 project

LATEX-L@LISTSERV.UNI-HEIDELBERG.DE

Options: Use Forum View

Use Proportional Font
Show Text Part by Default
Show All Mail Headers

Message: [<< First] [< Prev] [Next >] [Last >>]
Topic: [<< First] [< Prev] [Next >] [Last >>]
Author: [<< First] [< Prev] [Next >] [Last >>]

Print Reply
Subject:
From:
Heiko Oberdiek <[log in to unmask]>
Reply To:
Mailing list for the LaTeX3 project <[log in to unmask]>
Date:
Sun, 26 Apr 2009 12:22:31 +0200
Content-Type:
text/plain
Parts/Attachments:
text/plain (236 lines)
On Sun, Apr 26, 2009 at 08:27:38AM +0200, Uwe Lück wrote:

> The difference in safety is that other proposals can be fooled by \in@ or 
> \in@@ in \in@ arguments. The "safe" proposal still can be fooled by \@nil 
> or so in \in@ arguments.

This can be fixed without a new command token:

> Right, on the other hand \in@{a}{a#} and \in@{b}{a#} work with the 
> expandable tests only.

This can be fixed using token registers, see below.

`#' cannot be used in the first substring argument, because
it cannot be used as ordinary delimiter in the parameter text of a
definition (except as last token, but this doesn't help here).
Then `#' should be a better token than \@nil or \[log in to unmask]
  ==> \in@@#2###1\in@\in@@

> 1. LaTeX-so-far,

We are currently discussing the bugs of this version.

> 2.changing it by just inserting a separator in the last 
> line according to Morten:
> 
> \def\in@#1#2{%
> \def\in@@##1#1##2##3\in@@{%
>   \ifx\in@##2\in@false\else\in@true\fi}%
>  \in@@#2\@nil#1\in@\in@@}

It can be broken like the original macro by
  \in@{}{}
Then the call of \in@@ is one argument too short.
This can be fixed by preceding the string by a token.
  ==> \in@@###2###1\in@\in@@

> (where I proposed to use \@in@ in place of \@nil), and 

(or `#', see above.)

> 3. mine (expandable 
> test without \in@). (Attached is a file with testing tools for the four 
> versions.)

The expandable tests can be broken by conditionals, e.g.:
  \in@{a}{a{\fi\fi}}

> In favour of Morten's: it is the least change (compatibility).

Then I would prefer the following modification:

% Version M:
\def\in@#1#2{% 
  \def\in@@##1#1##2##3\in@@{%
    \ifx\in@##2\relax % \relax added for the case that ##2 is empty
      \in@false
    \else
      \in@true
    \fi
  }%
  \in@@###2###1\in@\in@@
}

I think, a little more robust is, but longer is:

% Version H:
\def\in@#1#2{%
  \def\in@@{#1}%
  \ifx\in@@\@empty
    \in@true
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {%
    \def\in@@##1#1##2\in@@{%
      \begingroup
        \toks@{##2}%
        \edef\in@@{\the\toks@}%
      \expandafter\endgroup
      \ifx\in@@\@empty
        \in@false
      \else
        \in@true
      \fi
    }%
    \in@@#2###1\in@@
  }%
}

There are some cases to discuss, if the first argument is empty.
The kernel version behaves the following way:
  \in@{}{} breaks
and
  \in@{}{a} -> false
  \in@{}{ab} -> true
is inconsistent.

I would like a more standardized behaviour (e.g. Perl's `index' function
for substring searching): If the first argument is empty, then the
result is always true:
  \in@{}{} -> true
  \in@{}{a} -> true
  \in@{}{ab} -> true
Both versions M and H behave in this way.

Test file:

\catcode`\@=11 %
\def\@empty{}% (for testing with plain-TeX)
\long\def\@gobble#1{}
\long\def\@firstofone#1{#1}

\def\kernel@in@#1#2{%
  \def\in@@##1#1##2##3\in@@{%
    \ifx\in@##2%
      \in@false
    \else
      \in@true   
    \fi
  }%
  \in@@#2#1\in@\in@@
}

%%% Variant M begin %%%
\def\M@in@#1#2{%
  \def\in@@##1#1##2##3\in@@{%
    \ifx\in@##2\relax
      \in@false
    \else
      \in@true
    \fi
  }%
  \in@@###2###1\in@\in@@
}
%%% Variant M end %%%

%%% Variant H begin %%%
\def\H@in@#1#2{%
  \def\in@@{#1}%
  \ifx\in@@\@empty
    \in@true
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi   
  {%
    \def\in@@##1#1##2\in@@{%
      \begingroup
        \toks@{##2}%
        \edef\in@@{\the\toks@}%
      \expandafter\endgroup
      \ifx\in@@\@empty  
        \in@false
      \else
        \in@true
      \fi
    }%
    \in@@#2###1\in@@
  }%
}
%%% Variant H end %%%

\expandafter\ifx\csname ifin@\endcsname\relax
  \expandafter\newif\csname ifin@\endcsname
\fi

\def\msg#{\immediate\write16}
\def\@test#1#2#3#4{%
  #4{#1}{#2}%  
  \edef\@result{%
    \ifin@ yes\else no\fi
  }%
  \def\@expected{#3}%
  \toks@={{#1}{#2}}%
  \msg{%
    [\string#4\the\toks@\space = \@result\space] => %
    \ifx\@result\@expected OK.\else FAILED!\fi
  }%
}

\def\test#1#2#3{%
  \@test{#1}{#2}{#3}\in@
}

\msg{}
\msg{* Inconsistent behaviour of the kernel}
\@test{}{a}{yes}\kernel@in@ 
\@test{}{ab}{yes}\kernel@in@

\@test{a}{}{no}\kernel@in@
\@test{\iftrue}{\iftrue}{yes}\kernel@in@  
\@test{\iffalse}{\iffalse}{yes}\kernel@in@
\@test{\iftrue}{\iffalse}{no}\kernel@in@

% \let\M@in@  
\let\in@\H@in@

\msg{}
\msg{* Tests for fixed version of \string\in@}
\test{}{a}{yes}   
\test{}{}{yes}    
\test{a}{}{no}    
\test{a}{{}}{no}  
\test{a}{a{}}{yes}
\test{a}{{}a}{yes}
\test{a}{b{}}{no}
\test{a}{{}b}{no}
\test{bon}{#bon}{yes}
\expandafter\test\expandafter{\string#bon}{#bon}{no}
\test{bon\@in}{bon}{no}
\test{bon\@in}{bon\@in}{yes}
\test{\@in}{\@in}{yes}
\test{\@in bon}{\@in\@in bon}{yes}
\test{\iftrue}{\iftrue}{yes}  
\test{\iftrue}{\iffalse}{no}  
\test{\iffalse}{\iftrue}{no}  
\test{\iffalse}{\iffalse}{yes}
\test{\else}{\else}{yes}
\test{\else}{\fi}{no}
\test{\fi}{\else}{no}
\test{\fi}{\fi}{yes}
\test{a}{a{\fi\fi}}{yes}
\test{bonbon}{bon}{no} 
\test{bon}{bonbon}{yes}
\test{bona}{bon}{no} 
\test{bon}{bona}{yes}
\test{ionization}{ionizat}{no}
\test{client-to-client}{client-to-}{no}

\csname @@end\endcsname\end

Yours sincerely
  Heiko <[log in to unmask]>

ATOM RSS1 RSS2