この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。
Roslyn で構文解析 (ASTからXMLへの変換を書いてみた) [プログラム]
Roslyn が使えるようになったので、まずは構文解析から。
構文解析結果の処理パターン
Roslynによる構文解析は、SyntaxTree.ParseCompilationUnit(<ソース(string)>) 一発で簡単にできる。では構文解析結果の抽象構文木(以下AST)はどう利用するか?大きく以下の2パターンになる。
- Visitorパターンで、AST中で関心のあるノードへの処理を記述しておく
- ASTのルートから、「○○である子供の先頭の、最後の子供の・・・」という風に、関心のあるノードまで自分でたどり処理する
XMLの処理における、SAXの利用が1、DOMの利用が2、というイメージ。
1は2に比べ処理を簡潔に書けることが多いが、ノードを探す条件が複雑(例えば「ネストしたfor文のカウンタ変数定義の2番目を・・・」のような)な場合、Visitorに状態を持たせたりVisitメソッド内で周辺のノードのチェックなどの処理が必要となり、可読性が下がったり処理が冗長になる。このため、1と2はどちらが優れているというわけではなく、使い分けることが望ましい。
SyntaxVisitorの使用
RoslynにおいてVisitorを使った処理の記述は以下の手順。
- SyntaxWalkerを継承したクラスを作る
- 関心のあるノードへのVisitメソッドをオーバーライド
- 例えばForStatementに関心があれば、VistForStatementをオーバーライド。
- 全てのノードへの処理はDefaultVisitをオーバーライドする手もある。
- Vistメソッドに処理を記述する。Visitメソッドには以下を必要に応じて記述する。
- そのノードに関する処理
- 処理が必要な子ノードのVisitメソッドの呼出
- preorder ⇒ 自ノードの処理, 各子ノードを順にVisit呼出
- postorder ⇒ 各子ノードを順にVisit呼出, 自ノードの処理
試しに、ASTをXMLに変換するVisitorを書いてみるとこんな感じ。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Xml; using Roslyn.Compilers; using Roslyn.Compilers.CSharp; namespace AstToXml { class AstVisitor : SyntaxWalker { private XmlWriter writer; public AstVisitor(XmlWriter argWriter) { writer = argWriter; } protected override void VisitCompilationUnit(CompilationUnitSyntax node) { writer.WriteStartDocument(); DefaultVisit(node); writer.WriteEndDocument(); } protected override void VisitIdentifierName(IdentifierNameSyntax node) { openNode(node); writer.WriteAttributeString("PlainName", node.PlainName); closeNode(node); } protected override void DefaultVisit(SyntaxNode node) { openNode(node); base.DefaultVisit(node); closeNode(node); } private void openNode(SyntaxNode node) { Type type = node.GetType(); writer.WriteStartElement(type.Name); } private void closeNode(SyntaxNode node) { writer.WriteEndElement(); } } }
このVisitorの意図とそのための処理は以下の通り。
- 全てのノードに対してXMLタグを出力したい。
⇒ DefaultVisitをオーバーライド、開始タグと終了タグの間でASTの子ノードに対応するXMLを生成したいので、途中でbase.DefaultVisitを呼ぶ。 - CompilationUnit(ASTのルート)は生成するXMLタグの外にDocument要素をつけたい。
⇒ VisitCompilationUnitをオーバーライド、自身のXMLタグと子ノードの処理のため DefaultVisitを呼ぶ。 - IdentifierNameは生成するXMLタグの属性にPlainNameをつけたい。
⇒ VisitIdentifierNameをオーバーライド、子ノードはないのでVisitは呼ばない。
※ 実際には、DefaultVisitの中でプロパティを列挙・フィルタして属性につけちゃう方がいいけど、サンプルということで。
このVisitorを使うコードは以下のような感じ。
SyntaxTree ast = SyntaxTree.ParseCompilationUnit(<ソース>); XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; using(XmlWriter writer = XmlWriter.Create(xmlFile, settings)) { new AstVisitor(writer).Visit(ast.Root); }
ちなみに、Roslyn.Compilers.CSharp の下にあるクラスを覗くと、C#の○○Syntaxに紛れて、XmlElementSyntaxとか、XML関連のノードのクラスがある。C#の構文にXML書けるところなんてあったっけ?それとも単に利便性のため?それにしては Syntax.Parse○○()メソッド群にXML用のがないんだよな・・・
Visual Studio Express で Roslyn [プログラム]
Microsoft “Roslyn” CTP をVisual Studio Expressで使った際のメモ。
Roslynとは
要はC#とかVBのコンパイラをマネージドコードで書き直してAPIを公開し、いろんなツールから使えるようにしましょう、という理解。ツール毎に解析ロジックを再発明しているのは、プログラミング言語の解析関連の仕事をしている人の間では共通の問題意識だと思うので、C#の意味解析までやってくれる共通基盤を本家が提供してくれるというのはなかなかうれしい。
まずは普通にインストール・・・
Download Center から、RoslynSetup.exe をダウンロードしてインストールしようとするも、"Visual Studio 2010 Service Pack 1"がないよ、と怒られインストールできない。
入ってんだけどなー?うーん、としばし悩んだ末以下を発見。
Required Pre-installed Products:- SP1 versions of Visual Studio 2010 Professional, Premium, or Ultimate
- Visual Studio 2010 SP1 SDK
Expressではダメか・・・
NuGetでインストール
と、あきらめていたところ、NuGetでRoslyn CTPが配布されているという話を聞いた。これなら、Expressでも使えるかも!?ただしVisual Studio から NuGet を使うには、これまた Professional 以上でないとダメ、コマンドラインから使わないといけないらしい。ということでこの手順のまとめ。
- コマンドのダウンロード
http://nuget.codeplex.com/ から[Downloads]、[NuGet.exe Command Line bootstrapper] にある、[NuGet.exe Command Line] のリンクでNuGet.exeをダウンロード - コマンドのインストール
どこかにディレクトリを作り(この中にNuGetでインストールしたパッケージも入る)、NuGet.exe を置く。このNuGet.exeを実行すると、最新版のNuGet.exeで置き換えてくれる。なので"bootstrapper"なのね。 - Roslynのインストール
コマンドラインから「NuGet.exe install Roslyn」で実行。「Successfully installed 'Roslyn 1.0.11014.5'.」と出ればOK。
これで、NuGet.exeを置いたディレクトリに、Roslyn.1.0.11014.5 というディレクトリができているはず。
使ってみる
NuGetでインストールした場合、Visual Studio のテンプレートは入らないので、若干手間が増える。
- プロジェクトの作成
普通に、Visual C# を立ち上げ、[ファイル]-[新しいプロジェクト]を選択し、"コンソールアプリケーション"を選んで[OK] - 参照の追加
[ソリューションエクスプローラー]で、"参照設定"を右クリックして[参照の追加]を選択。[参照の追加]ダイアログで[参照タブ]をクリックし、"/Roslyn.1.0.11014.5/lib/net40/"の、"Roslyn.Compilers.CSharp.dll"と"Roslyn.Compilers.dll"を選択して[OK] - ソース
とりあえず、こんな感じで書いてみる。using System; using System.Collections.Generic; using System.Linq; using System.Text; using Roslyn.Compilers; using Roslyn.Compilers.CSharp; namespace RoslynSample1 { class Program { public static string src = @" using System; namespace HelloWorld { class Program { static void Main(string[] args) { Console.WriteLine(""Hello, World!""); } } } "; static void Main(string[] args) { var st = ((ExpressionStatementSyntax)Syntax .ParseStatement(src, 110)); var exp = (InvocationExpressionSyntax)st.Expression; var arg = (ArgumentSyntax)exp.ArgumentList.Arguments[0]; Console.WriteLine(arg.ToString()); } } }
- 実行
「"Hello, World!"」と表示されればOK