-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMathEnv.cs
More file actions
116 lines (90 loc) · 4.19 KB
/
MathEnv.cs
File metadata and controls
116 lines (90 loc) · 4.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
namespace MathExpressionParser;
public class MathFunc(int argCount, Func<decimal[], decimal> function) {
public readonly int ArgCount = argCount;
public readonly Func<decimal[], decimal> Function = function;
}
public class MathEnv {
public readonly Dictionary<string, decimal> Variables = new();
public readonly Dictionary<string, MathFunc> Functions = new();
public MathEnv() {
Variables["pi"] = (decimal) Math.PI;
Variables["e"] = (decimal) Math.E;
Functions["sin"] = new MathFunc(1, args => (decimal)Math.Sin((double)args[0]));
Functions["cos"] = new MathFunc(1, args => (decimal)Math.Cos((double)args[0]));
Functions["tan"] = new MathFunc(1, args => (decimal)Math.Tan((double)args[0]));
Functions["atan"] = new MathFunc(1, args => (decimal)Math.Atan((double)args[0]));
Functions["atan2"] = new MathFunc(2, args => (decimal)Math.Atan2((double)args[0], (double)args[1]));
}
public decimal EvalExpression(Expression expr) {
if (expr is NumberExpr numberExpr) {
return numberExpr.Number;
}
if (expr is VariableExpr varExpr) {
string name = varExpr.Name;
decimal? number = Variables.GetValueOrDefault(name);
if (number == null) {
throw new MathEvalException($"Unknown variable \"{name}\"");
}
return number.Value;
}
if (expr is CallExpr callExpr) {
string name = callExpr.Name;
var definition = Functions.GetValueOrDefault(name);
if (definition == null) {
throw new MathEvalException($"Unknown function \"{name}\"");
}
var args = callExpr.Args;
int argCount = args.Length;
int definedArgCount = definition.ArgCount;
if (argCount != definedArgCount) {
throw new MathEvalException($"Incorrect argument count for function \"{name}\" (expected {definedArgCount}, got {argCount})");
}
var evaluatedArgs = new decimal[definedArgCount];
for (int i = 0; i < definedArgCount; i++) {
evaluatedArgs[i] = EvalExpression(args[i]);
}
return definition.Function(evaluatedArgs);
}
if (expr is UnaryExpr unaryExpr) {
if (unaryExpr.Op == TokenType.Minus) {
return -EvalExpression(unaryExpr.Subject);
}
throw new MathEvalException("Unsupported unary operator");
}
if (expr is BinaryExpr binaryExpr) {
return binaryExpr.Op switch {
TokenType.Plus => EvalExpression(binaryExpr.Left) + EvalExpression(binaryExpr.Right),
TokenType.Minus => EvalExpression(binaryExpr.Left) - EvalExpression(binaryExpr.Right),
TokenType.Star => EvalExpression(binaryExpr.Left) * EvalExpression(binaryExpr.Right),
TokenType.Slash => EvalExpression(binaryExpr.Left) / EvalExpression(binaryExpr.Right),
TokenType.Caret => (decimal)Math.Pow(
(double)EvalExpression(binaryExpr.Left),
(double)EvalExpression(binaryExpr.Right)
),
_ => throw new MathEvalException("Unsupported binary operator")
};
}
throw new MathEvalException("Unsupported expression type");
}
public decimal EvalMathString(string mathString) {
var tokens = new MathLexer(mathString).Lex();
var parser = new MathParser(tokens);
var expr = parser.Expr();
if (!parser.Finished()) {
throw new MathParseException("Unexpected extra tokens");
}
return EvalExpression(expr);
}
// method used for testing the parser
public static void Main() {
var env = new MathEnv();
decimal result;
try {
result = env.EvalMathString("1 + 1 * (2 * 2) + sin(3) + sin(--pi)^2 / 123^2");
} catch (MathException m) {
Console.WriteLine($"Couldn't parse the expression: {m.Message}");
return;
}
Console.WriteLine($"The result is {result}");
}
}