.NET Compiler Platform SDKnode
今天,Visual Basic和C#編譯器是黑盒子:輸入文本而後輸出字節,編譯管道的中間階段沒有透明性。使用.NET編譯器平臺(之前稱爲「Roslyn」),工具和開發人員能夠利用編譯器使用的徹底相同的數據結構和算法來分析和理解代碼。 本篇文章,咱們將會慢慢熟悉語法API,經過語法API來查看解析器,語法樹,用於推理和構造它們的實用程序。git
Trivia,Token和Node造成了一個徹底表明Visual Basic或C#代碼片斷中全部內容的樹github
它的實例表示整個解析樹。SyntaxTree是一個抽象類,具備特定於語言的派生類。要解析特定語言的語法,您須要使用CSharpSyntaxTree(或VisualBasicSyntaxTree)類上的解析方法。算法
它的實例表示的語法結構如聲明,語句,子句和表達式。api
它表明一個單獨的關鍵字,識別符,操做員或標點符號數據結構
它表示語法上可有可無的信息,例如令牌之間的空白,預處理指令和註釋。數據結構和算法
下圖示例:SyntaxNode: 藍色 | SyntaxToken: 綠色 | SyntaxTrivia: 紅色ide
Microsoft.CodeAnalysis.CSharp Microsoft.CodeAnalysis.CSharp.Workspaces
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax;
using System; namespace UsingCollectorCS { class Program { static void Main(string[] args) { Console.WriteLine("Hello World"); } } class Student { public string Name { get; set; } } }
/// <summary>
///解析語法樹 /// </summary> /// <param name="code"></param> /// <returns></returns> public SyntaxNode GetRoot(string code) { var tree = CSharpSyntaxTree.ParseText(code); //SyntaxTree的根root var root = (CompilationUnitSyntax)tree.GetRoot(); //member var firstmember = root.Members[0]; //命名空間Namespace var helloWorldDeclaration = (NamespaceDeclarationSyntax)firstmember; //類 class var programDeclaration = (ClassDeclarationSyntax)helloWorldDeclaration.Members[0]; //方法 Method var mainDeclaration = (MethodDeclarationSyntax)programDeclaration.Members[0]; //參數 Parameter var argsParameter = mainDeclaration.ParameterList.Parameters[0]; //查詢方法,查詢方法名稱爲Main的第一個參數。 var firstParameters = from methodDeclaration in root.DescendantNodes() .OfType<MethodDeclarationSyntax>() where methodDeclaration.Identifier.ValueText == "Main" select methodDeclaration.ParameterList.Parameters.First(); var argsParameter2 = firstParameters.Single(); return root; }
var code = @"using System; namespace UsingCollectorCS { class Program { static void Main(string[] args) { Console.WriteLine(""Hello World""); } } class Student { public string Name { get; set; } } }"; var tree = new AnalysisDemo().GetRoot(code);
通過對比可知如下部分工具
利用CSharpSyntaxTree.ParseText(code)獲取語法樹SyntaxTree
利用(CompilationUnitSyntax)tree.GetRoot()獲取語法樹的跟節點
利用 (NamespaceDeclarationSyntax)root.Members[0]可獲取命名空間
利用 (ClassDeclarationSyntax)helloWorldDeclaration.Members[0]可獲取類
利用 (MethodDeclarationSyntax)programDeclaration.Members[0]可獲取方法
利用linq查詢,可從**root.DescendantNodes()**節點內查詢方法/參數等成員。
一般,您須要在語法樹中查找特定類型的全部節點,例如,文件中的每一個屬性聲明。
經過擴展CSharpSyntaxWalker類並重寫VisitPropertyDeclaration方法,您能夠在不事先知道其結構的狀況下處理語法樹中的每一個屬性聲明。
CSharpSyntaxWalker是一種特殊的SyntaxVisitor,它以遞歸方式訪問節點及其每一個子節點。
咱們先來演示CSharpSyntaxWalker的兩個虛virtual方法VisitUsingDirective 和VisitPropertyDeclaration
/// <summary>
/// 收集器 /// </summary> public class UsingCollector : CSharpSyntaxWalker { public readonly Dictionary<string, List<string>> models = new Dictionary<string, List<string>>(); public readonly List<UsingDirectiveSyntax> Usings = new List<UsingDirectiveSyntax>(); public override void VisitUsingDirective(UsingDirectiveSyntax node) { if (node.Name.ToString() != "System" && !node.Name.ToString().StartsWith("System.")) { this.Usings.Add(node); } } public override void VisitPropertyDeclaration(PropertyDeclarationSyntax node) { var classnode = node.Parent as ClassDeclarationSyntax; if (!models.ContainsKey(classnode.Identifier.ValueText)) { models.Add(classnode.Identifier.ValueText, new List<string>()); } models[classnode.Identifier.ValueText].Add(node.Identifier.ValueText); } } /// <summary> /// 演示CSharpSyntaxWalker /// </summary> /// <param name="code"></param> /// <returns></returns> public UsingCollector GetCollector(string code) { var tree = CSharpSyntaxTree.ParseText(code); var root = (CompilationUnitSyntax)tree.GetRoot(); var collector = new UsingCollector(); collector.Visit(root); return collector; }
var code2 = @"using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; namespace TopLevel { using Microsoft; using System.ComponentModel; namespace Child1 { using Microsoft.Win32; using System.Runtime.InteropServices; class Foo { public string FChildA{get;set;} public string FChildB{get;set;} } } namespace Child2 { using System.CodeDom; using Microsoft.CSharp; class Bar { public string BChildA{get;set;} public string BChildB{get;set;} } } }"; var collector = new AnalysisDemo().GetCollector(code2); foreach (var directive in collector.Usings) { Console.WriteLine($"Name:{directive.Name}"); } Console.WriteLine($"models:{JsonConvert.SerializeObject(collector.models)}");
咱們能夠得出結論
VisitUsingDirective 主要用於獲取Using命名空間
VisitPropertyDeclaration主要用於獲取屬性。
本篇文章主要講了
語法樹SyntaxTree,以及SyntaxNode,SyntaxToken,SyntaxTrivia。
經過重寫CSharpSyntaxWalker的虛方法,能夠實現自定義獲取。
附上官方截取的部分流程圖
Roslyn由兩個主要的API層組成 - 編譯器API和工做區API。
Getting Started C# Syntax Analysis