mirror of
https://github.com/0xJ1M/MathsEngine.git
synced 2026-06-04 23:10:09 +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 (#7)
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>
|
||||
internal class NodeFactory
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a binary TreeNode, that is a node with a root value and two children
|
||||
/// </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>
|
||||
/// Returns a Node that holds a numerical value
|
||||
/// </summary>
|
||||
|
||||
@@ -126,6 +126,24 @@ namespace MathEngine.Parser.Parser
|
||||
}
|
||||
OperatorStack.Push(CurrentToken);
|
||||
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());
|
||||
//OutputStack.Push(Node);
|
||||
break;
|
||||
case Token.Type.Function:
|
||||
Node = NodeFactory.CreateFunctionNode(CurrentToken, OutputStack);
|
||||
OutputStack.Push(Node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return OutputStack.Pop();
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
/// </summary>
|
||||
internal readonly struct Token
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents the token for +
|
||||
/// </summary>
|
||||
@@ -26,10 +27,20 @@
|
||||
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);
|
||||
|
||||
/// Represents the token for ^
|
||||
/// </summary>
|
||||
public static readonly Token Exponentiation = new("^", Type.Exponentiation, NumericType.NaN, 0);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enum representing the token type
|
||||
/// </summary>
|
||||
@@ -138,6 +149,22 @@
|
||||
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
|
||||
/// <summary>
|
||||
/// Debug String; Used to give a string representation of a token
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
internal static class Tokeniser
|
||||
{
|
||||
private static readonly List<char> Operators = new(new char[] { '+', '-', '*', '/', '^' });
|
||||
private static readonly List<char> Parenthesis = new(new char[] { '(', ')'});
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next non-whitespace char or returns the null terminator is at EOS (End of stream)
|
||||
@@ -36,6 +37,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>
|
||||
/// Tokenises a given Mathematical expression given as a string to a list of tokens
|
||||
/// </summary>
|
||||
@@ -65,9 +76,14 @@
|
||||
Tokenstack.Add(Tokeniser.GetOperatorToken(curChar));
|
||||
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.
|
||||
//Case 1
|
||||
if (Char.IsDigit(curChar))
|
||||
if (Char.IsDigit(curChar))
|
||||
{
|
||||
bool hasDecimalPlace = false;
|
||||
Int32 tempIndex = currentIndex;
|
||||
@@ -86,14 +102,27 @@
|
||||
tempChar = GetNextChar(Expression, ref 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 == '.');
|
||||
//Iterate until we hit the next special character or EOS
|
||||
|
||||
//Token is a number so add this to the stack
|
||||
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