| #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 |