In an attempt to specify a seemingly endless amount of arguments to a macro, I would like to supply only one #1
, where #1
is a string that contains a bunch of specific characters. For example
\mymacro{1jq4PK2/62Bb/-krkR/...}
Then, I'd like to sequentially parse #1
on a character-by-character basis and do something based on the character. As in the example above, characters could be anything from numbers 0...9
, letters a...z
and A...Z
(so conditioning should be case sensitive) or symbols -
,/
,…
The bigger picture here is create a macro that uses chessfss
to typeset a chess board or arbitrary shape using FEN notation. The symbol -
will denote a blank square, numbers will denote empty squares (with no piece on it), /
will denote the next rank, and letters will denote the colour (via capitalization) and type (via the specific letter) of the piece.
The code provided by the skak
package is limited to a fixed 8-rank (or file) chess board. Specifically, skak
provides \InitBoard
that is defined as
\def\InitBoard(#1/#2/#3/#4/#5/#6/#7/#8){%
%<code>
}
where each one of the 8 arguments is processed in a very "nested fashion" using (I'm skipping some intermediate steps for the sake of brevity):
\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)}}
I would like to steer clear from this since it is not entirely reader-friendly and somewhat unappealing, and rather use some "case statement", where I condition on the characters in the macro argument, one by one. The boolexpr
package provides such a switch
environment, but I'm not sure how to extend it to work in the way I want to.
Best Answer
I have implemented character or token parsers like this in my
tikz-timing
,collcell
and theydoc
packages. They are all available under https://bitbucket.org/martin_scharrer/ and on CTAN, of course. Basically you are either use\futurelet
or a macro argument (#1
) to read the next token or character and branch accordantly, then call your input collection macro recursively.In
collcell
I added a case statement for tokens which could be changed to take a character instead. The current syntax is:where the token was read before using
\futurelet\collect@cell@lettoken\next
. See the source code of the case statement for more details.In
ydoc
I started with a large\ifcase
statement but then changed it to a look-up table-like feature: There is one macro define for every input character, e.g.\@namedef{mypkg@char@<char>}{<code for this character>}
, and then the parser macro only need to read the next character and use\@nameuse{mypkg@char@#1}
(or\csname
directly). As long you don't have any special tokens like{
,}
and spaces this works fine. Otherwise you need to use\futurelet
/\@ifnextchar
before to check for these first.It is also possible to always use tokens with
\futurelet
and use\meaning
to build the macro names, i.e.\@nameuse{mypkg@char@\meaning\@let@token}
. This works with all tokens, normal or special. The handler macro should then remove the token from the input stream.A simply character-by-character parser based on this looks like this:
This prints:
(1) (j) (q4) (.) (.) (.)
.The recursive call is placed in every character handler so that there is nothing after it so that it can read further input character if required.