feat: add challenge code

This commit is contained in:
2024-03-13 01:55:14 +00:00
commit 5634d1bb39
22 changed files with 925 additions and 0 deletions
+156
View File
@@ -0,0 +1,156 @@
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignConsecutiveMacros: AcrossEmptyLines
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: true
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: false
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: true
BreakBeforeInheritanceComma: false
BreakInheritanceList: AfterColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: AfterColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DeriveLineEnding: true
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
EmptyLineBeforeAccessModifier: LogicalBlock
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^<ext/.*\.h>'
Priority: 2
- Regex: '^<.*\.h>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
IndentRequires: false
IndentWrappedFunctionNames: false
InsertNewlineAtEOF: true
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LineEnding: LF
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Never
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
RawStringFormats:
- Language: Cpp
Delimiters:
- cc
- CC
- cpp
- Cpp
- CPP
- 'c++'
- 'C++'
CanonicalDelimiter: ''
BasedOnStyle: google
- Language: TextProto
Delimiters:
- pb
- PB
- proto
- PROTO
EnclosingFunctions:
- EqualsProto
- EquivToProto
- PARSE_PARTIAL_TEXT_PROTO
- PARSE_TEST_PROTO
- PARSE_TEXT_PROTO
- ParseTextOrDie
- ParseTextProtoOrDie
CanonicalDelimiter: ''
BasedOnStyle: google
ReferenceAlignment: Left
ReflowComments: true
ShortNamespaceLines: 0
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceBeforeCaseColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 2
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Latest
TabWidth: 4
UseTab: Never
...
+44
View File
@@ -0,0 +1,44 @@
---
Checks: '*,-modernize-use-nodiscard'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: file
CheckOptions:
- key: bugprone-use-after-move
value: '1'
- key: bugprone-assert-side-effect.AssertMacros
value: assert
- key: bugprone-suspicious-enum-usage.StrictMode
value: '0'
- key: bugprone-unused-return-value.CheckedFunctions
value: '::std::async;::std::launder;::std::remove;::std::remove_if;::std::unique;::std::unique_ptr::release;::std::basic_string::empty;::std::vector::empty'
- key: cert-dcl59-cpp.HeaderFileExtensions
value: ',h,hh,hpp,hxx'
- key: cert-err09-cpp.CheckThrowTemporaries
value: '1'
- key: cert-err61-cpp.CheckThrowTemporaries
value: '1'
- key: cert-msc32-c.DisallowedSeedTypes
value: 'time_t,std::time_t'
- key: cert-msc51-cpp.DisallowedSeedTypes
value: 'time_t,std::time_t'
- key: cert-oop11-cpp.IncludeStyle
value: llvm
- key: cppcoreguidelines-no-malloc.Allocations
value: '::malloc;::calloc'
- key: cppcoreguidelines-no-malloc.Deallocations
value: '::free'
- key: cppcoreguidelines-no-malloc.Reallocations
value: '::realloc'
- key: cppcoreguidelines-pro-type-member-init.IgnoreArrays
value: '0'
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
value: '0'
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
value: '0'
- key: hicpp-special-member-functions.AllowMissingMoveFunctions
value: '0'
- key: hicpp-special-member-functions.AllowSoleDefaultDtor
value: '0'
...
+2
View File
@@ -0,0 +1,2 @@
CompileFlags:
Add: [-std=c++20]
+2
View File
@@ -0,0 +1,2 @@
.cache/**
build/**
+10
View File
@@ -0,0 +1,10 @@
{
"configurations": [
{
"compileCommands": "${workspaceFolder}/build/debug/compile_commands.json",
"cppStandard": "c++23"
}
],
"version": 4
}
+17
View File
@@ -0,0 +1,17 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug",
"program": "${workspaceFolder}/build/calculator-challege",
"args": [],
"cwd": "${workspaceFolder}",
"preLaunchTask": "CMake: build"
}
]
}
+12
View File
@@ -0,0 +1,12 @@
{
"testMate.cpp.test.advancedExecutables": [
{
"pattern": "${workspaceFolder}/build/*-tests",
"runTask": {
"before": [
"CMake: build"
]
}
}
],
}
+16
View File
@@ -0,0 +1,16 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "cmake",
"label": "CMake: build",
"command": "build",
"targets": [
"all"
],
"group": "build",
"problemMatcher": [],
"detail": "CMake template build task"
}
]
}
+32
View File
@@ -0,0 +1,32 @@
#ifndef CALCULATOR_CHALLENGE_BINARYOPERATORFACTORY_HPP
#define CALCULATOR_CHALLENGE_BINARYOPERATORFACTORY_HPP
#include <optional>
#include <string>
#include <string_view>
#include <unordered_map>
#include "Operand.hpp"
template<typename T>
class BinaryOperatorFactory
{
using Operator = BinaryOperatorType<T>;
public:
BinaryOperatorFactory() noexcept;
BinaryOperatorFactory(const BinaryOperatorFactory&) noexcept = delete;
BinaryOperatorFactory(BinaryOperatorFactory&&) noexcept = delete;
auto operator=(const BinaryOperatorFactory&) noexcept -> BinaryOperatorFactory& = delete;
auto operator=(BinaryOperatorFactory&&) noexcept -> BinaryOperatorFactory& = delete;
~BinaryOperatorFactory() noexcept = default;
auto make(std::string_view text) noexcept -> std::optional<Operator>;
private:
std::unordered_map<std::string, Operator> _binary_operations;
};
#include "BinaryOperatorFactory.tcc"
#endif
+20
View File
@@ -0,0 +1,20 @@
#include "BinaryOperatorFactory.hpp"
#include "utils.hpp"
template<typename T>
BinaryOperatorFactory<T>::BinaryOperatorFactory() noexcept
{
_binary_operations["+"] = [](auto lhs, auto rhs) { return lhs + rhs; };
_binary_operations["-"] = [](auto lhs, auto rhs) { return lhs - rhs; };
_binary_operations["*"] = [](auto lhs, auto rhs) { return lhs * rhs; };
_binary_operations["/"] = [](auto lhs, auto rhs) { return lhs / rhs; };
}
template<typename T>
auto BinaryOperatorFactory<T>::make(std::string_view text) noexcept -> std::optional<Operator>
{
auto found_op = _binary_operations.find(std::string{sanitize_text(text)});
if (found_op == _binary_operations.end()) return std::nullopt;
return found_op->second;
}
+30
View File
@@ -0,0 +1,30 @@
#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>
#include "BinaryOperatorFactory.hpp"
TEMPLATE_TEST_CASE("BinaryOperatorFactory", "[calculator-challenge]", std::int64_t, double)
{
constexpr TestType kLeftOperand{20};
constexpr TestType kRightOperand{2};
BinaryOperatorFactory<TestType> factory;
auto check_factory = [&](std::string_view str, TestType result) {
auto operation = factory.make(str);
if (!operation.has_value()) {
return false;
}
return (*operation)(kLeftOperand, kRightOperand) == result;
};
CHECK(check_factory("+", kLeftOperand + kRightOperand));
CHECK(check_factory(" + ", kLeftOperand + kRightOperand));
CHECK(check_factory(" +", kLeftOperand + kRightOperand));
CHECK(check_factory("+ ", kLeftOperand + kRightOperand));
CHECK(check_factory("+ a", kLeftOperand + kRightOperand));
CHECK(check_factory("-", kLeftOperand - kRightOperand));
CHECK(check_factory("*", kLeftOperand * kRightOperand));
CHECK(check_factory("/", kLeftOperand / kRightOperand));
}
+53
View File
@@ -0,0 +1,53 @@
cmake_minimum_required(VERSION 3.27)
project(CalculatorChallenge CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_EXTENSIONS ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
INCLUDE(FetchContent)
FetchContent_Declare(
fmtformat
GIT_REPOSITORY https://github.com/fmtlib/fmt
GIT_TAG 10.2.1
)
FetchContent_MakeAvailable(fmtformat)
FetchContent_Declare(
Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.5.3
)
FetchContent_MakeAvailable(Catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
set(CMAKE_C_COMPILER /opt/homebrew/Cellar/llvm@16/16.0.6/bin/clang)
set(CMAKE_CXX_COMPILER /opt/homebrew/Cellar/llvm@16/16.0.6/bin/clang++)
SET(CALCULATOR_CHALLENGE_SOURCES
Calculator.cpp
CalculatorCli.cpp
utils.cpp
)
add_executable(calculator-challege
main.cpp
${CALCULATOR_CHALLENGE_SOURCES}
)
target_link_libraries(calculator-challege PRIVATE fmt::fmt)
add_executable(calculator-challege-tests
CalculatorTests.cpp
BinaryOperatorFactoryTests.cpp
CalculatorCliTests.cpp
${CALCULATOR_CHALLENGE_SOURCES}
)
target_link_libraries(calculator-challege-tests PRIVATE Catch2::Catch2WithMain fmt::fmt)
catch_discover_tests(calculator-challege-tests)
+85
View File
@@ -0,0 +1,85 @@
#include "Calculator.hpp"
#include <ranges>
#include <unordered_map>
#include "Operand.hpp"
namespace {
using Workspace = std::unordered_map<std::string, double>;
class Visitor;
auto compute(Visitor& visitor, const Operation& binary_operation) noexcept -> std::optional<double>;
class Visitor
{
public:
Visitor(std::span<const Operation> operations, Workspace& variables) noexcept :
_operations{operations}, _variables{variables}
{}
auto operator()(const IntegerOperand& operand) noexcept -> std::optional<double> { return operand.get(); }
auto operator()(const VariableOperand& operand) noexcept -> std::optional<double>
{
auto name = operand.get();
auto found_variable = _variables.get().find(name);
if (found_variable != _variables.get().end()) return found_variable->second;
auto found_operation = std::ranges::find(_operations, name, &Operation::variable);
if (found_operation == _operations.end()) return std::nullopt;
return compute(*this, *found_operation);
}
private:
std::span<const Operation> _operations;
std::reference_wrapper<Workspace> _variables;
};
auto compute(Visitor& visitor, const Operation& operation) noexcept -> std::optional<double>
{
auto lhs = std::visit(visitor, operation.lhs);
if (!lhs.has_value()) return std::nullopt;
auto rhs = std::visit(visitor, operation.rhs);
if (!rhs.has_value()) return std::nullopt;
return operation.binary_op(lhs.value(), rhs.value());
}
} // namespace
void Calculator::addOperation(Operation operation) noexcept
{
_operations.push_back(std::move(operation));
}
void Calculator::addOperation(std::string result, Operand lhs, Operand rhs, Operator binary_operator) noexcept
{
addOperation(Operation{std::move(result), std::move(lhs), std::move(rhs), std::move(binary_operator)});
}
void Calculator::undoOperation(std::size_t count) noexcept
{
count = std::min(_operations.size(), count);
_operations.erase(_operations.end() - static_cast<std::int64_t>(count), _operations.end());
}
auto Calculator::computeLast() noexcept -> std::pair<std::string, std::optional<double>>
{
Workspace workspace;
Visitor visitor{_operations, workspace};
for (auto& operation : std::views::reverse(_operations)) {
auto result = compute(visitor, operation);
if (result.has_value()) return std::make_pair(operation.variable, result.value());
}
return std::make_pair("na", std::nullopt);
}
auto Calculator::computeLastValue() noexcept -> double
{
auto [_, result] = computeLast();
return result.value_or(0.0);
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef CALCULATOR_CHALLENGE_WORKSPACE_HPP
#define CALCULATOR_CHALLENGE_WORKSPACE_HPP
#include <cstdint>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
#include "Operand.hpp"
struct Operation
{
std::string variable;
Operand lhs;
Operand rhs;
Operator binary_op;
};
class Calculator
{
public:
Calculator() noexcept = default;
Calculator(const Calculator&) noexcept = default;
Calculator(Calculator&&) noexcept = default;
auto operator=(const Calculator&) noexcept -> Calculator& = default;
auto operator=(Calculator&&) noexcept -> Calculator& = default;
~Calculator() noexcept = default;
void addOperation(Operation operation) noexcept;
void addOperation(std::string result, Operand lhs, Operand rhs, Operator binary_operator) noexcept;
void undoOperation(std::size_t count) noexcept;
auto computeLast() noexcept -> std::pair<std::string, std::optional<double>>;
auto computeLastValue() noexcept -> double;
private:
std::vector<Operation> _operations;
};
#endif
+131
View File
@@ -0,0 +1,131 @@
#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
{
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;
}
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef CALCULATOR_CHALLENGE_CALCULATORCLI_HPP
#define CALCULATOR_CHALLENGE_CALCULATORCLI_HPP
#include "BinaryOperatorFactory.hpp"
#include "Calculator.hpp"
class CalculatorCli
{
public:
CalculatorCli() noexcept = default;
CalculatorCli(const CalculatorCli&) noexcept = delete;
CalculatorCli(CalculatorCli&&) noexcept = delete;
auto operator=(const CalculatorCli&) noexcept -> CalculatorCli& = delete;
auto operator=(CalculatorCli&&) noexcept -> CalculatorCli& = delete;
~CalculatorCli() noexcept = default;
auto parse(std::string_view text) noexcept -> std::string;
void runLoop() noexcept;
private:
Calculator _calculator;
BinaryOperatorFactory<double> _operation_factory;
};
#endif
+80
View File
@@ -0,0 +1,80 @@
#include <catch2/catch_test_macros.hpp>
#include "CalculatorCli.hpp"
TEST_CASE("CalculatorCli - can parse operations", "[calculator-challenge]")
{
CalculatorCli calculator;
calculator.parse("a = 4 + 1");
CHECK(calculator.parse("result") == "a = 5");
calculator.parse(" a = 4 + 1");
CHECK(calculator.parse("result") == "a = 5");
calculator.parse("a = 4 + 1 ");
CHECK(calculator.parse("result") == "a = 5");
calculator.parse("a = 4 + 1");
CHECK(calculator.parse("result") == "a = 5");
}
TEST_CASE("CalculatorCli - edge cases", "[calculator-challenge]")
{
CalculatorCli calculator;
calculator.parse("a=31+2");
CHECK_FALSE(calculator.parse("result") == "a = 33"); // TODO: @JC do not require spaces
}
TEST_CASE("CalculatorCli - can undo operations", "[calculator-challenge]")
{
CalculatorCli calculator;
calculator.parse("a = 4 + 1");
CHECK(calculator.parse("result") == "a = 5");
calculator.parse(" a = 6 + 1");
CHECK(calculator.parse("result") == "a = 7");
SECTION("simple command")
{
calculator.parse("undo");
CHECK(calculator.parse("result") == "a = 5");
}
SECTION("two undos")
{
calculator.parse("undo");
CHECK(calculator.parse("result") == "a = 5");
calculator.parse("undo");
CHECK(calculator.parse("result") == "N/A");
}
SECTION("with space")
{
calculator.parse("undo ");
CHECK(calculator.parse("result") == "a = 5");
}
SECTION("with zero")
{
calculator.parse("undo 0");
CHECK(calculator.parse("result") == "a = 7");
}
}
TEST_CASE("CalculatorCli - can undo multiple operations", "[calculator-challenge]")
{
CalculatorCli calculator;
calculator.parse("a = 4 + 1");
CHECK(calculator.parse("result") == "a = 5");
calculator.parse(" a = 6 + 1");
CHECK(calculator.parse("result") == "a = 7");
calculator.parse("b = 6 + 1");
CHECK(calculator.parse("result") == "b = 7");
calculator.parse("undo 2");
CHECK(calculator.parse("result") == "a = 5");
}
+80
View File
@@ -0,0 +1,80 @@
#include <catch2/catch_test_macros.hpp>
#include "Calculator.hpp"
#include "Operand.hpp"
constexpr auto addiction = [](auto lhs, auto rhs) { return lhs + rhs; };
constexpr auto subtraction = [](auto lhs, auto rhs) { return lhs - rhs; };
TEST_CASE("Calculator - can add operations", "[calculator-challenge]")
{
Calculator calculator;
calculator.addOperation("a", IntegerOperand{5}, IntegerOperand{10}, addiction);
CHECK(calculator.computeLastValue() == 15.0);
calculator.addOperation("b", IntegerOperand{1}, IntegerOperand{5}, addiction);
CHECK(calculator.computeLastValue() == 6.0);
calculator.addOperation("c", IntegerOperand{1}, IntegerOperand{5}, subtraction);
CHECK(calculator.computeLastValue() == -4.0);
}
TEST_CASE("Calculator - can undo operations", "[calculator-challenge]")
{
Calculator calculator;
calculator.addOperation("a", IntegerOperand{5}, IntegerOperand{10}, addiction);
CHECK(calculator.computeLastValue() == 15.0);
calculator.addOperation("b", IntegerOperand{1}, IntegerOperand{5}, subtraction);
CHECK(calculator.computeLastValue() == -4.0);
SECTION("one by one")
{
calculator.undoOperation(1);
CHECK(calculator.computeLastValue() == 15.0);
calculator.undoOperation(1);
CHECK(calculator.computeLastValue() == 0.0);
}
SECTION("all")
{
calculator.undoOperation(2);
CHECK(calculator.computeLastValue() == 0.0);
}
SECTION("more than was added")
{
calculator.undoOperation(50);
CHECK(calculator.computeLastValue() == 0.0);
}
}
TEST_CASE("Calculator - can add operations with variables", "[calculator-challenge]")
{
Calculator calculator;
calculator.addOperation("a", IntegerOperand{1}, IntegerOperand{2}, addiction);
CHECK(calculator.computeLastValue() == 3);
calculator.addOperation("b", VariableOperand{"a"}, IntegerOperand{10}, addiction);
CHECK(calculator.computeLastValue() == 13);
calculator.addOperation("b", VariableOperand{"a"}, IntegerOperand{2}, addiction);
CHECK(calculator.computeLastValue() == 5);
}
TEST_CASE("Calculator - if a variable is missing the last available is returned", "[calculator-challenge]")
{
Calculator calculator;
calculator.addOperation("a", VariableOperand{"c"}, IntegerOperand{10}, addiction);
CHECK(calculator.computeLastValue() == 0.0);
calculator.addOperation("a", IntegerOperand{1}, IntegerOperand{2}, addiction);
CHECK(calculator.computeLastValue() == 3);
calculator.addOperation("a", VariableOperand{"c"}, IntegerOperand{10}, addiction);
CHECK(calculator.computeLastValue() == 3.0);
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef CALCULATOR_CHALLENGE_OPERAND_HPP
#define CALCULATOR_CHALLENGE_OPERAND_HPP
#include <functional>
#include <string>
#include <variant>
template<typename T>
using BinaryOperatorType = std::function<T(T, T)>;
using Operator = BinaryOperatorType<double>;
class IntegerOperand
{
public:
explicit IntegerOperand(double value) noexcept : _value{value} {}
auto get() const noexcept -> double { return _value; };
private:
double _value{0};
};
class VariableOperand
{
public:
explicit VariableOperand(std::string name) noexcept : _name{std::move(name)} {}
auto get() const noexcept -> std::string { return _name; };
private:
std::string _name;
};
using Operand = std::variant<IntegerOperand, VariableOperand>;
#endif
+10
View File
@@ -0,0 +1,10 @@
#include <fmt/format.h>
#include "CalculatorCli.hpp"
auto main() -> int
{
CalculatorCli cli;
cli.runLoop();
return 0;
}
+33
View File
@@ -0,0 +1,33 @@
#include "utils.hpp"
#include <string>
namespace {
constexpr char kSpace{' '};
} // namespace
auto trim_whitespaces(std::string_view text) noexcept -> std::string_view
{
auto leading_spaces = text.find_first_not_of(kSpace);
if (leading_spaces == std::string::npos) return {};
auto sub_view = text.substr(leading_spaces);
auto trailling_spaces = sub_view.find_last_not_of(kSpace);
if (trailling_spaces == std::string::npos) return sub_view;
return sub_view.substr(0, trailling_spaces + 1);
}
auto sanitize_text(std::string_view text) noexcept -> std::string_view
{
auto leading_spaces = text.find_first_not_of(kSpace);
if (leading_spaces == std::string::npos) return {};
auto sub_view = text.substr(leading_spaces);
auto trailling_spaces = sub_view.find_first_of(kSpace);
if (trailling_spaces == std::string::npos) return sub_view;
return sub_view.substr(0, trailling_spaces);
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef CALCULATOR_CHALLENGE_UTILS_HPP
#define CALCULATOR_CHALLENGE_UTILS_HPP
#include <string_view>
auto trim_whitespaces(std::string_view text) noexcept -> std::string_view;
auto sanitize_text(std::string_view text) noexcept -> std::string_view;
#endif