//Methods public ITokenable Parse() { while (indexString < input.Length - 1) { ReadNextChar(); if (double.TryParse(currentChar.ToString(), out _) || currentChar == '.') { //this is a number, use a while loop to build the token, storing the value as a string for now string numberString = ""; //numbers can optionally contain a single decimal point, but no more than that, use a bool to track bool isDecimal = false; //if the number input started with a period if (currentChar == '.') { numberString = "0"; isDecimal = true; } while ((int.TryParse(currentChar.ToString(), out _) || currentChar == '.') && indexString < input.Length - 1) { numberString += currentChar; ReadNextChar(); if (currentChar == '.') { //the user is attempting to add a decilmal, which might be acceptable if (!isDecimal) { //this is the first decimal, that's ok numberString += currentChar; isDecimal = true; if (indexString < input.Length - 1) { ReadNextChar(); } } else { //the user is attempting to add a second decimal to the number, throw an error throw new Exception("You can only have one decimal point in the same number."); } } } //the current character isn't a number, so the numberToken's value is now complete. double.TryParse(numberString, out double value); numToken = new NumberToken(value); AddTokenToChain(numToken); } switch (currentChar) { case '+': //This an addition opperator token = new OperatorToken(Expression.Add); AddTokenToChain(token); break; case 'π': case 'p': //This is adding pi, it will add three token, including open and close parentheses openToken = new OperatorToken(Expression.OpenParenthesis); numToken = new NumberToken(3.1416d); closeToken = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(openToken); AddTokenToChain(numToken); AddTokenToChain(closeToken); break; case 'e': //This is adding e, it will add three token, including open and close parentheses openToken = new OperatorToken(Expression.OpenParenthesis); numToken = new NumberToken(2.71828d); closeToken = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(openToken); AddTokenToChain(numToken); AddTokenToChain(closeToken); break; case 'α': case 'a': //This is adding alpha, it will add three token, including open and close parentheses openToken = new OperatorToken(Expression.OpenParenthesis); numToken = new NumberToken(2.5029d); closeToken = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(openToken); AddTokenToChain(numToken); AddTokenToChain(closeToken); break; case 'δ': case 'd': //This is adding delta, it will add three token, including open and close parentheses openToken = new OperatorToken(Expression.OpenParenthesis); numToken = new NumberToken(4.6692d); closeToken = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(openToken); AddTokenToChain(numToken); AddTokenToChain(closeToken); break; case 'ζ': case 'z': //This is adding zeta, it will add three token, including open and close parentheses openToken = new OperatorToken(Expression.OpenParenthesis); numToken = new NumberToken(1.20206d); closeToken = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(openToken); AddTokenToChain(numToken); AddTokenToChain(closeToken); break; case 'φ': case 'f': //This is adding phi, it will add three token, including open and close parentheses openToken = new OperatorToken(Expression.OpenParenthesis); numToken = new NumberToken(1.618d); closeToken = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(openToken); AddTokenToChain(numToken); AddTokenToChain(closeToken); break; case 'γ': case 'g': //This is adding phi, it will add three token, including open and close parentheses openToken = new OperatorToken(Expression.OpenParenthesis); numToken = new NumberToken(0.57721d); closeToken = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(openToken); AddTokenToChain(numToken); AddTokenToChain(closeToken); break; case '-': //This a subtraction opperator token = new OperatorToken(Expression.Subtract); AddTokenToChain(token); break; case '*': //This a multiply opperator token = new OperatorToken(Expression.Multiply); AddTokenToChain(token); break; case '/': //This a division opperator token = new OperatorToken(Expression.Divide); AddTokenToChain(token); break; case '^': //This an exponent opperator token = new OperatorToken(Expression.Exponent); AddTokenToChain(token); break; case '(': //This an openParenthesis token, but not really an operator token = new OperatorToken(Expression.OpenParenthesis); AddTokenToChain(token); parenthesesBalance++; break; case ')': //This ac closeParenthesis token token = new OperatorToken(Expression.CloseParenthesis); AddTokenToChain(token); parenthesesBalance--; if (parenthesesBalance < 0) { throw new Exception("You must close all parentheses."); } break; case '=': //Error for bad input if (parenthesesBalance != 0) { throw new Exception("You must close all parentheses."); } //this is the end of the equation Executor executor = new Executor(FirstToken); executor.SimplifyExpression(); Value = executor.Value; IsComplete = true; break; default: if (!Int32.TryParse(currentChar.ToString(), out _) && currentChar != '.') { throw new SyntaxError("Invalid input: " + currentChar); } break; } } return(CurrentToken); }
public ITokenable SimplifyExpression() { currentToken = firstToken; //Since the order of operations are stored as enums we can use this value to track which expressions //need to be worked on now. The max value is the last operator on the enum list. int orderOfOperations = 0; int maxOperation = (int)Enum.GetValues(typeof(Expression)).Cast <Expression>().Max() - 3; //A first pass must be made to check for open parenthesis while (currentToken != null && currentToken.Expression != Expression.CloseParenthesis) { if (currentToken.Expression == Expression.OpenParenthesis) { //A new open parenthesis has been found, send the next token to avoid infinite method calls Executor executor = new Executor(currentToken.NextToken); ITokenable newToken = executor.SimplifyExpression(); if (newToken.PreviousToken == null) { firstToken = newToken; } currentToken = newToken; //If this is true we just came out of parentheses, they should have been removed from the chain so this resets the chain if (firstToken.Expression == Expression.OpenParenthesis) { firstToken = currentToken; } } currentToken = currentToken.NextToken; } //Now we will loop through the order of operations. do { currentToken = firstToken; while (currentToken != null && currentToken.Expression != Expression.CloseParenthesis) { //Check each token to see if the expression equals the current order of operation if ((int)currentToken.Expression == orderOfOperations) { //The operator matches the current operator that we are looking for. //Binary and unary operations are handled differently if (orderOfOperations > 1) { //A binary operation acts on the two numbers before and after it, changing the value of the //previous number and then removing the operator and the second number from the link list of nodes. NumberToken firstOperand = (NumberToken)currentToken.PreviousToken; firstOperand.Value = currentToken.Evaluation(); firstOperand.NextToken = currentToken.NextToken.NextToken; if (firstOperand.NextToken != null) { firstOperand.NextToken.PreviousToken = firstOperand; } currentToken = firstOperand; } else { //This is a unary operation, the operand will be updated and this token removed from the link list NumberToken operand = (NumberToken)currentToken.NextToken; operand.Value = currentToken.Evaluation(); operand.PreviousToken = currentToken.PreviousToken; if (operand.PreviousToken != null) { operand.PreviousToken.NextToken = operand; } //If the unary operator was the first token in the chain we need to update that if (firstToken == currentToken) { firstToken = operand; } currentToken = operand; } } currentToken = currentToken.NextToken; } //Iterate to the next order of operation orderOfOperations++; } while (orderOfOperations <= maxOperation); //At this point there is either only one token in the link chain or we have parentheses that need cleaning up if (firstToken.Expression == Expression.OpenParenthesis && firstToken.PreviousToken == null) { firstToken = firstToken.NextToken; firstToken.PreviousToken = null; } if (firstToken.PreviousToken != null && firstToken.PreviousToken.Expression == Expression.OpenParenthesis) { //This code cleans up the open parenthesis //If a number is next to a parenthesis then the parenthesis needs to be converted into a multiply operator, //if an operator is there then just delete this parenthesis from the chain, if it is null then delete this token. if (firstToken.PreviousToken.PreviousToken != null) { if (firstToken.PreviousToken.PreviousToken.Expression != Expression.Number) { //Delete parenthesis token, linking the next number token into the chain. firstToken.PreviousToken = firstToken.PreviousToken.PreviousToken; firstToken.PreviousToken.NextToken = firstToken; } else { //The previous token is a number, convert the parenthesis token into a multiply token firstToken.PreviousToken.Expression = Expression.Multiply; } } else { //Delete parenthesis token, there is nothing before it. firstToken.PreviousToken = null; } //Clean up the close parenthesis //Now find the close parenthesis (either the second or third token in the sub-chain we are working on) and apply similar logic currentToken = firstToken; while (currentToken.Expression != Expression.CloseParenthesis) { currentToken = currentToken.NextToken; } if (currentToken.NextToken != null) { if (currentToken.NextToken.Expression == Expression.Number || currentToken.NextToken.Expression == Expression.OpenParenthesis) { currentToken.Expression = Expression.Multiply; } else { //The next token is an operator, just delete this currentToken.PreviousToken.NextToken = currentToken.NextToken; currentToken.NextToken.PreviousToken = currentToken.PreviousToken; currentToken.NextToken = null; currentToken.PreviousToken = null; } } else { //This is the end of the chain currentToken.PreviousToken.NextToken = null; } } Value = firstToken.Evaluation(); return(firstToken); }