-
Notifications
You must be signed in to change notification settings - Fork 12
Строганцев Антон #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| build/ | ||
|
|
||
| # Prerequisites | ||
| *.d | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| cmake_minimum_required(VERSION 3.5) | ||
| project(ArithmeticParser LANGUAGES CXX) | ||
|
|
||
| set(CMAKE_CXX_STANDARD 14) | ||
| set(CMAKE_CXX_EXTENSIONS OFF) | ||
| set(CMAKE_CXX_STANDARD_REQUIRED TRUE) | ||
|
|
||
| if(NOT CMAKE_BUILD_TYPE) | ||
| set(CMAKE_BUILD_TYPE Release) | ||
| endif() | ||
|
|
||
| FILE(GLOB SRC_HPP "src/*.hpp") | ||
|
|
||
| add_executable(${PROJECT_NAME} "src/main.cpp" ${SRC_HPP}) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| #pragma once | ||
| #include "Token.hpp" | ||
| #include "Variables.hpp" | ||
| #include "Expression.hpp" | ||
| #include "Operations.hpp" | ||
|
|
||
| namespace ap | ||
| { | ||
| class Computer | ||
| { | ||
| public: | ||
| Computer(const Expression& exprGraphg, const Variables& variables) : | ||
| _graph(exprGraphg), | ||
| _variables(variables) | ||
| { } | ||
|
|
||
| double compute() | ||
| { | ||
| return compute(_graph); | ||
| } | ||
|
|
||
| private: | ||
| const Expression& _graph; | ||
| const Variables& _variables; | ||
|
|
||
| double compute(const Expression& expr) | ||
| { | ||
| TokenType type = expr.token.type; | ||
| if (type == TokenType::DIGIT) | ||
| return std::stod(expr.token.expr); | ||
| else if (type == TokenType::VARIABLE) | ||
| return _variables.findVariable(expr.token.expr)->second; | ||
| else if (type == TokenType::UNARY_OPERATOR) | ||
| return applyUnaryOperation(expr.token.expr, expr.operands[0]); | ||
| else if (type == TokenType::BINARY_OPERATOR) | ||
| return applyBinaryOperation(expr.token.expr, expr.operands[0], expr.operands[1]); | ||
| else | ||
| throw std::runtime_error("unordered TokenType"); | ||
| } | ||
|
|
||
| double applyUnaryOperation(const std::string& operation, const Expression& expr) | ||
| { | ||
| return Operations::Instance().findUnaryBySymbols(operation)->compute(compute(expr)); | ||
| } | ||
|
|
||
| double applyBinaryOperation(const std::string& operation, const Expression& expr1, const Expression& expr2) | ||
| { | ||
| return Operations::Instance().findBinaryBySymbols(operation)->compute(compute(expr1), compute(expr2)); | ||
| } | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| #pragma once | ||
| #include <string> | ||
| #include <stdexcept> | ||
|
|
||
| namespace ap | ||
| { | ||
| class UnorderedSymbols : public std::runtime_error | ||
| { | ||
| public: | ||
| UnorderedSymbols(size_t where, const std::string& ch) : | ||
| std::runtime_error("Unordered symbols at " + std::to_string(where) + " : \"" + ch + "\"") | ||
| { } | ||
|
|
||
| UnorderedSymbols(size_t where, char ch) : | ||
| UnorderedSymbols(where, std::string() + ch) | ||
| { } | ||
| }; | ||
|
|
||
| class UnexpectedSymbols : public std::runtime_error | ||
| { | ||
| public: | ||
| UnexpectedSymbols(size_t where, const std::string& ch) : | ||
| std::runtime_error("Unexpected symbols at " + std::to_string(where) + " : \"" + ch + "\"") | ||
| { } | ||
|
|
||
| UnexpectedSymbols(size_t where, char ch) : | ||
| UnexpectedSymbols(where, std::string() + ch) | ||
| { } | ||
| }; | ||
|
|
||
| class ExpectedSymbols : public std::runtime_error | ||
| { | ||
| public: | ||
| ExpectedSymbols(size_t where, const std::string& expectedChar) : | ||
| std::runtime_error("Symbol \"" + std::string(expectedChar) + std::string("\" expected at ") + std::to_string(where)) | ||
| { } | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| #pragma once | ||
| #include <string> | ||
| #include <vector> | ||
| #include "Token.hpp" | ||
|
|
||
| namespace ap | ||
| { | ||
| struct Expression | ||
| { | ||
| Expression(Token digit) : | ||
| token(digit) | ||
| { } | ||
|
|
||
| Expression(Token operation, Expression operand) : | ||
| token(operation), | ||
| operands{ operand } | ||
| { } | ||
|
|
||
| Expression(Token operation, Expression operand1, Expression operand2) : | ||
| token(operation), | ||
| operands{ operand1, operand2 } | ||
| { } | ||
|
|
||
| Token token; | ||
| std::vector<Expression> operands; | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| #pragma once | ||
| #include <vector> | ||
|
|
||
| namespace ap | ||
| { | ||
| class IUnaryOperation | ||
| { | ||
| public: | ||
| virtual std::string getSymbols() = 0; | ||
| virtual double compute(double arg) = 0; | ||
| }; | ||
|
|
||
| class IBinaryOperation | ||
| { | ||
| public: | ||
| virtual int getPriority() = 0; | ||
| virtual std::string getSymbols() = 0; | ||
| virtual double compute(double arg1, double arg2) = 0; | ||
| }; | ||
|
|
||
|
|
||
| class UnaryMinus : public IUnaryOperation | ||
| { | ||
| virtual std::string getSymbols() override { return "-"; } | ||
| virtual double compute(double arg) override { return -arg; } | ||
| }; | ||
|
|
||
| class Sinus : public IUnaryOperation | ||
| { | ||
| virtual std::string getSymbols() override { return "sin"; } | ||
| virtual double compute(double arg) override { return std::sin(arg); } | ||
| }; | ||
|
|
||
| class Cosinus : public IUnaryOperation | ||
| { | ||
| virtual std::string getSymbols() override { return "cos"; } | ||
| virtual double compute(double arg) override { return std::cos(arg); } | ||
| }; | ||
|
|
||
| class BinaryPlus : public IBinaryOperation | ||
| { | ||
| public: | ||
| virtual int getPriority() override { return 10; } | ||
| virtual std::string getSymbols() override { return "+"; } | ||
| virtual double compute(double arg1, double arg2) override { return arg1 + arg2; } | ||
| }; | ||
|
|
||
| class BinaryMinus : public IBinaryOperation | ||
| { | ||
| public: | ||
| virtual int getPriority() override { return 10; } | ||
| virtual std::string getSymbols() override { return "-"; } | ||
| virtual double compute(double arg1, double arg2) override { return arg1 - arg2; } | ||
| }; | ||
|
|
||
| class Multiplication : public IBinaryOperation | ||
| { | ||
| public: | ||
| virtual int getPriority() override { return 30; } | ||
| virtual std::string getSymbols() override { return "*"; } | ||
| virtual double compute(double arg1, double arg2) override { return arg1 * arg2; } | ||
| }; | ||
|
|
||
| class Division : public IBinaryOperation | ||
| { | ||
| public: | ||
| virtual int getPriority() override { return 40; } | ||
| virtual std::string getSymbols() override { return "/"; } | ||
| virtual double compute(double arg1, double arg2) override { return arg1 / arg2; } | ||
| }; | ||
|
|
||
| class Pow : public IBinaryOperation | ||
| { | ||
| public: | ||
| virtual int getPriority() override { return 50; } | ||
| virtual std::string getSymbols() override { return "^"; } | ||
| virtual double compute(double arg1, double arg2) override { return std::pow(arg1, arg2); } | ||
| }; | ||
|
|
||
| // ��� ���������� �������� - �������� �� ��������� � ������ ���������������� ���� �������� � Operations() | ||
| class Operations | ||
| { | ||
| using TUnarys = std::vector<std::unique_ptr<IUnaryOperation>>; | ||
| using TBinarys = std::vector<std::unique_ptr<IBinaryOperation>>; | ||
| public: | ||
| static Operations& Instance() | ||
| { | ||
| static Operations operations; | ||
| return operations; | ||
| } | ||
|
|
||
| IUnaryOperation* findUnaryBySymbols(const std::string& symbols) const | ||
| { | ||
| for (const auto& operation : _unarys) | ||
| if (std::strncmp(operation->getSymbols().c_str(), symbols.c_str(), operation->getSymbols().size()) == 0) | ||
| return operation.get(); | ||
| return nullptr; | ||
| } | ||
|
|
||
| IBinaryOperation* findBinaryBySymbols(const std::string& symbols) const | ||
| { | ||
| for (const auto& operation : _binarys) | ||
| if (std::strncmp(operation->getSymbols().c_str(), symbols.c_str(), operation->getSymbols().size()) == 0) | ||
| return operation.get(); | ||
| return nullptr; | ||
| } | ||
|
|
||
| const TUnarys& getUnarys() | ||
| { | ||
| return _unarys; | ||
| } | ||
|
|
||
| const TBinarys& getBynarys() | ||
| { | ||
| return _binarys; | ||
| } | ||
|
|
||
| private: | ||
| TUnarys _unarys; | ||
| TBinarys _binarys; | ||
|
Comment on lines
+119
to
+120
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Казалось бы _unarys, _binarys можно сделать статическими, все остальные методы тоже. Тогда получается можно и без паттерна Singleton можно обойтись? Или нет? А если можно обойтись, то почему (и в каких случаях) это хуже?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В принципе, любой шаблон проектирования имеет более "топорные" аналоги. В данном случае замена на статические списки и правда не дала бы заметных изменений, но существуют ситуации, например, где экземпляр класса имеет промежуточные состояния, для которых также бы пришлось заводить отдельные переменные и неиспользуемые пользователем (приватные) методы, что привело бы к загромождению рабочего пространства и возможным ошибкам использования сущности. |
||
|
|
||
| Operations() | ||
| { | ||
| _unarys.emplace_back(new UnaryMinus()); | ||
| _unarys.emplace_back(new Sinus()); | ||
| _unarys.emplace_back(new Cosinus()); | ||
|
|
||
| _binarys.emplace_back(new BinaryPlus()); | ||
| _binarys.emplace_back(new BinaryMinus()); | ||
| _binarys.emplace_back(new Multiplication()); | ||
| _binarys.emplace_back(new Division()); | ||
| _binarys.emplace_back(new Pow()); | ||
| } | ||
|
Comment on lines
+122
to
+133
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Конструктор копирования, оператор присваивания наверно тоже нужно сделать приватными. Если этого не сделать, ведь их можно будет вызвать?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Да, это действительно стоило сделать. |
||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
можно использовать std::find
только нужно перегрузить оператор ==
можно классы IUnaryOperation, IBinaryOperation,... унаследовать от общего класса с перегруженным ==, равенство можно проверять через getSymbols
Правда вопрос, что делать с унарным и бинарным минусом (dynamic_cast?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Да, так можно было сделать, но две эти функции все равно нельзя было бы объединить в одну, т.к. для корректного анализа вводимых данных парсер ожидает в зависимости от предыдущего токена операцию конкретного типа (это как раз таки сделано во избежание конфликта операций разных типов, имеющих одно обозначение, как "-").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
тут проще добавить поле с уникальным id (для каждого нового класса) и делать проверку по нему