/// <summary> /// Returns a string representing the input converted to RPN. /// Only call this function if ParseInput() was successfull. /// </summary> /// <returns>String representing input converted into RPN.</returns> public String GetRPNOutputAsString() { Debug.Assert(curr_state != ParserState.ParserState_Error); String rpn_output = ""; Stack <CalculatorToken> tmpstk = new Stack <CalculatorToken>(output_queue); while (tmpstk.Any()) { CalculatorToken token = tmpstk.Pop(); switch (token.ct_type) { case CalculatorToken.TOKEN_Type.TOKEN_Type_Function: case CalculatorToken.TOKEN_Type.TOKEN_Type_Operator: rpn_output = rpn_output + token.ct_func + " "; break; case CalculatorToken.TOKEN_Type.TOKEN_Type_Number: rpn_output = rpn_output + token.ct_fval.ToString() + " "; break; default: break; } } return(rpn_output); }
/// <summary> /// Called when the parser reaches a left parenthesis. /// </summary> private void Handle_LeftParen() { // // Left parentheses simply get pushed into the operator stack. CalculatorToken new_token = new CalculatorToken { ct_type = CalculatorToken.TOKEN_Type.TOKEN_Type_LeftParen, ct_fval = 0.0f, ct_func = null }; op_stack.Push(new_token); AdvanceToNextAtomEx(); }
/// <summary> /// Called when the parser reaches a function. /// </summary> private void Handle_Function() { String func_name = null; int fname_start = input_iter; // // Build function name. for (; ;) { if (IsEndOfInput()) { break; } Char curr_atom = GetCurrentAtomFromInput(); if (Char.IsLetterOrDigit(curr_atom)) { AdvanceToNextAtomEx(); } else { break; } } Debug.Assert(fname_start < input_iter); func_name = input_string.Substring(fname_start, input_iter - fname_start); // // Check if the calculator implements this function, and if so push it into // the operator stack. if (!ValidateFunction(func_name)) { curr_state = ParserState.ParserState_Error; return; } CalculatorToken new_token = new CalculatorToken { ct_type = CalculatorToken.TOKEN_Type.TOKEN_Type_Function, ct_fval = 0.0f, ct_func = func_name }; op_stack.Push(new_token); }
/// <summary> /// Called when the parser reaches an operator. /// </summary> private void Handle_Operator() { Char op1 = GetCurrentAtomFromInput(); // // While there is a seconds operator (op2) at the top of the operators stack // and either op1 is left assoc and has a lower or equal precedence to op2 // or op1 is right assoc and has a lower precedence that op2, pop op2 // off the operator stack and into the output queue. while (op_stack.Any() && op_stack.Peek().ct_type == CalculatorToken.TOKEN_Type.TOKEN_Type_Operator) { Char op2 = op_stack.Peek().ct_func[0]; if ((IsOperatorLeftAssociative(op1) && (GetOperatorPrecedenceLevel(op1) <= GetOperatorPrecedenceLevel(op2))) || (!IsOperatorLeftAssociative(op1) && (GetOperatorPrecedenceLevel(op1) < GetOperatorPrecedenceLevel(op2)))) { output_queue.Push(op_stack.Pop()); } else { break; } } // // Push op1 into the operator stack. CalculatorToken new_token = new CalculatorToken { ct_type = CalculatorToken.TOKEN_Type.TOKEN_Type_Operator, ct_fval = 0.0f, ct_func = new String(op1, 1) }; op_stack.Push(new_token); AdvanceToNextAtomEx(); }
/// <summary> /// Reads the next token from the input stream and sets the parser's state /// based on that token's type. /// </summary> private void ReadNextTokenFromInput() { // // Eat any whitespaces. while (!IsEndOfInput() && Char.IsWhiteSpace(GetCurrentAtomFromInput())) { AdvanceToNextAtomEx(); } if (IsEndOfInput()) { return; } // // Analyze current token and set parser's state. Char curr_atom = GetCurrentAtomFromInput(); if (curr_atom == '-' && CanPeekNextAtomAndIsDigit()) { if (!op_stack.Any() || op_stack.Peek().ct_type == CalculatorToken.TOKEN_Type.TOKEN_Type_RightParen) { CalculatorToken fake_plus = new CalculatorToken { ct_func = "+", ct_type = CalculatorToken.TOKEN_Type.TOKEN_Type_Operator, ct_fval = 0.0f }; op_stack.Push(fake_plus); } curr_state = ParserState.ParserState_Digit; return; } if (Char.IsDigit(curr_atom)) { curr_state = ParserState.ParserState_Digit; return; } if (TokenIsAnOperator(curr_atom)) { curr_state = ParserState.ParserState_Operator; return; } if (Char.IsLetter(curr_atom)) { curr_state = ParserState.ParserState_Func; return; } if (curr_atom == '(') { curr_state = ParserState.ParserState_LeftParen; return; } if (curr_atom == ')') { curr_state = ParserState.ParserState_RightParen; return; } if (curr_atom == ',') { curr_state = ParserState.ParserState_Comma; return; } curr_state = ParserState.ParserState_Error; }
/// <summary> /// Called when the parser reaches a function. /// </summary> private void Handle_Function() { String func_name = null; int fname_start = input_iter; // // Build function name. for (; ; ) { if (IsEndOfInput()) break; Char curr_atom = GetCurrentAtomFromInput(); if (Char.IsLetterOrDigit(curr_atom)) { AdvanceToNextAtomEx(); } else { break; } } Debug.Assert(fname_start < input_iter); func_name = input_string.Substring(fname_start, input_iter - fname_start); // // Check if the calculator implements this function, and if so push it into // the operator stack. if (!ValidateFunction(func_name)) { curr_state = ParserState.ParserState_Error; return; } CalculatorToken new_token = new CalculatorToken { ct_type = CalculatorToken.TOKEN_Type.TOKEN_Type_Function, ct_fval = 0.0f, ct_func = func_name }; op_stack.Push(new_token); }
/// <summary> /// Called when the parser reaches a digit. /// </summary> private void Handle_Digit() { bool is_float = false; String curr_num = ""; // // Check for minus sign and skip it if present. Char curr_atom = GetCurrentAtomFromInput(); if (curr_atom == '-') { curr_num = curr_num + curr_atom; AdvanceToNextAtomEx(); } // // Get the rest of the number's digits. for (; ; ) { if (IsEndOfInput()) break; curr_atom = GetCurrentAtomFromInput(); if (Char.IsDigit(curr_atom)) { curr_num = curr_num + curr_atom; AdvanceToNextAtomEx(); } else if (curr_atom == '.') { if (is_float) { // // Misplaced dot, halt processing. curr_state = ParserState.ParserState_Error; return; } // // Floating point number. is_float = true; curr_num = curr_num + curr_atom; AdvanceToNextAtomEx(); } else { break; } } // // Numbers get pushed into the output queue. Debug.Assert(curr_num.Length > 0); CalculatorToken new_token = new CalculatorToken { ct_type = CalculatorToken.TOKEN_Type.TOKEN_Type_Number, ct_fval = float.Parse(curr_num), ct_func = null }; output_queue.Push(new_token); }
/// <summary> /// Splits up the input string and returns all of the digits between the delimiters /// </summary> /// <param name="input"></param> /// <returns></returns> private List <CalculatorToken> Tokenize(string input) { var delimiters = new List <string> { ",", _secondaryDelimiter }; var regexBracketDelimiters = new Regex("//\\[.*?\\]\\n"); var regexSingleCharDelimiter = new Regex("\\/\\/.*?\\n"); var bracketDelimiterMatches = regexBracketDelimiters.Matches(input); var singleCharMatches = regexSingleCharDelimiter.Matches(input); var numbersStart = input.IndexOf("\n") + 1; //If there are any matches where the custom delimiters fall inside brackets if (bracketDelimiterMatches.Any()) { var match = bracketDelimiterMatches.First().Value; var innerString = match.Substring(2, match.Length - 3); var innerRegex = new Regex("\\[.*?\\]"); var matches = innerRegex.Matches(innerString).ToList(); //If there is only one, it will not be inside nested brackets if (matches.Count == 1) { var m = matches.Single().Value; var withoutBrackets = m.Substring(1, m.Length - 2); delimiters.Add(withoutBrackets); } //If there are multiples, they will each be inside their own brackets else if (matches.Count > 1) { foreach (var m in matches) { var matchInner = m.Value; var withoutBrackets = matchInner.Substring(1, matchInner.Length - 2); delimiters.Add(withoutBrackets); } } } //Single character without brackets else if (singleCharMatches.Any()) { var match = singleCharMatches.First().Value; var innerString = match.Substring(2, match.Length - 3); delimiters.Add(innerString); } else { //No Custom Delimiters numbersStart = 0; } var inputNumbers = input.Substring(numbersStart, input.Length - numbersStart); var rawTokens = inputNumbers.Split(delimiters.ToArray(), StringSplitOptions.None); var tokens = new List <CalculatorToken>(); foreach (var raw in rawTokens) { var token = new CalculatorToken { Token = raw }; decimal value; if (Decimal.TryParse(raw, out value)) { token.Value = value; //Check lower bound (error) if (value < 0 && _rejectNegatives) { token.Errored = true; } //Check upper bound (act as 0) if (value > _upperBound) { token.Value = 0; } } tokens.Add(token); } return(tokens); }
/// <summary> /// Called when the parser reaches a digit. /// </summary> private void Handle_Digit() { bool is_float = false; String curr_num = ""; // // Check for minus sign and skip it if present. Char curr_atom = GetCurrentAtomFromInput(); if (curr_atom == '-') { curr_num = curr_num + curr_atom; AdvanceToNextAtomEx(); } // // Get the rest of the number's digits. for (; ;) { if (IsEndOfInput()) { break; } curr_atom = GetCurrentAtomFromInput(); if (Char.IsDigit(curr_atom)) { curr_num = curr_num + curr_atom; AdvanceToNextAtomEx(); } else if (curr_atom == '.') { if (is_float) { // // Misplaced dot, halt processing. curr_state = ParserState.ParserState_Error; return; } // // Floating point number. is_float = true; curr_num = curr_num + curr_atom; AdvanceToNextAtomEx(); } else { break; } } // // Numbers get pushed into the output queue. Debug.Assert(curr_num.Length > 0); CalculatorToken new_token = new CalculatorToken { ct_type = CalculatorToken.TOKEN_Type.TOKEN_Type_Number, ct_fval = float.Parse(curr_num), ct_func = null }; output_queue.Push(new_token); }