257 lines
5.5 KiB
C#
257 lines
5.5 KiB
C#
using System;
|
||
using System.Globalization;
|
||
|
||
namespace AxCopilot.Handlers;
|
||
|
||
internal static class MathEvaluator
|
||
{
|
||
private class Evaluator
|
||
{
|
||
private readonly string _s;
|
||
|
||
private int _i;
|
||
|
||
public Evaluator(string s)
|
||
{
|
||
_s = s;
|
||
_i = 0;
|
||
}
|
||
|
||
public double Parse()
|
||
{
|
||
double result = ParseExpr();
|
||
if (_i < _s.Length)
|
||
{
|
||
throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
|
||
}
|
||
return result;
|
||
}
|
||
|
||
private double ParseExpr()
|
||
{
|
||
double num = ParseTerm();
|
||
while (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-'))
|
||
{
|
||
char c = _s[_i++];
|
||
double num2 = ParseTerm();
|
||
num = ((c == '+') ? (num + num2) : (num - num2));
|
||
}
|
||
return num;
|
||
}
|
||
|
||
private double ParseTerm()
|
||
{
|
||
double num = ParsePower();
|
||
while (_i < _s.Length && (_s[_i] == '*' || _s[_i] == '/' || _s[_i] == '%'))
|
||
{
|
||
char c = _s[_i++];
|
||
double num2 = ParsePower();
|
||
num = c switch
|
||
{
|
||
'/' => num / num2,
|
||
'*' => num * num2,
|
||
_ => num % num2,
|
||
};
|
||
}
|
||
return num;
|
||
}
|
||
|
||
private double ParsePower()
|
||
{
|
||
double num = ParseUnary();
|
||
if (_i < _s.Length && _s[_i] == '^')
|
||
{
|
||
_i++;
|
||
double y = ParseUnary();
|
||
return Math.Pow(num, y);
|
||
}
|
||
return num;
|
||
}
|
||
|
||
private double ParseUnary()
|
||
{
|
||
if (_i < _s.Length && _s[_i] == '-')
|
||
{
|
||
_i++;
|
||
return 0.0 - ParsePrimary();
|
||
}
|
||
if (_i < _s.Length && _s[_i] == '+')
|
||
{
|
||
_i++;
|
||
return ParsePrimary();
|
||
}
|
||
return ParsePrimary();
|
||
}
|
||
|
||
private double ParsePrimary()
|
||
{
|
||
if (_i >= _s.Length)
|
||
{
|
||
throw new InvalidOperationException("수식이 불완전합니다.");
|
||
}
|
||
if (_i + 1 < _s.Length && _s[_i] == '0' && _s[_i + 1] == 'x')
|
||
{
|
||
_i += 2;
|
||
int i = _i;
|
||
while (_i < _s.Length && "0123456789abcdef".Contains(_s[_i]))
|
||
{
|
||
_i++;
|
||
}
|
||
string s = _s;
|
||
int num = i;
|
||
return Convert.ToInt64(s.Substring(num, _i - num), 16);
|
||
}
|
||
if (char.IsDigit(_s[_i]) || _s[_i] == '.')
|
||
{
|
||
int i2 = _i;
|
||
while (_i < _s.Length && (char.IsDigit(_s[_i]) || _s[_i] == '.'))
|
||
{
|
||
_i++;
|
||
}
|
||
if (_i < _s.Length && _s[_i] == 'e')
|
||
{
|
||
_i++;
|
||
if (_i < _s.Length && (_s[_i] == '+' || _s[_i] == '-'))
|
||
{
|
||
_i++;
|
||
}
|
||
while (_i < _s.Length && char.IsDigit(_s[_i]))
|
||
{
|
||
_i++;
|
||
}
|
||
}
|
||
string s2 = _s;
|
||
int num = i2;
|
||
return double.Parse(s2.Substring(num, _i - num), NumberStyles.Float, CultureInfo.InvariantCulture);
|
||
}
|
||
if (_s[_i] == '(')
|
||
{
|
||
_i++;
|
||
double result = ParseExpr();
|
||
if (_i < _s.Length && _s[_i] == ')')
|
||
{
|
||
_i++;
|
||
}
|
||
return result;
|
||
}
|
||
if (char.IsLetter(_s[_i]))
|
||
{
|
||
int i3 = _i;
|
||
while (_i < _s.Length && (char.IsLetterOrDigit(_s[_i]) || _s[_i] == '_'))
|
||
{
|
||
_i++;
|
||
}
|
||
string s3 = _s;
|
||
int num = i3;
|
||
string text = s3.Substring(num, _i - num);
|
||
switch (text)
|
||
{
|
||
case "pi":
|
||
return Math.PI;
|
||
case "e":
|
||
return Math.E;
|
||
case "inf":
|
||
return double.PositiveInfinity;
|
||
default:
|
||
if (_i < _s.Length && _s[_i] == '(')
|
||
{
|
||
_i++;
|
||
double num2 = ParseExpr();
|
||
double? num3 = null;
|
||
if (_i < _s.Length && _s[_i] == ',')
|
||
{
|
||
_i++;
|
||
num3 = ParseExpr();
|
||
}
|
||
if (_i < _s.Length && _s[_i] == ')')
|
||
{
|
||
_i++;
|
||
}
|
||
if (1 == 0)
|
||
{
|
||
}
|
||
double result2;
|
||
switch (text)
|
||
{
|
||
case "sqrt":
|
||
result2 = Math.Sqrt(num2);
|
||
break;
|
||
case "abs":
|
||
result2 = Math.Abs(num2);
|
||
break;
|
||
case "ceil":
|
||
result2 = Math.Ceiling(num2);
|
||
break;
|
||
case "floor":
|
||
result2 = Math.Floor(num2);
|
||
break;
|
||
case "round":
|
||
result2 = (num3.HasValue ? Math.Round(num2, (int)num3.Value) : Math.Round(num2));
|
||
break;
|
||
case "sin":
|
||
result2 = Math.Sin(num2 * Math.PI / 180.0);
|
||
break;
|
||
case "cos":
|
||
result2 = Math.Cos(num2 * Math.PI / 180.0);
|
||
break;
|
||
case "tan":
|
||
result2 = Math.Tan(num2 * Math.PI / 180.0);
|
||
break;
|
||
case "asin":
|
||
result2 = Math.Asin(num2) * 180.0 / Math.PI;
|
||
break;
|
||
case "acos":
|
||
result2 = Math.Acos(num2) * 180.0 / Math.PI;
|
||
break;
|
||
case "atan":
|
||
result2 = Math.Atan(num2) * 180.0 / Math.PI;
|
||
break;
|
||
case "log":
|
||
result2 = (num3.HasValue ? Math.Log(num2, num3.Value) : Math.Log10(num2));
|
||
break;
|
||
case "log2":
|
||
result2 = Math.Log2(num2);
|
||
break;
|
||
case "ln":
|
||
result2 = Math.Log(num2);
|
||
break;
|
||
case "exp":
|
||
result2 = Math.Exp(num2);
|
||
break;
|
||
case "pow":
|
||
if (!num3.HasValue)
|
||
{
|
||
throw new InvalidOperationException("pow(x,y) 형식으로 사용하세요.");
|
||
}
|
||
result2 = Math.Pow(num2, num3.Value);
|
||
break;
|
||
case "min":
|
||
result2 = (num3.HasValue ? Math.Min(num2, num3.Value) : num2);
|
||
break;
|
||
case "max":
|
||
result2 = (num3.HasValue ? Math.Max(num2, num3.Value) : num2);
|
||
break;
|
||
default:
|
||
throw new InvalidOperationException("알 수 없는 함수: " + text + "()");
|
||
}
|
||
if (1 == 0)
|
||
{
|
||
}
|
||
return result2;
|
||
}
|
||
throw new InvalidOperationException("알 수 없는 식별자: " + text);
|
||
}
|
||
}
|
||
throw new InvalidOperationException($"예기치 않은 문자: '{_s[_i]}'");
|
||
}
|
||
}
|
||
|
||
public static double Evaluate(string expr)
|
||
{
|
||
Evaluator evaluator = new Evaluator(expr.Replace(" ", "").Replace("×", "*").Replace("÷", "/")
|
||
.Replace(",", ",")
|
||
.ToLowerInvariant());
|
||
return evaluator.Parse();
|
||
}
|
||
}
|