2020-09-10 05:01:30 +02:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System ;
using System.Text.RegularExpressions ;
2021-01-20 11:38:52 +01:00
namespace Microsoft.PowerToys.Run.Plugin.Calculator
2020-09-10 05:01:30 +02:00
{
public static class CalculateHelper
{
private static readonly Regex RegValidExpressChar = new Regex (
@"^(" +
2021-06-22 08:54:28 -07:00
@"%|" +
2023-02-22 17:25:48 +01:00
@"ceil\s*\(|floor\s*\(|exp\s*\(|max\s*\(|min\s*\(|abs\s*\(|log(?:2|10)?\s*\(|ln\s*\(|sqrt\s*\(|pow\s*\(|" +
2020-10-21 16:19:37 -07:00
@"factorial\s*\(|sign\s*\(|round\s*\(|rand\s*\(|" +
@"sin\s*\(|cos\s*\(|tan\s*\(|arcsin\s*\(|arccos\s*\(|arctan\s*\(|" +
2021-01-15 16:05:56 +01:00
@"sinh\s*\(|cosh\s*\(|tanh\s*\(|arsinh\s*\(|arcosh\s*\(|artanh\s*\(|" +
2020-10-21 16:19:37 -07:00
@"pi|" +
2020-09-10 05:01:30 +02:00
@"==|~=|&&|\|\||" +
2021-12-13 17:01:12 +00:00
@"e|[0-9]|0x[0-9a-fA-F]+|0b[01]+|[\+\-\*\/\^\., ""]|[\(\)\|\!\[\]]" +
2022-12-18 14:27:14 +01:00
@")+$" ,
RegexOptions . Compiled ) ;
2020-09-10 05:01:30 +02:00
public static bool InputValid ( string input )
{
if ( string . IsNullOrWhiteSpace ( input ) )
{
throw new ArgumentNullException ( paramName : nameof ( input ) ) ;
}
if ( ! RegValidExpressChar . IsMatch ( input ) )
{
return false ;
}
if ( ! BracketHelper . IsBracketComplete ( input ) )
{
return false ;
}
2022-08-26 17:40:15 +02:00
// If the input ends with a binary operator then it is not a valid input to mages and the Interpret function would throw an exception. Because we expect here that the user has not finished typing we block those inputs.
2020-10-21 16:19:37 -07:00
string trimmedInput = input . TrimEnd ( ) ;
2022-08-26 17:40:15 +02:00
if ( trimmedInput . EndsWith ( '+' ) | | trimmedInput . EndsWith ( '-' ) | | trimmedInput . EndsWith ( '*' ) | | trimmedInput . EndsWith ( '|' ) | | trimmedInput . EndsWith ( '\\' ) | | trimmedInput . EndsWith ( '^' ) | | trimmedInput . EndsWith ( '=' ) | | trimmedInput . EndsWith ( '&' ) | | trimmedInput . EndsWith ( '/' ) | | trimmedInput . EndsWith ( '%' ) )
2020-10-21 16:19:37 -07:00
{
return false ;
}
2020-09-10 05:01:30 +02:00
return true ;
}
2023-03-10 21:48:04 +01:00
public static string FixHumanMultiplicationExpressions ( string input )
{
var output = CheckNumberOrConstantThenParenthesisExpr ( input ) ;
output = CheckNumberOrConstantThenFunc ( output ) ;
output = CheckParenthesisExprThenFunc ( output ) ;
output = CheckParenthesisExprThenParenthesisExpr ( output ) ;
output = CheckNumberThenConstant ( output ) ;
output = CheckConstantThenConstant ( output ) ;
return output ;
}
/ *
* num ( exp )
* const ( exp )
* /
private static string CheckNumberOrConstantThenParenthesisExpr ( 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 ;
}
/ *
* num func
* const func
* /
private static string CheckNumberOrConstantThenFunc ( 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 ;
}
/ *
* ( 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 ) ;
}
/ *
* ( exp ) ( exp )
* func ( exp )
* /
private static string CheckParenthesisExprThenParenthesisExpr ( string input )
{
var p = @"(\))\s*(\()" ;
var r = "$1 * $2" ;
return Regex . Replace ( input , p , r ) ;
}
/ *
* num const
* /
private static string CheckNumberThenConstant ( 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 ;
}
/ *
* const const
* /
private static string CheckConstantThenConstant ( 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 ;
}
2020-09-10 05:01:30 +02:00
}
}