feat: 完成 CodePlay 项目初始化和核心解析器实现
- 创建 CodePlay 解决方案和 4 个项目 (Core, Web, CLI, Tests) - 实现核心数据模型 (ConversionRequest, ConversionResult, Project 等) - 实现核心接口 (IParser, IConverter, ICodeGenerator 等) - 实现基础抽象类 BaseParser - 实现 CSharpParser 解析器 (基于 Roslyn) - 添加 Microsoft.CodeAnalysis.CSharp NuGet 包 - 所有代码编译通过 Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user