src/chess/pgn.c

changeset 140
e92ffe046659
parent 133
c58ae152733e
child 142
780337e5cf6a
equal deleted inserted replaced
139:fa10bee331ac 140:e92ffe046659
58 58
59 const char* pgn_error_str(int code) { 59 const char* pgn_error_str(int code) {
60 return pgn_error_strings[code]; 60 return pgn_error_strings[code];
61 } 61 }
62 62
63 int read_pgn(FILE* stream, GameState *gamestate) { 63 struct pgn_input {
64 union {
65 FILE *f;
66 const char *s;
67 } data;
68 union {
69 int (*f)(FILE*);
70 int (*s)(const char*);
71 } getc;
72 void (*backc)(struct pgn_input *);
73 };
74
75 static int pgngetc(struct pgn_input *pgndata) {
76 return pgndata->getc.f(pgndata->data.f);
77 }
78
79 static void pgnbackc(struct pgn_input *pgndata) {
80 pgndata->backc(pgndata);
81 }
82
83 static void fbackc(struct pgn_input *pgn) {
84 fseek(pgn->data.f, -1, SEEK_CUR);
85 }
86
87 static void sbackc(struct pgn_input *pgn) {
88 --(pgn->data.s);
89 }
90
91 static int sgetc(const char *s) {
92 return *s;
93 }
94
95 static int read_pgn_(struct pgn_input pgndata, GameState *gamestate) {
64 int c, i; 96 int c, i;
65 97
66 char result[8]; 98 char result[8];
67 99
68 char tagkey[32]; 100 char tagkey[32];
69 char tagvalue[128]; 101 char tagvalue[128];
70 102
71 /* read tag pairs */ 103 /* read tag pairs */
72 while (true) { 104 while (true) {
73 while (isspace(c = fgetc(stream))); 105 while (isspace(c = pgngetc(&pgndata)));
74 if (c == '1') { 106 if (c == '1') {
75 break; 107 break;
76 } 108 }
77 if (c != '[') { 109 if (c != '[') {
78 return pgn_error_missing_bracket; 110 return pgn_error_missing_bracket;
79 } 111 }
80 while (isspace(c = fgetc(stream))); 112 while (isspace(c = pgngetc(&pgndata)));
81 i = 0; 113 i = 0;
82 do { 114 do {
83 tagkey[i++] = c; 115 tagkey[i++] = c;
84 } while (!isspace(c = fgetc(stream))); 116 } while (!isspace(c = pgngetc(&pgndata)));
85 tagkey[i] = '\0'; 117 tagkey[i] = '\0';
86 while (isspace(c = fgetc(stream))); 118 while (isspace(c = pgngetc(&pgndata)));
87 if (c != '"') { 119 if (c != '"') {
88 return pgn_error_missing_quote; 120 return pgn_error_missing_quote;
89 } 121 }
90 i = 0; 122 i = 0;
91 while ((c = fgetc(stream)) != '"') { 123 while ((c = pgngetc(&pgndata)) != '"') {
92 if (c == '\n' || c == EOF) { 124 if (c == '\n' || c == EOF) {
93 return pgn_error_missing_quote; 125 return pgn_error_missing_quote;
94 } 126 }
95 tagvalue[i++] = c; 127 tagvalue[i++] = c;
96 } 128 }
97 tagvalue[i] = '\0'; 129 tagvalue[i] = '\0';
98 if (fgetc(stream) != ']') { 130 if (pgngetc(&pgndata) != ']') {
99 return pgn_error_missing_bracket; 131 return pgn_error_missing_bracket;
100 } 132 }
101 133
102 // TODO: read clock info 134 // TODO: read clock info
103 if (strcmp("Result", tagkey) == 0) { 135 if (strcmp("Result", tagkey) == 0) {
105 memcpy(result, tagvalue, 8); 137 memcpy(result, tagvalue, 8);
106 } 138 }
107 } 139 }
108 140
109 /* read moves */ 141 /* read moves */
110 if (fgetc(stream) != '.') { 142 if (pgngetc(&pgndata) != '.') {
111 return pgn_error_missing_dot; 143 return pgn_error_missing_dot;
112 } 144 }
113 145
114 char movestr[10]; 146 char movestr[10];
115 Move move; 147 Move move;
116 uint8_t curcol = WHITE; 148 uint8_t curcol = WHITE;
117 149
118 while (true) { 150 while (true) {
119 /* move */ 151 /* move */
120 while (isspace(c = fgetc(stream))); 152 while (isspace(c = pgngetc(&pgndata)));
121 i = 0; 153 i = 0;
122 do { 154 do {
123 movestr[i++] = c; 155 movestr[i++] = c;
124 if (i >= 10) { 156 if (i >= 10) {
125 return pgn_error_move_syntax; 157 return pgn_error_move_syntax;
126 } 158 }
127 } while (!isspace(c = fgetc(stream))); 159 } while (!isspace(c = pgngetc(&pgndata)));
128 movestr[i] = '\0'; 160 movestr[i] = '\0';
129 if (eval_move(gamestate, movestr, &move, curcol) 161 if (eval_move(gamestate, movestr, &move, curcol)
130 != VALID_MOVE_SYNTAX) { 162 != VALID_MOVE_SYNTAX) {
131 return pgn_error_move_syntax; 163 return pgn_error_move_syntax;
132 } 164 }
134 return pgn_error_move_semantics; 166 return pgn_error_move_semantics;
135 } 167 }
136 apply_move(gamestate, &move); 168 apply_move(gamestate, &move);
137 169
138 /* skip spaces */ 170 /* skip spaces */
139 while (isspace(c = fgetc(stream))); 171 while (isspace(c = pgngetc(&pgndata)));
140 172
141 /* parse possible comment */ 173 /* parse possible comment */
142 if (c == '{') { 174 if (c == '{') {
143 // TODO: interpret comment (may contain clock info) 175 // TODO: interpret comment (may contain clock info)
144 do { 176 do {
145 c = fgetc(stream); 177 c = pgngetc(&pgndata);
146 } while (c != '}' && c != EOF); 178 } while (c != '}' && c != EOF);
147 if (c == EOF) { 179 if (c == EOF) {
148 return pgn_error_missing_brace; 180 return pgn_error_missing_brace;
149 } 181 }
150 /* skip spaces */ 182 /* skip spaces */
151 while (isspace(c = fgetc(stream))); 183 while (isspace(c = pgngetc(&pgndata)));
152 } 184 }
153 185
154 /* end of game data encountered */ 186 /* end of game data encountered */
155 if (c == EOF) { 187 if (c == EOF) {
156 break; 188 break;
157 } 189 }
158 if (c == '1' || c == '0') { 190 if (c == '1' || c == '0') {
159 c = fgetc(stream); 191 c = pgngetc(&pgndata);
160 // TODO: #842 - allow games to be continued if possible 192 // TODO: #842 - allow games to be continued if possible
161 if (c == '-') { 193 if (c == '-') {
162 if (!gamestate->checkmate) { 194 if (!gamestate->checkmate) {
163 // TODO: maybe we should not parse this here; 195 // TODO: maybe we should not parse this here;
164 // there is an unused result string from the STR 196 // there is an unused result string from the STR
165 c = fgetc(stream); 197 c = pgngetc(&pgndata);
166 if (c == '1') { 198 if (c == '1') {
167 gamestate->wresign = true; 199 gamestate->wresign = true;
168 } else if (c == '0') { 200 } else if (c == '0') {
169 gamestate->bresign = true; 201 gamestate->bresign = true;
170 } else { 202 } else {
175 } else if (c == '/') { 207 } else if (c == '/') {
176 gamestate->remis = !gamestate->stalemate; 208 gamestate->remis = !gamestate->stalemate;
177 break; 209 break;
178 } else { 210 } else {
179 /* oops, it was a move number, go back! */ 211 /* oops, it was a move number, go back! */
180 fseek(stream, -1, SEEK_CUR); 212 pgnbackc(&pgndata);
181 } 213 }
182 } 214 }
183 215
184 /* we have eaten the next valuable byte, so go back */ 216 /* we have eaten the next valuable byte, so go back */
185 fseek(stream, -1, SEEK_CUR); 217 pgnbackc(&pgndata);
186 218
187 /* skip move number after black move */ 219 /* skip move number after black move */
188 if (curcol == BLACK) { 220 if (curcol == BLACK) {
189 while (isdigit(c = fgetc(stream))); 221 while (isdigit(c = pgngetc(&pgndata)));
190 if (c != '.') { 222 if (c != '.') {
191 return pgn_error_missing_dot; 223 return pgn_error_missing_dot;
192 } 224 }
193 } 225 }
194 curcol = opponent_color(curcol); 226 curcol = opponent_color(curcol);
195 } 227 }
196 228
197 return 0; 229 return 0;
230 }
231
232 int read_pgn(FILE *stream, GameState *gamestate) {
233 return read_pgn_(
234 (struct pgn_input){
235 {.f = stream},
236 {.f = fgetc},
237 fbackc
238 }, gamestate);
239 }
240
241 int parse_pgn(const char *data, GameState *gamestate) {
242 return read_pgn_(
243 (struct pgn_input){
244 {.s = data},
245 {.s = sgetc},
246 sbackc
247 }, gamestate);
198 } 248 }
199 249
200 static void pgn_insert_newlines(char *block) { 250 static void pgn_insert_newlines(char *block) {
201 size_t pos = 0; 251 size_t pos = 0;
202 size_t last_space_pos = 0; 252 size_t last_space_pos = 0;

mercurial