24 #ifndef SCID_PGNPARSE_H 25 #define SCID_PGNPARSE_H 41 std::vector<std::pair<size_t, std::string>> errors_;
43 int nErrorsAllowed_ = 2;
45 using TView = std::pair<const char*, const char*>;
52 if (nErrorsAllowed_ < 0)
55 return logErr(
"Unexpected end of input (result missing ?).");
59 if (nErrorsAllowed_ < 0)
62 return logErr(
"Unexpected end of game: PGN header '[' seen " 63 "inside game (result missing ?).");
67 if (nErrorsAllowed_ < 0) {
68 linenum_ += std::count(comment.first, comment.second,
'\n');
74 auto prevSz = str.size();
75 str.append(comment.first, comment.second);
86 ASSERT(nErrorsAllowed_ >= 0);
87 std::string tmp(line.first, line.second);
89 return logErr(
"Failed to parse EPD record: ", line);
92 auto opcode = std::find_if(line.first, line.second, [&](
char ch) {
93 return (ch ==
' ') ? spaces++ == 4 : spaces == 4;
103 if (nErrorsAllowed_ < 0)
107 if (nag_code == 0 || game.
AddNag(nag_code) !=
OK)
108 return logErr(
"Invalid annotation symbol: ", token);
130 if (result != prev_result) {
133 if (prev_result !=
RESULT_None && nErrorsAllowed_ >= 0)
134 logErr(
"Final result did not match the header tag.");
140 if (nErrorsAllowed_ < 0)
147 logWarning(
"Warning: illegal castling ", tok);
152 return logFatalErr(
"Failed to parse the move: ", tok);
157 : logFatalErr(
"Failed to add the move: ", tok);
163 linenum_ += std::count(value.first, value.second,
'\n');
164 if (nErrorsAllowed_ < 0)
167 auto tagLen = std::distance(tag.first, tag.second);
168 auto valueLen = std::distance(value.first, value.second);
169 if (tagLen == 0 || tagLen + valueLen > 240 ||
170 !parseTagPair(tag.first, tagLen, value))
172 std::string err(tag.first, tag.second);
174 err.append(value.first, value.second);
176 logErr(
"Error parsing the tag pair: ",
177 {err.c_str(), err.c_str() + err.size()});
183 if (nErrorsAllowed_ < 0)
187 std::string tmp(token.first, token.second);
188 if (tmp ==
"0-0" || tmp ==
"00") {
192 if (tmp ==
"0-0-0" || tmp ==
"000") {
197 return logErr(
"Unknown token: ", token);
201 if (nErrorsAllowed_ < 0)
205 return logFatalErr(
"Failed to add a new variation.");
211 if (nErrorsAllowed_ < 0)
215 return logFatalErr(
"Failed to exit from variation.");
221 bool logWarning(
const char* str1, TView str2 = {
nullptr,
nullptr}) {
222 errors_.emplace_back(linenum_, str1);
223 if (std::distance(str2.first, str2.second) > 200) {
224 errors_.back().second.append(str2.first, 200);
225 errors_.back().second.append(
"...");
227 errors_.back().second.append(str2.first, str2.second);
232 bool logErr(
const char* str1, TView str2 = {
nullptr,
nullptr}) {
234 return logWarning(str1, str2);
237 bool logFatalErr(
const char* str1, TView str2 = {
nullptr,
nullptr}) {
239 return logErr(str1, str2);
242 bool parseTagResult(TView str) {
243 auto len = std::distance(str.first, str.second);
244 if (len > 0 && *str.first ==
'*') {
249 if (std::equal(str.first, str.first + 3,
"1-0")) {
253 if (std::equal(str.first, str.first + 3,
"0-1")) {
257 if (std::equal(str.first, str.first + 3,
"1/2")) {
262 return logErr(
"Invalid Result tag: ", str);
265 bool parseTagPair(
const char* tag,
size_t tagLen, TView value) {
268 if (std::equal(tag, tag + 3,
"ECO")) {
269 std::string tmp{value.first, value.second};
273 if (std::equal(tag, tag + 3,
"FEN")) {
274 std::string tmp{value.first, value.second};
279 if (std::equal(tag, tag + 4,
"Date")) {
285 if (std::equal(tag, tag + 6,
"Result"))
286 return parseTagResult(value);
289 if (std::equal(tag, tag + 7,
"UTCDate") &&
296 if (std::equal(tag, tag + 9,
"EventDate")) {
300 if (std::equal(tag, tag + 9,
"ScidFlags")) {
302 std::distance(value.first, value.second));
311 if (std::equal(tag, tag + 5,
"White") && game.
GetWhiteElo() == 0) {
315 }
else if (std::equal(tag, tag + 5,
"Black") &&
323 str.assign(value.first, value.second);
324 linenum_ += pgn::normalize<true>(str, 0);
334 unsigned long long n_bytes = 0;
335 unsigned long long n_lines = 0;
336 unsigned long long n_games = 0;
345 for (
auto& e : visitor.errors_) {
346 log +=
"(game " + std::to_string(n_games);
347 log +=
", line " + std::to_string(n_lines + e.first) +
") ";
351 n_lines += visitor.linenum_;
353 if (visitor.nErrorsAllowed_ < 0) {
354 log +=
"(game " + std::to_string(n_games);
355 log +=
", line " + std::to_string(n_lines) +
") ";
356 log +=
"End of game, ignored the part after the last error.\n";
382 if (!log.
logGame(parse.first, visitor))
385 if (parse.first == inputLen && !parse.second &&
392 #endif // idndef SCID_PGNPARSE_H bool visitPGN_TagPair(TView tag, TView value)
int setRating(colorT col, const char *ratingType, size_t ratingTypeLen, std::pair< const char *, const char *> rating)
bool pgnParseGame(const char *input, size_t inputLen, Game &game, PgnParseLog &log)
Convert PGN text into a SCID's Game object.
std::pair< std::size_t, bool > parse_game(pgn_impl::InputMemory input, TVisitor &&parser)
Read a PGN game from memory, grouping characters in tokens and dispatching them to a PGN parser...
std::string & accessMoveComment()
bool visitPGN_EndOfLine()
std::string & accessTagValue(const char *tag, size_t tagLen)
errorT SetStartFen(const char *fenStr)
Setup the start position from a FEN string and remove all the moves.
This class implements a PGN "visitor" that invokes the appropriate member functions of the associated...
const char * GetMoveComment() const
Split input into PGN tokens and dispatch them to a "visiting" parser.
const resultT RESULT_Black
void SetEventDate(dateT date)
const errorT ERROR_CastlingAvailability
bool visitPGN_VariationStart()
bool visitPGN_inputUnexpectedPGNHeader()
bool visitPGN_Suffix(TView token)
dateT date_parsePGNTag(const char *str, size_t len)
Creates a dateT object from a PGN tag value string.
void SetResult(resultT res)
const resultT RESULT_Draw
bool visitPGN_Escape(TView)
bool logGame(size_t nBytes, const PgnVisitor &visitor)
Format and store errors occurred while parsing a Game.
byte game_parseNag(std::pair< const char *, const char *> strview)
resultT GetResult() const
const resultT RESULT_White
bool visitPGN_VariationEnd()
bool visitPGN_SANMove(TView tok)
Position * GetCurrentPos()
errorT ParseMove(simpleMoveT *sm, const char *str)
bool visitPGN_MoveNum(TView)
errorT MoveExitVariation()
std::size_t trim(TView &str)
Trim leading and trailing white spaces.
bool visitPGN_Comment(TView comment)
ecoT eco_FromString(const char *ecoStr)
bool visitPGN_EPD(TView line)
bool visitPGN_ResultFinal(char resultCh)
errorT AddMove(const simpleMoveT *sm)
const resultT RESULT_None
std::size_t normalize(TString &str, std::size_t pos)
Normalize white spaces and converts Latin-1 chars to UTF-8 sequences.
bool visitPGN_NAG(TView token)
void SetScidFlags(const char *s, size_t len)
bool visitPGN_Unknown(TView token)