Skip to content

Instantly share code, notes, and snippets.

@z-rui
Last active May 8, 2018 17:39
Show Gist options
  • Save z-rui/05107484a563b1ac180bf1662cc2d545 to your computer and use it in GitHub Desktop.
Save z-rui/05107484a563b1ac180bf1662cc2d545 to your computer and use it in GitHub Desktop.
Brainf*** interpreter in TeX
% Brainf*** interpreter
% Usage:
%
% tex bf <file>
%
% Be aware that this interpreter is very slow...
\newcount\memptr \newcount\maxptr \newcount\minptr
\newcount\memval \newcount\maxval \newcount\minval
\minptr=0 \maxptr=65535
\minval=0 \maxval=255
\let\inbuf=\empty \let\outbuf=\empty
\catcode`\@=11
% Plain old TeX does not have a good (read: expandable) way
% to convert from an ASCII codepoint to the actual character...
% If using XeTeX/LuaTeX, we could use \Uchar
\newtoks\xchar
\begingroup\catcode`\^^@=12
\gdef\setxchar{\begingroup\afterassignment\setxch@r\lccode`^^@}
\gdef\setxch@r{\lowercase{\global\xchar{^^@}}\endgroup}
\endgroup
% the machine
\newif\ifdirty \newif\ifvalid
\def\getmemloc{%
\xdef\memloc{\expandafter\noexpand\csname mem\the\memptr\endcsname}%
\expandafter\ifx\memloc\relax
\expandafter\global
\expandafter\chardef\memloc\z@
\fi}
\def\readmem{\global\memval\memloc\relax \global\validtrue}
\def\maybefetch{\ifvalid\else \getmemloc \readmem \fi}
\def\writemem{%
\expandafter\global
\expandafter\chardef\memloc\memval
\global\dirtyfalse}
\def\moveptr#1{\ifdirty \ifvalid\else \getmemloc\fi \writemem \fi
\checkadvance \memptr #1\minptr \maxptr
\global\validfalse}
\def\addval#1{\maybefetch
\checkadvance \memval #1\minval \maxval
\global\dirtytrue}
\def\checkadvance#1#2#3#4{% register delta min max
\global\advance#1#2\relax
\ifnum#1<#3%
%\wlog{\string#1 underflow! wrapped to \number#3^^J}%
\global#1=#4%
\else\ifnum#1>#4%
%\wlog{\string#1 overflow! wrapped to \number#2^^J}%
\global#1=#3%
\fi\fi}
\def\gettok#1{\expandafter\gettok@\expandafter#1#1\relax}
\def\gettok@#1#2#3\relax{\memval=`#2\relax\xdef#1{#3}}
\def\puttok#1{\setxchar=\memval \xdef#1{#1\the\xchar}}
\def\morein{\maybeflush \global\read\sixt@@n to\inbuf}
\def\flushout{\immediate\write\sixt@@n{\outbuf}%
\global\let\outbuf\empty}
\def\maybeflush{\relax\ifx\outbuf\empty\else \flushout \fi}
% brainf*** commands
\def\setcatcode{%
\catcode`[\@ne
\catcode`]\tw@
\catcode`<\active
\catcode`>\active
\catcode`+\active
\catcode`-\active
\catcode`,\active
\catcode`.\active}
{\setcatcode
\gdef<{\moveptr\m@ne}
\gdef>{\moveptr\@ne}
\gdef+{\addval\@ne}
\gdef-{\addval\m@ne}
\gdef,{\ifx\inbuf\empty \morein \fi
\gettok\inbuf \global\dirtytrue}
\gdef.{\maybefetch
\ifnum\memval=`\^^J\flushout \else \puttok\outbuf \fi}
\gdef\setzero{-}
\gdef\bfloop#1{\def\body{#1}%
\ifx\body\setzero \expandafter\bfclear % [-] optimization
\else \expandafter\bfiterate \fi \dobf}
\gdef\bfclear{\ifvalid\else \getmemloc\fi
\global\memval\z@ \writemem}
\gdef\bfiterate{\maybefetch
\ifnum\memval=\z@ \else {\expandafter\dobf\body}\expandafter\bfiterate \fi}}
% stream scanner
\def\dobf{\futurelet\next\dobf@}
\def\dobf@{%
\ifcat\noexpand\next\bgroup
\let\next\bfloop % let TeX scan a group (balanced [...])
\else\ifcat\noexpand\next\egroup
\let\next\relax % we're done
\else
\let\next\dobf@@
\fi\fi
\next}%
\def\dobf@@#1{\ifcat\noexpand#1\noexpand~#1\fi \dobf}
\def\uncatcodespecials{\def\do##1{\catcode`##1=12 }\dospecials\do\^^M}
\def\readbf#1 {\uncatcodespecials \setcatcode
%\loggingall
{\expandafter\dobf\input#1 }\maybeflush\end}
\expandafter\readbf
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..
+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment