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:
monkeycode-ai
2026-06-03 02:57:13 +00:00
parent d260763e7a
commit a0971cf974
21 changed files with 1553 additions and 16 deletions
+63
View File
@@ -0,0 +1,63 @@
using CodePlay.Core.Interfaces;
using CodePlay.Core.Models;
using CodePlay.Core.Common;
namespace CodePlay.Core.Parsers;
/// <summary>
/// 解析器基类
/// </summary>
public abstract class BaseParser : IParser
{
/// <summary>
/// 支持的语言类型
/// </summary>
public abstract LanguageType SupportedLanguage { get; }
/// <summary>
/// 解析源代码并生成 AST
/// </summary>
public abstract Task<SyntaxTree> ParseAsync(string sourceCode, CancellationToken cancellationToken = default);
/// <summary>
/// 创建语法树
/// </summary>
protected SyntaxTree CreateSyntaxTree()
{
return new SyntaxTree
{
Language = SupportedLanguage,
Root = new SyntaxNode
{
Type = SyntaxNodeType.CompilationUnit
}
};
}
/// <summary>
/// 添加注释到语法树
/// </summary>
protected void AddComment(SyntaxTree tree, string text, CommentType type, int lineNumber)
{
tree.Comments.Add(new SyntaxComment
{
Text = text,
Type = type,
LineNumber = lineNumber
});
}
/// <summary>
/// 记录解析日志
/// </summary>
protected TransformationLog CreateLog(string operation, string details, LogLevel level = LogLevel.Info)
{
return new TransformationLog
{
Timestamp = DateTime.UtcNow,
Operation = operation,
Details = details,
Level = level
};
}
}
+129
View File
@@ -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
});
}
}
}