前回は 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 だと,本当に簡単に言語内言語が実装できてしまうのですね。