Skip to content

Commit

Permalink
Improve value declarations in intermediate representation for readabi…
Browse files Browse the repository at this point in the history
…lity

+ 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.
  • Loading branch information
Viir committed Feb 17, 2024
1 parent 14ca6a5 commit d4a2916
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 25 deletions.
128 changes: 128 additions & 0 deletions implement/elm-time/Pine/CompilePineToDotNet/CSharpDeclarationOrder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,134 @@ public static IEnumerable<PineValue> OrderValuesForDeclaration(IEnumerable<PineV
return blobs.Cast<PineValue>().Concat(orderedLists);
}

public class ValueSyntaxKindDeclarationOrder : IComparer<CompileToCSharp.ValueSyntaxKind>
{
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<PineValue>
{
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<PineValue.BlobValue>
{
public int Compare(PineValue.BlobValue? x, PineValue.BlobValue? y)
Expand Down
112 changes: 88 additions & 24 deletions implement/elm-time/Pine/CompilePineToDotNet/CompileToCSharp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,17 +214,18 @@ 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);

var memberName = DeclarationNameForValue(pineValue);

return
(memberName,
((memberName,
SyntaxFactory.IdentifierName("PineValue"),
valueExpression);
valueExpression.exprSyntax),
valueExpression.syntaxKind);
}

(string memberName, TypeSyntax typeSyntax, ExpressionSyntax memberDeclaration)
Expand All @@ -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 =
Expand Down Expand Up @@ -1190,40 +1194,96 @@ public static Result<string, CompiledExpression> CompileToCSharpExpression(
SyntaxFactory.TriviaList())));
}

public static ExpressionSyntax CompileToCSharpLiteralExpression(
public abstract record ValueSyntaxKind
{
public record AsSignedInteger(long Value)
: ValueSyntaxKind;

public record AsListOfSignedIntegers(IReadOnlyList<long> Values)
: ValueSyntaxKind;

public record AsString(string Value)
: ValueSyntaxKind;

public record Other
: ValueSyntaxKind;
}

public static (ExpressionSyntax exprSyntax, ValueSyntaxKind syntaxKind) CompileToCSharpLiteralExpression(
PineValue pineValue,
Func<PineValue, ExpressionSyntax?> 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<string, BigInteger>.Ok okInteger &&
PineValueAsInteger.ValueFromSignedInteger(okInteger.Value) == pineValue)
static long? attemptMapToSignedInteger(PineValue pineValue)
{
if (okInteger.Value < long.MaxValue)
{
return
if (PineValueAsInteger.SignedIntegerFromValue(pineValue) is Result<string, BigInteger>.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<CollectionElementSyntax>(
asIntegers
.Select(item => SyntaxFactory.ExpressionElement(ExpressionSyntaxForSignedInt(item))))))))),
new ValueSyntaxKind.AsListOfSignedIntegers(asIntegers));
}
}

if (PineValueAsString.StringFromValue(pineValue) is Result<string, string>.Ok okString)
{
return
SyntaxFactory.InvocationExpression(
(SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(nameof(PineValueAsString)),
Expand All @@ -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<byte> blob)
Expand Down Expand Up @@ -1270,8 +1332,8 @@ ExpressionSyntax defaultRepresentationOfBlob(ReadOnlyMemory<byte> blob)

ExpressionSyntax defaultRepresentationOfList(IReadOnlyList<PineValue> list)
{
var elementsSyntaxes =
list.Select(continueCompile).ToImmutableList();
var itemSyntaxes =
list.Select(item => continueCompile(item).Item1).ToImmutableList();

return
SyntaxFactory.InvocationExpression(
Expand All @@ -1285,18 +1347,20 @@ ExpressionSyntax defaultRepresentationOfList(IReadOnlyList<PineValue> list)
SyntaxFactory.Argument(
SyntaxFactory.CollectionExpression(
SyntaxFactory.SeparatedList<CollectionElementSyntax>(
elementsSyntaxes
itemSyntaxes
.Select(SyntaxFactory.ExpressionElement)))
))));
}

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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private static Result<string, ExpressionSyntax> EncodePineExpressionAsCSharpExpr
Result<string, ExpressionSyntax>.ok(
NewConstructorOfExpressionVariant(
nameof(Expression.LiteralExpression),
CompileToCSharpLiteralExpression(literal.Value, overrideDefaultExpressionForValue))),
CompileToCSharpLiteralExpression(literal.Value, overrideDefaultExpressionForValue).exprSyntax)),

Expression.EnvironmentExpression =>
Result<string, ExpressionSyntax>.ok(
Expand Down

0 comments on commit d4a2916

Please sign in to comment.