コード例 #1
0
ファイル: Eval.cs プロジェクト: vstrien/KOCus
 /// <summary>
 /// Parses and extracts a symbol at the current position
 /// </summary>
 /// <param name="parser">TextParser object</param>
 /// <returns></returns>
 protected string ParseSymbolToken(TextParser parser)
 {
     int start = parser.Position;
     while (Char.IsLetterOrDigit(parser.Peek()) || parser.Peek() == '_')
         parser.MoveAhead();
     return parser.Extract(start, parser.Position);
 }
コード例 #2
0
ファイル: Eval.cs プロジェクト: vstrien/KOCus
        /// <summary>
        /// Converts a standard infix expression to list of tokens in
        /// postfix order.
        /// </summary>
        /// <param name="expression">Expression to evaluate</param>
        /// <returns></returns>
        protected List<string> TokenizeExpression(string expression)
        {
            List<string> tokens = new List<string>();
            Stack<string> stack = new Stack<string>();
            State state = State.None;
            int parenCount = 0;
            string temp;

            TextParser 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 EvalException(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
                    parenCount++;
                }
                else if (parser.Peek() == ')')
                {
                    // Must follow operand
                    if (state != State.Operand)
                        throw new EvalException(ErrOperandExpected, parser.Position);
                    // Must have matching open parenthesis
                    if (parenCount == 0)
                        throw new EvalException(ErrUnmatchedClosingParen, parser.Position);
                    // Pop all operators until matching "(" found
                    temp = stack.Pop();
                    while (temp != "(")
                    {
                        tokens.Add(temp);
                        temp = stack.Pop();
                    }
                    // Track number of parentheses
                    parenCount--;
                }
                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 EvalException(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 EvalException(ErrOperandExpected, parser.Position);
                        }
                    }
                }
                else if (Char.IsDigit(parser.Peek()) || parser.Peek() == '.')
                {
                    if (state == State.Operand)
                    {
                        // Cannot follow other operand
                        throw new EvalException(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 EvalException(ErrOperatorExpected, parser.Position);
                    }
                    if (!(Char.IsLetter(parser.Peek()) || parser.Peek() == '_'))
                    {
                        // Invalid character
                        temp = String.Format(ErrUnexpectedCharacter, parser.Peek());
                        throw new EvalException(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 EvalException(ErrOperandExpected, parser.Position);
            // Check for balanced parentheses
            if (parenCount > 0)
                throw new EvalException(ErrClosingParenExpected, parser.Position);
            // Retrieve remaining operators from stack
            while (stack.Count > 0)
                tokens.Add(stack.Pop());
            return tokens;
        }
コード例 #3
0
ファイル: Eval.cs プロジェクト: vstrien/KOCus
        /// <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>
        protected List<double> ParseParameters(TextParser parser)
        {
            // Move past open parenthesis
            parser.MoveAhead();

            // Look for function parameters
            List<double> parameters = new List<double>();
            parser.MovePastWhitespace();
            if (parser.Peek() != ')')
            {
                // Parse function parameter list
                int paramStart = parser.Position;
                int parenCount = 1;

                while (!parser.EndOfText)
                {
                    if (parser.Peek() == ',')
                    {
                        // Note: Ignore commas inside parentheses. They could be
                        // from a parameter list for a function inside the parameters
                        if (parenCount == 1)
                        {
                            parameters.Add(EvaluateParameter(parser, paramStart));
                            paramStart = parser.Position + 1;
                        }
                    }
                    if (parser.Peek() == ')')
                    {
                        parenCount--;
                        if (parenCount == 0)
                        {
                            parameters.Add(EvaluateParameter(parser, paramStart));
                            break;
                        }
                    }
                    else if (parser.Peek() == '(')
                    {
                        parenCount++;
                    }
                    parser.MoveAhead();
                }
            }
            // Make sure we found a closing parenthesis
            if (parser.Peek() != ')')
                throw new EvalException(ErrClosingParenExpected, parser.Position);
            // Move past closing parenthesis
            parser.MoveAhead();
            // Return parameter list
            return parameters;
        }
コード例 #4
0
ファイル: Eval.cs プロジェクト: vstrien/KOCus
 /// <summary>
 /// Parses and extracts a numeric value at the current position
 /// </summary>
 /// <param name="parser">TextParser object</param>
 /// <returns></returns>
 protected 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 EvalException(ErrMultipleDecimalPoints, parser.Position);
             hasDecimal = true;
         }
         parser.MoveAhead();
     }
     // Extract token
     string token = parser.Extract(start, parser.Position);
     if (token == ".")
         throw new EvalException(ErrInvalidOperand, parser.Position - 1);
     return token;
 }
コード例 #5
0
ファイル: Eval.cs プロジェクト: vstrien/KOCus
 /// <summary>
 /// Extracts and evaluates a function parameter and returns its value. If an
 /// exception occurs, it is caught and the column is adjusted to reflect the
 /// position in original string, and the exception is rethrown.
 /// </summary>
 /// <param name="parser">TextParser object</param>
 /// <param name="paramStart">Column where this parameter started</param>
 /// <returns></returns>
 protected double EvaluateParameter(TextParser parser, int paramStart)
 {
     try
     {
         // Extract expression and evaluate it
         string expression = parser.Extract(paramStart, parser.Position);
         return Execute(expression);
     }
     catch (EvalException ex)
     {
         // Adjust column and rethrow exception
         ex.Column += paramStart;
         throw ex;
     }
 }
コード例 #6
0
ファイル: Eval.cs プロジェクト: vstrien/KOCus
        /// <summary>
        /// Evaluates a function and returns its value. It is assumed the current
        /// position is at the opening parenthesis of the argument list.
        /// </summary>
        /// <param name="parser">TextParser object</param>
        /// <param name="name">Name of function</param>
        /// <param name="pos">Position at start of function</param>
        /// <returns></returns>
        protected double EvaluateFunction(TextParser parser, string name, int pos)
        {
            double result = default(double);

            // Parse function parameters
            List<double> parameters = ParseParameters(parser);

            // We found a function reference
            FunctionStatus status = FunctionStatus.UndefinedFunction;
            if (ProcessFunction != null)
            {
                FunctionEventArgs args = new FunctionEventArgs();
                args.Name = name;
                args.Parameters = parameters;
                args.Result = result;
                args.Status = FunctionStatus.OK;
                ProcessFunction(this, args);
                result = args.Result;
                status = args.Status;
            }
            if (status == FunctionStatus.UndefinedFunction)
                throw new EvalException(String.Format(ErrUndefinedFunction, name), pos);
            if (status == FunctionStatus.WrongParameterCount)
                throw new EvalException(ErrWrongParamCount, pos);
            return result;
        }
コード例 #7
0
ファイル: Eval.cs プロジェクト: vstrien/KOCus
        /// <summary>
        /// Converts a standard infix expression to list of tokens in
        /// postfix order.
        /// </summary>
        /// <param name="expression">Expression to evaluate</param>
        /// <returns></returns>
        protected List <string> TokenizeExpression(string expression)
        {
            List <string>  tokens     = new List <string>();
            Stack <string> stack      = new Stack <string>();
            State          state      = State.None;
            int            parenCount = 0;
            string         temp;

            TextParser 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 EvalException(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
                    parenCount++;
                }
                else if (parser.Peek() == ')')
                {
                    // Must follow operand
                    if (state != State.Operand)
                    {
                        throw new EvalException(ErrOperandExpected, parser.Position);
                    }
                    // Must have matching open parenthesis
                    if (parenCount == 0)
                    {
                        throw new EvalException(ErrUnmatchedClosingParen, parser.Position);
                    }
                    // Pop all operators until matching "(" found
                    temp = stack.Pop();
                    while (temp != "(")
                    {
                        tokens.Add(temp);
                        temp = stack.Pop();
                    }
                    // Track number of parentheses
                    parenCount--;
                }
                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 EvalException(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 EvalException(ErrOperandExpected, parser.Position);
                        }
                    }
                }
                else if (Char.IsDigit(parser.Peek()) || parser.Peek() == '.')
                {
                    if (state == State.Operand)
                    {
                        // Cannot follow other operand
                        throw new EvalException(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 EvalException(ErrOperatorExpected, parser.Position);
                    }
                    if (!(Char.IsLetter(parser.Peek()) || parser.Peek() == '_'))
                    {
                        // Invalid character
                        temp = String.Format(ErrUnexpectedCharacter, parser.Peek());
                        throw new EvalException(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 EvalException(ErrOperandExpected, parser.Position);
            }
            // Check for balanced parentheses
            if (parenCount > 0)
            {
                throw new EvalException(ErrClosingParenExpected, parser.Position);
            }
            // Retrieve remaining operators from stack
            while (stack.Count > 0)
            {
                tokens.Add(stack.Pop());
            }
            return(tokens);
        }