diff options
| author | Loic Guegan <manzerbredes@mailbox.org> | 2022-01-24 15:29:22 +0100 |
|---|---|---|
| committer | Loic Guegan <manzerbredes@mailbox.org> | 2022-01-24 15:29:22 +0100 |
| commit | 8c77f7a054eb15d3e73649bce5447f172a99969c (patch) | |
| tree | 051e2761b2f12c11861ce47fde4ea5c3ff2810b7 /src | |
| parent | fd78f92863361a3b25808f2ca988f820f2d35618 (diff) | |
Improve overall parsing
Diffstat (limited to 'src')
| -rw-r--r-- | src/pgnp.cpp | 310 | ||||
| -rw-r--r-- | src/pgnp.hpp | 89 |
2 files changed, 311 insertions, 88 deletions
diff --git a/src/pgnp.cpp b/src/pgnp.cpp index 391e6ee..bb9fd29 100644 --- a/src/pgnp.cpp +++ b/src/pgnp.cpp @@ -2,80 +2,274 @@ #include "pgnp.hpp" #include <iostream> -#define IS_BLANK(c) (c==' ' || c=='\n' || c=='\t') -#define IS_EOF(loc) (loc>=pgn_content.size()) -#define EOF_CHECK(loc) {if(IS_EOF(loc)) throw UnexpectedEOF();} +#define IS_BLANK(c) (c == ' ' || c == '\n' || c == '\t') +#define IS_DIGIT(c) \ + (c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || \ + c == '6' || c == '7' || c == '8' || c == '9') +#define IS_EOF(loc) (loc >= pgn_content.size()) +#define EOF_CHECK(loc) \ + { \ + if (IS_EOF(loc)) \ + throw UnexpectedEOF(); \ + } namespace pgnp { - void PGN::FromFile(std::string filepath){ - std::ifstream file(filepath); - - std::string content((std::istreambuf_iterator<char>(file)), - std::istreambuf_iterator<char>()); - FromString(content); - } - - - void PGN::FromString(std::string pgn_content){ - this->pgn_content=pgn_content; - int loc=0; - while(!IS_EOF(loc)) { - char c=pgn_content[loc]; - if(!IS_BLANK(c)){ - switch (c) { - case '[': - loc=ParseNextTag(loc); - break; - } +HalfMove::HalfMove() : isBlack(false), MainLine(NULL) {} + +HalfMove::~HalfMove() { + for (auto *move : variations) { + delete move; + } +} + +void HalfMove::NestedDump(HalfMove *m, int indent) { + for (int i = 0; i < indent; i++) { + std::cout << " "; + } + std::cout << " " + << " Move=" << m->move << " Count=" << m->count << " Comment=\"" + << m->comment << "\"" + << " IsBlack=" << m->isBlack + << " Variations=" << m->variations.size() << std::endl; + + for (auto *var : m->variations) { + NestedDump(var, indent + 1); + } + + if (m->MainLine != NULL) { + NestedDump(m->MainLine, indent); + } +} + +void HalfMove::Dump() { NestedDump(this, 0); } + +int HalfMove::GetLength() { + int length = 0; + HalfMove *m = this; + while (m != NULL) { + length++; + m = m->MainLine; + } + return length; +} + +PGN::~PGN() { + if (moves != NULL) + delete moves; +} + +void PGN::FromFile(std::string filepath) { + std::ifstream file(filepath); + + std::string content((std::istreambuf_iterator<char>(file)), + std::istreambuf_iterator<char>()); + FromString(content); +} + +void PGN::FromString(std::string pgn_content) { + this->pgn_content = pgn_content; + moves = NULL; + int loc = 0; + while (!IS_EOF(loc)) { + char c = pgn_content[loc]; + if (!IS_BLANK(c)) { + if (c == '[') { + loc = ParseNextTag(loc); + } else if (IS_DIGIT(c)) { + moves = new HalfMove(); + loc = ParseLine(loc, moves); + break; } - loc++; } + loc++; + } +} - /*for (auto const& [key, val] : tags){ - std::cout << key <<"="<<val<<std::endl; - }*/ +void PGN::STRCheck() { + int i = 0; + // Locate Event tag + while (i < tagkeys.size()) { + if (tagkeys[i] == "Event") { + break; + } + i++; } - int PGN::ParseNextTag(int start_loc){ - // Parse key - std::string key; - int keyloc=start_loc+1; - EOF_CHECK(keyloc); - char c=pgn_content[keyloc]; - while(!IS_BLANK(c)){ - key+=c; - keyloc++; - EOF_CHECK(keyloc); - c=pgn_content[keyloc]; + // Check tags + if (i + 6 < tagkeys.size()) { + bool valid = (tagkeys[i] == "Event") && (tagkeys[i + 1] == "Site") && + (tagkeys[i + 2] == "Date") && (tagkeys[i + 3] == "Round") && + (tagkeys[i + 4] == "White") && (tagkeys[i + 5] == "Black") && + (tagkeys[i + 6] == "Result"); + if (!valid) { + throw STRCheckFailed(); } + } else { + throw STRCheckFailed(); + } +} - // Parse value - std::string value; - int valueloc=NextNonBlank(keyloc)+1; - EOF_CHECK(keyloc); - c=pgn_content[valueloc]; - while(c!='"' or IS_EOF(valueloc)){ - value+=c; - valueloc++; - EOF_CHECK(keyloc); - c=pgn_content[valueloc]; +bool PGN::HasTag(std::string key) { + auto tags = GetTagList(); + return (std::find(tags.begin(), tags.end(), key) != tags.end()); +} + +int PGN::ParseLine(int loc, HalfMove *hm) { + // Goto next char + loc = NextNonBlank(loc); + EOF_CHECK(loc); + char c = pgn_content[loc]; + + // Check if we reach score entry (* or 1-0 or 0-1 or 1/2-1/2) + if (!IS_EOF(loc + 1)) { + char nc = pgn_content[loc + 1]; // Next c + if ((IS_DIGIT(c) && nc == '-') or (IS_DIGIT(c) && nc == '/')) { + return (loc); } + } - // Add tag - tags[key]=value; + // Parse (move number + if (IS_DIGIT(c)) { + std::string move_nb; + while (IS_DIGIT(c)) { + move_nb += c; + loc++; + c = pgn_content[loc]; + EOF_CHECK(loc); + } + hm->count = std::stoi(move_nb); + loc++; + EOF_CHECK(loc); + if (pgn_content[loc] == '.') { + hm->isBlack = true; + loc += 2; // Skip two dots + EOF_CHECK(loc); + } + } else { + hm->isBlack = true; + } + + // Parse the HalfMove + loc = NextNonBlank(loc); + EOF_CHECK(loc); + c = pgn_content[loc]; + std::string move; + while (!IS_BLANK(c) && c != ')') { + move += c; + loc++; + c = pgn_content[loc]; + EOF_CHECK(loc); + } + hm->move = move; - - return(valueloc+1); // +1 For the last char of the tag which is ']' + // Skip end of variation + if (c == ')') { + loc++; + return (loc); } - int PGN::NextNonBlank(int loc){ - char c=pgn_content[loc]; - while(IS_BLANK(c)){ + // Check for comment + loc = NextNonBlank(loc); + if (!IS_EOF(loc) && pgn_content[loc] == '{') { + loc++; // Skip '{' + c = pgn_content[loc]; + while (c != '}') { + hm->comment += c; loc++; - c=pgn_content[loc]; + EOF_CHECK(loc); + c = pgn_content[loc]; } - return(loc); + loc++; // Skip '}' + } + + // Check for variations + loc = NextNonBlank(loc); + while (!IS_EOF(loc) && pgn_content[loc] == '(') { + loc++; // Skip '(' + HalfMove *var = new HalfMove; + loc = ParseLine(loc, var); + hm->variations.push_back(var); + loc++; // Skip ')' + } + + // Parse next HalfMove + loc = NextNonBlank(loc); + if (!IS_EOF(loc)) { + HalfMove *next_hm = new HalfMove; + next_hm->count = hm->count; + loc = ParseLine(loc, next_hm); + // Check if move parsed successfuly + if (next_hm->move.size() > 0) { + hm->MainLine = next_hm; + } else { + delete next_hm; + } + } + + return (loc); +} + +int PGN::ParseNextTag(int start_loc) { + // Parse key + std::string key; + int keyloc = start_loc + 1; + EOF_CHECK(keyloc); + char c = pgn_content[keyloc]; + while (!IS_BLANK(c)) { + key += c; + keyloc++; + EOF_CHECK(keyloc); + c = pgn_content[keyloc]; + } + + // Parse value + std::string value; + int valueloc = NextNonBlank(keyloc) + 1; + EOF_CHECK(keyloc); + c = pgn_content[valueloc]; + while (c != '"' or IS_EOF(valueloc)) { + value += c; + valueloc++; + EOF_CHECK(keyloc); + c = pgn_content[valueloc]; + } + + // Add tag + tags[key] = value; + tagkeys.push_back(key); + + // TODO: Check that caracters if a ] + return (valueloc + 1); // +1 For the last char of the tag which is ']' +} + +HalfMove *PGN::GetMoves() { return (moves); } + +std::vector<std::string> PGN::GetTagList() { return tagkeys; } + +std::string PGN::GetTagValue(std::string key) { return tags[key]; } + +void PGN::Dump() { + std::cout << "---------- PGN DUMP ----------" << std::endl; + std::cout << "Tags:" << std::endl; + for (auto &tag : GetTagList()) { + std::cout << " " << tag << "=" << GetTagValue(tag) << std::endl; + } + std::cout << "Moves:" << std::endl; + + if (moves != NULL) + moves->Dump(); +} + +int PGN::NextNonBlank(int loc) { + char c = pgn_content[loc]; + while (IS_BLANK(c)) { + loc++; + if (IS_EOF(loc)) + return (loc); + c = pgn_content[loc]; } + return (loc); +} -}
\ No newline at end of file +} // namespace pgnp
\ No newline at end of file diff --git a/src/pgnp.hpp b/src/pgnp.hpp index 25e74c8..a89de73 100644 --- a/src/pgnp.hpp +++ b/src/pgnp.hpp @@ -1,46 +1,75 @@ -#include <unordered_map> -#include <string> +#include <algorithm> +#include <exception> #include <fstream> -#include <streambuf> #include <iostream> -#include <exception> +#include <streambuf> +#include <string> +#include <unordered_map> +#include <vector> namespace pgnp { +class HalfMove { +private: + /// @brief Recursive dump + void NestedDump(HalfMove *, int); - typedef struct HalfMove { - - } HalfMove; +public: + int count; + bool isBlack; + std::string move; + std::string comment; + HalfMove *MainLine; + std::vector<HalfMove *> variations; + HalfMove(); + ~HalfMove(); + int GetLength(); + /// @brief Dump move and all its variations + void Dump(); +}; - class PGN { - private: - std::unordered_map<std::string,std::string> tags; - HalfMove moves; - std::string pgn_content; +class PGN { +private: + std::unordered_map<std::string, std::string> tags; + std::vector<std::string> tagkeys; - public: - void FromFile(std::string); - void FromString(std::string); + HalfMove *moves; + std::string pgn_content; +public: + ~PGN(); + void FromFile(std::string); + void FromString(std::string); + bool HasTag(std::string); + /// @brief Perform a Seven Tag Roster compliance check + void STRCheck(); + /// @brief Dump parsed PGN + void Dump(); + std::vector<std::string> GetTagList(); + std::string GetTagValue(std::string); + HalfMove *GetMoves(); - private: +private: + /// @brief Populate @a tags with by parsing the one starting at location in + /// argument + int ParseNextTag(int); - /// @brief Populate @a tags with by parsing the one starting at location in argument - int ParseNextTag(int); - - /// @brief Get the next non-blank char location starting from location in argument - int NextNonBlank(int); - }; + /// @brief Get the next non-blank char location starting from location in + /// argument + int NextNonBlank(int); + int ParseLine(int, HalfMove *); +}; +struct UnexpectedEOF : public std::exception { + const char *what() const throw() { return "Unexpected end of pgn file"; } +}; - struct UnexpectedEOF : public std::exception - { - const char * what () const throw () - { - return "Unexpected end of pgn file"; - } - }; +struct STRCheckFailed : public std::exception { + const char *what() const throw() { + return "Seven Tag Roster compliance check failed"; + } +}; -} +} // namespace pgnp |
