難解プログラミング言語の作り方/HQ9+をもとにする

出典: フリー教科書『ウィキブックス(Wikibooks)』

このページでは、HQ9+をもとにして難解プログラミング言語を自作します。

HQ9+って何ですか[編集]

HQ9+は、命令が4つしかない難解プログラミング言語です。

HQ9+の命令セット
命令 動作
H Hello, world!を表示
Q クワイン[1]
9 99 bottles of beer
+ アキュムレータ(変数のようなもの)をインクリメント

たったこれだけです。実装も非常に簡単なので、数多くの派生言語が作られています。 例えば...

  • FizzBuzzを追加した「HQ9F+」。
  • HとQと9がなくなってしまった「+」。
  • アキュムレータが二次元配列になった「HQ9+2D」。
  • Deadfishという別の言語とくっつけられた「FISHQ9+」。

などなど。

計画を立てましょう[編集]

HQ9+が何者か学んだので、計画を立てましょう。

  • アキュムレータはuint[2]型(なのでEAX[3]です)。
  • 命令以外の文字はそのまま出力。
  • ここに書かれていないものはすべてHQ9+の仕様に従う。

命令は以下の通りです。

命令
命令 意味
H ランダムでHello, world!Good afternoon!を表示。
Q クワイン。
E 例外(アキュムレータに応じて変わる。下の表を参照)をスローする。
+ アキュムレータをインクリメント。

E命令でスローされる例外は以下の通りです。

例外
アキュムレータ 例外の名前 用途
1 Exception 例外全般。
2 ArgumentNullException nullを渡してはいけない関数にnullを渡した場合にスローされる。
3 IOException I/Oエラーが発生した際にスローされる。
4 MissingMemberException 存在しないメンバーに動的[4]にアクセスしようとした場合にスローされる。
5 EntryPointNotFoundException エントリポイント[5]が存在しない場合にスローされる。
それ以外 Exception 例外全般。

実装[編集]

計画ができました。作りましょう。

変数の実装[編集]

これは非常に簡単で、変数を必要な分用意するだけで終わります。

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文を使います。

テスト[編集]

H命令[編集]

だいたい二分の一でメッセージが表示されます。

コード: HH

出力例: Hello, world! Good afternoon!

Q命令[編集]

入力をそのまま表示します。

コード: 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();
            }
        }
    }
}

演習問題[編集]

  1. 上記のスクリプトのアキュムレータの初期値を10000に設定し、+命令の動作をデクリメントに書き換えましょう。

脚注[編集]

  1. ^ ソースコードをそのまま出力することです。ほとんどのプログラミング言語では実装が非常に大変ですが、もともとそれを簡単にするために設計されていたりするのでこれだけでおわります。
  2. ^ 32ビットの符号なし整数型。0~4,294,967,295までの整数を扱えます。
  3. ^ アセンブラにおける変数のようなものの一つ。サイズは32ビットと決まっています。ほかにもEBXやECXなどがあります。
  4. ^ コンパイルなど実行前に決めておかず、実行時にその都度決めておくこと。
  5. ^ 最初に実行される関数のこと。Main()など。


前は「難解プログラミング言語の作り方/Brainfuckをもとにする」です。

次は「難解プログラミング言語の作り方/Deadfishをもとにする」です。