DLRのASTのみを使う

簡単な言語を作るシリーズでNyaRuRuさんがDLRのASTだけを使った方法というのを解説していると取り上げました。DLRのソースコードを使わないで、DLRのASTだけを扱う方法を以下に示します。

 using System;
using System.Collections.Generic;

using Microsoft.Scripting;
using Microsoft.Scripting.Ast;

namespace AstTest
{
  delegate void MyFunc();

  class Program
  {
    static void Main(string[] args)
    {
       var helper = typeof(Console).GetMethod("WriteLine", 
                    new[] {typeof(string)});
       // コードブロックが、関数名の一種と考えれば良い
       CodeBlock cb = Ast.CodeBlock("MyFunc", typeof(void));
       cb.Body =
          Ast.Block(
              Ast.Call(helper, Ast.Constant("Hello, World")),
              Ast.Call(helper, Ast.Constant("Hello, World")),
              Ast.Call(helper, Ast.Constant("Hello, World"))
            );
       // 型パラメータを作成したコードブロックに一致させる
       MyFunc myFunc = TreeCompiler.CompileBlock<myfunc>(cb);
       myFunc();

       Console.ReadKey();
    }
  }
}

このプログラムにMicrosft.Scripting.dllへの参照を追加して実行すれば、「Hello, World」の文字列が3回コンソールに出力されます。このコードが何をしているかと云えば、以下のようなことです。

  • CodeBlock定義:一種の関数定義と思っていただければ良いと思います。第一引数にブロック(関数)名、第二引数に戻り値の型を指定します。
  • Body定義:何を実行するかを定義します。この場合で云えば、Console.WriteLineメソッド呼び出しに、Hello, Worldという定数を引き渡します。
  • TreeCompiler:Microsoft.Scripting.Ast.TreeCompilerクラスのComipleBlockメソッドでコードブロックをコンパイルして、結果をデリゲートインスタンスとして返します。ここで指定している型パラメータは、作成したコードブロック(関数のようなもの)に合わせます。
  • 作成したデリゲートを呼び出します。

これで作成したDLRのASTが無事に実行されるのです。この例では、戻り値の無いMyFuncというコードブロックを作成するために、「delegate void MyFunc()」というデリゲートを定義しています。戻り値や引数がある場合は、Funcデリゲートを流用しても良いでしょう。

 この例以外に引数を取るブロックやループなどが出来るのは、NyaRuRuさんのエントリーの通りです。他の言語を使用するまでも無く、ちょっとしたロジックを自分でASTを組み立てて実行したい時などに利用することができるでしょう。