難解プログラミング言語の作り方/Brainfuckをもとにする
このページでは、有名な難解プログラミング言語であるw:Brainfuckをもとにして新たな難解プログラミング言語を作成する手順を紹介します。
言語の設計
[編集]まず最初にするべきことは、どのような言語をつくるのか、計画を立てておかなければなりません。 行き当たりばったりでも何とかなるとは思いますが、やはり計画があったほうがやりやすいでしょう。
このページで作成する言語は、以下のような言語とします。できる限りC#初心者でも実装できるよう、単純にしました。
- 命令は、
+
、-
、>
、<
、.
、,
、?
、!
とする。[1] - メモリはchar型、セルは256個。ただし、メモリの両端はつながっていないものとする。
- byte型の数値1つをポインターとして現在のメモリセルの位置を指し示す。
- ここに書いていないものはすべてBrainfuckの仕様に従う。
命令の詳細は以下に示します。
命令 | 意味 | 備考 |
---|---|---|
+ |
Brainfuckに存在する。現在のセルをインクリメントする。 | 65535を超えた場合、オーバーフローする。 |
- |
Brainfuckに存在する。現在のセルをデクリメントする。 | 0を下回った場合、負にオーバーフローする。 |
> |
Brainfuckに存在する。ポインターをインクリメントし、セルを移動する。 | これによりポインターが65535を超えた状態でメモリを操作した場合例外が発生する。 |
< |
Brainfuckに存在する。ポインターをデクリメントし、セルを移動する。 | これによりポインターが0を下回った状態でメモリを操作した場合例外が発生する。 |
. |
Brainfuckに存在する。現在のセルを文字コードとして出力する。 | |
, |
Brainfuckに存在する。1文字入力を受け付け、現在のセルに文字コードを代入する。 | |
? |
Brainfuckに存在しない。現在のセルに乱数を代入する。 | 0~65535 |
! |
Brainfuckに存在しない。現在のセルの数値を出力する。 |
実装
[編集]計画が出来上がったので、さっそく作っていきましょう。
メモリ・ポインタの実装
[編集]これはただ単に変数を作っているだけです。同じ動作のために毎回違うコードを入力するのは面倒なので、インタプリタのループに入れてしまうことにします。
char[] memory = new char[256];
byte ptr = 0;
インタプリタの作成
[編集]無限ループを作り、その中で入力を受け付ける文を書いています。そのあと、命令を実行します。
while (true)
{
char[] memory = new char[256];
byte ptr = 0;
Random rnd = new Random(); // ?命令で使います。
Console.Write("> ");
string cmd = Console.ReadLine();
for(int i = 0;i <= cmd.Length; i++)
{
// TODO: ここに命令を追加します。
}
Console.WriteLine();
}
?命令で使う、Random
クラスは、ハローワールドには(おそらく)出てきません。
Random
クラスは、乱数を生成するためのクラスです。変数にしておかないと利用できません。
よくわからない方は調べて下さい。
命令の実装
[編集]ここが最も肝心です。ただ、単純にswitch文で分岐しているだけなので特に難しいところはありません。
switch (cmd[i])
{
case '+':
memory[ptr]++;
break;
case '-':
memory[ptr]--;
break;
case '>':
ptr++;
break;
case '<':
ptr--;
break;
case '.':
Console.Write(memory[ptr]);
break;
case ',':
memory[ptr] = Console.ReadKey().KeyChar;
break;
case '?':
memory[ptr] = (char)rnd.Next(0, 65536); // 65536 = 65535 + 1
break;
case '!':
Console.WriteLine(memory[ptr].ToString());
break;
}
テスト
[編集]これらのコードを一つのソースコードにまとめて、うまくできるかためしてみましょう。 テストのコードをいくつか用意してあります。使ってみましょう。 うまくいかない場合はソースコードを見直してみましょう。それでもわからない場合は、全体図を写しましょう。
インクリメント・デクリメント・ポインタのテスト
[編集]ポインタを使って文字を出力しています。
コード:
++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ +++++ . >
++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ ++++++++++ +++++++-- . < .
出力:
AAA
入出力のテスト(cat)
[編集]入力を求められ、入力した文字をそのまま表示します。
コード:
,.
入力例:
R
出力例:
R
乱数・数値出力のテスト
[編集]0~65535の間の乱数が出力されます。
コード:
?!
出力例:
157
全体図
[編集]すべてのソースコードをのせます。これをコンパイルすればインタプリタが動作します。
using System;
namespace not_brainfuck
{
class Program
{
static void Main(string[] args)
{
while (true)
{
char[] memory = new char[256];
byte ptr = 0;
Random rnd = new Random();
Console.Write("> ");
string cmd = Console.ReadLine();
for(int i = 0;i < cmd.Length; i++)
{
switch (cmd[i])
{
case '+':
memory[ptr]++;
break;
case '-':
memory[ptr]--;
break;
case '>':
ptr++;
break;
case '<':
ptr--;
break;
case '.':
Console.Write(memory[ptr]);
break;
case ',':
memory[ptr] = Console.ReadKey().KeyChar;
break;
case '?':
memory[ptr] = (char)rnd.Next(0, 65535);
break;
case '!':
Console.WriteLine(((int)memory[ptr]).ToString());
break;
}
}
Console.WriteLine();
}
}
}
}
脚注
[編集]- ^ Brainfuckには、
[
と]
というループの命令がありましたが、実装が大変なので省きました。
次は「難解プログラミング言語の作り方/HQ9+をもとにする」です。