From a33e39706420fd1c73e206d8a6c4469285865f27 Mon Sep 17 00:00:00 2001
From: Jim <112640460+0xJ1M@users.noreply.github.com>
Date: Tue, 12 Sep 2023 22:00:04 +0100
Subject: [PATCH] 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
---
MathEngine/MathEngine/Evaluator/Evaluator.cs | 74 -------------------
.../MathEngine/Parser/Nodes/FunctionNode.cs | 30 ++++++++
.../MathEngine/Parser/Nodes/NodeFactory.cs | 29 ++++++++
MathEngine/MathEngine/Parser/Parser/Parser.cs | 18 +++++
.../MathEngine/Parser/Parser/TreeGenerator.cs | 4 +
.../MathEngine/Parser/Tokeniser/Token.cs | 27 +++++++
.../MathEngine/Parser/Tokeniser/Tokeniser.cs | 35 ++++++++-
7 files changed, 140 insertions(+), 77 deletions(-)
delete mode 100644 MathEngine/MathEngine/Evaluator/Evaluator.cs
create mode 100644 MathEngine/MathEngine/Parser/Nodes/FunctionNode.cs
diff --git a/MathEngine/MathEngine/Evaluator/Evaluator.cs b/MathEngine/MathEngine/Evaluator/Evaluator.cs
deleted file mode 100644
index 7496f40..0000000
--- a/MathEngine/MathEngine/Evaluator/Evaluator.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-using MathEngine.Parser.Parser;
-using MathEngine.Parser.Tokeniser;
-
-namespace MathEngine.Parser.Evaluator
-{
- ///
- /// Class that evaluate TreeNodes
- ///
- 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/Nodes/FunctionNode.cs b/MathEngine/MathEngine/Parser/Nodes/FunctionNode.cs
new file mode 100644
index 0000000..509a854
--- /dev/null
+++ b/MathEngine/MathEngine/Parser/Nodes/FunctionNode.cs
@@ -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
+{
+ ///
+ /// Represents a Tree node that stores a function
+ ///
+ internal class FunctionNode: BaseNode where T : INumber
+ {
+ Action func;
+ List> args;
+
+ public FunctionNode(Action Function, List> arguments)
+ {
+ this.func = Function;
+ args = arguments;
+ }
+
+ public override BaseNode Evaluate()
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/MathEngine/MathEngine/Parser/Nodes/NodeFactory.cs b/MathEngine/MathEngine/Parser/Nodes/NodeFactory.cs
index d631bc1..e0a680a 100644
--- a/MathEngine/MathEngine/Parser/Nodes/NodeFactory.cs
+++ b/MathEngine/MathEngine/Parser/Nodes/NodeFactory.cs
@@ -7,6 +7,8 @@ namespace MathEngine.Parser.Parser
///
internal class NodeFactory
{
+
+
///
/// Creates a binary TreeNode, that is a node with a root value and two children
///
@@ -33,6 +35,33 @@ namespace MathEngine.Parser.Parser
}
}
+ ///
+ /// Creates a Function Node, that is a node that operates a number of arguments
+ ///
+ /// The function to create the node for
+ /// Reference to Stack of BaseNode objects to take the arguments from
+ ///
+ public static BaseNode CreateFunctionNode(Token FunctionToken, Stack 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;
+ }*/
+ }
+
///
/// Returns a Node that holds a numerical value
///
diff --git a/MathEngine/MathEngine/Parser/Parser/Parser.cs b/MathEngine/MathEngine/Parser/Parser/Parser.cs
index b45ae77..126116c 100644
--- a/MathEngine/MathEngine/Parser/Parser/Parser.cs
+++ b/MathEngine/MathEngine/Parser/Parser/Parser.cs
@@ -125,6 +125,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;
}
}
diff --git a/MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs b/MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
index 11da6f6..1068af7 100644
--- a/MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
+++ b/MathEngine/MathEngine/Parser/Parser/TreeGenerator.cs
@@ -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();
diff --git a/MathEngine/MathEngine/Parser/Tokeniser/Token.cs b/MathEngine/MathEngine/Parser/Tokeniser/Token.cs
index 52ae344..cc855eb 100644
--- a/MathEngine/MathEngine/Parser/Tokeniser/Token.cs
+++ b/MathEngine/MathEngine/Parser/Tokeniser/Token.cs
@@ -5,6 +5,7 @@
///
internal readonly struct Token
{
+
///
/// Represents the token for +
///
@@ -25,6 +26,16 @@
///
public static readonly Token Divide = new("/", Type.Division, NumericType.NaN, 0);
+ ///
+ /// Represents the token for (
+ ///
+ public static readonly Token LeftParenthesis = new Token("(", Token.Type.LeftParenthesis, Token.NumericType.NaN, 0);
+
+ ///
+ /// Represents the token for )
+ ///
+ public readonly static Token RightParenthesis = new Token(")", Token.Type.RightParenthesis, Token.NumericType.NaN, 0);
+
///
/// Enum representing the token type
///
@@ -133,6 +144,22 @@
this.Arity = FunctionArity;
}
+
+ ///
+ /// Initializes a new instance of the Tokeniser.Token structure with a given TokenValue, TokenType, TokenNumericType and Arity
+ ///
+ /// Char representing the value of the token
+ /// The type that the token instance represents
+ /// The numeric type of the token
+ /// The token Arity
+ 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
///
/// Debug String; Used to give a string representation of a token
diff --git a/MathEngine/MathEngine/Parser/Tokeniser/Tokeniser.cs b/MathEngine/MathEngine/Parser/Tokeniser/Tokeniser.cs
index 827c30c..63dcf36 100644
--- a/MathEngine/MathEngine/Parser/Tokeniser/Tokeniser.cs
+++ b/MathEngine/MathEngine/Parser/Tokeniser/Tokeniser.cs
@@ -6,6 +6,7 @@
internal static class Tokeniser
{
private static readonly List Operators = new(new char[] { '+', '-', '*', '/', '^' });
+ private static readonly List Parenthesis = new(new char[] { '(', ')'});
///
/// 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)),
+ };
+ }
+
///
/// Tokenises a given Mathematical expression given as a string to a list of tokens
///
@@ -64,9 +75,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;
@@ -85,14 +101,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
}
}