参考:https://github.com/dotnet/roslyn/wiki/Getting-Started-C%23-Syntax-Analysis
语法分析过程主要用到以下类或结构:
- SyntaxTree类:编译器分析源代码时,将源代码作为一个字符串处理,并将其转换成一个语法树。所以语法树总是与一段源代码对应,既可以从源代码生成语法树也可以从语法树还原出源代码。SyntaxTree类是一个抽象类,不同的语言(C#或VB)会实现相应的语法树类进行源代码的分析。SyntaxTree类是immutable的,也就是说生成后就不能再修改,如果要修改语法树,就需要创建一个新的语法树实例。
- SyntaxNode类:语法树中的结点,与源代码中的语句、表达式等内容对应。一般不会是语法树中的叶结点。
- SyntaxToken结构:语法树中的叶结点,与源代码中的关键字,运算符等内容对应。
- SyntaxTrivia结构:代表了源代码中与语法无关的内容,如空格、换行、注释等
参考代码:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using Microsoft.CodeAnalysis; 7 using Microsoft.CodeAnalysis.CSharp; 8 using Microsoft.CodeAnalysis.CSharp.Syntax; 9 10 namespace GettingStartedCS 11 { 12 class Program 13 { 14 static void Main(string[] args) 15 { 16 // 从源代码生成语法树 17 SyntaxTree tree = CSharpSyntaxTree.ParseText( 18 @"using System; 19 using System.Collections; 20 using System.Linq; 21 using System.Text; 22 23 namespace HelloWorld 24 { 25 class Program 26 { 27 static void Main(string[] args) 28 { 29 Console.WriteLine(""Hello, World!""); 30 } 31 } 32 }"); 33 // 获得语法树的根结点root,root.KindText为CompliationUnit。注意Using语句通过root.Usings属性返回,而不是作为root的子节点 34 var root = (CompilationUnitSyntax)tree.GetRoot(); 35 // root只有一个namespace子节点,代表了整个namespace语句,也就是从namespace开始到最后的反大括号之间所有代码,KindText属性值为NamespaceDeclaration 36 var firstMember = root.Members[0]; 37 // Members列表的默认类型为MemberDeclarationSyntax,这里转换为NamespaceDeclarationSyntax 38 var helloWorldDeclaration = (NamespaceDeclarationSyntax)firstMember; 39 // namespace节点只有一个class子节点,代表了整个类型声明语句 40 var programDeclaration = (ClassDeclarationSyntax)helloWorldDeclaration.Members[0]; 41 // class节点也只有一个method子节点,代表了整个Main方法 42 var mainDeclaration = (MethodDeclarationSyntax)programDeclaration.Members[0]; 43 // main方法节点的子节点不再通过Members属性获得,ParameterList返回参数节点列表,Body属性返回方法体节点 44 var argsParameter = mainDeclaration.ParameterList.Parameters[0]; 45 } 46 } 47 }