- 创建 CodePlay 解决方案和 4 个项目 (Core, Web, CLI, Tests) - 实现核心数据模型 (ConversionRequest, ConversionResult, Project 等) - 实现核心接口 (IParser, IConverter, ICodeGenerator 等) - 实现基础抽象类 BaseParser - 实现 CSharpParser 解析器 (基于 Roslyn) - 添加 Microsoft.CodeAnalysis.CSharp NuGet 包 - 所有代码编译通过 Co-authored-by: monkeycode-ai <[email protected]>
130 lines
5.9 KiB
C#
130 lines
5.9 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// C# 语法解析器
|
|
/// </summary>
|
|
public class CSharpParser : BaseParser
|
|
{
|
|
/// <summary>
|
|
/// 支持的语言类型
|
|
/// </summary>
|
|
public override LanguageType SupportedLanguage => LanguageType.CSharp;
|
|
|
|
/// <summary>
|
|
/// 解析 C# 源代码
|
|
/// </summary>
|
|
public override Task<Interfaces.SyntaxTree> 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<string, object?>()
|
|
};
|
|
|
|
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
|
|
});
|
|
}
|
|
}
|
|
}
|