300 lines
16 KiB
C#
300 lines
16 KiB
C#
using Microsoft.CodeAnalysis;
|
||
using Microsoft.CodeAnalysis.CSharp;
|
||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||
using Microsoft.CodeAnalysis.Text;
|
||
using System.Text;
|
||
|
||
namespace QWERTYkez.Mensura.Generator;
|
||
|
||
[Generator]
|
||
public class UnitOperatorsGenerator : IIncrementalGenerator
|
||
{
|
||
private const string AttributeName = "UnitOperatorsGenerator";
|
||
|
||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||
{
|
||
// Генерируем атрибут
|
||
context.RegisterPostInitializationOutput(ctx =>
|
||
{
|
||
string attributeSource = @"
|
||
namespace QWERTYkez.Mensura
|
||
{
|
||
[System.AttributeUsage(System.AttributeTargets.Struct, AllowMultiple = false)]
|
||
public sealed class UnitOperatorsGeneratorAttribute : System.Attribute { }
|
||
}";
|
||
ctx.AddSource("UnitOperatorsGeneratorAttribute.g.cs", SourceText.From(attributeSource, Encoding.UTF8));
|
||
});
|
||
|
||
// Ищем все readonly partial record struct с атрибутом
|
||
var structsProvider = context.SyntaxProvider
|
||
.CreateSyntaxProvider(
|
||
predicate: static (node, _) => IsTargetStruct(node),
|
||
transform: static (ctx, _) => GetStructInfo(ctx))
|
||
.Where(info => info.HasValue)
|
||
.Select((info, _) => info!.Value)
|
||
.Collect();
|
||
|
||
context.RegisterSourceOutput(structsProvider, (spc, structs) =>
|
||
{
|
||
foreach (var structInfo in structs)
|
||
{
|
||
string generatedCode = GeneratePartial(structInfo);
|
||
spc.AddSource($"{structInfo.TypeName}.Generated.g.cs", SourceText.From(generatedCode, Encoding.UTF8));
|
||
}
|
||
});
|
||
}
|
||
|
||
private static bool IsTargetStruct(SyntaxNode node)
|
||
{
|
||
if (node is not RecordDeclarationSyntax record)
|
||
return false;
|
||
|
||
// Должен быть record struct с модификаторами readonly и partial
|
||
if (!record.Modifiers.Any(SyntaxKind.ReadOnlyKeyword) ||
|
||
!record.Modifiers.Any(SyntaxKind.PartialKeyword) ||
|
||
!record.Keyword.IsKind(SyntaxKind.RecordKeyword) ||
|
||
!record.ClassOrStructKeyword.IsKind(SyntaxKind.StructKeyword))
|
||
return false;
|
||
|
||
// Проверяем наличие атрибута [UnitOperatorsGenerator]
|
||
foreach (var attrList in record.AttributeLists)
|
||
foreach (var attr in attrList.Attributes)
|
||
{
|
||
string name = attr.Name.ToString();
|
||
if (name == AttributeName || name == AttributeName + "Attribute")
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
private static StructInfo? GetStructInfo(GeneratorSyntaxContext context)
|
||
{
|
||
var record = (RecordDeclarationSyntax)context.Node;
|
||
var semanticModel = context.SemanticModel;
|
||
if (semanticModel.GetDeclaredSymbol(record) is not INamedTypeSymbol typeSymbol)
|
||
return null;
|
||
|
||
string namespaceName = typeSymbol.ContainingNamespace?.ToString();
|
||
if (string.IsNullOrEmpty(namespaceName))
|
||
return null;
|
||
|
||
return new StructInfo(namespaceName, typeSymbol.Name);
|
||
}
|
||
|
||
private static string GeneratePartial(StructInfo info)
|
||
{
|
||
string typeName = info.TypeName;
|
||
string ns = info.Namespace;
|
||
|
||
// Здесь должен быть полный код из вашего файла XXXXXXXXXXXX.cs
|
||
// с заменой {typeName} на {typeName}. Для краткости приведён скелет.
|
||
// Вы должны скопировать сюда всё содержимое вашего второго файла,
|
||
// заменив {typeName} на {typeName}.
|
||
string skeleton = @"
|
||
global using {typeName}Extensions = QWERTYkez.Mensura.Units.{typeName}Extensions;
|
||
global using {typeName} = QWERTYkez.Mensura.Units.{typeName};
|
||
|
||
using System.Runtime.Serialization;
|
||
|
||
namespace QWERTYkez.Mensura.Units;
|
||
|
||
public class {typeName}Converter : UnitJsonConverter<{typeName}> { }
|
||
|
||
[JsonConverter(typeof({typeName}Converter))]
|
||
public readonly partial record struct {typeName} : IMensuraUnit<{typeName}>, IEquatable<{typeName}>, IMensuraUnit
|
||
{
|
||
[JsonInclude, DataMember, JsonPropertyName(""v""), Obsolete] // для JSON / EF на случай сбоев, если пробелма с _Value
|
||
internal double Value { get => _Value; init => _Value = value; }
|
||
internal readonly double _Value;
|
||
internal {typeName}(double value) => _Value = value;
|
||
|
||
public override int GetHashCode() => _Value.GetHashCode();
|
||
public int CompareTo({typeName}? other) => _Value.CompareTo(other is null ? 0d : other.Value._Value);
|
||
public int CompareTo({typeName} other) => _Value.CompareTo(other._Value);
|
||
|
||
public bool Equals({typeName}? other) => _Value.Equals(other?._Value);
|
||
|
||
|
||
[JsonIgnore, IgnoreDataMember] public bool IsPositive => _Value >= 0;
|
||
[JsonIgnore, IgnoreDataMember] public bool IsGreaterThanZero => _Value > 0;
|
||
[JsonIgnore, IgnoreDataMember] public bool IsNegative => double.IsNegative(_Value);
|
||
[JsonIgnore, IgnoreDataMember] public bool IsZero => _Value == 0;
|
||
[JsonIgnore, IgnoreDataMember] public bool IsNaN => double.IsNaN(_Value);
|
||
[JsonIgnore, IgnoreDataMember] public bool IsFinite => double.IsFinite(_Value);
|
||
[JsonIgnore, IgnoreDataMember] public bool IsInfinity => double.IsInfinity(_Value);
|
||
[JsonIgnore, IgnoreDataMember] public bool IsPositiveInfinity => double.IsPositiveInfinity(_Value);
|
||
[JsonIgnore, IgnoreDataMember] public bool IsNegativeInfinity => double.IsNegativeInfinity(_Value);
|
||
|
||
|
||
public static {typeName} Zero { get; } = new(0d);
|
||
public static {typeName} Min { get; } = new(double.MinValue);
|
||
public static {typeName} Max { get; } = new(double.MaxValue);
|
||
public static {typeName} NegativeInfinity { get; } = new(double.NegativeInfinity);
|
||
public static {typeName} PositiveInfinity { get; } = new(double.PositiveInfinity);
|
||
|
||
public static bool operator ==({typeName}? T1, {typeName}? T2) => T1.Protected() == T2.Protected();
|
||
public static bool operator !=({typeName}? T1, {typeName}? T2) => T1.Protected() != T2.Protected();
|
||
|
||
public static bool operator <({typeName}? T1, {typeName}? T2) => T1.Protected() < T2.Protected();
|
||
public static bool operator <=({typeName}? T1, {typeName}? T2) => T1.Protected() <= T2.Protected();
|
||
public static bool operator >({typeName}? T1, {typeName}? T2) => T1.Protected() > T2.Protected();
|
||
public static bool operator >=({typeName}? T1, {typeName}? T2) => T1.Protected() >= T2.Protected();
|
||
|
||
|
||
public static {typeName} operator +({typeName} T2) => new(+T2._Value);
|
||
public static {typeName} operator +({typeName} T1, {typeName} T2) => new(T1._Value + T2._Value);
|
||
public static {typeName} operator -({typeName} T2) => new(-T2._Value);
|
||
public static {typeName} operator -({typeName} T1, {typeName} T2) => new(T1._Value - T2._Value);
|
||
|
||
|
||
// double
|
||
public static {typeName} operator *({typeName} T1, double T2) => new(T1._Value * T2);
|
||
public static {typeName} operator *({typeName} T1, double? T2) => T1 * (T2 ?? 0d);
|
||
public static {typeName} operator *(double T1, {typeName} T2) => new(T1 * T2._Value);
|
||
public static {typeName} operator *(double? T1, {typeName} T2) => (T1 ?? 0d) * T2;
|
||
public static {typeName} operator /({typeName} T1, double T2) => new(T1._Value / T2);
|
||
public static {typeName} operator /({typeName} T1, double? T2) => T1 / (T2 ?? 0d);
|
||
public static double operator /({typeName} T1, {typeName} T2) => T1._Value / T2._Value;
|
||
|
||
// sbyte
|
||
public static {typeName} operator *({typeName} T1, sbyte T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, sbyte? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(sbyte T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(sbyte? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, sbyte T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, sbyte? T2) => T1 / T2.ToDouble();
|
||
|
||
// short
|
||
public static {typeName} operator *({typeName} T1, short T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, short? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(short T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(short? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, short T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, short? T2) => T1 / T2.ToDouble();
|
||
|
||
// int
|
||
public static {typeName} operator *({typeName} T1, int T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, int? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(int T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(int? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, int T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, int? T2) => T1 / T2.ToDouble();
|
||
|
||
// long
|
||
public static {typeName} operator *({typeName} T1, long T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, long? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(long T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(long? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, long T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, long? T2) => T1 / T2.ToDouble();
|
||
|
||
// byte
|
||
public static {typeName} operator *({typeName} T1, byte T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, byte? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(byte T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(byte? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, byte T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, byte? T2) => T1 / T2.ToDouble();
|
||
|
||
// ushort
|
||
public static {typeName} operator *({typeName} T1, ushort T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, ushort? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(ushort T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(ushort? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, ushort T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, ushort? T2) => T1 / T2.ToDouble();
|
||
|
||
// uint
|
||
public static {typeName} operator *({typeName} T1, uint T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, uint? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(uint T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(uint? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, uint T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, uint? T2) => T1 / T2.ToDouble();
|
||
|
||
// ulong
|
||
public static {typeName} operator *({typeName} T1, ulong T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, ulong? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(ulong T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(ulong? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, ulong T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, ulong? T2) => T1 / T2.ToDouble();
|
||
|
||
// nint
|
||
public static {typeName} operator *({typeName} T1, nint T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, nint? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(nint T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(nint? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, nint T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, nint? T2) => T1 / T2.ToDouble();
|
||
|
||
// nuint
|
||
public static {typeName} operator *({typeName} T1, nuint T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, nuint? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(nuint T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(nuint? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, nuint T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, nuint? T2) => T1 / T2.ToDouble();
|
||
|
||
// float
|
||
public static {typeName} operator *({typeName} T1, float T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, float? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(float T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(float? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, float T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, float? T2) => T1 / T2.ToDouble();
|
||
|
||
// decimal
|
||
public static {typeName} operator *({typeName} T1, decimal T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, decimal? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(decimal T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(decimal? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, decimal T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, decimal? T2) => T1 / T2.ToDouble();
|
||
|
||
#if NET7_0_OR_GREATER
|
||
// Int128
|
||
public static {typeName} operator *({typeName} T1, Int128 T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, Int128? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(Int128 T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(Int128? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, Int128 T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, Int128? T2) => T1 / T2.ToDouble();
|
||
|
||
// UInt128
|
||
public static {typeName} operator *({typeName} T1, UInt128 T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *({typeName} T1, UInt128? T2) => T1 * T2.ToDouble();
|
||
public static {typeName} operator *(UInt128 T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator *(UInt128? T1, {typeName} T2) => T1.ToDouble() * T2;
|
||
public static {typeName} operator /({typeName} T1, UInt128 T2) => T1 / T2.ToDouble();
|
||
public static {typeName} operator /({typeName} T1, UInt128? T2) => T1 / T2.ToDouble();
|
||
#endif
|
||
|
||
public static explicit operator {typeName}(double val) => Unsafe.As<double, {typeName}>(ref val);
|
||
public static explicit operator double({typeName} unit) => unit._Value;
|
||
|
||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
public {typeName} Abs() => new(Math.Abs(_Value));
|
||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
internal R Pow2_Internal<R>() where R : struct, IMensuraUnit, IEquatable<R> => (_Value * _Value).ToUnit<R>();
|
||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
internal R Sqrt_Internal<R>() where R : struct, IMensuraUnit, IEquatable<R> => Math.Sqrt(_Value).ToUnit<R>();
|
||
}
|
||
|
||
public static class {typeName}Extensions
|
||
{
|
||
public static double Protected(this {typeName}? unit) => unit is null ? 0d : unit.Value._Value;
|
||
}
|
||
";
|
||
return skeleton.Replace("{typeName}", typeName).Replace("{ns}", ns);
|
||
}
|
||
|
||
private readonly struct StructInfo(string ns, string name)
|
||
{
|
||
public string Namespace { get; } = ns;
|
||
public string TypeName { get; } = name;
|
||
}
|
||
} |