BrainF*** Interpreter in Batch
Posted: 08 Jul 2014 14:22
As probably many have done before, I have proven Batch's Turing-completeness by making a BrainF*** interpreter in Batch.
I need to work a lot more on this, definitely. Rewrite the input system, for example. And make it faster. But it is fully functional!
BrainF*** (for posterity, I have censored the name) is an esoteric programming language that is one of the smallest possible Turing-complete languages ever. See http://esolangs.org/wiki/BF for more information. It consists of only eight instructions, each one character:
+ : Increment the current memory value.
- : Decrement the current memory value.
> : Move the memory pointer forwards.
< : Move the memory pointer backwards.
[ : If current cell is 0, skip to the matching ].
] : If current cell is nonzero, go back to the matching [.
. : Output the ASCII value of the current memory value.
, : Input a value to the current memory value.
Execute a BF file by either loading it as an argument, dragging and dropping it on the interpreter file, or specifying it when loading without an argument.
According to the original specs, there is an infinite amount of memory to work with. However, seeing as though none of us have that kind of space, I have an array of 4096 values as the memory tape.
Any memory cell can only have values from 0 to 255. I do support wraparounds.
In this interpretation, you can only input numbers, not text. I should get to fixing that soon.
Once it's done, it displays "Press any key to continue...", and the interpreter closes when a key is pressed.
Here is an example program (it should print "Hello, World!"):
And here is the source code to the interpreter (credit to...Jeb, was it? for the :echoWithoutLinefeed routine, and Aacini for the character-extraction code):
I need to work a lot more on this, definitely. Rewrite the input system, for example. And make it faster. But it is fully functional!
BrainF*** (for posterity, I have censored the name) is an esoteric programming language that is one of the smallest possible Turing-complete languages ever. See http://esolangs.org/wiki/BF for more information. It consists of only eight instructions, each one character:
+ : Increment the current memory value.
- : Decrement the current memory value.
> : Move the memory pointer forwards.
< : Move the memory pointer backwards.
[ : If current cell is 0, skip to the matching ].
] : If current cell is nonzero, go back to the matching [.
. : Output the ASCII value of the current memory value.
, : Input a value to the current memory value.
Execute a BF file by either loading it as an argument, dragging and dropping it on the interpreter file, or specifying it when loading without an argument.
According to the original specs, there is an infinite amount of memory to work with. However, seeing as though none of us have that kind of space, I have an array of 4096 values as the memory tape.
Any memory cell can only have values from 0 to 255. I do support wraparounds.
In this interpretation, you can only input numbers, not text. I should get to fixing that soon.
Once it's done, it displays "Press any key to continue...", and the interpreter closes when a key is pressed.
Here is an example program (it should print "Hello, World!"):
Code: Select all
++++++++++[>+++++++>++++++++++>+++++++++>++++>+++>+<<<<<<-]>++.>+.+++++++..+++.>>++++.>++.<<---.<.+++.------.--------.>>>+.>.
And here is the source code to the interpreter (credit to...Jeb, was it? for the :echoWithoutLinefeed routine, and Aacini for the character-extraction code):
Code: Select all
@echo off
setlocal enabledelayedexpansion
if "%~1"=="" (set /p sourcefile=File? ) else set sourcefile=%~1
set "allowedChars=+-<>.,[]"
set i=-1
<nul set /p=Processing file commands.
for /f "usebackq delims=" %%g in ("%sourcefile%") do (
for /f "delims=" %%h in ('cmd /u /c echo "%%g"^| find /v ""') do (
if "!allowedChars:%%h=!" neq "%allowedChars%" (
set /a i+=1
set code[!i!]="%%h"
)
)
<nul set /p=.
)
set /a length=%i%+1
set codepointer=0
set mempointer=0
echo(
<nul set /p=Preparing memory array.
for /l %%g in (0,1,4095) do set mem[%%g]=0&set /a h=%%g%%400&if !h!==0 <nul set /p=.
echo(
echo Done.
echo Press any key to start interpretation...
pause >nul
cls
:startLoop
if !code[%codepointer%]!=="+" set /a mem[%mempointer%]=!mem[%mempointer%]!+1%%256
if !code[%codepointer%]!=="-" set /a mem[%mempointer%]-=1&if !mem[%mempointer%]!==-1 set mem[%mempointer%]=255
if !code[%codepointer%]!==">" set /a mempointer=%mempointer%+1%%4096
if !code[%codepointer%]!=="<" set /a mempointer-=1&if !mempointer!==-1 set mempointer=4095
if !code[%codepointer%]!=="." (
cmd /c exit /b !mem[%mempointer%]!
if !mem[%mempointer%]!==10 echo(
if !mem[%mempointer%]!==32 call :echoWithoutLinefeed " "
if !mem[%mempointer%]! gtr 32 call :echoWithoutLinefeed "!=ExitCodeAscii!"
)
if !code[%codepointer%]!=="," (
set attempted=0
:enterNumber
if !attempted!==1 (echo Try again...number invalid.) else echo(
set /p "mem[%mempointer%]=Input number: "
call :isNumber !mem[%mempointer%]!
if !?!==0 set attempted=1&goto enterNumber
set /a mem[%mempointer%]=!mem[%mempointer%]!%%256
)
if !code[%codepointer%]!=="[" (
if !mem[%mempointer%]!==0 (
set brackets=1
:findEndBracket
set /a codepointer+=1
for %%g in (!codepointer!) do if !code[%%g]!=="[" set /a brackets+=1
for %%g in (!codepointer!) do if !code[%%g]!=="]" set /a brackets-=1
if !brackets! neq 0 goto findEndBracket
)
)
if !code[%codepointer%]!=="]" (
if !mem[%mempointer%]! neq 0 (
set brackets=1
:findStartBracket
set /a codepointer-=1
for %%g in (!codepointer!) do if !code[%%g]!=="]" set /a brackets+=1
for %%g in (!codepointer!) do if !code[%%g]!=="[" set /a brackets-=1
if !brackets! neq 0 goto findStartBracket
)
)
set /a codepointer+=1
if %codepointer% lss %length% goto startLoop
pause
exit /b
:echoWithoutLinefeed
setlocal disabledelayedexpansion
set "ln=%~1"
for /f %%A in ('copy /z "%~dpf0" nul') do set EOL=%%A^
setlocal enabledelayedexpansion
<nul set /p "=x!EOL!!ln!" >ln.tmp
findstr /v $ ln.tmp
del ln.tmp
exit /b
:isNumber
for /f "tokens=*" %%g in ("%~1")do set #=%%~g
for /f "tokens=* delims=0" %%g in ("%#%")do set #=%%g
if not defined # set #=0
set ?=1&for /f "tokens=1 delims=0123456789" %%g in ("%#%") do set ?=0
exit /b