DLR で俺言語を作ってみる講座:第2回目

id:SiroKuro:20070922:1190471726 の続きです。

Sample_Skeleton の構成について

今回の本題に入る前に、Sample_Skeleton.lzh のクラス構成について書いておきます。

Sample.Program エントリクラス。public static void Main
Sample.SampleLanguageContext 言語共通のコンテキストを格納するクラス
Sample.SampleUtils ごった煮
Sample.Actions.SampleActionBinder アクションを抽象構文木に関連付けるクラス。今はまだデフォルト
Sample.Hosting.SampleConsoleHost コンソールのホスト
Sample.Hosting.SampleEngine Sample 言語のエンジン本体
Sample.Hosting.SampleLanguageProvider LanguageProvider。SampleEngine を生成したり
Sample.Shell.SampleCommandLine コンソールのコマンドライン関係
Sample.Shell.SampleConsoleOptions Console 関連の設定
Sample.Shell.SampleEngineOptions Engine 関連の設定
Sample.Shell.OptionsParser コマンドライン引数をパースするクラス

これらの生成関係を見てみるとこんな感じに。


└ Sample.Program
  └ Sample.Hosting.ConsoleHost
    └ Sample.Hosting.SampleLanguageProvider
      ├ Sample.Shell.OptionsParser
      | ├ Sample.Shell.SampleConsoleOptions
      | └ Sample.Shell.SampleEngineOptions
      ├ Sample.SampleLanguageContext
      └ Sample.Hosting.SampleEngine
        └ Sample.Actions.ActionBuilder

ConsoleHost が LanguageProvider を生成、引数をパースして LanguageContext を生成、そして Engine を作るといった感じです。一応これらのコードでコマンドラインインタプリタが動作します。

パース処理の詳細

さて、実際に抽象構文木を作成している Sample.SampleLanguageContext#ParseSourceCode の詳細を見てみます。

public override CodeBlock ParseSourceCode(CompilerContext context)
{
	CodeBlock code = Ast.CodeBlock("__top__");
	code.Body = Ast.Statement(
		Ast.Call(null, typeof(SampleUtils).GetMethod("WriteLine"), 
			Ast.Constant(context.SourceUnit.GetReader().ReadToEnd())));
	return code;
}

ソースが入力されるとこのメソッドが呼び出されます。入力されたソースは context.SourceUnit.GetReader() から TextReader として取得できます。


Ast.Constant(object obj) メソッドは、obj を定数として保持する AST を作成するメソッドです。今回は入力されたソースを引数に渡すことで、その文字列を値として持つ ConstantExpression が生成され返されます。

Ast.Call(Expression inst, MethodInfo method, params Expression[] args) メソッドは、method で表されるメソッドを呼び出す AST を作成するメソッドです。今回は public static void SampleUtils.WriteLine(object obj) を呼び出すために、インスタンスは null、args は Ast.Constant で生成された値を引数に CallExpression を生成しています。


この AST によって実行されるコードは、C# で書くならば次のような感じです。

SampleUtils.WriteLine("{入力されたソース}");

実行するとこんな感じに。

さんぷるげんごー!
sample> abcdefg
abcdefg

sample>

ちゃんとエコーされているのが分かるかと思います。

余談

言い忘れてました。Sample_Skeleton.lzh の中に Microsoft.Scripting.dll は入ってません。Python2.0Alpha4 の中から拾って使っていただければ幸いです。



どんな抽象構文木がクラス Ast から生成できるのか、詳細はまた次回の講釈で。