
Brainfuck方言の仕様
Windowsコマンドプロセッサには多くの制限があります。 それらのほんの一部を次に示します。
- 文字 " < "および " > "はI / Oのリダイレクトに使用されるため、これらをブレインファックコマンドとして使用すると、コードが不必要に複雑になります。 したがって、それらを同様のスペル「 ( 」および「」」に置き換えます。
- テキスト出力とキーボード入力のオプションは制限されています。
最後に、言語の仕様を取得します。
- メモリはリングバッファを表します。
- 出力はコードページの最初の127文字に制限されていますが、印刷できない文字と、バッチファイルが機能しているときにエラーを発生させた文字は例外です。 後者は記号「 。 」に置き換えられています。 スペースは「 _ 」記号に置き換えられました。
- 言語の「キーワード」:
- 「)」 -次のメモリセルに移動します
- 「(」 -前のメモリ位置に移動
- 「+」 -セルの値を1つ増やします
- 「-」 -セル値を1つ減らす
- 「、」 -標準入力デバイスからセルに値を読み込みます。 現在実装されていません
- 「。」 -標準出力デバイスにセル値を出力します
- 「[」 -サイクルを開始します。 現在のセルの値が0でない場合、次のコマンドに進みます。 それ以外の場合は、次へ]ネストを検討する
- 「]」はサイクルの終わりです。 現在のセルの値が0でない場合、ループを続行します
バッチファイルでの配列の実装の作成者に感謝したいと思います。 コードがなければ、Brainfuckインタープリターを書くのは難しいでしょう。
コードを最適化するためのヒントについては、 Royの habrayuzerも同様です。
通訳者の説明
通訳の全文はこちら: http : //pastebin.com/KZXUuz48
プログラムは次のグローバル変数を使用します。
- mp(メモリポインタ) -現在のメモリセルへのポインタ
- cp(コマンドポインター) -現在のコマンドへのポインター
変数が初期化され、「メモリ」がゼロで満たされた後、プログラムファイルを読み取ります。 読み取りのプロセスでは、複数行のプログラムが1行に接着されます。 その後、Brainfuckプログラムの実行中、すべての非辞書文字は無視されます。 *から行末までのすべての文字は無視されます。 これにより、「美しく」設計されたプログラムを作成できるようになります。 プログラム例:
+++++++++[ * while mem[0]<=9 { )+++++ * mem[1]+=5 (-] * } ). * print chr(mem[1])
上記のすべてのアクションは、バッチファイルの2行だけで実行されます
set bf_prog= FOR /F "eol=c delims=*" %%I IN (%work_file%) DO SET bf_prog=!bf_prog!%%I
希望する人は、プログラムからすべての非辞書文字の削除を追加できます。
次に、インタープリターのメインサイクルが開始されます。 ここのすべては非常に透明です。
cpポインターで操作コードを読み取ります。 次に、古典的なif ... elseを使用して、対応するサブルーチンに進みます。 文字が「言語辞書」に含まれていない場合、無視されます。 次に、 cpカウンターを増やして、ループの先頭に移動します。
:work set cop=!bf_prog:~%cp%,1! if "%cop%" == "+" ( call :array get mem %mp% tmp set /a tmp += 1 if "!tmp!" == "256" set tmp=0 call :array set mem %mp% !tmp! ) else if "%cop%" == "-" ( call :array get mem %mp% tmp set /a tmp -= 1 if !tmp! LSS 0 set tmp=255 call :array set mem %mp% !tmp! ) else if "%cop%" == ")" ( set /a mp +=1 if !mp! == %maxmem% set mp=0 ) else if "%cop%" == "(" ( set /a mp -=1 if !mp! == -1 set /a mp=%maxmem% - 1 ) else if "%cop%" == "," ( call :comma ) else if "%cop%" == "." ( call :array get mem %mp% tmp call :Echochr !tmp! ) else if "%cop%" == "[" ( call :array get mem %mp% tmp if "!tmp!" == "0" ( call :skip1 ) ) else if "%cop%" == "]" ( call :array get mem %mp% tmp if "!tmp!" NEQ "0" ( call :skip2 ) ) set /a cp += 1 if %cp% == %bf_len% goto :exit goto :work
いくつかの興味深いソリューションの説明
chr()関数をエミュレートするには、 char変数の文字を、その文字のコードに対応する位置から読み取る必要があります。
:echochr rem . , cmd "." set char=".#$...'()*+,-./0123456789.....?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]._`abcdefghijklmnopqrstuvwxyz{.}~_" if %1==10 echo. if %1==13 echo. if %1==32 <nul set /p strTemp="_" if %1 GTR 32 ( set /a code=%1 - 32 for /f %%t in ('cmd /c "echo %%char:~!code!,1%%"') do <nul set /p strTemp="%%t" ) exit /b 0
シンボルを直接カットすることはできません。 つまり 建設
set t=%char:~%code%,1%
どちらか set t=%char:~!code!,1%
エラーが発生します。 問題は、文字列処理関数が変数としてではなく数値としての指示を必要とすることです。 例: set t =%char:〜5.1%は 、位置5から始まる行1から1文字をカットします。 ただし、変数の内容(この場合はcode )をsetコマンドに直接渡すことはできません。 これを回避するには、ランタイムバインディングを使用します。 これはコマンドの場合にのみ機能します。したがって、コマンドecho %% char:〜!Code !, 1 %% ( コードの内容は既に数字として送信されている)でシェルのコピーを呼び出し、 「for / f」構成で出力をインターセプトする必要があります。
まだデザインがあります
set t=!char:~%code%,1!
、しかしこの場合は正しく動作しません。 なぜなら 出力操作がそれほど多くないため、プログラムの開発とデバッグの速度のために実行速度を犠牲にすることが決定されました。
この関数(:echochr)は、別の興味深いソリューションを使用します。改行なしでテキストを出力します。
echoコマンドは、出力時に常に改行を追加します。 この場合、これは適切ではありません。 幸いなことに、この制限を回避する方法があります。
<nul set /p strTemp="%~1"
つまり、 set / pコマンドでnulデバイスからの入力を使用して、 echoコマンドをエミュレートします。
このソリューションは、フォーラムで見ることができるいくつかのより興味深い機能を提供できます。リンクは以下にあります。
おわりに
++++[)++++++++)++++++++++++++++++)++++++++++++++++) ++++++++++++++++++++)+++++++++++++++++++++++++(((((-] ))+.(.))))++++++++.+++.+++++++.-----------------. ((((.)-.)+.+.)++.(-.(.).+.).
警告:
コンピューターのパフォーマンスに関係なく、すべてが非常に遅く動作します。 マイクロソフトは、低速のコンピューターの所有者が高速のコンピューターの所有者をen望しないようにしています。
コードは、Windows XP、Windows Vista、Windows 7でテストされました。
参照資料
- 配列の実装の著者の投稿: http : //habrahabr.ru/blogs/crazydev/75951/
- いくつかのアイデアが発見されたフォーラム: http : //forum.script-coding.com/viewforum.php?id=12
- ここでは、バッチファイルで使用されるソリューションの機能について説明します。http : //rsdn.ru/article/winshell/NTCommandProcessor.xml
batファイルの速度の最適化について
私の意見では、別の記事に十分な資料がないので、ここに投稿します。 なぜなら、それはここに示されている通訳者の速度を上げることに直接関係しているからです。
インタープリターでの作業の過程で、cmd子プロセスの一定の生成が確認されました。 そして、全体的な速度は小さいです。 呼び出し関数を呼び出すことがすべてです。 ヘルプを読む:
CALL .
:
CALL :
, ,
.
. ,
CALL, .
GOTO /? GOTO :EOF,
.
したがって、callを介してサブルーチンを呼び出すたびにcmdが起動されることに注意してください。これには時間がかかります。
同じことを行う簡単なテスト。 ただし、最初の場合は直接、2番目の場合はサブルーチン呼び出しを使用します。
@echo off setlocal ENABLEDELAYEDEXPANSION ENABLEEXTENSIONS echo %time% - start test1 set a=0 for /l %%i in (1,1,1000) do set /aa=!a!+1 echo %a% echo %time% - end test1 echo -------------- echo %time% - start test2 set a=0 for /l %%i in (1,1,1000) do call :m1 echo %a% echo %time% - end test2 goto :eof :m1 set /aa=%a%+1 exit /b /0
彼らが言うように、コメントは不要です。
そのため、ルーチンを介した配列の実装の使用を拒否し、インタープリターコードを書き直しました。 その結果、速度は15倍以上増加しました!
実際には、最適化されたインタープリター自体http://pastebin.com/MDbVJXsE