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