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

Conversation

stephenquan
Copy link

@stephenquan stephenquan commented Sep 5, 2024

Description of Change

  • MultiMathExpressionConverterPage.xaml
    • Add VerticalStackLayout and move existing sample inside
    • Added a Label with a DataTrigger on "x0 + x1 + x2 >= 60" to demonstrate the comparison operator
  • MathOperator.shared.cs
    • Replaced double with object
    • MathOperatorPrecedence obsolete (part of the original parsing engine but no longer relevant in new engine)
  • MathExpressionConverterTests.cs
    • New tests for comparison, equality, and boolean operators
    • Certain operations allow nulls now
  • MathExpression.shared.cs
    • Refactor/rewrite to use Parsing expression grammar
    • The grammar parity with original operators +, -, *, / and ^ as well as math functions
    • Added to grammar and, or, ?, :, >=, >, <=, <, ==, != as well as true and false constants
    • Relax null strictness (e.g. equality and ternary operators can have nulls)
  • MathExpressionConverter.shared.cs
    • Replaced double with object
  • MultiMathExpressionConverter.cs
    • Relax null strictness (e.g. equality and ternary operators can have nulls)

General comments:

  • Original implementation sanitizes all inputs by calling double.Parse(value.ToString) rejecting nulls
  • New implementation calls Convert.ToDouble() when needed and, in certain situations, allows nulls
  • When a boolean value is needed, boolean coercion uses the following rules:
    • If value is null, return false
    • If value is a boolean, return value
    • If value is a double, return true if value != 0 && value != NaN, false otherwise
    • If value is a string, return true, if value is not null or empty, false otherwise

Linked Issues

#2181

PR Checklist

Additional information

@stephenquan
Copy link
Author

@dotnet-policy-service agree company="Esri"

@stephenquan stephenquan marked this pull request as ready for review September 6, 2024 01:49
@stephenquan
Copy link
Author

Hi @brminnick since your review, I have:

  • attempted to update the fork with the base branch
  • corrected the unit test issues you raised
  • added more unit test for better coverage of the boolean operators
  • updated the handling of nulls

The fork is ready for another round of review

@pictos
Copy link
Member

pictos commented Sep 17, 2024

@stephenquan here's the build log:

D:\a\1\s\src\CommunityToolkit.Maui\Converters\MathExpressionConverter\MathExpressionConverter.shared.cs(25,10): error CS8603: Possible null reference return. [D:\a\1\s\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj::TargetFramework=net8.0-ios]
D:\a\1\s\src\CommunityToolkit.Maui\Converters\MathExpressionConverter\MathExpressionConverter.shared.cs(25,10): error CS8603: Possible null reference return. [D:\a\1\s\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj::TargetFramework=net8.0-windows10.0.19041.0]
D:\a\1\s\src\CommunityToolkit.Maui\Converters\MathExpressionConverter\MathExpressionConverter.shared.cs(25,10): error CS8603: Possible null reference return. [D:\a\1\s\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj::TargetFramework=net8.0-tizen]
D:\a\1\s\src\CommunityToolkit.Maui\Converters\MathExpressionConverter\MathExpressionConverter.shared.cs(25,10): error CS8603: Possible null reference return. [D:\a\1\s\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj::TargetFramework=net8.0-maccatalyst]
D:\a\1\s\src\CommunityToolkit.Maui\Converters\MathExpressionConverter\MathExpressionConverter.shared.cs(25,10): error CS8603: Possible null reference return. [D:\a\1\s\src\CommunityToolkit.Maui\CommunityToolkit.Maui.csproj::TargetFramework=net8.0-android]
  

@stephenquan
Copy link
Author

Thanks @pictos, I have reviewed the CS8063 possible null reference return issues and have resolved it by making nullability updates to both the MathExpression.shared.cs and MathExpressionConverter.shared.cs. The nullability updates also required additional Asserts added to the MathExpressionConverterTests.cs. With these changes, the build checks are now passing.

Copy link
Member

@pictos pictos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we're getting really close to merge this!

@stephenquan
Copy link
Author

stephenquan commented Sep 25, 2024

@pictos @bijington I have pushed updates to the fork address both the C# property name pattern and logging invalid math expressions with TraceWarning. Because we're no longer raising invalid math expressions I had to change the relevant unit test from an exception test to a null check. The fork is ready for review again.

…ssionConverter and corresponding unit tests.
@stephenquan
Copy link
Author

stephenquan commented Oct 6, 2024

@pictos @brminnick as requested, I have removed the Null Forgiving Operator from the MultiMathExpressionConverter. I have corrected the null checks in the MultiMathExpressionConverter and that resulted in a necessary minor update to the unit tests.

This fork is ready for review again.

@stephenquan
Copy link
Author

stephenquan commented Oct 20, 2024

Hi all, we are using snapshot copies of these classes in our projects. I am eager to help out and remove any blockers on this PR.

pictos
pictos previously approved these changes Oct 22, 2024
Copy link
Member

@pictos pictos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@stephenquan
Copy link
Author

I have some new developers working with Boolean operators in XAML, but they struggled with the syntax.

To simplify I things added new commits which adds XAML-friendly alternatives. Additionally, I added more coverage to the logical AND/OR unit tests.

Unfortunately, in doing so, 3 of the 6 automated test are failing and I am not sure why. Also, the PR will now require a re-review.

Operator XAML Alternate
&& &amp;&amp; and
|| || or
>= &gt;= ge
> &gt; gt
<= &lt;= le
< &lt; lt

Here are some examples of the alternate syntax:

Original Alternate
x &gt;= 0 ? x1 : x2 x ge 0 ? x1 : x2
x &amp;&amp; !x1 x and !x1
x &gt;= 0 &amp;&amp; x &lt;= 10 x ge 0 and x le 10

@stephenquan
Copy link
Author

The azure pipelines https://dev.azure.com/dotnet/CommunityToolkit/_build/results?buildId=112270&view=logs&j=67da6a11-18ef-5f70-9b43-fae7e6c83e18&t=54ba8226-26d8-53c8-39e8-392f99f22929&l=538 is showing 11 new errors that I do not know how to resolve. These 11 new errors do not relate the commits done, so, I'm hoping they will automatically resolve when the fork catches up with the main branch:

D:\a\1\s\samples\CommunityToolkit.Maui.Sample\MauiProgram.cs(38,20): error CS0234: The type or namespace name 'Composition' does not exist in the namespace 'Microsoft.UI' (are you missing an assembly reference?) [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-windows10.0.19041.0]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\MauiProgram.cs(39,20): error CS0234: The type or namespace name 'Windowing' does not exist in the namespace 'Microsoft.UI' (are you missing an assembly reference?) [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-windows10.0.19041.0]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\MauiProgram.cs(40,25): error CS0234: The type or namespace name 'Media' does not exist in the namespace 'Microsoft.UI.Xaml' (are you missing an assembly reference?) [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-windows10.0.19041.0]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\Platforms\Windows\App.xaml.cs(6,28): error CS0246: The type or namespace name 'MauiWinUIApplication' could not be found (are you missing a using directive or an assembly reference?) [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-windows10.0.19041.0]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\Platforms\MacCatalyst\AppDelegate.cs(6,28): error CS0246: The type or namespace name 'MauiUIApplicationDelegate' could not be found (are you missing a using directive or an assembly reference?) [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-maccatalyst]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\Platforms\iOS\AppDelegate.cs(6,28): error CS0246: The type or namespace name 'MauiUIApplicationDelegate' could not be found (are you missing a using directive or an assembly reference?) [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-ios]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\obj\Release\net8.0-android\lp\148\jl\res\layout\tabbar.xml(2): error APT2260: attribute tabGravity (aka com.microsoft.CommunityToolkit.Maui.Sample:tabGravity) not found. [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\obj\Release\net8.0-android\lp\148\jl\res\layout\tabbar.xml(2): error APT2260: [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\obj\Release\net8.0-android\lp\148\jl\res\layout\tabbar.xml(2): error APT2260: attribute tabIndicatorColor (aka com.microsoft.CommunityToolkit.Maui.Sample:tabIndicatorColor) not found. [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\obj\Release\net8.0-android\lp\148\jl\res\layout\tabbar.xml(2): error APT2260: [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\obj\Release\net8.0-android\lp\148\jl\res\layout\tabbar.xml(2): error APT2260: attribute tabMode (aka com.microsoft.CommunityToolkit.Maui.Sample:tabMode) not found. [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]
D:\a\1\s\samples\CommunityToolkit.Maui.Sample\obj\Release\net8.0-android\lp\148\jl\res\layout\tabbar.xml(2): error APT2260: [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]
C:\hostedtoolcache\windows\dotnet\packs\Microsoft.Android.Sdk.Windows\34.0.145\tools\Xamarin.Android.Aapt2.targets(156,3): error APT2061: failed linking file resources. [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]
C:\hostedtoolcache\windows\dotnet\packs\Microsoft.Android.Sdk.Windows\34.0.145\tools\Xamarin.Android.Aapt2.targets(156,3): error APT2061: [D:\a\1\s\samples\CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.csproj::TargetFramework=net8.0-android]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 5 out of 6 changed files in this pull request and generated no suggestions.

Files not reviewed (1)
  • samples/CommunityToolkit.Maui.Sample/Pages/Converters/MultiMathExpressionConverterPage.xaml: Language not supported
Comments skipped due to low confidence (3)

src/CommunityToolkit.Maui.UnitTests/Converters/MathExpressionConverterTests.cs:66

  • [nitpick] The variable name 'variables' should be renamed to 'values' for consistency with the method parameter name in MultiMathExpressionConverter.shared.cs.
public void MathExpressionConverter_WithMultipleVariable_ReturnsCorrectResult(string expression, object[] variables, double expectedResult)

src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpressionConverter.shared.cs:20

  • [nitpick] The variable name 'value' is less descriptive in the context of mathematical expressions. Consider renaming it to 'inputValue' for clarity.
public override object? ConvertFrom(object? value, string parameter, CultureInfo? culture = null)

src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpressionConverter.shared.cs:20

  • The removal of the '[return: NotNullIfNotNull(nameof(values))]' attribute might lead to unclear error messages when null values are encountered. Consider adding a clear error message for null values.
public override object? ConvertFrom(object? value, string parameter, CultureInfo? culture = null)
@brminnick
Copy link
Collaborator

Hi @stephenquan! Could you update this PR to resolve the merge conflicts?

We've finally merged the massive .NET 9 PR today and it introduced a few merge conflicts across our open PRs.

@brminnick brminnick removed the help wanted This proposal has been approved and is ready to be implemented label Jan 2, 2025
@brminnick brminnick requested a review from Copilot January 2, 2025 18:54

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 5 out of 6 changed files in this pull request and generated no comments.

Files not reviewed (1)
  • samples/CommunityToolkit.Maui.Sample/Pages/Converters/MultiMathExpressionConverterPage.xaml: Language not supported
[InlineData("Cat", false, false, "Cat")]
[InlineData("", false, "", false)]
[InlineData(false, "", false, "")]
[InlineData(null, "Cat", null, "Cat")]
Copy link
Collaborator

@brminnick brminnick Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @stephenquan! There's one thing I'm having trouble wrapping my head around in this PR: How/why we are evaluating null using boolean operators, as demonstrated here in this test?

If I'm understanding everything correctly, the below line of code is equivalent to evaluating the boolean math equation of null OR null, right?

mathExpressionConverter.Convert(variables, typeof(object), "x || x1")

I haven't studied boolean algebra since college, but, my understanding is that boolean algebra does not include the concept of null. Is my understanding correct?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My gut tells me that we should be throwing an Exception in the scenarios where null is being used in a Boolean Algebraic equation.

In other words, MathExpression.Calculate() should not return null in these scenarios and instead throw an exception alerting the developer that they've input an invalid equation.

Copy link
Author

@stephenquan stephenquan Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brminnick thanks for your question. The inspiration for logical-AND and logical-OR operators is derived from both Javascript and Python with respect to how they handle truthy and falsy concepts. For reference:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND

If a value can be converted to true, the value is so-called truthy. If a value can be converted to false, the value is so-called falsy.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR

If x can be converted to true, returns x; else, returns y.

And the unit test mirror the results from the Javascript documentation examples.

The Python documentation also describes this behavior: https://docs.python.org/3/reference/expressions.html#boolean-operations

The expression x and y first evaluates x; if x is false, its value is returned; otherwise, y is evaluated and the resulting value is returned.
The expression x or y first evaluates x; if x is true, its value is returned; otherwise, y is evaluated and the resulting value is returned.

If I wanted to implement the following case statement

// states of water
if (x0 < 0) then x1 // "ice";
else if (x0 < 100) then x2 // "water"
else x3 // "steam"

I could write it as:

// using the conditional operator
x0 < 0 ? x1
       : x0 < 100
         ? x2
         : x3

Or, using the truthy and falsy states, I could rewrite it as

// using the truthy and falsy application of logical-AND and logical-OR
     (x0 <   0) and x1
  or (x0 < 100) and x2
  or x3

Generalizing the pattern, it would be:

   conditional-1 and result-1
or conditional-2 and result-2
or ...
or conditional-n and result-n
or result-else

There are limitations to this approach since the things fail if any of the intermediate results are "falsy" then it doesn't work. In the above example, "ice", "water" and "steam" are truthy values since they're non-empty strings. It is also because of this behavior, that we do not require any of the inputs to be strictly boolean. They can be of any object including null and it would still behave.

Copy link
Author

@stephenquan stephenquan Jan 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Javascript

console.log(null || null); // output: null
console.log(null && null); // output: null

Python

>>> print(None or None) // output: None
None
>>> print(None and None) // output: None
None

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the equivalent of a Nullable<bool> expression so I think it's fine

@brminnick brminnick requested review from Copilot January 2, 2025 19:30

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 5 out of 6 changed files in this pull request and generated no comments.

Files not reviewed (1)
  • samples/CommunityToolkit.Maui.Sample/Pages/Converters/MultiMathExpressionConverterPage.xaml: Language not supported
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants