コード例 #1
0
        /// <summary>
        /// Creates a Formula from a string that consists of a standard infix expression composed
        /// from non-negative floating-point numbers (using C#-like syntax for double/int literals),
        /// variable symbols (a letter followed by zero or more letters and/or digits), left and right
        /// parentheses, and the four binary operator symbols +, -, *, and /.  White space is
        /// permitted between tokens, but is not required.
        ///
        /// Examples of a valid parameter to this constructor are:
        ///     "2.5e9 + x5 / 17"
        ///     "(5 * 2) + 8"
        ///     "x*y-2+35/9"
        ///
        /// Examples of invalid parameters are:
        ///     "_"
        ///     "-5.3"
        ///     "2 5 + 3"
        ///
        /// If the formula is syntacticaly invalid, throws a FormulaFormatException with an
        /// explanatory Message.
        /// </summary>
        public Formula(String formula)
        {
            var Operators = new List <string>();
            var Values    = new List <string>();
            var Var       = new List <string>();
            IEnumerable <string> tokens = Formula.GetTokens(formula);

            int parcount = 0;

            if (formula == null)
            {
                throw new ArgumentNullException("Requires a non-null formula string.");
            }


            if (tokens.Count() == 0)
            {
                throw new FormulaFormatException("No input detected.");
            }

            string first    = tokens.ElementAt <string>(0);
            string last     = tokens.ElementAt <string>(tokens.Count() - 1);
            string previous = "";

            //Checks first element in formula.
            if (Regex.IsMatch(first, rpPattern) || Regex.IsMatch(first, opPattern))
            {
                throw new FormulaFormatException("The first element of the formula is not either a (, number, or variable.");
            }

            //Checks last element in formula.
            if (Regex.IsMatch(last, lpPattern) || Regex.IsMatch(last, opPattern))
            {
                throw new FormulaFormatException("The last element of the formula is not either a ), number, or variable.");
            }

            foreach (string t in tokens)
            {
                //Checks for invalid input tokens.
                if (!Regex.IsMatch(t, lpPattern) && !Regex.IsMatch(t, rpPattern) && !Regex.IsMatch(t, doublePattern) &&
                    !Regex.IsMatch(t, varPattern) && !Regex.IsMatch(t, opPattern))
                {
                    throw new FormulaFormatException("Unexpected object in formula: " + t);
                }

                //Checks token for ( and adds to Operator list.
                if (Regex.IsMatch(t, lpPattern))
                {
                    Operators.Add(t);
                    parcount += 1;
                }

                //Checks token for ) and adds to Operator list.
                if (Regex.IsMatch(t, rpPattern))
                {
                    Operators.Add(t);
                    parcount -= 1;
                }

                //Checks token for valid operators and adds to Operator list.
                if (Regex.IsMatch(t, opPattern))
                {
                    Operators.Add(t);
                }

                //Checks token for numbers or variables and adds to Values list.
                if (Regex.IsMatch(t, doublePattern))
                {
                    Values.Add(t);
                }

                //Checks token for variables and adds to Variables list.
                if (Regex.IsMatch(t, varPattern) && !Regex.IsMatch(t, powpattern))
                {
                    Var.Add(t);
                }

                //Checks proper formatting following ( and operators.
                if (Regex.IsMatch(previous, lpPattern) || Regex.IsMatch(previous, opPattern) && !Regex.IsMatch(previous, powpattern))
                {
                    if (!Regex.IsMatch(t, varPattern) && !Regex.IsMatch(t, doublePattern) && !Regex.IsMatch(t, lpPattern))
                    {
                        throw new FormulaFormatException("Check 5: Formula containing " + previous + " followed by " + t + " is invalid.");
                    }
                }

                //Checks proper formatting following ), numbers, and variables.
                if (Regex.IsMatch(previous, rpPattern) || Regex.IsMatch(previous, doublePattern) || Regex.IsMatch(previous, varPattern))
                {
                    if (!Regex.IsMatch(t, opPattern) && !Regex.IsMatch(t, rpPattern))
                    {
                        throw new FormulaFormatException("Check 6: `Formula containing " + previous + " followed by " + t + " is invalid.");
                    }
                }

                //Checks relative parenthesis count.
                if (parcount < 0)
                {
                    throw new FormulaFormatException("Parenthesis mismatch: Formula contains " + parcount + " More ) than ( .");
                }

                previous = t;
            }
            if (parcount != 0)
            {
                throw new FormulaFormatException("Parenthesis mismatch: Formula contains More ( than ) .");
            }

            output    = formula;
            Variables = Var;
        }
コード例 #2
0
        /// <summary>
        /// Evaluates this Formula, using the Lookup delegate to determine the values of variables.  (The
        /// delegate takes a variable name as a parameter and returns its value (if it has one) or throws
        /// an UndefinedVariableException (otherwise).  Uses the standard precedence rules when doing the evaluation.
        ///
        /// If no undefined variables or divisions by zero are encountered when evaluating
        /// this Formula, its value is returned.  Otherwise, throws a FormulaEvaluationException
        /// with an explanatory Message.
        /// </summary>
        public double Evaluate(Lookup lookup)
        {
            var Operators = new Stack <string>();
            var Values    = new Stack <string>();
            IEnumerable <string> tokens = Formula.GetTokens(output);

            double FinalAnswer = 0;
            bool   foundexp    = false;

            if (lookup == null)
            {
                throw new ArgumentNullException("Requires a non-null lookup delegate for evaluating variables.");
            }

            //For the empty constructor case. We need it to behave like Formula("0") so evaluate should return 0.
            if (output == null)
            {
                return(0);
            }


            foreach (string t in tokens)
            {
                //Current item is a double.
                if (Regex.IsMatch(t, doublePattern) && !Regex.IsMatch(t, varPattern))
                {
                    double exp = 0;
                    //If we found an exponential we should convert to a double then run through normal procedure.
                    //For example, 5.0e2 should equal 500.0 .
                    if (Regex.IsMatch(t, powpattern))
                    {
                        MatchCollection matches = Regex.Matches(t, powpattern);
                        double          start   = double.Parse(matches[0].Groups[1].Value);
                        double          end     = double.Parse(matches[0].Groups[3].Value);
                        exp      = start * Math.Pow(10, end);
                        foundexp = true;
                    }


                    if (Operators.Count == 0)
                    {
                        //If t is an exponential phrase, push its value.
                        if (foundexp == true)
                        {
                            Values.Push(exp.ToString());
                        }

                        else
                        {
                            Values.Push(t);
                        }
                    }

                    if (Operators.Count != 0)
                    {
                        if (Operators.Peek() == "*" || Operators.Peek() == "/")
                        {
                            if (Operators.Peek() == "*")
                            {
                                //If current token is a exponential phrase we should use it's value.
                                if (foundexp == true)
                                {
                                    Values.Push(exp.ToString());
                                    DoMultiplication(ref Values);
                                    Operators.Pop();
                                }
                                else
                                {
                                    Values.Push(t);
                                    DoMultiplication(ref Values);
                                    Operators.Pop();
                                }
                            }
                            else
                            {
                                //If current token is a exponential phrase we should use it's value.
                                if (foundexp == true)
                                {
                                    Values.Push(exp.ToString());
                                    DoDivision(ref Values);
                                    Operators.Pop();
                                }
                                else
                                {
                                    Values.Push(t);
                                    DoDivision(ref Values);
                                    Operators.Pop();
                                }
                            }
                        }
                        else
                        {
                            if (foundexp == true)
                            {
                                Values.Push(exp.ToString());
                            }
                            else
                            {
                                Values.Push(t);
                            }
                        }
                    }
                }

                if (Regex.IsMatch(t, varPattern))
                {
                    try
                    {
                        lookup(t);
                    }
                    catch (UndefinedVariableException)
                    {
                        throw new FormulaEvaluationException("Value of variable " + t + " is undefined.");
                    }

                    if (Operators.Count == 0)
                    {
                        Values.Push(lookup(t).ToString());
                    }

                    if (Operators.Count != 0)
                    {
                        if (Operators.Peek() == "*" || Operators.Peek() == "/")
                        {
                            if (Operators.Peek() == "*")
                            {
                                Values.Push(lookup(t).ToString());
                                DoMultiplication(ref Values);
                                Operators.Pop();
                            }
                            else
                            {
                                Values.Push(lookup(t).ToString());
                                DoDivision(ref Values);
                                Operators.Pop();
                            }
                        }

                        else
                        {
                            Values.Push(lookup(t).ToString());
                        }
                    }
                }

                if (t == "+" || t == "-")
                {
                    if (Operators.Count != 0)
                    {
                        if (Operators.Peek() == "+" || Operators.Peek() == "-")
                        {
                            if (Operators.Peek() == "+")
                            {
                                DoAddition(ref Values);
                                Operators.Pop();
                            }
                            else
                            {
                                DoSubtraction(ref Values);
                                Operators.Pop();
                            }
                        }
                    }
                    Operators.Push(t);
                }

                if (t == "*" || t == "/")
                {
                    Operators.Push(t);
                }

                if (Regex.IsMatch(t, lpPattern))
                {
                    Operators.Push(t);
                }

                if (Regex.IsMatch(t, rpPattern))
                {
                    if (Operators.Peek() == "+" || Operators.Peek() == "-")
                    {
                        if (Operators.Peek() == "+")
                        {
                            DoAddition(ref Values);
                        }
                        else
                        {
                            DoSubtraction(ref Values);
                        }
                        Operators.Pop();
                    }
                    //Always removes the ) on the top of the stack.
                    Operators.Pop();

                    //Checks Operator stack for * or / and calculates if needed.
                    if (Operators.Count != 0)
                    {
                        if (Operators.Peek() == "*" || Operators.Peek() == "/")
                        {
                            if (Operators.Peek() == "*")
                            {
                                DoMultiplication(ref Values);
                            }
                            else
                            {
                                DoDivision(ref Values);
                            }
                            Operators.Pop();
                        }
                    }
                }
                foundexp = false;
            }

            if (Operators.Count == 0)
            {
                FinalAnswer = double.Parse(Values.Pop());
            }


            if (Operators.Count == 1)
            {
                if (Operators.Peek() == "+")
                {
                    DoAddition(ref Values);
                    FinalAnswer = double.Parse(Values.Pop());
                }

                else
                {
                    DoSubtraction(ref Values);
                    FinalAnswer = double.Parse(Values.Pop());
                }
            }

            return(FinalAnswer);
        }
コード例 #3
0
ファイル: Formula.cs プロジェクト: kaibjorkman/MySpreadSheet
        /// <summary>
        /// Creates a Formula from a string that consists of a standard infix expression composed
        /// from non-negative floating-point numbers (using C#-like syntax for double/int literals),
        /// variable symbols (a letter followed by zero or more letters and/or digits), left and right
        /// parentheses, and the four binary operator symbols +, -, *, and /.  White space is
        /// permitted between tokens, but is not required.
        ///
        /// Examples of a valid parameter to this constructor are:
        ///     "2.5e9 + x5 / 17"
        ///     "(5 * 2) + 8"
        ///     "x*y-2+35/9"
        ///
        /// Examples of invalid parameters are:
        ///     "_"
        ///     "-5.3"
        ///     "2 5 + 3"
        ///
        /// If the formula is syntacticaly invalid, throws a FormulaFormatException with an
        /// explanatory Message.
        ///
        /// An ArgumentNullException is thrown if string is null.
        /// </summary>
        public Formula(String formula, Normalizer n, Validator v)
        {
            if (formula == null)
            {
                throw new ArgumentNullException("Parameter is Null");
            }

            //get tokens
            IEnumerable <string> tokens = Formula.GetTokens(formula);

            //valid token patterns
            String opPattern    = @"^[\+\-*/]$";
            Regex  operation    = new Regex(opPattern);
            String varPattern   = @"^[a-zA-Z][0-9a-zA-Z]*$";
            Regex  variable     = new Regex(varPattern);
            String spacePattern = @"^\s+$";
            Regex  space        = new Regex(spacePattern);

            //counters
            int    opening        = 0;
            int    closing        = 0;
            int    elementCounter = 0;
            String previous       = null;

            //keep track of normalized variables

            normalized_vars = new HashSet <string>();

            //check if the there are any tokens to check
            if (tokens.Count <string>() == 0)
            {
                throw new FormulaFormatException("No tokens");
            }

            //check if the formua meets the syntactic standards
            foreach (string element in tokens)
            {
                elementCounter++;

                //check if there is at least one token
                if (element == null)
                {
                    throw new FormulaFormatException("No tokens");
                }


                double number;
                //check for all valid tokens
                if (!(element.Equals("(") || element.Equals(")") || operation.IsMatch(element) || variable.IsMatch(element) || Double.TryParse(element, out number) || space.IsMatch(element)))
                {
                    throw new FormulaFormatException("Invalid Tokens");
                }

                //check if the number of closing parathensis is ever greater than the number of openeing parathensis
                if (element.Equals("("))
                {
                    opening++;
                }

                if (element.Equals(")"))
                {
                    closing++;
                }

                if (closing > opening)
                {
                    throw new FormulaFormatException("More closing than opening");
                }

                if (elementCounter == tokens.Count <string>())
                {
                    //check if The total number of opening parentheses must equal the total number of closing parentheses.
                    if (opening != closing)
                    {
                        throw new FormulaFormatException("closing and opening do not equal");
                    }
                }

                //check if The first token of a formula must be a number, a variable, or an opening parenthesis.

                if (!(tokens.First().Equals("(") || variable.IsMatch(tokens.First()) || Double.TryParse(tokens.First(), out number)))
                {
                    throw new FormulaFormatException("First token was not a number, variable, or opening parenthesis");
                }

                //check if Any token that immediately follows an opening parenthesis or an operator must be either a number,
                //a variable, or an opening parenthesis.
                if (previous != null)
                {
                    if (previous.Equals("(") || operation.IsMatch(previous))
                    {
                        if (!(variable.IsMatch(element) || Double.TryParse(element, out number) || element.Equals("(")))
                        {
                            throw new FormulaFormatException("token immediatly following an open parethesis or operater was not a number, variable, or open parenthesis");
                        }
                    }

                    if (element.Equals("(") || operation.IsMatch(element))
                    {
                        if (element == tokens.Last <string>())
                        {
                            throw new FormulaFormatException("The last element can not be an open parathesis or an operator");
                        }
                    }

                    //check if Any token that immediately follows a number, a variable, or a closing parenthesis must be either
                    //an operator or a closing parenthesis.

                    if (Double.TryParse(previous, out number) || variable.IsMatch(previous) || previous.Equals(")"))
                    {
                        if (!(operation.IsMatch(element) || element.Equals(")")))
                        {
                            throw new FormulaFormatException("The token immediatlely folloeing a number, varibale, or colsing parenthesis was not either an operator or closing parethesis");
                        }
                    }
                }
                //change the previous reference
                previous = element;
            }


            //if all syntactical arrors pass then put the tokens in a string array to be accessed by other methods
            int stringCounter = 0;

            stringFormula = new String[tokens.Count()];
            foreach (string element in tokens)
            {
                //check if the element is a variable
                if (variable.IsMatch(element))
                {
                    if (!(variable.IsMatch(n.Invoke(element))))
                    {
                        throw new FormulaFormatException("normalized variable could not be recognized as a variable");
                    }

                    if (!(v.Invoke(n.Invoke(element))))
                    {
                        throw new FormulaFormatException("the normalized varible was not valid");
                    }

                    //add the normalized variable to the array

                    stringFormula[stringCounter] = n.Invoke(element);

                    //keep track of the normalized variables
                    normalized_vars.Add(n.Invoke(element));
                }
                stringFormula[stringCounter] = element;
                stringCounter++;
            }
        }