This commit is contained in:
2026-06-01 01:31:31 +07:00
parent ba77411c4a
commit e43c885fbe
7 changed files with 5146 additions and 2238 deletions

View File

@@ -1,226 +1,226 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
//using Microsoft.CodeAnalysis;
//using Microsoft.CodeAnalysis.CSharp.Syntax;
//using Microsoft.CodeAnalysis.Text;
//using System.Collections.Generic;
//using System.Collections.Immutable;
//using System.Linq;
//using System.Text;
namespace QWERTYkez.Mensura.Generator;
//namespace QWERTYkez.Mensura.Generator;
[Generator(LanguageNames.CSharp)]
internal class CollectionsOperatorsGenerator : IIncrementalGenerator
{
private const string AttributeShortName = "CollectionsOperatorsGenerator";
private const string AttributeFullName = AttributeShortName + "Attribute";
//[Generator(LanguageNames.CSharp)]
//internal class CollectionsOperatorsGenerator : IIncrementalGenerator
//{
// private const string AttributeShortName = "CollectionsOperatorsGenerator";
// private const string AttributeFullName = AttributeShortName + "Attribute";
private const string AttributeSource = @"namespace QWERTYkez.Mensura;
// private const string AttributeSource = @"namespace QWERTYkez.Mensura;
[System.AttributeUsage(System.AttributeTargets.Struct | System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
internal sealed class CollectionsOperatorsGeneratorAttribute : System.Attribute { }";
//[System.AttributeUsage(System.AttributeTargets.Struct | System.AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
//internal sealed class CollectionsOperatorsGeneratorAttribute : System.Attribute { }";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(context =>
context.AddSource(
$"{AttributeFullName}.g",
SourceText.From(AttributeSource, Encoding.UTF8)));
// public void Initialize(IncrementalGeneratorInitializationContext context)
// {
// context.RegisterPostInitializationOutput(context =>
// context.AddSource(
// $"{AttributeFullName}.g",
// SourceText.From(AttributeSource, Encoding.UTF8)));
var operatorsPipeline =
context.SyntaxProvider.CreateSyntaxProvider<KeyValuePair<ClassData, ImmutableArray<Operation>>>(
(node, _) =>
{
if (node is ClassDeclarationSyntax cds)
{
SyntaxNode sn = cds;
while (sn.Parent is not null &&
sn.Parent is not FileScopedNamespaceDeclarationSyntax)
{
sn = sn.Parent;
}
if (sn.Parent is FileScopedNamespaceDeclarationSyntax
&& cds.TypeParameterList is null
&&
((cds.Members.OfType<OperatorDeclarationSyntax>()
.Where(m => m.AttributeLists.SelectMany(al => al.Attributes)
.Any(a => a.Name.GetText().ToString().Contains(AttributeShortName)))
.Any())
||
(cds.Modifiers.Any(m => m.Text == "partial")
&& cds.AttributeLists
.SelectMany(al => al.Attributes)
.Any(a => a.Name
.GetText()
.ToString()
.Contains(AttributeShortName)))))
{
return true;
}
}
return false;
},
(syntax, _) =>
{
string nameSpace;
var cds = (ClassDeclarationSyntax)syntax.Node;
{
SyntaxNode sn = cds;
while (sn.Parent is not FileScopedNamespaceDeclarationSyntax)
{
sn = sn.Parent!;
}
var nds = (FileScopedNamespaceDeclarationSyntax)sn.Parent;
nameSpace = nds.Name.ToString();
}
var Res = new StringBuilder();
{
Res.Append(cds.Modifiers);
Res.Append(" class ");
Res.Append(cds.Identifier.Text);
Res.Append(" ");
Res.Append(cds.BaseList);
Res.Append(" ");
Res.Append(cds.ConstraintClauses);
}
// var operatorsPipeline =
// context.SyntaxProvider.CreateSyntaxProvider<KeyValuePair<ClassData, ImmutableArray<Operation>>>(
// (node, _) =>
// {
// if (node is ClassDeclarationSyntax cds)
// {
// SyntaxNode sn = cds;
// while (sn.Parent is not null &&
// sn.Parent is not FileScopedNamespaceDeclarationSyntax)
// {
// sn = sn.Parent;
// }
// if (sn.Parent is FileScopedNamespaceDeclarationSyntax
// && cds.TypeParameterList is null
// &&
// ((cds.Members.OfType<OperatorDeclarationSyntax>()
// .Where(m => m.AttributeLists.SelectMany(al => al.Attributes)
// .Any(a => a.Name.GetText().ToString().Contains(AttributeShortName)))
// .Any())
// ||
// (cds.Modifiers.Any(m => m.Text == "partial")
// && cds.AttributeLists
// .SelectMany(al => al.Attributes)
// .Any(a => a.Name
// .GetText()
// .ToString()
// .Contains(AttributeShortName)))))
// {
// return true;
// }
// }
// return false;
// },
// (syntax, _) =>
// {
// string nameSpace;
// var cds = (ClassDeclarationSyntax)syntax.Node;
// {
// SyntaxNode sn = cds;
// while (sn.Parent is not FileScopedNamespaceDeclarationSyntax)
// {
// sn = sn.Parent!;
// }
// var nds = (FileScopedNamespaceDeclarationSyntax)sn.Parent;
// nameSpace = nds.Name.ToString();
// }
// var Res = new StringBuilder();
// {
// Res.Append(cds.Modifiers);
// Res.Append(" class ");
// Res.Append(cds.Identifier.Text);
// Res.Append(" ");
// Res.Append(cds.BaseList);
// Res.Append(" ");
// Res.Append(cds.ConstraintClauses);
// }
var operators = cds.Members.OfType<OperatorDeclarationSyntax>()
.Where(m => m.AttributeLists
.SelectMany(al => al.Attributes)
.Any(a => a.Name.GetText().ToString().Contains(AttributeShortName)))
.Where(mb => mb.ParameterList.Parameters.Count == 2)
.Select(mb => new Operation()
{
ReturnType = mb.ReturnType.ToString(),
OperatorToken = mb.OperatorToken.Text,
TypeA = mb.ParameterList.Parameters[0].Type!.ToString(),
TypeB = mb.ParameterList.Parameters[1].Type!.ToString(),
});
return new(new(nameSpace, cds.Identifier.Text, Res.ToString()), [.. operators]);
})
.Collect();
// var operators = cds.Members.OfType<OperatorDeclarationSyntax>()
// .Where(m => m.AttributeLists
// .SelectMany(al => al.Attributes)
// .Any(a => a.Name.GetText().ToString().Contains(AttributeShortName)))
// .Where(mb => mb.ParameterList.Parameters.Count == 2)
// .Select(mb => new Operation()
// {
// ReturnType = mb.ReturnType.ToString(),
// OperatorToken = mb.OperatorToken.Text,
// TypeA = mb.ParameterList.Parameters[0].Type!.ToString(),
// TypeB = mb.ParameterList.Parameters[1].Type!.ToString(),
// });
// return new(new(nameSpace, cds.Identifier.Text, Res.ToString()), [.. operators]);
// })
// .Collect();
context.RegisterSourceOutput(operatorsPipeline, GenerateOperators);
}
// context.RegisterSourceOutput(operatorsPipeline, GenerateOperators);
// }
readonly static string[] CollectionTypes = ["MetricArray", "MetricList", "MetricObservableCollection"];
static void GenerateOperators(SourceProductionContext context, ImmutableArray<KeyValuePair<ClassData, ImmutableArray<Operation>>> pairs)
{
foreach (var ng in pairs.GroupBy(c => c.Key.NameSpace))
{
StringBuilder document = new("namespace ");
document.Append(ng.Key);
document.Append(";");
// readonly static string[] CollectionTypes = ["MetricArray", "MetricList", "MetricObservableCollection"];
// static void GenerateOperators(SourceProductionContext context, ImmutableArray<KeyValuePair<ClassData, ImmutableArray<Operation>>> pairs)
// {
// foreach (var ng in pairs.GroupBy(c => c.Key.NameSpace))
// {
// StringBuilder document = new("namespace ");
// document.Append(ng.Key);
// document.Append(";");
var classes = ng.ToList().Select(c => (ClassData?)c.Key).ToList();
var operations = ng.ToList().SelectMany(c => c.Value).ToList();
var multiplications = operations.Where(op => op.OperatorToken == "*").ToList();
var divisions = operations.Where(op => op.OperatorToken == "/").ToList();
// var classes = ng.ToList().Select(c => (ClassData?)c.Key).ToList();
// var operations = ng.ToList().SelectMany(c => c.Value).ToList();
// var multiplications = operations.Where(op => op.OperatorToken == "*").ToList();
// var divisions = operations.Where(op => op.OperatorToken == "/").ToList();
foreach (var ops in multiplications.GroupBy(op => op.TypeA))
{
var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key);
if (Class is not null)
{
document.AppendLine();
document.AppendLine();
document.AppendLine(Class.Value.ClassHeader);
document.AppendLine("{");
foreach (var op in ops)
foreach (var ct in CollectionTypes)
{
document.AppendLine(@$"
public static {ct}<{op.ReturnType}> operator *({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left * UU);
public static {ct}<{op.ReturnType}> operator *({ct}<{op.TypeB}> left, {op.TypeA} right) => left.MetricSelect(UU => UU * right);
");
}
document.Append("}");
}
else
{
context.ReportDiagnostic(Diagnostic.Create(new(
"MSG0001",
"Need a class with an attribute",
$"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"",
"category",
DiagnosticSeverity.Error,
true), null, ops.Key));
}
}
// foreach (var ops in multiplications.GroupBy(op => op.TypeA))
// {
// var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key);
// if (Class is not null)
// {
// document.AppendLine();
// document.AppendLine();
// document.AppendLine(Class.Value.ClassHeader);
// document.AppendLine("{");
// foreach (var op in ops)
// foreach (var ct in CollectionTypes)
// {
// document.AppendLine(@$"
// public static {ct}<{op.ReturnType}> operator *({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left * UU);
// public static {ct}<{op.ReturnType}> operator *({ct}<{op.TypeB}> left, {op.TypeA} right) => left.MetricSelect(UU => UU * right);
//");
// }
// document.Append("}");
// }
// else
// {
// context.ReportDiagnostic(Diagnostic.Create(new(
// "MSG0001",
// "Need a class with an attribute",
// $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"",
// "category",
// DiagnosticSeverity.Error,
// true), null, ops.Key));
// }
// }
foreach (var ops in divisions.GroupBy(op => op.TypeA))
{
var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key);
if (Class is not null)
{
document.AppendLine();
document.AppendLine();
document.AppendLine(Class.Value.ClassHeader);
document.AppendLine("{");
foreach (var op in ops)
foreach (var ct in CollectionTypes)
{
document.AppendLine(@$"
public static {ct}<{op.ReturnType}> operator /({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left / UU);
");
}
document.Append("}");
}
else
{
context.ReportDiagnostic(Diagnostic.Create(new(
"MSG0001",
"Need a class with an attribute",
$"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"",
"category",
DiagnosticSeverity.Error,
true), null, ops.Key));
}
}
foreach (var ops in divisions.GroupBy(op => op.TypeB))
{
var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key);
if (Class is not null)
{
document.AppendLine();
document.AppendLine();
document.AppendLine(Class.Value.ClassHeader);
document.AppendLine("{");
foreach (var op in ops)
foreach (var ct in CollectionTypes)
{
document.AppendLine(@$"
public static {ct}<{op.ReturnType}> operator /({ct}<{op.TypeA}> left, {op.TypeB} right) => left.MetricSelect(UU => UU / right);
");
}
document.Append("}");
}
else
{
context.ReportDiagnostic(Diagnostic.Create(new(
"MSG0001",
"Need a class with an attribute",
$"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"",
"category",
DiagnosticSeverity.Error,
true), null, ops.Key));
}
}
// foreach (var ops in divisions.GroupBy(op => op.TypeA))
// {
// var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key);
// if (Class is not null)
// {
// document.AppendLine();
// document.AppendLine();
// document.AppendLine(Class.Value.ClassHeader);
// document.AppendLine("{");
// foreach (var op in ops)
// foreach (var ct in CollectionTypes)
// {
// document.AppendLine(@$"
// public static {ct}<{op.ReturnType}> operator /({op.TypeA} left, {ct}<{op.TypeB}> right) => right.MetricSelect(UU => left / UU);
//");
// }
// document.Append("}");
// }
// else
// {
// context.ReportDiagnostic(Diagnostic.Create(new(
// "MSG0001",
// "Need a class with an attribute",
// $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"",
// "category",
// DiagnosticSeverity.Error,
// true), null, ops.Key));
// }
// }
// foreach (var ops in divisions.GroupBy(op => op.TypeB))
// {
// var Class = classes.FirstOrDefault(cl => cl!.Value.ClassName == ops.Key);
// if (Class is not null)
// {
// document.AppendLine();
// document.AppendLine();
// document.AppendLine(Class.Value.ClassHeader);
// document.AppendLine("{");
// foreach (var op in ops)
// foreach (var ct in CollectionTypes)
// {
// document.AppendLine(@$"
// public static {ct}<{op.ReturnType}> operator /({ct}<{op.TypeA}> left, {op.TypeB} right) => left.MetricSelect(UU => UU / right);
//");
// }
// document.Append("}");
// }
// else
// {
// context.ReportDiagnostic(Diagnostic.Create(new(
// "MSG0001",
// "Need a class with an attribute",
// $"It is necessary to have a empty partial class \"{ops.Key}\" with the attribute \"{AttributeShortName}\"",
// "category",
// DiagnosticSeverity.Error,
// true), null, ops.Key));
// }
// }
context.AddSource($"operations.{ng.Key}.g", document.ToString());
}
}
}
// context.AddSource($"operations.{ng.Key}.g", document.ToString());
// }
// }
//}
public struct ClassData(string nameSpace, string className, string classHeader)
{
public string NameSpace = nameSpace;
public string ClassName = className;
public string ClassHeader = classHeader;
}
public struct Operation(string operatorToken, string typeA, string typeB, string returnType)
{
public string ReturnType = returnType;
public string OperatorToken = operatorToken;
public string TypeA = typeA;
public string TypeB = typeB;
}
//public struct ClassData(string nameSpace, string className, string classHeader)
//{
// public string NameSpace = nameSpace;
// public string ClassName = className;
// public string ClassHeader = classHeader;
//}
//public struct Operation(string operatorToken, string typeA, string typeB, string returnType)
//{
// public string ReturnType = returnType;
// public string OperatorToken = operatorToken;
// public string TypeA = typeA;
// public string TypeB = typeB;
//}