src/chess/pgn.c

changeset 150
7d57a252948e
parent 149
ea2448961441
equal deleted inserted replaced
149:ea2448961441 150:7d57a252948e
50 50
51 const char* pgn_error_str(int code) { 51 const char* pgn_error_str(int code) {
52 return pgn_error_strings[code]; 52 return pgn_error_strings[code];
53 } 53 }
54 54
55 size_t pgn_error_pos;
56
57 size_t pgn_error_position(void) {
58 return pgn_error_pos;
59 }
60
55 static const char *pgn_result(GameState *gamestate) { 61 static const char *pgn_result(GameState *gamestate) {
56 if (gamestate->stalemate || gamestate->remis) { 62 if (gamestate->stalemate || gamestate->remis) {
57 return "1/2-1/2"; 63 return "1/2-1/2";
58 } else if (gamestate->wresign) { 64 } else if (gamestate->wresign) {
59 return "0-1"; 65 return "0-1";
68 } else { 74 } else {
69 return "*"; 75 return "*";
70 } 76 }
71 } 77 }
72 78
79 #define return(code) pgn_error_pos = pgndata - pgndata0; return code
80
73 int parse_pgn(const char *pgndata, GameState *gamestate) { 81 int parse_pgn(const char *pgndata, GameState *gamestate) {
82 const char * const pgndata0 = pgndata;
74 int i; 83 int i;
75 char c; 84 char c;
76 85
77 char result[8] = {0}; 86 char result[8] = {0};
78 87
84 while (isspace(c = *(pgndata++))); 93 while (isspace(c = *(pgndata++)));
85 if (c == '1') { 94 if (c == '1') {
86 break; 95 break;
87 } 96 }
88 if (c != '[') { 97 if (c != '[') {
89 return pgn_error_missing_bracket; 98 return(pgn_error_missing_bracket);
90 } 99 }
91 while (isspace(c = *(pgndata++))); 100 while (isspace(c = *(pgndata++)));
92 i = 0; 101 i = 0;
93 do { 102 do {
94 tagkey[i++] = c; 103 tagkey[i++] = c;
95 } while (!isspace(c = *(pgndata++))); 104 } while (!isspace(c = *(pgndata++)));
96 tagkey[i] = '\0'; 105 tagkey[i] = '\0';
97 while (isspace(c = *(pgndata++))); 106 while (isspace(c = *(pgndata++)));
98 if (c != '"') { 107 if (c != '"') {
99 return pgn_error_missing_quote; 108 return(pgn_error_missing_quote);
100 } 109 }
101 i = 0; 110 i = 0;
102 while ((c = *(pgndata++)) != '"') { 111 while ((c = *(pgndata++)) != '"') {
103 if (c == '\n' || c == 0) { 112 if (c == '\n' || c == 0) {
104 return pgn_error_missing_quote; 113 return(pgn_error_missing_quote);
105 } 114 }
106 tagvalue[i++] = c; 115 tagvalue[i++] = c;
107 } 116 }
108 tagvalue[i] = '\0'; 117 tagvalue[i] = '\0';
109 if (*(pgndata++) != ']') { 118 if (*(pgndata++) != ']') {
110 return pgn_error_missing_bracket; 119 return(pgn_error_missing_bracket);
111 } 120 }
112 121
113 // TODO: read clock info 122 // TODO: read clock info
114 123
115 if (strcmp("Result", tagkey) == 0) { 124 if (strcmp("Result", tagkey) == 0) {
117 } 126 }
118 } 127 }
119 128
120 /* read moves */ 129 /* read moves */
121 if (*(pgndata++) != '.') { 130 if (*(pgndata++) != '.') {
122 return pgn_error_missing_dot; 131 return(pgn_error_missing_dot);
123 } 132 }
124 133
125 char movestr[10]; 134 char movestr[10];
126 Move move; 135 Move move;
127 uint8_t curcol = WHITE; 136 uint8_t curcol = WHITE;
132 while (isspace(c = *(pgndata++))); 141 while (isspace(c = *(pgndata++)));
133 i = 0; 142 i = 0;
134 do { 143 do {
135 movestr[i++] = c; 144 movestr[i++] = c;
136 if (i >= 10) { 145 if (i >= 10) {
137 return pgn_error_move_syntax; 146 return(pgn_error_move_syntax);
138 } 147 }
139 } while (!isspace(c = *(pgndata++))); 148 } while (!isspace(c = *(pgndata++)));
140 movestr[i] = '\0'; 149 movestr[i] = '\0';
141 if (eval_move(gamestate, movestr, &move, curcol) 150 if (eval_move(gamestate, movestr, &move, curcol)
142 != VALID_MOVE_SYNTAX) { 151 != VALID_MOVE_SYNTAX) {
143 return pgn_error_move_syntax; 152 return(pgn_error_move_syntax);
144 } 153 }
145 if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) { 154 if (validate_move(gamestate, &move) != VALID_MOVE_SEMANTICS) {
146 return pgn_error_move_semantics; 155 return(pgn_error_move_semantics);
147 } 156 }
148 apply_move(gamestate, &move); 157 apply_move(gamestate, &move);
149 158
150 /* skip spaces */ 159 /* skip spaces */
151 while (isspace(c = *(pgndata++))); 160 while (isspace(c = *(pgndata++)));
155 // TODO: interpret comment (may contain clock info) 164 // TODO: interpret comment (may contain clock info)
156 do { 165 do {
157 c = *(pgndata++); 166 c = *(pgndata++);
158 } while (c != '}' && c != 0); 167 } while (c != '}' && c != 0);
159 if (c == 0) { 168 if (c == 0) {
160 return pgn_error_missing_brace; 169 return(pgn_error_missing_brace);
161 } 170 }
162 /* skip spaces */ 171 /* skip spaces */
163 while (isspace(c = *(pgndata++))); 172 while (isspace(c = *(pgndata++)));
164 } 173 }
165 174
178 if (c == '1') { 187 if (c == '1') {
179 gamestate->wresign = true; 188 gamestate->wresign = true;
180 } else if (c == '0') { 189 } else if (c == '0') {
181 gamestate->bresign = true; 190 gamestate->bresign = true;
182 } else { 191 } else {
183 return pgn_error_result_syntax; 192 return(pgn_error_result_syntax);
184 } 193 }
185 } 194 }
186 movetext_ends_with_result = true; 195 movetext_ends_with_result = true;
187 break; 196 break;
188 } else if (c == '/') { 197 } else if (c == '/') {
200 209
201 /* skip move number after black move */ 210 /* skip move number after black move */
202 if (curcol == BLACK) { 211 if (curcol == BLACK) {
203 while (isdigit(c = *(pgndata++))); 212 while (isdigit(c = *(pgndata++)));
204 if (c != '.') { 213 if (c != '.') {
205 return pgn_error_missing_dot; 214 return(pgn_error_missing_dot);
206 } 215 }
207 } 216 }
208 curcol = opponent_color(curcol); 217 curcol = opponent_color(curcol);
209 } 218 }
210 219
211 /* sanity check result - if it was specified */ 220 /* sanity check result - if it was specified */
212 if (movetext_ends_with_result && result[0]) { 221 if (movetext_ends_with_result && result[0]) {
213 if (strncmp(result, pgn_result(gamestate), 8) != 0) { 222 if (strncmp(result, pgn_result(gamestate), 8) != 0) {
214 fprintf(stderr, "Fuck it: %s\n", result); 223 return(pgn_error_result_mismatch);
215 return pgn_error_result_mismatch; 224 }
216 } 225 }
217 } 226
218 227 return(pgn_no_error);
219 return pgn_no_error; 228 }
220 } 229
230 #undef return
221 231
222 int read_pgn(FILE *stream, GameState *gamestate) { 232 int read_pgn(FILE *stream, GameState *gamestate) {
223 fseek(stream, 0, SEEK_END); 233 fseek(stream, 0, SEEK_END);
224 long int size = ftell(stream); 234 long int size = ftell(stream);
225 fseek(stream, 0, SEEK_SET); 235 fseek(stream, 0, SEEK_SET);
226 char *data = malloc(size + 1); 236 char *data = malloc(size + 1);
227 memset(data, 0, size + 1); 237 memset(data, 0, size + 1);
228 size_t read = 0; 238 size_t read = 0;
229 do { 239 do {
230 size_t r = fread(data + read, 1, size - read, stream); 240 size_t r = fread(data + read, 1, size - read, stream);
231 if (r == 0) return pgn_error_unexpected_eof; 241 if (r == 0) {
242 pgn_error_pos = read;
243 return pgn_error_unexpected_eof;
244 }
232 read += r; 245 read += r;
233 } while (read < size); 246 } while (read < size);
234 int result = parse_pgn(data, gamestate); 247 int result = parse_pgn(data, gamestate);
235 free(data); 248 free(data);
236 return result; 249 return result;

mercurial