Mon, 13 Jul 2026 22:13:34 +0200
add parse_pgn function which can later be used for automated testing
| src/chess/pgn.c | file | annotate | diff | comparison | revisions | |
| src/chess/pgn.h | file | annotate | diff | comparison | revisions |
--- a/src/chess/pgn.c Sat Jun 13 13:45:19 2026 +0200 +++ b/src/chess/pgn.c Mon Jul 13 22:13:34 2026 +0200 @@ -60,7 +60,39 @@ return pgn_error_strings[code]; } -int read_pgn(FILE* stream, GameState *gamestate) { +struct pgn_input { + union { + FILE *f; + const char *s; + } data; + union { + int (*f)(FILE*); + int (*s)(const char*); + } getc; + void (*backc)(struct pgn_input *); +}; + +static int pgngetc(struct pgn_input *pgndata) { + return pgndata->getc.f(pgndata->data.f); +} + +static void pgnbackc(struct pgn_input *pgndata) { + pgndata->backc(pgndata); +} + +static void fbackc(struct pgn_input *pgn) { + fseek(pgn->data.f, -1, SEEK_CUR); +} + +static void sbackc(struct pgn_input *pgn) { + --(pgn->data.s); +} + +static int sgetc(const char *s) { + return *s; +} + +static int read_pgn_(struct pgn_input pgndata, GameState *gamestate) { int c, i; char result[8]; @@ -70,32 +102,32 @@ /* read tag pairs */ while (true) { - while (isspace(c = fgetc(stream))); + while (isspace(c = pgngetc(&pgndata))); if (c == '1') { break; } if (c != '[') { return pgn_error_missing_bracket; } - while (isspace(c = fgetc(stream))); + while (isspace(c = pgngetc(&pgndata))); i = 0; do { tagkey[i++] = c; - } while (!isspace(c = fgetc(stream))); + } while (!isspace(c = pgngetc(&pgndata))); tagkey[i] = '\0'; - while (isspace(c = fgetc(stream))); + while (isspace(c = pgngetc(&pgndata))); if (c != '"') { return pgn_error_missing_quote; } i = 0; - while ((c = fgetc(stream)) != '"') { + while ((c = pgngetc(&pgndata)) != '"') { if (c == '\n' || c == EOF) { return pgn_error_missing_quote; } tagvalue[i++] = c; } tagvalue[i] = '\0'; - if (fgetc(stream) != ']') { + if (pgngetc(&pgndata) != ']') { return pgn_error_missing_bracket; } @@ -107,7 +139,7 @@ } /* read moves */ - if (fgetc(stream) != '.') { + if (pgngetc(&pgndata) != '.') { return pgn_error_missing_dot; } @@ -117,14 +149,14 @@ while (true) { /* move */ - while (isspace(c = fgetc(stream))); + while (isspace(c = pgngetc(&pgndata))); i = 0; do { movestr[i++] = c; if (i >= 10) { return pgn_error_move_syntax; } - } while (!isspace(c = fgetc(stream))); + } while (!isspace(c = pgngetc(&pgndata))); movestr[i] = '\0'; if (eval_move(gamestate, movestr, &move, curcol) != VALID_MOVE_SYNTAX) { @@ -136,19 +168,19 @@ apply_move(gamestate, &move); /* skip spaces */ - while (isspace(c = fgetc(stream))); + while (isspace(c = pgngetc(&pgndata))); /* parse possible comment */ if (c == '{') { // TODO: interpret comment (may contain clock info) do { - c = fgetc(stream); + c = pgngetc(&pgndata); } while (c != '}' && c != EOF); if (c == EOF) { return pgn_error_missing_brace; } /* skip spaces */ - while (isspace(c = fgetc(stream))); + while (isspace(c = pgngetc(&pgndata))); } /* end of game data encountered */ @@ -156,13 +188,13 @@ break; } if (c == '1' || c == '0') { - c = fgetc(stream); + c = pgngetc(&pgndata); // TODO: #842 - allow games to be continued if possible if (c == '-') { if (!gamestate->checkmate) { // TODO: maybe we should not parse this here; // there is an unused result string from the STR - c = fgetc(stream); + c = pgngetc(&pgndata); if (c == '1') { gamestate->wresign = true; } else if (c == '0') { @@ -177,16 +209,16 @@ break; } else { /* oops, it was a move number, go back! */ - fseek(stream, -1, SEEK_CUR); + pgnbackc(&pgndata); } } /* we have eaten the next valuable byte, so go back */ - fseek(stream, -1, SEEK_CUR); + pgnbackc(&pgndata); /* skip move number after black move */ if (curcol == BLACK) { - while (isdigit(c = fgetc(stream))); + while (isdigit(c = pgngetc(&pgndata))); if (c != '.') { return pgn_error_missing_dot; } @@ -197,6 +229,24 @@ return 0; } +int read_pgn(FILE *stream, GameState *gamestate) { + return read_pgn_( + (struct pgn_input){ + {.f = stream}, + {.f = fgetc}, + fbackc + }, gamestate); +} + +int parse_pgn(const char *data, GameState *gamestate) { + return read_pgn_( + (struct pgn_input){ + {.s = data}, + {.s = sgetc}, + sbackc + }, gamestate); +} + static void pgn_insert_newlines(char *block) { size_t pos = 0; size_t last_space_pos = 0;
--- a/src/chess/pgn.h Sat Jun 13 13:45:19 2026 +0200 +++ b/src/chess/pgn.h Mon Jul 13 22:13:34 2026 +0200 @@ -39,6 +39,7 @@ #endif int read_pgn(FILE *stream, GameState *gamestate); +int parse_pgn(const char *data, GameState *gamestate); void write_pgn(FILE* stream, GameState *gamestate, bool export_comments); void compute_fen(char *str, GameState *gamestate);