using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using CodePlay.Core.Models; using CodePlay.Core.Common; using CodePlay.Core.Interfaces; namespace CodePlay.Core.Parsers; /// /// C# 语法解析器 /// public class CSharpParser : BaseParser { /// /// 支持的语言类型 /// public override LanguageType SupportedLanguage => LanguageType.CSharp; /// /// 解析 C# 源代码 /// public override Task ParseAsync(string sourceCode, CancellationToken cancellationToken = default) { var tree = CreateSyntaxTree(); tree.SourceCode = sourceCode; var compilationUnit = CSharpSyntaxTree.ParseText(sourceCode, cancellationToken: cancellationToken); var root = compilationUnit.GetRoot(cancellationToken); tree.Root = ConvertNode(root); ExtractComments(tree, root); ExtractDocumentation(tree, root); return Task.FromResult(tree); } private Interfaces.SyntaxNode ConvertNode(Microsoft.CodeAnalysis.SyntaxNode node) { var newNode = new Interfaces.SyntaxNode { Type = MapNodeType(node.Kind()), Text = node.ToString(), Metadata = new Dictionary() }; foreach (var child in node.ChildNodes()) { var childNode = ConvertNode(child); childNode.Parent = newNode; newNode.Children.Add(childNode); } return newNode; } private SyntaxNodeType MapNodeType(Microsoft.CodeAnalysis.CSharp.SyntaxKind kind) { return kind switch { Microsoft.CodeAnalysis.CSharp.SyntaxKind.CompilationUnit => SyntaxNodeType.CompilationUnit, Microsoft.CodeAnalysis.CSharp.SyntaxKind.NamespaceDeclaration => SyntaxNodeType.Namespace, Microsoft.CodeAnalysis.CSharp.SyntaxKind.ClassDeclaration => SyntaxNodeType.Class, Microsoft.CodeAnalysis.CSharp.SyntaxKind.InterfaceDeclaration => SyntaxNodeType.Interface, Microsoft.CodeAnalysis.CSharp.SyntaxKind.MethodDeclaration => SyntaxNodeType.Method, Microsoft.CodeAnalysis.CSharp.SyntaxKind.PropertyDeclaration => SyntaxNodeType.Property, Microsoft.CodeAnalysis.CSharp.SyntaxKind.FieldDeclaration => SyntaxNodeType.Field, Microsoft.CodeAnalysis.CSharp.SyntaxKind.ConstructorDeclaration => SyntaxNodeType.Constructor, Microsoft.CodeAnalysis.CSharp.SyntaxKind.Parameter => SyntaxNodeType.Parameter, Microsoft.CodeAnalysis.CSharp.SyntaxKind.TypeArgumentList or Microsoft.CodeAnalysis.CSharp.SyntaxKind.GenericName => SyntaxNodeType.Type, Microsoft.CodeAnalysis.CSharp.SyntaxKind.Block or Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExpressionStatement or Microsoft.CodeAnalysis.CSharp.SyntaxKind.IfStatement or Microsoft.CodeAnalysis.CSharp.SyntaxKind.ForStatement or Microsoft.CodeAnalysis.CSharp.SyntaxKind.ForEachStatement or Microsoft.CodeAnalysis.CSharp.SyntaxKind.WhileStatement or Microsoft.CodeAnalysis.CSharp.SyntaxKind.ReturnStatement => SyntaxNodeType.Statement, Microsoft.CodeAnalysis.CSharp.SyntaxKind.InvocationExpression or Microsoft.CodeAnalysis.CSharp.SyntaxKind.AddExpression or Microsoft.CodeAnalysis.CSharp.SyntaxKind.SubtractExpression or Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiplyExpression or Microsoft.CodeAnalysis.CSharp.SyntaxKind.DivideExpression or Microsoft.CodeAnalysis.CSharp.SyntaxKind.SimpleAssignmentExpression => SyntaxNodeType.Expression, Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineCommentTrivia or Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineCommentTrivia => SyntaxNodeType.Comment, Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineDocumentationCommentTrivia or Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineDocumentationCommentTrivia => SyntaxNodeType.DocumentationComment, _ => SyntaxNodeType.Unknown }; } private void ExtractComments(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root) { var trivia = root.DescendantTrivia() .Where(t => t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineCommentTrivia) || t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineCommentTrivia)); foreach (var commentTrivia in trivia) { var lineNumber = root.SyntaxTree.GetLineSpan(commentTrivia.Span).StartLinePosition.Line + 1; var type = commentTrivia.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineCommentTrivia) ? CommentType.SingleLine : CommentType.MultiLine; AddComment(tree, commentTrivia.ToString(), type, lineNumber); } } private void ExtractDocumentation(Interfaces.SyntaxTree tree, Microsoft.CodeAnalysis.SyntaxNode root) { var docComments = root.DescendantTrivia() .Where(t => t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.SingleLineDocumentationCommentTrivia) || t.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.MultiLineDocumentationCommentTrivia)); foreach (var docComment in docComments) { var node = root.FindNode(docComment.Span); var lineNumber = root.SyntaxTree.GetLineSpan(docComment.Span).StartLinePosition.Line + 1; tree.Documentation.Add(new SyntaxDocumentation { ElementName = node.Parent?.ToString() ?? "Unknown", Content = docComment.ToString(), Format = DocFormat.XmlDoc }); } } }