これは、Visual Basic Advent Calendar 2013 12/15日の記事。
Roslynの機能としてコード解析の一部をコーディングしてみます。
その前にVBならではの問題点を紹介します。
ソースコードを文字列変数に格納するコードをC#で書いた場合、下記のようになります。
public static string src = @"
using System;
namespace HelloWorld {
class Program {
static void Main(string[] args) {
Console.WriteLine(""Hello, World!"");
}
}
}
";
ところが、これと同じことをVBで書いてみますと、下記コードのように悲しいことになるわけです。
Dim src As String =
"Imports System" & vbNewLine &
vbNewLine &
"Namespace HelloWorld" & vbNewLine &
" Module Program" & vbNewLine &
" Sub Main(args As String())" & vbNewLine &
" Console.WriteLine(""Hello, World!"")" & vbNewLine &
" End Sub" & vbNewLine &
" End Module" & vbNewLine &
"End Namespace"
これ、なんとかならないかなーと調べていくと、下記サイトがいい解決法を提示してくれています。
Mutliline String Literals in VB.NET
Dim src As String =
<string>
Imports System
Namespace HelloWorld
Module Program
Sub Main(args As String())
Console.WriteLine("Hello, World!")
End Sub
End Module
End Namespace
</string>.Value
XMLリテラルにすれば、C#と同様な感じで記述できるようになります。
今回は”<string>”タグにしてますが、他の例をみると”<a>”タグを使用しているところもあります。
また、<![CDATA[文字列]]>.Valueでも同様のことが出来ますが、この場合、囲まれた文字列がVisual Studio上の文字色設定では薄いグレーで見辛いので、「VBのXML CDataセクション」の文字色を変更するといいでしょう。
あと、VB2010以降は暗黙の行継続である「_」(アンダースコア)が不要になっているので、読みやすくなっています。
下記のソースは、文字列変数のソースコードをコード解析して、ノード種類とノード値を「|」(パイプ)区切りで出力しています。
Imports Roslyn.Compilers
Imports Roslyn.Compilers.VisualBasic
Imports Roslyn.Scripting
Imports Roslyn.Scripting.VisualBasic
Module Module1
Sub Main()
Dim src As String =
<string>
Imports System
Namespace HelloWorld
Module Program
Sub Main(args As String())
Console.WriteLine("Hello, World!")
End Sub
End Module
End Namespace
</string>.Value
Dim tree = SyntaxTree.ParseText(src)
For Each node As SyntaxNode In tree.GetRoot().ChildNodesAndTokens()
PrintSyntaxTree(node)
Next
Console.ReadLine()
End Sub
Private Sub PrintSyntaxTree(node As SyntaxNode)
If node IsNot Nothing Then
Console.WriteLine(node.Kind.ToString & " | " & node.ToString())
For Each child As SyntaxNode In node.ChildNodes()
PrintSyntaxTree(child)
Next
End If
End Sub
End Module
【出力結果】
ImportsStatement | Imports System
MembersImportsClause | System
IdentifierName | System
NamespaceBlock | Namespace HelloWorld
Module Program
Sub Main(args As String())
Console.WriteLine("Hello, World!")
End Sub
End Module
End Namespace
NamespaceStatement | Namespace HelloWorld
IdentifierName | HelloWorld
ModuleBlock | Module Program
Sub Main(args As String())
Console.WriteLine("Hello, World!")
End Sub
End Module
ModuleStatement | Module Program
SubBlock | Sub Main(args As String())
Console.WriteLine("Hello, World!")
End Sub
SubStatement | Sub Main(args As String())
ParameterList | (args As String())
Parameter | args As String()
ModifiedIdentifier | args
SimpleAsClause | As String()
ArrayType | String()
PredefinedType | String
ArrayRankSpecifier | ()
CallStatement | Console.WriteLine("Hello, World!")
InvocationExpression | Console.WriteLine("Hello, World!")
MemberAccessExpression | Console.WriteLine
IdentifierName | Console
IdentifierName | WriteLine
ArgumentList | ("Hello, World!")
SimpleArgument | "Hello, World!"
StringLiteralExpression | "Hello, World!"
EndSubStatement | End Sub
EndModuleStatement | End Module
EndNamespaceStatement | End Namespace
次回は、Roslynについてもう少し踏み込んだことをしてみます。