Files
calculator-challenge/CalculatorCli.cpp
T
2024-03-15 00:53:14 +00:00

132 lines
3.6 KiB
C++

#include "CalculatorCli.hpp"
#include <fmt/format.h>
#include <cassert>
#include <iostream>
#include <string_view>
#include <type_traits>
#include "Operand.hpp"
#include "utils.hpp"
namespace {
constexpr std::string_view kExit{"exit"};
constexpr std::string_view kUndo{"undo"};
constexpr std::string_view kResult{"result"};
constexpr auto kSeparator{' '};
constexpr auto kEqualOperator{'='};
struct OperationMembers
{
std::string_view result;
std::string_view lhs;
std::string_view rhs;
std::string_view operation;
};
template<typename T>
auto text_to_number(std::string_view text) noexcept -> T
{
if (text.empty()) return T{};
if constexpr (std::is_integral_v<T>) {
return std::stoll(text.data());
}
if constexpr (std::is_floating_point_v<T>) {
return T{0}; // TODO: @JC check how to do doubles
}
return T{};
}
auto parse_operation_members(std::string_view text) noexcept -> std::optional<OperationMembers>
{
OperationMembers result;
auto result_sep = text.find_first_of(kEqualOperator);
if (result_sep == std::string::npos) return {};
result.result = sanitize_text(text.substr(0, result_sep));
auto operation_text = trim_whitespaces(text.substr(result_sep + 1));
auto left_sep = operation_text.find_first_of(kSeparator);
if (left_sep == std::string::npos) return {};
result.lhs = operation_text.substr(0, left_sep);
auto right_side = operation_text.substr(left_sep + 1);
auto right_sep = right_side.find_first_of(kSeparator);
result.operation = right_side.substr(0, right_sep);
result.rhs = sanitize_text(right_side.substr(right_sep + 1));
return result;
}
auto parse_operand(std::string_view text) noexcept -> Operand
{
assert(!text.empty());
if (!std::isdigit(text.front())) { // NOLINT
return VariableOperand{std::string{text}};
}
auto operand{static_cast<double>(text_to_number<std::int64_t>(text))};
return IntegerOperand{operand};
}
} // namespace
auto CalculatorCli::parse(std::string_view text) noexcept -> std::string
{
auto sanitized_text = trim_whitespaces(text);
if (sanitized_text.empty()) return {};
if (sanitized_text == kUndo) {
_calculator.undoOperation(1);
return {};
}
if (sanitized_text.starts_with(kUndo)) {
auto left_sep = sanitized_text.find_first_of(kSeparator);
_calculator.undoOperation(text_to_number<std::int64_t>(text.substr(left_sep)));
return {};
}
if (sanitized_text.starts_with(kResult)) {
auto [variable, value] = _calculator.computeLast();
if (!value.has_value()) return "N/A";
return fmt::format("{} = {}", variable, value.value());
}
auto components = parse_operation_members(sanitized_text);
if (!components.has_value()) return {};
auto operation = _operation_factory.make(components->operation);
if (!operation.has_value()) return {};
_calculator.addOperation(
std::string{components->result},
parse_operand(components->lhs),
parse_operand(components->rhs),
operation.value());
return fmt::format("{} = {} {} {}", components->result, components->lhs, components->operation, components->rhs);
}
void CalculatorCli::runLoop() noexcept
{
std::string line;
while (true) {
std::cout << "> ";
std::getline(std::cin, line);
if (std::cin.fail()) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
if (line == kExit) return;
auto result = parse(line);
std::cout << result << std::endl;
}
}