diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..7a670013a09c963c460d628013ae363317d78fb7 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,53 @@ +name: Cpp + +on: + push: + branches: [ "dev", "main" ] + pull_request: + branches: [ "dev", "main" ] + +defaults: + run: + shell: bash + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Installing + run: | + sudo apt update + git clone https://github.com/google/googletest.git -b release-1.11.0 + cd googletest + mkdir build + cd build + cmake .. -DBUILD_GMOCK=OFF + make + sudo make install + cd ../.. + - name: Building + run: | + mkdir build + cd build + cmake .. + make + # - name: Testing + # run: | + # ./build/tests/test_parser + linters: + runs-on: ubuntu-latest + needs: [build] + steps: + - uses: actions/checkout@v3 + - name: Installing + run: | + sudo apt update + sudo apt install cppcheck + pip install cpplint + - name: Cppcheck + run: | + cppcheck --language=c++ ./src/main.cpp + - name: Cpplint + run: | + cpplint --filter=-legal/copyright,-readability/casting,-whitespace/tab,-build/include_subdir --linelength=110 ./src/main.cpp \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..870ca9cbdc136f4823abee3dce85671fc5ccd26c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/cmake-build-debug/ +/build/ +.vscode +.idea +CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..bdb1810c7b391a75e4698cda543484a29daccb31 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +set(CMAKE_CXX_STANDARD 17) +cmake_minimum_required(VERSION 3.19) +set(CMAKE_PREFIX_PATH build) +project(SourcedOut CXX) +find_package(antlr4-runtime REQUIRED) +find_package(Boost 1.8.1 REQUIRED) +find_package(libpqxx REQUIRED) +find_package(GTest REQUIRED) +message(STATUS ${Boost_LIBRARIES}) +add_executable(${PROJECT_NAME} src/main.cpp) +#add_executable(${PROJECT_NAME} text-basic-metrics/tbm_main.cpp text-basic-metrics/tbm_main.cpp) строка для запуска моей части в text-basic-metrics +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${antlr4-runtime_LIBRARIES} ${libpqxx_LIBRARIES} ${GTest_LIBRARIES}) \ No newline at end of file diff --git a/conanfile.txt b/conanfile.txt new file mode 100644 index 0000000000000000000000000000000000000000..18ee8ce4289f4e6832e3384009f500ec0562d3b4 --- /dev/null +++ b/conanfile.txt @@ -0,0 +1,10 @@ +[requires] +boost/1.81.0 +antlr4-cppruntime/4.12.0 +libpqxx/7.7.5 +gtest/cci.20210126 + + +[generators] +CMakeDeps +CMakeToolchain \ No newline at end of file diff --git a/config.sh b/config.sh new file mode 100644 index 0000000000000000000000000000000000000000..b05efd594bca48b52508091471581bbdcc2e89b6 --- /dev/null +++ b/config.sh @@ -0,0 +1,8 @@ +pip install conan +conan profile detect --force +#------------- +mkdir "build" +cd build/ +#------------ +conan install . --build=missing -s build_type=Debug +cmake .. -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a222488a58895354eb7cb45f7d88ebc36a23a799 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,321 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +using tcp = boost::asio::ip::tcp; // from + +//------------------------------------------------------------------------------ + +// Return a reasonable mime type based on the extension of a file. +beast::string_view +mime_type(beast::string_view path) +{ + using beast::iequals; + auto const ext = [&path] + { + auto const pos = path.rfind("."); + if(pos == beast::string_view::npos) + return beast::string_view{}; + return path.substr(pos); + }(); + if(iequals(ext, ".htm")) return "text/html"; + if(iequals(ext, ".html")) return "text/html"; + if(iequals(ext, ".php")) return "text/html"; + if(iequals(ext, ".css")) return "text/css"; + if(iequals(ext, ".txt")) return "text/plain"; + if(iequals(ext, ".js")) return "application/javascript"; + if(iequals(ext, ".json")) return "application/json"; + if(iequals(ext, ".xml")) return "application/xml"; + if(iequals(ext, ".swf")) return "application/x-shockwave-flash"; + if(iequals(ext, ".flv")) return "video/x-flv"; + if(iequals(ext, ".png")) return "image/png"; + if(iequals(ext, ".jpe")) return "image/jpeg"; + if(iequals(ext, ".jpeg")) return "image/jpeg"; + if(iequals(ext, ".jpg")) return "image/jpeg"; + if(iequals(ext, ".gif")) return "image/gif"; + if(iequals(ext, ".bmp")) return "image/bmp"; + if(iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; + if(iequals(ext, ".tiff")) return "image/tiff"; + if(iequals(ext, ".tif")) return "image/tiff"; + if(iequals(ext, ".svg")) return "image/svg+xml"; + if(iequals(ext, ".svgz")) return "image/svg+xml"; + return "application/text"; +} + +// Append an HTTP rel-path to a local filesystem path. +// The returned path is normalized for the platform. +std::string +path_cat( + beast::string_view base, + beast::string_view path) +{ + if (base.empty()) + return std::string(path); + std::string result(base); +#ifdef BOOST_MSVC + char constexpr path_separator = '\\'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); + for(auto& c : result) + if(c == '/') + c = path_separator; +#else + char constexpr path_separator = '/'; + if(result.back() == path_separator) + result.resize(result.size() - 1); + result.append(path.data(), path.size()); +#endif + return result; +} + +// This function produces an HTTP response for the given +// request. The type of the response object depends on the +// contents of the request, so the interface requires the +// caller to pass a generic lambda for receiving the response. +template< + class Body, class Allocator, + class Send> +void +handle_request( + beast::string_view doc_root, + http::request>&& req, + Send&& send) +{ + // Returns a bad request response + auto const bad_request = + [&req](beast::string_view why) + { + http::response res{http::status::bad_request, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = std::string(why); + res.prepare_payload(); + return res; + }; + + // Returns a not found response + auto const not_found = + [&req](beast::string_view target) + { + http::response res{http::status::not_found, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "The resource '" + std::string(target) + "' was not found."; + res.prepare_payload(); + return res; + }; + + // Returns a server error response + auto const server_error = + [&req](beast::string_view what) + { + http::response res{http::status::internal_server_error, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, "text/html"); + res.keep_alive(req.keep_alive()); + res.body() = "An error occurred: '" + std::string(what) + "'"; + res.prepare_payload(); + return res; + }; + + // Make sure we can handle the method + if( req.method() != http::verb::get && + req.method() != http::verb::head) + return send(bad_request("Unknown HTTP-method")); + + // Request path must be absolute and not contain "..". + if( req.target().empty() || + req.target()[0] != '/' || + req.target().find("..") != beast::string_view::npos) + return send(bad_request("Illegal request-target")); + + // Build the path to the requested file + std::string path = path_cat(doc_root, req.target()); + if(req.target().back() == '/') + path.append("index.html"); + + // Attempt to open the file + beast::error_code ec; + http::file_body::value_type body; + body.open(path.c_str(), beast::file_mode::scan, ec); + + // Handle the case where the file doesn't exist + if(ec == beast::errc::no_such_file_or_directory) + return send(not_found(req.target())); + + // Handle an unknown error + if(ec) + return send(server_error(ec.message())); + + // Cache the size since we need it after the move + auto const size = body.size(); + + // Respond to HEAD request + if(req.method() == http::verb::head) + { + http::response res{http::status::ok, req.version()}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); + } + + // Respond to GET request + http::response res{ + std::piecewise_construct, + std::make_tuple(std::move(body)), + std::make_tuple(http::status::ok, req.version())}; + res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::content_type, mime_type(path)); + res.content_length(size); + res.keep_alive(req.keep_alive()); + return send(std::move(res)); +} + +//------------------------------------------------------------------------------ + +// Report a failure +void +fail(beast::error_code ec, char const* what) +{ + std::cerr << what << ": " << ec.message() << "\n"; +} + +// This is the C++11 equivalent of a generic lambda. +// The function object is used to send an HTTP message. +template +struct send_lambda +{ + Stream& stream_; + bool& close_; + beast::error_code& ec_; + + explicit + send_lambda( + Stream& stream, + bool& close, + beast::error_code& ec) + : stream_(stream) + , close_(close) + , ec_(ec) + { + } + + template + void + operator()(http::message&& msg) const + { + // Determine if we should close the connection after + close_ = msg.need_eof(); + + // We need the serializer here because the serializer requires + // a non-const file_body, and the message oriented version of + // http::write only works with const messages. + http::serializer sr{msg}; + http::write(stream_, sr, ec_); + } +}; + +// Handles an HTTP server connection +void +do_session( + tcp::socket& socket, + std::shared_ptr const& doc_root) +{ + bool close = false; + beast::error_code ec; + + // This buffer is required to persist across reads + beast::flat_buffer buffer; + + // This lambda is used to send messages + send_lambda lambda{socket, close, ec}; + + for(;;) + { + // Read a request + http::request req; + http::read(socket, buffer, req, ec); + if(ec == http::error::end_of_stream) + break; + if(ec) + return fail(ec, "read"); + + // Send the response + handle_request(*doc_root, std::move(req), lambda); + if(ec) + return fail(ec, "write"); + if(close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + break; + } + } + + // Send a TCP shutdown + socket.shutdown(tcp::socket::shutdown_send, ec); + + // At this point the connection is closed gracefully +} + +//------------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + try + { + // Check command line arguments. + if (argc != 4) + { + std::cerr << + "Usage: http-server-sync
\n" << + "Example:\n" << + " http-server-sync 0.0.0.0 8080 .\n"; + return EXIT_FAILURE; + } + auto const address = net::ip::make_address(argv[1]); + auto const port = static_cast(std::atoi(argv[2])); + auto const doc_root = std::make_shared(argv[3]); + + // The io_context is required for all I/O + net::io_context ioc{1}; + + // The acceptor receives incoming connections + tcp::acceptor acceptor{ioc, {address, port}}; + for(;;) + { + // This will receive the new connection + tcp::socket socket{ioc}; + + // Block until we get a connection + acceptor.accept(socket); + + // Launch the session, transferring ownership of the socket + std::thread{std::bind( + &do_session, + std::move(socket), + doc_root)}.detach(); + } + } + catch (const std::exception& e) + { + std::cerr << "Error: " << e.what() << std::endl; + return EXIT_FAILURE; + } +} \ No newline at end of file diff --git a/text-basic-metrics/code1.txt b/text-basic-metrics/code1.txt new file mode 100644 index 0000000000000000000000000000000000000000..5fa95b8f8462b43d7b70943fc4b1f5d124dec881 --- /dev/null +++ b/text-basic-metrics/code1.txt @@ -0,0 +1,29 @@ +// однострочный комментарий +// еще один +// вау еще один + +#include +#include +#include + +using namespace std; + +/* многострочный комм + * // внутри него однострочный + * + */ + + +int main() { + stringstream ss; + string res; + // ещё в код напихаю комментов + ss << "a bwfw ce "; + while(getline(ss, res, ' ')){ //комментарий после строки с кодом + /* + * летс гоу + * худшее место для многострочного коммента + */ + cout << res << endl; /* многострочный однострочно */ + } +} diff --git a/text-basic-metrics/code2.txt b/text-basic-metrics/code2.txt new file mode 100644 index 0000000000000000000000000000000000000000..2115b76e6866f8af7e14dbd7de1ed6fe3a2c9ec2 --- /dev/null +++ b/text-basic-metrics/code2.txt @@ -0,0 +1,29 @@ +// однострочный комментарий +// еще один +// вау еще один + +#include +#include +#include + +using namespace std; + +/* многострочный комм + * // внутри него однострочный + * + */ + + +int main() { + stringstream ss1; + string res1; + // ещё в код напихаю комментов + ss1 << "a bwfw ce "; + while(getline(ss, res1, ' ')){ //комментарий после строки с кодом + /* + * летс гоу + * худшее место для многострочного коммента + */ + cout << res1 << endl; /* многострочный однострочно */ + } +} \ No newline at end of file diff --git a/text-basic-metrics/tbm_main.cpp b/text-basic-metrics/tbm_main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fdd4253768b6369af5fe434526e26cbb9fc30bcd --- /dev/null +++ b/text-basic-metrics/tbm_main.cpp @@ -0,0 +1,153 @@ +// +// Created by march on 21.04.2023. +// + +#include +#include +#include +#include +#include +#include +#include + +#include + + +std::string deleteComms(const std::string& text){ + std::string modif; + std::string res; + + std::stringstream ss; + std::string line; + + ss << text; + + while(getline(ss, line)){ + line.pop_back(); + line.push_back('\0'); + modif += line; + } + + bool s_comm = false; + bool m_comm = false; + + for (int i = 0; i < modif.size(); i++){ + if (s_comm && modif[i] == '\0') + s_comm = false; + else if (m_comm && modif[i] == '*' && modif[i + 1] == '/') + m_comm = false, i++; + else if (s_comm || m_comm) + continue; + else if (modif[i] == '/' && modif[i+1] == '/') + s_comm = true, i++; + else if (modif[i] == '/' && modif[i+1] == '*') + m_comm = true, i++; + + else if (modif[i] != '\0') + res += modif[i]; + else{ + res += '\n'; + } + } + return res; +} + +std::vector tbm_tokenizer(const std::string &text){ + boost::char_separator sep(" {}();,\"\0\'"); + std::vector res; + boost::tokenizer < boost::char_separator > tokens(text, sep); + + for (const std::string &s: tokens) { + if (!s.empty() && s[0] != '\n' && s[0] != '\0'){ + res.push_back(s); + } + } + return res; +} + +// % = intersection(A, B) / union(A, B) +double Jaccard_metric(const std::vector & tokens1, const std::vector & tokens2){ + std::set s1; + std::set s2; + + for (auto &i : tokens1) s1.insert(i); + for (auto &i : tokens2) s2.insert(i); + + + std::set intersect_sets; + set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), + std::inserter(intersect_sets, intersect_sets.begin())); + + std::set union_sets; + set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), + std::inserter(union_sets, union_sets.begin())); + + std::cout << intersect_sets.size() << " " << union_sets.size() << std::endl; + + return static_cast (intersect_sets.size()) / static_cast (union_sets.size()); +} + +double Livenstain_dist(std::vector tokens1, std::vector tokens2){ + unsigned long n = tokens1.size(); + unsigned long m = tokens2.size(); + int x, y, z; + + std::vector > lev (n, std::vector (m, 0)); + + for (int i = 0; i < n; i++){ + for (int j = 0; j < m; j++){ + if (std::min(i, j) == 0){ + lev[i][j] = std::max(i, j); + } + else{ + x = lev[i-1][j]; + y = lev[i][j-1]; + z = lev[i-1][j-1]; + lev[i][j] = std::min(x, std::min(y, z)); + if (tokens1[i] != tokens2[j]){ + lev[i][j]++; + } + } + } + } + + return lev[n-1][m-1]; +} + +std::pair textCompare(std::istream& fin1, std::istream& fin2){ + std::string line; + + std::string text1( (std::istreambuf_iterator(fin1) ), + (std::istreambuf_iterator() ) ); + + std::string text2( (std::istreambuf_iterator(fin2) ), + (std::istreambuf_iterator() ) ); + + std::string non_comm_text1 = deleteComms(text1); + std::string non_comm_text2 = deleteComms(text2); + + std::vector tokens1 = tbm_tokenizer(non_comm_text1); + std::vector tokens2 = tbm_tokenizer(non_comm_text2); + + double res1 = Jaccard_metric(tokens1, tokens2); + double res2 = 1 - Livenstain_dist(tokens1, tokens2) / std::max(tokens1.size(), tokens2.size()); + + return {res1, res2}; +} + +int main(){ + + std::ifstream fin1; + fin1.open("text-basic-metrics/code1.txt"); + assert(fin1.is_open()); + + std::ifstream fin2; + fin2.open("text-basic-metrics/code2.txt"); + assert(fin2.is_open()); + + std::pair metrics_res = textCompare(fin1, fin2); + + std::cout << "Jaccard metric "<< metrics_res.first << "\nLivenstein distance: " << metrics_res.second; + fin1.close(); + fin2.close(); +} \ No newline at end of file