mirror of
https://github.com/0xJ1M/MathsEngine.git
synced 2026-06-05 03:10:08 +00:00
Major refactor
This commit is contained in:
218
MathEngine/MathEngine/Parser/Parser.cs
Normal file
218
MathEngine/MathEngine/Parser/Parser.cs
Normal file
@@ -0,0 +1,218 @@
|
||||
using MathEngine.Tokenizer;
|
||||
using System.Security;
|
||||
using static MathEngine.Tokenizer.Token;
|
||||
|
||||
namespace MathEngine.Parser
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the conversion from a list of Tokens representing Mathematical expression to List in Reverse Polish Notation form
|
||||
/// </summary>
|
||||
internal class Parser
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Return the Precdence of a given token operator
|
||||
/// </summary>
|
||||
/// <param name="X">Token to get Precdence of</param>
|
||||
/// <returns>An integer value that represent the precedence value of the token</returns>
|
||||
private static int OperatorPrecedence(Token X)
|
||||
{
|
||||
switch (X.Type)
|
||||
{
|
||||
case TokenType.Addition:
|
||||
case TokenType.Subtraction:
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
case TokenType.Multiplication:
|
||||
case TokenType.Division:
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
case TokenType.UnaryPlus:
|
||||
case TokenType.UnaryMinus:
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
case TokenType.Exponentiation:
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
case TokenType.LeftParenthesis:
|
||||
case TokenType.RightParenthesis:
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new Exception("Unknown operator precedence" + X.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the operation left associative
|
||||
/// </summary>
|
||||
/// <param name="X">Operation to check</param>
|
||||
/// <returns>True if the token is left associative, False otherwise</returns>
|
||||
private static bool IsLeftAssociatve(Token X)
|
||||
{
|
||||
switch (X.Type)
|
||||
{
|
||||
case TokenType.Addition:
|
||||
case TokenType.Subtraction:
|
||||
case TokenType.Multiplication:
|
||||
case TokenType.Division:
|
||||
case TokenType.LeftParenthesis:
|
||||
case TokenType.RightParenthesis:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
case TokenType.Exponentiation:
|
||||
case TokenType.UnaryMinus:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new Exception("If you are seeing this something went wrong when trying to determine if a token was Left Associatve");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Pops operators from the operator stack to the output queue based on precedence and associativity rules.
|
||||
/// This method is part of the Shunting Yard algorithm, ensuring that operators with higher or equal precedence
|
||||
/// (when left-associative) are correctly ordered in the resulting Reverse Polish Notation (RPN).
|
||||
/// </summary>
|
||||
/// <param name="operatorStack">The stack holding operators and functions that are yet to be added to the output.</param>
|
||||
/// <param name="outputQueue">The queue that accumulates tokens in RPN order.</param>
|
||||
/// <param name="current">The current operator token being processed from the input expression.</param>
|
||||
private static void PopOperatorsToQueue(Stack<Token> operatorStack, Queue<Token> outputQueue, Token current)
|
||||
{
|
||||
while (operatorStack.Count > 0)
|
||||
{
|
||||
Token top = operatorStack.Peek();
|
||||
bool isFunction = top.Type == TokenType.Function;
|
||||
bool isHigherPrecedence = OperatorPrecedence(top) > OperatorPrecedence(current);
|
||||
bool isEqualPrecedenceAndLeftAssociative = ((OperatorPrecedence(top) == OperatorPrecedence(current)) && IsLeftAssociatve(current));
|
||||
bool isNotLeftParenthesis = top.Type != TokenType.LeftParenthesis;
|
||||
|
||||
if ((isFunction || isHigherPrecedence || isEqualPrecedenceAndLeftAssociative) && isNotLeftParenthesis)
|
||||
{
|
||||
outputQueue.Enqueue(operatorStack.Pop());
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles pushing either an addition or subtraction operator token onto the operator stack, distinguishing
|
||||
/// between unary and binary addition or subtract operators based on the previous token context.
|
||||
/// </summary>
|
||||
/// <param name="operatorStack">The stack holding operators during parsing.</param>
|
||||
/// <param name="outputQueue">The output queue representing the RPN expression.</param>
|
||||
/// <param name="current">The current operator token being processed.</param>
|
||||
/// <param name="previous">The previous token processed, used to determine if the current operator is unary.</param>
|
||||
private static void PushAdditionSubtractionWithUnaryCheck(Stack<Token> operatorStack, Queue<Token> outputQueue, Token current, Token previous)
|
||||
{
|
||||
bool isUnary = previous.Type == TokenType.None ||
|
||||
previous.Type == TokenType.LeftParenthesis ||
|
||||
previous.Type == TokenType.Addition ||
|
||||
previous.Type == TokenType.Subtraction ||
|
||||
previous.Type == TokenType.Multiplication ||
|
||||
previous.Type == TokenType.Division ||
|
||||
previous.Type == TokenType.Exponentiation;
|
||||
|
||||
if (isUnary)
|
||||
{
|
||||
var unaryToken = current.Type == TokenType.Subtraction ? Token.UnaryMinus : Token.UnaryPlus;
|
||||
PopOperatorsToQueue(operatorStack, outputQueue, unaryToken);
|
||||
operatorStack.Push(unaryToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
PopOperatorsToQueue(operatorStack, outputQueue, current);
|
||||
operatorStack.Push(current);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses a list of tokens into a valid RPN expression
|
||||
/// </summary>
|
||||
/// <param name="Expression">List of tokens to parse</param>
|
||||
/// <returns>Returns a <see cref="Queue{Token}"/> representing the Reverse polish notation form of the expression</returns>
|
||||
internal static Queue<Token> Parse(List<Token> Expression)
|
||||
{
|
||||
//Temp holding stack for operators
|
||||
Stack<Token> OperatorStack = new(Expression.Count/2);
|
||||
//The final stack to return
|
||||
Queue<Token> rpnQueue = new(Expression.Count);
|
||||
//Stack used to hold the number of input params to a function
|
||||
//Stack<uint> ArityStack = new Stack<uint>();
|
||||
Token PreviousToken = Token.None;
|
||||
Token CurrentToken;
|
||||
|
||||
for (int i = 0; i < Expression.Count; i++)
|
||||
{
|
||||
CurrentToken = Expression[i];
|
||||
switch (CurrentToken.Type)
|
||||
{
|
||||
case TokenType.Numeric:
|
||||
rpnQueue.Enqueue(CurrentToken);
|
||||
break;
|
||||
case TokenType.Addition:
|
||||
case TokenType.Subtraction:
|
||||
PushAdditionSubtractionWithUnaryCheck(OperatorStack, rpnQueue, CurrentToken, PreviousToken);
|
||||
break;
|
||||
|
||||
case TokenType.Multiplication:
|
||||
case TokenType.Division:
|
||||
case TokenType.Exponentiation:
|
||||
PopOperatorsToQueue(OperatorStack, rpnQueue, CurrentToken);
|
||||
OperatorStack.Push(CurrentToken);
|
||||
break;
|
||||
case TokenType.Function:
|
||||
OperatorStack.Push(CurrentToken);
|
||||
break;
|
||||
case TokenType.LeftParenthesis:
|
||||
OperatorStack.Push(CurrentToken);
|
||||
break;
|
||||
case TokenType.RightParenthesis:
|
||||
while (OperatorStack.Count != 0 && (OperatorStack.Peek() != Token.LeftParenthesis))
|
||||
{
|
||||
rpnQueue.Enqueue(OperatorStack.Pop());
|
||||
}
|
||||
if (OperatorStack.Count == 0)
|
||||
{
|
||||
throw new ParserException("Mismatched parentheses: Missing left parenthesis");
|
||||
}
|
||||
OperatorStack.Pop(); // Discard the left parenthesis
|
||||
|
||||
if (OperatorStack.Count > 0 && (OperatorStack.Peek().Type == TokenType.Function))
|
||||
{
|
||||
rpnQueue.Enqueue(OperatorStack.Pop());
|
||||
}
|
||||
break;
|
||||
}
|
||||
PreviousToken = CurrentToken;
|
||||
}
|
||||
|
||||
while ((OperatorStack.Count > 0))
|
||||
{
|
||||
if (OperatorStack.Peek().Type == TokenType.LeftParenthesis || OperatorStack.Peek().Type == TokenType.RightParenthesis)
|
||||
throw new Exception("Mismatched parentheses; Expected (");
|
||||
else
|
||||
rpnQueue.Enqueue(OperatorStack.Pop());
|
||||
}
|
||||
return rpnQueue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user