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(); + } + } +}