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
});
}
}
}