逐个字符地解析宏参数以进行条件执行

逐个字符地解析宏参数以进行条件执行

为了给宏指定看似无穷无尽的参数,我只想提供一个#1,其中#1是一个包含一堆的字符串具体的字符。例如

\mymacro{1jq4PK2/62Bb/-krkR/...}

然后,我想#1按顺序逐个字符进行解析,并根据字符执行某些操作。如上例所示,字符可以是数字0...9、字母a...zA...Z(因此条件应区分大小写)或符号-、、/...

这里的重点是创建一个使用chessfss使用排版棋盘或任意形状FEN 符号。符号-表示空白格,数字表示空格(上面没有棋子),/表示下一等级,字母表示棋子的颜色(通过大写)和类型(通过特定字母)。

代码由skak包裹仅限于固定的 8 阶(或文件)棋盘。具体而言,skak规定\InitBoard定义为

\def\InitBoard(#1/#2/#3/#4/#5/#6/#7/#8){%
  %<code>
}

其中 8 个参数中的每一个都以非常“嵌套的方式”进行处理(为了简洁起见,我跳过了一些中间步骤):

\def\FenConvert#1{%
  \EqStr{8}{#1}%
  {EEEEEEEE}%
  {\EqStr{7}{#1}%
    {EEEEEEE}%
    {\EqStr{6}{#1}%
      {EEEEEE}%
      {\EqStr{5}{#1}%
        {EEEEE}%
        {\EqStr{4}{#1}%
          {EEEE}%
          {\EqStr{3}{#1}%
            {EEE}%
            {\EqStr{2}{#1}%
              {EE}%
              {\EqStr{1}{#1}%
                {E}%
                {#1}}}}}}}}}

\def\ParseFenRank#1{\ParseFenRankA(#1Z)}
\def\ParseFenRankA(#1#2){%
  \EqStr{Z}{#1}%
  {}%
  {\FenConvert{#1}\ParseFenRankA(#2)}}

我希望避免这种情况,因为它不是完全读者友好的,而且有点不吸引人,而是使用一些“case语句”,在其中我逐个条件化宏参数中的字符。boolexpr包裹提供了这样的switch环境,但我不确定如何扩展它以按照我想要的方式工作。

答案1

tikz-timing我已经在我的和collcell包中实现了这样的字符或标记解析器ydoc。它们都可以在https://bitbucket.org/martin_scharrer/当然还有 CTAN。基本上,您可以使用\futurelet或宏参数(#1)来读取下一个标记或字符并相应地分支,然后递归调用输入集合宏。

collcell添加了一个用于标记的 case 语句,可以将其更改为采用字符。当前语法是:

\cc@case
   <token>{<code>}
   <token>{<code>}
   <token>{<code>}
   <token>{<code>}
\endcc@case

在使用之前读取令牌的位置\futurelet\collect@cell@lettoken\next。请参阅case 语句的源代码更多细节。

我一ydoc开始用了一个大\ifcase语句,但后来把它改成了查找表类似功能:每个输入字符都有一个宏定义,例如\@namedef{mypkg@char@<char>}{<code for this character>},然后解析器宏只需要读取下一个字符并使用\@nameuse{mypkg@char@#1}(或\csname直接使用)。只要您没有任何特殊标记,如{,和空格,这就可以正常工作。否则您需要先}使用\futurelet/来检查这些。\@ifnextchar

也可以始终使用带有 和 的标记\futurelet\meaning构建宏名称,即\@nameuse{mypkg@char@\meaning\@let@token}。这适用于所有标记,无论是普通标记还是特殊标记。然后,处理程序宏应从输入流中删除该标记。

基于此的简单逐字符解析器如下所示:

\documentclass{article}

\makeatletter
\newcommand\mymacro[1]{%
    \@mymacro#1\@nnil
}
\def\@mymacro#1{%
    \ifx\@nnil#1\relax\else
        \@nameuse{mymacro@char@#1\expandafter}%
    \fi
}
\def\defcharcode#1{%
    \@namedef{mymacro@char@#1}%
}
\defcharcode{1}{%
    (1)
    \@mymacro
}
\defcharcode{j}{%
    (j)
    \@mymacro
}
\defcharcode{q}#1{% reads further character!
    (q#1)
    \@mymacro
}
\defcharcode{.}{%
    (.)
    \@mymacro
}
\makeatother

\begin{document}

\mymacro{1jq4...}

\end{document}

这将打印:(1) (j) (q4) (.) (.) (.)

递归调用被放置在每个字符处理程序中,以便它之后没有任何内容,以便它可以在需要时读取进一步的输入字符。

相关内容