mirror of
https://github.com/0xJ1M/MathsEngine.git
synced 2026-06-05 02:20:07 +00:00
Feature/basic evaluator (#2)
* Refactor * Updated TreeNode system to use abstract base class and inheritence * Updated unit test coverage * Improved code coverage
This commit is contained in:
@@ -1,13 +1,19 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Parser Tests\TreeNodeTests.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
|
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
|
||||||
|
|||||||
@@ -16,15 +16,8 @@ namespace EngineTests
|
|||||||
public void TestExpressionTreeSimpleExpression()
|
public void TestExpressionTreeSimpleExpression()
|
||||||
{
|
{
|
||||||
string testExp = "3+4";
|
string testExp = "3+4";
|
||||||
TreeNode exptectedTree = new(Token.Plus);
|
|
||||||
Token tokfour = new("4", Token.Type.Numeric, Token.NumericType.Decimal, 0);
|
|
||||||
Token tokthree = new("3", Token.Type.Numeric, Token.NumericType.Decimal, 0);
|
|
||||||
TreeNode four = new(tokfour);
|
|
||||||
TreeNode three = new(tokthree);
|
|
||||||
exptectedTree.AddChildNode(four);
|
|
||||||
exptectedTree.AddChildNode(three);
|
|
||||||
ExpressionTree returnedTree = new(testExp);
|
ExpressionTree returnedTree = new(testExp);
|
||||||
Assert.IsTrue(returnedTree.Equals(exptectedTree));
|
Assert.IsTrue(returnedTree.ToString() == "7");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -34,11 +27,8 @@ namespace EngineTests
|
|||||||
public void TestExpressionTreeSimpleExpressionEvaluation()
|
public void TestExpressionTreeSimpleExpressionEvaluation()
|
||||||
{
|
{
|
||||||
string testExp = "3+4*7";
|
string testExp = "3+4*7";
|
||||||
Token tok31 = new("31", Token.Type.Numeric, Token.NumericType.Decimal, 0);
|
|
||||||
TreeNode exptectedTree = new(tok31);
|
|
||||||
ExpressionTree returnedTree = new(testExp);
|
ExpressionTree returnedTree = new(testExp);
|
||||||
ExpressionTree? evaluatedTree = returnedTree.Evaluate();
|
Assert.IsTrue(returnedTree.ToString() == "31");
|
||||||
Assert.IsTrue(evaluatedTree.Equals(exptectedTree));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -49,11 +39,17 @@ namespace EngineTests
|
|||||||
{
|
{
|
||||||
string testExp = "3+4*7-8/7";
|
string testExp = "3+4*7-8/7";
|
||||||
decimal testValue = decimal.Divide(209 , 7);
|
decimal testValue = decimal.Divide(209 , 7);
|
||||||
Token tok31 = new(testValue.ToString(), Token.Type.Numeric, Token.NumericType.Decimal, 0);
|
|
||||||
TreeNode exptectedTree = new(tok31);
|
|
||||||
ExpressionTree returnedTree = new(testExp);
|
ExpressionTree returnedTree = new(testExp);
|
||||||
ExpressionTree? evaluatedTree = returnedTree.Evaluate();
|
Assert.IsTrue(returnedTree.ToString() == testValue.ToString());
|
||||||
Assert.IsTrue(evaluatedTree.Equals(exptectedTree));
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TestExpressionTreeGetHashCodeReturnsHashCode()
|
||||||
|
{
|
||||||
|
string testExp = "1+1";
|
||||||
|
ExpressionTree returnedTree1 = new(testExp);
|
||||||
|
int hash = returnedTree1.GetHashCode();
|
||||||
|
Assert.IsInstanceOfType(hash, typeof(int));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using MathEngine.Parser.Tokeniser;
|
|
||||||
|
|
||||||
namespace EngineTests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Class for testing the Tokeniser
|
|
||||||
/// </summary>
|
|
||||||
[TestClass]
|
|
||||||
public class TokeniserTests
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Test the tokeniser on a basic string
|
|
||||||
/// </summary>
|
|
||||||
[TestMethod]
|
|
||||||
public void TestTokeniseBasicString()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
string testString = "1+1";
|
|
||||||
Token one = new("1", Token.Type.Numeric, Token.NumericType.Decimal, 0);
|
|
||||||
List<Token> expectedValue = new()
|
|
||||||
{
|
|
||||||
one,
|
|
||||||
Token.Plus,
|
|
||||||
one
|
|
||||||
};
|
|
||||||
//Act
|
|
||||||
List<Token> returnedValue = Tokeniser.Tokenise(testString);
|
|
||||||
//Assert
|
|
||||||
Assert.IsTrue(expectedValue.SequenceEqual(returnedValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test the tokeniser on a basic string, but with significant ammounts of whitespace
|
|
||||||
/// </summary>
|
|
||||||
[TestMethod]
|
|
||||||
public void TestTokeniseBasicStringWithWhiteSpace()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
string testString = " 1 + 1 ";
|
|
||||||
Token one = new("1", Token.Type.Numeric, Token.NumericType.Decimal, 0);
|
|
||||||
List<Token> expectedValue = new()
|
|
||||||
{
|
|
||||||
one,
|
|
||||||
Token.Plus,
|
|
||||||
one
|
|
||||||
};
|
|
||||||
//Act
|
|
||||||
List<Token> returnedValue = Tokeniser.Tokenise(testString);
|
|
||||||
//Assert
|
|
||||||
Assert.IsTrue(expectedValue.SequenceEqual(returnedValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test the tokeniser on a string which contains a number which is not formatted correctly
|
|
||||||
/// </summary>
|
|
||||||
[TestMethod]
|
|
||||||
public void TestTokeniseStringWithInvalidNumbr()
|
|
||||||
{
|
|
||||||
//Arrange
|
|
||||||
string testString = "1+11.2.5";
|
|
||||||
//Act and Assert
|
|
||||||
Assert.ThrowsException<Exception>(() => Tokeniser.Tokenise(testString));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
74
MathEngine/MathEngine/Evaluator/Evaluator.cs
Normal file
74
MathEngine/MathEngine/Evaluator/Evaluator.cs
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
using MathEngine.Parser.Parser;
|
||||||
|
using MathEngine.Parser.Tokeniser;
|
||||||
|
|
||||||
|
namespace MathEngine.Parser.Evaluator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class that evaluate TreeNodes
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates branches of a given tree
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Branch">The TreeNode branch to evaluate</param>
|
||||||
|
/// <returns>Returns the Evaluation of the Branch</returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evlautes a binary node where the root node is an operator and given two branches the left and the right
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Operator_Token"></param>
|
||||||
|
/// <param name="Left_Branch"></param>
|
||||||
|
/// <param name="Right_Branch"></param>
|
||||||
|
/// <returns>Returns the evaluated value of the operator as a TreeNode</returns>
|
||||||
|
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?"),
|
||||||
|
};
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
|
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Parser\Nodes\TreeNode.cs" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
|
||||||
<_Parameter1>EngineTests</_Parameter1>
|
<_Parameter1>EngineTests</_Parameter1>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using MathEngine.Parser.Tokeniser;
|
||||||
using MathEngine.Parser.Tokeniser;
|
|
||||||
namespace MathEngine.Parser.Parser
|
namespace MathEngine.Parser.Parser
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -10,7 +9,8 @@ namespace MathEngine.Parser.Parser
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The root node of the expression tree;
|
/// The root node of the expression tree;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly TreeNode rootNode;
|
private readonly BaseNode rootNode;
|
||||||
|
private readonly BaseNode? evaluated_expression;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialises a new instance of the MathEngine.Parser.Parser.Node class with a given Token
|
/// Initialises a new instance of the MathEngine.Parser.Parser.Node class with a given Token
|
||||||
@@ -21,150 +21,23 @@ namespace MathEngine.Parser.Parser
|
|||||||
{
|
{
|
||||||
List<Token> tokens = Tokeniser.Tokeniser.Tokenise(Expression);
|
List<Token> tokens = Tokeniser.Tokeniser.Tokenise(Expression);
|
||||||
Stack<Token> rpnForm = Parser.Parse(tokens);
|
Stack<Token> rpnForm = Parser.Parse(tokens);
|
||||||
rootNode = GenerateExpressionTree(rpnForm);
|
rootNode = TreeGenerator.TreeFromRPN(rpnForm);
|
||||||
|
evaluated_expression = rootNode.Evaluate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionTree(TreeNode rootNode)
|
private ExpressionTree(BaseNode rootNode)
|
||||||
{
|
{
|
||||||
this.rootNode = rootNode;
|
this.rootNode = rootNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/* /// <summary>
|
||||||
/// Creates a binary TreeNode, that is a node with a root value and two children
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="CurrentToken">The token to be the root node of the TreeNode</param>
|
|
||||||
/// <param name="LeftToken">TreeNode that is the left branch of the current node</param>
|
|
||||||
/// <param name="RightToken">TreeNode that is the right branch of the current node</param>
|
|
||||||
/// <returns>A TreeNode with CurrentToken as the root value and LeftBranch and RightBranch as Children</returns>
|
|
||||||
private static TreeNode CreateBinaryNode(Token CurrentToken, TreeNode LeftBranch, TreeNode RightBranch)
|
|
||||||
{
|
|
||||||
TreeNode root = new(CurrentToken);
|
|
||||||
root.AddChildNode(LeftBranch);
|
|
||||||
root.AddChildNode(RightBranch);
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a unary TreeNode, that is a node with a root value and two children
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="CurrentToken">The token to be the root node of the TreeNode</param>
|
|
||||||
/// <param name="LeftToken">TreeNode that is the child of the current node</param>
|
|
||||||
/// <returns>A TreeNode with CurrentToken as the root value and ChildNode as the sole child node</returns>
|
|
||||||
private static TreeNode CreateUnaryNode(Token CurrentToken, TreeNode ChildNode)
|
|
||||||
{
|
|
||||||
TreeNode root = new(CurrentToken);
|
|
||||||
root.AddChildNode(ChildNode);
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generates the full expression tree given an RPN expression stack
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="rpnExpression">RPN expression stack to generate an expression tree from</param>
|
|
||||||
/// <returns>An expression Tree that represents the Mathematical expression given by rpnExpression</returns>
|
|
||||||
private static TreeNode GenerateExpressionTree(Stack<Token> rpnExpression)
|
|
||||||
{
|
|
||||||
Stack<TreeNode> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Evaluates branches of a given tree
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Branch">The TreeNode branch to evaluate</param>
|
|
||||||
/// <returns>Returns the Evaluation of the Branch</returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Evlautes a binary node where the root node is an operator and given two branches the left and the right
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Operator_Token"></param>
|
|
||||||
/// <param name="Left_Branch"></param>
|
|
||||||
/// <param name="Right_Branch"></param>
|
|
||||||
/// <returns>Returns the evaluated value of the operator as a TreeNode</returns>
|
|
||||||
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?"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Evaluates the current instance of ExpressionTree
|
/// Evaluates the current instance of ExpressionTree
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Returns an update of the current instance which the expression Evaluated</returns>
|
/// <returns>Returns an update of the current instance which the expression Evaluated</returns>
|
||||||
public ExpressionTree? Evaluate()
|
public ExpressionTree Evaluate()
|
||||||
{
|
{
|
||||||
if (rootNode == null)
|
return new ExpressionTree(rootNode.Evaluate());
|
||||||
{
|
}*/
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value indicating if the given object is equal to the current instance of ExpressionTree
|
/// Returns a value indicating if the given object is equal to the current instance of ExpressionTree
|
||||||
@@ -173,9 +46,9 @@ namespace MathEngine.Parser.Parser
|
|||||||
/// <returns>True if they are equal, False otherwise</returns>
|
/// <returns>True if they are equal, False otherwise</returns>
|
||||||
public override bool Equals(object? other)
|
public override bool Equals(object? other)
|
||||||
{
|
{
|
||||||
if (other is TreeNode)
|
if (other is BaseNode)
|
||||||
{
|
{
|
||||||
ExpressionTree otherTree = new((TreeNode)other);
|
ExpressionTree otherTree = new((BaseNode)other);
|
||||||
return this.Equals(otherTree);
|
return this.Equals(otherTree);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -201,7 +74,12 @@ namespace MathEngine.Parser.Parser
|
|||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return System.HashCode.Combine(this.rootNode);
|
return System.HashCode.Combine(rootNode, evaluated_expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return evaluated_expression.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
using MathEngine.Parser.Tokeniser;
|
|
||||||
namespace MathEngine.Parser.Parser
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a node in a Tree structure
|
|
||||||
/// </summary>
|
|
||||||
internal class TreeNode
|
|
||||||
{
|
|
||||||
private TreeNode? Parent;
|
|
||||||
private List<TreeNode>? Children;
|
|
||||||
private readonly Token Value;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialises a new instance of the MathEngine.Parser.Parser.Node class with a given Token
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The token for the nodes value</param>
|
|
||||||
public TreeNode(Token value)
|
|
||||||
{
|
|
||||||
Parent = null;
|
|
||||||
Children = null;
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the value of the node
|
|
||||||
/// </summary>
|
|
||||||
public Token NodeValue
|
|
||||||
{
|
|
||||||
get { return Value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the parent node of the current node, or null if it does not exist
|
|
||||||
/// </summary>
|
|
||||||
public TreeNode? ParentNode
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Parent == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns all of the child nodes of the current node, or null if it odes not exist
|
|
||||||
/// </summary>
|
|
||||||
public List<TreeNode>? GetChildrenNodes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Children == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the child node specified by the index, if there are no children nodes or if the index is out of bounds than null is returned
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">The index of the child node to get</param>
|
|
||||||
/// <returns>The ChildNode at the specified index, null if it is null or the index is out-of-bounds</returns>
|
|
||||||
public TreeNode? GetChildNode(int index)
|
|
||||||
{
|
|
||||||
if (Children == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (index < 0 || index >= Children.Count)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Children[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a child node to the current root node, if there are no children nodes a list is created
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="Node">The value for the child node that is to be added</param>
|
|
||||||
public void AddChildNode(TreeNode Node)
|
|
||||||
{
|
|
||||||
if (Children == null)
|
|
||||||
{
|
|
||||||
Children = new()
|
|
||||||
{
|
|
||||||
Node
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Children.Add(Node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value that indicates if the given object is equal to the current instance of TreeNode
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="other">The object to compare to the current instance of TreeNode</param>
|
|
||||||
/// <returns>True if the object innstace is equal to the current ExpressionTree instance, False otherwise</returns>
|
|
||||||
public override bool Equals(object? other)
|
|
||||||
{
|
|
||||||
if (other is ExpressionTree)
|
|
||||||
{
|
|
||||||
return other.Equals(this);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns a value that indicates if the given TreeNode instance is equal to another
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="other">The TreeNode to check for equality</param>
|
|
||||||
/// <returns>True if they are equal, False otherwise</returns>
|
|
||||||
public bool Equals(TreeNode other)
|
|
||||||
{
|
|
||||||
if (this.Value != other.Value) //If the root values are not equal we are done
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// otherwise,
|
|
||||||
if (this.Children != null && other.Children != null) // If both children are NOT null then we reursively check the child nodes
|
|
||||||
{
|
|
||||||
//Covered all nullable cases, we now need to recursively check the child nodes
|
|
||||||
if (Children.Count != other.Children.Count)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (int childNodeIndex = 0; childNodeIndex < Children.Count; childNodeIndex++)
|
|
||||||
{
|
|
||||||
if (!Children[childNodeIndex].Equals(other.Children[childNodeIndex]))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (this.Children == null && other.Children == null) //Special case is if both children lists are null then the TreeNodes are equal
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else // otherwise at least one is null and the other is not so they can't be equal
|
|
||||||
{
|
|
||||||
return false; // if both children are not null than at least one is null so they can't be equal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
52
MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
Normal file
52
MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using MathEngine.Parser.Tokeniser;
|
||||||
|
|
||||||
|
namespace MathEngine.Parser.Parser
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class for converting from RPN form to an expression tree
|
||||||
|
/// </summary>
|
||||||
|
internal class TreeGenerator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Generates the full expression tree given an RPN expression stack
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rpnExpression">RPN expression stack to generate an expression tree from</param>
|
||||||
|
/// <returns>An expression Tree that represents the Mathematical expression given by rpnExpression</returns>
|
||||||
|
public static BaseNode TreeFromRPN(Stack<Token> rpnExpression)
|
||||||
|
{
|
||||||
|
Stack<BaseNode> OutputStack = new(rpnExpression.Count);
|
||||||
|
BaseNode Node;
|
||||||
|
Token CurrentToken;
|
||||||
|
while (rpnExpression.Count != 0)
|
||||||
|
{
|
||||||
|
CurrentToken = rpnExpression.Pop();
|
||||||
|
switch (CurrentToken.Token_Type)
|
||||||
|
{
|
||||||
|
case Token.Type.Numeric:
|
||||||
|
Node = NodeFactory.CreateNumericNode(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:
|
||||||
|
BaseNode Right = OutputStack.Pop();
|
||||||
|
BaseNode Left = OutputStack.Pop();
|
||||||
|
Node = NodeFactory.CreateBinaryNode(CurrentToken, Left, Right);
|
||||||
|
OutputStack.Push(Node);
|
||||||
|
break;
|
||||||
|
case Token.Type.UnaryPlus:
|
||||||
|
case Token.Type.UnaryMinus:
|
||||||
|
//Node = NodeFactory.CreateUnaryNode(CurrentToken, OutputStack.Pop());
|
||||||
|
//OutputStack.Push(Node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return OutputStack.Pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -179,24 +179,24 @@
|
|||||||
/// <returns>Returns true if the two Tokens are not equal and false otherwise</returns>
|
/// <returns>Returns true if the two Tokens are not equal and false otherwise</returns>
|
||||||
public static bool operator !=(Token X, Token Y)
|
public static bool operator !=(Token X, Token Y)
|
||||||
{
|
{
|
||||||
if (X.TokenValue == Y.TokenValue)
|
if (X.TokenValue != Y.TokenValue)
|
||||||
{
|
{
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (X.TokenType == Y.TokenType)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (X.NumericalType == Y.NumericalType)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (X.Arity == Y.Arity)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (X.TokenType != Y.TokenType)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (X.NumericalType != Y.NumericalType)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (X.Arity != Y.Arity)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a value that indicates whether the given Token is equal to the current Token instance.
|
/// Returns a value that indicates whether the given Token is equal to the current Token instance.
|
||||||
|
|||||||
Reference in New Issue
Block a user