498 lines
9.4 KiB
C++
498 lines
9.4 KiB
C++
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "IntegerCalculator.hpp"
|
|
|
|
#include <sstream>
|
|
#include <iomanip>
|
|
|
|
namespace N_SST
|
|
{
|
|
IntegerCalculator::IntegerCalculator()
|
|
: m_OperationList({{"*", 6},{"/", 6},{"%", 6},{"+", 5},{"-", 5},{"<<", 4},{">>", 4}})
|
|
, m_AdditionalErrorText()
|
|
, m_ParanthesisCount(0)
|
|
, m_Error(IntegerCalculator::e_Ok)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int64_t IntegerCalculator::calc(const std::string& a_calcText, E_Error& a_error, std::string* a_addErrorText)
|
|
{
|
|
std::string calcText = a_calcText;
|
|
|
|
for (auto iter = m_OperationList.begin(); iter != m_OperationList.end(); ++iter)
|
|
replace(calcText, iter->first, " "+iter->first+" ");
|
|
|
|
|
|
replace(calcText, "(", " ( ");
|
|
replace(calcText, ")", " ) ");
|
|
|
|
|
|
|
|
m_ParanthesisCount = 0;
|
|
m_Error = e_Ok;
|
|
m_AdditionalErrorText = "";
|
|
size_t pos = 0;
|
|
bool dummy;
|
|
int64_t value = calc(calcText, pos, dummy, "", 0, "+");
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
a_error = m_Error;
|
|
if (a_error != e_Ok)
|
|
{
|
|
value = 0;
|
|
if (a_addErrorText)
|
|
{
|
|
*a_addErrorText = m_AdditionalErrorText;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
|
|
|
|
|
|
void IntegerCalculator::addVariable(const std::string& a_name, const int64_t a_value)
|
|
{
|
|
std::map<std::string, int64_t>::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));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void IntegerCalculator::removeVariable(const std::string& a_name)
|
|
{
|
|
std::map<std::string, int64_t>::iterator iter = m_VariableList.find(a_name);
|
|
if (iter != m_VariableList.end())
|
|
m_VariableList.erase(iter);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
size_t tmpPos = a_pos;
|
|
std::string nextOp = getToken(a_calcText, tmpPos);
|
|
if (isOperation(nextOp) && isOperation(a_prevOp) && !isOperationPriorityHigher(nextOp, a_prevOp))
|
|
{
|
|
|
|
return value1;
|
|
}
|
|
|
|
operation = "";
|
|
}
|
|
else if (')' == calcText[a_pos])
|
|
{
|
|
m_ParanthesisCount--;
|
|
a_pos++;
|
|
a_IsEndParanthesis = true;
|
|
return value1;
|
|
}
|
|
else if (isDigit(calcText[a_pos]))
|
|
{
|
|
|
|
|
|
int64_t value2 = calcSign(convertValue(calcText, a_pos), isNegative);
|
|
if (calcWithPriority(calcText, a_pos, value1, operation, value2, a_prevOp)) return value1;
|
|
}
|
|
else
|
|
{
|
|
|
|
|
|
std::string oldOperation = operation;
|
|
operation = getToken(calcText, a_pos);
|
|
|
|
|
|
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
|
|
{
|
|
|
|
m_Error = e_UnknownOperationOrVariable;
|
|
m_AdditionalErrorText = operation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
size_t tmpPos = a_pos;
|
|
std::string nextOp = getToken(a_calcText, tmpPos);
|
|
if (isOperation(a_operation) && isOperation(nextOp) && isOperationPriorityHigher(nextOp, a_operation))
|
|
{
|
|
|
|
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)))
|
|
{
|
|
|
|
return true;
|
|
}
|
|
a_operation = "";
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int64_t IntegerCalculator::calcSign(const int64_t a_value, bool& a_isNegative)
|
|
{
|
|
if (a_isNegative)
|
|
{
|
|
a_isNegative = false;
|
|
return -a_value;
|
|
}
|
|
return a_value;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
size_t i;
|
|
for (i=startpos; i<valueText.length(); i++)
|
|
{
|
|
if (!isDigit(valueText[i], isHex, isBin))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (valueText.length() > 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)
|
|
{
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string IntegerCalculator::getToken(const std::string &a_calcText, size_t& a_pos)
|
|
{
|
|
|
|
while (a_pos < a_calcText.length())
|
|
{
|
|
if (a_calcText[a_pos] == ' ') a_pos++;
|
|
else break;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool IntegerCalculator::isOperation(const std::string& a_operation) const
|
|
{
|
|
std::map<std::string, int>::const_iterator iter = m_OperationList.find(a_operation);
|
|
return (iter != m_OperationList.end());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int64_t IntegerCalculator::doOperation(const int64_t a_value1, const std::string& a_operation, const int64_t 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;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool IntegerCalculator::isOperationPriorityHigher(const std::string& a_operation1, const std::string& a_operation2) const
|
|
{
|
|
return (getPriority(a_operation1) > getPriority(a_operation2));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool IntegerCalculator::isDigit(const char a_char, const bool a_isHex, const bool a_isBin)
|
|
{
|
|
if (a_isHex)
|
|
{
|
|
|
|
return (a_char >= '0' && a_char <= '9') || (a_char >= 'A' && a_char <= 'F') || (a_char >= 'a' && a_char <= 'f');
|
|
}
|
|
else if (a_isBin)
|
|
{
|
|
|
|
return (a_char >= '0' && a_char <= '1');
|
|
}
|
|
|
|
return (a_char >= '0' && a_char <= '9');
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int16_t IntegerCalculator::getPriority(const std::string& a_operation) const
|
|
{
|
|
std::map<std::string, int>::const_iterator iter = m_OperationList.find(a_operation);
|
|
if (iter != m_OperationList.end())
|
|
return iter->second;
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|