Nemerle で Brainfuck をインラインで実行


前回は Nemerle で Brainfuck のパーサーを書いて普通に実行しました。今回は Nemerle の強力なマクロ機能を利用して,インラインで Brainfuck のソースコードを実行してみます。

前回のままでも良いのですが,最初に Brainfuck の実行環境である仮想マシンのクラスを作成します。

public class BrainfuckMachine
   invariant 0 <= pointer && pointer < memory.Length
{
   private memory : array[char];
   private mutable pointer : int;

   public this(memorySize = 64 : int)
      requires memorySize > 0
         otherwise throw ArgumentOutOfRangeException("memorySize")
   {
      memory = array (memorySize);
   }

   public Initialize() : void
   {
      Array.Clear(memory, 0, memory.Length);
      pointer = 0;
   }

   public Execute(source : string) : void
   {
      def parser = BrainfuckParser();
      match (parser.Parse(source))
      {
         | option.Some(commands) =>
               foreach (c in commands)
               {
                  Proceed(c);
               }
         | option.None => throw FormatException();
      }
   }

   private Proceed(command : BrainfuckExpression) : void
   {
      match (command)
      {
         | BrainfuckExpression.MoveRight => ++pointer;
         | BrainfuckExpression.MoveLeft => --pointer;
         | BrainfuckExpression.Increment => ++memory[pointer];
         | BrainfuckExpression.Decrement => --memory[pointer];
         | BrainfuckExpression.Get => memory[pointer] = ReadKey(true).KeyChar;
         | BrainfuckExpression.Put => Write(memory[pointer]);
         | BrainfuckExpression.Loop as loop =>
               while (memory[pointer] != 0)
               {
                  foreach (c in loop.Body)
                  {
                     Proceed(c);
                  }
               }
         | _ => throw InvalidEnumArgumentException();
      }
   }
}

このクラスを用いて,マクロを使わずに普通に Brainfuck のソースコードを実行すると次のようになります。

def machine = BrainfuckMachine();
machine.Execute(source);

さて,マクロの登場です。 Nemerle ではマクロを用いて自由に構文を拡張することができます。例えば次のように構文を拡張できます。。

bf(source);
bf[256](source);  // 仮想マシンのメモリーサイズを指定

マクロによる構文の拡張は単純で,次のように記述してやります。

macro BrainfuckInlineExecution(memorySize, source)
syntax ("bf", Optional("[", memorySize, "]"), "(", source, ")")
{
   def memorySize = if (memorySize == null) <[ 64 ]> else memorySize;
   <[
      def machine = BrainfuckMachine($memorySize);
      machine.Execute($source);
   ]>
}

syntax で構文のマクロを作成することができます。 Optional で指定した構文は省略可能になります。ここでは Brainfuck を実行する仮想マシンのメモリーサイズを省略できるようになっています。

Nemerle だと,本当に簡単に言語内言語が実装できてしまうのですね。

ソースコード

ソースコードを Bitbucket にアップロードしています