From eaeb64b3144b49217f207fbdea2fe7e2dc1c07ee Mon Sep 17 00:00:00 2001
From: Jim <112640460+0xJ1M@users.noreply.github.com>
Date: Thu, 31 Aug 2023 22:03:17 +0100
Subject: [PATCH] Refactor
---
MathEngine/MathEngine/Evaluator/Evaluator.cs | 74 ++++++++++
.../Parser/Parser/ExpressionTree.cs | 138 +-----------------
.../MathEngine/Parser/Parser/TreeGenerator.cs | 81 ++++++++++
3 files changed, 160 insertions(+), 133 deletions(-)
create mode 100644 MathEngine/MathEngine/Evaluator/Evaluator.cs
create mode 100644 MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
diff --git a/MathEngine/MathEngine/Evaluator/Evaluator.cs b/MathEngine/MathEngine/Evaluator/Evaluator.cs
new file mode 100644
index 0000000..f3f0e8c
--- /dev/null
+++ b/MathEngine/MathEngine/Evaluator/Evaluator.cs
@@ -0,0 +1,74 @@
+using MathEngine.Parser.Parser;
+using MathEngine.Parser.Tokeniser;
+
+namespace MathEngine.Parser.Evaluator
+{
+ ///
+ ///
+ ///
+ internal class Evaluator
+ {
+ public static TreeNode? Evaluate(TreeNode rootNode)
+ {
+ if (rootNode == null)
+ {
+ return null;
+ }
+ else // To evaluate we go anti-clockwise around the tree
+ {
+ TreeNode? Root, LeftBranch, RightBranch;
+ if (rootNode.NodeValue.Token_Type == Token.Type.Numeric)
+ return rootNode;
+ else
+ { //For now these can't be null, this will need to be updated in the future
+ LeftBranch = Evaluate_Tree_Branch(rootNode.GetChildNode(0));
+ RightBranch = Evaluate_Tree_Branch(rootNode.GetChildNode(1));
+ Root = Evaluate_Operator(rootNode.NodeValue, LeftBranch, RightBranch);
+ return Root;
+ }
+ }
+ }
+
+
+ ///
+ /// Evaluates branches of a given tree
+ ///
+ /// The TreeNode branch to evaluate
+ /// Returns the Evaluation of the Branch
+ private static TreeNode Evaluate_Tree_Branch(TreeNode Branch)
+ {
+ TreeNode Root, LeftBranch, RightBranch;
+ if (Branch.NodeValue.Token_Type == Token.Type.Numeric)
+ return Branch;
+ else
+ {
+ LeftBranch = Evaluate_Tree_Branch(Branch.GetChildNode(0));
+ RightBranch = Evaluate_Tree_Branch(Branch.GetChildNode(1));
+ // We finally combine the computed branches with the operator that links them and return the result
+ Root = Evaluate_Operator(Branch.NodeValue, LeftBranch, RightBranch);
+ return Root;
+ }
+ }
+
+ ///
+ /// Evlautes a binary node where the root node is an operator and given two branches the left and the right
+ ///
+ ///
+ ///
+ ///
+ /// Returns the evaluated value of the operator as a TreeNode
+ private static TreeNode Evaluate_Operator(Token Operator_Token, TreeNode Left_Branch, TreeNode Right_Branch)
+ {
+ decimal lhs = decimal.Parse(Left_Branch.NodeValue.TokenValue);
+ decimal rhs = decimal.Parse(Right_Branch.NodeValue.TokenValue);
+ return Operator_Token.Token_Type switch
+ {
+ Token.Type.Addition => new TreeNode(new Token((lhs + rhs).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
+ Token.Type.Subtraction => new TreeNode(new Token((lhs - rhs).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
+ Token.Type.Multiplication => new TreeNode(new Token(((decimal)(lhs * rhs)).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
+ Token.Type.Division => new TreeNode(new Token(((decimal)(lhs / rhs)).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
+ _ => throw new Exception("Potentially invalid token?"),
+ };
+ }
+ }
+}
diff --git a/MathEngine/MathEngine/Parser/Parser/ExpressionTree.cs b/MathEngine/MathEngine/Parser/Parser/ExpressionTree.cs
index 18ba17a..6f36a1e 100644
--- a/MathEngine/MathEngine/Parser/Parser/ExpressionTree.cs
+++ b/MathEngine/MathEngine/Parser/Parser/ExpressionTree.cs
@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using MathEngine.Parser.Tokeniser;
+using MathEngine.Parser.Tokeniser;
namespace MathEngine.Parser.Parser
{
///
@@ -11,6 +10,7 @@ namespace MathEngine.Parser.Parser
/// The root node of the expression tree;
///
private readonly TreeNode rootNode;
+ private TreeNode? evaluated_expression;
///
/// Initialises a new instance of the MathEngine.Parser.Parser.Node class with a given Token
@@ -21,7 +21,7 @@ namespace MathEngine.Parser.Parser
{
List tokens = Tokeniser.Tokeniser.Tokenise(Expression);
Stack rpnForm = Parser.Parse(tokens);
- rootNode = GenerateExpressionTree(rpnForm);
+ rootNode = TreeGenerator.TreeFromRPN(rpnForm);
}
private ExpressionTree(TreeNode rootNode)
@@ -29,141 +29,13 @@ namespace MathEngine.Parser.Parser
this.rootNode = rootNode;
}
- ///
- /// Creates a binary TreeNode, that is a node with a root value and two children
- ///
- /// The token to be the root node of the TreeNode
- /// TreeNode that is the left branch of the current node
- /// TreeNode that is the right branch of the current node
- /// A TreeNode with CurrentToken as the root value and LeftBranch and RightBranch as Children
- private static TreeNode CreateBinaryNode(Token CurrentToken, TreeNode LeftBranch, TreeNode RightBranch)
- {
- TreeNode root = new(CurrentToken);
- root.AddChildNode(LeftBranch);
- root.AddChildNode(RightBranch);
- return root;
- }
-
- ///
- /// Creates a unary TreeNode, that is a node with a root value and two children
- ///
- /// The token to be the root node of the TreeNode
- /// TreeNode that is the child of the current node
- /// A TreeNode with CurrentToken as the root value and ChildNode as the sole child node
- private static TreeNode CreateUnaryNode(Token CurrentToken, TreeNode ChildNode)
- {
- TreeNode root = new(CurrentToken);
- root.AddChildNode(ChildNode);
- return root;
- }
-
- ///
- /// Generates the full expression tree given an RPN expression stack
- ///
- /// RPN expression stack to generate an expression tree from
- /// An expression Tree that represents the Mathematical expression given by rpnExpression
- private static TreeNode GenerateExpressionTree(Stack rpnExpression)
- {
- Stack OutputStack = new(rpnExpression.Count);
- TreeNode Node;
- Token CurrentToken;
- while (rpnExpression.Count != 0)
- {
- CurrentToken = rpnExpression.Pop();
- switch (CurrentToken.Token_Type)
- {
- case Token.Type.Numeric:
- Node = new TreeNode(CurrentToken);
- OutputStack.Push(Node);
- break;
- // We need to preserve "Left handness" of the ExpressionTree
- // i.e 7/8 gives a root node of / with Cnode(0) = 7 and Cnod(1) = 8 etc.
- // This should preserve non commutativity
- case Token.Type.Addition:
- case Token.Type.Subtraction:
- case Token.Type.Multiplication:
- case Token.Type.Division:
- case Token.Type.Exponentiation:
- TreeNode Right = OutputStack.Pop();
- TreeNode Left = OutputStack.Pop();
- Node = CreateBinaryNode(CurrentToken, Left, Right);
- OutputStack.Push(Node);
- break;
- case Token.Type.UnaryPlus:
- case Token.Type.UnaryMinus:
- Node = CreateUnaryNode(CurrentToken, OutputStack.Pop());
- OutputStack.Push(Node);
- break;
- }
- }
- return OutputStack.Pop();
- }
-
-
- ///
- /// Evaluates branches of a given tree
- ///
- /// The TreeNode branch to evaluate
- /// Returns the Evaluation of the Branch
- private static TreeNode Evaluate_Tree_Branch(TreeNode Branch)
- {
- TreeNode Root, LeftBranch, RightBranch;
- if (Branch.NodeValue.Token_Type == Token.Type.Numeric)
- return Branch;
- else
- {
- LeftBranch = Evaluate_Tree_Branch(Branch.GetChildNode(0));
- RightBranch = Evaluate_Tree_Branch(Branch.GetChildNode(1));
- // We finally combine the computed branches with the operator that links them and return the result
- Root = Evaluate_Operator(Branch.NodeValue, LeftBranch, RightBranch);
- return Root;
- }
- }
-
- ///
- /// Evlautes a binary node where the root node is an operator and given two branches the left and the right
- ///
- ///
- ///
- ///
- /// Returns the evaluated value of the operator as a TreeNode
- private static TreeNode Evaluate_Operator(Token Operator_Token, TreeNode Left_Branch, TreeNode Right_Branch)
- {
- decimal lhs = decimal.Parse(Left_Branch.NodeValue.TokenValue);
- decimal rhs = decimal.Parse(Right_Branch.NodeValue.TokenValue);
- return Operator_Token.Token_Type switch
- {
- Token.Type.Addition => new TreeNode(new Token((lhs + rhs).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
- Token.Type.Subtraction => new TreeNode(new Token((lhs - rhs).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
- Token.Type.Multiplication => new TreeNode(new Token(((decimal)(lhs * rhs)).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
- Token.Type.Division => new TreeNode(new Token(((decimal)(lhs / rhs)).ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0)),
- _ => throw new Exception("Potentially invalid token?"),
- };
- }
-
///
/// Evaluates the current instance of ExpressionTree
///
/// Returns an update of the current instance which the expression Evaluated
- public ExpressionTree? Evaluate()
+ public ExpressionTree Evaluate()
{
- if (rootNode == null)
- {
- return null;
- }
- else // To evaluate we go anti-clockwise around the tree
- {
- TreeNode? Root, LeftBranch, RightBranch;
- if (rootNode.NodeValue.Token_Type == Token.Type.Numeric)
- return this;
- else
- { //For now these can't be null, this will need to be updated in the future
- LeftBranch = Evaluate_Tree_Branch(rootNode.GetChildNode(0));
- RightBranch = Evaluate_Tree_Branch(rootNode.GetChildNode(1));
- Root = Evaluate_Operator(rootNode.NodeValue, LeftBranch, RightBranch);
- return new ExpressionTree(Root);
- }
- }
+ return new(Evaluator.Evaluator.Evaluate(rootNode));
}
///
diff --git a/MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs b/MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
new file mode 100644
index 0000000..f6ba921
--- /dev/null
+++ b/MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
@@ -0,0 +1,81 @@
+using MathEngine.Parser.Tokeniser;
+
+namespace MathEngine.Parser.Parser
+{
+ ///
+ /// Class for converting from RPN form to an expression tree
+ ///
+ internal class TreeGenerator
+ {
+
+ ///
+ /// Creates a binary TreeNode, that is a node with a root value and two children
+ ///
+ /// The token to be the root node of the TreeNode
+ /// TreeNode that is the left branch of the current node
+ /// TreeNode that is the right branch of the current node
+ /// A TreeNode with CurrentToken as the root value and LeftBranch and RightBranch as Children
+ private static TreeNode CreateBinaryNode(Token CurrentToken, TreeNode LeftBranch, TreeNode RightBranch)
+ {
+ TreeNode root = new(CurrentToken);
+ root.AddChildNode(LeftBranch);
+ root.AddChildNode(RightBranch);
+ return root;
+ }
+
+ ///
+ /// Creates a unary TreeNode, that is a node with a root value and two children
+ ///
+ /// The token to be the root node of the TreeNode
+ /// TreeNode that is the child of the current node
+ /// A TreeNode with CurrentToken as the root value and ChildNode as the sole child node
+ private static TreeNode CreateUnaryNode(Token CurrentToken, TreeNode ChildNode)
+ {
+ TreeNode root = new(CurrentToken);
+ root.AddChildNode(ChildNode);
+ return root;
+ }
+
+ ///
+ /// Generates the full expression tree given an RPN expression stack
+ ///
+ /// RPN expression stack to generate an expression tree from
+ /// An expression Tree that represents the Mathematical expression given by rpnExpression
+ public static TreeNode TreeFromRPN(Stack rpnExpression)
+ {
+ Stack OutputStack = new(rpnExpression.Count);
+ TreeNode Node;
+ Token CurrentToken;
+ while (rpnExpression.Count != 0)
+ {
+ CurrentToken = rpnExpression.Pop();
+ switch (CurrentToken.Token_Type)
+ {
+ case Token.Type.Numeric:
+ Node = new TreeNode(CurrentToken);
+ OutputStack.Push(Node);
+ break;
+ // We need to preserve "Left handness" of the ExpressionTree
+ // i.e 7/8 gives a root node of / with Cnode(0) = 7 and Cnod(1) = 8 etc.
+ // This should preserve non commutativity
+ case Token.Type.Addition:
+ case Token.Type.Subtraction:
+ case Token.Type.Multiplication:
+ case Token.Type.Division:
+ case Token.Type.Exponentiation:
+ TreeNode Right = OutputStack.Pop();
+ TreeNode Left = OutputStack.Pop();
+ Node = CreateBinaryNode(CurrentToken, Left, Right);
+ OutputStack.Push(Node);
+ break;
+ case Token.Type.UnaryPlus:
+ case Token.Type.UnaryMinus:
+ Node = CreateUnaryNode(CurrentToken, OutputStack.Pop());
+ OutputStack.Push(Node);
+ break;
+ }
+ }
+ return OutputStack.Pop();
+ }
+ }
+}