/// <summary>
        /// Parse the assigner to get the tree of operations and their operands (of type literal, reference, or operation)
        /// </summary>
        /// <param name="assignerToken"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        private Boolean ParseAssigner(String assignerToken, ref String errorMessage)
        {
            Boolean     returnValue = default(Boolean);
            OperandBase operand     = default(OperandBase);

            try
            {
                if (assignerToken == String.Empty)
                {
                    throw new ArgumentException(String.Format("Incorrectly formatted formula; the assigner is not defined: '{0}'", assignerToken));
                }

                //init parsing variables dictionary
                if (ParsingVariables == null)
                {
                    ParsingVariables = new Dictionary <String, String>();
                }
                else
                {
                    ParsingVariables.Clear();
                }

                //pre-parse token for parenthesis, and replace expressions in parenthesis with variables
                if (!ParseParenthesis(ref assignerToken, ref errorMessage))
                {
                    throw new ApplicationException(errorMessage);
                }

                //recursively parse assignerToken and build operation tree
                if (!ParseToken(assignerToken, ref operand, ref errorMessage))
                {
                    throw new ApplicationException(errorMessage);
                }

                //clear parsing variables dictionary
                ParsingVariables.Clear();

                AssignerOperand = operand;

                returnValue = true;
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);
                //throw;
            }
            return(returnValue);
        }
        /// <summary>
        /// Parse token to identify whether it is a pre-parsing variable, a literal, or a cell-reference.
        /// </summary>
        /// <param name="token"></param>
        /// <param name="operand"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        private Boolean ParseOperand(String token, ref OperandBase operand, ref String errorMessage)
        {
            Boolean             returnValue   = default(Boolean);
            Single              numericResult = default(Single);
            Sheet               parentSheet   = default(Sheet);
            List <CategoryItem> criteria      = default(List <CategoryItem>);
            String              variableValue = default(String);

            try
            {
                //trap string leterals
                if ((token.Contains("'")) || (token.Contains("\"")))
                {
                    throw new ApplicationException(String.Format("String literals not supported: '{0}'", token));
                }
                //trap parentheses
                if ((token.Contains("(")) || (token.Contains(")")))
                {
                    throw new ApplicationException(String.Format("Parentheses found after pre-processing: '{0}'", token));
                }

                token = token.Trim();

                if (token.StartsWith(Formula.ParsingVariableIndicator))
                {
                    //token is a parsing variable; look up value and process token as an expression

                    //look up parsing variable
                    if (!ParsingVariables.TryGetValue(token, out variableValue))
                    {
                        throw new ApplicationException(String.Format("Unable to look up variable value for variable name: '{0}'", token));
                    }

                    //parse expression and return operand
                    if (!ParseToken(variableValue, ref operand, ref errorMessage))
                    {
                        throw new ApplicationException(errorMessage);
                    }

                    returnValue = true;
                }
                else if (token.StartsWith(Formula.FunctionIndicator))
                {
                    //token is a function

                    //parse function and return operand
                    if (!ParseFunction(token, ref operand, ref errorMessage))
                    {
                        throw new ApplicationException(errorMessage);
                    }

                    returnValue = true;
                }
                else if (Single.TryParse(token, out numericResult))
                {
                    //token is a numeric literal
                    //return literal operand with numeric value
                    operand = new OperandLiteral(numericResult);

                    returnValue = true;
                }
                else
                {
                    //token should be reference-operand
                    parentSheet = SettingsController <Settings> .Settings.Sheets.Find(s => s.Equals(this.Parent));

                    //parentSheet = SettingsController<Settings>.Settings.Sheets.Find(s => s.Name == this.Parent);
                    if (parentSheet == null)
                    {
                        throw new ApplicationException(String.Format("Unable to find sheet '{0}' for formula '{1}'.", this.Parent, this.Value));
                    }

                    criteria = parentSheet.GetCategoryItemsForAddressName(token, true);
                    if ((criteria == null) || (criteria.Count < 1))
                    {
                        throw new ApplicationException(String.Format("Unable to find category-item criteria in sheet '{0}' for formula '{1}'.", this.Parent, this.Value));
                    }

                    //store criteria
                    operand = new OperandReference(criteria);

                    returnValue = true;
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);
                //throw;
            }
            return(returnValue);
        }
        /// <summary>
        /// Pre-parse the token for parenthesis, and replace (from the inside out)
        /// each expression in parenthesis with a variable that acts as a placeholder.
        /// The expression is stored in a dictionary identified by @variable_name.
        /// The variable is used later during regular parsing to look up the expression.
        /// </summary>
        /// <param name="assignerToken"></param>
        /// <param name="errorMessage"></param>
        /// <returns></returns>
        private Boolean ParseParenthesis(ref String assignerToken, ref String errorMessage)
        {
            Boolean returnValue             = default(Boolean);
            Regex   regEx                   = default(Regex);
            Int32   countOfLeftParenthesis  = default(Int32);
            Int32   countOfRightParenthesis = default(Int32);
            Match   match                   = default(Match);
            String  variableName            = default(String);
            String  variableValue           = default(String);

            try
            {
                countOfLeftParenthesis = (from Char ch in assignerToken
                                          where ch == Formula.LeftParenthesis
                                          select ch).Count();
                countOfRightParenthesis = (from Char ch in assignerToken
                                           where ch == Formula.RightParenthesis
                                           select ch).Count();
                if ((countOfLeftParenthesis > 0) || (countOfRightParenthesis > 0))
                {
                    //parenthesis found

                    if (countOfLeftParenthesis != countOfRightParenthesis)
                    {
                        //mismatched pair
                        throw new ApplicationException(String.Format("Mis-matched parenthesis in token: '{0}'", assignerToken));
                    }

                    regEx = new Regex(Formula.RegExFindInnermostParenthesisAndContents);
                    while ((assignerToken.Contains(Formula.LeftParenthesis)) || (assignerToken.Contains(Formula.RightParenthesis)))
                    {
                        //process until there are no more pairs left

                        match = regEx.Match(assignerToken);
                        if ((match.Value == null) || (match.Value == String.Empty))
                        {
                            //pair did not match pattern
                            throw new ApplicationException(String.Format("Parenthesis in token not formatted correctly: '{0}'", assignerToken));
                        }

                        //generate variable
                        variableName  = String.Format("{0}{1}", Formula.ParsingVariableIndicator, Guid.NewGuid().ToString().Replace('-'.ToString(), String.Empty));
                        variableValue = match.Value.Replace(Formula.LeftParenthesis.ToString(), String.Empty).Replace(Formula.RightParenthesis.ToString(), String.Empty);
                        if ((variableValue == null) || (variableValue == String.Empty))
                        {
                            //pair did not contain an expression
                            throw new ApplicationException(String.Format("Value in parenthesis in token not formatted correctly: '{0}'", assignerToken));
                        }

                        //store variable in dictionary
                        ParsingVariables.Add(variableName, variableValue);

                        //replace expression (including parenthesis) in token w/ variable name
                        assignerToken = assignerToken.Replace(match.Value, variableName);
                    }
                }

                returnValue = true;
            }
            catch (Exception ex)
            {
                Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error);
                //throw;
            }
            return(returnValue);
        }