BrainF*** Interpreter in Batch

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
JWinslow23
Posts: 58
Joined: 17 Jun 2014 10:38

BrainF*** Interpreter in Batch

#1 Post by JWinslow23 » 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!"):

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
Last edited by JWinslow23 on 08 Jul 2014 15:51, edited 1 time in total.

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: BrainF*** Interpreter in Batch

#2 Post by Squashman » 08 Jul 2014 14:35

And maybe give credit to Antonio for his code contribution.

JWinslow23
Posts: 58
Joined: 17 Jun 2014 10:38

Re: BrainF*** Interpreter in Batch

#3 Post by JWinslow23 » 08 Jul 2014 15:50

I almost forgot, sorry about that. Post edited.

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: BrainF*** Interpreter in Batch

#4 Post by Aacini » 08 Jul 2014 22:35

Interesting application for Batch! This is my version:

EDIT 2014-07-09: I fixed a couple small bugs in the overflow of arithmetic operations. I also added support for the 256 Ascii characters in "." (output) command. This is achieved creating 164 one-character text files, so I suggest to do so in a new empty folder.

Code: Select all

@echo off
setlocal EnableDelayedExpansion

rem bf.bat: Brainf*ck interpreter in Batch
rem Antonio Perez Ayala
rem Version 1.1: - Bug fixed in arithmetic overflow
rem              - Full support of 256 Ascii characters in "." (output) command

if "%~1" equ "" echo Usage: %~N0 filename.ext & goto :EOF
if not exist "%~1" echo File not found: %1 & goto :EOF

set "operators=+-<>.,[]"
set /A ip=0, sp=0
set /P "=Loading file." < NUL
for /F "usebackq delims=" %%a in ("%~1") do (
   for /F "delims=" %%b in ('cmd /U /C echo "%%a"^| find /V ""') do (
      if "!operators:%%b=!" neq "%operators%" (
         set /A ip+=1
         set "code[!ip!]=%%b"
         if "%%b" equ "," set "code[!ip!]=comma"
         if "%%b" equ "[" (
            set /A sp+=1
            set stack[!sp!]=!ip!
         ) else if "%%b" equ "]" (
            for %%s in (!sp!) do set /A jump[!ip!]=stack[%%s]-1, jump[!stack[%%s]!]=ip, sp-=1
         )
      )
   )
   set /P "=." < NUL
)
echo/
if %sp% neq 0 echo ERROR: Unbalanced brackets & goto :EOF
set /A ip+=1
set "code[%ip%]=EOF"

set /P "=Creating Ascii tables." < NUL
type nul > t.tmp
set "options=/d compress=off /d reserveperdatablocksize=26"
for /L %%i in (0,1,34) do if not exist %%i.chr call :genchr %%i
set /P "=." < NUL
for /L %%i in (127,1,255) do if not exist %%i.chr call :genchr %%i
set "options="
del t.tmp temp.tmp
set /P "=." < NUL
set "Ascii=01234567890123456789012345678901"
for /L %%i in (32,1,126) do (
   cmd /C exit /B %%i
   set "Ascii=!Ascii!!=ExitCodeAscii!"
)
for /L %%i in (126,-1,32) do (
   cmd /C exit /B %%i
   set "Char[!=ExitCodeAscii!]=%%i"
)
set "upCase=ABCDEFGHIJKLMNOPQRSTUVWXYZ"
echo/

set /P "=Initializing memory." < NUL
for /L %%i in (0,1,4095) do (
   set m[%%i]=0
   set /A mod=%%i %% 400
   if !mod! equ 0 set /P "=." < NUL
)
echo/

echo Running.

set /A ip=0, mp=0
:nextOpCode
set /A ip+=1
goto OpCode"!code[%ip%]!"

:OpCode"+"
set /A "m[%mp%]=(m[%mp%]+1) & 255"
goto nextOpCode

:OpCode"-"
set /A "m[%mp%]=(m[%mp%]-1) & 255"
goto nextOpCode

:OpCode"^>"
set /A "mp=(mp+1) & 4095"
goto nextOpCode

:OpCode"^<"
set /A "mp=(mp-1) & 4095"
goto nextOpCode

:OpCode"."
set char=!m[%mp%]!
if %char% equ 10 (
    echo/
) else if %char% leq 34 (
   type %char%.chr
) else if %char% leq 126 (
    set /P "=!Ascii:~%char%,1!" < NUL
) else (
   type %char%.chr
)
goto nextOpCode

:OpCode"comma"
set "key="
setlocal DisableDelayedExpansion
for /F "delims=" %%k in ('xcopy /L /W "%~F0" "%~F0" 2^>NUL') do (
   if not defined key set "key=%%k"
)
endlocal & set "key=%key:~-1%"
set "m[%mp%]=!Char[%key%]!"
if "!upCase:%key%=%key%!" neq "%upCase%" set /A m[%mp%]+=32
goto nextOpCode

:OpCode"["
if !m[%mp%]! equ 0 set ip=!jump[%ip%]!
goto nextOpCode

:OpCode"]"
set ip=!jump[%ip%]!
goto nextOpCode

:OpCode"EOF"
goto :EOF

:genchr
REM This code creates one single byte. Parameter: int
REM Teamwork of carlos, penpen, aGerman, dbenham
REM Tested under Win2000, XP, Win7, Win8
if %~1 neq 26 (
   makecab %options% /d reserveperfoldersize=%~1 t.tmp %~1.chr > nul
   type %~1.chr | ( (for /l %%N in (1,1,38) do pause)>nul & findstr "^" > temp.tmp )
   >nul copy /y temp.tmp /a %~1.chr /b
) else (
   copy /y nul + nul /a 26.chr /a >nul
)
exit /B

Antonio

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: BrainF*** Interpreter in Batch

#5 Post by Aacini » 09 Jul 2014 07:11

I modified my BF interpreter in order to show the 256 Ascii characters in "." (output) command; the new code is in the same place than the original, as usual.

Below there is a BF program that show some example output using the characters of the extended set in code page 850:

EDIT: I modified the "4x10 Frame" to use multiple "." (output) commands instead of just one command in a loop, so it run faster in my new optimized BF version.

Code: Select all

Characters in Spanish: ¿La mamá del niño "el güero" Pérez?
                                                       
>++++         [ >++++++++       [> + <-] <-]             space in m(3)
 ++++++++++++ [ >++++++++++++++ [>>+<<-] <-] >>>.<<<     ¿ in m(4)
 +++++++++    [ >++++++++       [<<+>>-] <-] <++++ .     L
+++++++++++++++++++++                              .     a
>>>                                                .
<<< ++++++++++++                                   .     m
------------                                       .     a
++++++++++++                                       .     m
>>>>--------                                       .<.   á  space
<<<---------                                       .     d
+                                                  .     e
+++++++                                            .>>>. l  space
<<<++                                              .     n
-----                                              .     i
>>>>++++                                           .     ñ
<<<<++++++                                         .>>>. o  space
++                                                 .--   quote
<<<----------                                      .     e
+++++++                                            .>>>. l  space
<<<-----                                           .     g
>>>>-----------------------------------            .     ü
<<<<--                                             .     e
+++++++++++++                                      .     r
---                                                .     o
>>>++                                              .--.  quote  space
<<<-------------------------------                 .     P
>>>>+                                              .     é
<<<<++++++++++++++++++++++++++++++++++             .     r
-------------                                      .     e
+++++++++++++++++++++                              .     z
>>>+++++++++++++++++++++++++++++++                 .     ?
<<++++++++++                                       ..    NL NL

A 4x10 frame:

>>>>> ++++ [ >++++++++++++++++ [>->-<<-] <-]      192 in two cells
>>>++++++++++++++++++++++++++                  .  218  top left
<++++ ..........                                  196  top 10 times
-----                                          .  191  top right
<<++++++++++                                   .  NL
>>------------                                    179  left
<++++++++++++++++++++++++++++++++                 a space
<<++++                                            4 times
[ >>>                                          .     show left
   < ..........                                      show 10 spaces
   >                                           .     show  right
   <<                                          .     NL
<-]                                               end 4 times
>>> +++++++++++++                              .  192  bottom left
++++ ..........                                   196  bottom 10 times
>-                                             .  217  bottom right
<<<                                            .  NL

Output:

Code: Select all

Loading file......................................................
Creating Ascii tables...
Initializing memory..
Running.
¿La mamá del niño "el güero" Pérez?

┌──────────┐
│          │
│          │
│          │
│          │
└──────────┘

Antonio

PS - The "," (input) command is limited by the "xcopy /L /W" trick used to get one keystroke. Anybody knows if there is a way to accurately read all keyboard characters using this method? The BF interpreter currently fails when an exclamation mark, a quote or an equal-sign is read.
Last edited by Aacini on 10 Jul 2014 13:31, edited 1 time in total.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: BrainF*** Interpreter in Batch

#6 Post by dbenham » 09 Jul 2014 10:34

It should be possible to pre-process the code into memory, stripping out comments (non command characters), and establishing loop pointers. Then a single infinite loop should be able to interpret the code extremely fast, without resorting to any CALL or GOTO statements.

The XCOPY method of reading a keypress has only 2 restrictons:

1) It cannot read 0x00 (pretty much true of any batch command, with a few exceptions)

2) It cannot distinguish between <Ctrl-C> and <LF>

Below is an infinite loop that prints out each keypress until <CR> is entered via <Enter>, <Ctrl-M>, or <Alt>13

My code interprets both <Ctrl-C> and <LF> (entered as <Alt>10) as <LF>

Code: Select all

@echo off
if "%~1" equ "" (
  cmd /c "%~f0" x
  exit /b
)
setlocal enableDelayedExpansion
for /f %%a in ('copy /Z "%~dpf0" nul') do set "CR=%%a"
set ^"LF=^

^"
for /l %%. in () do (
  set "key="
  for /f "delims=" %%A in ('xcopy /w "%~f0" "%~f0" 2^>nul') do (
    if not defined key (
      setlocal disableDelayedExpansion
      set "key=%%A"
    )
  )
  setlocal enableDelayedExpansion
  if "!key:~-2,1!" neq ")" (
    endlocal&endlocal
    set "key=!LF!"
  ) else if "!key:~-1!" equ "^!" (
    endlocal&endlocal
    set "key=^!"
  ) else if "!key:~-1!" equ "^^" (
    endlocal&endlocal
    set "key=^"
  ) else if "!key:~-1!" equ "!CR!" (
    endlocal&endlocal
    set "key=!CR!"
  ) else (
    for /f delims^=^ eol^= %%A in ("!key:~-1!") do (
      endlocal&endlocal
      set "key=%%A"
    )
  )
  if "!key!" equ "!CR!" exit
  echo [!key!]
)


Dave Benham

miskox
Posts: 630
Joined: 28 Jun 2010 03:46

Re: BrainF*** Interpreter in Batch

#7 Post by miskox » 09 Jul 2014 10:40

Dumping the source file first would make it possible to process all values (0-255) (agerman's excelent file dump: viewtopic.php?p=14361#p14361).

Saso

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: BrainF*** Interpreter in Batch

#8 Post by dbenham » 09 Jul 2014 23:59

Here is my optimized version.

Upon reading Aacini's code more carefully, I realized he already strips out the comments and establishes loop pointers. So I used a derivative of his code parser, but completely redesigned the actual interpreter. It uses an infinite loop without any CALL or GOTO.

Instead of initializing the memory array elements to 0, I undefined all elements of the array, and allow unbounded extension to the left and right. The interpreter treats undefined values the same as 0.

I also used a highly optimized version of the ASCII generator

Code: Select all

:: BF.BAT - BrainF*** interpreter in Batch
::
:: The memory array consists of unsigned bytes
:: The array is unbounded both to the left and right
::
:: BF.BAT can only read input from the keyboard. Input will fail
:: if data is piped into BF.BAT or if stdin is redirected to a file.
::
:: The <Enter> key is read as <CR>
:: <Ctrl-C> is read as <LF>
:: All other inputs are normal.
::
:: Any byte value (except 0x00) may be entered by holding down the <Alt> key
:: and entering a decimal code on the numeric keypad.
::
:: BF.BAT was written by Dave Benham, with help from others as commented below
::


@echo off
setlocal EnableDelayedExpansion
if "%~2" equ "run" goto :run

::-------------------------------------------------------------------------------------
:: Validate parameters and parse bf code
:: This section of code is derived from code by Antonio Parez Ayala,
:: known as Aacini on DosTips
::
if "%~1" equ "" >&2 echo Usage: %~N0 filename.ext&exit /b 1
if not exist "%~1" >&2 echo File not found: %1&exit /b 1

set "operators=+-<>.,[]"
set /a ip=0, sp=0
for /f %%b in ('cmd /U /C type %1^|find /V ""') do (
  if "!operators:%%b=!" neq "%operators%" (
    set /a ip+=1
    set "code[!ip!]=%%b"
    if "%%b" equ "[" (
      set /a sp+=1
      set stack[!sp!]=!ip!
    ) else if "%%b" equ "]" (
      for %%s in (!sp!) do set /a jump[!ip!]=stack[%%s]+1, jump[!stack[%%s]!]=ip+1, sp-=1
    )
  )
)
if %sp% neq 0 >&2 echo ERROR: Unbalanced brackets&exit /b 1
::
:: End of Aacini derived code
::---------------------------------------------------------------------------------------


:: Create character files if they don't already exist
set "needChars="
for /l %%N in (0 1 255) do for %%F in (%%N.bf.chr) do if "%%~zF" neq "1" set "needChars=1"
if defined needChars call :genAllChr

:: Initialize memory to undefined (equivalent to 0)
for /f "delims==" %%A in ('set m[ 2^>nul') do set "%%A="

:: Define CR to hold <CarriageReturn> for use in input routine
for /f %%A in ('copy /Z "%~dpf0" nul') do set "CR=%%A"

:: Define SUB to hold <0x1A>
for /f "delims=" %%A in (26.bf.chr) do set "SUB=%%A"

:: Launch the interpreter
cmd /c "%~f0" x run&&exit /b 0||exit /b 1


:run  - This is the main interpreter loop
set /a "ip=mp=1"
for /l %%. in () do for /f "tokens=1,2" %%A in ("!ip! !mp!") do (
  if        "!code[%%A]!" equ "+" (
    set /a "m[%%B]=(m[%%B]+1)&255, ip+=1"
  ) else if "!code[%%A]!" equ "-" (
    set /a "m[%%B]=(m[%%B]-1)&255, ip+=1"
  ) else if "!code[%%A]!" equ ">" (
    set /a "mp+=1, ip+=1"
  ) else if "!code[%%A]!" equ "<" (
    set /a "mp-=1, ip+=1"
  ) else if "!code[%%A]!" equ "[" (
    set /a ip+=1
    if not defined m[%%B] set "ip=!jump[%%A]!"
    if "!m[%%B]!" equ "0" set "ip=!jump[%%A]!"
  ) else if "!code[%%A]!" equ "]" (
    set /a ip+=1
    if defined m[%%B] if "!m[%%B]!" neq "0" set "ip=!jump[%%A]!"
  ) else if "!code[%%A]!" equ "." (
    if "!m[%%B]!" equ "26" (<nul set/p"=!SUB!") else type !m[%%B]!.bf.chr
    set /a ip+=1
  ) else if "!code[%%A]!" equ "," (
    set "key="
    for /f "delims=" %%K in (
      'xcopy /w "%~f0" "%~f0" 2^>nul'
    ) do if not defined key (
      setlocal disableDelayedExpansion
      set "key=%%K"
    )
    setlocal enableDelayedExpansion
    if "!key:~-2,1!" neq ")" (
      endlocal&endlocal
      set "m[%%B]=10"
    ) else if "!key:~-1!" equ "!CR!" (
      endlocal&endlocal
      set "m[%%B]=13"
    ) else (
      >key.txt.tmp echo(!key:~-1!
      endlocal&endlocal
      for /f "delims=." %%K in ('findstr /lg:key.txt.tmp *.bf.chr') do set "m[%%B]=%%K"
    )
    set /a ip+=1
  ) else exit
)


:genAllChr
::This code creates 256 1 byte files, one for each possible byte value.
::This is encoded in a macro way to be called asynchronously with start cmd /c
::Teamwork of carlos, penpen, aGerman, dbenham, einstein1969
::Tested under Win7 and XP
setlocal disableDelayedExpansion
set ^"genchr=(^
for /l %%N in (%%A !cnt! 255) do (^
  if %%N equ 26 (^
    copy /y nul + nul /a 26.bf.chr /a ^>nul^
  ) else (if %%N geq 35 if %%N leq 126 if %%N neq 61 (^
    ^<nul set /p "=!ascii:~%%N,1!" ^>%%N.bf.chr^
  ))^&^
  if not exist %%N.bf.chr (^
    makecab /d compress=off /d reserveperdatablocksize=26 /d reserveperfoldersize=%%N %%A.tmp %%N.bf.chr ^>nul^&^
    type %%N.bf.chr ^| ((for /l %%n in (1 1 38) do pause)^>nul^&findstr "^^" ^>%%N.temp)^&^
    ^>nul copy /y %%N.temp /a %%N.bf.chr /b^&^
    del %%N.temp^
  )^
))^&^
del %%A.tmp^"
del /f /q /a *bf.chr >nul 2>&1
set "ascii=                                   #$%%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
set /a cnt=number_of_processors
if %cnt% lss 1 set cnt=1
if %cnt% gtr 256 set cnt=256
set /a "end=cnt-1"
for /l %%A in (0 1 %end%) do (
  type nul >%%A.tmp
  if %%A equ %end% (
    cmd /q /v:on /c "%genchr%"
  ) else (
    start "" /b cmd /q /v:on /c "%genchr%"
  )
)
:genAllChr.check
for /l %%N in (0 1 %end%) do if exist %%N.tmp goto :genAllChr.check
exit /b


Here is a small bf program that reads input in a loop, displaying each entered value. The program terminates when <Enter> or <Ctrl-M> is pressed. <Ctrl-C> is interpreted as <LF>. Any value except <0x00> may be entered by holding <Alt>while entering a decimal code on the numeric keypad.

Code: Select all

++++++++++++++++++++++++++++++++>
++++++++++++++++++++++++++++++++++++++++>>
+++++++++++++++++++++++++++++++++++++++++>
++++++++++<<
+
[,<<.>.>.>.>.<<-------------]


Dave Benham

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: BrainF*** Interpreter in Batch

#9 Post by Aacini » 10 Jul 2014 13:00

dbenham wrote:It should be possible to pre-process the code into memory, stripping out comments (non command characters), and establishing loop pointers. Then a single infinite loop should be able to interpret the code extremely fast, without resorting to any CALL or GOTO statements.

Dave Benham


Oops! Last night, after read your suggestion on using a "while loop" instead goto's, I planned 3 modifications: the "while" loop, don't define the memory array, and combine groups of same operators in a single command. Today morning I realized that you have already implemented the first two points! :shock: (It seems that you sleep less than I :) ). Anyway, I borrowed your code for the "," (input) operator, but the way to detect the Enter key is language dependant! The last character in XCOPY /W message in Spanish is an "s" instead of ")".

The optimizing modification of group in a single command several repeated BF operators allows the program to run much faster! Here it is:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
if "%~1" equ ":nextOpCode" goto %1

rem bf.bat: Brainf*ck interpreter in Batch
rem Antonio Perez Ayala

rem Version 1.1: - Bug fixed in arithmetic overflow
rem              - Full support of 256 Ascii characters in "." (output) command

rem Version 2:   - Change "goto" main loop by a "while" one
rem              - No define memory tape array.
rem              - Optimize groups of the same + - > < . commands in a single one
rem              - Use Dave Benham's code for "," (input) command

if "%~1" equ "" echo Usage: %~N0 filename.ext & goto :EOF
if not exist "%~1" echo File not found: %1 & goto :EOF

set "operators=+-<>.,[]"
set "lastOp="
set /A ip=0, sp=0, count=0
set /P "=Loading file." < NUL
for /F "usebackq delims=" %%a in ("%~1") do (
   for /F "delims=" %%b in ('cmd /U /C echo "%%a"^| find /V ""') do (
      if "!operators:%%b=!" neq "%operators%" (
         if "!lastOp!" equ "%%b" (
            set /A count+=1
         ) else (
            if defined lastOp (
               set /A ip+=1
               set "code[!ip!]=!lastOp! !count!"
               set "lastOp="
               set count=0
            )
            if "%%b" equ "[" (
               set /A ip+=1, sp+=1
               set stack[!sp!]=!ip!
            ) else if "%%b" equ "]" (
               set /A ip+=1
               for %%s in (!sp!) do (
                  set "code[!ip!]=] !stack[%%s]!"
                  set "code[!stack[%%s]!]=[ !ip!"
               )
               set /A sp-=1
            ) else (
               set "lastOp=%%b"
               set count=1
            )
         )
      )
   )
   set /P "=." < NUL
)
if %count% gtr 0 (
   set /A ip+=1
   set "code[!ip!]=%lastOp% %count%"
)
echo/
if %sp% neq 0 echo ERROR: Unbalanced brackets & goto :EOF

set /P "=Creating Ascii tables." < NUL
type nul > t.tmp
set "options=/d compress=off /d reserveperdatablocksize=26"
for /L %%i in (0,1,255) do (
   if not exist %%i.chr call :genchr %%i
   set /A mod=%%i %% 16
   if !mod! equ 0 set /P "=." < NUL
)
for /F %%a in ('copy /Z "%~F0" NUL') do set "CR=%%a"
for /F "delims=" %%a in (26.chr) do set "SUB=%%a"
set "options="
del t.tmp temp.tmp
echo/

echo Running.
set /A ip=0, mp=0
cmd /Q /C "%~F0" :nextOpCode
exit /B %errorlevel%

:nextOpCode
for /L %%? in () do (
   set /A ip+=1
   for %%i in (!ip!) do for /F "tokens=1-3" %%a in ("!mp! !code[%%i]!") do (
   if        "%%b" equ "+" (
      set /A "m[%%a]=(m[%%a]+%%c) & 255"
   ) else if "%%b" equ "-" (
      set /A "m[%%a]=(m[%%a]-%%c) & 255"
   ) else if "%%b" equ ">" (
      set /A "mp=(mp+%%c) & 4095"
   ) else if "%%b" equ "<" (
      set /A "mp=(mp-%%c) & 4095"
   ) else if "%%b" equ "[" (
      if not defined m[%%a] ( set ip=%%c
      ) else if "!m[%%a]!" equ "0" set ip=%%c
   ) else if "%%b" equ "]" (
      if defined m[%%a] if "!m[%%a]!" neq "0" set ip=%%c
   ) else if "%%b" equ "." (

      for /L %%i in (1,1,%%c) do (
         if defined m[%%a] (
            if        "!m[%%a]!" equ "10" (
                echo/
            ) else if "!m[%%a]!" equ "26" (
               set /P "=!SUB!" < NUL
            ) else (
               type !m[%%a]!.chr
            )
         ) else (
            type 0.chr
         )
      )

   ) else if "%%b" equ "," (

      setlocal DisableDelayedExpansion
      set "key="
      for /F "delims=" %%K in ('xcopy /W "%~F0" "%~F0" 2^>NUL') do (
         if not defined key set "key=%%K"
      )
      setlocal EnableDelayedExpansion
      if "!key:~-2,1!" neq ")" (
         endlocal & endlocal
         set "m[%%a]=10"
      ) else if "!key:~-1!" equ "!CR!" (
         endlocal & endlocal
         set "m[%%a]=13"
      ) else (
         > key.txt.tmp echo(!key:~-1!
         endlocal & endlocal
         for /F "delims=." %%K in ('findstr /LG:key.txt.tmp *.chr') do set "m[%%a]=%%K"
      )

   ) else exit !m[%%a]!
   )
)


:genchr
REM This code creates one single byte. Parameter: int
REM Teamwork of carlos, penpen, aGerman, dbenham
REM Tested under Win2000, XP, Win7, Win8
if %~1 neq 26 (
   makecab %options% /d reserveperfoldersize=%~1 t.tmp %~1.chr > nul
   type %~1.chr | ( (for /l %%N in (1,1,38) do pause)>nul & findstr "^" > temp.tmp )
   >nul copy /y temp.tmp /a %~1.chr /b
) else (
   copy /y nul + nul /a 26.chr /a >nul
)
exit /B


Antonio

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: BrainF*** Interpreter in Batch

#10 Post by einstein1969 » 10 Jul 2014 17:59

Hi,

i have tried this but seem don't work...

EDIT:
ref: towers of hanoi

einstein1969

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: BrainF*** Interpreter in Batch

#11 Post by Aacini » 10 Jul 2014 18:29

Perhaps this is the reason: "The terminal must be able to understand vt100 escape codes..."

Antonio

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: BrainF*** Interpreter in Batch

#12 Post by einstein1969 » 10 Jul 2014 19:42

Aacini wrote:Perhaps this is the reason: "The terminal must be able to understand vt100 escape codes..."

Antonio


I do not think ... maybe it's too slow?

There are 2 type of escape sequence: ← is ESC

Code: Select all

←[H←[2J

which equals CLS

And

Code: Select all

←[x;yH

which is goto x,y

The output is blocked at

Code: Select all

←[H←[2J←[2;27HTowers of Hanoi in Brainf*ck←[3;15HWritten by Clifford Wolf <http://www.clifford.at/bfcpu/>←[14;43H



einstein1969

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: BrainF*** Interpreter in Batch

#13 Post by Aacini » 10 Jul 2014 20:18

It is not blocked, it is Running!

You must realize that by its very nature, the executable code generated by Brainf*uck compilers is very inefficient. If the BF support is an interpreter, the code is even more inefficient -> slow execution.

In this case we have a BF interpreter running in a Batch file, perhaps the worst possible combination, so any BF program moderately complex will take a lot of time to complete!

You may test the BF program in any Brainf*ck optimizing compiler and run it to have an idea of the "shortest" possible execution time we may expect from a given program. Then, multiply that time for a several thousand times (a suitable factor, perhaps?) to have an idea of the time required by the Batch file interpreter.

Antonio

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: BrainF*** Interpreter in Batch

#14 Post by einstein1969 » 10 Jul 2014 20:35

Aacini wrote:It is not blocked, it is Running!

You must realize that by its very nature, the executable code generated by Brainf*uck compilers is very inefficient. If the BF support is an interpreter, the code is even more inefficient -> slow execution.

In this case we have a BF interpreter running in a Batch file, perhaps the worst possible combination, so any BF program moderately complex will take a lot of time to complete!

You may test the BF program in any Brainf*ck optimizing compiler and run it to have an idea of the "shortest" possible execution time we may expect from a given program. Then, multiply that time for a several thousand times (a suitable factor, perhaps?) to have an idea of the time required by the Batch file interpreter.

Antonio


Do you know an "Brainf*ck optimizing compiler"? what it this?

einstein1969

einstein1969
Expert
Posts: 960
Joined: 15 Jun 2012 13:16
Location: Italy, Rome

Re: BrainF*** Interpreter in Batch

#15 Post by einstein1969 » 10 Jul 2014 21:00

I have misured the "performance" of BF interpreter with the previous program:

dbenham BF about 15 cycles/s
aacini BF about 45 cycles/s

einstein1969

Post Reply