Пример #1
0
        //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);
        }
Пример #2
0
        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);
        }