How does one enumerate and apply a macro to every character in a string token?

TikZ has the parser module for that purpose. It allows you to parse a string of characters and do something with them.

\documentclass[12pt]{article}

\usepackage{tikz}
\usepgfmodule{parser}
\newcounter{pft}
\pgfparserdef{ASCIIswitch}{initial}{the letter X}% 
{\stepcounter{pft}%
\fill (0.18*\number\value{pft},0) circle [radius=0.08];}%
\pgfparserdef{ASCIIswitch}{initial}{the letter O}% 
{\stepcounter{pft}}%
\pgfparserdef{ASCIIswitch}{initial}{the character ;}% 
{\pgfparserswitch{final}}%

\listfiles
\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

Comma-separated list:

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle [radius=0.08] \fi;
        }
    }
\end{tikzpicture}

\bigskip

ASCII strings parsed by parser module:

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
       \begin{scope}[yshift=-0.18*\yi*1cm]
        \setcounter{pft}{0}
        \edef\temp{\noexpand\pgfparserparse{ASCIIswitch}\row;}
        \temp
       \end{scope}
    }
\end{tikzpicture}

\end{document}

enter image description here

ADDENDUM: As for your question in the comments whether you can split the string into a comma-separated list of atoms: of course, you almost had the code yourself.

\documentclass[12pt]{article}

\usepackage{tikz}

\makeatletter
\def\splitstring#1#2->#3;{%
      \edef#3{#1}\@tfor\chr:=#2\do{%
        \edef#3{#3,\chr}%
      }%
}
\makeatother
\begin{document}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\splitstring \row->\myrow;
        \foreach\chr [count=\xi] in \myrow {\def\myX{X}
            \ifx\chr\myX \fill (0.18*\xi,-0.18*\yi) circle[radius=0.08] \fi;
        }
    }
\end{tikzpicture}
\end{document}

You can use expl3: no need for fancy indexing, which is provided out of the box with “indexed maps”.

\documentclass[12pt]{article}

\usepackage{tikz}
\usepackage{xparse}

\newcommand{\makedot}[2]{%
  \fill (0.18*#1,-0.18*#2) circle (0.08);
}

\ExplSyntaxOn
\NewDocumentCommand{\ASCIIfont}{O{}m}
 {
  \begin{tikzpicture}[#1]
  \jlettvin_asciifont:n { #2 }
  \end{tikzpicture}
 }

\seq_new:N \l__jlettvin_asciifont_rows_seq
\seq_new:N \l__jlettvin_asciifont_row_seq

\cs_new_protected:Nn \jlettvin_asciifont:n
 {
  \seq_set_from_clist:Nn \l__jlettvin_asciifont_rows_seq { #1 }
  \seq_indexed_map_function:NN \l__jlettvin_asciifont_rows_seq \__jlettvin_asciifont_row:nn
 }

\cs_new_protected:Nn \__jlettvin_asciifont_row:nn
 {% #1 = row index, #2 = row
  \seq_set_split:Nnn \l__jlettvin_asciifont_row_seq { } { #2 }
  \seq_indexed_map_inline:Nn \l__jlettvin_asciifont_row_seq
   {% ##1 = column index, ##2 = item
    \str_if_eq:nnT { ##2 } { X }
     {
      \makedot{##1}{#1}
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
}

\end{document}

The input is split at commas and each row is processed taking into account its number (y-coordinate); the row is again split into items (at nothing), and the same is done taking into account the item number (x-coordinate).

The command \ASCIIfont has an optional argument for specifying options to the tikzpicture.

The trailing row of zeros doesn't produce vertical space, but that's a problem also with the other implementation.

enter image description here


Here are two ways to fix your code (quite different from the one given by Schrödinger's cat).

Probably, your most important “mistake” is that you needed to expand the \row macro before feeding the result to \dowithchar, in the second picture. Also, I don't see the purpose of your \myspace macro, so I removed it.

The second problem is that you needed \if rather than \ifx (well, \ifx could be used, but not this way). What happens with your \ifx test is that it compares the \chr macro with an opening brace { of category code 1 (beginning of group): the comparison is always false; that is why the second picture was invisible. \ifx compares the next two tokens, it never expands them. On the other hand, \if expands what follows until it has two unexpandable tokens; then, it compares their character codes (this is a bit simplified: there are special rules for unexpandable control sequence tokens and active characters—see the TeXbook p. 209 or specialized questions).

With \pgfmathtruncatemacro

Here, inside \dowithchar, \xi is a macro, just as in the first picture.

\documentclass{article}
\usepackage{tikz}

\makeatletter
\def\dowithchar#1#2{%
  \begingroup
    \def\xi{1}
      \@tfor\chr:=#1\do{%
        \if X\chr \fill (0.18*\xi,-0.18*#2) circle (0.08); \fi
        \pgfmathtruncatemacro{\xi}{\xi+1}
      }%
  \endgroup
}
\makeatother

\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\bigskip
\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\dowithchar\expandafter{\row}\yi
    }
\end{tikzpicture}

\end{document}

With a TeX \count register

Here, we use a TeX \count register via a \countdef token to count the columns in \ASCIIfont (second picture). The \countdef token is \myXi. We could reuse the same name \xi, but I prefer using a different name to avoid confusion since in the first picture, \xi is a macro (a \countdef token is unexpandable; in particular, it is not a macro).

\documentclass{article}
\usepackage{tikz}

\newcount\myXi

\makeatletter
\def\dowithchar#1#2{%
  \myXi=1                     % <--- don't remove the space
  \@tfor\chr:=#1\do{%
    \if X\chr \fill (0.18*\myXi,-0.18*#2) circle (0.08); \fi
    \advance \myXi by 1       % <--- don't remove the space
  }%
}
\makeatother

\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\bigskip
\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\dowithchar\expandafter{\row}\yi
    }
\end{tikzpicture}

\end{document}

Output

The output is the same in both cases:

enter image description here