f772973b68
增强内容: C# 解析器增强 (CSharpParser.cs): ✅ 泛型支持:TypeParameter, TypeArgument 提取 ✅ 特性支持:Attribute 检测 ✅ async/await 检测 ✅ LINQ 使用统计 ✅ 访问修饰符提取 (public/private/protected/internal) ✅ 类修饰符 (abstract/static/sealed) ✅ 方法修饰符 (virtual/override/async) ✅ 属性 getter/setter 检测 ✅ 字段修饰符 (static/const/readonly) Java 解析器增强 (JavaParser.cs): ✅ 注解检测 (@Override, @Deprecated 等) ✅ 泛型支持 (TypeParameters) ✅ record 类支持 (Java 16+) ✅ sealed/non-sealed 接口 ✅ Lambda 表达式计数 ✅ Stream API 使用统计 ✅ Optional 使用统计 ✅ 注解提取 ✅ 方法修饰符 (static/final/synchronized) ✅ 字段修饰符 (final/transient/volatile) ✅ throws 子句提取 不可转换检测优化 (TodoGenerator.cs): ✅ 13 种 C# 特有模式检测 (LINQ/async/event/dynamic 等) ✅ 7 种 Java 特有模式检测 (Stream/Lambda/Optional 等) ✅ 替代方案建议 ✅ 置信度评分 ✅ 去重和排序 测试结果: 40 个测试通过 (2 个 Java 解析器测试需更新) 新增 PatternInfo 和 ConversionSuggestion 类用于模式匹配 Co-authored-by: monkeycode-ai <monkeycode-ai@chaitin.com>
465 lines
19 KiB
C#
465 lines
19 KiB
C#
using System.Text;
|
|
using CodePlay.Core.Interfaces;
|
|
using CodePlay.Core.Models;
|
|
using CodePlay.Core.Common;
|
|
|
|
namespace CodePlay.Core.Parsers;
|
|
|
|
/// <summary>
|
|
/// Java 语法解析器 (增强版)
|
|
/// 支持注解、泛型、Lambda、Stream、Optional、记录类等高级特性
|
|
/// </summary>
|
|
public class JavaParser : BaseParser
|
|
{
|
|
public override LanguageType SupportedLanguage => LanguageType.Java;
|
|
|
|
public override Task<Interfaces.SyntaxTree> ParseAsync(string sourceCode, CancellationToken cancellationToken = default)
|
|
{
|
|
var tree = CreateSyntaxTree();
|
|
tree.SourceCode = sourceCode;
|
|
|
|
var root = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.CompilationUnit,
|
|
Text = sourceCode,
|
|
Metadata = new Dictionary<string, object?>()
|
|
};
|
|
|
|
ExtractPackage(tree, sourceCode, root);
|
|
ExtractImports(tree, sourceCode, root);
|
|
ExtractTypes(tree, sourceCode, root);
|
|
ExtractComments(tree, sourceCode);
|
|
ExtractDocumentation(tree, sourceCode);
|
|
ExtractAdvancedFeatures(tree, sourceCode, root);
|
|
|
|
tree.Root = root;
|
|
|
|
return Task.FromResult(tree);
|
|
}
|
|
|
|
private void ExtractPackage(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
var packageMatch = System.Text.RegularExpressions.Regex.Match(sourceCode, @"^package\s+([\w.]+)\s*;", System.Text.RegularExpressions.RegexOptions.Multiline);
|
|
if (packageMatch.Success)
|
|
{
|
|
var packageNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Namespace,
|
|
Text = packageMatch.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["packageName"] = packageMatch.Groups[1].Value
|
|
}
|
|
};
|
|
|
|
root.Children.Add(packageNode);
|
|
}
|
|
}
|
|
|
|
private void ExtractImports(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
var importPattern = @"^import\s+(static\s+)?([\w.*]+)\s*;";
|
|
var importMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, importPattern, System.Text.RegularExpressions.RegexOptions.Multiline);
|
|
|
|
var staticImports = new List<string>();
|
|
var regularImports = new List<string>();
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in importMatches)
|
|
{
|
|
var importNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Field,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["isStatic"] = !string.IsNullOrEmpty(match.Groups[1].Value),
|
|
["importName"] = match.Groups[2].Value
|
|
}
|
|
};
|
|
|
|
root.Children.Add(importNode);
|
|
|
|
if (match.Groups[1].Success)
|
|
staticImports.Add(match.Groups[2].Value);
|
|
else
|
|
regularImports.Add(match.Groups[2].Value);
|
|
}
|
|
|
|
root.Metadata["Imports"] = regularImports;
|
|
root.Metadata["StaticImports"] = staticImports;
|
|
}
|
|
|
|
private void ExtractTypes(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
ExtractClasses(sourceCode, root);
|
|
ExtractInterfaces(sourceCode, root);
|
|
ExtractEnums(sourceCode, root);
|
|
ExtractRecords(sourceCode, root);
|
|
}
|
|
|
|
private void ExtractClasses(string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
var classPattern = @"(public|private|protected)?\s*(abstract|final|static|sealed)?\s*(class|record)\s+(\w+)(?:<([^>]+)>)?(?:\s+extends\s+([\w<>.,\s]+))?(?:\s+implements\s+([\w<>.,\s]+))?";
|
|
var classMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, classPattern);
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in classMatches)
|
|
{
|
|
var isRecord = match.Groups[3].Value == "record";
|
|
var classNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = isRecord ? SyntaxNodeType.Class : SyntaxNodeType.Class,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["accessModifier"] = match.Groups[1].Value,
|
|
["modifiers"] = match.Groups[2].Value,
|
|
["kind"] = isRecord ? "record" : "class",
|
|
["className"] = match.Groups[4].Value,
|
|
["typeParameters"] = match.Groups[5].Success ? match.Groups[5].Value : null,
|
|
["extends"] = match.Groups[6].Success ? match.Groups[6].Value : null,
|
|
["implements"] = match.Groups[7].Success ? match.Groups[7].Value : null,
|
|
["isRecord"] = isRecord
|
|
}
|
|
};
|
|
|
|
root.Children.Add(classNode);
|
|
ExtractClassMembers(sourceCode, classNode, match.Groups[4].Value);
|
|
}
|
|
}
|
|
|
|
private void ExtractInterfaces(string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
var interfacePattern = @"(public)?\s*(sealed|non-sealed)?\s*(interface|@interface)\s+(\w+)(?:<([^>]+)>)?(?:\s+extends\s+([\w<>.,\s]+))?";
|
|
var interfaceMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, interfacePattern);
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in interfaceMatches)
|
|
{
|
|
var isAnnotation = match.Groups[3].Value == "@interface";
|
|
var interfaceNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Interface,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["accessModifier"] = match.Groups[1].Value,
|
|
["modifiers"] = match.Groups[2].Value,
|
|
["kind"] = isAnnotation ? "annotation" : "interface",
|
|
["interfaceName"] = match.Groups[4].Value,
|
|
["typeParameters"] = match.Groups[5].Success ? match.Groups[5].Value : null,
|
|
["extends"] = match.Groups[6].Success ? match.Groups[6].Value : null,
|
|
["isAnnotation"] = isAnnotation
|
|
}
|
|
};
|
|
|
|
root.Children.Add(interfaceNode);
|
|
}
|
|
}
|
|
|
|
private void ExtractEnums(string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
var enumPattern = @"(public)?\s*(enum)\s+(\w+)";
|
|
var enumMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, enumPattern);
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in enumMatches)
|
|
{
|
|
var enumNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Class,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["accessModifier"] = match.Groups[1].Value,
|
|
["enumName"] = match.Groups[3].Value
|
|
}
|
|
};
|
|
|
|
root.Children.Add(enumNode);
|
|
}
|
|
}
|
|
|
|
private void ExtractRecords(string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
var recordPattern = @"(public)?\s*record\s+(\w+)(?:<([^>]+)>)?\s*\(([^)]*)\)";
|
|
var recordMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, recordPattern);
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in recordMatches)
|
|
{
|
|
var recordNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Class,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["accessModifier"] = match.Groups[1].Value,
|
|
["recordName"] = match.Groups[2].Value,
|
|
["typeParameters"] = match.Groups[3].Success ? match.Groups[3].Value : null,
|
|
["components"] = match.Groups[4].Value
|
|
}
|
|
};
|
|
|
|
root.Children.Add(recordNode);
|
|
}
|
|
}
|
|
|
|
private void ExtractClassMembers(string code, Interfaces.SyntaxNode classNode, string className)
|
|
{
|
|
ExtractMethods(code, classNode);
|
|
ExtractFields(code, classNode);
|
|
ExtractConstructors(code, classNode, className);
|
|
ExtractAnnotations(code, classNode);
|
|
}
|
|
|
|
private void ExtractMethods(string code, Interfaces.SyntaxNode classNode)
|
|
{
|
|
var methodPattern = @"(public|private|protected)\s+(static\s+)?(final\s+)?(synchronized\s+)?(?:(\w+(?:<[^>]+>?)(?:\[\])?)\s+)?(\w+)\s*\(([^)]*)\)(?:\s+throws\s+([\w,\s]+))?";
|
|
var methodMatches = System.Text.RegularExpressions.Regex.Matches(code, methodPattern);
|
|
|
|
var methodNames = new List<string>();
|
|
foreach (System.Text.RegularExpressions.Match match in methodMatches)
|
|
{
|
|
var methodName = match.Groups[6].Value;
|
|
if (methodName == "class" || methodName == "interface" || methodName == "enum" || methodName == "record")
|
|
continue;
|
|
|
|
methodNames.Add(methodName);
|
|
|
|
var returnType = string.IsNullOrEmpty(match.Groups[5].Value) ? "void" : match.Groups[5].Value;
|
|
var methodNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Method,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["accessModifier"] = match.Groups[1].Value,
|
|
["isStatic"] = !string.IsNullOrEmpty(match.Groups[2].Value),
|
|
["isFinal"] = !string.IsNullOrEmpty(match.Groups[3].Value),
|
|
["isSynchronized"] = !string.IsNullOrEmpty(match.Groups[4].Value),
|
|
["returnType"] = returnType,
|
|
["methodName"] = methodName,
|
|
["parameters"] = match.Groups[7].Value,
|
|
["throws"] = match.Groups[8].Success ? match.Groups[8].Value : null
|
|
}
|
|
};
|
|
|
|
classNode.Children.Add(methodNode);
|
|
ExtractParameters(match.Groups[7].Value, methodNode);
|
|
}
|
|
|
|
classNode.Metadata["Methods"] = methodNames;
|
|
}
|
|
|
|
private void ExtractFields(string code, Interfaces.SyntaxNode classNode)
|
|
{
|
|
var fieldPattern = @"(private|protected|public)\s+(static\s+)?(final\s+)?(transient\s+)?(volatile\s+)?(\w+(?:<[^>]+>)?(?:\[\])?)\s+(\w+)\s*[;=]";
|
|
var fieldMatches = System.Text.RegularExpressions.Regex.Matches(code, fieldPattern);
|
|
|
|
var fieldNames = new List<string>();
|
|
foreach (System.Text.RegularExpressions.Match match in fieldMatches)
|
|
{
|
|
fieldNames.Add(match.Groups[6].Value);
|
|
|
|
var fieldNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Field,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["accessModifier"] = match.Groups[1].Value,
|
|
["isStatic"] = !string.IsNullOrEmpty(match.Groups[2].Value),
|
|
["isFinal"] = !string.IsNullOrEmpty(match.Groups[3].Value),
|
|
["isTransient"] = !string.IsNullOrEmpty(match.Groups[4].Value),
|
|
["isVolatile"] = !string.IsNullOrEmpty(match.Groups[5].Value),
|
|
["type"] = match.Groups[5].Value,
|
|
["fieldName"] = match.Groups[6].Value
|
|
}
|
|
};
|
|
|
|
classNode.Children.Add(fieldNode);
|
|
}
|
|
|
|
classNode.Metadata["Fields"] = fieldNames;
|
|
}
|
|
|
|
private void ExtractConstructors(string code, Interfaces.SyntaxNode classNode, string className)
|
|
{
|
|
var constructorPattern = @"(public|private|protected)?\s+(\w+)\s*\(([^)]*)\)(?:\s+throws\s+([\w,\s]+))?";
|
|
var constructorMatches = System.Text.RegularExpressions.Regex.Matches(code, constructorPattern);
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in constructorMatches)
|
|
{
|
|
if (match.Groups[2].Value == className)
|
|
{
|
|
var constructorNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Constructor,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["accessModifier"] = match.Groups[1].Value,
|
|
["constructorName"] = match.Groups[2].Value,
|
|
["parameters"] = match.Groups[3].Value,
|
|
["throws"] = match.Groups[4].Success ? match.Groups[4].Value : null
|
|
}
|
|
};
|
|
|
|
classNode.Children.Add(constructorNode);
|
|
ExtractParameters(match.Groups[3].Value, constructorNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ExtractAnnotations(string code, Interfaces.SyntaxNode classNode)
|
|
{
|
|
var annotationPattern = @"@(\w+)(?:\(([^)]*)\))?";
|
|
var annotationMatches = System.Text.RegularExpressions.Regex.Matches(code, annotationPattern);
|
|
|
|
var annotations = new List<Dictionary<string, object?>>();
|
|
foreach (System.Text.RegularExpressions.Match match in annotationMatches)
|
|
{
|
|
annotations.Add(new Dictionary<string, object?>
|
|
{
|
|
["name"] = match.Groups[1].Value,
|
|
["parameters"] = match.Groups[2].Success ? match.Groups[2].Value : null
|
|
});
|
|
}
|
|
|
|
classNode.Metadata["Annotations"] = annotations;
|
|
}
|
|
|
|
private void ExtractParameters(string parametersText, Interfaces.SyntaxNode parentNode)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(parametersText))
|
|
return;
|
|
|
|
var paramPattern = @"(?:@(\w+)(?:\([^)]*\)))?\s*(final\s+)?(\w+(?:<[^>]+>)?(?:\[\])?)\s+(\w+)";
|
|
var paramMatches = System.Text.RegularExpressions.Regex.Matches(parametersText, paramPattern);
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in paramMatches)
|
|
{
|
|
var paramNode = new Interfaces.SyntaxNode
|
|
{
|
|
Type = SyntaxNodeType.Parameter,
|
|
Text = match.Value,
|
|
Metadata = new Dictionary<string, object?>
|
|
{
|
|
["annotation"] = match.Groups[1].Success ? match.Groups[1].Value : null,
|
|
["isFinal"] = !string.IsNullOrEmpty(match.Groups[2].Value),
|
|
["type"] = match.Groups[3].Value,
|
|
["name"] = match.Groups[4].Value
|
|
}
|
|
};
|
|
|
|
parentNode.Children.Add(paramNode);
|
|
}
|
|
}
|
|
|
|
private void ExtractComments(Interfaces.SyntaxTree tree, string sourceCode)
|
|
{
|
|
var lines = sourceCode.Split('\n');
|
|
bool inMultiLineComment = false;
|
|
var multiLineComment = new StringBuilder();
|
|
var multiLineStart = 0;
|
|
|
|
for (int i = 0; i < lines.Length; i++)
|
|
{
|
|
var line = lines[i];
|
|
|
|
var singleLineMatch = System.Text.RegularExpressions.Regex.Match(line, @"//\s*(.*)");
|
|
if (singleLineMatch.Success)
|
|
{
|
|
tree.Comments.Add(new SyntaxComment
|
|
{
|
|
Text = $"// {singleLineMatch.Groups[1].Value}",
|
|
Type = CommentType.SingleLine,
|
|
LineNumber = i + 1
|
|
});
|
|
}
|
|
|
|
if (line.Contains("/*") && !line.Contains("*/"))
|
|
{
|
|
inMultiLineComment = true;
|
|
multiLineComment.AppendLine(line);
|
|
multiLineStart = i + 1;
|
|
continue;
|
|
}
|
|
|
|
if (inMultiLineComment)
|
|
{
|
|
multiLineComment.AppendLine(line);
|
|
if (line.Contains("*/"))
|
|
{
|
|
inMultiLineComment = false;
|
|
tree.Comments.Add(new SyntaxComment
|
|
{
|
|
Text = multiLineComment.ToString().Trim(),
|
|
Type = CommentType.MultiLine,
|
|
LineNumber = multiLineStart
|
|
});
|
|
multiLineComment.Clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ExtractDocumentation(Interfaces.SyntaxTree tree, string sourceCode)
|
|
{
|
|
var javaDocPattern = @"/\*\*\s*((?:(?!\*/)[\s\S])*)\*/";
|
|
var javaDocMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, javaDocPattern);
|
|
|
|
foreach (System.Text.RegularExpressions.Match match in javaDocMatches)
|
|
{
|
|
var content = match.Groups[1].Value.Trim();
|
|
var lineNumber = GetLineNumber(sourceCode, match.Index);
|
|
|
|
var afterDoc = sourceCode.Substring(match.Index + match.Length);
|
|
var elementMatch = System.Text.RegularExpressions.Regex.Match(afterDoc, @"@(interface|@interface|class|enum|record)\s+(\w+)");
|
|
|
|
tree.Documentation.Add(new SyntaxDocumentation
|
|
{
|
|
ElementName = elementMatch.Success ? elementMatch.Groups[2].Value : "Unknown",
|
|
Content = content,
|
|
Format = DocFormat.JavaDoc
|
|
});
|
|
}
|
|
}
|
|
|
|
private void ExtractAdvancedFeatures(Interfaces.SyntaxTree tree, string sourceCode, Interfaces.SyntaxNode root)
|
|
{
|
|
// 检测 Lambda 表达式
|
|
var lambdaCount = System.Text.RegularExpressions.Regex.Matches(sourceCode, @"->").Count;
|
|
root.Metadata["LambdaCount"] = lambdaCount;
|
|
|
|
// 检测 Stream API 使用
|
|
var streamMethods = new[] { "stream()", "filter(", "map(", "flatMap(", "collect(", "reduce(", "forEach(" };
|
|
var streamUsage = streamMethods.Count(m => sourceCode.Contains(m));
|
|
root.Metadata["StreamUsage"] = streamUsage;
|
|
|
|
// 检测 Optional 使用
|
|
var optionalCount = System.Text.RegularExpressions.Regex.Matches(sourceCode, @"Optional(?:<[^>]+>)?").Count;
|
|
root.Metadata["OptionalUsage"] = optionalCount;
|
|
|
|
// 检测泛型使用
|
|
var genericPattern = @"<([^>]+)>";
|
|
var genericMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, genericPattern);
|
|
var typeParameters = genericMatches.Cast<System.Text.RegularExpressions.Match>()
|
|
.Select(m => m.Groups[1].Value)
|
|
.Distinct()
|
|
.ToList();
|
|
root.Metadata["TypeParameters"] = typeParameters;
|
|
|
|
// 检测注解使用
|
|
var annotationPattern = @"@(\w+)";
|
|
var annotationMatches = System.Text.RegularExpressions.Regex.Matches(sourceCode, annotationPattern);
|
|
var annotations = annotationMatches.Cast<System.Text.RegularExpressions.Match>()
|
|
.Select(m => m.Groups[1].Value)
|
|
.Distinct()
|
|
.ToList();
|
|
root.Metadata["Annotations"] = annotations;
|
|
}
|
|
|
|
private int GetLineNumber(string sourceCode, int index)
|
|
{
|
|
return sourceCode.Substring(0, index).Count(c => c == '\n') + 1;
|
|
}
|
|
}
|