Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
49597f9
Added main
R9quiem Oct 27, 2025
b74fd6b
Added .gitignore file
R9quiem Oct 27, 2025
fada2a6
Добавлен заголовочный файл plugin_api.h с определениями для работы с …
R9quiem Oct 27, 2025
f6e7fd5
Добавлен CMakeLists.txt для основного проекта и плагинов с настройкам…
R9quiem Oct 27, 2025
8129a36
Добавлен класс DllLoader для управления сырым DLL
R9quiem Oct 28, 2025
2cec2cd
Изменен тип поля help в структуре plugin_descriptor: вместо указателя…
R9quiem Oct 28, 2025
ff71346
Добавлена структура Plugin для управления плагинами с поддержкой загр…
R9quiem Oct 28, 2025
d3a20af
Добавлен временный плагин "funcadd" для тестов. Также небольшие измен…
R9quiem Oct 29, 2025
89c10e3
Добавлена папка для исходников /src и соответствующие изменения в cmake
R9quiem Oct 29, 2025
eac2f1a
Добавил плагины для вычисления синуса, натурального логарифма и степе…
R9quiem Oct 29, 2025
47b93be
Добавил обработку проблемых случаях в плагинах и выкидывания соответс…
R9quiem Oct 29, 2025
b40f84a
Добавил обработку проблемых случаях в плагинах и выкидывания соответс…
R9quiem Oct 29, 2025
d4c6f5d
Унифицировал сообщение исключения, сделал русский язык
R9quiem Oct 29, 2025
05762f3
Добавлена возможность создания встроенных плагинов (просто для унифиц…
R9quiem Oct 29, 2025
735a793
Добавлено поле ассоциативности и приоритета в структуру описания плагина
R9quiem Oct 30, 2025
b6db8d7
Изменен тип параметра функции exec_ и встроенной функции на unsigned …
R9quiem Oct 30, 2025
80a7aa6
Добавлен класс PluginRegistry для управления плагинами
R9quiem Oct 30, 2025
231f773
Добавлен класс Parser для разбора выражений и реализации парсинга с и…
R9quiem Oct 30, 2025
948ccc7
Добавлено директива #pragma once для предотвращения повторного включе…
R9quiem Oct 30, 2025
5289f07
Исправлена работа парсера. Корректная обработка скобок в парсере и др…
R9quiem Oct 30, 2025
74a63cf
Добавлен файл builtin_plugins.h с реализацией базовых арифметических …
R9quiem Oct 30, 2025
e0064c5
Добавлены файлы Loader.h и Loader.cpp с реализацией функций загрузки …
R9quiem Oct 30, 2025
baa9f6c
Разделил обьявление и реализацию функций класса Plugin
R9quiem Oct 30, 2025
0fd01b6
Реализована основная логика калькулятора с использованием плагинов. Д…
R9quiem Oct 30, 2025
7b7ba6e
Update README.md
R9quiem Nov 22, 2025
12952f9
Исправил:
R9quiem Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Build artifacts
/build/
CMakeCache.txt
CMakeFiles/
Makefile
*.o
*.obj
*.exe
*.dll
*.so
*.dylib

# IDE files
.vscode/
.idea/
*.user
*.sln
*.vcxproj*
31 changes: 31 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
cmake_minimum_required(VERSION 3.21)

project(calc LANGUAGES CXX)

# Укажем стандарт C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include_directories(${CMAKE_SOURCE_DIR}/include)

add_library(plugin_api INTERFACE)

target_include_directories(plugin_api INTERFACE
${CMAKE_SOURCE_DIR}/include
)


# Исполняемый файл проекта
file(GLOB_RECURSE SRC_FILES ${CMAKE_SOURCE_DIR}/src/*.cpp)
add_executable(calc ${SRC_FILES} app/main.cpp)

target_link_libraries(calc PRIVATE plugin_api)

target_include_directories(calc PRIVATE
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/src
)

add_subdirectory(${CMAKE_SOURCE_DIR}/plugins)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,40 @@
start
Инструменты которые используются:
1. CMake 3.21+
2. Компилятор с поддержкой C++17 (у меня используется g++ 15.2.0)
3. ninja 1.13.1

Поддерживаемые операции калькулятора:

1. Сложение: +

2. Вычитание: -

3. Умножение: *

4. Деление: /

Операции, которые реализованы как сторонние dll плагины:

5. Возведение в степень: ^

6. Натуральный логарифм: ln

7. Синус: sin (аргумент в радианах)


Сборка проекта:

1. Перейдите в корневую директорию проекта.
2. Настройка CMake:

```bash
cmake -S . -B build -G Ninja -DCMAKE_CXX_COMPILER="C:/msys64/ucrt64/bin/g++.exe"
```

3. Сборка проекта:

```bash
cmake --build build
```

Далее в папке build появляется calc.exe
37 changes: 37 additions & 0 deletions app/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "Plugin.h"
#include "PluginRegistry.h"
#include "Parser.h"
#include "Loader.h"

#include <iostream>

int main() {

SetConsoleOutputCP(CP_UTF8);
PluginRegistry reg;
load_plugins(reg);

std::cout << "Калькулятор запущен. Введите выражение или 'exit' для выхода.\n";

for (;;) {
std::cout << "\n> ";
std::string input;
std::getline(std::cin, input);
if (input == "exit" || input == "quit")
break;
if (input.empty())
continue;

try {
Parser parser(input.begin(), input.end(), reg);
Expression expr = parser.parse();
double result = eval_with_plugins(expr, reg);
std::cout << "= " << result << "\n";
} catch (const std::exception& ex) {
std::cerr << "Ошибка: " << ex.what() << "\n";
}
}

std::cout << "Выход из программы.\n";
return 0;
}
41 changes: 41 additions & 0 deletions include/DllLoader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include <windows.h>
#include <filesystem>
#include "plugin_api.h"

// Вспомогательная обёртка для сырой DLL.
// загрузка/выгрузка/поиск символа.
class DllLoader {
HMODULE h_ = nullptr;

void close() noexcept {
if (h_) { ::FreeLibrary(h_); h_ = nullptr; }
}

public:
DllLoader() = default;
explicit DllLoader(const std::filesystem::path& path) {
h_ = ::LoadLibraryW(path.wstring().c_str());
if (!h_)
throw std::runtime_error("Не удалось загрузить DLL: " + path.string());
}

DllLoader(const DllLoader&) = delete;
DllLoader& operator=(const DllLoader&) = delete;

DllLoader(DllLoader&& o) noexcept : h_(o.h_) { o.h_ = nullptr; }
DllLoader& operator=(DllLoader&& o) noexcept {
if (this != &o) { close(); h_ = o.h_; o.h_ = nullptr; }
return *this;
}

~DllLoader() { close(); }

void* load_symbol_raw(const char* name) const {
if (!h_) throw std::runtime_error("DLL не загружена");
void* p = reinterpret_cast<void*>(::GetProcAddress(h_, name));
if (!p) throw std::runtime_error(std::string("GetProcAddress failed: ") + name);
return p;
}
};
12 changes: 12 additions & 0 deletions include/Loader.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include "PluginRegistry.h"
#include "builtin_plugins.h"
#include <filesystem>

namespace fs = std::filesystem;

void load_builtin_plugins(PluginRegistry& reg);
void load_dll_plugins(PluginRegistry& reg);
void load_plugins(PluginRegistry& reg);

36 changes: 36 additions & 0 deletions include/Parser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <string>
#include <vector>

#include "Plugin.h"
#include "PluginRegistry.h"

struct Expression {
std::string token;
std::vector<Expression> args;

Expression(std::string t) : token(std::move(t)) {}
Expression(std::string t, Expression a) : token(std::move(t)), args{ std::move(a) } {}
Expression(std::string t, Expression a, Expression b) : token(std::move(t)), args{ std::move(a), std::move(b) } {}
};

class Parser {
std::string parse_token();
Expression parse_simple_expression();
Expression parse_expression(int min_priority);

std::string::const_iterator it, end;

const PluginRegistry& r;
public:
explicit Parser(std::string::const_iterator begin,
std::string::const_iterator end,
const PluginRegistry &reg
)
: it(begin), end(end), r(reg) {}
Expression parse();

};

double eval_with_plugins(const Expression& e, const PluginRegistry& R);
60 changes: 60 additions & 0 deletions include/Plugin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#pragma once

#include <filesystem>
#include <functional>
#include <string>
#include <vector>
#include "DllLoader.h" // требуется тут, т.к. DllLoader — data-member
#include "plugin_api.h" // для plugin_descriptor и PLUGIN_CALL

// Ассоциативность операторов
enum class Associativity : unsigned int {
ASSOC_LEFT = 0,
ASSOC_RIGHT = 1,
};

struct Plugin {
// ==== данные плагина ====
std::filesystem::path path_;
DllLoader dll_; // владелец загруженной DLL (для внешних плагинов)
std::string symbol_;
std::string name_;
unsigned int arity_{};
std::function<double(const double*, unsigned int)> exec_; // единая точка вызова
std::string help_{};
unsigned int priority_{};
Associativity assoc_{Associativity::ASSOC_LEFT};

using GetDescFn = const plugin_descriptor* (PLUGIN_CALL *)();

public:
// ---- конструкторы ----
// Загрузка плагина из DLL
explicit Plugin(const std::filesystem::path& dll_path);

// Встроенный плагин
using builtin_fn = double (*)(const double*, unsigned int);
Plugin(std::string symbol, std::string name, unsigned int arity,
builtin_fn fn, std::string help = "no info",
Associativity assoc = Associativity::ASSOC_LEFT,
unsigned int priority = 0);

Plugin(const Plugin&) = delete;
Plugin& operator=(const Plugin&) = delete;

Plugin(Plugin&&) noexcept = default;
Plugin& operator=(Plugin&&) noexcept = default;

// ---- доступ к метаданным ----
const std::string& symbol() const noexcept { return symbol_; }
const std::string& name() const noexcept { return name_; }
unsigned int arity() const noexcept { return arity_; }
const std::filesystem::path& path() const noexcept { return path_; }
std::string help() const noexcept { return help_; }
unsigned int priority() const noexcept { return priority_; }
Associativity assoc() const noexcept { return assoc_; }

// ---- вызов операции ----
double call(const double* args, unsigned int n) const;
double call(const std::vector<double>& args) const;
};
50 changes: 50 additions & 0 deletions include/PluginRegistry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once

#include <unordered_map>
#include <string>
#include <stdexcept>
#include <iostream>
#include <memory>
#include <vector>
#include <string_view>
#include <windows.h>
#include <algorithm>

#include "Plugin.h"

class PluginRegistry {
std::unordered_map<std::string, std::unique_ptr<Plugin>> plugins;

public:
const Plugin& at(const std::string& sym) const {
auto it = plugins.find(sym);
if (it == plugins.end()) throw std::runtime_error("Unknown op/function: " + sym);
return *it->second;
}
int priority_of(const std::string& sym) const {
auto it = plugins.find(sym);
if (it == plugins.end()) throw std::runtime_error("Unknown op/function: " + sym);
return static_cast<int>(it->second->priority());
}
bool is_right_assoc(const std::string& sym) const {
auto it = plugins.find(sym);
if (it == plugins.end()) throw std::runtime_error("Unknown op/function: " + sym);
return it->second->assoc() == Associativity::ASSOC_RIGHT;
}
void add(std::unique_ptr<Plugin> p) {
std::string sym = p->symbol(), name = p->name();
plugins[p->symbol()] = std::move(p);
std::cout << "Загружен плагин: " << sym << " (" << name << ") " << std::endl;
}
std::vector<std::string> get_all_sym() const {
std::vector<std::string> symbols;
symbols.reserve(plugins.size());
for (const auto& [sym, plugin] : plugins) {
symbols.push_back(sym); // копия — string_view не нужен и не «висячий»
}
// полезно, если будут многосимвольные токены (например, "==", "<=", "**")
std::sort(symbols.begin(), symbols.end(),
[](const std::string& a, const std::string& b){ return a.size() > b.size(); });
return symbols;
}
};
13 changes: 13 additions & 0 deletions include/builtin_plugins.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once
#include <stdexcept>

inline double add_fn(const double* args, unsigned int n) { return args[0] + args[1]; }
inline double sub_fn(const double* args, unsigned int n) { return args[0] - args[1]; }
inline double mul_fn(const double* args, unsigned int n) { return args[0] * args[1]; }
inline double div_fn(const double* args, unsigned int n) {
if (args[1] == 0) throw std::runtime_error("div: деление на 0");
return args[0] / args[1];
}
static double unary_minus_fn(const double* args, unsigned int n) {
return -args[0];
}
34 changes: 34 additions & 0 deletions include/plugin_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once
#include <cstddef>

#ifdef _WIN32
#define PLUGIN_EXPORT extern "C" __declspec(dllexport)
#define PLUGIN_CALL __cdecl
#else
#define PLUGIN_EXPORT extern "C"
#define PLUGIN_CALL
#endif

static unsigned int PLUGIN_ARITY_VARIADIC = 0;

using plugin_apply_fn = double (PLUGIN_CALL *)(const double* args, unsigned int n);

enum plugin_assoc : unsigned int {
ASSOC_LEFT = 0,
ASSOC_RIGHT = 1, // например ^
};

struct plugin_descriptor {
const char* name;
const char* symbol;
unsigned int arity;
plugin_apply_fn apply;
const char* help;

unsigned int priority; // меньше - больше приоритет
plugin_assoc associativity; // для arity = 2
};

PLUGIN_EXPORT const plugin_descriptor* plugin_get_descriptor();

//приоритет "^" > "*","/" > "+", "-"
Loading