だからBrainfuck。 要するに、アイデアは、N個のレジスタ/セルがあるということです。 プログラマはそれらすべてにアクセスできますが、それらを移動することは明示的に行われます。 つまり セル2からすぐにセル7に移動することは不可能であるため、順次必要です。
言語の「キーワード」:
- >-右側のセルに移動します。
- <-左のセルに移動します。
- +-セルの値を1つ増やします。
- --セル値を1つ減らします。
- 、-標準入力デバイスからセルに値を読み込みます。
- 。 -標準出力デバイスでセル値を出力します。
- [-現在のセルの値が0でない場合はwhileループを開始し、次のセルに移動します。
- ] whileブロックの終わりです。 「条件付き」セルの値が0でない場合は、サイクルを続行します(「条件付きセル」はサイクルが開始されたセルです)。
「キーワード」を追加しました:
- $-数値としてセルに値を読み取ります(> ANCII文字として読み取りとして再定義)
- ! -番号として印刷
- {-関数の始まり、関数の始まりの後に、関数の名前があります(名前は、文字%<関数名>%の間の任意の文字のシーケンスにすることができます。任意の関数に対してセルのコピーが作成され、戻り値は呼び出しブロックの現在のレジスタに書き込まれます
- }-関数の終わり
- (-コメントの開始
- )-コメントの終わり
- @%<関数名>%-関数呼び出し
- ^-セルをリセット
キーワードのセット全体がANCII文字で構成されているため、次のものがあります。
//
const char bf_next = '>';
const char bf_prev = '<';
const char bf_incr = '+';
const char bf_decr = '-';
const char bf_prnt = '.';
const char bf_read = ',';
const char bf_wBeg = '[';
const char bf_wEnd = ']';
//
const char bf_pNum = '!' ;
const char bf_rNum = '$';
const char bf_fBeg = '{';
const char bf_fEnd = '}';
const char bf_fNme = '%';
const char bf_comm= '(';
const char bf_call = '@';
const char bf_null = '^';
一般性を失うことなく、限られた数のセル、たとえば256を使用します。無効なセルに移動しようとすると、最初のセル(遷移が左の場合)または最後(右への遷移の場合)に移動します。
追加:
const unsigned long regSize = 256; //
long reg[ regSize ]; //
long stck[ regSize ]; // ,
void resetRegisters(); //
void printRegistres(); //
ここで、入力ファイルとしてtest.bfがあり、私の言語またはネイティブのBrainfuckのコードが含まれているとします。 インタープリターは「後方互換性」を提供する必要があります。
繰り返しますが、一般性を失わずに、すべてのコードを制限された配列に保存できます。 つまり インタープリターはサイズが制限されたファイルで動作します。これを例に挙げましょう。
const unsigned long maxCodeSize = 1024000; /* */
unsigned long realCodeSize; // realCodeSize < maxCodeSize
char code[maxCodeSize]; //
インタープリターはすべてのコードを一度に読み取ります。 1つの文字配列では、このためにreadCode()関数を使用します。 空でないテキストを読み取った後、m_realCodeSizeにはコード内の正確な文字数が含まれます(コメントを除く)。読み取り中にコメントは破棄されます。
int main(int argc、char ** argv)
{
ようこそ();
resetRegisters();
readCode(“ test.bf”);
ループ(0、realCodeSize-1、regSize、reg);
0を返します。
}
次に、whileループとスタックをコピーして実際に関数を実行するための関数をいくつか定義します。
bool loop( unsigned long from,
unsigned long to,
unsigned long condRegIndx,
unsigned long currReg,
long* registers );
bool runFunction( unsigned long from,
unsigned int to,
unsigned int& retValue);
void copyRegistersTo( long* source, long* destination );
最初のループはループを実行し、ループが問題なく完了した場合にtrueを返します。 構文エラーはありません。
2番目の関数は実際に関数を実行し、戻り値はretValに書き込まれます。retValは、関数が呼び出されたレジスタに割り当てられます。 戻り値は、終了後の関数スタックの最初のレジスタと見なされます。
whileループといえば、一般的な場合、ループは無期限に継続できます。 ただし、インタープリターのフリーズの問題に遭遇しないように、最大サイクル数を担当する変数を導入します。
const unsigned long maxLoopCycles = 999999;
まず、後方互換性を実装します。 インタープリターが今のところBrainfuckコードのみを実行できるようにします。
機能が必要になります。
bool makeCommand( char command, long* registers, unsigned long currReg )
unsigned long findLoopEnd( const unsigned long from )
最初の関数の2番目と3番目のパラメーターが必要です。 3番目のパラメーターは、操作するセルをナビゲートするために必要です。2番目は、各関数のレジスタが異なり、それらに対する操作が同じであるため、2番目が必要です。
2番目の関数は、名前に基づいて、サイクルの終わりを見つけます。 対応する文字は「[」です。
したがって、Brainfuck言語のインタープリターがあります。
インタープリターのソースコードとテストコードをレコードに添付しました
$[+<->]<<$>!<>>++++[++++++++++<->]<+++.++++++++++++++++++<<<!>[<-<+>>]>>.<<<!
上記のコードに対して、私のインタープリターは、a + b = cの形式で入力された2つの数値の合計を出力します。
幸運...プログラミング!
PS
興味があれば、あとでどのように実装したかを後で説明します。