aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLoic Guegan <manzerbredes@mailbox.org>2022-01-24 15:29:22 +0100
committerLoic Guegan <manzerbredes@mailbox.org>2022-01-24 15:29:22 +0100
commit8c77f7a054eb15d3e73649bce5447f172a99969c (patch)
tree051e2761b2f12c11861ce47fde4ea5c3ff2810b7 /src
parentfd78f92863361a3b25808f2ca988f820f2d35618 (diff)
Improve overall parsing
Diffstat (limited to 'src')
-rw-r--r--src/pgnp.cpp310
-rw-r--r--src/pgnp.hpp89
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