// ************************************************************************** // // (C) Copyright 2009 Swiss SIMTEC AG // // ************************************************************************** // // $HeadURL$ // $LastChangedBy$ // $LastChangedDate$ // $LastChangedRevision$ // // ************************************************************************** // manual class headers #include "IntegerCalculator.hpp" #include #include namespace N_SST { IntegerCalculator::IntegerCalculator() : m_OperationList({{"*", 6},{"/", 6},{"%", 6},{"+", 5},{"-", 5},{"<<", 4},{">>", 4}}) , m_AdditionalErrorText() , m_ParanthesisCount(0) , m_Error(IntegerCalculator::e_Ok) { } // --------------------------------------------------------------- // Converts the expressen in a_calcText into a signed value. // Returns whether an error happend in the variable a_error. // If an error happend, the variable a_addErrorText can // contain more informaition about the error (see E_Error for // more information about errors). // --------------------------------------------------------------- int64_t IntegerCalculator::calc(const std::string& a_calcText, E_Error& a_error, std::string* a_addErrorText) { std::string calcText = a_calcText; // separate operations from variables for (auto iter = m_OperationList.begin(); iter != m_OperationList.end(); ++iter) replace(calcText, iter->first, " "+iter->first+" "); // separate paranthesis from variables replace(calcText, "(", " ( "); replace(calcText, ")", " ) "); // now calculate the expression m_ParanthesisCount = 0; m_Error = e_Ok; m_AdditionalErrorText = ""; size_t pos = 0; bool dummy; int64_t value = calc(calcText, pos, dummy, "", 0, "+"); // todo: use member-variable m_CalcText //M_INFO("calc: " << calcText << " value: " << value); // check the paranthesis if (m_Error == e_Ok) { if (m_ParanthesisCount > 0) { m_Error = e_MissingRightParanthesis; } else if (m_ParanthesisCount < 0) { m_Error = e_MissingLeftParanthesis; } else if (pos != calcText.length()) { m_Error = e_UnknownError; } } // actions if a failure happens a_error = m_Error; if (a_error != e_Ok) { value = 0; if (a_addErrorText) { *a_addErrorText = m_AdditionalErrorText; } } return value; } // --------------------------------------------------------------- // Adds or changes a case-sensitive variable to the calculator. // --------------------------------------------------------------- void IntegerCalculator::addVariable(const std::string& a_name, const int64_t a_value) { std::map::iterator iter = m_VariableList.find(a_name); if (iter != m_VariableList.end()) removeVariable(a_name); m_VariableList.insert(std::make_pair(a_name, a_value)); } // --------------------------------------------------------------- // Removes a case-sensitive variable from the calculator. // --------------------------------------------------------------- void IntegerCalculator::removeVariable(const std::string& a_name) { std::map::iterator iter = m_VariableList.find(a_name); if (iter != m_VariableList.end()) m_VariableList.erase(iter); } // --------------------------------------------------------------- // Calculates the expression (recursive) // --------------------------------------------------------------- int64_t IntegerCalculator::calc(const std::string& a_calcText, size_t& a_pos, bool& a_IsEndParanthesis, const std::string& a_prevOp, const int64_t a_prevValue, const std::string& a_initOp) { std::string operation = a_initOp; int64_t value1 = a_prevValue; bool isNegative = false; std::string calcText = a_calcText; a_IsEndParanthesis = false; while (a_pos < calcText.length()) { if (' ' == calcText[a_pos]) { a_pos++; } else if ('(' == calcText[a_pos]) { m_ParanthesisCount++; bool dummy; int64_t value2 = calcSign(calc(calcText, ++a_pos, dummy), isNegative); value1 = doOperation(value1, operation, value2); // get next operation to check priority of operation size_t tmpPos = a_pos; // do not change the position, we only pre-read std::string nextOp = getToken(a_calcText, tmpPos); if (isOperation(nextOp) && isOperation(a_prevOp) && !isOperationPriorityHigher(nextOp, a_prevOp)) { // end of higher priority operation return value1; } operation = ""; } else if (')' == calcText[a_pos]) { m_ParanthesisCount--; a_pos++; a_IsEndParanthesis = true; return value1; } else if (isDigit(calcText[a_pos])) { // value // ----- int64_t value2 = calcSign(convertValue(calcText, a_pos), isNegative); if (calcWithPriority(calcText, a_pos, value1, operation, value2, a_prevOp)) return value1; } else { // operation or variable // --------------------- std::string oldOperation = operation; operation = getToken(calcText, a_pos); // is it a variable? if (!isOperation(operation)) { int64_t value2 = 0; if (getVariableValue(operation, value2)) { value2 = calcSign(value2, isNegative); if (calcWithPriority(calcText, a_pos, value1, oldOperation, value2, a_prevOp)) return value1; operation = ""; } else { // error m_Error = e_UnknownOperationOrVariable; m_AdditionalErrorText = operation; } } else { // do we have a sign? if (oldOperation != "") { if (operation == "+") { operation = oldOperation; } else if (operation == "-") { isNegative = !isNegative; operation = oldOperation; } else { m_Error = e_NoOperandBetweenOperators; m_AdditionalErrorText = oldOperation + " " + operation; return 0; } } } } } return value1; } // --------------------------------------------------------------- // Calculates an operation with respecting the priority of // operations. This will be done with recursion. // --------------------------------------------------------------- bool IntegerCalculator::calcWithPriority(const std::string& a_calcText, size_t& a_pos, int64_t& a_value1, std::string& a_operation, int64_t a_value2, const std::string& a_prevOp) { bool isParaEnd = false; // get next operation to check priority of operation size_t tmpPos = a_pos; // only pre-read std::string nextOp = getToken(a_calcText, tmpPos); if (isOperation(a_operation) && isOperation(nextOp) && isOperationPriorityHigher(nextOp, a_operation)) { // next operation has higher priority => calc the next operation first a_value2 = calc(a_calcText, a_pos, isParaEnd, a_operation, a_value2); } a_value1 = doOperation(a_value1, a_operation, a_value2); if (isParaEnd || (isOperation(nextOp) && isOperation(a_prevOp) && !isOperationPriorityHigher(nextOp, a_prevOp))) { // end of higher priority operation return true; } a_operation = ""; return false; } // --------------------------------------------------------------- // Returns the inverted a_value if a_isNegative is true. // a_isNegative will be false after calling this function. // If a_isNegative is true, the NOT inverted a_value will be // returned. // --------------------------------------------------------------- int64_t IntegerCalculator::calcSign(const int64_t a_value, bool& a_isNegative) { if (a_isNegative) { a_isNegative = false; return -a_value; } return a_value; } // --------------------------------------------------------------- // Converts the value in form of text to a number. The Text // can be decimal, hex (0x) or binary (0b). // --------------------------------------------------------------- int64_t IntegerCalculator::convertValue(const std::string &a_valueText, size_t& a_pos) { std::string valueText = a_valueText.substr(a_pos); int base = 10; size_t startpos = 0; // hex- and binary-stuff bool isHex = (0 == valueText.find("0X")) || (0 == valueText.find("0x")); bool isBin = (0 == valueText.find("0B")) || (0 == valueText.find("0b")); if (isHex) { base = 16; startpos = 2; } else if (isBin) { base = 2; startpos = 2; } // search end of value size_t i; for (i=startpos; i i && valueText[i] != ' ') { m_Error = e_InvalidOperand; int spacePos = valueText.find(' '); m_AdditionalErrorText = valueText.substr(0, spacePos); } a_pos += i; int64_t value = 0; std::stringstream stream; stream.str(valueText.substr(startpos, i-startpos)); if (2 == base) { // our stl is not able to read in binary-values, therefore we have to implement it ourself const std::string binValueAsText = stream.str(); for (const char c : binValueAsText) { value = value << 1; if (c == '1') value += 1; } } else { stream << std::setbase(base); stream >> value; } return value; } // --------------------------------------------------------------- // Returns the next token (with space as separator). Heading // spaces will be ignored. // --------------------------------------------------------------- std::string IntegerCalculator::getToken(const std::string &a_calcText, size_t& a_pos) { // skip spaces at begin while (a_pos < a_calcText.length()) { if (a_calcText[a_pos] == ' ') a_pos++; else break; } // search until next space (or end) size_t startPos = a_pos; while (a_pos < a_calcText.length()) { if (' ' == a_calcText[a_pos]) break; a_pos++; } return a_calcText.substr(startPos, a_pos-startPos); } // --------------------------------------------------------------- // returns true if a_operation is a known operation. // --------------------------------------------------------------- bool IntegerCalculator::isOperation(const std::string& a_operation) const { std::map::const_iterator iter = m_OperationList.find(a_operation); return (iter != m_OperationList.end()); } // --------------------------------------------------------------- // Calculates the opeation a_operations with a_value1 as first // and a_value2 as second operand. // --------------------------------------------------------------- int64_t IntegerCalculator::doOperation(const int64_t a_value1, const std::string& a_operation, const int64_t a_value2) { // M_INFO("" << a_value1 << " " << a_operation << " " << a_value2); if (("+" == a_operation) || a_operation.empty()) { return a_value1 + a_value2; } else if ("-" == a_operation) { return a_value1 - a_value2; } else if ("*" == a_operation) { return a_value1 * a_value2; } else if ("/" == a_operation) { if (0 == a_value2) { m_Error = e_DivisionByZero; } else { return a_value1 / a_value2; } } else if ("%" == a_operation) { if (0 == a_value2) { m_Error = e_DivisionByZero; } else { return a_value1 % a_value2; } } else if ("<<" == a_operation) { return a_value1 << a_value2; } else if (">>" == a_operation) { return a_value1 >> a_value2; } else { m_Error = e_UnknownOperationOrVariable; m_AdditionalErrorText = a_operation; } return 0; } // --------------------------------------------------------------- // returns true if priority of a_opeation1 is higher than // priority of a_operation2. // --------------------------------------------------------------- bool IntegerCalculator::isOperationPriorityHigher(const std::string& a_operation1, const std::string& a_operation2) const { return (getPriority(a_operation1) > getPriority(a_operation2)); } // --------------------------------------------------------------- // Returns true if the variable named a_variableName is known // and a_value will be filled with its value. If the variable // is not found false will be returned. // Overload this virtual function if you want to implement // your own variable-list. // --------------------------------------------------------------- bool IntegerCalculator::getVariableValue(const std::string& a_variableName, int64_t& a_value) const { auto iter = m_VariableList.find(a_variableName); if (iter != m_VariableList.end()) { a_value = iter->second; return true; } // error return false; } // --------------------------------------------------------------- // Returns true if a_char is a digit. When a_isHex is true, // also the charactes a-f will be accepted as digits. When // a_isBin is true, only 0 and 1 will be treated as digit. // --------------------------------------------------------------- bool IntegerCalculator::isDigit(const char a_char, const bool a_isHex, const bool a_isBin) { if (a_isHex) { // for hex-values also A..F are 'digits' return (a_char >= '0' && a_char <= '9') || (a_char >= 'A' && a_char <= 'F') || (a_char >= 'a' && a_char <= 'f'); } else if (a_isBin) { // for binary-values only 0 and 1 are allowed return (a_char >= '0' && a_char <= '1'); } // normal digits return (a_char >= '0' && a_char <= '9'); } // --------------------------------------------------------------- // Returns the priority of the operation. A higher value means // more priority. // --------------------------------------------------------------- int16_t IntegerCalculator::getPriority(const std::string& a_operation) const { std::map::const_iterator iter = m_OperationList.find(a_operation); if (iter != m_OperationList.end()) return iter->second; return -1; } // --------------------------------------------------------------- // replaces in a_haystack all a_searchText with a_replaceText // (not greedy). // --------------------------------------------------------------- void IntegerCalculator::replace(std::string& a_haystack, const std::string &a_searchText, const std::string &a_replaceText) { std::string::size_type searchSize = a_searchText.length(); std::string::size_type replaceSize = a_replaceText.length(); std::string::size_type pos = a_haystack.find(a_searchText, 0); while (std::string::npos != pos) { a_haystack.replace(pos, searchSize, a_replaceText); pos += replaceSize; pos = a_haystack.find(a_searchText, pos); } } } // namespace N_SST