使用 soul 作为 tokenizer 会在特定字符串上产生奇怪的结果

使用 soul 作为 tokenizer 会在特定字符串上产生奇怪的结果

下列的这个帖子,我试图将其用作soulhack-y 标记器,但在某些字符串上遇到了意外行为。我将问题简化为以下 MWE,但未能取得进一步进展:

\documentclass{article}
\usepackage{soul}

% Initially empty, then should be nonempty afterward
\newcommand*{\state}{}

\makeatletter
\def\SOUL@soeverytoken{%
  \ifx\state\empty%  Should only happen once...
    \renewcommand*{\state}{Nonempty}%
    (\the\SOUL@token)%
  \else%
    [\the\SOUL@token]%
  \fi%
}
\makeatother

\begin{document}
  % For some reason, the string "unrnn" gives an unexpected result.
  \so{nnrnn} \par % (n)[n][r][n][n]
  \so{unrnn} \par % (u)[n](r)[n][n]
  \so{unnnn} \par % (u)[n][n][n][n]
  \so{unrn}  \par % (u)[n][r][n]
\end{document}

在字符串上unrnn,它打印出来与我预期的(r)不一样。从打印出来来看,似乎在读取后它变得非空,但我不确定为什么在其他示例中没有发生这种情况。[r]\stater

答案1

我不知道为什么,但是当检查字符串时unrnn,TeX 处于分组级别 3;然而,在r处理时,分组级别降低到 2。

\documentclass{article}
\usepackage{soul}

% Initially empty, then should be nonempty afterward
\newcommand*{\state}{}

\makeatletter
\def\SOUL@soeverytoken{%
\showthe\currentgrouplevel
\show\state
\showthe\SOUL@token
  \ifx\state\empty%  Should only happen once...
    \renewcommand*{\state}{Nonempty}%
    (\the\SOUL@token)%
  \else
    [\the\SOUL@token]%
  \fi
}
\makeatother

\begin{document}
  % For some reason, the string "unrnn" gives an unexpected result.
%  \so{nnrnn} \par % (n)[n][r][n][n]
  \so{unrnn} \par % (u)[n](r)[n][n]
%  \so{unnnn} \par % (u)[n][n][n][n]
%  \so{unrn}  \par % (u)[n][r][n]
\end{document}

我添加了一些诊断命令来查看发生了什么。

> 3.
\SOUL@everytoken ->\showthe \currentgrouplevel
                                               \show \state \showthe \SOUL@t...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> \state=macro:
->.
\SOUL@everytoken ...urrentgrouplevel \show \state
                                                  \showthe \SOUL@token \ifx ...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> u.
\SOUL@everytoken ...w \state \showthe \SOUL@token
                                                  \ifx \state \empty \renewc...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> 3.
\SOUL@everytoken ->\showthe \currentgrouplevel
                                               \show \state \showthe \SOUL@t...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> \state=macro:
->Nonempty.
\SOUL@everytoken ...urrentgrouplevel \show \state
                                                  \showthe \SOUL@token \ifx ...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> n.
\SOUL@everytoken ...w \state \showthe \SOUL@token
                                                  \ifx \state \empty \renewc...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> 2.
\SOUL@everytoken ->\showthe \currentgrouplevel
                                               \show \state \showthe \SOUL@t...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> \state=macro:
->.
\SOUL@everytoken ...urrentgrouplevel \show \state
                                                  \showthe \SOUL@token \ifx ...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> r.
\SOUL@everytoken ...w \state \showthe \SOUL@token
                                                  \ifx \state \empty \renewc...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?
> 2.
\SOUL@everytoken ->\showthe \currentgrouplevel
                                               \show \state \showthe \SOUL@t...
l.24   \so{unrnn}
                  \par % (u)[n](r)[n][n]
?

如果我尝试\so{nnrnn}分组级别从 2 开始。

我会避免超负荷\so

\documentclass{article}
\usepackage{soul}

\makeatletter
\newcommand{\myso}[1]{%
  \begingroup
  \gdef\my@state{}%
  \def\SOUL@soeverytoken{%
    \ifx\my@state\empty%  Should only happen once...
      \gdef\my@state{x}%
      (\the\SOUL@token)%
    \else
      [\the\SOUL@token]%
    \fi
  }%
  \so{#1}
  \endgroup
}
\makeatother

\begin{document}

\myso{nnrnn} \par % (n)[n][r][n][n]
\myso{unrnn} \par % (u)[n](r)[n][n]
\myso{unnnn} \par % (u)[n][n][n][n]
\myso{unrn}  \par % (u)[n][r][n]

\end{document}

在此处输入图片描述

expl3使用和 的不同实现\text_map_inline:nn

\documentclass{article}

\ExplSyntaxOn
\NewDocumentCommand{\myso}{m}
 {
  \airwreck_process:n { #1 }
 }

\bool_new:N \l_airwreck_first_bool

\cs_new_protected:Nn \airwreck_process:n
 {
  \bool_set_true:N \l_airwreck_first_bool
  \text_map_inline:nn { #1 }
   {
    \bool_if:NTF \l_airwreck_first_bool
     {% first item
      (##1)
      \bool_set_false:N \l_airwreck_first_bool
     }
     {% other items
      [##1]
     }
   }
 }

\ExplSyntaxOff

\begin{document}

\myso{nnrnn} \par % (n)[n][r][n][n]
\myso{unrnn} \par % (u)[n](r)[n][n]
\myso{unnnn} \par % (u)[n][n][n][n]
\myso{unrn}  \par % (u)[n][r][n]

\end{document}

在此处输入图片描述

答案2

作为背景:soul 分析音节,以便进行连字符连接。因此,解析结果可能取决于语言:在德语中unt是一个音节,因此解析器重新启动:

\documentclass{article}
\usepackage[ngerman,english]{babel}
\usepackage{soul}

% Initially empty, then should be nonempty afterward
\newcommand*{\state}{}

\makeatletter
\def\SOUL@soeverytoken{%
  \ifx\state\empty%  Should only happen once...
    \renewcommand*{\state}{Nonempty}%
    (\the\SOUL@token)%
  \else%
    [\the\SOUL@token]%
  \fi%
}
\makeatother

\begin{document}
  \so{untnn} \par %
  
  \selectlanguage{ngerman}
  
  \so{untnn} 
\end{document}

在此处输入图片描述

相关内容