このページでは、HQ9+をもとにして難解プログラミング言語を自作します。
HQ9+は、命令が4つしかない難解プログラミング言語です。
たったこれだけです。実装も非常に簡単なので、数多くの派生言語が作られています。
例えば...
FizzBuzzを追加した「HQ9F+」。
HとQと9がなくなってしまった「+」。
アキュムレータが二次元配列になった「HQ9+2D」。
Deadfishという別の言語とくっつけられた「FISHQ9+」。
などなど。
HQ9+が何者か学んだので、計画を立てましょう。
アキュムレータはuint32ビットの符号なし整数型。0～4,294,967,295までの整数を扱えます。型(なのでEAXアセンブラにおける変数のようなものの一つ。サイズは32ビットと決まっています。ほかにもEBXやECXなどがあります。です)。
命令以外の文字はそのまま出力。
ここに書かれていないものはすべてHQ9+の仕様に従う。
命令は以下の通りです。
E命令でスローされる例外は以下の通りです。
計画ができました。作りましょう。
これは非常に簡単で、変数を必要な分用意するだけで終わります。
uint accumulator = 0;    // アキュムレータ
Random rnd = new Random();
前のページで作ったBrainfuck派生言語のソースコードを流用することにしましょう。
これはただ地道にSwitch文を書くだけですが、非常に重要です。がんばりましょう。
switch (cmd[i])
{
    case 'H':
        if (rnd.Next(0, 2) == 0)    // 一文のみの場合、かっこをつけなくてよい。
            Console.WriteLine("Good afternoon!");
        else
            Console.WriteLine("Hello, world!");
        break;
    case 'Q':
        Console.WriteLine(cmd);
        break;
    case 'E':
        switch (accumulator)
        {
            case 1:
                throw new Exception();  // 例外を起こすときなど、必ず終わる場合break文は必要ない。
            case 2:
                throw new ArgumentNullException();
            case 3:
                throw new System.IO.IOException();
            case 4:
                throw new MissingMemberException();
            case 5:
                throw new EntryPointNotFoundException();
            default:
                throw new Exception();
        }
    case '+':
        accumulator++;
        break;
    default:
        Console.Write(cmd[i]);
        break;
}
例外をスローするためにはthrow文を使います。
だいたい二分の一でメッセージが表示されます。
コード:
HH
出力例:
Hello, world!
Good afternoon!
入力をそのまま表示します。
コード:
Q
出力:
Q
全部のソースコードです。
using System;
namespace not_HQ9plus
{
    class Program
    {
        static void Main()
        {
            while (true)
            {
                uint accumulator = 0;   // アキュムレータ
                Random rnd = new Random();
                Console.Write("> ");
                string cmd = Console.ReadLine();
                for (int i = 0; i < cmd.Length; i++)
                {
                    switch (cmd[i])
                    {
                        case 'H':
                            if (rnd.Next(0, 2) == 0)    // 一文のみの場合、かっこをつけなくてよい。
                                Console.WriteLine("Good afternoon!");
                            else
                                Console.WriteLine("Hello, world!");
                            break;
                        case 'Q':
                            Console.WriteLine(cmd);
                            break;
                        case 'E':
                            switch (accumulator)
                            {
                                case 1:
                                    throw new Exception();  // 例外を起こすときなど、必ず終わる場合break文は必要ない。
                                case 2:
                                    throw new ArgumentNullException();
                                case 3:
                                    throw new System.IO.IOException();
                                case 4:
                                    throw new MissingMemberException();
                                case 5:
                                    throw new EntryPointNotFoundException();
                                default:
                                    throw new Exception();
                            }
                        case '+':
                            accumulator++;
                            break;
                        default:
                            Console.Write(cmd[i]);
                            break;
                    }
                }
                Console.WriteLine();
            }
        }
    }
}
上記のスクリプトのアキュムレータの初期値を10000に設定し、+命令の動作をデクリメントに書き換えましょう。
前は「難解プログラミング言語の作り方/Brainfuckをもとにする」です。
次は「難解プログラミング言語の作り方/Deadfishをもとにする」です。