16 static const bool DispMove = FALSE;
17 static const bool DispToken = FALSE;
18 static const bool DispChar = FALSE;
20 static const int TAB_SIZE = 8;
22 static const int CHAR_EOF = 256;
38 static void pgn_token_read (pgn_t * pgn);
39 static void pgn_token_unread (pgn_t * pgn);
41 static void pgn_read_token (pgn_t * pgn);
43 static bool is_symbol_start (int c);
44 static bool is_symbol_next (int c);
46 static void pgn_skip_blanks (pgn_t * pgn);
48 static void pgn_char_read (pgn_t * pgn);
49 static void pgn_char_unread (pgn_t * pgn);
55 void pgn_open(pgn_t * pgn, const char file_name[]) {
58 ASSERT(file_name!=NULL);
60 pgn->file = fopen(file_name,"r");
61 if (pgn->file == NULL) my_fatal("pgn_open(): can't open file \"%s\": %s\n",file_name,strerror(errno));
63 pgn->char_hack = CHAR_EOF; // DEBUG
66 pgn->char_unread = FALSE;
67 pgn->char_first = TRUE;
69 pgn->token_type = TOKEN_ERROR; // DEBUG
70 strcpy(pgn->token_string,"?"); // DEBUG
71 pgn->token_length = -1; // DEBUG
72 pgn->token_line = -1; // DEBUG
73 pgn->token_column = -1; // DEBUG
74 pgn->token_unread = FALSE;
75 pgn->token_first = TRUE;
77 strcpy(pgn->result,"?"); // DEBUG
78 strcpy(pgn->fen,"?"); // DEBUG
80 pgn->move_line = -1; // DEBUG
81 pgn->move_column = -1; // DEBUG
86 void pgn_close(pgn_t * pgn) {
95 bool pgn_next_game(pgn_t * pgn) {
97 char name[PGN_STRING_SIZE];
98 char value[PGN_STRING_SIZE];
104 strcpy(pgn->result,"*");
113 if (pgn->token_type != '[') break;
118 if (pgn->token_type != TOKEN_SYMBOL) {
119 my_fatal("pgn_next_game(): malformed tag at line %d, column %d, game %d\n",pgn->token_line,pgn->token_column,pgn->game_nb);
121 strcpy(name,pgn->token_string);
124 if (pgn->token_type != TOKEN_STRING) {
125 my_fatal("pgn_next_game(): malformed tag at line %d, column %d, game %d\n",pgn->token_line,pgn->token_column,pgn->game_nb);
127 strcpy(value,pgn->token_string);
130 if (pgn->token_type != ']') {
131 my_fatal("pgn_next_game(): malformed tag at line %d, column %d, game %d\n",pgn->token_line,pgn->token_column,pgn->game_nb);
137 } else if (my_string_equal(name,"Result")) {
138 strcpy(pgn->result,value);
139 } else if (my_string_equal(name,"FEN")) {
140 strcpy(pgn->fen,value);
144 if (pgn->token_type == TOKEN_EOF) return FALSE;
146 pgn_token_unread(pgn);
153 bool pgn_next_move(pgn_t * pgn, char string[], int size) {
158 ASSERT(string!=NULL);
159 ASSERT(size>=PGN_STRING_SIZE);
163 pgn->move_line = -1; // DEBUG
164 pgn->move_column = -1; // DEBUG
176 } else if (pgn->token_type == '(') {
182 } else if (pgn->token_type == ')') {
187 my_fatal("pgn_next_move(): malformed variation at line %d, column %d, game %d\n",pgn->token_line,pgn->token_column,pgn->game_nb);
193 } else if (pgn->token_type == TOKEN_RESULT) {
198 my_fatal("pgn_next_move(): malformed variation at line %d, column %d, game %d\n",pgn->token_line,pgn->token_column,pgn->game_nb);
205 // skip optional move number
207 if (pgn->token_type == TOKEN_INTEGER) {
208 do pgn_token_read(pgn); while (pgn->token_type == '.');
211 // move must be a symbol
213 if (pgn->token_type != TOKEN_SYMBOL) {
214 my_fatal("pgn_next_move(): malformed move at line %d, column %d, game %d\n",pgn->token_line,pgn->token_column,pgn->game_nb);
217 // store move for later use
221 if (pgn->token_length >= size) {
222 my_fatal("pgn_next_move(): move too long at line %d, column %d, game %d\n",pgn->token_line,pgn->token_column,pgn->game_nb);
225 strcpy(string,pgn->token_string);
226 pgn->move_line = pgn->token_line;
227 pgn->move_column = pgn->token_column;
230 // skip optional NAGs
232 do pgn_token_read(pgn); while (pgn->token_type == TOKEN_NAG);
233 pgn_token_unread(pgn);
238 if (DispMove) printf("move=\"%s\"\n",string);
251 static void pgn_token_read(pgn_t * pgn) {
257 if (pgn->token_unread) {
258 pgn->token_unread = FALSE;
262 // consume the current token
264 if (pgn->token_first) {
265 pgn->token_first = FALSE;
267 ASSERT(pgn->token_type!=TOKEN_ERROR);
268 ASSERT(pgn->token_type!=TOKEN_EOF);
274 if (pgn->token_type == TOKEN_ERROR) my_fatal("pgn_token_read(): lexical error at line %d, column %d, game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
276 if (DispToken) printf("< L%d C%d \"%s\" (%03X)\n",pgn->token_line,pgn->token_column,pgn->token_string,pgn->token_type);
279 // pgn_token_unread()
281 static void pgn_token_unread(pgn_t * pgn) {
285 ASSERT(!pgn->token_unread);
286 ASSERT(!pgn->token_first);
288 pgn->token_unread = TRUE;
293 static void pgn_read_token(pgn_t * pgn) {
297 // skip white-space characters
299 pgn_skip_blanks(pgn);
303 pgn->token_type = TOKEN_ERROR;
304 strcpy(pgn->token_string,"");
305 pgn->token_length = 0;
306 pgn->token_line = pgn->char_line;
307 pgn->token_column = pgn->char_column;
309 // determine token type
313 } else if (pgn->char_hack == CHAR_EOF) {
315 pgn->token_type = TOKEN_EOF;
317 } else if (strchr(".[]()<>",pgn->char_hack) != NULL) {
319 // single-character token
321 pgn->token_type = pgn->char_hack;
322 sprintf(pgn->token_string,"%c",pgn->char_hack);
323 pgn->token_length = 1;
325 } else if (pgn->char_hack == '*') {
327 pgn->token_type = TOKEN_RESULT;
328 sprintf(pgn->token_string,"%c",pgn->char_hack);
329 pgn->token_length = 1;
331 } else if (pgn->char_hack == '!') {
337 } else if (pgn->char_hack == '!') { // "!!"
339 pgn->token_type = TOKEN_NAG;
340 strcpy(pgn->token_string,"3");
341 pgn->token_length = 1;
343 } else if (pgn->char_hack == '?') { // "!?"
345 pgn->token_type = TOKEN_NAG;
346 strcpy(pgn->token_string,"5");
347 pgn->token_length = 1;
351 pgn_char_unread(pgn);
353 pgn->token_type = TOKEN_NAG;
354 strcpy(pgn->token_string,"1");
355 pgn->token_length = 1;
358 } else if (pgn->char_hack == '?') {
364 } else if (pgn->char_hack == '?') { // "??"
366 pgn->token_type = TOKEN_NAG;
367 strcpy(pgn->token_string,"4");
368 pgn->token_length = 1;
370 } else if (pgn->char_hack == '!') { // "?!"
372 pgn->token_type = TOKEN_NAG;
373 strcpy(pgn->token_string,"6");
374 pgn->token_length = 1;
378 pgn_char_unread(pgn);
380 pgn->token_type = TOKEN_NAG;
381 strcpy(pgn->token_string,"2");
382 pgn->token_length = 1;
385 } else if (is_symbol_start(pgn->char_hack)) {
387 // symbol, integer, or result
389 pgn->token_type = TOKEN_INTEGER;
390 pgn->token_length = 0;
394 if (pgn->token_length >= PGN_STRING_SIZE-1) {
395 my_fatal("pgn_read_token(): symbol too long at line %d, column %d,game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
398 if (!isdigit(pgn->char_hack)) pgn->token_type = TOKEN_SYMBOL;
400 pgn->token_string[pgn->token_length++] = pgn->char_hack;
404 } while (is_symbol_next(pgn->char_hack));
406 pgn_char_unread(pgn);
408 ASSERT(pgn->token_length>0&&pgn->token_length<PGN_STRING_SIZE);
409 pgn->token_string[pgn->token_length] = '\0';
411 if (my_string_equal(pgn->token_string,"1-0")
412 || my_string_equal(pgn->token_string,"0-1")
413 || my_string_equal(pgn->token_string,"1/2-1/2")) {
414 pgn->token_type = TOKEN_RESULT;
417 } else if (pgn->char_hack == '"') {
421 pgn->token_type = TOKEN_STRING;
422 pgn->token_length = 0;
428 if (pgn->char_hack == CHAR_EOF) {
429 my_fatal("pgn_read_token(): EOF in string at line %d, column %d, game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
432 if (pgn->char_hack == '"') break;
434 if (pgn->char_hack == '\\') {
438 if (pgn->char_hack == CHAR_EOF) {
439 my_fatal("pgn_read_token(): EOF in string at line %d, column %d, game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
442 if (pgn->char_hack != '"' && pgn->char_hack != '\\') {
444 // bad escape, ignore
446 if (pgn->token_length >= PGN_STRING_SIZE-1) {
447 my_fatal("pgn_read_token(): string too long at line %d, column %d,game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
450 pgn->token_string[pgn->token_length++] = '\\';
454 if (pgn->token_length >= PGN_STRING_SIZE-1) {
455 my_fatal("pgn_read_token(): string too long at line %d, column %d,game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
458 pgn->token_string[pgn->token_length++] = pgn->char_hack;
461 ASSERT(pgn->token_length>=0&&pgn->token_length<PGN_STRING_SIZE);
462 pgn->token_string[pgn->token_length] = '\0';
464 } else if (pgn->char_hack == '$') {
468 pgn->token_type = TOKEN_NAG;
469 pgn->token_length = 0;
475 if (!isdigit(pgn->char_hack)) break;
477 if (pgn->token_length >= 3) {
478 my_fatal("pgn_read_token(): NAG too long at line %d, column %d, game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
481 pgn->token_string[pgn->token_length++] = pgn->char_hack;
484 pgn_char_unread(pgn);
486 if (pgn->token_length == 0) {
487 my_fatal("pgn_read_token(): malformed NAG at line %d, column %d,game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
490 ASSERT(pgn->token_length>0&&pgn->token_length<=3);
491 pgn->token_string[pgn->token_length] = '\0';
497 my_fatal("lexical error at line %d, column %d, game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
503 static void pgn_skip_blanks(pgn_t * pgn) {
512 }else if(pgn->char_hack==CHAR_EOF){ break;
513 } else if (isspace(pgn->char_hack)) {
517 } else if (pgn->char_hack == ';') {
519 // skip comment to EOL
525 if (pgn->char_hack == CHAR_EOF) {
526 my_fatal("pgn_skip_blanks(): EOF in comment at line %d, column %d,game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
529 } while (pgn->char_hack != '\n');
531 } else if (pgn->char_hack == '%' && pgn->char_column == 0) {
533 // skip comment to EOL
539 if (pgn->char_hack == CHAR_EOF) {
540 my_fatal("pgn_skip_blanks(): EOF in comment at line %d, column %d, game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
543 } while (pgn->char_hack != '\n');
545 } else if (pgn->char_hack == '{') {
547 // skip comment to next '}'
553 if (pgn->char_hack == CHAR_EOF) {
554 my_fatal("pgn_skip_blanks(): EOF in comment at line %d, column %d, game %d\n",pgn->char_line,pgn->char_column,pgn->game_nb);
557 } while (pgn->char_hack != '}');
559 } else { // not a white space
568 static bool is_symbol_start(int c) {
570 return strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",c) != NULL;
575 static bool is_symbol_next(int c) {
577 return strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+#=:-/",c) != NULL;
582 static void pgn_char_read(pgn_t * pgn) {
588 if (pgn->char_unread) {
589 pgn->char_unread = FALSE;
593 // consume the current character
595 if (pgn->char_first) {
597 pgn->char_first = FALSE;
603 ASSERT(pgn->char_hack!=CHAR_EOF);
606 } else if (pgn->char_hack == '\n') {
608 pgn->char_column = 0;
609 } else if (pgn->char_hack == '\t') {
610 pgn->char_column += TAB_SIZE - (pgn->char_column % TAB_SIZE);
616 // read a new character
618 pgn->char_hack = fgetc(pgn->file);
620 if (pgn->char_hack == EOF) {
621 if (ferror(pgn->file)) my_fatal("pgn_char_read(): fgetc(): %s\n",strerror(errno));
622 pgn->char_hack = CHAR_EOF;
625 if (DispChar) printf("< L%d C%d '%c' (%02X)\n",pgn->char_line,pgn->char_column,pgn->char_hack,pgn->char_hack);
630 static void pgn_char_unread(pgn_t * pgn) {
634 ASSERT(!pgn->char_unread);
635 ASSERT(!pgn->char_first);
637 pgn->char_unread = TRUE;