|
1 | 1 | using System; |
2 | | -using System.Collections.Generic; |
3 | | -using System.Linq; |
4 | | -using System.Runtime.CompilerServices; |
5 | 2 |
|
6 | | -[assembly: InternalsVisibleTo("StringMath.Tests")] |
7 | 3 | namespace StringMath |
8 | 4 | { |
9 | | - public static class Calculator |
| 5 | + public class Calculator |
10 | 6 | { |
11 | | - internal static MathContext MathContext { get; } = new MathContext(); |
| 7 | + private readonly MathContext _mathContext = new MathContext(); |
| 8 | + private readonly Replacements _replacements; |
12 | 9 |
|
13 | | - public static void AddOperator(string operatorName, Func<decimal, decimal, decimal> operation) |
14 | | - => MathContext.AddBinaryOperator(operatorName, operation); |
| 10 | + private static Reducer Reducer { get; } = new Reducer(); |
15 | 11 |
|
16 | | - public static void AddOperator(string operatorName, Func<decimal, decimal> operation) |
17 | | - => MathContext.AddUnaryOperator(operatorName, operation); |
18 | | - |
19 | | - private static readonly Dictionary<Type, Func<Expression, Replacement[], Expression>> _expressionEvaluators = new Dictionary<Type, Func<Expression, Replacement[], Expression>> |
20 | | - { |
21 | | - [typeof(BinaryExpression)] = EvaluateBinaryExpression, |
22 | | - [typeof(UnaryExpression)] = EvaluateUnaryExpression, |
23 | | - [typeof(ConstantExpression)] = EvaluateConstantExpression, |
24 | | - [typeof(GroupingExpression)] = EvaluateGroupingExpression, |
25 | | - [typeof(ReplacementExpression)] = EvaluateReplacementExpression |
26 | | - }; |
27 | | - |
28 | | - public static decimal Evaluate(string expression) |
29 | | - => Evaluate(expression, default); |
30 | | - |
31 | | - public static decimal Evaluate(string expression, params Replacement[] replacements) |
32 | | - { |
33 | | - SourceText text = new SourceText(expression); |
34 | | - Lexer lex = new Lexer(text, MathContext); |
35 | | - Parser parse = new Parser(lex, MathContext); |
36 | | - |
37 | | - Expression expr = Reduce(parse.Parse(), replacements); |
38 | | - return ((NumberExpression)expr).Value; |
39 | | - } |
40 | | - |
41 | | - internal static Expression Reduce(Expression expression, Replacement[] replacements) |
42 | | - { |
43 | | - if (expression is NumberExpression) |
| 12 | + /// <summary> |
| 13 | + /// Create an instance of a Calculator which has it's own operators and variable definitions. |
| 14 | + /// </summary> |
| 15 | + /// <param name="replacements"></param> |
| 16 | + public Calculator(Replacements replacements = default) |
| 17 | + => _replacements = replacements ?? new Replacements |
44 | 18 | { |
45 | | - return expression; |
46 | | - } |
47 | | - |
48 | | - var expr = _expressionEvaluators[expression.Type](expression, replacements); |
49 | | - return Reduce(expr, replacements); |
50 | | - } |
51 | | - |
52 | | - private static Expression EvaluateConstantExpression(Expression arg, Replacement[] replacements) |
53 | | - => new NumberExpression(decimal.Parse(((ConstantExpression)arg).Value)); |
54 | | - |
55 | | - private static Expression EvaluateGroupingExpression(Expression arg, Replacement[] replacements) |
56 | | - => ((GroupingExpression)arg).Inner; |
57 | | - |
58 | | - private static Expression EvaluateUnaryExpression(Expression arg, Replacement[] replacements) |
| 19 | + ["PI"] = (decimal)Math.PI, |
| 20 | + ["E"] = (decimal)Math.E |
| 21 | + }; |
| 22 | + |
| 23 | + /// <summary> |
| 24 | + /// Add a new binary operator or overwrite an existing operator's logic. |
| 25 | + /// </summary> |
| 26 | + /// <param name="operatorName">The operator's string representation.</param> |
| 27 | + /// <param name="operation">The operation to execute for this operator.</param> |
| 28 | + /// <param name="precedence">Logarithmic precedence by default.</param> |
| 29 | + public void AddOperator(string operatorName, Func<decimal, decimal, decimal> operation, Precedence precedence = default) |
| 30 | + => _mathContext.AddBinaryOperator(operatorName, operation, precedence); |
| 31 | + |
| 32 | + /// <summary> |
| 33 | + /// Add a new unary operator or overwrite an existing operator's logic. |
| 34 | + /// <see cref="Precedence"/> is always <see cref="Precedence.Prefix" /> |
| 35 | + /// </summary> |
| 36 | + /// <param name="operatorName">The operator's string representation.</param> |
| 37 | + /// <param name="operation">The operation to execute for this operator.</param> |
| 38 | + public void AddOperator(string operatorName, Func<decimal, decimal> operation) |
| 39 | + => _mathContext.AddUnaryOperator(operatorName, operation); |
| 40 | + |
| 41 | + /// <summary> |
| 42 | + /// Evaluates a mathematical expression which can contain variables and returns a decimal value. |
| 43 | + /// </summary> |
| 44 | + /// <param name="expression">The math expression to evaluate.</param> |
| 45 | + /// <returns>The result as a decimal value.</returns> |
| 46 | + public decimal Evaluate(string expression) |
59 | 47 | { |
60 | | - var unary = (UnaryExpression)arg; |
61 | | - var value = (NumberExpression)Reduce(unary.Operand, replacements); |
62 | | - |
63 | | - var result = MathContext.EvaluateUnary(unary.OperatorName, value.Value); |
64 | | - return new NumberExpression(result); |
65 | | - } |
66 | | - |
67 | | - private static Expression EvaluateBinaryExpression(Expression expr, Replacement[] replacements) |
68 | | - { |
69 | | - var binary = (BinaryExpression)expr; |
70 | | - var left = (NumberExpression)Reduce(binary.Left, replacements); |
71 | | - var right = (NumberExpression)Reduce(binary.Right, replacements); |
| 48 | + SourceText text = new SourceText(expression); |
| 49 | + Lexer lex = new Lexer(text, _mathContext); |
| 50 | + Parser parse = new Parser(lex, _mathContext); |
72 | 51 |
|
73 | | - var result = MathContext.EvaluateBinary(binary.OperatorName, left.Value, right.Value); |
74 | | - return new NumberExpression(result); |
| 52 | + return Reducer.Reduce<ResultExpression>(parse.Parse(), _mathContext, _replacements).Value; |
75 | 53 | } |
76 | 54 |
|
77 | | - private static Expression EvaluateReplacementExpression(Expression expr, Replacement[] replacements) |
78 | | - { |
79 | | - var replacement = (ReplacementExpression)expr; |
80 | | - var value = replacements.FirstOrDefault(r => r.Identifier == replacement.Name); |
81 | | - |
82 | | - return new NumberExpression(value.Value); |
83 | | - } |
| 55 | + /// <summary> |
| 56 | + /// Replaces the value of a variable. |
| 57 | + /// </summary> |
| 58 | + /// <param name="name">The variable's name.</param> |
| 59 | + /// <param name="value">The new value.</param> |
| 60 | + public void Replace(string name, decimal value) |
| 61 | + => _replacements[name] = value; |
84 | 62 | } |
85 | 63 | } |
0 commit comments