Files
qt5-legatus-legacy/TetraIF/LogView/IntegerCalculator.cpp
T
2026-08-13 18:33:10 +02:00

498 lines
14 KiB
C++

// **************************************************************************
//
// (C) Copyright 2009 Swiss SIMTEC AG
//
// **************************************************************************
//
// $HeadURL$
// $LastChangedBy$
// $LastChangedDate$
// $LastChangedRevision$
//
// **************************************************************************
// manual class headers
#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)
{
}
// ---------------------------------------------------------------
// 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<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));
}
// ---------------------------------------------------------------
// Removes a case-sensitive variable from the calculator.
// ---------------------------------------------------------------
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);
}
// ---------------------------------------------------------------
// 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<valueText.length(); i++)
{
if (!isDigit(valueText[i], isHex, isBin))
{
break;
}
}
// check the next char, it must be a space
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)
{
// 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<std::string, int>::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<std::string, int>::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