From d4a29168fd0195b3f507c8ef023acc26942df0c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20R=C3=A4tzel?= Date: Sat, 17 Feb 2024 20:50:30 +0000 Subject: [PATCH] Improve value declarations in intermediate representation for readability + Improve the readability of representations of simple values and align them with more common usages: Do not use representation as a string when we can represent it as a list containing signed integers. + Avoid noise when comparing intermediate representations from multiple tests (in text diff view): Ensure deterministic ordering of the value member declarations. --- .../CSharpDeclarationOrder.cs | 128 ++++++++++++++++++ .../CompilePineToDotNet/CompileToCSharp.cs | 112 +++++++++++---- .../EncodePineExpression.cs | 2 +- 3 files changed, 217 insertions(+), 25 deletions(-) diff --git a/implement/elm-time/Pine/CompilePineToDotNet/CSharpDeclarationOrder.cs b/implement/elm-time/Pine/CompilePineToDotNet/CSharpDeclarationOrder.cs index 38d74c65..404e4b27 100644 --- a/implement/elm-time/Pine/CompilePineToDotNet/CSharpDeclarationOrder.cs +++ b/implement/elm-time/Pine/CompilePineToDotNet/CSharpDeclarationOrder.cs @@ -20,6 +20,134 @@ public static IEnumerable OrderValuesForDeclaration(IEnumerable().Concat(orderedLists); } + public class ValueSyntaxKindDeclarationOrder : IComparer + { + public int Compare(CompileToCSharp.ValueSyntaxKind? x, CompileToCSharp.ValueSyntaxKind? y) + { + if (x == y) + return 0; + + if (x is null) + return -1; + + if (y is null) + return 1; + + if (x is CompileToCSharp.ValueSyntaxKind.AsSignedInteger xSignedInt) + { + if (y is CompileToCSharp.ValueSyntaxKind.AsSignedInteger ySignedInt) + return xSignedInt.Value.CompareTo(ySignedInt.Value); + + return -1; + } + + if (y is CompileToCSharp.ValueSyntaxKind.AsSignedInteger) + return 1; + + if (x is CompileToCSharp.ValueSyntaxKind.AsListOfSignedIntegers xListOfSignedIntegers) + { + if (y is CompileToCSharp.ValueSyntaxKind.AsListOfSignedIntegers yListOfSignedIntegers) + { + if (xListOfSignedIntegers.Values.Count < yListOfSignedIntegers.Values.Count) + return -1; + + if (yListOfSignedIntegers.Values.Count < xListOfSignedIntegers.Values.Count) + return 1; + + for (var i = 0; i < xListOfSignedIntegers.Values.Count; i++) + { + if (xListOfSignedIntegers.Values[i] < yListOfSignedIntegers.Values[i]) + return -1; + + if (yListOfSignedIntegers.Values[i] < xListOfSignedIntegers.Values[i]) + return 1; + } + + return 0; + } + + return -1; + } + + if (y is CompileToCSharp.ValueSyntaxKind.AsListOfSignedIntegers) + return 1; + + if (x is CompileToCSharp.ValueSyntaxKind.AsString xString) + { + if (y is CompileToCSharp.ValueSyntaxKind.AsString yString) + return xString.Value.CompareTo(yString.Value); + + return -1; + } + + if (y is CompileToCSharp.ValueSyntaxKind.AsString) + return 1; + + return 0; + } + } + + public class ValueDeclarationOrder : IComparer + { + public int Compare(PineValue? x, PineValue? y) + { + if (x == y) + return 0; + + if (x is null) + return -1; + + if (y is null) + return 1; + + if (x is PineValue.BlobValue xBlob) + { + if (y is PineValue.BlobValue yBlob) + return new BlobValueDeclarationOrder().Compare(xBlob, yBlob); + + return -1; + } + + if (y is PineValue.BlobValue) + return 1; + + if (x is PineValue.ListValue xList) + { + if (y is PineValue.ListValue yList) + { + var xSize = AggregateSizeIncludingDescendants(xList); + var ySize = AggregateSizeIncludingDescendants(yList); + + if (xSize < ySize) + return -1; + + if (xSize > ySize) + return 1; + + if (xList.Elements.Count < yList.Elements.Count) + return -1; + + if (yList.Elements.Count < xList.Elements.Count) + return 1; + + for (var i = 0; i < xList.Elements.Count; i++) + { + var itemComparison = Compare(xList.Elements[i], yList.Elements[i]); + + if (itemComparison != 0) + return itemComparison; + } + + return 0; + } + + return -1; + } + + return 0; + } + } + public class BlobValueDeclarationOrder : IComparer { public int Compare(PineValue.BlobValue? x, PineValue.BlobValue? y) diff --git a/implement/elm-time/Pine/CompilePineToDotNet/CompileToCSharp.cs b/implement/elm-time/Pine/CompilePineToDotNet/CompileToCSharp.cs index 2c39d328..4d352a7c 100644 --- a/implement/elm-time/Pine/CompilePineToDotNet/CompileToCSharp.cs +++ b/implement/elm-time/Pine/CompilePineToDotNet/CompileToCSharp.cs @@ -214,7 +214,7 @@ void registerValueUsagesRecursive(PineValue pineValue) ? SyntaxFactory.IdentifierName(DeclarationNameForValue(pineValue)) : null; - (string memberName, TypeSyntax typeSyntax, ExpressionSyntax memberDeclaration) + ((string memberName, TypeSyntax typeSyntax, ExpressionSyntax memberDeclaration) commonProps, ValueSyntaxKind syntaxKind) memberDeclarationForValue(PineValue pineValue) { var valueExpression = CompileToCSharpLiteralExpression(pineValue, specialSyntaxForPineValue); @@ -222,9 +222,10 @@ void registerValueUsagesRecursive(PineValue pineValue) var memberName = DeclarationNameForValue(pineValue); return - (memberName, + ((memberName, SyntaxFactory.IdentifierName("PineValue"), - valueExpression); + valueExpression.exprSyntax), + valueExpression.syntaxKind); } (string memberName, TypeSyntax typeSyntax, ExpressionSyntax memberDeclaration) @@ -248,7 +249,10 @@ void registerValueUsagesRecursive(PineValue pineValue) var valuesStaticMembers = valuesToDeclare - .Select(memberDeclarationForValue) + .Select(valueToInclude => (valueToInclude, decl: memberDeclarationForValue(valueToInclude))) + .OrderBy(valueAndMember => valueAndMember.decl.syntaxKind, new CSharpDeclarationOrder.ValueSyntaxKindDeclarationOrder()) + .ThenBy(valueAndMember => valueAndMember.valueToInclude, new CSharpDeclarationOrder.ValueDeclarationOrder()) + .Select(valueAndMember => valueAndMember.decl.commonProps) .ToImmutableList(); var expressionStaticMembers = @@ -1190,40 +1194,96 @@ public static Result CompileToCSharpExpression( SyntaxFactory.TriviaList()))); } - public static ExpressionSyntax CompileToCSharpLiteralExpression( + public abstract record ValueSyntaxKind + { + public record AsSignedInteger(long Value) + : ValueSyntaxKind; + + public record AsListOfSignedIntegers(IReadOnlyList Values) + : ValueSyntaxKind; + + public record AsString(string Value) + : ValueSyntaxKind; + + public record Other + : ValueSyntaxKind; + } + + public static (ExpressionSyntax exprSyntax, ValueSyntaxKind syntaxKind) CompileToCSharpLiteralExpression( PineValue pineValue, Func overrideDefaultExpression) { - ExpressionSyntax continueCompile(PineValue pineValue) => - overrideDefaultExpression(pineValue) ?? + (ExpressionSyntax, ValueSyntaxKind) continueCompile(PineValue pineValue) => + overrideDefaultExpression(pineValue) is { } fromOverride ? + (fromOverride, new ValueSyntaxKind.Other()) + : CompileToCSharpLiteralExpression(pineValue, overrideDefaultExpression); if (pineValue == PineValue.EmptyList) - return PineCSharpSyntaxFactory.PineValueEmptyListSyntax; + return (PineCSharpSyntaxFactory.PineValueEmptyListSyntax, new ValueSyntaxKind.Other()); - if (PineValueAsInteger.SignedIntegerFromValue(pineValue) is Result.Ok okInteger && - PineValueAsInteger.ValueFromSignedInteger(okInteger.Value) == pineValue) + static long? attemptMapToSignedInteger(PineValue pineValue) { - if (okInteger.Value < long.MaxValue) - { - return + if (PineValueAsInteger.SignedIntegerFromValue(pineValue) is Result.Ok okInteger && + PineValueAsInteger.ValueFromSignedInteger(okInteger.Value) == pineValue && + okInteger.Value < long.MaxValue && long.MinValue < okInteger.Value) + return (long)okInteger.Value; + + return null; + } + + static ExpressionSyntax ExpressionSyntaxForSignedInt(long asInt64) + { + return SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(nameof(PineValueAsInteger)), SyntaxFactory.IdentifierName(nameof(PineValueAsInteger.ValueFromSignedInteger)))) .WithArgumentList( - SyntaxFactory.ArgumentList( - SyntaxFactory.SingletonSeparatedList( - SyntaxFactory.Argument( - PineCSharpSyntaxFactory.ExpressionSyntaxForIntegerLiteral((long)okInteger.Value))))); + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + PineCSharpSyntaxFactory.ExpressionSyntaxForIntegerLiteral(asInt64))))); + } + + if (attemptMapToSignedInteger(pineValue) is { } asInt64) + { + return (ExpressionSyntaxForSignedInt(asInt64), new ValueSyntaxKind.AsSignedInteger(asInt64)); + } + + if (pineValue is PineValue.ListValue list) + { + var asIntegers = + list.Elements + .Select(attemptMapToSignedInteger) + .WhereHasValue() + .ToImmutableArray(); + + if (asIntegers.Length == list.Elements.Count) + { + return + (SyntaxFactory.InvocationExpression( + SyntaxFactory.MemberAccessExpression( + SyntaxKind.SimpleMemberAccessExpression, + SyntaxFactory.IdentifierName("PineValue"), + SyntaxFactory.IdentifierName("List"))) + .WithArgumentList( + SyntaxFactory.ArgumentList( + SyntaxFactory.SingletonSeparatedList( + SyntaxFactory.Argument( + SyntaxFactory.CollectionExpression( + SyntaxFactory.SeparatedList( + asIntegers + .Select(item => SyntaxFactory.ExpressionElement(ExpressionSyntaxForSignedInt(item))))))))), + new ValueSyntaxKind.AsListOfSignedIntegers(asIntegers)); } } if (PineValueAsString.StringFromValue(pineValue) is Result.Ok okString) { return - SyntaxFactory.InvocationExpression( + (SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(nameof(PineValueAsString)), @@ -1234,7 +1294,9 @@ ExpressionSyntax continueCompile(PineValue pineValue) => SyntaxFactory.Argument( SyntaxFactory.LiteralExpression( SyntaxKind.StringLiteralExpression, - SyntaxFactory.Literal(okString.Value)))))); + SyntaxFactory.Literal(okString.Value)))))), + new ValueSyntaxKind.AsString(okString.Value)); + } ExpressionSyntax defaultRepresentationOfBlob(ReadOnlyMemory blob) @@ -1270,8 +1332,8 @@ ExpressionSyntax defaultRepresentationOfBlob(ReadOnlyMemory blob) ExpressionSyntax defaultRepresentationOfList(IReadOnlyList list) { - var elementsSyntaxes = - list.Select(continueCompile).ToImmutableList(); + var itemSyntaxes = + list.Select(item => continueCompile(item).Item1).ToImmutableList(); return SyntaxFactory.InvocationExpression( @@ -1285,7 +1347,7 @@ ExpressionSyntax defaultRepresentationOfList(IReadOnlyList list) SyntaxFactory.Argument( SyntaxFactory.CollectionExpression( SyntaxFactory.SeparatedList( - elementsSyntaxes + itemSyntaxes .Select(SyntaxFactory.ExpressionElement))) )))); } @@ -1293,10 +1355,12 @@ ExpressionSyntax defaultRepresentationOfList(IReadOnlyList list) return pineValue switch { PineValue.BlobValue blobValue => - defaultRepresentationOfBlob(blobValue.Bytes), + (defaultRepresentationOfBlob(blobValue.Bytes), + new ValueSyntaxKind.Other()), PineValue.ListValue listValue => - defaultRepresentationOfList(listValue.Elements), + (defaultRepresentationOfList(listValue.Elements), + new ValueSyntaxKind.Other()), _ => throw new Exception("Unknown value type: " + pineValue.GetType().FullName) diff --git a/implement/elm-time/Pine/CompilePineToDotNet/EncodePineExpression.cs b/implement/elm-time/Pine/CompilePineToDotNet/EncodePineExpression.cs index 73c70cad..d5f044c1 100644 --- a/implement/elm-time/Pine/CompilePineToDotNet/EncodePineExpression.cs +++ b/implement/elm-time/Pine/CompilePineToDotNet/EncodePineExpression.cs @@ -22,7 +22,7 @@ private static Result EncodePineExpressionAsCSharpExpr Result.ok( NewConstructorOfExpressionVariant( nameof(Expression.LiteralExpression), - CompileToCSharpLiteralExpression(literal.Value, overrideDefaultExpressionForValue))), + CompileToCSharpLiteralExpression(literal.Value, overrideDefaultExpressionForValue).exprSyntax)), Expression.EnvironmentExpression => Result.ok(