Skip to content

Commit

Permalink
feat: Support ternary operator and relational operators in Expression…
Browse files Browse the repository at this point in the history
…Animation
  • Loading branch information
Youssef1313 committed Mar 4, 2024
1 parent a1a6402 commit 927fed4
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,58 @@ public override object Evaluate(ExpressionAnimation expressionAnimation)
_ => throw new ArgumentException($"Cannot evaluate binary / between types '{leftValue.GetType()}' and '{rightValue.GetType()}'.")
};
}
else if (_operatorToken.Kind == ExpressionAnimationTokenKind.GreaterThanToken)
{
return (leftValue, rightValue) switch
{
(float leftFloat, float rightFloat) => leftFloat > rightFloat,
(byte leftByte, byte rightByte) => leftByte > rightByte,
(short leftShort, short rightShort) => leftShort > rightShort,
(int leftInt, int rightInt) => leftInt > rightInt,
(int leftInt, float rightFloat) => leftInt > rightFloat,
(float leftFloat, int rightInt) => leftFloat > rightInt,
_ => throw new ArgumentException($"Cannot evaluate binary > between types '{leftValue.GetType()}' and '{rightValue.GetType()}'.")
};
}
else if (_operatorToken.Kind == ExpressionAnimationTokenKind.GreaterThanEqualsToken)
{
return (leftValue, rightValue) switch
{
(float leftFloat, float rightFloat) => leftFloat >= rightFloat,
(byte leftByte, byte rightByte) => leftByte >= rightByte,
(short leftShort, short rightShort) => leftShort >= rightShort,
(int leftInt, int rightInt) => leftInt >= rightInt,
(int leftInt, float rightFloat) => leftInt >= rightFloat,
(float leftFloat, int rightInt) => leftFloat >= rightInt,
_ => throw new ArgumentException($"Cannot evaluate binary >= between types '{leftValue.GetType()}' and '{rightValue.GetType()}'.")
};
}
else if (_operatorToken.Kind == ExpressionAnimationTokenKind.LessThanToken)
{
return (leftValue, rightValue) switch
{
(float leftFloat, float rightFloat) => leftFloat < rightFloat,
(byte leftByte, byte rightByte) => leftByte < rightByte,
(short leftShort, short rightShort) => leftShort < rightShort,
(int leftInt, int rightInt) => leftInt < rightInt,
(int leftInt, float rightFloat) => leftInt < rightFloat,
(float leftFloat, int rightInt) => leftFloat < rightInt,
_ => throw new ArgumentException($"Cannot evaluate binary < between types '{leftValue.GetType()}' and '{rightValue.GetType()}'.")
};
}
else if (_operatorToken.Kind == ExpressionAnimationTokenKind.LessThanEqualsToken)
{
return (leftValue, rightValue) switch
{
(float leftFloat, float rightFloat) => leftFloat <= rightFloat,
(byte leftByte, byte rightByte) => leftByte <= rightByte,
(short leftShort, short rightShort) => leftShort <= rightShort,
(int leftInt, int rightInt) => leftInt <= rightInt,
(int leftInt, float rightFloat) => leftInt <= rightFloat,
(float leftFloat, int rightInt) => leftFloat <= rightInt,
_ => throw new ArgumentException($"Cannot evaluate binary <= between types '{leftValue.GetType()}' and '{rightValue.GetType()}'.")
};
}

throw new ArgumentException($"Unable to binary expression for operator '{_operatorToken.Kind}'.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,29 @@ public override object Evaluate(ExpressionAnimation expressionAnimation)
{
value.AddContext(expressionAnimation, null);
_result = value;
return value;
}

if (expressionAnimation.ScalarParameters.TryGetValue(identifierValue, out var scalarValue))
else if (expressionAnimation.ScalarParameters.TryGetValue(identifierValue, out var scalarValue))
{
_result = scalarValue;
return scalarValue;
}

if (identifierValue.Equals("Pi", StringComparison.Ordinal))
else if (identifierValue.Equals("Pi", StringComparison.OrdinalIgnoreCase))
{
_result = (float)Math.PI;
}
else if (identifierValue.Equals("True", StringComparison.OrdinalIgnoreCase))
{
_result = true;
}
else if (identifierValue.Equals("False", StringComparison.OrdinalIgnoreCase))
{
_result = false;
}
else
{
_result = Math.PI;
return Math.PI;
throw new ArgumentException($"Unrecognized identifier '{Identifier.Value}'.");
}

throw new ArgumentException($"Unrecognized identifier '{Identifier.Value}'.");
return _result;
}

public override void Dispose()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using System.Numerics;

namespace Microsoft.UI.Composition;

internal sealed class AnimationTernaryExpressionSyntax : AnimationExpressionSyntax
{
private readonly AnimationExpressionSyntax _condition;
private readonly AnimationExpressionSyntax _whenTrue;
private readonly AnimationExpressionSyntax _whenFalse;

public AnimationTernaryExpressionSyntax(AnimationExpressionSyntax condition, AnimationExpressionSyntax whenTrue, AnimationExpressionSyntax whenFalse)
{
_condition = condition;
_whenTrue = whenTrue;
_whenFalse = whenFalse;
}

public override object Evaluate(ExpressionAnimation expressionAnimation)
{
var value = _condition.Evaluate(expressionAnimation);
if (value is not bool valueBool)
{
throw new Exception($"Ternary expression condition evaluated to '{value}'. It must evaluate to a bool value.");
}

if (valueBool)
{
return _whenTrue.Evaluate(expressionAnimation);
}

return _whenFalse.Evaluate(expressionAnimation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ internal sealed class ExpressionAnimationLexer
[','] = ExpressionAnimationTokenKind.CommaToken,
['('] = ExpressionAnimationTokenKind.OpenParenToken,
[')'] = ExpressionAnimationTokenKind.CloseParenToken,
['?'] = ExpressionAnimationTokenKind.QuestionMarkToken,
[':'] = ExpressionAnimationTokenKind.ColonToken,
};

public ExpressionAnimationLexer(string text)
Expand Down Expand Up @@ -85,6 +87,30 @@ private char Peek(int i)
return new ExpressionAnimationToken(kind, null);
}

if (Current == '>')
{
_position++;
if (Current == '=')
{
_position++;
return new ExpressionAnimationToken(ExpressionAnimationTokenKind.GreaterThanEqualsToken, null);
}

return new ExpressionAnimationToken(ExpressionAnimationTokenKind.GreaterThanToken, null);
}

if (Current == '<')
{
_position++;
if (Current == '=')
{
_position++;
return new ExpressionAnimationToken(ExpressionAnimationTokenKind.LessThanEqualsToken, null);
}

return new ExpressionAnimationToken(ExpressionAnimationTokenKind.LessThanToken, null);
}

if (char.IsLetter(Current))
{
int start = _position;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ internal static int GetBinaryPrecedence(ExpressionAnimationToken token)
{
return token.Kind switch
{
ExpressionAnimationTokenKind.PlusToken or ExpressionAnimationTokenKind.MinusToken => 1,
ExpressionAnimationTokenKind.MultiplyToken or ExpressionAnimationTokenKind.DivisionToken => 2,
ExpressionAnimationTokenKind.QuestionMarkToken => 1,
ExpressionAnimationTokenKind.LessThanToken or ExpressionAnimationTokenKind.LessThanEqualsToken or ExpressionAnimationTokenKind.GreaterThanToken or ExpressionAnimationTokenKind.GreaterThanToken => 2,
ExpressionAnimationTokenKind.PlusToken or ExpressionAnimationTokenKind.MinusToken => 3,
ExpressionAnimationTokenKind.MultiplyToken or ExpressionAnimationTokenKind.DivisionToken => 4,
_ => 0
};
}
Expand All @@ -59,7 +61,7 @@ internal static int GetUnaryPrecedence(ExpressionAnimationToken token)
{
return token.Kind switch
{
ExpressionAnimationTokenKind.PlusToken or ExpressionAnimationTokenKind.MinusToken => 3,
ExpressionAnimationTokenKind.PlusToken or ExpressionAnimationTokenKind.MinusToken => 5,
_ => 0
};
}
Expand Down Expand Up @@ -88,9 +90,20 @@ private AnimationExpressionSyntax ParseExpression(int parentPrecedence = 0)
break;
}

var operatorToken = NextToken();
var right = ParseExpression(precedence);
left = new AnimationBinaryExpressionSyntax(left, operatorToken, right);
if (HasCurrent && Current.Kind == ExpressionAnimationTokenKind.QuestionMarkToken)
{
_ = NextToken();
var whenTrue = ParseExpression();
_ = Match(ExpressionAnimationTokenKind.ColonToken);
var whenFalse = ParseExpression();
left = new AnimationTernaryExpressionSyntax(left, whenTrue, whenFalse);
}
else
{
var operatorToken = NextToken();
var right = ParseExpression(precedence);
left = new AnimationBinaryExpressionSyntax(left, operatorToken, right);
}
}

return left;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ internal enum ExpressionAnimationTokenKind
DivisionToken,
OpenParenToken,
CloseParenToken,
QuestionMarkToken,
ColonToken,
GreaterThanEqualsToken,
GreaterThanToken,
LessThanEqualsToken,
LessThanToken,
IdentifierToken,
NumericLiteralToken,
// TODO: QuestionMarkToken and ColonToken to support ternary operators.
}
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ private protected override void SetAnimatableProperty(ReadOnlySpan<char> propert
{
MaxPosition = UpdateVector3(subPropertyName, MaxPosition, propertyValue);
}
else if (propertyName.Equals(nameof(MinScale), StringComparison.OrdinalIgnoreCase))
{
MinScale = ValidateValue<float>(propertyValue);
}
else if (propertyName.Equals(nameof(MaxScale), StringComparison.OrdinalIgnoreCase))
{
MaxScale = ValidateValue<float>(propertyValue);
}
else
{
base.SetAnimatableProperty(propertyName, subPropertyName, propertyValue);
Expand All @@ -142,6 +150,18 @@ internal override object GetAnimatableProperty(string propertyName, string subPr
{
return GetVector3(subPropertyName, MinPosition);
}
else if (propertyName.Equals(nameof(Scale), StringComparison.OrdinalIgnoreCase))
{
return ValidateValue<float>(Scale);
}
else if (propertyName.Equals(nameof(MinScale), StringComparison.OrdinalIgnoreCase))
{
return ValidateValue<float>(MinScale);
}
else if (propertyName.Equals(nameof(MaxScale), StringComparison.OrdinalIgnoreCase))
{
return ValidateValue<float>(MaxScale);
}
else
{
return base.GetAnimatableProperty(propertyName, subPropertyName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,48 @@ public void TestUnaryMinusExpressionWithMemberAccess()
var result = expression.Evaluate(expressionAnimation);
Assert.AreEqual(-5.0f, result);
}

[TestMethod]
public void TestTernaryExpression_Simple_When_True()
{
var compositor = Compositor.GetSharedCompositor();
var expressionAnimation = compositor.CreateExpressionAnimation("true ? 5 : 4");
var parser = new ExpressionAnimationParser(expressionAnimation.Expression);
var expression = parser.Parse();
var result = expression.Evaluate(expressionAnimation);
Assert.AreEqual(5.0f, result);
}

[TestMethod]
public void TestTernaryExpression_Simple_When_False()
{
var compositor = Compositor.GetSharedCompositor();
var expressionAnimation = compositor.CreateExpressionAnimation("false ? 5 : 4");
var parser = new ExpressionAnimationParser(expressionAnimation.Expression);
var expression = parser.Parse();
var result = expression.Evaluate(expressionAnimation);
Assert.AreEqual(4.0f, result);
}

[TestMethod]
public void TestTernaryExpression_Condition_Is_Binary_When_True()
{
var compositor = Compositor.GetSharedCompositor();
var expressionAnimation = compositor.CreateExpressionAnimation("5 > 4 ? 8.0f : 9.0f");
var parser = new ExpressionAnimationParser(expressionAnimation.Expression);
var expression = parser.Parse();
var result = expression.Evaluate(expressionAnimation);
Assert.AreEqual(8.0f, result);
}

[TestMethod]
public void TestTernaryExpression_Condition_Is_Binary_When_False()
{
var compositor = Compositor.GetSharedCompositor();
var expressionAnimation = compositor.CreateExpressionAnimation("5 <= 4 ? 8.0f : 9.0f");
var parser = new ExpressionAnimationParser(expressionAnimation.Expression);
var expression = parser.Parse();
var result = expression.Evaluate(expressionAnimation);
Assert.AreEqual(9.0f, result);
}
}

0 comments on commit 927fed4

Please sign in to comment.