blob: cc54e4b414f17895fe6bc4b87c1062b50b823083 [file]
#include "tlbmc/configs/expression.h"
#include <cstddef>
#include <cstdint>
#include <limits>
#include <string>
#include <string_view>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_replace.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "g3/macros.h"
namespace milotic_tlbmc {
constexpr absl::string_view kSupportedOperators = "+-*/";
absl::StatusOr<std::string> EvaluateStrExpressionSubstitution(
absl::string_view expression, absl::string_view sub_key,
absl::string_view sub_value) {
size_t start = expression.find(sub_key);
std::string expression_subbed =
absl::StrReplaceAll(expression, {{sub_key, sub_value}}).substr(start);
ECCLESIA_ASSIGN_OR_RETURN(std::string result,
EvaluateStrExpression(expression_subbed));
return absl::StrCat(expression.substr(0, start), result);
}
bool IsSupportedOperator(absl::string_view op) {
if (op.size() != 1) {
return false;
}
return absl::StrContains(kSupportedOperators, op[0]);
}
absl::StatusOr<std::string> EvaluateStrExpression(
absl::string_view expression) {
std::vector<absl::string_view> tokens =
absl::StrSplit(expression, ' ', absl::SkipEmpty());
if (tokens.empty()) {
return absl::InvalidArgumentError(
"Invalid expression: Expression is empty");
}
std::string remainder;
int result;
if (!absl::SimpleAtoi(tokens[0], &result)) {
// The substituted expression is not an integer expression, return as is.
return absl::StrCat(expression);
}
int64_t current_result = result;
for (std::vector<absl::string_view>::iterator it = tokens.begin() + 1;
it != tokens.end(); it += 2) {
// If the current token is not a valid expression token, reached the end of
// the expression, return the remainder.
absl::string_view op_str = *it;
if (!IsSupportedOperator(op_str)) {
remainder = absl::StrJoin(it, tokens.end(), " ");
break;
}
char op = op_str[0];
int operand_int = 0;
if (it + 1 >= tokens.end() || !absl::SimpleAtoi(*(it + 1), &operand_int)) {
return absl::InvalidArgumentError(
absl::StrCat("Invalid expression: Illformed expression: ", expression,
" hanging operator: ", op_str));
}
int64_t operand = operand_int;
int64_t temp_result;
bool overflow = false;
switch (op) {
case '+':
overflow =
__builtin_add_overflow(current_result, operand, &temp_result);
break;
case '-':
overflow =
__builtin_sub_overflow(current_result, operand, &temp_result);
break;
case '*':
overflow =
__builtin_mul_overflow(current_result, operand, &temp_result);
break;
case '/':
if (operand == 0) {
return absl::InvalidArgumentError(absl::StrCat(
"Invalid expression: Division by zero: ", expression));
}
if (current_result == std::numeric_limits<int64_t>::min() &&
operand == -1) {
overflow = true;
} else {
temp_result = current_result / operand;
}
break;
default:
return absl::InvalidArgumentError(absl::StrCat(
"Invalid expression: Illformed expression: ", expression,
" invalid operator: ", op_str));
}
if (overflow) {
return absl::InvalidArgumentError(absl::StrCat(
"Integer overflow in expression. Input operator: ", op_str));
}
current_result = temp_result;
}
if (current_result > std::numeric_limits<int>::max() ||
current_result < std::numeric_limits<int>::min()) {
return absl::InvalidArgumentError("Integer overflow in expression");
}
return remainder.empty() ? absl::StrCat(current_result)
: absl::StrCat(current_result, " ", remainder);
}
} // namespace milotic_tlbmc