/// <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); }
/// <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)); }
/// <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> private double EvaluateParameter(TextParser parser, int paramStart) { try { // Extract expression and evaluate it string expression = parser.Extract(paramStart, parser.Position); return(Execute(expression)); } catch (FormulaException ex) { // Adjust column and rethrow exception ex.Column += paramStart; throw; } }
/// <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); }