From 64b35f398776d4404d815edd0ab5d77e75899e90 Mon Sep 17 00:00:00 2001 From: Kevin Lu Date: Sun, 28 May 2023 20:45:04 -0600 Subject: [PATCH] add proper argparsing; fix small bug when using multiple uses --- CMakeLists.txt | 10 ++++- CONTRIBUTING.md | 8 ++-- README.md | 10 +++-- src/cpcc/utils/macros | 4 +- src/main.cpp | 99 ++++++++++++++++++++++++------------------- src/warn.cpp | 11 +++-- todo | 7 ++- 7 files changed, 87 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 335d3ed..2dcd1b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.10) -project(CPCC VERSION 0.1.0) +project(CPCC VERSION 0.2.0) add_definitions("-DCPCC_VER=\"${PROJECT_VERSION}\"") add_definitions("-DCPCC_INCLUDE_DIR=\"/usr/local/include/cpcc/\"") @@ -9,5 +9,11 @@ add_executable(cpcc src/main.cpp src/warn.cpp) target_compile_options(cpcc PRIVATE -O3 -std=c++20) +find_package(Boost 1.75.0 REQUIRED COMPONENTS system) +target_link_libraries(cpcc PRIVATE Boost::system + -lboost_system + -lboost_program_options +) + install(TARGETS cpcc DESTINATION bin) -install(DIRECTORY src/cpcc/ DESTINATION /usr/local/include/cpcc) \ No newline at end of file +install(DIRECTORY src/cpcc/ DESTINATION /usr/local/include/cpcc) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3a61e1..7f42dd1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,8 +12,8 @@ We are mainly looking for contributions to utility parts of CPCC - they should b ## Guidelines -1. Code inside of the files should not be offensive - they should not contain offensive language, variable names, etc. -2. Parts of the code where the user is welcome to modify should be commented as such: e.g. `// subtract 1 if index is 1-indexed` -3. Before writing your code, take a look at `src/crucial/base/` to see the base code: you can use the macros provided there to make your code more readable +1. Code inside of the files should not contain offensive language. +2. Parts of the code where the user is welcome to modify should be commented as such: e.g. `// subtract 1 if it is 1-indexed`. +3. Before writing your code, take a look at `src/crucial/base/` to see the base code: you can use the macros provided there to make your code more readable. -Check out the [todo](https://github.com/kevlu8/CPCC/blob/master/todo) for a list of things that need to be done. \ No newline at end of file +Check out the [todo](https://github.com/kevlu8/CPCC/blob/master/todo) for a list of things that need to be done. diff --git a/README.md b/README.md index a308af8..fc82a42 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ The competitive programmer's C++ translator. `use`s must be before all other code in CP files: ```cpp +use macros; use factorial; int main() { @@ -20,6 +21,7 @@ will work, but ```cpp #include +use macros; use factorial; int main() { @@ -33,13 +35,15 @@ CPC is virtually identical to C++, except for having `use` libraries. To use a library, simply put `use ;` at the beginning of your code. -To compile your CP program, run `cpcc file.cpc`. +To compile your CPC program, run `cpcc file.cpc`. If you would not like the credits comments at the beginning of your code, simplify run `cpcc --nocredits file.cpc`. +If you would not like warnings to be printed, simply run `cpcc --Wnone file.cpc`. + ### Installing -As CPCC depends on its libraries, it is **highly recommended** that you build from source as opposed to installing a prebuilt version. +As CPCC depends on its libraries, it is **highly recommended** that you build from source as opposed to installing a prebuilt version. You may need to install Boost for CPCC to work. 1. Clone the repository: @@ -73,4 +77,4 @@ make -j4 sudo make install ``` -You can now use CPCC! Run `cpcc --version` to check if it is installed properly. \ No newline at end of file +You can now use CPCC! Run `cpcc --version` to check if it is installed properly. diff --git a/src/cpcc/utils/macros b/src/cpcc/utils/macros index aa494ff..266a128 100644 --- a/src/cpcc/utils/macros +++ b/src/cpcc/utils/macros @@ -7,8 +7,8 @@ #define debug(x) cout << #x << " = " << x << endl; #define indexed_set tree, rb_tree_tag, tree_order_statistics_node_update> #define fastio() cin.tie(0)->sync_with_stdio(0) -#define printx(x) { cout << (x) << '\n'; exit(0); } -#define inputvector(x, n) x.resize(n); forj(n) { cin >> x[j]; } +#define printx(x) do { cout << (x) << '\n'; exit(0); } while (0) +#define inputvector(x, n) x.resize(n); forj(n) cin >> x[j] #define hashtable gp_hash_table #define all(x) x.begin(), x.end() #define rall(x) x.rbegin(), x.rend() diff --git a/src/main.cpp b/src/main.cpp index 6e21608..54d2a1c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,13 +1,19 @@ +#include #include #include #include #include #include -#include -#include + +#include #include "warn.hpp" +#ifndef CPCC_VER // Should only happen if compiled improperly +#define CPCC_VER "ERR" +#define CPCC_INCLUDE_DIR "ERR" +#endif + void ffprint(std::string path, std::ofstream *out) { std::ifstream f(path); if (!f.is_open()) { @@ -21,13 +27,33 @@ void ffprint(std::string path, std::ofstream *out) { f.close(); } -bool handle_args(char *arg) { - bool credit = true; - if (strcmp(arg, "version") == 0) { - std::cout << "CPCC version " << CPCC_VER << std::endl; - exit(0); +int main(int argc, char *argv[]) { + boost::program_options::options_description desc("CPCC usage"); + desc.add_options() + ("help", "Prints this message") + ("version", "Prints the version of CPCC you are using") + ("libs", "Prints available CPCC libraries") + ("nocredit", "Compiles the file without the credits at the beginning") + ("Wnone", "Disables all warnings") + ("o", boost::program_options::value(), "The output file (out.cpp by default)") + ("file", boost::program_options::value(), "The file to compile (using --file is optional)"); + + boost::program_options::positional_options_description p; + p.add("file", -1); + + boost::program_options::variables_map vm; + boost::program_options::store(boost::program_options::command_line_parser(argc, argv).options(desc).positional(p).run(), vm); + boost::program_options::notify(vm); + + if (vm.count("help") || argc < 2) { + std::cout << desc << std::endl; + return argc < 2; } - else if (strcmp(arg, "libs") == 0) { + if (vm.count("version")) { + std::cout << "CPCC version " << CPCC_VER << " with include directory " << CPCC_INCLUDE_DIR << std::endl; + return 0; + } + if (vm.count("libs")) { std::cout << "Available libraries: "; for (const auto &entry : std::filesystem::directory_iterator(std::string(CPCC_INCLUDE_DIR) + "utils")) { std::string file = entry.path().filename().string(); @@ -35,48 +61,30 @@ bool handle_args(char *arg) { std::cout << file << ' '; } std::cout << std::endl; - exit(0); - } - else if (strcmp(arg, "nocredit") == 0) { - credit = false; - } - else { - std::cerr << "Invalid argument " << arg << " supplied!" << std::endl; - exit(1); + return 0; } - return credit; -} -int main(int argc, char *argv[]) { - if (argc < 2 || strcmp(argv[1], "--help") == 0) { - std::cout << "usage: " << argv[0] << " [--help] [--version] [--libs] [--nocredit] \n" - << "arguments:\n" - << " --help Prints this message\n" - << " --version Prints the version of CPCC you are using\n" - << " --libs Prints available CPCC libraries\n" - << " --nocredit Compiles the file without the credits at the beginning\n" - << std::flush; - - if (argc < 2) - return 1; - else - return 0; + std::string outfile = "out.cpp"; + if (vm.count("o")) { + outfile = vm["o"].as(); } - - long long timer = clock(); bool credit = true; + if (vm.count("nocredit")) { + credit = false; + } - if (strncmp(argv[1], "--", 2) == 0) - credit = handle_args(argv[1]+2); + std::string infile = vm["file"].as(); + + long long timer = clock(); - std::ifstream in(argv[argc-1]); + std::ifstream in(infile); if (!in.is_open()) { - std::cerr << "Invalid file " << argv[argc-1] << " supplied!" << std::endl; + std::cerr << "Invalid file " << infile << " supplied!" << std::endl; return 1; } - std::ofstream out("out.cpp"); + std::ofstream out(outfile); if (!out.is_open()) { std::cerr << "Could not open result file!" << std::endl; return 1; @@ -108,6 +116,7 @@ int main(int argc, char *argv[]) { return 1; } ffprint(path, &out); + out << '\n'; continue; } if (end_imports == -1) { @@ -120,21 +129,23 @@ int main(int argc, char *argv[]) { lines.pop_front(); } - out << '\n'; + while (lines.front().size() == 0) { + lines.pop_front(); + } std::string main_buf; main_buf.reserve(65536); - for (int i = end_imports; i < lines.size(); i++) { + for (int i = 0; i < lines.size(); i++) { out << lines[i] << '\n'; main_buf += lines[i] + '\n'; } - Warnings::analyze(main_buf, lines); + if (!vm.count("Wnone")) + Warnings::analyze(main_buf, lines); out.close(); - long long timer2 = clock(); - std::cout << "\033[1;32mSuccess!\033[0m Compiled in " << (timer2 - timer) / (double)CLOCKS_PER_SEC * 1000 << " ms." << std::endl; + std::cout << "\033[1;32mSuccess!\033[0m Compiled in " << (clock() - timer) / (double)CLOCKS_PER_SEC * 1000 << " ms." << std::endl; return 0; } diff --git a/src/warn.cpp b/src/warn.cpp index 22d5f3c..ed364cd 100644 --- a/src/warn.cpp +++ b/src/warn.cpp @@ -23,6 +23,11 @@ void Warnings::analyze(std::string &code, std::deque &lines) { std::cout << "\033[1;33mWarning: \033[0m" << "Using `std::` is not recommended, as `using namespace std;` " << "is already included." << std::endl; } + // check for use of unordered set/map + if (code.find("unordered_set") != std::string::npos || code.find("unordered_map") != std::string::npos) { + std::cout << "\033[1;33mWarning: \033[0m" << "Using `unordered_set` or `unordered_map` is not recommended, " + << "as the worst case time complexity can be O(n). Instead, use `set` or `map`." << std::endl; + } int depth = 0; std::vector> functions; @@ -35,7 +40,7 @@ void Warnings::analyze(std::string &code, std::deque &lines) { depth++; flag = true; } - if (lines[i].find('}') != std::string::npos) { + if (lines[i].find('}') != std::string::npos) { // mega scuffed depth--; flag = true; } @@ -70,7 +75,7 @@ void Warnings::analyze(std::string &code, std::deque &lines) { } if (functions[i][j].find('}') != std::string::npos) { depth--; - if (fors.find(depth + 1) != fors.end()) { + if (fors.count(depth + 1)) { exp_n = std::max(exp_n, (int)fors.size()); fors.erase(depth + 1); } @@ -80,4 +85,4 @@ void Warnings::analyze(std::string &code, std::deque &lines) { else if (exp_n == 1) std::cout << name << " O(n)" << std::endl; else std::cout << name << " O(n^" << exp_n << ")" << std::endl; } -} \ No newline at end of file +} diff --git a/todo b/todo index 8170d6d..c1bd4cf 100644 --- a/todo +++ b/todo @@ -1,9 +1,8 @@ - un-hardcode include directory for cmake -- have proper argument parsing - display stats after compilation - - time taken - **estimated time complexity of code** - - output time complexity for every function (beta) + - output time complexity for every function - add compiler warnings for: - running complete search on a sorted array - - lower_bound, upper_bound on an unsorted array \ No newline at end of file + - lower_bound, upper_bound on an unsorted array + - other bad practices in competitive programming