From 6048228ed7801869059b7970e82ff74de89b4327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Javier=20Rodr=C3=ADguez=20Zas?= Date: Mon, 6 Mar 2023 10:20:52 +0100 Subject: [PATCH 1/3] fixes #20187 --- .../ExtendedCalculatorParserTests.cs | 2 - .../QueryTests.cs | 98 ++++++++++++++- .../CalculateEngine.cs | 2 + .../CalculateHelper.cs | 114 ++++++++++++++++++ 4 files changed, 212 insertions(+), 4 deletions(-) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs index 929fa8489ac..9332eee8134 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/ExtendedCalculatorParserTests.cs @@ -37,8 +37,6 @@ public void Interpret_ThrowError_WhenCalledNullOrEmpty(string input) [DataTestMethod] [DataRow("test")] - [DataRow("pi(2)")] // Incorrect input, constant is being treated as a function. - [DataRow("e(2)")] [DataRow("[10,10]")] // '[10,10]' is interpreted as array by mages engine public void Interpret_NoResult_WhenCalled(string input) { diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs index 56dd89430a1..acd5f7e00ce 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs @@ -14,7 +14,6 @@ public class QueryTests { [DataTestMethod] [DataRow("=pi(9+)", "Expression wrong or incomplete (Did you forget some parentheses?)")] - [DataRow("=pi(9)", "Expression wrong or incomplete (Did you forget some parentheses?)")] [DataRow("=pi,", "Expression wrong or incomplete (Did you forget some parentheses?)")] [DataRow("=log()", "Expression wrong or incomplete (Did you forget some parentheses?)")] [DataRow("=0xf0x6", "Expression wrong or incomplete (Did you forget some parentheses?)")] @@ -41,7 +40,6 @@ public void ErrorResultOnInvalidKeywordQuery(string typedString, string expected [DataTestMethod] [DataRow("pi(9+)")] - [DataRow("pi(9)")] [DataRow("pi,")] [DataRow("log()")] [DataRow("0xf0x6")] @@ -113,5 +111,101 @@ public void NoErrorForDivisionByNumberWithDecimalDigits(string typedString) Assert.AreEqual(result, "Copy this number to the clipboard"); Assert.AreEqual(resultWithKeyword, "Copy this number to the clipboard"); } + + [DataTestMethod] + [DataRow("2(1+1)", "2 * (1+1)")] + [DataRow("pi(1+1)", "pi * (1+1)")] + [DataRow("pilog(100)", "pi * log(100)")] + [DataRow("3log(100)", "3 * log(100)")] + [DataRow("2e", "2 * e")] + [DataRow("(1+1)(3+2)", "(1+1) * (3+2)")] + [DataRow("(1+1)cos(pi)", "(1+1) * cos(pi)")] + [DataRow("sin(pi)cos(pi)", "sin(pi) * cos(pi)")] + [DataRow("2 (1+1)", "2 * (1+1)")] + [DataRow("pi (1+1)", "pi * (1+1)")] + [DataRow("pi log(100)", "pi * log(100)")] + [DataRow("3 log(100)", "3 * log(100)")] + [DataRow("2 e", "2 * e")] + [DataRow("(1+1) (3+2)", "(1+1) * (3+2)")] + [DataRow("(1+1) cos(pi)", "(1+1) * cos(pi)")] + [DataRow("sin (pi) cos(pi)", "sin (pi) * cos(pi)")] + [DataRow("2picos(pi)(1+1)", "2 * pi * cos(pi) * (1+1)")] + [DataRow("pilog(100)log(1000)", "pi * log(100) * log(1000)")] + [DataRow("pipipie", "pi * pi * pi * e")] + [DataRow("(1+1)(3+2)(1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] + [DataRow("(1+1) (3+2) (1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] + public void RightHumaMultiplicationExpressionTransformation(string typedString, string expectedQuery) + { + // Setup + + // Act + var result = CalculateHelper.FixHumanMultiplicationExpressions(typedString); + + // Assert + Assert.AreEqual(expectedQuery, result); + } + + [DataTestMethod] + [DataRow("2(1+1)")] + [DataRow("pi(1+1)")] + [DataRow("pilog(100)")] + [DataRow("3log(100)")] + [DataRow("2e")] + [DataRow("(1+1)(3+2)")] + [DataRow("(1+1)cos(pi)")] + [DataRow("sin(pi)cos(pi)")] + [DataRow("2 (1+1)")] + [DataRow("pi (1+1)")] + [DataRow("pi log(100)")] + [DataRow("3 log(100)")] + [DataRow("2 e")] + [DataRow("(1+1) (3+2)")] + [DataRow("(1+1) cos(pi)")] + [DataRow("sin (pi) cos(pi)")] + [DataRow("2picos(pi)(1+1)")] + [DataRow("pilog(100)log(1000)")] + [DataRow("pipipie")] + [DataRow("(1+1)(3+2)(1+1)(1+1)")] + [DataRow("(1+1) (3+2) (1+1)(1+1)")] + public void NoErrorForHumaMultiplicationExpressions(string typedString) + { + // Setup + Mock
main = new(); + Query expectedQuery = new(typedString); + Query expectedQueryWithKeyword = new("=" + typedString, "="); + + // Act + var result = main.Object.Query(expectedQuery).FirstOrDefault()?.SubTitle; + var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.SubTitle; + + // Assert + Assert.AreEqual("Copy this number to the clipboard", result); + Assert.AreEqual("Copy this number to the clipboard", resultWithKeyword); + } + + [DataTestMethod] + [DataRow("2(1+1)", "4")] + [DataRow("pi(1+1)", "6.2831853072")] + [DataRow("pilog(100)", "6.2831853072")] + [DataRow("3log(100)", "6")] + [DataRow("2e", "5.4365636569")] + [DataRow("(1+1)(3+2)", "10")] + [DataRow("(1+1)cos(pi)", "-2")] + [DataRow("log(100)cos(pi)", "-2")] + public void RightAnswerForHumaMultiplicationExpressions(string typedString, string answer) + { + // Setup + Mock
main = new(); + Query expectedQuery = new(typedString); + Query expectedQueryWithKeyword = new("=" + typedString, "="); + + // Act + var result = main.Object.Query(expectedQuery).FirstOrDefault()?.Title; + var resultWithKeyword = main.Object.Query(expectedQueryWithKeyword).FirstOrDefault()?.Title; + + // Assert + Assert.AreEqual(answer, result); + Assert.AreEqual(answer, resultWithKeyword); + } } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs index e69a5eee051..eed608e24fe 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs @@ -49,6 +49,8 @@ public CalculateResult Interpret(string input, CultureInfo cultureInfo, out stri Replace("log(", "log10(", true, CultureInfo.CurrentCulture). Replace("ln(", "log(", true, CultureInfo.CurrentCulture); + input = CalculateHelper.FixHumanMultiplicationExpressions(input); + var result = _magesEngine.Interpret(input); // This could happen for some incorrect queries, like pi(2) diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs index d0908b3de0b..0cc60810753 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs @@ -48,5 +48,119 @@ public static bool InputValid(string input) return true; } + + public static string FixHumanMultiplicationExpressions(string input) + { + var output = Check_NumberOrConstant_ParenthesisExpr(input); + output = Check_NumberOrConstant_Func(output); + output = Check_ParenthesisExpr_Func(output); + output = Check_ParenthesisExpr_ParenthesisExpr(output); + output = Check_Number_Constant(output); + output = Check_Constant_Constant(output); + return output; + } + + private static string Check_NumberOrConstant_ParenthesisExpr(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+|pi|e)\s*(\()", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + private static string Check_NumberOrConstant_Func(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+|pi|e)\s*([a-zA-Z]+[0-9]*\s*\()", m => + { + if (input[m.Index] == 'e' && input[m.Index + 1] == 'x' && input[m.Index + 2] == 'p') + { + return m.Value; + } + + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + private static string Check_ParenthesisExpr_Func(string input) + { + var p = @"(\))\s*([a-zA-Z]+[0-9]*\s*\()"; + var r = "$1 * $2"; + return Regex.Replace(input, p, r); + } + + private static string Check_ParenthesisExpr_ParenthesisExpr(string input) + { + var p = @"(\))\s*(\()"; + var r = "$1 * $2"; + return Regex.Replace(input, p, r); + } + + private static string Check_Number_Constant(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(\d+)\s*(pi|e)", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } + + private static string Check_Constant_Constant(string input) + { + var output = input; + do + { + input = output; + output = Regex.Replace(input, @"(pi|e)\s*(pi|e)", m => + { + if (m.Index > 0 && char.IsLetter(input[m.Index - 1])) + { + return m.Value; + } + + return $"{m.Groups[1].Value} * {m.Groups[2].Value}"; + }); + } + while (output != input); + + return output; + } } } From 6000a1b10cac4d024e66d290683b8ffd2a656ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Javier=20Rodr=C3=ADguez=20Zas?= Date: Wed, 8 Mar 2023 13:45:33 +0100 Subject: [PATCH 2/3] handles PR reviews - fix some typos - updated dev docs - added PR examples to tests - improve method naming style --- .../modules/launcher/plugins/calculator.md | 7 ++- .../QueryTests.cs | 16 +++++-- .../CalculateHelper.cs | 46 ++++++++++++++----- 3 files changed, 53 insertions(+), 16 deletions(-) diff --git a/doc/devdocs/modules/launcher/plugins/calculator.md b/doc/devdocs/modules/launcher/plugins/calculator.md index 79ffe208a84..e3050a52796 100644 --- a/doc/devdocs/modules/launcher/plugins/calculator.md +++ b/doc/devdocs/modules/launcher/plugins/calculator.md @@ -21,7 +21,12 @@ The Calculator plugin as the name suggests is used to perform calculations on th ### [`CalculateHelper`](/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs) - The [`CalculateHelper.cs`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs) class checks to see if the user entered query is a valid input to the calculator and only if the input is valid does it perform the operation. -- It does so by matching the user query to a valid regex. + - It does so by matching the user query to a valid regex. +- This class also handles some human multiplication expression like `2(1+2)` and `(2+3)(3+4)` in order to be computed by `Mages` lib. + - It does so by matching some regex and inserting `'*'` where appropriate, e.g: `2(1+2) -> 2 * (1+2)` + - It takes into account the combination of numbers (`num`), constants (`const`), functions (`func`) and expressions in parentheses (`(exp)`). + - The blank spaces between them is also considered. + - Some combinations were not handled as they are not common such as `'const num'` or `'func const'` ### [`CalculateEngine`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs) - The main computation is done in the [`CalculateEngine.cs`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs) file using the `Mages` library. diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs index acd5f7e00ce..d5e11bbc8a7 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator.UnitTest/QueryTests.cs @@ -113,6 +113,16 @@ public void NoErrorForDivisionByNumberWithDecimalDigits(string typedString) } [DataTestMethod] + [DataRow("pie", "pi * e")] + [DataRow("eln(100)", "e * ln(100)")] + [DataRow("pi(1+1)", "pi * (1+1)")] + [DataRow("2pi", "2 * pi")] + [DataRow("2log10(100)", "2 * log10(100)")] + [DataRow("2(3+4)", "2 * (3+4)")] + [DataRow("sin(pi)cos(pi)", "sin(pi) * cos(pi)")] + [DataRow("log10(100)(2+3)", "log10(100) * (2+3)")] + [DataRow("(1+1)cos(pi)", "(1+1) * cos(pi)")] + [DataRow("(1+1)(2+2)", "(1+1) * (2+2)")] [DataRow("2(1+1)", "2 * (1+1)")] [DataRow("pi(1+1)", "pi * (1+1)")] [DataRow("pilog(100)", "pi * log(100)")] @@ -134,7 +144,7 @@ public void NoErrorForDivisionByNumberWithDecimalDigits(string typedString) [DataRow("pipipie", "pi * pi * pi * e")] [DataRow("(1+1)(3+2)(1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] [DataRow("(1+1) (3+2) (1+1)(1+1)", "(1+1) * (3+2) * (1+1) * (1+1)")] - public void RightHumaMultiplicationExpressionTransformation(string typedString, string expectedQuery) + public void RightHumanMultiplicationExpressionTransformation(string typedString, string expectedQuery) { // Setup @@ -167,7 +177,7 @@ public void RightHumaMultiplicationExpressionTransformation(string typedString, [DataRow("pipipie")] [DataRow("(1+1)(3+2)(1+1)(1+1)")] [DataRow("(1+1) (3+2) (1+1)(1+1)")] - public void NoErrorForHumaMultiplicationExpressions(string typedString) + public void NoErrorForHumanMultiplicationExpressions(string typedString) { // Setup Mock
main = new(); @@ -192,7 +202,7 @@ public void NoErrorForHumaMultiplicationExpressions(string typedString) [DataRow("(1+1)(3+2)", "10")] [DataRow("(1+1)cos(pi)", "-2")] [DataRow("log(100)cos(pi)", "-2")] - public void RightAnswerForHumaMultiplicationExpressions(string typedString, string answer) + public void RightAnswerForHumanMultiplicationExpressions(string typedString, string answer) { // Setup Mock
main = new(); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs index 0cc60810753..50b94b1e541 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateHelper.cs @@ -51,16 +51,20 @@ public static bool InputValid(string input) public static string FixHumanMultiplicationExpressions(string input) { - var output = Check_NumberOrConstant_ParenthesisExpr(input); - output = Check_NumberOrConstant_Func(output); - output = Check_ParenthesisExpr_Func(output); - output = Check_ParenthesisExpr_ParenthesisExpr(output); - output = Check_Number_Constant(output); - output = Check_Constant_Constant(output); + var output = CheckNumberOrConstantThenParenthesisExpr(input); + output = CheckNumberOrConstantThenFunc(output); + output = CheckParenthesisExprThenFunc(output); + output = CheckParenthesisExprThenParenthesisExpr(output); + output = CheckNumberThenConstant(output); + output = CheckConstantThenConstant(output); return output; } - private static string Check_NumberOrConstant_ParenthesisExpr(string input) + /* + * num (exp) + * const (exp) + */ + private static string CheckNumberOrConstantThenParenthesisExpr(string input) { var output = input; do @@ -81,7 +85,11 @@ private static string Check_NumberOrConstant_ParenthesisExpr(string input) return output; } - private static string Check_NumberOrConstant_Func(string input) + /* + * num func + * const func + */ + private static string CheckNumberOrConstantThenFunc(string input) { var output = input; do @@ -107,21 +115,32 @@ private static string Check_NumberOrConstant_Func(string input) return output; } - private static string Check_ParenthesisExpr_Func(string input) + /* + * (exp) func + * func func + */ + private static string CheckParenthesisExprThenFunc(string input) { var p = @"(\))\s*([a-zA-Z]+[0-9]*\s*\()"; var r = "$1 * $2"; return Regex.Replace(input, p, r); } - private static string Check_ParenthesisExpr_ParenthesisExpr(string input) + /* + * (exp) (exp) + * func (exp) + */ + private static string CheckParenthesisExprThenParenthesisExpr(string input) { var p = @"(\))\s*(\()"; var r = "$1 * $2"; return Regex.Replace(input, p, r); } - private static string Check_Number_Constant(string input) + /* + * num const + */ + private static string CheckNumberThenConstant(string input) { var output = input; do @@ -142,7 +161,10 @@ private static string Check_Number_Constant(string input) return output; } - private static string Check_Constant_Constant(string input) + /* + * const const + */ + private static string CheckConstantThenConstant(string input) { var output = input; do From 1aba0037e311e786d82251185b93c1220b40be90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Javier=20Rodr=C3=ADguez=20Zas=20=28JJ=29?= Date: Fri, 10 Mar 2023 14:09:03 +0100 Subject: [PATCH 3/3] Fix typo Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> --- doc/devdocs/modules/launcher/plugins/calculator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/devdocs/modules/launcher/plugins/calculator.md b/doc/devdocs/modules/launcher/plugins/calculator.md index e3050a52796..d9ac8e1e56a 100644 --- a/doc/devdocs/modules/launcher/plugins/calculator.md +++ b/doc/devdocs/modules/launcher/plugins/calculator.md @@ -25,7 +25,7 @@ The Calculator plugin as the name suggests is used to perform calculations on th - This class also handles some human multiplication expression like `2(1+2)` and `(2+3)(3+4)` in order to be computed by `Mages` lib. - It does so by matching some regex and inserting `'*'` where appropriate, e.g: `2(1+2) -> 2 * (1+2)` - It takes into account the combination of numbers (`num`), constants (`const`), functions (`func`) and expressions in parentheses (`(exp)`). - - The blank spaces between them is also considered. + - The blank spaces between them are also considered. - Some combinations were not handled as they are not common such as `'const num'` or `'func const'` ### [`CalculateEngine`](src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/CalculateEngine.cs)