mirror of
https://github.com/0xJ1M/MathsEngine.git
synced 2026-06-05 00:10:08 +00:00
Work in progress: To continue on this ticket additional work has to be done enable dynamic collection and organisation of built-in types to be callable (#5)
This commit is contained in:
@@ -1,74 +0,0 @@
|
|||||||
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?"),
|
|
||||||
};
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
30
MathEngine/MathEngine/Parser/Nodes/FunctionNode.cs
Normal file
30
MathEngine/MathEngine/Parser/Nodes/FunctionNode.cs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
using MathEngine.Parser.Parser.Nodes;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace MathEngine.Parser.Parser
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a Tree node that stores a function
|
||||||
|
/// </summary>
|
||||||
|
internal class FunctionNode<T>: BaseNode where T : INumber<T>
|
||||||
|
{
|
||||||
|
Action func;
|
||||||
|
List<NumericNode<T>> args;
|
||||||
|
|
||||||
|
public FunctionNode(Action Function, List<NumericNode<T>> arguments)
|
||||||
|
{
|
||||||
|
this.func = Function;
|
||||||
|
args = arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override BaseNode Evaluate()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,8 @@ namespace MathEngine.Parser.Parser
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class NodeFactory
|
internal class NodeFactory
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a binary TreeNode, that is a node with a root value and two children
|
/// Creates a binary TreeNode, that is a node with a root value and two children
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -33,6 +35,33 @@ namespace MathEngine.Parser.Parser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a Function Node, that is a node that operates a number of arguments
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="FunctionToken">The function to create the node for</param>
|
||||||
|
/// <param name="ArgumentStack">Reference to Stack of BaseNode objects to take the arguments from</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static BaseNode CreateFunctionNode(Token FunctionToken, Stack<BaseNode> ArgumentStack)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Not implemented at this time");
|
||||||
|
/* Check that the function exists, if it does we then check that the number of arguments
|
||||||
|
* for the function is are avaliable on the ArgumentStack. If so we can construct the Node.
|
||||||
|
* Otherwise, either the function does not exist or there is an argument mismatch */
|
||||||
|
/*try
|
||||||
|
{
|
||||||
|
functionAction = GetFunction(FunctionToken, out int functionArity);
|
||||||
|
if (functionArity >= ArgumentStack.Count)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Number of required arguments is greater than avaliable arugments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a Node that holds a numerical value
|
/// Returns a Node that holds a numerical value
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -125,6 +125,24 @@ namespace MathEngine.Parser.Parser
|
|||||||
}
|
}
|
||||||
OperatorStack.Push(CurrentToken);
|
OperatorStack.Push(CurrentToken);
|
||||||
break;
|
break;
|
||||||
|
case Token.Type.Function:
|
||||||
|
OperatorStack.Push(CurrentToken);
|
||||||
|
break;
|
||||||
|
case Token.Type.LeftParenthesis:
|
||||||
|
OperatorStack.Push(CurrentToken);
|
||||||
|
break;
|
||||||
|
case Token.Type.RightParenthesis:
|
||||||
|
while (OperatorStack.Peek() != Token.LeftParenthesis && OperatorStack.Count != 0)
|
||||||
|
{
|
||||||
|
OutputStack.Push(OperatorStack.Pop());
|
||||||
|
}
|
||||||
|
OperatorStack.Pop(); // Discard the left parenthesis
|
||||||
|
|
||||||
|
if (OperatorStack.Peek().Token_Type == Token.Type.Function)
|
||||||
|
{
|
||||||
|
OutputStack.Push(OperatorStack.Pop());
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,10 @@ namespace MathEngine.Parser.Parser
|
|||||||
//Node = NodeFactory.CreateUnaryNode(CurrentToken, OutputStack.Pop());
|
//Node = NodeFactory.CreateUnaryNode(CurrentToken, OutputStack.Pop());
|
||||||
//OutputStack.Push(Node);
|
//OutputStack.Push(Node);
|
||||||
break;
|
break;
|
||||||
|
case Token.Type.Function:
|
||||||
|
Node = NodeFactory.CreateFunctionNode(CurrentToken, OutputStack);
|
||||||
|
OutputStack.Push(Node);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return OutputStack.Pop();
|
return OutputStack.Pop();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly struct Token
|
internal readonly struct Token
|
||||||
{
|
{
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the token for +
|
/// Represents the token for +
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -25,6 +26,16 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly Token Divide = new("/", Type.Division, NumericType.NaN, 0);
|
public static readonly Token Divide = new("/", Type.Division, NumericType.NaN, 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the token for (
|
||||||
|
/// </summary>
|
||||||
|
public static readonly Token LeftParenthesis = new Token("(", Token.Type.LeftParenthesis, Token.NumericType.NaN, 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the token for )
|
||||||
|
/// </summary>
|
||||||
|
public readonly static Token RightParenthesis = new Token(")", Token.Type.RightParenthesis, Token.NumericType.NaN, 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enum representing the token type
|
/// Enum representing the token type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -133,6 +144,22 @@
|
|||||||
this.Arity = FunctionArity;
|
this.Arity = FunctionArity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the Tokeniser.Token structure with a given TokenValue, TokenType, TokenNumericType and Arity
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="TokenValue">Char representing the value of the token</param>
|
||||||
|
/// <param name="TokenType">The type that the token instance represents</param>
|
||||||
|
/// <param name="TokenNumericType">The numeric type of the token</param>
|
||||||
|
/// <param name="FunctionArity">The token Arity</param>
|
||||||
|
public Token(char TokenValue, Type TokenType, NumericType TokenNumericType, uint FunctionArity = 0)
|
||||||
|
{
|
||||||
|
this.Value = TokenValue.ToString();
|
||||||
|
this.TokenType = TokenType;
|
||||||
|
this.Numeric_Type = TokenNumericType;
|
||||||
|
this.Arity = FunctionArity;
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Debug String; Used to give a string representation of a token
|
/// Debug String; Used to give a string representation of a token
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
internal static class Tokeniser
|
internal static class Tokeniser
|
||||||
{
|
{
|
||||||
private static readonly List<char> Operators = new(new char[] { '+', '-', '*', '/', '^' });
|
private static readonly List<char> Operators = new(new char[] { '+', '-', '*', '/', '^' });
|
||||||
|
private static readonly List<char> Parenthesis = new(new char[] { '(', ')'});
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the next non-whitespace char or returns the null terminator is at EOS (End of stream)
|
/// Gets the next non-whitespace char or returns the null terminator is at EOS (End of stream)
|
||||||
@@ -35,6 +36,16 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static private Token GetParenthesisToken(char curChar)
|
||||||
|
{
|
||||||
|
return curChar switch
|
||||||
|
{
|
||||||
|
'(' => Token.LeftParenthesis,
|
||||||
|
')' => Token.RightParenthesis,
|
||||||
|
_ => throw new Exception(String.Format("Character {0} is not Parenthesis", curChar)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tokenises a given Mathematical expression given as a string to a list of tokens
|
/// Tokenises a given Mathematical expression given as a string to a list of tokens
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -64,9 +75,14 @@
|
|||||||
Tokenstack.Add(Tokeniser.GetOperatorToken(curChar));
|
Tokenstack.Add(Tokeniser.GetOperatorToken(curChar));
|
||||||
continue; //Next loop interation
|
continue; //Next loop interation
|
||||||
}
|
}
|
||||||
|
if (Parenthesis.Contains(curChar))
|
||||||
|
{
|
||||||
|
Tokenstack.Add(Tokeniser.GetParenthesisToken(curChar));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
//Number, two cases two consider, case 1) number is something like 142.2; case 2 .5 which is clearly 0.5.
|
//Number, two cases two consider, case 1) number is something like 142.2; case 2 .5 which is clearly 0.5.
|
||||||
//Case 1
|
//Case 1
|
||||||
if (Char.IsDigit(curChar))
|
if (Char.IsDigit(curChar))
|
||||||
{
|
{
|
||||||
bool hasDecimalPlace = false;
|
bool hasDecimalPlace = false;
|
||||||
Int32 tempIndex = currentIndex;
|
Int32 tempIndex = currentIndex;
|
||||||
@@ -85,14 +101,27 @@
|
|||||||
tempChar = GetNextChar(Expression, ref tempIndex);
|
tempChar = GetNextChar(Expression, ref tempIndex);
|
||||||
}
|
}
|
||||||
string errString = Expression[currentIndex..tempIndex];
|
string errString = Expression[currentIndex..tempIndex];
|
||||||
throw new Exception(String.Format("Syntax error: {0} has multiple decimal point when at most one is allowed",errString));
|
throw new Exception(String.Format("Syntax error: {0} has multiple decimal point when at most one is allowed", errString));
|
||||||
}
|
}
|
||||||
} while (Char.IsDigit(tempChar) || tempChar == '.');
|
} while (Char.IsDigit(tempChar) || tempChar == '.');
|
||||||
//Iterate until we hit the next special character or EOS
|
//Iterate until we hit the next special character or EOS
|
||||||
|
|
||||||
//Token is a number so add this to the stack
|
//Token is a number so add this to the stack
|
||||||
Tokenstack.Add(new Token(Expression[currentIndex..tempIndex], Token.Type.Numeric, Token.NumericType.Decimal, 0));
|
Tokenstack.Add(new Token(Expression[currentIndex..tempIndex], Token.Type.Numeric, Token.NumericType.Decimal, 0));
|
||||||
currentIndex = tempIndex-1; //Sets the index variable to just before the non numeric char
|
currentIndex = tempIndex - 1; //Sets the index variable to just before the non numeric char
|
||||||
|
}
|
||||||
|
else if (Char.IsAsciiLetter(curChar)) // Keep reading until a parenthesis
|
||||||
|
{
|
||||||
|
Int32 tempIndex = currentIndex;
|
||||||
|
char tempChar;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
tempChar = GetNextChar(Expression, ref tempIndex);
|
||||||
|
|
||||||
|
} while (Char.IsAscii(tempChar) && tempChar != '(');
|
||||||
|
//Token is a potential function so add this to the stack
|
||||||
|
Tokenstack.Add(new Token(Expression[currentIndex..tempIndex], Token.Type.Function, Token.NumericType.NaN, 0));
|
||||||
|
currentIndex = tempIndex - 1; //Sets the index variable to just before the end of the function
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user