From 456cebed8981a7a2db43da884238117ca8325871 Mon Sep 17 00:00:00 2001 From: visper Date: Thu, 19 Dec 2019 11:29:59 +0200 Subject: [PATCH] initial pol calc --- CMakeLists.txt | 4 +- Calculator.cpp | 49 +++++++++ Calculator.h | 16 +++ Parser.cpp | 49 +++++++++ Parser.h | 20 ++++ Stack.cpp | 45 +++++++++ Stack.h | 22 ++++ Token.cpp | 40 ++++++++ Token.h | 36 +++++++ executor.cpp | 16 +++ executor.h | 9 ++ main.cc | 63 +++++++++++- project.cc | 21 ++++ project.h | 1 + test/project_test.cc | 233 ++++++++++++++++++++++++++++++++++++++++++- 15 files changed, 618 insertions(+), 6 deletions(-) create mode 100644 Calculator.cpp create mode 100644 Calculator.h create mode 100644 Parser.cpp create mode 100644 Parser.h create mode 100644 Stack.cpp create mode 100644 Stack.h create mode 100644 Token.cpp create mode 100644 Token.h create mode 100644 executor.cpp create mode 100644 executor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e6318a3..04cf393 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,10 +4,10 @@ project(dummy_cmake_project) set(CMAKE_CXX_STANDARD 14) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) -set(SOURCES project.cc) +set(SOURCES project.cc Parser.cpp Token.cpp Stack.cpp Calculator.cpp executor.cpp) add_library(ProjectLib ${SOURCES}) -add_executable(project main.cc) +add_executable(project main.cc Parser.cpp Token.cpp Stack.cpp Calculator.cpp executor.cpp) target_link_libraries(project ProjectLib) enable_testing() diff --git a/Calculator.cpp b/Calculator.cpp new file mode 100644 index 0000000..9246e69 --- /dev/null +++ b/Calculator.cpp @@ -0,0 +1,49 @@ +#include "Calculator.h" +#include + +Calculator::Calculator(Stack &stk) : stk_(stk) +{} + +double Calculator::exe() { + + std::vector numbers; + double rec = 0; + for(;;) { + int rc; + Token t = stk_.pop(rc); + if(t.is_number()) { + numbers.push_back(t.getNumber()); + } else { //operation + + long double result = numbers[0]; + for(int i = 1; i < numbers.size(); ++i) { + + switch (t.getOperand()) { + case TOper::ADD: + result += numbers[i]; + break; + case TOper::SUB: + result -= numbers[i]; + break; + case TOper::MUL: + result *= numbers[i]; + break; + case TOper::DIV: + result /= numbers[i]; + break; + default: + std::cout << "exe error" << std::endl; + break; + } + } + numbers.clear(); + Token nt(true, TOper::NONE, result); + stk_.push(nt); + std::cout << result << std::endl; + rec = result; + } + + if(!rc) break; + } + return rec; +} diff --git a/Calculator.h b/Calculator.h new file mode 100644 index 0000000..1743235 --- /dev/null +++ b/Calculator.h @@ -0,0 +1,16 @@ +#ifndef CALCULATOR_H +#define CALCULATOR_H + +#include "Stack.h" + +class Calculator +{ +public: + Calculator(Stack &stk); + double exe();///TODO rename execute + +private: + Stack stk_; +}; + +#endif // CALCULATOR_H diff --git a/Parser.cpp b/Parser.cpp new file mode 100644 index 0000000..b8b0348 --- /dev/null +++ b/Parser.cpp @@ -0,0 +1,49 @@ +#include "Parser.h" + +#include //just for test +#include + +Parser::Parser(const std::vector & line) + : rawLines(line) + , currentPosition(0) +{} + +void Parser::parse() +{ + for(size_t i = 0; i < rawLines.size(); ++i) { + currentPosition = i+1; + //check for operator + ///TODO: check Factory applicance + if(rawLines[i] == std::string("+")) { + Token t(TOper::ADD); + tokens.push_back(t); + } else if (rawLines[i] == std::string("-")) { + Token t(TOper::SUB); + tokens.push_back(t); + } else if (rawLines[i] == std::string("*")) { + Token t(TOper::MUL); + tokens.push_back(t); + } else if (rawLines[i] == std::string("/")) { + Token t(TOper::DIV); + tokens.push_back(t); + } else { //check for number + long double dd; +// try { + dd = std::stod (rawLines[i]); + //try to throw position also +// } catch (...) { +// std::cerr << "wrong data on " << i+1 +// << " position" << std::endl; +// return; +// } + + Token t(dd); + tokens.push_back(t); + } + } +} + +const std::vector &Parser::getTokens() +{ + return tokens; +} diff --git a/Parser.h b/Parser.h new file mode 100644 index 0000000..92261d1 --- /dev/null +++ b/Parser.h @@ -0,0 +1,20 @@ +#ifndef PARSER_H +#define PARSER_H + +#include "Token.h" +#include + +class Parser +{ +public: + Parser(const std::vector & line); + void parse(); + const std::vector& getTokens(); + size_t getPositionOfErrorInRawLine() const; +private: + std::vector tokens; + std::vector rawLines; + size_t currentPosition; +}; + +#endif // PARSER_H diff --git a/Stack.cpp b/Stack.cpp new file mode 100644 index 0000000..1b68333 --- /dev/null +++ b/Stack.cpp @@ -0,0 +1,45 @@ +#include "Stack.h" +#include + +Stack::Stack(const std::vector& t) : size(0) /*: size(t.size()) */ //init vector +{ + //for(int i = size-1; i >= 0; --i) { + for(int i = t.size()-1; i >= 0; --i) { + push(t[i]); + } +} + +Stack::~Stack() +{ + size = 0; + stk.clear(); +} + +int Stack::getSize() const +{ + return size; +} + +void Stack::push(const Token& elem) +{ + if ( stk.size() <= size ) { // here size must be added after compate + stk.push_back(elem); + ++size; + } else + std::cout << "stack is full" << std::endl; +} + +Token Stack::pop(int &rc) +{ + rc = 1; + if ( stk.empty() ) { + std::cout << "stack is empty" << std::endl; + //return -EINVAL; + rc = 0; + } else { + Token temp = stk.back(); + stk.pop_back(); + --size; + return temp; + } +} diff --git a/Stack.h b/Stack.h new file mode 100644 index 0000000..54744bf --- /dev/null +++ b/Stack.h @@ -0,0 +1,22 @@ +#ifndef STACK_H +#define STACK_H + +#include +#include +#include "Token.h" + +///TODO:namespace +class Stack { + int size; + std::vector stk; +public: + ///TODO: stack is stack. do adapter for stack with Token + Stack(const std::vector& t); + ~Stack(); + void push(const Token& elem); + Token pop(int &rc); ///TODO: think about another one interface + int getSize() const; +}; + + +#endif // STACK_H diff --git a/Token.cpp b/Token.cpp new file mode 100644 index 0000000..826937e --- /dev/null +++ b/Token.cpp @@ -0,0 +1,40 @@ +#include "Token.h" + +Token::Token(const bool is_number, const TOper::Operand opr, const long double num) + : is_num(is_number) +{ + if(is_num) { + d = num; + this->opr = TOper::NONE; + } else { + this->opr = opr; + d = 0; //default + } +} + +Token::Token(const long double num) + : is_num(true) + , d(num) + , opr(TOper::NONE) +{} + +Token::Token(const TOper::Operand opr) + : is_num(false) + , d(0) + , opr(opr) +{} + +TOper::Operand Token::getOperand() +{ + return opr; +} + +long double Token::getNumber() +{ + return d; +} + +bool Token::is_number() +{ + return is_num; +} diff --git a/Token.h b/Token.h new file mode 100644 index 0000000..c19514b --- /dev/null +++ b/Token.h @@ -0,0 +1,36 @@ +#ifndef TOKEN_H +#define TOKEN_H + +#include + +namespace TOper { + +///TODO: rename +///TODO: enum class +enum Operand { + ADD, + SUB, + DIV, + MUL, + NONE +}; + +} + +class Token +{ +public: + ///TODO: split to different constructors + Token(const bool is_number, const TOper::Operand opr, const long double num); + Token(const long double num); + Token(const TOper::Operand opr); + bool is_number(); + TOper::Operand getOperand(); + long double getNumber(); +private: + long double d; + TOper::Operand opr; + bool is_num; +}; + +#endif // TOKEN_H diff --git a/executor.cpp b/executor.cpp new file mode 100644 index 0000000..f0b7a83 --- /dev/null +++ b/executor.cpp @@ -0,0 +1,16 @@ +#include "executor.h" + +#include "Parser.h" +#include "Stack.h" +#include "Calculator.h" +#include "executor.h" + +double execute(const std::vector & src) +{ + Parser p(src); + p.parse(); + Stack stk(p.getTokens()); + //std::cout << "test" << std::endl; + Calculator calc(stk); + return calc.exe(); +} diff --git a/executor.h b/executor.h new file mode 100644 index 0000000..82b1f70 --- /dev/null +++ b/executor.h @@ -0,0 +1,9 @@ +#ifndef EXECUTOR_H +#define EXECUTOR_H + +#include +#include +///TODO: namespace wrrap +double execute(const std::vector & src); + +#endif // EXECUTOR_H diff --git a/main.cc b/main.cc index 5b2357f..73c8621 100644 --- a/main.cc +++ b/main.cc @@ -1,5 +1,64 @@ #include "project.h" -int main() { - return 0; +#include "executor.h" + +#include +#include +#include +using namespace std; // test + +/* + * The calculator input is double numbers, operators + - * / + * The maximum input line is 255 + * + * */ + +static int show_usage() +{ + std::cerr << "Usage: || " + << "Options:\n" + << "\t-h,--help\t\tShow this help message\n" + << "Valid arguments:\n" + << "\tSpecify target string after program name\n" + << "\tNumbers with point and operations + - / * with space separation" + << std::endl; + return -1; +} + +int main(int argc, char** argv) { + + try { + if (argc < 2 /*|| argc >= 12*/) { + show_usage(); + return 1; + } + + std::vector sources; + for (int i = 1; i < argc; ++i) { + std::string arg = argv[i]; + if ((arg == "-h") || (arg == "--help")) { + show_usage(); + return 0; + } else { + sources.push_back(arg); + } + } + + execute(sources); + } catch (const std::invalid_argument& e) { + std::cout << "std::invalid_argument" << std::endl; + show_usage(); + } catch (const std::out_of_range& e) { + std::cout << "std::out_of_range" << std::endl; + show_usage(); + } catch (...) { + /* +If no conversion could be performed, an invalid_argument exception is thrown. +If the value read is out of the range of representable values by a double (in some library implementations, this includes underflows), an out_of_range exception is thrown. + */ + + show_usage(); + } + + return 0; } diff --git a/project.cc b/project.cc index fdc1225..38b4fbf 100644 --- a/project.cc +++ b/project.cc @@ -1,8 +1,29 @@ #include "project.h" +#include "Stack.h" +#include "Token.h" +#include "Calculator.h" +#include "Parser.h" + +#include namespace dev { +void Project::stackTest() +{ + /* + std::cout << "stack" << std::endl; + std::vector tk; + tk.push_back(Token(true, TOper::NONE, 3)); + tk.push_back(Token(true, TOper::NONE, 2)); + tk.push_back(Token(true, TOper::PLUS, 0)); + Stack stk(tk); + std::cout << "test" << std::endl; + */ +} + int Project::run() { + stackTest(); + std::cout << "rrrrrrrrun" << std::endl; return 0; } } // namespace dev diff --git a/project.h b/project.h index b5cc813..b94c803 100644 --- a/project.h +++ b/project.h @@ -7,5 +7,6 @@ class Project : public IProject { // IProject interface public: int run(); + void stackTest(); }; } // namespace dev diff --git a/test/project_test.cc b/test/project_test.cc index 5dbeb2f..6f4b77f 100644 --- a/test/project_test.cc +++ b/test/project_test.cc @@ -1,19 +1,248 @@ #include "project.h" #include #include + +#include + +#include "Token.h" +#include "Stack.h" +#include "Parser.h" +#include "executor.h" + namespace dev { namespace testing { +/* + * List of tests: + * -ввод одной цифры + * -ввод однонго символа + * -ввод одной операции + * -ввод неправильной последовательности + 2 3 + * -ввод одних символов + * -ввод дробных чисел + * -ряд тестов с простыми операциями + - * / + * -ввод отрицательных чисел + * -такая комбинация чисел при которой получается отрицательный результат + * -сложное вырежение. 15 7 1 1 + - / 3 \* 2 1 1 + + - + * -пустая строка + * -нет операции(одни числа) + * -деление на 0 + * -лишнее операнды + * -числа overflow + * -попробовать вызвать exec с вызовом программы с параметрами. + * + * 5 2 \* 2 / 6.5 - + * + * вопрос. + * хороший тест вызов одной функции на одну строку + * не пишем специальных функций для тестов. + * + * */ + class ProjectTest : public ::testing::Test { public: - void SetUp() override {} - void TearDown() override {} + void SetUp() override; + void TearDown() override; dev::Project project_; }; +void ProjectTest::SetUp() +{ + std::cout << "SetUp" << std::endl; +} + +void ProjectTest::TearDown() +{ + std::cout << "TearDown" << std::endl; +} + TEST_F(ProjectTest, Run) { + + std::cout << "lalala" << std::endl; ASSERT_EQ(0, project_.run()); } +//Token test start +TEST_F(ProjectTest, TokenNumber) { + constexpr double test_number = -9.985; + Token t(test_number); + ASSERT_DOUBLE_EQ(test_number, t.getNumber()); + ASSERT_EQ(true, t.is_number()); + ASSERT_EQ(TOper::NONE, t.getOperand()); +} + +TEST_F(ProjectTest, TokenOperation) { + TOper::Operand test_operation = TOper::DIV; + Token t(test_operation); + ASSERT_EQ(0, t.getNumber()); + ASSERT_EQ(false, t.is_number()); + ASSERT_EQ(test_operation, t.getOperand()); +} + +TEST_F(ProjectTest, TokenComplexConstructor) { + constexpr double test_number = -9.985; + Token t(true, TOper::ADD, test_number); + ASSERT_DOUBLE_EQ(test_number, t.getNumber()); + ASSERT_EQ(true, t.is_number()); + ASSERT_EQ(TOper::NONE, t.getOperand()); + + Token t2(false, TOper::ADD, test_number); + ASSERT_EQ(0, t2.getNumber()); + ASSERT_EQ(false, t2.is_number()); + ASSERT_EQ(TOper::ADD, t2.getOperand()); +} +//Token test end + +//Stack test start +TEST_F(ProjectTest, Stack) { + std::vector tk; + tk.push_back(Token(true, TOper::NONE, 3)); + tk.push_back(Token(true, TOper::NONE, 2)); + tk.push_back(Token(false, TOper::ADD, 0)); + tk.push_back(Token(true, TOper::NONE, 1)); + tk.push_back(Token(false, TOper::SUB, 0)); + Stack stk(tk); + + int rc = 0; + Token t = stk.pop(rc); + ASSERT_EQ(1, rc); + ASSERT_EQ(1, t.is_number()); + ASSERT_EQ(t.getNumber(), 3); + ASSERT_EQ(4, stk.getSize()); + + t = stk.pop(rc); + ASSERT_EQ(1, rc); + ASSERT_EQ(1, t.is_number()); + ASSERT_EQ(t.getNumber(), 2); + ASSERT_EQ(stk.getSize(), 3); + + t = stk.pop(rc); + +// std::cout << "test " << t.is_number() << std::endl; +// std::cout << t.getNumber() << std::endl; +// std::cout << t.getOperand() << std::endl; + + ASSERT_EQ(1, rc); + ASSERT_EQ(0, t.is_number()); + ASSERT_EQ(t.getOperand(), TOper::ADD); + ASSERT_EQ(stk.getSize(), 2); + + t = stk.pop(rc); + ASSERT_EQ(1, rc); + ASSERT_EQ(1, t.is_number()); + ASSERT_EQ(t.getNumber(), 1); + ASSERT_EQ(stk.getSize(), 1); + + t = stk.pop(rc); + ASSERT_EQ(1, rc); + ASSERT_EQ(false, t.is_number()); + ASSERT_EQ(t.getOperand(), TOper::SUB); + ASSERT_EQ(stk.getSize(), 0); + + t = stk.pop(rc); + ASSERT_EQ(0, rc); +} +//Stack test end + +//Parser test start +TEST_F(ProjectTest, Parser) { + std::vector customLine; + customLine.push_back(std::string("638763")); + customLine.push_back(std::string("0.2121")); + customLine.push_back(std::string("-0.212")); + customLine.push_back(std::string("-8678")); + customLine.push_back(std::string("-")); + customLine.push_back(std::string("+")); + customLine.push_back(std::string("/")); + customLine.push_back(std::string("*")); + customLine.push_back(std::string("-0,212")); + //can`t check wrong input because of try catch =(( + + Parser p1(customLine); + p1.parse(); + + std::vector resultTokins(p1.getTokens()); + ASSERT_EQ(resultTokins[0].getNumber(), 638763); + ASSERT_DOUBLE_EQ(resultTokins[1].getNumber(), 0.2121); + ASSERT_DOUBLE_EQ(resultTokins[2].getNumber(), -0.212); + ASSERT_EQ(resultTokins[3].getNumber(), -8678); + ASSERT_EQ(resultTokins[4].getOperand(), TOper::SUB); + ASSERT_EQ(resultTokins[5].getOperand(), TOper::ADD); + ASSERT_EQ(resultTokins[6].getOperand(), TOper::DIV); + ASSERT_EQ(resultTokins[7].getOperand(), TOper::MUL); + ASSERT_DOUBLE_EQ(resultTokins[2].getNumber(), -0.212); +} +//Parser test end + + +//Calculator test start +TEST_F(ProjectTest, simpleAdd) { + std::vector sources; + sources.push_back(std::string("5")); + sources.push_back(std::string("2")); + sources.push_back(std::string("+")); + + ASSERT_DOUBLE_EQ(execute(sources), 7); +} + +TEST_F(ProjectTest, simpleSub) { + std::vector sources; + sources.push_back(std::string("5")); + sources.push_back(std::string("2")); + sources.push_back(std::string("-")); + + ASSERT_DOUBLE_EQ(execute(sources), 3); +} + +TEST_F(ProjectTest, simpleMul) { + std::vector sources; + sources.push_back(std::string("5")); + sources.push_back(std::string("2")); + sources.push_back(std::string("*")); + + ASSERT_DOUBLE_EQ(execute(sources), 10); +} + +TEST_F(ProjectTest, simpleDiv) { + std::vector sources; + sources.push_back(std::string("5")); + sources.push_back(std::string("2")); + sources.push_back(std::string("/")); + + ASSERT_DOUBLE_EQ(execute(sources), 2.5); +} + +TEST_F(ProjectTest, mediumRear) { + std::vector sources; + sources.push_back(std::string("5")); + sources.push_back(std::string("2")); + sources.push_back(std::string("/")); + sources.push_back(std::string("2")); + sources.push_back(std::string("*")); + sources.push_back(std::string("1")); + sources.push_back(std::string("-")); + sources.push_back(std::string("2")); + sources.push_back(std::string("*")); + + ASSERT_DOUBLE_EQ(execute(sources), 8); +} + +TEST_F(ProjectTest, wrongImput) { + std::vector sources; + sources.push_back(std::string("5")); + sources.push_back(std::string("2")); + sources.push_back(std::string("/")); + sources.push_back(std::string("2")); + sources.push_back(std::string("*")); + sources.push_back(std::string("1")); + sources.push_back(std::string("-")); + sources.push_back(std::string("2")); + sources.push_back(std::string("H")); + + ASSERT_THROW(execute(sources), std::invalid_argument); +} + +//Calculator test end + } // namespace testing } // namespace dev