Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to PEG parser. Introduce boolean operators and constants. #2182

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1d2577d
Migrate to PEG parser. Introduce boolean operators and constants.
stephenquan Aug 19, 2024
bf34590
Update UnitTest. Added conditional sample.
stephenquan Sep 6, 2024
584eb1e
Merge branch 'main' of github.com:CommunityToolkit/Maui
stephenquan Sep 6, 2024
5a830f4
Improve null handling and added extra boolean/null unit tests
stephenquan Sep 10, 2024
7472ad0
Additional boolean tests
stephenquan Sep 10, 2024
d580a45
Allow nulls if operator is ? : && || == !=
stephenquan Sep 10, 2024
c60652f
__bool and MathToken record refactor
stephenquan Sep 18, 2024
ef3380a
First attempt to block null reference return
stephenquan Sep 18, 2024
cd8def9
Second attempt to block null reference return
stephenquan Sep 18, 2024
3c9b632
Addessed CS8603 possible null reference return issues
stephenquan Sep 18, 2024
5aaf5ef
Merge branch 'main' into main
pictos Sep 18, 2024
fde4fc1
Merge branch 'main' into main
pictos Sep 22, 2024
9bf1bef
Use correct C# property name pattern. Log invalid math expressions in…
stephenquan Sep 25, 2024
34d3883
Merge branch 'main' into main
pictos Sep 26, 2024
88b0103
Merge branch 'main' into main
stephenquan Oct 6, 2024
9b64f7b
Null Forgiving Operator removed. Adjust null checks in MultiMathExpre…
stephenquan Oct 6, 2024
c3ea28b
Merge branch 'main' into main
pictos Oct 21, 2024
9994c63
Merge branch 'main' into main
brminnick Nov 26, 2024
6808590
Add more logical and/or unit tests. Add XAML friendly comparator oper…
stephenquan Dec 5, 2024
31d712e
Improve unit test coverage on alternate operators.
stephenquan Dec 5, 2024
a983e7a
Improve null handling for logical operators && || and cover these cas…
stephenquan Dec 6, 2024
a4bcd8a
Merge branch 'main' into main
stephenquan Dec 8, 2024
ebf0d3a
Miscellaneous fixes for Copilot review
stephenquan Dec 12, 2024
91bd65b
Merge branch 'main' of github.com:stephenquan/Maui
stephenquan Dec 12, 2024
c59b197
Merge branch 'main' into pr/2182
brminnick Jan 2, 2025
241ed90
Update to .NET 9
brminnick Jan 2, 2025
11e9d72
Use Primary Constructor
brminnick Jan 2, 2025
53510d9
Merge branch 'main' into main
brminnick Jan 2, 2025
3e5b075
Remove redundant CodeAnalysis
brminnick Jan 2, 2025
5103c68
Update Regex naming
brminnick Jan 2, 2025
2967e7c
Use Index Operator
brminnick Jan 2, 2025
46e7b38
Merge branch 'main' of https://github.com/stephenquan/Maui into pr/2182
brminnick Jan 2, 2025
0bc4544
Update namings
brminnick Jan 3, 2025
3d6c9c8
Refactor
brminnick Jan 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,63 +15,86 @@
</ResourceDictionary>
</pages:BasePage.Resources>

<Grid Padding="20"
RowSpacing="20"
ColumnSpacing="12"
ColumnDefinitions="*,*,*,*,*,*,*"
RowDefinitions="100, *">
<Label Grid.ColumnSpan="7"
Grid.Row="0"
Text="This sample demonstrates the use of the MultiMathExpressionConverter. It utilizes the converter to perform various math operations of multiple variables."/>
<VerticalStackLayout Padding="20" Spacing="40">

<Entry Grid.Column="0"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="{Binding X0}"/>
<Grid RowSpacing="20"
ColumnSpacing="12"
ColumnDefinitions="*,*,*,*,*,*,*"
RowDefinitions="100, *">
<Label Grid.ColumnSpan="7"
Grid.Row="0"
Text="This sample demonstrates the use of the MultiMathExpressionConverter. It utilizes the converter to perform various math operations of multiple variables."/>

<Label Grid.Column="1"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="+" />
<Entry Grid.Column="0"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="{Binding X0}"/>

<Entry Grid.Column="2"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="{Binding X1}"/>
<Label Grid.Column="1"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="+" />

<Label Grid.Column="3"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="+" />
<Entry Grid.Column="2"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="{Binding X1}"/>

<Entry Grid.Column="4"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="{Binding X2}"/>
<Label Grid.Column="3"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="+" />

<Label Grid.Column="5"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="=" />
<Entry Grid.Column="4"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="{Binding X2}"/>

<Label Grid.Column="6"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center">
<Label.Text>
<MultiBinding Converter="{StaticResource MultiMathExpressionConverter}"
ConverterParameter="x0 + x1 + x2">
<Binding Path="X0" Mode="OneWay"/>
<Binding Path="X1" Mode="OneWay"/>
<Binding Path="X2" Mode="OneWay"/>
</MultiBinding>
</Label.Text>
<Label Grid.Column="5"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center"
Text="=" />

<Label Grid.Column="6"
Grid.Row="1"
VerticalTextAlignment="Center"
VerticalOptions="Center">
<Label.Text>
<MultiBinding Converter="{StaticResource MultiMathExpressionConverter}"
ConverterParameter="x0 + x1 + x2">
<Binding Path="X0" Mode="OneWay"/>
<Binding Path="X1" Mode="OneWay"/>
<Binding Path="X2" Mode="OneWay"/>
</MultiBinding>
</Label.Text>
</Label>
</Grid>

<Label HorizontalOptions="Center"
Text="Valid: Value greater than or equal to 60"
TextColor="Green">
<Label.Triggers>
<DataTrigger TargetType="Label" Value="False">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MultiMathExpressionConverter}"
ConverterParameter="x0 + x1 + x2 &gt;= 60">
<Binding Path="X0" Mode="OneWay"/>
<Binding Path="X1" Mode="OneWay"/>
<Binding Path="X2" Mode="OneWay"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Text" Value="Error: Value not greater than or equal to 60"/>
<Setter Property="TextColor" Value="Red"/>
</DataTrigger>
</Label.Triggers>
</Label>
</Grid>

</VerticalStackLayout>

</pages:BasePage>
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,14 @@ public sealed class MathOperator
/// </summary>
/// <param name="name">Name</param>
/// <param name="numericCount">Number of Numerals</param>
/// <param name="precedence">Math Operator Preference</param>
/// <param name="calculateFunc">Calculation Function</param>
public MathOperator(
string name,
int numericCount,
MathOperatorPrecedence precedence,
Func<double[], double> calculateFunc)
Func<object?[], object?> calculateFunc)
{
Name = name;
CalculateFunc = calculateFunc;
Precedence = precedence;
NumericCount = numericCount;
}

Expand All @@ -59,5 +56,5 @@ public MathOperator(
/// <summary>
/// Calculation Function
/// </summary>
public Func<double[], double> CalculateFunc { get; }
public Func<object?[], object?> CalculateFunc { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,41 @@ public void MathExpressionConverter_ReturnsCorrectResult(string expression, doub
var convertResult = ((ICommunityToolkitValueConverter)mathExpressionConverter).Convert(x, mathExpressionTargetType, expression, cultureInfo) ?? throw new NullReferenceException();
var convertFromResult = mathExpressionConverter.ConvertFrom(x, expression);

Assert.True(convertFromResult is not null);
Assert.True(Math.Abs((double)convertResult - expectedResult) < tolerance);
Assert.True(Math.Abs(convertFromResult - expectedResult) < tolerance);
Assert.True(Math.Abs((double)convertFromResult - expectedResult) < tolerance);
}

[Theory]
[InlineData("3 < x", 2d, false)]
[InlineData("x > 3", 2d, false)]
[InlineData("3 < x == x > 3", 2d, true)]
[InlineData("3 <= x != 3 >= x", 2d, true)]
[InlineData("x >= 1", 2d, true)]
[InlineData("x <= 3", 2d, true)]
[InlineData("x >= 1 && (x <= 3 || x >= 0)", 2d, true)]
[InlineData("true", 2d, true)]
[InlineData("false", 2d, false)]
[InlineData("-x > 2", 3d, false)]
[InlineData("!!! (---x > 2)", 3d, true)]
public void MathExpressionConverter_WithComparisonOperator_ReturnsCorrectBooleanResult(string expression, double x, bool expectedResult)
{
var mathExpressionConverter = new MathExpressionConverter();

var convertResult = ((ICommunityToolkitValueConverter)mathExpressionConverter).Convert(x, mathExpressionTargetType, expression, cultureInfo) ?? throw new NullReferenceException();
var convertFromResult = mathExpressionConverter.ConvertFrom(x, expression);

Assert.True(convertFromResult is not null);
Assert.True((bool)convertResult == expectedResult);
Assert.True((bool)convertFromResult == expectedResult);
}

[Theory]
[InlineData("x + x1 * x1", new object[] { 2d, 1d }, 3d)]
[InlineData("(x1 + x) * x1", new object[] { 2d, 3d }, 15d)]
[InlineData("3 + x * x1 / (1 - 5)^x1", new object[] { 4d, 2d }, 3.5d)]
[InlineData("3 + 4 * 2 + cos(100 + x) / (x1 - 5)^2 + pow(x0, 2)", new object[] { 20d, 1d }, 411.05088631065792d)]
public void MathExpressionConverter_WithMultiplyVariable_ReturnsCorrectResult(string expression, object[] variables, double expectedResult)
public void MathExpressionConverter_WithMultipleVariable_ReturnsCorrectResult(string expression, object[] variables, double expectedResult)
{
var mathExpressionConverter = new MultiMathExpressionConverter();

Expand All @@ -47,16 +72,74 @@ public void MathExpressionConverter_WithMultiplyVariable_ReturnsCorrectResult(st
Assert.True(Math.Abs((double)result - expectedResult) < tolerance);
}

[Theory]
[InlineData("x == 3 && x1", new object?[] { 3d, 4d }, 4d)]
[InlineData("x != 3 || x1", new object?[] { 3d, 4d }, 4d)]
[InlineData("x + x1 || true", new object?[] { 3d, 4d }, 7d)]
[InlineData("x + x1 && false", new object?[] { 2d, -2d }, 0d)]
public void MathExpressionConverter_WithBooleanOperator_ReturnsCorrectNumberResult(string expression, object[] variables, double expectedResult)
{
var mathExpressionConverter = new MultiMathExpressionConverter();

object? result = mathExpressionConverter.Convert(variables, mathExpressionTargetType, expression);

Assert.True(result is not null);
Assert.Equal(expectedResult, result);
}

[Theory]
[InlineData("x != 3 && x1", new object?[] { 3d, 4d }, false)]
[InlineData("x == 3 || x1", new object?[] { 3d, 4d }, true)]
public void MathExpressionConverter_WithBooleanOperator_ReturnsCorrectBooleanResult(string expression, object[] variables, bool expectedResult)
{
var mathExpressionConverter = new MultiMathExpressionConverter();

object? result = mathExpressionConverter.Convert(variables, mathExpressionTargetType, expression);

Assert.True(result is not null);
Assert.Equal(expectedResult, result);
}

[Theory]
[InlineData("x == 3 && x1", new object?[] { 3d, null})]
[InlineData("x != 3 || x1", new object?[] { 3d, null })]
[InlineData("x == 3 ? x1 : x2", new object?[] { 3d, null, 5d })]
[InlineData("x != 3 ? x1 : x2", new object?[] { 3d, 4d, null})]
public void MathExpressionConverter_ReturnsCorrectNullResult(string expression, object[] variables)
{
var mathExpressionConverter = new MultiMathExpressionConverter();

object? result = mathExpressionConverter.Convert(variables, mathExpressionTargetType, expression);

Assert.True(result is null);
}

[Theory]
[InlineData("x == x1", new object?[] { 2d, 2d }, true)]
[InlineData("x == x1", new object?[] { 2d, null }, false)]
[InlineData("x == x1", new object?[] { null, 2d}, false)]
[InlineData("x == x1", new object?[] { null, null }, true)]
[InlineData("(x ? x1 : x2) == null", new object?[] { true, null, 2d }, true)]
public void MathExpressionConverter_WithEqualityOperator_ReturnsCorrectBooleanResult(string expression, object[] variables, bool expectedResult)
{
var mathExpressionConverter = new MultiMathExpressionConverter();

object? result = mathExpressionConverter.Convert(variables, mathExpressionTargetType, expression);

Assert.True(result is not null);
Assert.Equal(expectedResult, result);
}

[Theory]
[InlineData("1 + 3 + 5 + (3 - 2))")]
[InlineData("1 + 2) + (9")]
[InlineData("100 + pow(2)")]
public void MathExpressionConverterThrowsArgumentException(string expression)
public void MathExpressionConverter_WithInvalidExpressions_ReturnsNullResult(string expression)
{
var mathExpressionConverter = new MathExpressionConverter();

Assert.Throws<ArgumentException>(() => ((ICommunityToolkitValueConverter)mathExpressionConverter).Convert(0d, mathExpressionTargetType, expression, cultureInfo));
Assert.Throws<ArgumentException>(() => mathExpressionConverter.ConvertFrom(0d, expression));
Assert.Null(((ICommunityToolkitValueConverter)mathExpressionConverter).Convert(0d, mathExpressionTargetType, expression, cultureInfo));
Assert.Null(mathExpressionConverter.ConvertFrom(0d, expression));
}

[Theory]
Expand All @@ -74,7 +157,7 @@ public void MultiMathExpressionConverterInvalidParameterThrowsArgumentException(
public void MultiMathExpressionConverterInvalidValuesReturnsNull()
{
var mathExpressionConverter = new MultiMathExpressionConverter();
var result = mathExpressionConverter.Convert([0d, null], mathExpressionTargetType, "x", cultureInfo);
var result = mathExpressionConverter.Convert([0d, null], mathExpressionTargetType, "x + x1", cultureInfo);
result.Should().BeNull();
}

Expand All @@ -85,9 +168,9 @@ public void MathExpressionConverterNullInputTest()
Assert.Throws<ArgumentNullException>(() => ((ICommunityToolkitValueConverter)new MathExpressionConverter()).Convert(0.0, null, "x", null));
Assert.Throws<ArgumentNullException>(() => ((ICommunityToolkitValueConverter)new MathExpressionConverter()).ConvertBack(0.0, null, null, null));
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
Assert.Throws<ArgumentNullException>(() => ((ICommunityToolkitValueConverter)new MathExpressionConverter()).Convert(null, typeof(bool), "x", null));
Assert.True(((ICommunityToolkitValueConverter)new MathExpressionConverter()).Convert(null, typeof(bool), "x", null) is null);
Assert.Throws<ArgumentNullException>(() => ((ICommunityToolkitValueConverter)new MathExpressionConverter()).Convert(null, typeof(bool), null, null));
Assert.Throws<ArgumentNullException>(() => ((ICommunityToolkitValueConverter)new MathExpressionConverter()).ConvertBack(null, typeof(bool), null, null));
Assert.Throws<NotSupportedException>(() => ((ICommunityToolkitValueConverter)new MathExpressionConverter()).ConvertBack(null, typeof(bool), null, null));
}

[Fact]
Expand Down
Loading