diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..6cf15e4 --- /dev/null +++ b/.clangd @@ -0,0 +1,8 @@ +CompileFlags: + CompilationDatabase: "build" + +Diagnostics: + ClangTidy: + Add: modernize* + Remove: modernize-use-trailing-return-type + UnusedIncludes: Strict diff --git a/.editorconfig b/.editorconfig index c1322dc..c1e2c64 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,5 +8,5 @@ indent_style = space indent_size = 4 end_of_line = lf charset = utf-8 -trim_trailing_whitespace = false -insert_final_newline = false \ No newline at end of file +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json deleted file mode 100644 index 4a169e7..0000000 --- a/.vscode/c_cpp_properties.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "configurations": [ - { - "name": "Linux", - "includePath": [ - "${workspaceFolder}/**" - ], - "defines": ["-Wall", "-Werror", "-DTRACE"], - "compilerPath": "/usr/bin/clang", - "cStandard": "c17", - "cppStandard": "c++20", - "intelliSenseMode": "linux-gcc-x64", - "configurationProvider": "ms-vscode.cmake-tools" - } - ], - "version": 4 -} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 11be88f..c34f037 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,6 +5,7 @@ "llvm-vs-code-extensions.vscode-clangd", "streetsidesoftware.code-spell-checker", "donjayamanne.githistory", - "jeff-hykin.better-cpp-syntax" + "jeff-hykin.better-cpp-syntax", + "ms-vscode.cmake-tools" ] -} \ No newline at end of file +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 5131412..f74875a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -24,6 +24,25 @@ "ignoreFailures": true } ] + }, + { + "name": "Debug App", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceRoot}/build/spreadsheet", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "externalConsole": false, + "MIMode": "gdb", + "preLaunchTask": "cmake: build", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] } ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..f343d76 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,81 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "cmake: configure", + "type": "shell", + "command": "cmake", + "args": [ + "-S", "${workspaceFolder}", + "-B", "${workspaceFolder}/build", + "-DCMAKE_BUILD_TYPE=${input:buildType}" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": ["$gcc"], + "detail": "Generated task to configure CMake project" + }, + { + "label": "cmake: build", + "type": "shell", + "command": "cmake", + "args": [ + "--build", "${workspaceFolder}/build", + "--config", "${input:buildType}", + "--target", "all", + "--", + "-j8" + ], + "dependsOn": "cmake: configure", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": ["$gcc"], + "detail": "Generated task to build CMake project" + }, + { + "label": "cleanup", + "type": "shell", + "command": "rm", + "args": [ + "-rf", + "${workspaceFolder}/build/*" + ], + "group": "none" + }, + { + "label": "install libspreadsheet", + "type": "shell", + "command": "cmake", + "args": [ + "--build", "${workspaceFolder}/build", + "--config", "${input:buildType}", + "--target", "install" + ], + "dependsOn": "cmake: build", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": ["$gcc"], + "detail": "Install libspreadsheet" + } + ], + "inputs": [ + { + "id": "buildType", + "type": "pickString", + "description": "Select the build type", + "options": [ + "Debug", + "Release", + "MinSizeRel", + "RelWithDebInfo" + ], + "default": "Debug" + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 222c3ee..005e61e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,47 @@ cmake_minimum_required(VERSION 3.24) -set(CMAKE_VERBOSE_MAKEFILE ON) +project(spreadsheet + VERSION 1.1.0 + DESCRIPTION "spreadsheet" + HOMEPAGE_URL "https://github.com/IgorKilipenko/cpp-spreadsheet" + LANGUAGES CXX +) -project(cpp-spreadsheet-app VERSION 1.1.0 LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) -# Set C++ standard -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-implicit-fallthrough") -# Add subdirectory with the library -add_subdirectory(spreadsheet) +# Source directiory +set(PROJECT_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/src") + +# Public include directiory +set(PROJECT_PUBLIC_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") + +# Installation directiory +set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/bin") + +# Add threads library for all lib targets +find_package(Threads REQUIRED) +link_libraries(Threads::Threads) + +# Find Java +find_package(Java REQUIRED) + +# Include the ANTLR setup +include(cmake/install_antlr.cmake) + +# Include the source files and create targets +include(cmake/install.cmake) + +if(CMAKE_PROJECT_NAME STREQUAL "spreadsheet") # if spreadsheet is top-level project + # add include path + include_directories(include) + include_directories(src/include) + include_directories(src) + + include(cmake/utils.cmake) + include(cmake/develop.cmake) +endif() + +set_directory_properties(PROPERTIES VS_STARTUP_PROJECT spreadsheet) diff --git a/README.md b/README.md index 9ec3936..dfdaefd 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,80 @@ -# Cpp-Spreadsheet - -## Дипломный проект: Электронная таблица +# Cpp-Spreadsheet (Электронная таблица) + +## Содержание + +- [Описание проекта](#project-description) +- [Функциональность](#main-features) +- [Пример использования](#use-examples) +- [Технологии](#project-technology) +- [Установка](#project-installation) + +## Описание проекта {#project-description} + +Этот проект представляет собой упрощённый аналог электронных таблиц. В ячейках таблицы могут содержаться как текст, так и формулы, включающие индексы (ссылки) других ячеек. + +## Функциональность {#main-features} + +- Поддержка чисел и строк в ячейках +- Возможность использования формул с числами, строками и ссылками на другие ячейки +- Автоматическое обновление значений ячеек при изменении зависимых ячеек +- Обработка циклических зависимостей и ошибок в формулах + +## Пример использования {#use-examples} + +```cpp +#include "spreadsheet.h" +#include + +int main() { + // Создаем новую таблицу + auto sheet = CreateSheet(); + + // Задаем значения ячеек + sheet->SetCell("A1"_pos, "Hello, world!"); + sheet->SetCell("B1"_pos, "=2+2"); + + // Получаем значения ячеек и выводим на экран + std::cout << "A1: " << sheet->GetCell("A1"_pos)->GetValue() << std::endl; + std::cout << "B1: " << sheet->GetCell("B1"_pos)->GetValue() << std::endl; + + // Очищаем ячейку + sheet->ClearCell("B1"_pos); + + // Получаем значение очищенной ячейки (должно быть пусто) + std::cout << "B1 after clearing: " << sheet->GetCell("B1"_pos)->GetValue() << std::endl; + + return 0; +} +``` + +## Технологии {#project-technology} + +- Стандарт языка: C++17 +- Парсинг формул: [ANTLR4](https://www.antlr.org/) +- Основная логика приложения: библиотека `libspreadsheet` +- Пример использования: консольное приложение `spreadsheet` + +## Установка {#project-installation} + +
+ +Для установки проекта выполните следующие шаги: + +```bash +# Клонировать репозиторий +git clone https://github.com/IgorKilipenko/cpp-spreadsheet.git + +# Перейти в каталог проекта +cd cpp-spreadsheet + +# Собрать проект с помощью CMake +mkdir build +cd build +cmake .. +make + +# Установить +make install +``` + +
diff --git a/cmake/develop.cmake b/cmake/develop.cmake new file mode 100644 index 0000000..acda107 --- /dev/null +++ b/cmake/develop.cmake @@ -0,0 +1,36 @@ +message(STATUS "-------------DEVELOP SETTING------------") + +# Enable address sanitizer +option(ENABLE_SANITIZER "Enable sanitizer(Debug+Gcc/Clang/AppleClang)" ON) + +if(ENABLE_SANITIZER AND NOT MSVC) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + check_asan(HAS_ASAN) + if(HAS_ASAN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") + else() + message(WARNING "sanitizer is no supported with current tool-chains") + endif() + endif() +endif() + +# warning +option(ENABLE_WARNING "Enable warning for all project " OFF) +if(ENABLE_WARNING) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + list(APPEND MSVC_OPTIONS "/W3") + if(MSVC_VERSION GREATER 1900) # Allow non fatal security warnings for msvc 2015 + list(APPEND MSVC_OPTIONS "/WX") + endif() + add_compile_options(MSVC_OPTIONS) + else() + add_compile_options(-Wall + -Wextra + -Wconversion + -pedantic + -Werror + -Wfatal-errors) + endif() +endif() + +message(STATUS "--------------------------------------------") diff --git a/cmake/install.cmake b/cmake/install.cmake new file mode 100644 index 0000000..239dedb --- /dev/null +++ b/cmake/install.cmake @@ -0,0 +1,69 @@ +message(STATUS "-------------INSTALL SETTING------------") + +# Add all source files except main.cpp +file(GLOB_RECURSE sources CONFIGURE_DEPENDS + "${PROJECT_SRC_DIR}/*.cpp" +) +list(FILTER sources EXCLUDE REGEX "${PROJECT_SRC_DIR}/main\\.cpp$") + +# Find all public and private headers +file(GLOB_RECURSE public_headers CONFIGURE_DEPENDS + "${PROJECT_PUBLIC_INCLUDE_DIR}/spreadsheet/*.h" +) +file(GLOB_RECURSE private_headers CONFIGURE_DEPENDS + "${PROJECT_PUBLIC_INCLUDE_DIR}/*.h" + "${PROJECT_SRC_DIR}/*.h" +) +list(REMOVE_ITEM private_headers ${public_headers}) + +# Create the library +add_library(libspreadsheet ${sources} ${private_headers} ${ANTLR_FormulaParser_CXX_OUTPUTS}) +target_link_libraries(libspreadsheet PRIVATE antlr4_static) +set_target_properties(libspreadsheet PROPERTIES + PUBLIC_HEADER "${public_headers}" + PREFIX "" +) + +# Specify the include directories +target_include_directories(libspreadsheet + INTERFACE + $ + $ + PRIVATE + "${antlr_SOURCE_DIR}/runtime/Cpp/runtime/src" + "${ANTLR_FormulaParser_OUTPUT_DIR}" + "${PROJECT_PUBLIC_INCLUDE_DIR}" +) + +# Installation rules for the library +install(TARGETS libspreadsheet antlr4_static + EXPORT libspreadsheet_Targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION bin + PUBLIC_HEADER DESTINATION include/spreadsheet +) +install(EXPORT libspreadsheet_Targets + FILE libspreadsheet-config.cmake + DESTINATION lib/cmake/libspreadsheet + NAMESPACE libspreadsheet:: +) +install( + FILES + ${private_headers} + DESTINATION include +) + +# Create executable target +add_executable(spreadsheet ${PROJECT_SRC_DIR}/main.cpp) +add_dependencies(spreadsheet libspreadsheet) +target_link_libraries(spreadsheet PRIVATE libspreadsheet) + +# Install target for spreadsheet +install( + TARGETS spreadsheet + DESTINATION app + # EXPORT spreadsheet_Targets +) + +message(STATUS "--------------------------------------------") diff --git a/cmake/install_antlr.cmake b/cmake/install_antlr.cmake new file mode 100644 index 0000000..780fcb8 --- /dev/null +++ b/cmake/install_antlr.cmake @@ -0,0 +1,42 @@ +# SetupAntlr.cmake - This file handles the setup for ANTLR + +set(ANTLR_TAG 4.13.1) +set(ANTLR4_WITH_STATIC_CRT OFF) +set(ANTLR_BUILD_SHARED OFF) +set(ANTLR_BUILD_CPP_TESTS OFF) + +message(STATUS "DOWNLOAD ANTLR4 JAR v" ${ANTLR_TAG}) + +# Define the path to the ANTLR JAR file and download it if it does not exist +set(ANTLR_EXECUTABLE "${CMAKE_BINARY_DIR}/antlr/antlr-${ANTLR_TAG}-complete.jar") +if (NOT EXISTS "${ANTLR_EXECUTABLE}") + file( + DOWNLOAD + "https://www.antlr.org/download/antlr-${ANTLR_TAG}-complete.jar" + "${ANTLR_EXECUTABLE}" + ) +endif() + +message(STATUS "FETCH ANTLR4 CPP") + +# Use FetchContent to download and make available the ANTLR C++ runtime +include(FetchContent) +FetchContent_Declare( + antlr + GIT_REPOSITORY https://github.com/antlr/antlr4 + GIT_TAG ${ANTLR_TAG} + SOURCE_SUBDIR "runtime/Cpp" +) +FetchContent_MakeAvailable(antlr) + +message(STATUS "ANTLR SOURCE DIR: " ${antlr_SOURCE_DIR}) + +include(${antlr_SOURCE_DIR}/runtime/Cpp/cmake/FindANTLR.cmake) + +# Generate ANTLR files for FormulaParser +antlr_target(FormulaParser "${PROJECT_SRC_DIR}/antlr/grammar/Formula.g4" LEXER PARSER LISTENER) + +# Include directories for ANTLR +include_directories("${antlr_SOURCE_DIR}/runtime/Cpp/src") + +message(STATUS "ANTLR RUNTIME DIR: " ${antlr_SOURCE_DIR}/runtime/Cpp/src) diff --git a/cmake/utils.cmake b/cmake/utils.cmake new file mode 100644 index 0000000..699bdd0 --- /dev/null +++ b/cmake/utils.cmake @@ -0,0 +1,14 @@ +macro(check_asan _RESULT) + include(CheckCXXSourceRuns) + set(CMAKE_REQUIRED_FLAGS "-fsanitize=address") + check_cxx_source_runs( + [====[ +int main() +{ + return 0; +} +]====] + ${_RESULT} + ) + unset(CMAKE_REQUIRED_FLAGS) +endmacro() diff --git a/spreadsheet/FormulaAST.h b/include/FormulaAST.h similarity index 100% rename from spreadsheet/FormulaAST.h rename to include/FormulaAST.h diff --git a/spreadsheet/cell.h b/include/cell.h similarity index 100% rename from spreadsheet/cell.h rename to include/cell.h diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..995014a --- /dev/null +++ b/include/common.h @@ -0,0 +1,228 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +inline constexpr char FORMULA_SIGN = '='; +inline constexpr char ESCAPE_SIGN = '\''; + +/** + * Position represents a position in a 2D space using row and column indices. + * Indices are zero-based. + */ +struct Position { + int row = 0; + int col = 0; + + bool operator==(Position rhs) const; + bool operator<(Position rhs) const; + + bool IsValid() const; + std::string ToString() const; + + static Position FromString(std::string_view str); + + static const int MAX_ROWS = 16384; + static const int MAX_COLS = 16384; + static const Position NONE; +}; + +struct Size { + int rows = 0; + int cols = 0; + + bool operator==(Size rhs) const; +}; + +/** + * Describes errors that can occur when computing a formula. + */ +class FormulaError { +public: + enum class Category { + Ref, // reference error (cell reference has an invalid position) + Value, // the cell value cannot be interpreted as a number + Div0, // division by zero occurred during computation + }; + + FormulaError(Category category); + + Category GetCategory() const; + + bool operator==(FormulaError rhs) const; + + std::string_view ToString() const; + +private: + Category category_; +}; + +std::ostream& operator<<(std::ostream& output, FormulaError fe); + +/** + * Exception thrown when an invalid position is passed to a method + */ +class InvalidPositionException : public std::out_of_range { +public: + using std::out_of_range::out_of_range; +}; + +/** + * FormulaException is an exception that is thrown when a syntactically + * invalid formula is encountered. + */ +class FormulaException : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +/** + * CyclicDependencyException is an exception that is thrown when a circular + * dependency between cells is detected while computing a formula. + */ +class CircularDependencyException : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +/** + * @class CellInterface + * @brief An interface representing a cell in a spreadsheet. + * + * The CellInterface class defines the essential operations that can be performed + * on a cell within a spreadsheet, such as retrieving its value, text, and any + * referenced cells. Cells can contain text, numeric values, or formulas that may + * result in a value or an error. + */ +class CellInterface { +public: + using Value = std::variant; + + virtual ~CellInterface() = default; + + /** + * @brief Returns the visible value of the cell. + * + * For a text cell, this returns the text without any escape characters. + * For a formula cell, this returns the numerical value of the formula or an error message. + * + * @return The visible value of the cell as a std::variant containing either a string, + * a double, or a FormulaError. + */ + virtual Value GetValue() const = 0; + + /** + * @brief Returns the internal text of the cell as if it were being edited. + * + * For a text cell, this returns the text, which may include escape characters. + * For a formula cell, this returns the formula expression. + * + * @return The internal text of the cell as a std::string. + */ + virtual std::string GetText() const = 0; + + /** + * Returns a list of cells that are directly referenced by this formula. + * The list is sorted in ascending order and does not contain duplicate cells. + * In the case of a text cell, the list is empty. + */ + virtual std::vector GetReferencedCells() const = 0; +}; + +// Интерфейс таблицы +class SheetInterface { +public: + virtual ~SheetInterface() = default; + + /** + * @brief Sets the content of a cell. + * + * If the text begins with the "=" sign, it is interpreted as a formula. + * If a syntactically incorrect formula is provided, a FormulaException is thrown, + * and the cell's value remains unchanged. + * If a formula leads to a cyclic dependency (e.g., if the formula references the + * current cell), a CircularDependencyException is thrown, and the cell's value + * remains unchanged. + * + * Formula specifics: + * - If the text contains only the "=" symbol and nothing else, it is not considered a formula. + * - If the text starts with the "'" (apostrophe) symbol, the apostrophe is omitted when + * retrieving the cell's value using the GetValue() method. This can be used if you need + * the text to start with the "=" sign without it being interpreted as a formula. + * + * @param pos The position of the cell to set. + * @param text The text to set in the cell. + */ + virtual void SetCell(Position pos, std::string text) = 0; + + /** + * @brief Returns the value of the cell at the given position. + * + * If the cell is empty, it may return nullptr. + * + * @param pos The position of the cell to retrieve. + * @return A pointer to the cell's interface. If the cell is empty, it may return nullptr. + */ + virtual const CellInterface* GetCell(Position pos) const = 0; + + /** + * @brief Retrieves a modifiable pointer to the cell at the given position. + * + * This method allows modification of the cell content at the specified position. + * If the cell is empty, it may return nullptr. + * + * @param pos The position of the cell to retrieve. + * @return A pointer to the cell's interface. If the cell is empty, it may return nullptr. + */ + virtual CellInterface* GetCell(Position pos) = 0; + + /** + * @brief Clears the content of a cell. + * + * After calling this method, a subsequent call to GetCell() for this cell + * will return either nullptr or an object with empty text. + * + * @param pos The position of the cell to clear. + */ + virtual void ClearCell(Position pos) = 0; + + /** + * @brief Computes the size of the area involved in printing. + * + * This method determines the bounding rectangle that encompasses all cells + * with non-empty text. + * + * @return The size of the printable area as a Size object. + */ + virtual Size GetPrintableSize() const = 0; + + /** + * @brief Outputs the entire sheet to the provided stream. + * + * Columns are separated by tab characters, and each row is followed by a newline character. + * The cells are converted to strings using the GetValue() or GetText() methods, respectively. + * An empty cell is represented as an empty string in any case. + * + * @param output The output stream to which the sheet will be printed. + */ + virtual void PrintValues(std::ostream& output) const = 0; + + /** + * @brief Outputs the text content of the entire sheet to the provided stream. + * + * Columns are separated by tab characters, and each row is followed by a newline character. + * The cells are converted to strings using the GetText() method. + * An empty cell is represented as an empty string. + * + * @param output The output stream to which the sheet will be printed. + */ + virtual void PrintTexts(std::ostream& output) const = 0; +}; + +// Создаёт готовую к работе пустую таблицу. +std::unique_ptr CreateSheet(); diff --git a/include/formula.h b/include/formula.h new file mode 100644 index 0000000..f6225f6 --- /dev/null +++ b/include/formula.h @@ -0,0 +1,60 @@ +#pragma once + +#include +#include + +#include "common.h" + +/** + * @brief Interface for working with formulas. + * + * This interface provides the ability to evaluate and update arithmetic expressions, + * which may include simple binary operations, numbers, parentheses, and cell references. + * Cells referenced in the formula can contain either formulas or text. If a cell contains + * text that represents a number, it will be treated as a number. An empty cell + * or a cell with an empty text is treated as the number zero. + */ +class FormulaInterface { +public: + using Value = std::variant; + + virtual ~FormulaInterface() = default; + + /** + * @brief Evaluates the formula and returns its computed value or an error. + * + * @param sheet The sheet interface containing the cell values referenced by the formula. + * @return The computed value of the formula if all referenced cells are valid, or an error + * if any referenced cell evaluation results in an error. If multiple errors occur, + * any one of them may be returned. + */ + virtual Value Evaluate(const SheetInterface& sheet) const = 0; + + /** + * @brief Returns the expression that describes the formula. + * + * The returned expression does not contain any whitespace or unnecessary parentheses. + * + * @return The expression representing the formula. + */ + virtual std::string GetExpression() const = 0; + + /** + * @brief Returns a list of cells that are directly involved in the evaluation of the formula. + * + * The list is sorted in ascending order and does not contain duplicate cells. + * + * @return A vector of Position objects representing the cells referenced by the formula. + */ + virtual std::vector GetReferencedCells() const = 0; +}; + +/** + * @brief Parses the given expression and returns a unique_ptr to a FormulaInterface object. + * + * @throws FormulaException if the formula is syntactically incorrect. + * + * @param expression The string representation of the formula to parse. + * @return A unique_ptr to a FormulaInterface object representing the parsed formula. + */ +std::unique_ptr ParseFormula(std::string expression); diff --git a/spreadsheet/graph.h b/include/graph.h similarity index 99% rename from spreadsheet/graph.h rename to include/graph.h index 22effbd..b7b675a 100644 --- a/spreadsheet/graph.h +++ b/include/graph.h @@ -364,4 +364,4 @@ namespace graph /* Graph implementation */ { inline bool DependencyGraph::DetectCircularDependency(const VertexId& from, const std::vector& to_refs) const { return forward_graph_.DetectCircularDependency(from, to_refs); } -} \ No newline at end of file +} diff --git a/spreadsheet/ranges.h b/include/ranges.h similarity index 100% rename from spreadsheet/ranges.h rename to include/ranges.h diff --git a/spreadsheet/sheet.h b/include/sheet.h similarity index 100% rename from spreadsheet/sheet.h rename to include/sheet.h diff --git a/include/spreadsheet/spreadsheet.h b/include/spreadsheet/spreadsheet.h new file mode 100644 index 0000000..0f88a9c --- /dev/null +++ b/include/spreadsheet/spreadsheet.h @@ -0,0 +1,4 @@ +#pragma once + +#include "../graph.h" +#include "../sheet.h" diff --git a/spreadsheet/CMakeLists.txt b/spreadsheet/CMakeLists.txt deleted file mode 100644 index 01f6b4e..0000000 --- a/spreadsheet/CMakeLists.txt +++ /dev/null @@ -1,30 +0,0 @@ -cmake_minimum_required(VERSION 3.24) -project(spreadsheet) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - -if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /JMC") -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-implicit-fallthrough") -endif() - -# Find Java -find_package(Java REQUIRED) - -# Include the ANTLR setup -include(cmake/SetupAntlr.cmake) - -# Include the source files and create targets -include(cmake/SetupTargets.cmake) - -# Install target for spreadsheet -install( - TARGETS spreadsheet - DESTINATION bin - EXPORT libspreadsheet -) - -set_directory_properties(PROPERTIES VS_STARTUP_PROJECT spreadsheet) diff --git a/spreadsheet/common.h b/spreadsheet/common.h deleted file mode 100644 index c3bf14d..0000000 --- a/spreadsheet/common.h +++ /dev/null @@ -1,149 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -// Позиция ячейки. Индексация с нуля. -struct Position { - int row = 0; - int col = 0; - - bool operator==(Position rhs) const; - bool operator<(Position rhs) const; - - bool IsValid() const; - std::string ToString() const; - - static Position FromString(std::string_view str); - - static const int MAX_ROWS = 16384; - static const int MAX_COLS = 16384; - static const Position NONE; -}; - -struct Size { - int rows = 0; - int cols = 0; - - bool operator==(Size rhs) const; -}; - -// Описывает ошибки, которые могут возникнуть при вычислении формулы. -class FormulaError { -public: - enum class Category { - Ref, // ссылка на ячейку с некорректной позицией - Value, // ячейка не может быть трактована как число - Div0, // в результате вычисления возникло деление на ноль - }; - - FormulaError(Category category); - - Category GetCategory() const; - - bool operator==(FormulaError rhs) const; - - std::string_view ToString() const; - -private: - Category category_; -}; - -std::ostream& operator<<(std::ostream& output, FormulaError fe); - -// Исключение, выбрасываемое при попытке передать в метод некорректную позицию -class InvalidPositionException : public std::out_of_range { -public: - using std::out_of_range::out_of_range; -}; - -// Исключение, выбрасываемое при попытке задать синтаксически некорректную -// формулу -class FormulaException : public std::runtime_error { -public: - using std::runtime_error::runtime_error; -}; - -// Исключение, выбрасываемое при попытке задать формулу, которая приводит к -// циклической зависимости между ячейками -class CircularDependencyException : public std::runtime_error { -public: - using std::runtime_error::runtime_error; -}; - -class CellInterface { -public: - // Либо текст ячейки, либо значение формулы, либо сообщение об ошибке из - // формулы - using Value = std::variant; - - virtual ~CellInterface() = default; - - // Возвращает видимое значение ячейки. - // В случае текстовой ячейки это её текст (без экранирующих символов). В - // случае формулы - числовое значение формулы или сообщение об ошибке. - virtual Value GetValue() const = 0; - // Возвращает внутренний текст ячейки, как если бы мы начали её - // редактирование. В случае текстовой ячейки это её текст (возможно, - // содержащий экранирующие символы). В случае формулы - её выражение. - virtual std::string GetText() const = 0; - - // Возвращает список ячеек, которые непосредственно задействованы в данной - // формуле. Список отсортирован по возрастанию и не содержит повторяющихся - // ячеек. В случае текстовой ячейки список пуст. - virtual std::vector GetReferencedCells() const = 0; -}; - -inline constexpr char FORMULA_SIGN = '='; -inline constexpr char ESCAPE_SIGN = '\''; - -// Интерфейс таблицы -class SheetInterface { -public: - virtual ~SheetInterface() = default; - - // Задаёт содержимое ячейки. Если текст начинается со знака "=", то он - // интерпретируется как формула. Если задаётся синтаксически некорректная - // формула, то бросается исключение FormulaException и значение ячейки не - // изменяется. Если задаётся формула, которая приводит к циклической - // зависимости (в частности, если формула использует текущую ячейку), то - // бросается исключение CircularDependencyException и значение ячейки не - // изменяется. - // Уточнения по записи формулы: - // * Если текст содержит только символ "=" и больше ничего, то он не считается - // формулой - // * Если текст начинается с символа "'" (апостроф), то при выводе значения - // ячейки методом GetValue() он опускается. Можно использовать, если нужно - // начать текст со знака "=", но чтобы он не интерпретировался как формула. - virtual void SetCell(Position pos, std::string text) = 0; - - // Возвращает значение ячейки. - // Если ячейка пуста, может вернуть nullptr. - virtual const CellInterface* GetCell(Position pos) const = 0; - virtual CellInterface* GetCell(Position pos) = 0; - - // Очищает ячейку. - // Последующий вызов GetCell() для этой ячейки вернёт либо nullptr, либо - // объект с пустым текстом. - virtual void ClearCell(Position pos) = 0; - - // Вычисляет размер области, которая участвует в печати. - // Определяется как ограничивающий прямоугольник всех ячеек с непустым - // текстом. - virtual Size GetPrintableSize() const = 0; - - // Выводит всю таблицу в переданный поток. Столбцы разделяются знаком - // табуляции. После каждой строки выводится символ перевода строки. Для - // преобразования ячеек в строку используются методы GetValue() или GetText() - // соответственно. Пустая ячейка представляется пустой строкой в любом случае. - virtual void PrintValues(std::ostream& output) const = 0; - virtual void PrintTexts(std::ostream& output) const = 0; -}; - -// Создаёт готовую к работе пустую таблицу. -std::unique_ptr CreateSheet(); \ No newline at end of file diff --git a/spreadsheet/formula.h b/spreadsheet/formula.h deleted file mode 100644 index 4db2d85..0000000 --- a/spreadsheet/formula.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -#include "common.h" - -// Формула, позволяющая вычислять и обновлять арифметическое выражение. -// Поддерживаемые возможности: -// * Простые бинарные операции и числа, скобки: 1+2*3, 2.5*(2+3.5/7) -// * Значения ячеек в качестве переменных: A1+B2*C3 -// Ячейки, указанные в формуле, могут быть как формулами, так и текстом. Если это -// текст, но он представляет число, тогда его нужно трактовать как число. Пустая -// ячейка или ячейка с пустым текстом трактуется как число ноль. -class FormulaInterface { -public: - using Value = std::variant; - - virtual ~FormulaInterface() = default; - - // Возвращает вычисленное значение формулы для переданного листа либо ошибку. - // Если вычисление какой-то из указанных в формуле ячеек приводит к ошибке, то - // возвращается именно эта ошибка. Если таких ошибок несколько, возвращается - // любая. - virtual Value Evaluate(const SheetInterface& sheet) const = 0; - - // Возвращает выражение, которое описывает формулу. - // Не содержит пробелов и лишних скобок. - virtual std::string GetExpression() const = 0; - - // Возвращает список ячеек, которые непосредственно задействованы в вычислении - // формулы. Список отсортирован по возрастанию и не содержит повторяющихся - // ячеек. - virtual std::vector GetReferencedCells() const = 0; -}; - -// Парсит переданное выражение и возвращает объект формулы. -// Бросает FormulaException в случае, если формула синтаксически некорректна. -std::unique_ptr ParseFormula(std::string expression); \ No newline at end of file diff --git a/spreadsheet/FormulaAST.cpp b/src/FormulaAST.cpp similarity index 100% rename from spreadsheet/FormulaAST.cpp rename to src/FormulaAST.cpp diff --git a/src/antlr/grammar/Formula.g4 b/src/antlr/grammar/Formula.g4 new file mode 100644 index 0000000..81e4626 --- /dev/null +++ b/src/antlr/grammar/Formula.g4 @@ -0,0 +1,30 @@ +grammar Formula; + +main + : expr EOF + ; + +expr + : '(' expr ')' # Parens + | (ADD | SUB) expr # UnaryOp + | expr (MUL | DIV) expr # BinaryOp + | expr (ADD | SUB) expr # BinaryOp + | CELL # Cell + | NUMBER # Literal + ; + +// number literals cannot be signed, or else 1-2 would be lexed as [1] [-2] +fragment INT: [-+]? UINT ; +fragment UINT: [0-9]+ ; +fragment EXPONENT: [eE] INT; +NUMBER + : UINT EXPONENT? + | UINT? '.' UINT EXPONENT? + ; + +ADD: '+' ; +SUB: '-' ; +MUL: '*' ; +DIV: '/' ; +CELL: [A-Z]+[0-9]+ ; +WS: [ \t\n\r]+ -> skip ; \ No newline at end of file diff --git a/spreadsheet/cell.cpp b/src/cell.cpp similarity index 100% rename from spreadsheet/cell.cpp rename to src/cell.cpp diff --git a/spreadsheet/formula.cpp b/src/formula.cpp similarity index 100% rename from spreadsheet/formula.cpp rename to src/formula.cpp diff --git a/spreadsheet/main.cpp b/src/main.cpp similarity index 99% rename from spreadsheet/main.cpp rename to src/main.cpp index eea66ff..3b7e688 100644 --- a/spreadsheet/main.cpp +++ b/src/main.cpp @@ -2,10 +2,8 @@ #include #include -#include "common.h" -#include "graph.h" -#include "sheet.h" -#include "test_runner_p.h" +#include "spreadsheet.h" +#include "test_utils/test_runner_p.h" inline std::ostream& operator<<(std::ostream& output, Position pos) { return output << "(" << pos.row << ", " << pos.col << ")"; diff --git a/spreadsheet/sheet.cpp b/src/sheet.cpp similarity index 100% rename from spreadsheet/sheet.cpp rename to src/sheet.cpp diff --git a/spreadsheet/structures.cpp b/src/structures.cpp similarity index 100% rename from spreadsheet/structures.cpp rename to src/structures.cpp diff --git a/spreadsheet/test_runner_p.h b/src/test_utils/test_runner_p.h similarity index 100% rename from spreadsheet/test_runner_p.h rename to src/test_utils/test_runner_p.h