예제 #1
0
        /// <summary>
        /// Parses and extracts a numeric value at the current position
        /// </summary>
        /// <param name="parser">TextParser object</param>
        /// <returns></returns>
        private string ParseNumberToken(TextParser parser)
        {
            bool hasDecimal = false;
            int  start      = parser.Position;

            while (char.IsDigit(parser.Peek()) || parser.Peek() == '.')
            {
                if (parser.Peek() == '.')
                {
                    if (hasDecimal)
                    {
                        throw new FormulaException(ErrMultipleDecimalPoints, parser.Position);
                    }
                    hasDecimal = true;
                }
                parser.MoveAhead();
            }
            // Extract token
            string token = parser.Extract(start, parser.Position);

            if (token == ".")
            {
                throw new FormulaException(ErrInvalidOperand, parser.Position - 1);
            }
            return(token);
        }
예제 #2
0
        /// <summary>
        /// Parses and extracts a symbol at the current position
        /// </summary>
        /// <param name="parser">TextParser object</param>
        /// <returns></returns>
        private static string ParseSymbolToken(TextParser parser)
        {
            int start = parser.Position;

            while (char.IsLetterOrDigit(parser.Peek()) || parser.Peek() == '_')
            {
                parser.MoveAhead();
            }
            return(parser.Extract(start, parser.Position));
        }
예제 #3
0
        /// <summary>
        /// Converts a standard infix expression to list of tokens in
        /// postfix order.
        /// </summary>
        /// <param name="expression">Expression to evaluate</param>
        /// <returns></returns>
        private List <string> TokenizeExpression(string expression)
        {
            var    tokens     = new List <string>();
            var    stack      = new Stack <string>();
            var    state      = State.None;
            int    parenDepth = 0;
            string temp;

            var parser = new TextParser(expression);

            while (!parser.EndOfText)
            {
                if (char.IsWhiteSpace(parser.Peek()))
                {
                    // ignore spaces, tabs, etc.
                }
                else if (parser.Peek() == '(')
                {
                    // cannot follow operand
                    if (state == State.Operand)
                    {
                        throw new FormulaException(ErrOperatorExpected, parser.Position);
                    }

                    // Allow additional unary operators after "("
                    if (state == State.UnaryOperator)
                    {
                        state = State.Operator;
                    }

                    // push opening parenthesis onto stack
                    stack.Push(parser.Peek().ToString());
                    // track number of parentheses
                    parenDepth++;
                }
                else if (parser.Peek() == ')')
                {
                    // Must follow operand
                    if (state != State.Operand)
                    {
                        throw new FormulaException(ErrOperandExpected, parser.Position);
                    }
                    // Must have matching open parenthesis
                    if (parenDepth == 0)
                    {
                        throw new FormulaException(ErrUnmatchedClosingParen, parser.Position);
                    }
                    // Pop all operators until matching "(" found
                    temp = stack.Pop();
                    while (temp != "(")
                    {
                        tokens.Add(temp);
                        temp = stack.Pop();
                    }
                    // Track number of parentheses
                    parenDepth--;
                }
                else if ("+-*/^".Contains(parser.Peek()))
                {
                    // Need a bit of extra code to support unary operators
                    if (state == State.Operand)
                    {
                        // Pop operators with precedence >= current operator
                        int currPrecedence = GetPrecedence(parser.Peek().ToString());
                        while (stack.Count > 0 && GetPrecedence(stack.Peek()) >= currPrecedence)
                        {
                            tokens.Add(stack.Pop());
                        }
                        stack.Push(parser.Peek().ToString());
                        state = State.Operator;
                    }
                    else if (state == State.UnaryOperator)
                    {
                        // Don't allow two unary operators together
                        throw new FormulaException(ErrOperandExpected, parser.Position);
                    }
                    else
                    {
                        // Test for unary operator
                        if (parser.Peek() == '-')
                        {
                            // Push unary minus
                            stack.Push(UnaryMinus);
                            state = State.UnaryOperator;
                        }
                        else if (parser.Peek() == '+')
                        {
                            // Just ignore unary plus
                            state = State.UnaryOperator;
                        }
                        else
                        {
                            throw new FormulaException(ErrOperandExpected, parser.Position);
                        }
                    }
                }
                else if (char.IsDigit(parser.Peek()) || parser.Peek() == '.')
                {
                    if (state == State.Operand)
                    {
                        // Cannot follow other operand
                        throw new FormulaException(ErrOperatorExpected, parser.Position);
                    }
                    // Parse number
                    temp = ParseNumberToken(parser);
                    tokens.Add(temp);
                    state = State.Operand;
                    continue;
                }
                else
                {
                    double result;

                    // Parse symbols and functions
                    if (state == State.Operand)
                    {
                        // Symbol or function cannot follow other operand
                        throw new FormulaException(ErrOperatorExpected, parser.Position);
                    }
                    if (!(char.IsLetter(parser.Peek()) || parser.Peek() == '_'))
                    {
                        // Invalid character
                        temp = string.Format(ErrUnexpectedCharacter, parser.Peek());
                        throw new FormulaException(temp, parser.Position);
                    }

                    // save start of symbol for error reporting
                    int symbolPos = parser.Position;
                    // parse this symbol
                    temp = ParseSymbolToken(parser);
                    // skip whitespace
                    parser.MovePastWhitespace();
                    // check for parameter list
                    if (parser.Peek() == '(')
                    {
                        // found parameter list, evaluate function
                        result = EvaluateFunction(parser, temp, symbolPos);
                    }
                    else
                    {
                        // no parameter list, evaluate symbol (variable)
                        result = EvaluateSymbol(temp, symbolPos);
                    }

                    // handle negative result
                    if (result < 0)
                    {
                        stack.Push(UnaryMinus);
                        result = Math.Abs(result);
                    }
                    tokens.Add(result.ToString());
                    state = State.Operand;
                    continue;
                }
                parser.MoveAhead();
            }
            // Expression cannot end with operator
            if (state == State.Operator || state == State.UnaryOperator)
            {
                throw new FormulaException(ErrOperandExpected, parser.Position);
            }
            // Check for balanced parentheses
            if (parenDepth > 0)
            {
                throw new FormulaException(ErrClosingParenExpected, parser.Position);
            }
            // Retrieve remaining operators from stack
            while (stack.Count > 0)
            {
                tokens.Add(stack.Pop());
            }
            return(tokens);
        }
예제 #4
0
        /// <summary>
        /// Evaluates each parameter of a function's parameter list and returns
        /// a list of those values. An empty list is returned if no parameters
        /// were found. It is assumed the current position is at the opening
        /// parenthesis of the argument list.
        /// </summary>
        /// <param name="parser">TextParser object</param>
        /// <returns></returns>
        private List <double> ParseParameters(TextParser parser)
        {
            // Move past open parenthesis
            parser.MoveAhead();

            // Look for function parameters
            var parameters = new List <double>();

            parser.MovePastWhitespace();
            if (parser.Peek() != ')')
            {
                // Parse function parameter list
                int paramStart = parser.Position;
                int pardepth   = 1;

                while (!parser.EndOfText)
                {
                    if (parser.Peek() == ':')
                    {
                        // assume current token and next token are cell references
                        var p1    = parser.Position;
                        var cell1 = parser.Extract(paramStart, parser.Position);
                        parser.MoveAhead();
                        var p2    = parser.Position;
                        var cell2 = ParseSymbolToken(parser);
                        paramStart = parser.Position;
                        parameters.AddRange(EvaluateCellReferences(cell1, cell2, p1, p2));
                    }
                    else if (parser.Peek() == ',')
                    {
                        // Note: Ignore commas inside parentheses. They could be
                        // from a parameter list for a function inside the parameters
                        if (pardepth == 1)
                        {
                            parameters.Add(EvaluateParameter(parser, paramStart));
                            paramStart = parser.Position + 1;
                        }
                    }

                    if (parser.Peek() == ')')
                    {
                        pardepth--;
                        if (pardepth == 0)
                        {
                            if (paramStart < parser.Position)
                            {
                                parameters.Add(EvaluateParameter(parser, paramStart));
                            }
                            break;
                        }
                    }
                    else if (parser.Peek() == '(')
                    {
                        pardepth++;
                    }
                    parser.MoveAhead();
                }
            }
            // Make sure we found a closing parenthesis
            if (parser.Peek() != ')')
            {
                throw new FormulaException(ErrClosingParenExpected, parser.Position);
            }
            // Move past closing parenthesis
            parser.MoveAhead();
            // Return parameter list
            return(parameters);
        }