Skip to content

Commit

Permalink
Add relations to CodeGen
Browse files Browse the repository at this point in the history
  • Loading branch information
Muximize committed Nov 13, 2023
1 parent b5be5bc commit 6557389
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 2 deletions.
84 changes: 84 additions & 0 deletions CodeGen/Generators/QuantityRelationsParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

using System.Collections.Generic;
using System.IO;
using CodeGen.JsonTypes;
using Newtonsoft.Json;

namespace CodeGen.Generators
{
internal static class QuantityRelationsParser
{
private static List<Relation> ParseRelations(string rootDir)
{
var relationsFileName = Path.Combine(rootDir, "Common/UnitRelations.json");
return JsonConvert.DeserializeObject<List<Relation>>(File.ReadAllText(relationsFileName)) ?? new List<Relation>();
}

public static void ApplyRelations(string rootDir, IEnumerable<Quantity> quantities)
{
var relations = ParseRelations(rootDir);
var timespanRelations = new List<Relation>();

foreach (var relation in relations)
{
relation.ResultQuantity = relation.Result?.Split('.')[0] ?? string.Empty;
relation.LeftQuantity = relation.Left?.Split('.')[0] ?? string.Empty;
relation.RightQuantity = relation.Right?.Split('.')[0] ?? string.Empty;

if (relation.LeftQuantity == "Duration")
{
timespanRelations.Add(relation with
{
Left = relation.Left?.Replace("Duration.", "TimeSpan.Total") ?? string.Empty,
LeftQuantity = "TimeSpan",
});
}

if (relation.RightQuantity == "Duration")
{
timespanRelations.Add(relation with
{
Right = relation.Right?.Replace("Duration.", "TimeSpan.Total") ?? string.Empty,
RightQuantity = "TimeSpan",
});
}
}

relations.AddRange(timespanRelations);

foreach (var quantity in quantities)
{
var quantityRelations = new List<Relation>();

foreach (var relation in relations)
{
if (relation.LeftQuantity == quantity.Name)
{
quantityRelations.Add(relation);
if (relation is { Operator: "*", RightQuantity: "TimeSpan" or "double" })
{
quantityRelations.Add(relation.Swapped());
}
}

if (relation.RightQuantity == quantity.Name)
{
if (relation.Operator == "inverse" || relation.Operator == "*" && relation.Left != relation.Right)
{
quantityRelations.Add(relation.Swapped());
}

if (relation is { Operator: "/", Left: "double" })
{
quantityRelations.Add(relation);
}
}
}

quantity.Relations = quantityRelations.ToArray();
}
}
}
}
115 changes: 113 additions & 2 deletions CodeGen/Generators/UnitsNetGen/QuantityGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ internal class QuantityGenerator : GeneratorBase
private readonly string _valueType;
private readonly Unit _baseUnit;

private readonly string[] _decimalTypes = { "BitRate", "Information", "Power" };

public QuantityGenerator(Quantity quantity)
{
_quantity = quantity ?? throw new ArgumentNullException(nameof(quantity));
Expand All @@ -39,8 +41,12 @@ public string Generate()
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using System.Linq;");
if (_quantity.Relations.Any(r => r.Operator is "*" or "/"))
Writer.WL(@"#if NET7_0_OR_GREATER
using System.Numerics;
#endif");
Writer.WL(@"using System.Runtime.Serialization;
using UnitsNet.InternalHelpers;
using UnitsNet.Units;
Expand All @@ -67,6 +73,35 @@ namespace UnitsNet
public readonly partial struct {_quantity.Name} :
{(_quantity.GenerateArithmetic ? "IArithmeticQuantity" : "IQuantity")}<{_quantity.Name}, {_unitEnumName}, {_quantity.ValueType}>,");

if (_quantity.Relations.Any(r => r.Operator is "*" or "/"))
{
Writer.WL(@$"
#if NET7_0_OR_GREATER");
foreach (var relation in _quantity.Relations)
{
if (relation.LeftQuantity == _quantity.Name)
{
switch (relation.Operator)
{
case "*":
Writer.W(@"
IMultiplyOperators");
break;
case "/":
Writer.W(@"
IDivisionOperators");
break;
default:
continue;
}
Writer.WL($"<{relation.LeftQuantity}, {relation.RightQuantity}, {relation.ResultQuantity}>,");
}
}

Writer.WL(@$"
#endif");
}

if (_quantity.ValueType == "decimal") Writer.WL(@$"
IDecimalQuantity,");

Expand Down Expand Up @@ -100,6 +135,7 @@ namespace UnitsNet
GenerateStaticFactoryMethods();
GenerateStaticParseMethods();
GenerateArithmeticOperators();
GenerateRelationalOperators();
GenerateEqualityAndComparison();
GenerateConversionMethods();
GenerateToString();
Expand Down Expand Up @@ -696,6 +732,81 @@ private void GenerateLogarithmicArithmeticOperators()
" );
}

private void GenerateRelationalOperators()
{
if (!_quantity.Relations.Any()) return;

Writer.WL($@"
#region Relational Operators
");

foreach (Relation relation in _quantity.Relations)
{
if (relation.Operator == "inverse")
{
Writer.WL($@"
/// <summary>Calculates the inverse of this quantity.</summary>
/// <returns>The corresponding inverse quantity, <see cref=""{relation.RightQuantity}""/>.</returns>
public {relation.RightQuantity} Inverse()
{{
return {relation.Left.Split('.')[1]} == 0.0 ? {relation.RightQuantity}.Zero : {relation.RightQuantity}.From{relation.Right.Split('.')[1]}(1 / {relation.Left.Split('.')[1]});
}}
");
}
else
{
var leftParameter = relation.LeftQuantity.ToCamelCase();
var leftConversionProperty = relation.Left.Split('.').ElementAtOrDefault(1);
var rightParameter = relation.RightQuantity.ToCamelCase();
var rightConversionProperty = relation.Right.Split('.').ElementAtOrDefault(1);

if (leftParameter == rightParameter)
{
leftParameter = "left";
rightParameter = "right";
}

var leftPart = $"{leftParameter}.{leftConversionProperty}";
var rightPart = $"{rightParameter}.{rightConversionProperty}";

if (leftParameter == "double")
{
leftParameter = "value";
leftPart = "value";
}

if (rightParameter == "double")
{
rightParameter = "value";
rightPart = "value";
}

var leftCast = _decimalTypes.Contains(relation.LeftQuantity) ? "(double)" : "";
var rightCast = _decimalTypes.Contains(relation.RightQuantity) ? "(double)" : "";

var expression = $"{leftCast}{leftPart} {relation.Operator} {rightCast}{rightPart}";

if (relation.Result is not ("double" or "decimal"))
{
expression = $"{relation.Result}({expression})";
}

Writer.WL($@"
/// <summary>Get <see cref=""{relation.ResultQuantity}""/> from <see cref=""{relation.LeftQuantity}""/> {relation.Operator} <see cref=""{relation.RightQuantity}""/>.</summary>
public static {relation.ResultQuantity} operator {relation.Operator}({relation.LeftQuantity} {leftParameter}, {relation.RightQuantity} {rightParameter})
{{
return {expression};
}}
");
}
}

Writer.WL($@"
#endregion
");
}

private void GenerateEqualityAndComparison()
{
Writer.WL($@"
Expand Down
1 change: 1 addition & 0 deletions CodeGen/JsonTypes/Quantity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal class Quantity
public int LogarithmicScalingFactor = 1;
public string Name = null!;
public Unit[] Units = Array.Empty<Unit>();
public Relation[] Relations = Array.Empty<Relation>();
public string? XmlDocRemarks;
public string XmlDocSummary = null!;
public string? ObsoleteText;
Expand Down
31 changes: 31 additions & 0 deletions CodeGen/JsonTypes/Relation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed under MIT No Attribution, see LICENSE file at the root.
// Copyright 2013 Andreas Gullberg Larsen ([email protected]). Maintained at https://github.com/angularsen/UnitsNet.

namespace CodeGen.JsonTypes
{
internal record Relation
{
// 0649 Field is never assigned to
#pragma warning disable 0649

public string Operator = null!;
public string Result = null!;
public string Left = null!;
public string Right = null!;

public string ResultQuantity = null!;
public string LeftQuantity = null!;
public string RightQuantity = null!;

public Relation Swapped() => this with
{
Left = Right,
Right = Left,
LeftQuantity = RightQuantity,
RightQuantity = LeftQuantity
};

// 0649 Field is never assigned to
#pragma warning restore 0649
}
}
2 changes: 2 additions & 0 deletions CodeGen/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public static int Main(bool verbose = false, DirectoryInfo? repositoryRoot = nul

QuantityNameToUnitEnumValues quantityNameToUnitEnumValues = UnitEnumValueAllocator.AllocateNewUnitEnumValues($"{rootDir}/Common/UnitEnumValues.g.json", quantities);

QuantityRelationsParser.ApplyRelations(rootDir, quantities);

UnitsNetGenerator.Generate(rootDir, quantities, quantityNameToUnitEnumValues);

if (updateNanoFrameworkDependencies)
Expand Down

0 comments on commit 6557389

Please sign in to comment.