26,06,05
This commit is contained in:
@@ -1,226 +1,195 @@
|
||||
//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;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
//namespace QWERTYkez.Mensura.Generator;
|
||||
namespace G;
|
||||
|
||||
//[Generator(LanguageNames.CSharp)]
|
||||
//internal class CollectionsOperatorsGenerator : IIncrementalGenerator
|
||||
//{
|
||||
// private const string AttributeShortName = "CollectionsOperatorsGenerator";
|
||||
// private const string AttributeFullName = AttributeShortName + "Attribute";
|
||||
[Generator(LanguageNames.CSharp)]
|
||||
public 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)));
|
||||
// Какие типы коллекций поддерживаем
|
||||
private static readonly (string Type, string Selector)[] CollectionTypes =
|
||||
[
|
||||
("T[]", "array => array.Select(u => left * u).ToArray()"), // но нужно адаптировать под конкретный тип
|
||||
// Проще генерировать отдельные методы для каждого типа
|
||||
];
|
||||
|
||||
// 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);
|
||||
// }
|
||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||
{
|
||||
context.RegisterPostInitializationOutput(ctx =>
|
||||
ctx.AddSource($"{AttributeFullName}.g", SourceText.From(AttributeSource, Encoding.UTF8)));
|
||||
|
||||
// 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 operatorsPipeline = context.SyntaxProvider
|
||||
.CreateSyntaxProvider(
|
||||
predicate: static (node, _) => IsTargetType(node),
|
||||
transform: static (ctx, _) => GetTypeInfo(ctx))
|
||||
.Where(info => info.HasValue) // использование nullable value type
|
||||
.Select((info, _) => info!.Value)
|
||||
.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(";");
|
||||
private static bool IsTargetType(SyntaxNode node)
|
||||
{
|
||||
if (node is not TypeDeclarationSyntax typeDecl)
|
||||
return false;
|
||||
|
||||
// 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();
|
||||
// Должен быть partial и не generic
|
||||
if (!typeDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
|
||||
return false;
|
||||
if (typeDecl.TypeParameterList != null)
|
||||
return false;
|
||||
|
||||
// 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 attrList in typeDecl.AttributeLists)
|
||||
foreach (var attr in attrList.Attributes)
|
||||
{
|
||||
string name = attr.Name.ToString();
|
||||
if (name == AttributeShortName || name == AttributeFullName)
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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 member in typeDecl.Members)
|
||||
{
|
||||
if (member is OperatorDeclarationSyntax opDecl &&
|
||||
opDecl.AttributeLists.SelectMany(al => al.Attributes)
|
||||
.Any(a => a.Name.ToString().Contains(AttributeShortName)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// context.AddSource($"operations.{ng.Key}.g", document.ToString());
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
private static TypeInfoData? GetTypeInfo(GeneratorSyntaxContext ctx)
|
||||
{
|
||||
var typeDecl = (TypeDeclarationSyntax)ctx.Node;
|
||||
var semanticModel = ctx.SemanticModel;
|
||||
|
||||
//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;
|
||||
//}
|
||||
var symbol = semanticModel.GetDeclaredSymbol(typeDecl);
|
||||
if (symbol == null) return null;
|
||||
|
||||
string namespaceName = symbol.ContainingNamespace?.ToString() ?? "";
|
||||
|
||||
// Формируем заголовок (для отладочных целей)
|
||||
var headerBuilder = new StringBuilder();
|
||||
foreach (var modifier in typeDecl.Modifiers)
|
||||
headerBuilder.Append(modifier.Text).Append(' ');
|
||||
if (typeDecl is RecordDeclarationSyntax)
|
||||
headerBuilder.Append("record ");
|
||||
headerBuilder.Append(typeDecl.Identifier.Text);
|
||||
string header = headerBuilder.ToString();
|
||||
|
||||
// Собираем операторы
|
||||
var operators = ImmutableArray.CreateBuilder<Operation>();
|
||||
foreach (var member in typeDecl.Members)
|
||||
{
|
||||
if (member is not OperatorDeclarationSyntax opDecl) continue;
|
||||
if (opDecl.ParameterList.Parameters.Count != 2) continue;
|
||||
if (!opDecl.AttributeLists.SelectMany(al => al.Attributes)
|
||||
.Any(a => a.Name.ToString().Contains(AttributeShortName))) continue;
|
||||
|
||||
operators.Add(new Operation(
|
||||
opDecl.OperatorToken.Text,
|
||||
opDecl.ParameterList.Parameters[0].Type!.ToString(),
|
||||
opDecl.ParameterList.Parameters[1].Type!.ToString(),
|
||||
opDecl.ReturnType.ToString()
|
||||
));
|
||||
}
|
||||
|
||||
if (operators.Count == 0) return null;
|
||||
|
||||
return new TypeInfoData(namespaceName, typeDecl.Identifier.Text, header, operators.ToImmutable());
|
||||
}
|
||||
|
||||
private static void GenerateOperators(SourceProductionContext context, ImmutableArray<TypeInfoData> types)
|
||||
{
|
||||
foreach (var group in types.GroupBy(t => t.Namespace))
|
||||
{
|
||||
var document = new StringBuilder();
|
||||
document.AppendLine("namespace ").Append(group.Key).AppendLine(";");
|
||||
document.AppendLine("using System.Collections.Generic;");
|
||||
document.AppendLine("using System.Linq;");
|
||||
|
||||
var allOperators = group.SelectMany(t => t.Operators).ToList();
|
||||
var multiplications = allOperators.Where(op => op.OperatorToken == "*").ToList();
|
||||
var divisions = allOperators.Where(op => op.OperatorToken == "/").ToList();
|
||||
|
||||
// Генерация для умножения: left * collection и collection * left
|
||||
foreach (var mul in multiplications)
|
||||
{
|
||||
// left * collection
|
||||
document.AppendLine($@"
|
||||
public static {mul.ReturnType}[] operator *({mul.TypeA} left, {mul.TypeB}[] right) =>
|
||||
right.Select(u => left * u).ToArray();
|
||||
public static List<{mul.ReturnType}> operator *({mul.TypeA} left, List<{mul.TypeB}> right) =>
|
||||
right.Select(u => left * u).ToList();
|
||||
public static IEnumerable<{mul.ReturnType}> operator *({mul.TypeA} left, IEnumerable<{mul.TypeB}> right) =>
|
||||
right.Select(u => left * u);
|
||||
");
|
||||
// collection * left
|
||||
document.AppendLine($@"
|
||||
public static {mul.ReturnType}[] operator *({mul.TypeB}[] left, {mul.TypeA} right) =>
|
||||
left.Select(u => u * right).ToArray();
|
||||
public static List<{mul.ReturnType}> operator *(List<{mul.TypeB}> left, {mul.TypeA} right) =>
|
||||
left.Select(u => u * right).ToList();
|
||||
public static IEnumerable<{mul.ReturnType}> operator *(IEnumerable<{mul.TypeB}> left, {mul.TypeA} right) =>
|
||||
left.Select(u => u * right);
|
||||
");
|
||||
}
|
||||
|
||||
// Деление: left / collection (left - тип A, collection - тип B)
|
||||
foreach (var div in divisions.Where(op => op.TypeA != null)) // все деления
|
||||
{
|
||||
// left / collection
|
||||
document.AppendLine($@"
|
||||
public static {div.ReturnType}[] operator /({div.TypeA} left, {div.TypeB}[] right) =>
|
||||
right.Select(u => left / u).ToArray();
|
||||
public static List<{div.ReturnType}> operator /({div.TypeA} left, List<{div.TypeB}> right) =>
|
||||
right.Select(u => left / u).ToList();
|
||||
public static IEnumerable<{div.ReturnType}> operator /({div.TypeA} left, IEnumerable<{div.TypeB}> right) =>
|
||||
right.Select(u => left / u);
|
||||
");
|
||||
// collection / right (где right - тип B)
|
||||
document.AppendLine($@"
|
||||
public static {div.ReturnType}[] operator /({div.TypeA}[] left, {div.TypeB} right) =>
|
||||
left.Select(u => u / right).ToArray();
|
||||
public static List<{div.ReturnType}> operator /(List<{div.TypeA}> left, {div.TypeB} right) =>
|
||||
left.Select(u => u / right).ToList();
|
||||
public static IEnumerable<{div.ReturnType}> operator /(IEnumerable<{div.TypeA}> left, {div.TypeB} right) =>
|
||||
left.Select(u => u / right);
|
||||
");
|
||||
}
|
||||
|
||||
context.AddSource($"operations.{group.Key}.g", document.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct TypeInfoData(string ns, string name, string header, ImmutableArray<Operation> ops)
|
||||
{
|
||||
public string Namespace { get; } = ns;
|
||||
public string TypeName { get; } = name;
|
||||
public string Header { get; } = header;
|
||||
public ImmutableArray<Operation> Operators { get; } = ops;
|
||||
}
|
||||
|
||||
private readonly struct Operation(string token, string a, string b, string ret)
|
||||
{
|
||||
public string OperatorToken { get; } = token;
|
||||
public string TypeA { get; } = a;
|
||||
public string TypeB { get; } = b;
|
||||
public string ReturnType { get; } = ret;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user