Maui icon indicating copy to clipboard operation
Maui copied to clipboard

[BUG] MathExpressionConverter doesn't support negative numbers when in Arabic (ar_AR)

Open stephenquan opened this issue 7 months ago • 3 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Did you read the "Reporting a bug" section on Contributing file?

  • [x] I have read the "Reporting a bug" section on Contributing file: https://github.com/CommunityToolkit/Maui/blob/main/CONTRIBUTING.md#reporting-a-bug

Current Behavior

When your CultureInfo.CurrentCulture = new CultureInfo("ar-AR") the MathExpressionConverter will raise an exception when the expression contains negative numbers (e.g. x0 ? -90 : 90):

HResult: -2146233033 (0x80131537) Message: The input string '-90' was not in a correct format.

Here's the call stack:

at System.Number.ThrowFormatException[TChar](ReadOnlySpan1 value) at System.Double.Parse(String s) at CommunityToolkit.Maui.Converters.MathExpression.ParsePrimary() in /_/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 365 at CommunityToolkit.Maui.Converters.MathExpression.ParseBinaryOperators(Regex BinaryOperators, Func1 ParseNext) in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 337 at CommunityToolkit.Maui.Converters.MathExpression.ParsePower() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 333 at CommunityToolkit.Maui.Converters.MathExpression.ParseBinaryOperators(Regex BinaryOperators, Func1 ParseNext) in /_/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 337 at CommunityToolkit.Maui.Converters.MathExpression.ParseProduct() in /_/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 331 at CommunityToolkit.Maui.Converters.MathExpression.ParseBinaryOperators(Regex BinaryOperators, Func1 ParseNext) in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 337 at CommunityToolkit.Maui.Converters.MathExpression.ParseSum() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 329 at CommunityToolkit.Maui.Converters.MathExpression.ParseBinaryOperators(Regex BinaryOperators, Func1 ParseNext) in /_/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 337 at CommunityToolkit.Maui.Converters.MathExpression.ParseCompare() in /_/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 327 at CommunityToolkit.Maui.Converters.MathExpression.ParseBinaryOperators(Regex BinaryOperators, Func1 ParseNext) in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 357 at CommunityToolkit.Maui.Converters.MathExpression.ParseEquality() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 325 at CommunityToolkit.Maui.Converters.MathExpression.ParseBinaryOperators(Regex BinaryOperators, Func1 ParseNext) in /_/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 337 at CommunityToolkit.Maui.Converters.MathExpression.ParseLogicalAnd() in /_/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 323 at CommunityToolkit.Maui.Converters.MathExpression.ParseBinaryOperators(Regex BinaryOperators, Func1 ParseNext) in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 337 at CommunityToolkit.Maui.Converters.MathExpression.ParseLogicalOR() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 321 at CommunityToolkit.Maui.Converters.MathExpression.ParseConditional() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 302 at CommunityToolkit.Maui.Converters.MathExpression.ParseExpr() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 287 at CommunityToolkit.Maui.Converters.MathExpression.ParseExpression() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 282 at CommunityToolkit.Maui.Converters.MathExpression.CalculateResult() in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs:line 128 at CommunityToolkit.Maui.Converters.MathExpressionConverter.ConvertFrom(Object inputValue, String parameter, CultureInfo culture) in //src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpressionConverter.shared.cs:line 25 at CommunityToolkit.Maui.Converters.BaseConverter`3.CommunityToolkit.Maui.Converters.ICommunityToolkitValueConverter.Convert(Object value, Type targetType, Object parameter, CultureInfo culture) in //src/CommunityToolkit.Maui/Converters/BaseConverter.shared.cs:line 91 at CommunityToolkit.Maui.Converters.ICommunityToolkitValueConverter.Microsoft.Maui.Controls.IValueConverter.Convert(Object value, Type targetType, Object parameter, CultureInfo culture) in /_/src/CommunityToolkit.Maui/Converters/ICommunityToolkitValueConverter.shared.cs:line 57 at Microsoft.Maui.Controls.Binding.GetSourceValue(Object value, Type targetPropertyType) at Microsoft.Maui.Controls.BindingExpression.ApplyCore(Object sourceObject, BindableObject target, BindableProperty property, Boolean fromTarget, SetterSpecificity specificity) at Microsoft.Maui.Controls.BindingExpression.Apply(Boolean fromTarget) at Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.<PropertyChanged>b__50_0() at Microsoft.Maui.Controls.DispatcherExtensions.DispatchIfRequired(IDispatcher dispatcher, Action action) at Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.PropertyChanged(Object sender, PropertyChangedEventArgs args) at Microsoft.Maui.Controls.BindingExpression.WeakPropertyChangedProxy.OnPropertyChanged(Object sender, PropertyChangedEventArgs e) at Microsoft.Maui.Controls.BindableObject.OnPropertyChanged(String propertyName) at Microsoft.Maui.Controls.Element.OnPropertyChanged(String propertyName) at Microsoft.Maui.Controls.BindableObject.OnBindablePropertySet(BindableProperty property, Object original, Object value, Boolean didChange, Boolean willFirePropertyChanged) at Microsoft.Maui.Controls.Element.OnBindablePropertySet(BindableProperty property, Object original, Object value, Boolean changed, Boolean willFirePropertyChanged) at Microsoft.Maui.Controls.BindableObject.SetValueActual(BindableProperty property, BindablePropertyContext context, Object value, Boolean currentlyApplying, SetValueFlags attributes, SetterSpecificity specificity, Boolean silent) at Microsoft.Maui.Controls.BindableObject.SetValueCore(BindableProperty property, Object value, SetValueFlags attributes, SetValuePrivateFlags privateAttributes, SetterSpecificity specificity) at Microsoft.Maui.Controls.BindableObject.SetValue(BindableProperty property, Object value, SetterSpecificity specificity) at Microsoft.Maui.Controls.CheckBox.Microsoft.Maui.ICheckBox.set_IsChecked(Boolean value) at Microsoft.Maui.Handlers.CheckBoxHandler.OnChecked(Object sender, RoutedEventArgs e) at WinRT._EventSource_global__Microsoft_UI_Xaml_RoutedEventHandler.EventState.<GetEventInvoke>b__1_0(Object sender, RoutedEventArgs e) at ABI.Microsoft.UI.Xaml.RoutedEventHandler.Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr e)

Expected Behavior

The MathExpressionConverter should be able to handle expressions with negative numbers without encountering exceptions.

Steps To Reproduce

The following is a XAML minimal repro case

<VerticalStackLayout Padding="30,0" Spacing="25">
    <Label Text="Click on the CheckBox below and observe the corresponding Label changing its orientation. When set to Arabic, however, clicking on the CheckBox will crash the app." />
    <CheckBox x:Name="ExpandedCheck" IsChecked="True" />
    <Label
         HorizontalOptions="Start"
         Rotation="{Binding IsChecked,
                            Source={Reference ExpandedCheck},
                            x:DataType=CheckBox,
                            Converter={StaticResource MathExpressionConverter},
                            ConverterParameter='x0 ? -90 : 90'}"
         Text="&gt;" />
</VerticalStackLayout>

Link to public reproduction project repository

https://github.com/stephenquan/MauiMCTMinusBug

Environment

- .NET MAUI CommunityToolkit: 12.0
- OS: Windows 11
- .NET MAUI: 9.0.70

Anything else?

I believe the bug is on the following offending line: https://github.com/CommunityToolkit/Maui/blob/ecc51ed31434b09c6e32003149d10c13023af6f2/src/CommunityToolkit.Maui/Converters/MathExpressionConverter/MathExpression.shared.cs#L365

stephenquan avatar Jun 16 '25 01:06 stephenquan

I am just curious what is the expected format for Arabic? 09-

VladislavAntonyuk avatar Jun 18 '25 03:06 VladislavAntonyuk

I think the parser should use InvariantCulture when parsing numeric values in mathematical expressions.

I was having a related problem where my expression was x*0.5 where it would multiply by 5 when the culture is German and divide by 2 when the culture is English. My application is bilingual (German and English) and German uses . as the thousands separator and , as the decimal separator unlike English. I worked around my problem by using x/2 as the expression.

I was thinking of creating a new issue but when I found this issue I think it is related, so I don't think I should create a new issue.

In my opinion, I don't see a case where the developer intends to use a culture specific number in the expression. Typically culture formatted values are intended only when the value is displayed to the user. Since the expression for the MathExpressionConverter is not intended for the user, it's safe to assume that InvariantCulture is the always the intent. The mathematical expression should be treated like expressions in C# code where the culture is always invariant.

However, fixing it may cause breaking changes if a developer depended on the buggy (in my opinion) behavior.

sherif-elmetainy avatar Oct 21 '25 06:10 sherif-elmetainy

I am just curious what is the expected format for Arabic? 09-

The expected format is "\u061c-90" where the Unicode character '\u0615' is ARABIC LETTER MARK which is used by the Bidirectional Algorithm to display text when there is mixed left to right and right to left characters in the text.

sherif-elmetainy avatar Oct 21 '25 06:10 sherif-elmetainy