kazuk は null に触れてしまった

C# / .NET 系技術ネタ縛りでお送りしております

an Quick Start for Roslyn (1)


というわけで、プロジェクトテンプレートに並行して Roslyn を触り始めてみました。

Roslyn が何であるかについては ufcpp さんが既に書かれてた記事 があるのでそちらを見てもらうとして、Compiler 内部のコンポーネントをユーザーが利用することができる物という認識で良いと思います。追加してシンタックスツリーを作るとそれのテキスト化(ソースコード化)等、いわゆる言語サービスに類するもの、これまでは Visual Studio と C# コンパイラーの内部でしか利用できなかった物が比較的簡単に利用できるようになっています。

自分としてはこれは言語内DSL等に使える他結構色々と使いでのあるツールなのですが、何しろコンパイラーという巨大な仕組みの内部が公開されたという事は大量のクラス、メソッドが表に出てきたわけで見るのも大変である事は事実で、とっかかりを得るのは結構苦労しそうだなと。根が優しい人なので、この辺のとっかかりについて書いておく事で後の人にもっと奥まで進んで貰えるかな等というわけで書いてみます。

disclaimer

この記事は Roslyn の製品版が出る以前に書かれていますので、RTM版では一部の手順や実装が異なる事が十分にあり得ます。blog記事なんて生ものだと思ってる人なので追補とかする気は全然ありませんので、遠い未来から突っ込みされても困ります。「あー、困った困った。」で流しちゃうと思いますけどご了承ください。

インストール

2012/06/16 現在で最新の Roslyn は以下からダウンロードできます。(2012/6/5 リリースのものです)

Microsoft “Roslyn” CTP 

普通にインストールすれば入ると思います。

インストールすると Roslyn のプロジェクトテンプレートが利用可能になります。

image

含まれているプロジェクトテンプレート

Console Application は普通のコンソールアプリケーションですが、それ以降は vsixmanifest が含まれている事からお察しの通り(お察しできないかもしれませんが)、Visual Studio 拡張のプロジェクトです。

以下プロジェクト作成直後のソリューションエクスプローラーの表示をペタペタと。

imageimageimageimageimage

まぁとっかかりとしての選択肢は Console Application 一択でしょう。 VS拡張を実装するのは追加の知識が大量に必要になってしまうので別個の知識として身に着けてからでないと必要知識の山の前に簡単に遭難できますので。

コンパイラーパイプライン

最初に Roslyn をダウンロードしたページの「Related resources」にある Microsoft “Roslyn” Project Overview› を見ると最初の方にある図を引用させてもらいます。これを読みながら一緒に見てもらうとわかりやすいかも。

CompilerPipeline

コンパイラーの内部処理は Parser で文字列なソースから構文木への変換がかかり、該当ソースで定義された要素が Symbols 、参照しているアセンブリが Metadata Importで取り込まれ、Binder で構文木から実際のコードに相当するモデルが構築され、IL Emitter がそれをPEアセンブリとして書き出すわけで、この各段階と各段階をまたぐデータ構造(構文木、シンボルストア…etc)が Roslyn で API 化されています。

Parserの呼び出しと簡易的なシンタックスツリーの表示

Parser には SyntaxTree の公開するスタティックメソッド、ParseCompilationUnit を介してアクセスします。構文木をスタックに積みながら要素の種別とテキストでの内容を表示するサンプルを以下に示します。

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using Roslyn.Compilers; using Roslyn.Compilers.CSharp; using Roslyn.Services; using Roslyn.Services.CSharp; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var tree = SyntaxTree.ParseCompilationUnit( @"using System; namespace RoslynTest { public class Program { public static void Main( string[] args ) { Console.WriteLine( Console.ReadLine() ); } } } "); stopWatch.Stop(); Console.WriteLine(stopWatch.Elapsed); Console.ReadLine(); Stack<SyntaxNode> nodes = new Stack<SyntaxNode>(); SyntaxNode node; tree.TryGetRoot(out node); nodes.Push( node ); while (nodes.Count != 0) { node = nodes.Pop(); Console.WriteLine(node.Kind); Console.WriteLine(node.GetText()); foreach (var child in node.ChildNodes()) { nodes.Push(child); } } Console.ReadLine(); } } }

 

表示としては以下の様になりました。

image

パースに21ms ぐらい要してる様に見えますが初回実行コスト(Roslyn のアセンブリの遅延ロードやJITコンパイル)が含まれていてこの速度は十分に早いと言っていいと思います。

IEnumerable<SyntaxNode> の foreach だけで実装できている事からわかる通りで簡単に LINQ の餌食になりそうに見えます。ただし、LINQ は再帰的なツリー構造に必ずしも強くないので多少の補完ライブラリが必要でしょうかね。

(1)のまとめ

内容としては Roslyn の ParseCompilationUnit を回して構文木を得た、軽く内容を表示してみて解析ができていることを確認した段階です。

少なくともコードは完全にSyntaxTreeに分解されていますので、既存コードに含まれがちな foreach での多段ネストループを検出するとかはこれで十分にできます。「コード的にはバグじゃないけどこのコードの実装者の頭ちとおかしいんじゃない?」を検出する検出器は検出条件が構文的に決まってしまえばこの段階で作れますのでぜひ作ってみてください。

アセンブリを参照設定していれば使えるわけですので msbuild のカスタムタスクで検出器を回すことができればあなたのソースのビルド時にチェックするとかもきっとできるでしょう。

 

この構文木を編集するなどを(2)でやってみたいと思います。単純な構文変換であればこの処理で十分ですが、意味を維持するという場合には相応の処置が必要なので意味を含む変換は(3)以降になると思います。

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

%d人のブロガーが「いいね」をつけました。