I think we need at least a 400% performance increase to get hanoi to be almost tolerable. I don't see that happening.einstein1969 wrote:Next step? a little more speed. This is very difficult... but we will do!
The execution of Hanoi_MOD.bf is now fast but more speed is necessary for a reasonable speed (for demo porpouse)
I can't reproduce your findings. But it should be easy to modify my code on your own to see how it works for you. There are two places where I clear the environment. There is already a mechanism to skip important variables that can be expanded.einstein1969 wrote:How increment the speed?
These are some tricks:
- problem with TMP/TEMP/USERPROFILE and FOR/F
I think that may set TMP=C:\ or other short path and leave the TMP variable.
(about 5-10% OFF)
I took your advice in my version 6. It is a good idea, but I'm not seeing much improvement.einstein1969 wrote:- use the counter of infinite loop. This drop off one variable in some cases. Examples for variable "cycles"
I don't see what else can be removed.einstein1969 wrote:- Remove other not used variables.
Feel free to experiment with my code on your own. My guess this is splitting hairs, and it will not make a significant difference. We've limited the size of the environment, so it shouldn't make much difference.einstein1969 wrote:- Fine Tuning for Order of variable in the environment. Variables most used at the TOP.
I tried this, as well as some other ideas to reduce the size of the SET /A "instruction". I was able to reduce the size of the instruction set significantly, but I did not see any performance increase. So I reverted back to the original notation for better readability. I've appended 3 versions to this post in case you want to experiment.einstein1969 wrote:- Reduce dimension of variables name and code for example C_1223=S ...
Well, my code already allows indefinite concatenation of + - and [-] But they are rare, and normally pointless. Otherwise, the answer is NO. It is not possible to get the correct result if any ops are concatenated after < > [ ]. There is one special case with room for optimization. Some programs use [-] to clear a value, followed by a series of + to set a fixed positive value. My current code will perform 2 computations. But they could be merged into a single computation that sets the end value directly. Hanoi uses this construct, but it is not particularly common. I don't think the optimization will be noticeable.einstein1969 wrote:- It is possible merge 3 instructions into one?
I can't think of any.einstein1969 wrote:- Other Ideas?
Possibly, but I think it will be negligible.einstein1969 wrote:EDIT : the set /a ip+=1 instruction can be merged with precedence SET and replicated. The performance could be increase.
Below are versions 5.6, 5.7, and 5.8. They are all derived from my version 5 (without my version 6 enhancements). The goal was to reduce the size of the SET /A instructions in order to conserve memory used by environment space. I didn't see any performance improvement, so I abandoned this approach.
5.5)
- mp becomes m
- ip becomes i
- m[n] becomes m.n
- c[n] becomes c.n
5.7) Same as 5.6, plus a number of constant string snippets that are repeated with each instruction are stored in variables. The variables are then referenced in the instruction with delayed expansion.
5.8) Same as 5.7, plus removed the quotes surrounding each SET /A instruction.
EDIT - Fixed the definition of LF in 5.6 and 5.7, removed the errant spaces
version 5.6
Code: Select all
::BF.BAT - BrainF*** interpreter in Batch, dbenham version 5.6
::
:: Usage:
::
:: bf sourceFile [-option [value]]...
::
:: Options
::
:: -i Filename = read Input from file Filename.
::
:: -z = <Ctrl-Z> terminates keyboard input (forces EOF).
::
:: -e Value = EOF value: Sets value read into memory upon End-Of-File.
:: Default is no change to memory.
::
:: -ee = terminate with Error if attempt is made to read past EOF.
::
:: -eo = terminate with Error if +/- results in Overflow.
::
:: -em = terminate with Error if < yields a negative Memory address.
::
:: -ea = terminate with Error on Any fault. Same as -ee -eo -em
::
:: -s = Silent mode. Don't print initialization messages, elapsed
:: times, or run statistics. Also disables the -l option.
::
:: -d Filename = Dump the parsed op codes to Filename.
:: Use con to print to screen.
::
:: -m Filename = write the interpreter Macro to Filename.
:: Use con to print to screen.
::
:: -t = Trace mode: Show op address/value, memory address/value
:: each cycle.
::
:: -# Count = Enables # op: Show current state. Count specifies the number
:: of values to display before and after the current address.
:: BUG ALERT: Some array values may not display properly if
:: Count > 0 and array members are swapped out to disk. This is
:: never a problem if -a is undefined.
::
:: -p = Parse only.
::
:: -c Count = Code memory (default = 100). Maximum amount of code allowed
:: to reside in memory at one time. Unlimited if undefined.
:: Excess code is swapped out to disk.
::
:: -a Count = Array memory (default = unlimited). Maximum number of array
:: members allowed to reside in memory at one time. Unlimited
:: if undefined. Excess array members are swapped out to disk.
::
:: -l Count = Limit the number of execution cycles to Count.
:: Ignored if -s enabled. Default is no limit (undefined).
::
:: The memory array consists of unsigned bytes. Increment and decrement
:: operations simply wrap when the limits are exceeded unless -eo is specified.
::
:: The memory array is unbounded both to the left and right. If -em is specified
:: then the array is unbounded to the right only.
::
:: In theory, the code pointer can range from 0 to 2147483647,
:: and the memory pointer can range from -2147483648 to 2147483647.
:: But performance will become unbearably long before those limits are
:: reached.
::
:: BF.BAT can only read input from a file supplied as a command line option, or
:: the keyboard. Input will fail if data is piped into BF.BAT or if stdin is
:: redirected to a file.
::
:: During keyboard input, the <Enter> key is read as <CR>. 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. The only way to enter
:: <LF> is via <Alt> and the numeric keypad.
::
:: This code was written by Dave Benham (aka dbenham)
:: with major contributions from Antonio Perez Ayala (aka Aacini),
:: and einstein1969.
::
:: Development efforts can be traced at
:: http://www.dostips.com/forum/viewtopic.php?f=3&t=5751
@echo off
:: Rentry point when running the bf program
if "%~1" equ ":run" ( rem
%bfInterpreter%
) %input%
:: Clear environment except for critical standard values
setlocal disableDelayedExpansion
for /f "eol== delims==" %%V in ('set^|findstr /rc:"^[^=]*!"') do set "%%V="
set "preserve= TEMP TMP PATH PATHEXT COMSPEC PRESERVE "
setlocal enableDelayedExpansion
for /f "eol== delims==" %%V in ('set') do (
if defined %%V if "!preserve: %%V =!" equ "!preserve!" set "%%V="
)
set "preserve="
:: Validate parameters
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
:: Define options
set options=-d:"" -s: -z: -e:"" -m:"" -ee: -eo: -em: -ea: -t: -#:"" -p: -i:"" -c:100 -a:"" -l:""
:: Set option defaults
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop Parse the option arguments
if not "%~2"=="" (
set "test=!options:*%~2:=! "
if "!test!"=="!options! " (
echo Error: Invalid option %~2
exit /b 1
) else if "!test:~0,1!"==" " (
set "%~2=1"
) else (
set "%~2=%~3"
shift /2
)
shift /2
goto :loop
)
:: Begin macro defnitions --------------
:: Define LF to contain a linefeed (0x0A) character
set ^"LF=^
^" The above empty line is critical - DO NOT REMOVE
:: define a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set showTime=(%\n%
for /f "tokens=1-4 delims=:.," %%a in ("^!t1: =0^!"^) do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"%\n%
for /f "tokens=1-4 delims=:.," %%a in ("^!t2: =0^!"^) do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"%\n%
set /a "tDiff=t2-t1"%\n%
if ^^!tDiff^^! lss 0 set /a tDiff+=24*60*60*100%\n%
set /a "s=tDiff/100, f=tDiff+100"%\n%
echo Elapsed time = ^^!s^^!.^^!f:~-2^^! s%\n%
^)
if defined -ea (
set -eo=1
set -em=1
set -ee=1
)
if defined -eo (
set "inc=over|=(m.^^^!m^^^!+=^!count^!)&-256"
set "dec=over|=(m.^^^!m^^^!-=^!count^!)&-256"
set overError=%\n%
if ^^!over^^! neq 0 ^>^&2 echo ^^!LF^^!ERROR: Overflow at i=%%I m=%%A^&exit 1
) else (
set "inc=m.^^^!m^^^!=(m.^^^!m^^^!+^!count^!)&255"
set "dec=m.^^^!m^^^!=(m.^^^!m^^^!-^!count^!)&255"
)
if defined -em (
set leftError=%\n%
if ^^!m^^! lss 0 ^>^&2 echo ^^!LF^^!ERROR: Invalid memory access at i=%%I m=%%A^&exit 1
)
if defined -t set trace=%\n%
echo c.%%I=^^!c.%%I^^! m.%%A=^^!m.%%A^^!
if defined -e set ^"setEOF=set /a "m.%%A=!-e!"^"
if defined -z set eofMacros=1
if defined -i set eofMacros=1
if defined eofMacros (
set "ifNotEOF=if not defined EOF"
set ifSub=else if "^!key:~-1^!" equ "^!SUB^!" (%\n%
endlocal^&endlocal%\n%
set EOF=1%\n%
!setEOF!%\n%
^)
if defined -ee (
set "elsePastEOF= else >&2 echo ^!LF^!ERROR: Read past EOF at i=%%I m=%%A&exit 1 "
) else if defined -e (
set "elsePastEOF= else !setEOF! "
)
)
if defined -i (
set ^"getInput=else if "%%B" equ "," ( for /l %%N in (1 1 %%C^) do !ifNotEOF! (%\n%
set "key="%\n%
set /p "key="%\n%
if defined key (set /a "m.%%A=0x^!key^!"^) else ( !setEOF!%\n%
set EOF=1%\n%
^)%\n%
^)!elsePastEOF!^) ^"
) else for %%F in (findstr.exe) do (
set ^"getInput=else if "%%B" equ "," (for /l %%N in (1 1 %%C^) do !ifNotEOF! (%\n%
set "key="%\n%
set "CtrlC=1"%\n%
setlocal disableDelayedExpansion%\n%
for /f "delims=" %%K in (%\n%
'^^^^^""%%~dp$path:Fxcopy.exe" /w "%~f0" "%~f0" 2^^^^^>nul^^^^^"'%\n%
^) do if not defined key (set "key=%%K"^) else set "CtrlC="%\n%
setlocal enableDelayedExpansion%\n%
if defined CtrlC (%\n%
endlocal^&endlocal%\n%
set "m.%%A=3"%\n%
^) else if "^!key^!" equ "^!line1^!" (%\n%
endlocal^&endlocal%\n%
set "m.%%A=10"%\n%
^) else if "^!key:~-1^!" equ "^!CR^!" (%\n%
endlocal^&endlocal%\n%
set "m.%%A=13"%\n%
^) !ifSub! else (%\n%
^>key.bf_tmp echo(^^!key:~-1^^!%\n%
endlocal^&endlocal%\n%
for /f "delims=." %%K in ('^^^^^""%%~$path:F" /lg:key.bf_tmp *.bf_chr^^^^^"'^) do set "m.%%A=%%K"%\n%
^)%\n%
^)!elsePastEOF!^) ^"
)
if defined -# (
set #=#
set #Parse=else if "%%b" equ "#" (%\n%
if defined lastCmd (%\n%
set /a i+=1%\n%
^>c.^^!i^^!.bf_tmp echo S "^^!lastCmd:~0,-1^^!"%\n%
set "lastCmd="%\n%
^)%\n%
set /a "i+=1, #Found=1, clr=0"%\n%
^>c.^^!i^^!.bf_tmp echo #%\n%
^)
if !-#! equ 0 (
set ^"#Exec=else if "%%B" equ "#" (%\n%
set /a next=i+1%\n%
set "msg="%\n%
^<c.^^!next^^!.bf_tmp set /p "msg="%\n%
echo c.^^!next^^!=^^!msg^^! m.%%A=^^!m.%%A^^!%\n%
^) ^"
) else set ^"#Exec=else if "%%B" equ "#" (%\n%
set /a next=i+1, m1=m-!-#!, m2=m-1, m3=m+1, m4=m+!-#!%\n%
set "msg="%\n%
^<c.^^!next^^!.bf_tmp set /p "msg="%\n%
set "msg=c.^!next^!=^!msg^! m=%%A: "%\n%
for /l %%N in (^^!m1^^! 1 ^^!m2^^!^) do set "msg=^!msg^!| ^!m.%%N^! "%\n%
set "msg=^!msg^!< ^!m.%%A^! >"%\n%
for /l %%N in (^^!m3^^! 1 ^^!m4^^!^) do set "msg=^!msg^! ^!m.%%N^! |"%\n%
echo ^^!msg^^!%\n%
^) ^"
)
if not defined -s (
set "getCycles=, cycles+=1"
set "getCodePageFaults=, codePageFaults+=1"
set "getArrayStats=, aSize+=1"
set "getArrayPageFaults=, arrayPageFautls+=1"
set showStats=%\n%
^>stats.bf_tmp (echo cycles=^^!cycles^^! codePageFaults=^^!codePageFaults^^!%\n%
echo arraySize=^^!aSize^^! arrayPageFaults=^^!arrayPageFaults^^!^)
if defined -l set getCycles=!getCycles! ^& if ^^!cycles^^! equ !-l! (!showStats!%\n%
exit 0%\n%
^)
)
if defined -c set codePaging=%\n%
if not defined c.%%I if exist c.%%I.bf_tmp (%\n%
if ^^!cdCnt^^! equ !-c! (%\n%
for /f "delims==" %%A in ('set c.') do set "%%A="%\n%
set cdCnt=0%\n%
)%\n%
set /a cdCnt+=1!getCodePageFaults!%\n%
^<c.%%I.bf_tmp set /p "c.%%I="%\n%
)
if defined -a (
set arrayPaging=if not defined m.%%A (%\n%
if ^^!aCnt^^! equ !-a! for /f "tokens=1,2 delims==" %%a in ('set m.'^) do (%\n%
^>%%a.bf_tmp echo %%b%\n%
set %%a=%\n%
set aCnt=0%\n%
^)%\n%
if exist m.%%A.bf_tmp (%\n%
^<m.%%A.bf_tmp set /p m.%%A=%\n%
set /a arrayPageFaults+=1%\n%
^) else set /a m.%%A=0!getArrayStats!%\n%
set /a aCnt+=1%\n%
^)
) else set "arrayPaging=if not defined m.%%A set /a m.%%A=0!getArrayStats!"
:: End macro definition ------------------
:: Create character files if they don't already exist
if not defined -s (
echo ----------------------------
echo Begin Initialization
echo Testing ASCII character files.
)
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 (
if not defined -s (
echo Creating ASCII character files...
set "t1=!time!"
)
call :genAllChr
if not defined -s (
set t2=!time!
%showTime%
)
)
:: Parse the input file
if not defined -i goto :skipInput
if not defined -s (
<nul set /p "=Parsing input."
set t1=!time!
)
if not exist "!-i!" >&2 echo ERROR: Input file "!-i!" not found & exit 1
if exist "!-i!\" >&2 echo ERROR: Input file "!-i!" not found & exit 1
copy 0.bf_chr compare.bf_tmp >nul
set /a size=skipStart=1
for %%F in ("!-i!") do set inSize=%%~zF
for /l %%N in (1 1 32) do if !size! lss !inSize! set /a "size*=2" & type compare.bf_tmp >>compare.bf_tmp
fc /b "!-i!" compare.bf_tmp | findstr /rxc:"........: .. .." >diff.bf_tmp
>input.bf_tmp (
for /f "tokens=1,2 delims=:[] " %%A in (diff.bf_tmp) do (
set /a skipEnd=0x%%A
for /l %%N in (!skipStart! 1 !skipEnd!) do echo 00
echo %%B
set /a skipStart=skipEnd+2
if "!skipStart:~-3!" equ "000" <nul set /p "=." >&2
)
for /l %%N in (!skipStart! 1 !inSize!) do echo 00
)
set "input=<input.bf_tmp"
if not defined -s (
set t2=!time!
echo(
%showTime%
)
:skipInput
:: Parse the code. Consecutive op codes are coalesced into one op with a count.
:: Optionally treats # as an additional show state op code (withoug coalesce).
:: Optimization collapses [-] into a special clear value op.
:: + - < > [ ] [-] are all converted into an S op followed by a SET /A expression.
:: Consecutive + - and [-] are concatenated into one op. If followed by
:: < > [ or ] then that op is also concatenated, but that is the limit for one op.
:: . and , are stored as themselves, followed by the count.
:: # is stored as itself without a count
:: Detects if input is not used so getInput can be removed from interpreter loop.
:: Treats loops at beginning or immedieately following ] as comments (ignores them)
if not defined -s (
<nul set /p "=Parsing code."
set "parseProgress=set /a n=(n+1^^)%%500&if ^!n^! equ 0 <nul set /p "=.""
set "t1=!time!"
)
2>nul del c.*.bf_tmp
for %%C in (+ - "<" ">" . "," #) do set "comment%%~C=0"
set /a i=comment]=-1, sp=count=comment=0, comment[=1
cmd /u /c type %1|find /v ""|findstr "[+\-<>.,[\]!#!]" >src.bf_tmp
for /f %%b in (src.bf_tmp) do ( %parseProgress%
if !comment! gtr 0 (
set /a comment+=!comment%%b!
) else if "!lastOp!" equ "%%b" (
set /a count+=1
) else (
if defined lastOp (
set "test="
if !clr! equ 1 if !lastOp! equ - if !count! equ 1 set /a clr=test=2
if not defined test set clr=0
if "!lastOp!" equ "+" (
set "lastCmd=!lastCmd!%inc%,"
) else if "!lastOp!" equ "-" (
set "lastCmd=!lastCmd!!delim!%dec%,"
) else (
set /a i+=1
if "!lastOp!" equ ">" (
>c.!i!.bf_tmp echo S "!lastCmd!m+=!count!"
) else if "!lastOp!" equ "<" (
>c.!i!.bf_tmp echo S "!lastCmd!m-=!count!"
) else (
if defined lastCmd (
>c.!i!.bf_tmp echo S "!lastCmd:~0,-1!"
set /a i+=1
)
>c.!i!.bf_tmp echo !lastOp! !count!
)
set "lastCmd="
)
set "lastOp="
)
if "%%b" equ "[" (
if defined comment (set /a comment+=1) else (
set /a "i+=1, sp+=1, clr=1"
set /a "stack.!sp!=i"
set "stackCmd.!sp!=!lastCmd!"
set "lastCmd="
)
) else if "%%b" equ "]" for %%s in (!sp!) do (
if !clr! equ 2 (
set "lastCmd=!stackCmd.%%s!m.^!m^!=0,"
set /a "i-=1, sp-=1"
) else (
set /a i+=1, sp-=1, count=i-stack.%%s
>c.!i!.bf_tmp echo S "!lastCmd!i-=^^^!^^^!m.^!m^!*!count!"
>c.!stack.%%s!.bf_tmp echo S "!stackCmd.%%s!i+=^^^!m.^!m^!*!count!"
set "lastCmd="
)
set /a "clr=comment=0"
) %#Parse% else (
set "comment="
set "lastOp=%%b"
set /a "count=1"
if "%%b" equ "," set inputRequired=1
)
)
)
if defined lastOp (
set /a i+=1
(
if "!lastOp!" equ "+" (
echo S "!lastCmd!%inc%"
) else if "!lastOp!" equ "-" (
echo S "!lastCmd!%dec%"
) else if "!lastOp!" equ ">" (
echo S "!lastCmd!m+=!count!"
) else if "!lastOp!" equ "<" (
echo S "!lastCmd!m-=!count!"
) else (
echo S "!lastCmd:~0,-1!"
echo !lastOp! !count!
)
)>c.!i!.bf_tmp
) else if defined lastCmd (
set /a i+=1
>c.!i!.bf_tmp echo S "!lastCmd:~0,-1!"
)
if not defined -s (
set "t2=!time!"
echo(
%showTime%
)
:: Error if unbalanced brackets
if %sp% neq 0 >&2 echo ERROR: Unbalanced brackets&exit /b 1
if defined comment if "%comment%" neq "0" >&2 echo ERROR: Unbalanced brackets&exit /b 1
:: Dump the parsed op codes
if defined -d (for /l %%N in (0 1 !i!) do (
<c.%%N.bf_tmp set /p "msg="
echo c.%%N=!msg!
)) >"!-d!"
:: Disable code paging if code length does not exceed -cp
if not defined -c set "-c=!i!
if !i! leq !-c! (
set "codePaging="
for /l %%N in (0 1 !i!) do <c.%%N.bf_tmp set /p "c.%%N="
)
:: Undefine getInput and/or #Parse if not needed
if not defined inputRequired set "getInput="
if not defined #Found set "#Exec="
:: 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"
:: Establish line one of input routine for this locale
set "line1="
for /f "delims=" %%L in (
'echo .^|xcopy /w "%~f0" "%~f0" 2^>nul'
) do if not defined line1 set "line1=%%L"
set "line1=!line1:~0,-1!"
:: Create final interpreter macro
set bfInterpreter=^
for /f "delims==" %%V in ('set^^^^^|findstr /bv "c. line1= CR= SUB="') do set "%%V="%\n%
set /a "i=m=cdCnt=cycles=aCnt=aSize=over=0"%\n%
for /l %%. in () do for %%I in (^^!i^^!) do (!codePaging!%\n%
for /f "tokens=1-3" %%A in ("^!m^! ^!c.%%I^!") do (%\n%
!arrayPaging!!trace!%\n%
if "%%B" equ "S" (%\n%
set /a "%%C"!overError!!leftError!%\n%
) else if "%%B" equ "." for /l %%N in (1 1 %%C) do (%\n%
if "^!m.%%A^!" equ "26" (set /p "=^!SUB^!" ^<nul) else type ^^!m.%%A^^!.bf_chr%\n%
) !getInput!!#Exec!else (!showStats!%\n%
exit 0%\n%
)%\n%
set /a i+=1!getCycles!%\n%
)%\n%
)
:: Print out the finalized brainF*** interpreter macro
if defined -m >"!-m!" echo bfInterpreter=!lf!!bfInterpreter:%%=%%%%!
if not defined -s (
echo Initialization complete
echo ============================
set t1=%time%
)
(call )
if defined -p goto :quit
:: Launch the interpreter
cmd /v:on /c "%~f0" :run&&(call )||(call)
set "err=%errolevel%
if not defined -s (
set t2=%time%
echo(
echo ============================
%showTime%
if exist stats.bf_tmp type stats.bf_tmp
)
:quit
del *.bf_tmp
exit /b %err%
: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
version 5.7
Code: Select all
::BF.BAT - BrainF*** interpreter in Batch, dbenham version 5.7
::
:: Usage:
::
:: bf sourceFile [-option [value]]...
::
:: Options
::
:: -i Filename = read Input from file Filename.
::
:: -z = <Ctrl-Z> terminates keyboard input (forces EOF).
::
:: -e Value = EOF value: Sets value read into memory upon End-Of-File.
:: Default is no change to memory.
::
:: -ee = terminate with Error if attempt is made to read past EOF.
::
:: -eo = terminate with Error if +/- results in Overflow.
::
:: -em = terminate with Error if < yields a negative Memory address.
::
:: -ea = terminate with Error on Any fault. Same as -ee -eo -em
::
:: -s = Silent mode. Don't print initialization messages, elapsed
:: times, or run statistics. Also disables the -l option.
::
:: -d Filename = Dump the parsed op codes to Filename.
:: Use con to print to screen.
::
:: -m Filename = write the interpreter Macro to Filename.
:: Use con to print to screen.
::
:: -t = Trace mode: Show op address/value, memory address/value
:: each cycle.
::
:: -# Count = Enables # op: Show current state. Count specifies the number
:: of values to display before and after the current address.
:: BUG ALERT: Some array values may not display properly if
:: Count > 0 and array members are swapped out to disk. This is
:: never a problem if -a is undefined.
::
:: -p = Parse only.
::
:: -c Count = Code memory (default = 100). Maximum amount of code allowed
:: to reside in memory at one time. Unlimited if undefined.
:: Excess code is swapped out to disk.
::
:: -a Count = Array memory (default = unlimited). Maximum number of array
:: members allowed to reside in memory at one time. Unlimited
:: if undefined. Excess array members are swapped out to disk.
::
:: -l Count = Limit the number of execution cycles to Count.
:: Ignored if -s enabled. Default is no limit (undefined).
::
:: The memory array consists of unsigned bytes. Increment and decrement
:: operations simply wrap when the limits are exceeded unless -eo is specified.
::
:: The memory array is unbounded both to the left and right. If -em is specified
:: then the array is unbounded to the right only.
::
:: In theory, the code pointer can range from 0 to 2147483647,
:: and the memory pointer can range from -2147483648 to 2147483647.
:: But performance will become unbearably long before those limits are
:: reached.
::
:: BF.BAT can only read input from a file supplied as a command line option, or
:: the keyboard. Input will fail if data is piped into BF.BAT or if stdin is
:: redirected to a file.
::
:: During keyboard input, the <Enter> key is read as <CR>. 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. The only way to enter
:: <LF> is via <Alt> and the numeric keypad.
::
:: This code was written by Dave Benham (aka dbenham)
:: with major contributions from Antonio Perez Ayala (aka Aacini),
:: and einstein1969.
::
:: Development efforts can be traced at
:: http://www.dostips.com/forum/viewtopic.php?f=3&t=5751
@echo off
:: Rentry point when running the bf program
if "%~1" equ ":run" ( rem
%bfInterpreter%
) %input%
:: Clear environment except for critical standard values
setlocal disableDelayedExpansion
for /f "eol== delims==" %%V in ('set^|findstr /rc:"^[^=]*!"') do set "%%V="
set "preserve= TEMP TMP PATH PATHEXT COMSPEC PRESERVE "
setlocal enableDelayedExpansion
for /f "eol== delims==" %%V in ('set') do (
if defined %%V if "!preserve: %%V =!" equ "!preserve!" set "%%V="
)
set "preserve="
:: Validate parameters
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
:: Define options
set options=-d:"" -s: -z: -e:"" -m:"" -ee: -eo: -em: -ea: -t: -#:"" -p: -i:"" -c:100 -a:"" -l:""
:: Set option defaults
for %%O in (%options%) do for /f "tokens=1,* delims=:" %%A in ("%%O") do set "%%A=%%~B"
:loop Parse the option arguments
if not "%~2"=="" (
set "test=!options:*%~2:=! "
if "!test!"=="!options! " (
echo Error: Invalid option %~2
exit /b 1
) else if "!test:~0,1!"==" " (
set "%~2=1"
) else (
set "%~2=%~3"
shift /2
)
shift /2
goto :loop
)
:: Begin macro defnitions --------------
:: Define LF to contain a linefeed (0x0A) character
set ^"LF=^
^" The above empty line is critical - DO NOT REMOVE
:: define a newline with line continuation
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
set showTime=(%\n%
for /f "tokens=1-4 delims=:.," %%a in ("^!t1: =0^!"^) do set /a "t1=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"%\n%
for /f "tokens=1-4 delims=:.," %%a in ("^!t2: =0^!"^) do set /a "t2=(((1%%a*60)+1%%b)*60+1%%c)*100+1%%d-36610100"%\n%
set /a "tDiff=t2-t1"%\n%
if ^^!tDiff^^! lss 0 set /a tDiff+=24*60*60*100%\n%
set /a "s=tDiff/100, f=tDiff+100"%\n%
echo Elapsed time = ^^!s^^!.^^!f:~-2^^! s%\n%
^)
if defined -ea (
set -eo=1
set -em=1
set -ee=1
)
set "+=i+=^!m."
set "-=i-=^!^!m."
if defined -eo (
set "e=)&-256"
set "o=over|=(m."
set "inc=^^^!o^^^!^^^!m^^^!+=^!count^!^^^!e^^^!"
set "dec=^^^!o^^^!^^^!m^^^!-=^!count^!^^^!e^^^!"
set overError=%\n%
if ^^!over^^! neq 0 ^>^&2 echo ^^!LF^^!ERROR: Overflow at i=%%I m=%%A^&exit 1
) else (
set "e==(m."
set "o=)&255"
set "inc=m.^^^!m^^^!^^^!e^^^!^^^!m^^^!+^!count^!^^^!o^^^!"
set "dec=m.^^^!m^^^!^^^!e^^^!^^^!m^^^!-^!count^!^^^!o^^^!"
)
if defined -em (
set leftError=%\n%
if ^^!m^^! lss 0 ^>^&2 echo ^^!LF^^!ERROR: Invalid memory access at i=%%I m=%%A^&exit 1
)
if defined -t set trace=%\n%
echo c.%%I=^^!c.%%I^^! m.%%A=^^!m.%%A^^!
if defined -e set ^"setEOF=set /a "m.%%A=!-e!"^"
if defined -z set eofMacros=1
if defined -i set eofMacros=1
if defined eofMacros (
set "ifNotEOF=if not defined EOF"
set ifSub=else if "^!key:~-1^!" equ "^!SUB^!" (%\n%
endlocal^&endlocal%\n%
set EOF=1%\n%
!setEOF!%\n%
^)
if defined -ee (
set "elsePastEOF= else >&2 echo ^!LF^!ERROR: Read past EOF at i=%%I m=%%A&exit 1 "
) else if defined -e (
set "elsePastEOF= else !setEOF! "
)
)
if defined -i (
set ^"getInput=else if "%%B" equ "," ( for /l %%N in (1 1 %%C^) do !ifNotEOF! (%\n%
set "key="%\n%
set /p "key="%\n%
if defined key (set /a "m.%%A=0x^!key^!"^) else ( !setEOF!%\n%
set EOF=1%\n%
^)%\n%
^)!elsePastEOF!^) ^"
) else for %%F in (findstr.exe) do (
set ^"getInput=else if "%%B" equ "," (for /l %%N in (1 1 %%C^) do !ifNotEOF! (%\n%
set "key="%\n%
set "CtrlC=1"%\n%
setlocal disableDelayedExpansion%\n%
for /f "delims=" %%K in (%\n%
'^^^^^""%%~dp$path:Fxcopy.exe" /w "%~f0" "%~f0" 2^^^^^>nul^^^^^"'%\n%
^) do if not defined key (set "key=%%K"^) else set "CtrlC="%\n%
setlocal enableDelayedExpansion%\n%
if defined CtrlC (%\n%
endlocal^&endlocal%\n%
set "m.%%A=3"%\n%
^) else if "^!key^!" equ "^!line1^!" (%\n%
endlocal^&endlocal%\n%
set "m.%%A=10"%\n%
^) else if "^!key:~-1^!" equ "^!CR^!" (%\n%
endlocal^&endlocal%\n%
set "m.%%A=13"%\n%
^) !ifSub! else (%\n%
^>key.bf_tmp echo(^^!key:~-1^^!%\n%
endlocal^&endlocal%\n%
for /f "delims=." %%K in ('^^^^^""%%~$path:F" /lg:key.bf_tmp *.bf_chr^^^^^"'^) do set "m.%%A=%%K"%\n%
^)%\n%
^)!elsePastEOF!^) ^"
)
if defined -# (
set #=#
set #Parse=else if "%%b" equ "#" (%\n%
if defined lastCmd (%\n%
set /a i+=1%\n%
^>c.^^!i^^!.bf_tmp echo S "^^!lastCmd:~0,-1^^!"%\n%
set "lastCmd="%\n%
^)%\n%
set /a "i+=1, #Found=1, clr=0"%\n%
^>c.^^!i^^!.bf_tmp echo #%\n%
^)
if !-#! equ 0 (
set ^"#Exec=else if "%%B" equ "#" (%\n%
set /a next=i+1%\n%
set "msg="%\n%
^<c.^^!next^^!.bf_tmp set /p "msg="%\n%
echo c.^^!next^^!=^^!msg^^! m.%%A=^^!m.%%A^^!%\n%
^) ^"
) else set ^"#Exec=else if "%%B" equ "#" (%\n%
set /a next=i+1, m1=m-!-#!, m2=m-1, m3=m+1, m4=m+!-#!%\n%
set "msg="%\n%
^<c.^^!next^^!.bf_tmp set /p "msg="%\n%
set "msg=c.^!next^!=^!msg^! m=%%A: "%\n%
for /l %%N in (^^!m1^^! 1 ^^!m2^^!^) do set "msg=^!msg^!| ^!m.%%N^! "%\n%
set "msg=^!msg^!< ^!m.%%A^! >"%\n%
for /l %%N in (^^!m3^^! 1 ^^!m4^^!^) do set "msg=^!msg^! ^!m.%%N^! |"%\n%
echo ^^!msg^^!%\n%
^) ^"
)
if not defined -s (
set "getCycles=, cycles+=1"
set "getCodePageFaults=, codePageFaults+=1"
set "getArrayStats=, aSize+=1"
set "getArrayPageFaults=, arrayPageFautls+=1"
set showStats=%\n%
^>stats.bf_tmp (echo cycles=^^!cycles^^! codePageFaults=^^!codePageFaults^^!%\n%
echo arraySize=^^!aSize^^! arrayPageFaults=^^!arrayPageFaults^^!^)
if defined -l set getCycles=!getCycles! ^& if ^^!cycles^^! equ !-l! (!showStats!%\n%
exit 0%\n%
^)
)
if defined -c set codePaging=%\n%
if not defined c.%%I if exist c.%%I.bf_tmp (%\n%
if ^^!cdCnt^^! equ !-c! (%\n%
for /f "delims==" %%A in ('set c.') do set "%%A="%\n%
set cdCnt=0%\n%
)%\n%
set /a cdCnt+=1!getCodePageFaults!%\n%
^<c.%%I.bf_tmp set /p "c.%%I="%\n%
)
if defined -a (
set arrayPaging=if not defined m.%%A (%\n%
if ^^!aCnt^^! equ !-a! for /f "tokens=1,2 delims==" %%a in ('set m.'^) do (%\n%
^>%%a.bf_tmp echo %%b%\n%
set %%a=%\n%
set aCnt=0%\n%
^)%\n%
if exist m.%%A.bf_tmp (%\n%
^<m.%%A.bf_tmp set /p m.%%A=%\n%
set /a arrayPageFaults+=1%\n%
^) else set /a m.%%A=0!getArrayStats!%\n%
set /a aCnt+=1%\n%
^)
) else set "arrayPaging=if not defined m.%%A set /a m.%%A=0!getArrayStats!"
:: End macro definition ------------------
:: Create character files if they don't already exist
if not defined -s (
echo ----------------------------
echo Begin Initialization
echo Testing ASCII character files.
)
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 (
if not defined -s (
echo Creating ASCII character files...
set "t1=!time!"
)
call :genAllChr
if not defined -s (
set t2=!time!
%showTime%
)
)
:: Parse the input file
if not defined -i goto :skipInput
if not defined -s (
<nul set /p "=Parsing input."
set t1=!time!
)
if not exist "!-i!" >&2 echo ERROR: Input file "!-i!" not found & exit 1
if exist "!-i!\" >&2 echo ERROR: Input file "!-i!" not found & exit 1
copy 0.bf_chr compare.bf_tmp >nul
set /a size=skipStart=1
for %%F in ("!-i!") do set inSize=%%~zF
for /l %%N in (1 1 32) do if !size! lss !inSize! set /a "size*=2" & type compare.bf_tmp >>compare.bf_tmp
fc /b "!-i!" compare.bf_tmp | findstr /rxc:"........: .. .." >diff.bf_tmp
>input.bf_tmp (
for /f "tokens=1,2 delims=:[] " %%A in (diff.bf_tmp) do (
set /a skipEnd=0x%%A
for /l %%N in (!skipStart! 1 !skipEnd!) do echo 00
echo %%B
set /a skipStart=skipEnd+2
if "!skipStart:~-3!" equ "000" <nul set /p "=." >&2
)
for /l %%N in (!skipStart! 1 !inSize!) do echo 00
)
set "input=<input.bf_tmp"
if not defined -s (
set t2=!time!
echo(
%showTime%
)
:skipInput
:: Parse the code. Consecutive op codes are coalesced into one op with a count.
:: Optionally treats # as an additional show state op code (withoug coalesce).
:: Optimization collapses [-] into a special clear value op.
:: + - < > [ ] [-] are all converted into an S op followed by a SET /A expression.
:: Consecutive + - and [-] are concatenated into one op. If followed by
:: < > [ or ] then that op is also concatenated, but that is the limit for one op.
:: . and , are stored as themselves, followed by the count.
:: # is stored as itself without a count
:: Detects if input is not used so getInput can be removed from interpreter loop.
:: Treats loops at beginning or immedieately following ] as comments (ignores them)
if not defined -s (
<nul set /p "=Parsing code."
set "parseProgress=set /a n=(n+1^^)%%500&if ^!n^! equ 0 <nul set /p "=.""
set "t1=!time!"
)
2>nul del c.*.bf_tmp
for %%C in (+ - "<" ">" . "," #) do set "comment%%~C=0"
set /a i=comment]=-1, sp=count=comment=0, comment[=1
cmd /u /c type %1|find /v ""|findstr "[+\-<>.,[\]!#!]" >src.bf_tmp
for /f %%b in (src.bf_tmp) do ( %parseProgress%
if !comment! gtr 0 (
set /a comment+=!comment%%b!
) else if "!lastOp!" equ "%%b" (
set /a count+=1
) else (
if defined lastOp (
set "test="
if !clr! equ 1 if !lastOp! equ - if !count! equ 1 set /a clr=test=2
if not defined test set clr=0
if "!lastOp!" equ "+" (
set "lastCmd=!lastCmd!%inc%,"
) else if "!lastOp!" equ "-" (
set "lastCmd=!lastCmd!!delim!%dec%,"
) else (
set /a i+=1
if "!lastOp!" equ ">" (
>c.!i!.bf_tmp echo S "!lastCmd!m+=!count!"
) else if "!lastOp!" equ "<" (
>c.!i!.bf_tmp echo S "!lastCmd!m-=!count!"
) else (
if defined lastCmd (
>c.!i!.bf_tmp echo S "!lastCmd:~0,-1!"
set /a i+=1
)
>c.!i!.bf_tmp echo !lastOp! !count!
)
set "lastCmd="
)
set "lastOp="
)
if "%%b" equ "[" (
if defined comment (set /a comment+=1) else (
set /a "i+=1, sp+=1, clr=1"
set /a "stack.!sp!=i"
set "stackCmd.!sp!=!lastCmd!"
set "lastCmd="
)
) else if "%%b" equ "]" for %%s in (!sp!) do (
if !clr! equ 2 (
set "lastCmd=!stackCmd.%%s!m.^!m^!=0,"
set /a "i-=1, sp-=1"
) else (
set /a i+=1, sp-=1, count=i-stack.%%s
>c.!i!.bf_tmp echo S "!lastCmd!^!-^!^!m^!*!count!"
>c.!stack.%%s!.bf_tmp echo S "!stackCmd.%%s!^!+^!^!m^!*!count!"
set "lastCmd="
)
set /a "clr=comment=0"
) %#Parse% else (
set "comment="
set "lastOp=%%b"
set /a "count=1"
if "%%b" equ "," set inputRequired=1
)
)
)
if defined lastOp (
set /a i+=1
(
if "!lastOp!" equ "+" (
echo S "!lastCmd!%inc%"
) else if "!lastOp!" equ "-" (
echo S "!lastCmd!%dec%"
) else if "!lastOp!" equ ">" (
echo S "!lastCmd!m+=!count!"
) else if "!lastOp!" equ "<" (
echo S "!lastCmd!m-=!count!"
) else (
echo S "!lastCmd:~0,-1!"
echo !lastOp! !count!
)
)>c.!i!.bf_tmp
) else if defined lastCmd (
set /a i+=1
>c.!i!.bf_tmp echo S "!lastCmd:~0,-1!"
)
if not defined -s (
set "t2=!time!"
echo(
%showTime%
)
:: Error if unbalanced brackets
if %sp% neq 0 >&2 echo ERROR: Unbalanced brackets&exit /b 1
if defined comment if "%comment%" neq "0" >&2 echo ERROR: Unbalanced brackets&exit /b 1
:: Dump the parsed op codes
if defined -d (for /l %%N in (0 1 !i!) do (
<c.%%N.bf_tmp set /p "msg="
echo c.%%N=!msg!
)) >"!-d!"
:: Disable code paging if code length does not exceed -cp
if not defined -c set "-c=!i!
if !i! leq !-c! (
set "codePaging="
for /l %%N in (0 1 !i!) do <c.%%N.bf_tmp set /p "c.%%N="
)
:: Undefine getInput and/or #Parse if not needed
if not defined inputRequired set "getInput="
if not defined #Found set "#Exec="
:: 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"
:: Establish line one of input routine for this locale
set "line1="
for /f "delims=" %%L in (
'echo .^|xcopy /w "%~f0" "%~f0" 2^>nul'
) do if not defined line1 set "line1=%%L"
set "line1=!line1:~0,-1!"
:: Create final interpreter macro
set bfInterpreter=^
for /f "delims==" %%V in ('set^^^^^|findstr /bv "c\. line1= CR= SUB= o= e= += -="') do set "%%V="%\n%
set /a "i=m=cdCnt=cycles=aCnt=aSize=over=0"%\n%
for /l %%. in () do for %%I in (^^!i^^!) do (!codePaging!%\n%
for /f "tokens=1-3" %%A in ("^!m^! ^!c.%%I^!") do (%\n%
!arrayPaging!!trace!%\n%
if "%%B" equ "S" (%\n%
set /a "%%C"!overError!!leftError!%\n%
) else if "%%B" equ "." for /l %%N in (1 1 %%C) do (%\n%
if "^!m.%%A^!" equ "26" (set /p "=^!SUB^!" ^<nul) else type ^^!m.%%A^^!.bf_chr%\n%
) !getInput!!#Exec!else (!showStats!%\n%
exit 0%\n%
)%\n%
set /a i+=1!getCycles!%\n%
)%\n%
)
:: Print out the finalized brainF*** interpreter macro
if defined -m >"!-m!" echo bfInterpreter=!lf!!bfInterpreter:%%=%%%%!
if not defined -s (
echo Initialization complete
echo ============================
set t1=%time%
)
(call )
if defined -p goto :quit
:: Launch the interpreter
cmd /v:on /c "%~f0" :run&&(call )||(call)
set "err=%errolevel%
if not defined -s (
set t2=%time%
echo(
echo ============================
%showTime%
if exist stats.bf_tmp type stats.bf_tmp
)
:quit
del *.bf_tmp
exit /b %err%
: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
Message size limit reached - version 5.8 is in next post.