#include "CalculatorCli.hpp" #include #include #include #include #include #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 auto text_to_number(std::string_view text) noexcept -> T { if (text.empty()) return T{}; if constexpr (std::is_integral_v) { return std::stoll(text.data()); } if constexpr (std::is_floating_point_v) { return T{0}; // TODO: @JC check how to do doubles } return T{}; } auto parse_operation_members(std::string_view text) noexcept -> std::optional { 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(text_to_number(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(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::max(), '\n'); } if (line == kExit) return; auto result = parse(line); std::cout << result << std::endl; } }