From 6603f96d69a130d9f0b5061420e27571bf01f204 Mon Sep 17 00:00:00 2001 From: John Breaux Date: Sat, 23 Apr 2022 04:47:55 -0500 Subject: [PATCH] Refactor for robustness and clarity --- README.md | 2 +- inc/graph.hpp | 13 ++---- src/graph.cpp | 6 +-- src/main.cpp | 7 ++- src/read_input.cpp | 112 +++++++++++++++++++++++++++------------------ test1.txt | 29 +++++++----- 6 files changed, 97 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index c6b0105..32fc8e0 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,6 @@ Project 2 for CSCE4600, for Team G4 Build with `make` -Run with `make run` +Run with `./main.out filename` Clean with `make clean` diff --git a/inc/graph.hpp b/inc/graph.hpp index dce5491..8fd14b7 100644 --- a/inc/graph.hpp +++ b/inc/graph.hpp @@ -11,9 +11,8 @@ public: graph() {} // Initializers: // Read a graph from a file - void read(std::string filename); - // TODO: generate a random graph - void random(int processes, int resources); + int read(std::string filename); + // check functions: // is the graph... bool reducible();// ? @@ -23,17 +22,13 @@ public: // print the graph void print (); - struct m{ - int x, y; - std::vector> data; - }; + typedef std::vector> matrix; private: int num_processes = 0; int num_resources = 0; std::vector resource_counts; - struct m matrix; // Tell me, Mr. Anderson, what good is a phone call if you are unable to speak? - bool is_blocked(int process_id); + matrix m; // Tell me, Mr. Anderson, what good is a phone call if you are unable to speak? }; #endif \ No newline at end of file diff --git a/src/graph.cpp b/src/graph.cpp index d60a78a..2886333 100644 --- a/src/graph.cpp +++ b/src/graph.cpp @@ -6,7 +6,7 @@ #include "graph.hpp" void graph::print () { - printf("np: %d\tnr: %d\n", num_processes, num_resources); - printf("resource_counts:\n"); for (auto e: resource_counts) printf("%d\t", e); printf("\n"); - printf("matrix:\n"); for (auto x: matrix.data) {for (auto y: x) printf("%d\t", y); printf("\n");} + printf("num_processes: %d\nnum_resources: %d\n", num_processes, num_resources); + printf("resource_counts: ["); for (auto e: resource_counts) printf("%d, ", e); printf("\b\b]\n"); + printf("matrix:\n| "); for (auto x: m) {for (auto y: x) printf("%d, ", y); printf("\b\b |\n| ");} printf("\b\b \b\b"); } diff --git a/src/main.cpp b/src/main.cpp index 8b9b0d2..46f913f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,11 +15,14 @@ int main(int argc, char** argv) { graph g; //TODO: Implement reading from a file - g.read(filename); + int err = g.read(filename); + if (err) { + return err; + } g.print(); // TODO: Implement graph reduction and/or knot detection - printf("Graph is %s\n", g.reducible()?"not reducible (deadlock!)":"reducible (no deadlock!)");//? + printf("Graph is %s\n", g.reducible()?"not reducible! Deadlock!":"reducible! No deadlock!");//? // TODO: Destroy the graph created by read_file return 0; diff --git a/src/read_input.cpp b/src/read_input.cpp index c821689..a612d41 100644 --- a/src/read_input.cpp +++ b/src/read_input.cpp @@ -7,45 +7,51 @@ #include #include "graph.hpp" -#define MAX_INT_LEN 16 - -// Vector of regices, in order of precedence. Earlier entries will be checked before later entries. +//* Vector of regex rules, in order of precedence. Earlier entries will be checked before later entries. std::vector patterns = { - //* Rule 1: A line starting with a % is a comment, and should not be parsed - std::regex("^\\s*(%)"), - //* Rule 2: A line starting with `num_processes=` contains the number of processes - std::regex("^.*(num_processes)\\s*=\\s*(\\d+).*"), - //* Rule 3: A line starting with `num_resources=` contains the number of resources - std::regex("^.*(num_resources)\\s*=\\s*(\\d+)"), - //* Rule 4: A line containing only comma-separated values should be captured completely - std::regex("^([\\-0-9, ]+)") // matches comma-separated space-separated signed decimal integers + //* Rule 1: A line containing % is a comment, and should not be parsed + std::regex("(%)"), + //* Rule 2: A line containing `num_processes` contains the number of processes + std::regex("(num_processes)\\D*(\\d+)"), + //* Rule 3: A line containing `num_resources` contains the number of resources + std::regex("(num_resources)\\D*(\\d+)"), + //* Rule 4: A line containing integers should be saved somewhere + std::regex("(-?[0-9]+.*-?[0-9]+)") // Magical! }; -// convert comma-separated string s to vector -std::vector stovi (const std::string &s); - -// TODO: Implement reading from a file -void graph::read(std::string filename) { - - // Open file with name filename as read-only - std::fstream f; f.open(filename, f.in); - // TODO: Check for file IO errors (I might have a solution for that in another project) - +// Say hi to Stovey, here to bake your cakes. +//* convert string s to vector no matter the cost +std::vector stovi (const std::string &s) { + // Create the number classification rule + std::regex rule("-?[0-9]+"); + // Make a new tokenizing iterator using the rule + std::sregex_token_iterator start{s.begin(), s.end(), rule, 0}, end; + // Match the numbers + std::vector res; + for (auto i = start; i != end; ++i) res.push_back(atoi(i->str().c_str())); + // give the numbers back + return res; +} +//* Read from a file of name `filename` +int graph::read(std::string filename) { + // Open file with name filename as input fstream + std::ifstream f(filename); + // Read the entire file while (!f.eof()) { // acquire a line std::string line; std::getline (f, line); - // Iterate over each pattern, and grab the associated data - for (auto pattern: patterns) { + for (const auto &pattern: patterns) { std::smatch res; + // Capture data with a regex search, using the pattern if (std::regex_search (line, res, pattern)) { // get the pattern type, value std::string type = res.format("$1"), value = res.format("$2"); // Handle the pattern // Rule 1: If line is a comment, ignore it and move on - if (type == "%"); else + if (type == "%") break; // Rule 2: If num_processes= matched, assign value to num_processes if (type == "num_processes") { num_processes = std::stoi(value); } else @@ -53,32 +59,48 @@ void graph::read(std::string filename) { // Rule 3: If num_resources= matched, assign value to num_resources if (type == "num_resources") { num_resources = std::stoi(value); } else - // Rule 4: If this line is a comma-separated list of numbers, - // and this is the first match, assign it to resource counts + // Rule 4: If this line is a comma-separated list of numbers: + // If resource_counts is empty, assume this is the resource counts, + // else it's part of the matrix being reloaded if (!resource_counts.size()) { resource_counts = stovi(type); } else - // and this is a subsequent match, push it onto the matrix - { matrix.data.push_back(stovi(type)); } - - // If a pattern is matched, go to the next line and skip evaluating other patterns + { m.push_back(stovi(type)); } break; } } } - // Close file - f.close(); - // TODO: Check for file IO errors (Shouldn't be any) + //* Test validity of the input file + // Check if all sections are properly defined: + const char errmsg[] = "%s: Definition failed for %s\n"; + int ret = 0; + if (!num_processes){ + printf(errmsg, filename.c_str(), "num_processes"); + ret |= 0b00001; + } + if (!num_resources){ + printf(errmsg, filename.c_str(), "num_resources"); + ret |= 0b00010; + } + if (resource_counts.size() != num_resources){ + printf(errmsg, filename.c_str(), "resource counts"); + ret |= 0b00100; + } + if (m.size() != num_processes + num_resources){ + printf(errmsg, filename.c_str(), "adjacency matrix rows"); + ret |= 0b01000; + } + for (auto i: m) { + if (i.size() != num_processes + num_resources) + ret |= 0b10000; + } + if (ret & 0b10000) { + printf(errmsg, filename.c_str(), "adjacency matrix columns"); + } + if (ret) { + printf("Check to ensure your input file is correct and follows the Rules.\n"); + } - return; -} - -std::vector stovi (const std::string &s) { - // Create the number classifier - std::regex pattern("[0-9]+"); - std::sregex_token_iterator first{s.begin(), s.end(), pattern, 0}, last; - // Match the numbers - std::vector res; - for (auto i = first; i != last; ++i) res.push_back(atoi(i->str().c_str())); - // give the numbers back - return res; + // fstream destructor closes file when going out of scope + // in this house we RAII sometimes. + return ret; } \ No newline at end of file diff --git a/test1.txt b/test1.txt index a9a6fba..76aa4af 100644 --- a/test1.txt +++ b/test1.txt @@ -1,13 +1,14 @@ % This file is a very poorly formatted example file, which contains all sorts of garbage -% This was written to ensure the parser is as robust as possible, and won't crash on malformed input +% This was written to stress-test the parser -nv249hvbwer8oysbvdofuvbfdz wsdfabsv num_processes = 4 -// Gee, this isn't actually a comment is it? -# but most of that is there to distract the program! +this is horrible to read! +nv249hvbwer8oysbvdofuvbfdzwsdfabsvnum_processesbgskjhdfd=lbahfbzjhxbc,4gsv4wf5fewf6, 7, 8 +^^^^ Believe it or not, this is a num_processes line! ^^^^ +// Gee, this isn't actually a comment is it? As long as I don't include a number, it won't care. [][][][]\\\\\';./` num_resources= 4ibeoifbavdivbkdhbz -% lines beginning with % are comments - % for example: num_processes = 128314 -% The first line containing a list of numbers will be interpreted as resource_counts +% lines containing % are comments +for example: num_processes = 128314% +% The first non-comment line containing a list of numbers will be interpreted as resource_counts 12 ,14,16 , 18 % this line is missing a comma 1,2 3,4,5,6,7,8 @@ -16,8 +17,12 @@ nv249hvbwer8oysbvdofuvbfdz wsdfabsv num_processes = 4 % and this one, before the comma 1 ,2 ,3 ,4 ,5 ,6 ,7,8 % nevertheless, it keeps on chugging. - 1,2,3,4,5,6,7,8 -1,2,3,4,5,6,7,8 -1,2,3,4,5,6,7,8 -1,2,3,4,5,6,7,8 -1,2,3,4,5,6,7,8 \ No newline at end of file +~~~1~~~2~~~3~~~4~~~5~~~6~~~7~~~8~~~ +1a 2b 3c, 4d 5e 6f, 7g 8h +^^^^ Testing mixed letters and numbers ^^^^ + @Xx_1_2_3_4_5_6_7_8_()_xX# +words, words, words! + +vvvv testing negative numbers vvvv +1,-2,3,4,5,6,-7,8 +-1,2,3,4,5,6,7,-8 \ No newline at end of file