/// <summary> /// Parse the string token to get an operand of type Literal, Reference, or Operation. /// 1) Literal operands return the value that they contain. /// 2) Reference operands use their own category item criteria /// to narrow down the list of cells to which the formula applies. Then the specific cell's criteria /// is used to identify categories not in the criteria, and use those to narrow down to a single source cell. /// 3) Operation operands perform their operation and trigger the evaulation of their child operands. /// </summary> /// <param name="token"></param> /// <param name="operand"></param> /// <param name="errorMessage"></param> /// <returns></returns> private Boolean ParseToken(String token, ref OperandBase operand, ref String errorMessage) { Boolean returnValue = default(Boolean); String[] tokens = default(String[]); String leftToken = default(String); String rightToken = default(String); Boolean isFoundOperator = default(Boolean); OperatorBase foundOperator = default(OperatorBase); OperandBase leftOperand = default(OperandBase); OperandBase rightOperand = default(OperandBase); OperationBase operation = default(OperationBase); try { //look for operators, if any, in reverse precedence order foreach (OperatorBase op in arithmeticOperators) { if (token.Contains(op.Name)) { isFoundOperator = true; foundOperator = op; break; } } //create an array of one or two sub-tokens if (isFoundOperator) { //split into 2 parts only tokens = token.Split(new Char[] { foundOperator.Name[0] }, 2); } else { //create list with single item String[] newList = { token }; tokens = newList; } //process sub-tokens if (tokens.Length == 0) { throw new ArgumentException(String.Format("Incorrectly formatted formula; there does not appear to be an operand: '{0}'", token)); } else if (tokens.Length == 1) { //no operator; process single operand leftToken = tokens[0].Trim(); if (!ParseOperand(leftToken, ref leftOperand, ref errorMessage)) { throw new ApplicationException(errorMessage); } operand = leftOperand; returnValue = true; } else if (tokens.Length == 2) { //operator found; process dual operands with recursive call leftToken = tokens[0].Trim(); if (!ParseToken(leftToken, ref leftOperand, ref errorMessage)) { throw new ApplicationException(errorMessage); } rightToken = tokens[1].Trim(); if (!ParseToken(rightToken, ref rightOperand, ref errorMessage)) { throw new ApplicationException(errorMessage); } //create operation from left/right operands and operator, and create OperandOperation //create OperationBinary which configures 2 OperandBase dictionary entries called Left, Right. operation = new OperationBinary(); operation.Operands["Left"] = leftOperand; operation.Operands["Right"] = rightOperand; operation.Operator = foundOperator; operand = new OperandOperation(operation); returnValue = true; } else if (tokens.Length > 2) { throw new ArgumentException(String.Format("Unexpected formula format: '{0}'", token)); } } catch (Exception ex) { errorMessage = ex.Message; Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error); //throw; } return(returnValue); }
/// <summary> /// Parse the string token to get an operand of type Operation, /// whose operation is of type Function(?), /// and where the operator is the defined in the token. /// </summary> /// <param name="token"></param> /// <param name="operand"></param> /// <param name="errorMessage"></param> /// <returns></returns> private Boolean ParseFunction(String token, ref OperandBase operand, ref String errorMessage) { Boolean returnValue = default(Boolean); Boolean isFoundOperator = default(Boolean); OperatorBase foundOperator = default(OperatorBase); Int32 countOfLeftBraces = default(Int32); Int32 countOfRightBraces = default(Int32); Regex regEx = default(Regex); Match match = default(Match); String parametersToken = default(String); String[] parameterTokens = default(String[]); String functionNameToken = default(String); Dictionary <String, OperandBase> parameterDictionary = default(Dictionary <String, OperandBase>); OperandBase parameterOperand = default(OperandBase); Int32 parameterIndex = default(Int32); try { countOfLeftBraces = (from Char ch in token where ch == Formula.LeftBrace select ch).Count(); countOfRightBraces = (from Char ch in token where ch == Formula.RightBrace select ch).Count(); if ((countOfLeftBraces > 0) || (countOfRightBraces > 0)) { //braces found if (countOfLeftBraces != countOfRightBraces) { //mismatched pair throw new ApplicationException(String.Format("Mis-matched braces in token: '{0}'", token)); } //get parameters regEx = new Regex(Formula.RegExFindInnermostBracesAndContents); match = regEx.Match(token); if ((match.Value == null) || (match.Value == String.Empty)) { //pair did not match pattern throw new ApplicationException(String.Format("Braces in token not formatted correctly: '{0}'", token)); } parametersToken = match.Value.Replace(Formula.LeftBrace.ToString(), String.Empty).Replace(Formula.RightBrace.ToString(), String.Empty); if ((parametersToken == null) || (parametersToken == String.Empty)) { //pair did not contain an expression throw new ApplicationException(String.Format("Value in parenthesis in token not formatted correctly: '{0}'", token)); } parameterTokens = parametersToken.Split(Formula.FunctionParameterDelimiter); if (parameterTokens.Length == 0) { //pair did not contain any values throw new ApplicationException(String.Format("Value in parenthesis in token not formatted correctly: '{0}'", token)); } //get function name regEx = new Regex(Formula.RegExFindFunctionNameDelimitersAndContents); match = regEx.Match(token); if ((match.Value == null) || (match.Value == String.Empty)) { //pair did not match pattern throw new ApplicationException(String.Format("Function name delimiters in token not formatted correctly: '{0}'", token)); } functionNameToken = match.Value.Replace(Formula.FunctionIndicator.ToString(), String.Empty).Replace(Formula.LeftBrace.ToString(), String.Empty); if ((functionNameToken == null) || (functionNameToken == String.Empty)) { //pair did not contain an expression throw new ApplicationException(String.Format("Function name in token not formatted correctly: '{0}'", token)); } //look for operators, if any foreach (OperatorBase op in functionOperators) { if (functionNameToken.ToUpper().Contains(op.Name)) { isFoundOperator = true; foundOperator = op; break; } } //get function if (isFoundOperator) { //build operation operand with function operation consisting of operator and each parameter token parsed into an operand. parameterDictionary = new Dictionary <String, OperandBase>(); foreach (String parameterToken in parameterTokens) { if (!ParseToken(parameterToken, ref parameterOperand, ref errorMessage)) { //error parsing operand token throw new ApplicationException(String.Format("Unable to parse parameter '{0}' in function token '{1}'.", parameterToken, token)); } parameterDictionary.Add(String.Format("Value{0}", parameterIndex++), parameterOperand); } //set operand to new OperandOperation of type OperationFunction using operator of type OperatorXXX (foundOperator) operand = new OperandOperation(new OperationFunction(foundOperator, parameterDictionary)); } else { throw new ApplicationException(String.Format("Unable to find function: '{0}'", token)); } } else { throw new ApplicationException(String.Format("Unable to find braces for parameters: '{0}'", token)); } returnValue = true; } catch (Exception ex) { Log.Write(ex, MethodBase.GetCurrentMethod(), EventLogEntryType.Error); //throw; } return(returnValue); }