fixed parser.l. Should fix bug #22812
[xboard.git] / parser.l
1 %a 10000
2 %o 10000
3 %e 2000
4 %k 2500
5 %p 7000
6 %n 1000
7 %{
8 /*
9  * parser.l -- lex parser of algebraic chess moves for XBoard
10  * $Id$
11  *
12  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
13  * Enhancements Copyright 1992-95 Free Software Foundation, Inc.
14  *
15  * The following terms apply to Digital Equipment Corporation's copyright
16  * interest in XBoard:
17  * ------------------------------------------------------------------------
18  * All Rights Reserved
19  *
20  * Permission to use, copy, modify, and distribute this software and its
21  * documentation for any purpose and without fee is hereby granted,
22  * provided that the above copyright notice appear in all copies and that
23  * both that copyright notice and this permission notice appear in
24  * supporting documentation, and that the name of Digital not be
25  * used in advertising or publicity pertaining to distribution of the
26  * software without specific, written prior permission.
27  *
28  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
29  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
30  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
31  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
32  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
33  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
34  * SOFTWARE.
35  * ------------------------------------------------------------------------
36  *
37  * The following terms apply to the enhanced version of XBoard distributed
38  * by the Free Software Foundation:
39  * ------------------------------------------------------------------------
40  * This program is free software; you can redistribute it and/or modify
41  * it under the terms of the GNU General Public License as published by
42  * the Free Software Foundation; either version 2 of the License, or
43  * (at your option) any later version.
44  *
45  * This program is distributed in the hope that it will be useful,
46  * but WITHOUT ANY WARRANTY; without even the implied warranty of
47  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
48  * GNU General Public License for more details.
49  *
50  * You should have received a copy of the GNU General Public License
51  * along with this program; if not, write to the Free Software
52  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
53  * ------------------------------------------------------------------------
54  */
55
56 /* This parser handles all forms of promotion.
57  * The parser resolves ambiguous moves by searching and check-testing.
58  * It also parses comments of the form [anything] or (anything).
59  */
60
61 #include "config.h"
62
63 #define NO_CONSTRAINT   -1
64 #undef YYLMAX
65 #define YYLMAX                  4096
66 #define UNPUT_BUF_SIZE          YYLMAX
67
68 #ifdef FLEX_SCANNER
69 /* yytext is probably a char*, but could be a char[].  yy_text is set
70    in YY_DECL below, because if yytext is a char*, its value is not
71    constant. */
72 char *yy_text;
73 #else /*!FLEX_SCANNER*/
74 /* yytext is definitely a char[], so yy_text can be set here, statically. */
75 char *yy_text = (char *) yytext;
76 #endif
77
78 #ifdef FLEX_SCANNER
79 /* This is flex */
80 /* use prototypes in function declarations */
81 #define YY_USE_PROTOS
82
83 #ifdef YY_USE_PROTOS
84 #define YY_PROTO(proto) proto
85 #else
86 #define YY_PROTO(proto) ()
87 #endif
88
89 #undef YY_INPUT
90 #define YY_INPUT(buf, result, max_size) my_yy_input(buf, &result, max_size)
91 #undef YY_DECL
92 #define YY_DECL                     \
93     int _yylex YY_PROTO((void));    \
94     int yylex YY_PROTO((void))      \
95     {                               \
96         int result = _yylex();      \
97         yy_text = (char *) yytext;  \
98         return(result);             \
99     }                               \
100     int _yylex YY_PROTO((void))
101 #else
102 /* This is lex */
103 #undef input
104 #undef output
105 #undef unput
106 #endif
107
108 /* The includes must be here, below the #undef input */
109
110 #include <ctype.h>
111
112 #if STDC_HEADERS
113 # include <stdlib.h>
114 # include <string.h>
115 #else /* not STDC_HEADERS */
116 # if HAVE_STRING_H
117 #  include <string.h>
118 # else /* not HAVE_STRING_H */
119 #  include <strings.h>
120 # endif /* not HAVE_STRING_H */
121 #endif /* not STDC_HEADERS */
122
123 #if HAVE_UNISTD_H
124 # include <unistd.h>
125 #endif
126
127 #if defined(_amigados)
128 # include <errno.h>
129 # if HAVE_FCNTL_H
130 #  include <fcntl.h>    /*  isatty() prototype  */
131 # endif /*  HAVE_FCNTL_H        */
132 #endif  /*  defined(_amigados)  */
133
134 #include "common.h"
135 #include "backend.h"
136 #include "frontend.h"
137 #include "parser.h"
138 #include "moves.h"
139
140 extern int PosFlags P((int));
141
142 extern Board    boards[MAX_MOVES];
143 int             yyboardindex;
144 int             yyskipmoves = FALSE;
145 char            currentMoveString[YYLMAX];
146 #ifndef FLEX_SCANNER
147 char            unputBuffer[UNPUT_BUF_SIZE];
148 int             unputCount = 0;
149 #endif
150
151 #ifdef FLEX_SCANNER
152 void my_yy_input P((char *buf, int *result, int max_size));
153 #else /*!FLEX_SCANNER*/
154 static int input P((void));
155 static void output P((int ch));
156 static void unput P((int ch));
157 int yylook P((void));
158 int yyback P((int *, int));
159 #endif
160 #undef yywrap
161 int yywrap P((void));
162 extern void CopyBoard P((Board to, Board from));
163
164 %}
165 %%
166
167 [RrBbNnQqKkPp][/]?[a-h][1-8][xX:-]?[a-h][1-8](=?\(?[RrBbNnQqKk]\)?)? {
168     /*
169      * Fully-qualified algebraic move, possibly with promotion
170      */
171     int skip1 = 0, skip2 = 0;
172     ChessSquare piece;
173     ChessMove result;
174     
175     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
176
177     /* remove the / */
178     if (yytext[1] == '/') skip1 = 1;
179     
180     /* remove the [xX:-] */
181     if ((yytext[3+skip1] == 'x') || (yytext[3+skip1] == 'X') ||
182         (yytext[3+skip1] == '-') || (yytext[3+skip1] == ':')) skip2 = 1;
183     
184     currentMoveString[0] = yytext[1+skip1];
185     currentMoveString[1] = yytext[2+skip1];
186     currentMoveString[2] = yytext[3+skip1+skip2];
187     currentMoveString[3] = yytext[4+skip1+skip2];
188     currentMoveString[4] = NULLCHAR;
189     
190     if (yyleng-skip1-skip2 > 5) {
191         if (yytext[yyleng-1] == ')') {
192             currentMoveString[4] = ToLower(yytext[yyleng-2]);
193         } else {
194             currentMoveString[4] = ToLower(yytext[yyleng-1]);
195         }
196         currentMoveString[5] = NULLCHAR;
197     }
198
199     piece = boards[yyboardindex]
200       [currentMoveString[1] - '1'][currentMoveString[0] - 'a'];
201     if (ToLower(yytext[0]) != ToLower(PieceToChar(piece)))
202       return (int) IllegalMove;
203
204     result = LegalityTest(boards[yyboardindex],
205                           PosFlags(yyboardindex), EP_UNKNOWN,
206                           currentMoveString[1] - '1',
207                           currentMoveString[0] - 'a',
208                           currentMoveString[3] - '1',
209                           currentMoveString[2] - 'a',
210                           currentMoveString[4]);
211
212     if (currentMoveString[4] == NULLCHAR &&
213         (result == WhitePromotionQueen || result == BlackPromotionQueen)) {
214         currentMoveString[4] = 'q';
215         currentMoveString[5] = NULLCHAR;
216     }
217
218     return (int) result;
219 }
220
221 [a-h][1-8][xX:-]?[a-h][1-8](=?\(?[RrBbNnQqKk]\)?)?      {
222     /*
223      * Simple algebraic move, possibly with promotion
224      */
225     int skip = 0;
226     ChessMove result;
227
228     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
229
230     /* remove the [xX:-] */
231     if ((yytext[2] == 'x') || (yytext[2] == 'X') ||
232         (yytext[2] == '-') || (yytext[2] == ':')) skip = 1;
233
234     currentMoveString[0] = yytext[0];
235     currentMoveString[1] = yytext[1];
236     currentMoveString[2] = yytext[2+skip];
237     currentMoveString[3] = yytext[3+skip];
238     currentMoveString[4] = NULLCHAR;
239
240     if (yyleng-skip > 4) {
241         if (yytext[yyleng-1] == ')') {
242             currentMoveString[4] = ToLower(yytext[yyleng-2]);
243         } else {
244             currentMoveString[4] = ToLower(yytext[yyleng-1]);
245         }
246         currentMoveString[5] = NULLCHAR;
247     }
248
249     result = LegalityTest(boards[yyboardindex],
250                           PosFlags(yyboardindex), EP_UNKNOWN,
251                           currentMoveString[1] - '1',
252                           currentMoveString[0] - 'a',
253                           currentMoveString[3] - '1',
254                           currentMoveString[2] - 'a',
255                           currentMoveString[4]);
256
257     if (currentMoveString[4] == NULLCHAR &&
258         (result == WhitePromotionQueen || result == BlackPromotionQueen)) {
259         currentMoveString[4] = 'q';
260         currentMoveString[5] = NULLCHAR;
261     }
262
263     return (int) result;
264 }
265
266 [a-h][1-8](=?\(?[RrBbNnQqKk]\)?)?       {
267     /*
268      * Pawn move, possibly with promotion
269      */
270     DisambiguateClosure cl;
271     int skip = 0;
272
273     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
274
275     /* remove the =() */
276     if (yytext[2] == '=') skip++;
277     if (yytext[2+skip] == '(') skip++;
278
279     cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn;
280     cl.rfIn = -1;
281     cl.ffIn = yytext[0] - 'a';
282     cl.rtIn = yytext[1] - '1';
283     cl.ftIn = yytext[0] - 'a';
284     cl.promoCharIn = yytext[2+skip];
285     Disambiguate(boards[yyboardindex],
286                  PosFlags(yyboardindex), EP_UNKNOWN, &cl);
287
288     currentMoveString[0] = cl.ff + 'a';
289     currentMoveString[1] = cl.rf + '1';
290     currentMoveString[2] = cl.ft + 'a';
291     currentMoveString[3] = cl.rt + '1';
292     currentMoveString[4] = cl.promoChar;
293     currentMoveString[5] = NULLCHAR;
294
295     return (int) cl.kind;
296 }
297
298
299 (ab|bc|cd|de|ef|fg|gh|hg|gf|fe|ed|dc|cb|ba|([a-h][xX:-][a-h]))(=?\(?[RrBbNnQqKk]\)?)?(ep|"e.p.")? {
300     /*
301      * Pawn capture, possibly with promotion, possibly ambiguous
302      */
303     DisambiguateClosure cl;
304     int skip1 = 0, skip2 = 0;
305
306     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
307
308     /* remove trailing ep or e.p. (nonstandard PGN) */
309     if (yytext[yyleng-1] == 'p') {
310       yyleng -= 2;
311       yytext[yyleng] = NULLCHAR;
312     } else if (yytext[yyleng-1] == '.') {
313       yyleng -= 4;
314       yytext[yyleng] = NULLCHAR;
315     }
316
317     /* remove the [xX:-] and =() */
318     if ((yytext[1] == 'x') || (yytext[1] == 'X')
319         || (yytext[1] == ':') || (yytext[1] == '-')) skip1 = 1;
320     if (yytext[2+skip1] == '=') skip2++;
321     if (yytext[2+skip1+skip2] == '(') skip2++;
322
323     cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn;
324     cl.rfIn = -1;
325     cl.ffIn = yytext[0] - 'a';
326     cl.rtIn = -1;
327     cl.ftIn = yytext[1+skip1] - 'a';
328     cl.promoCharIn = yytext[2+skip1+skip2];
329     Disambiguate(boards[yyboardindex],
330                  PosFlags(yyboardindex), EP_UNKNOWN, &cl);
331
332     currentMoveString[0] = cl.ff + 'a';
333     currentMoveString[1] = cl.rf + '1';
334     currentMoveString[2] = cl.ft + 'a';
335     currentMoveString[3] = cl.rt + '1';
336     currentMoveString[4] = cl.promoChar;
337     currentMoveString[5] = NULLCHAR;
338
339     return (int) cl.kind;
340 }
341
342 [a-h][xX:]?[a-h][1-8](=?\(?[RrBbNnQqKk]\)?)?(ep|"e.p.")? {
343     /*
344      * unambiguously abbreviated Pawn capture, possibly with promotion
345      */
346     int skip = 0;
347     ChessMove result;
348
349     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
350
351     /* remove trailing ep or e.p. (nonstandard PGN) */
352     if (yytext[yyleng-1] == 'p') {
353       yyleng -= 2;
354       yytext[yyleng] = NULLCHAR;
355     } else if (yytext[yyleng-1] == '.') {
356       yyleng -= 4;
357       yytext[yyleng] = NULLCHAR;
358     }
359
360     /* remove the [xX:-] */
361     if ((yytext[1] == 'x') || (yytext[1] == 'X')
362         || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1;
363
364     currentMoveString[0] = yytext[0];
365     currentMoveString[2] = yytext[1+skip];
366     currentMoveString[3] = yytext[2+skip];
367     if (WhiteOnMove(yyboardindex)) {
368         if (yytext[2+skip] == '1') return (int) ImpossibleMove;
369         currentMoveString[1] = yytext[2+skip] - 1;
370     } else {
371         if (yytext[2+skip] == '8') return (int) ImpossibleMove;
372         currentMoveString[1] = yytext[2+skip] + 1;
373     }
374     if (yyleng-skip > 3) {
375         if (yytext[yyleng-1] == ')')
376           currentMoveString[4] = ToLower(yytext[yyleng-2]);
377         else
378           currentMoveString[4] = ToLower(yytext[yyleng-1]);
379         currentMoveString[5] = NULLCHAR;
380     } else {
381         currentMoveString[4] = NULLCHAR;
382     }
383
384     result = LegalityTest(boards[yyboardindex],
385                           PosFlags(yyboardindex), EP_UNKNOWN,
386                           currentMoveString[1] - '1',
387                           currentMoveString[0] - 'a',
388                           currentMoveString[3] - '1',
389                           currentMoveString[2] - 'a',
390                           currentMoveString[4]);
391
392     if (currentMoveString[4] == NULLCHAR &&
393         (result == WhitePromotionQueen || result == BlackPromotionQueen)) {
394         currentMoveString[4] = 'q';
395         currentMoveString[5] = NULLCHAR;
396     }
397
398     if (result != IllegalMove) return (int) result;
399
400     /* Special case: improperly written en passant capture */
401     if (WhiteOnMove(yyboardindex)) {
402         if (currentMoveString[3] == '5') {
403             currentMoveString[1] = '5';
404             currentMoveString[3] = '6';
405         } else {
406             return (int) IllegalMove;
407         }
408     } else {
409         if (currentMoveString[3] == '4') {
410             currentMoveString[1] = '4';
411             currentMoveString[3] = '3';
412         } else {
413             return (int) IllegalMove;
414         }
415     }
416
417     result = LegalityTest(boards[yyboardindex],
418                           PosFlags(yyboardindex), EP_UNKNOWN,
419                           currentMoveString[1] - '1',
420                           currentMoveString[0] - 'a',
421                           currentMoveString[3] - '1',
422                           currentMoveString[2] - 'a',
423                           currentMoveString[4]);
424
425     if (result == WhiteCapturesEnPassant || result == BlackCapturesEnPassant)
426       return (int) result;
427     else
428       return (int) IllegalMove;
429 }
430
431 [RrBbNnQqKk][xX:-]?[a-h][1-8]  {
432     /*
433      * piece move, possibly ambiguous
434      */
435     DisambiguateClosure cl;
436     int skip = 0;
437
438     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
439
440     /* remove the [xX:-] */
441     if ((yytext[1] == 'x') || (yytext[1] == 'X')
442         || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1;
443
444     if (WhiteOnMove(yyboardindex)) {
445         cl.pieceIn = CharToPiece(ToUpper(yytext[0]));
446     } else {
447         cl.pieceIn = CharToPiece(ToLower(yytext[0]));
448     }
449     cl.rfIn = -1;
450     cl.ffIn = -1;
451     cl.rtIn = yytext[2+skip] - '1';
452     cl.ftIn = yytext[1+skip] - 'a';
453     cl.promoCharIn = NULLCHAR;
454     Disambiguate(boards[yyboardindex],
455                  PosFlags(yyboardindex), EP_UNKNOWN, &cl);
456
457     currentMoveString[0] = cl.ff + 'a';
458     currentMoveString[1] = cl.rf + '1';
459     currentMoveString[2] = cl.ft + 'a';
460     currentMoveString[3] = cl.rt + '1';
461     currentMoveString[4] = cl.promoChar;
462     currentMoveString[5] = NULLCHAR;
463
464     return (int) cl.kind;
465 }
466
467 [RrBbNnQqKk][a-h1-8][xX:-]?[a-h][1-8]   {
468     /*
469      * piece move with rank or file disambiguator
470      */
471     DisambiguateClosure cl;
472     int skip = 0;
473
474     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
475
476     /* remove the [xX:-] */
477     if ((yytext[2] == 'x') || (yytext[2] == 'X')
478         || (yytext[2] == ':') || (yytext[2] == '-')) skip = 1;
479
480     if (WhiteOnMove(yyboardindex)) {
481         cl.pieceIn = CharToPiece(ToUpper(yytext[0]));
482     } else {
483         cl.pieceIn = CharToPiece(ToLower(yytext[0]));
484     }
485     if (isalpha(yytext[1])) {
486         cl.rfIn = -1;
487         cl.ffIn = yytext[1] - 'a';
488     } else {
489         cl.rfIn = yytext[1] - '1';
490         cl.ffIn = -1;
491     }
492     cl.rtIn = yytext[3+skip] - '1';
493     cl.ftIn = yytext[2+skip] - 'a';
494     cl.promoCharIn = NULLCHAR;
495     Disambiguate(boards[yyboardindex],
496                  PosFlags(yyboardindex), EP_UNKNOWN, &cl);
497
498     currentMoveString[0] = cl.ff + 'a';
499     currentMoveString[1] = cl.rf + '1';
500     currentMoveString[2] = cl.ft + 'a';
501     currentMoveString[3] = cl.rt + '1';
502     currentMoveString[4] = cl.promoChar;
503     currentMoveString[5] = NULLCHAR;
504
505     return (int) cl.kind;
506 }
507
508 000|0-0-0|ooo|OOO|o-o-o|O-O-O   {
509     int rf, ff, rt, ft;
510
511     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
512
513     if (WhiteOnMove(yyboardindex)) {
514         if (boards[yyboardindex][0][3] == WhiteKing) {
515             /* ICS wild castling */
516             strcpy(currentMoveString, "d1f1");
517             rf = 0;
518             ff = 3;
519             rt = 0;
520             ft = 5;
521         } else {
522             strcpy(currentMoveString, "e1c1");
523             rf = 0;
524             ff = 4;
525             rt = 0;
526             ft = 2;
527         }
528     } else{ 
529         if (boards[yyboardindex][7][3] == BlackKing) {
530             /* ICS wild castling */
531             strcpy(currentMoveString, "d8f8");
532             rf = 7;
533             ff = 3;
534             rt = 7;
535             ft = 5;
536         } else {
537             strcpy(currentMoveString, "e8c8");
538             rf = 7;
539             ff = 4;
540             rt = 7;
541             ft = 2;
542         }
543     }
544     return (int) LegalityTest(boards[yyboardindex],
545                               PosFlags(yyboardindex), EP_UNKNOWN,
546                               rf, ff, rt, ft, NULLCHAR);
547 }
548
549 00|0-0|oo|OO|o-o|O-O    {
550     int rf, ff, rt, ft;
551
552     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
553
554     if (WhiteOnMove(yyboardindex)) {
555         if (boards[yyboardindex][0][3] == WhiteKing) {
556             /* ICS wild castling */
557             strcpy(currentMoveString, "d1b1");
558             rf = 0;
559             ff = 3;
560             rt = 0;
561             ft = 1;
562         } else {
563             strcpy(currentMoveString, "e1g1");
564             rf = 0;
565             ff = 4;
566             rt = 0;
567             ft = 6;
568         }
569     } else {
570         if (boards[yyboardindex][7][3] == BlackKing) {
571             /* ICS wild castling */
572             strcpy(currentMoveString, "d8b8");
573             rf = 7;
574             ff = 3;
575             rt = 7;
576             ft = 1;
577         } else {
578             strcpy(currentMoveString, "e8g8");
579             rf = 7;
580             ff = 4;
581             rt = 7;
582             ft = 6;
583         }
584     }
585     return (int) LegalityTest(boards[yyboardindex],
586                               PosFlags(yyboardindex), EP_UNKNOWN,
587                               rf, ff, rt, ft, NULLCHAR);
588 }
589
590 [PpNnBbRrQq]@[a-h][1-8] {
591     /* Bughouse piece drop.  No legality checking for now. */
592     currentMoveString[1] = '@';
593     currentMoveString[2] = yytext[2];
594     currentMoveString[3] = yytext[3];
595     currentMoveString[4] = NULLCHAR;
596     if (WhiteOnMove(yyboardindex)) {
597         currentMoveString[0] = ToUpper(yytext[0]);
598         return (int) WhiteDrop;
599     } else {
600         currentMoveString[0] = ToLower(yytext[0]);
601         return (int) BlackDrop;
602     }
603 }
604
605 [Rr]esign(s|ed)?  {
606     if (WhiteOnMove(yyboardindex))
607       return (int) BlackWins;
608     else
609       return (int) WhiteWins;
610 }
611
612 (([Ww](hite)?)|([Bb](lack)?))" "(([Rr]esign)|([Ff]orfeit))(s|ed)?  {
613     return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins);
614 }
615
616 (([Ww](hite)?)|([Bb](lack)?))" "[Dd]isconnect(s|ed)  {
617     return (int) GameUnfinished;
618 }
619
620 [Ss]talemate  {
621     return (int) GameIsDrawn;
622 }
623
624 "+-+"  {
625     return (int) GameIsDrawn;
626 }
627
628 ([Cc]heck)?[Mm]ate {
629     if (WhiteOnMove(yyboardindex))
630       return (int) BlackWins;
631     else
632       return (int) WhiteWins;
633 }
634
635 "++"  {
636     if (WhiteOnMove(yyboardindex))
637       return (int) BlackWins;
638     else
639       return (int) WhiteWins;
640 }
641
642 [Dd]raw(n)?(" "by)?(" "[Rr]epetition)|(" "[Aa]gree(d|ment))  {
643     return (int) GameIsDrawn;
644 }
645
646 [Dd]raw(n)?(" (".*")")?  {
647     return (int) GameIsDrawn;
648 }
649
650 (([Ww](hite)?)|([Bb](lack)?))" "([Mm]ate(s|ed)?)|([Ww][io]n(s)?.*)  {
651     return (int) (ToUpper(yytext[0]) == 'W' ? WhiteWins : BlackWins);
652 }
653
654 (([Ww](hite)?)|([Bb](lack)?))" "([Mm]ate(s|ed)?)|([Ll]os[tes]+.*)  {
655     return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins);
656 }
657
658 ("{"[^\}\n]*"} ")?(1-0|"1 - 0"|"1/0"|"1 / 0"|"1:0"|"1 : 0")(" (".*")"|" {".*"}")? { 
659     return (int) WhiteWins;
660 }
661
662 ("{"[^\}\n]*"} ")?(0-1|"0 - 1"|"0/1"|"0 / 1"|"0:1"|"0 : 1")(" (".*")"|" {".*"}")? { 
663     return (int) BlackWins;
664 }
665
666 ("{"[^\}\n]*"} ")?("1/2"|"1 / 2")(" "?[-:]" "?("1/2"|"1 / 2"))?(" (".*")"|" {".*"}")? {
667     return (int) GameIsDrawn;
668 }
669
670 ("{"[^\}\n]*"} ")?"*"(" (".*")"|" {".*"}")? {
671     return (int) GameUnfinished;
672 }
673
674 [1-9][0-9]*/"."?[ \t\n]*[a-hNnPpRrBQqKkOo]    {
675     /* move numbers */
676     if ((yyleng == 1) && (yytext[0] == '1'))
677       return (int) MoveNumberOne;
678 }
679
680 \([0-9]+:[0-9][0-9](\.[0-9]+)?\)|\{[0-9]+:[0-9][0-9](\.[0-9]+)?\} {
681     /* elapsed time indication, e.g. (0:12) or {10:21.071} */ 
682     return (int) ElapsedTime;
683 }
684
685 "[--"[^\]]*"--]" {
686     /* position diagram enclosed in [-- --] */
687     return (int) PositionDiagram;
688 }
689
690 ^"{--------------"\n[^\}]*\n"--------------}"$ {
691     /* position diagram enclosed in {-- --} */
692     return (int) PositionDiagram;
693 }
694
695 \[[ \t\n]*[A-Za-z0-9][A-Za-z0-9_+#=-]*[ \t\n]*\"[^"]*\"[ \t\n]*\] {
696     return (int) PGNTag;
697 }    
698
699 [Gg](nu|NU)" "?[Cc](hess|HESS).*[Gg](ame|AME) {
700     return (int) GNUChessGame;
701 }
702
703 ^[#;%]" "[^ ]*(" game file"|" position file").*$ {
704     return (int) XBoardGame;
705 }
706
707 \$[0-9]+        {                               /* numeric annotation glyph */
708     return (int) NAG;
709 }
710
711 \{[^\}]*\}      {                               /* anything in {} */
712     return (int) Comment; 
713 }
714
715 ;.*$ {                                          /* ; to end of line */
716     return (int) Comment;
717 }
718
719 \[[^\]]*\]      {                               /* anything in [] */
720     return (int) Comment; 
721 }
722
723 \([^()]*(\([^()]*\)[^()]*)+[^()]*\)  {          /* nested () */
724     return (int) Comment; 
725 }
726
727 \([^)][^)]+\)   {                               /* >=2 chars in () */
728     return (int) Comment; 
729 }       
730
731 ^[-a-zA-Z0-9]+:" ".*(\n[ \t]+.*)*  {
732         /* Skip mail headers */
733 }
734
735 [a-zA-Z0-9'-]+                  {
736         /* Skip random words */
737 }
738
739 .|\n                            {
740         /* Skip everything else */
741 }
742
743 %%
744
745
746 static char *StringToLex;
747
748 #ifndef FLEX_SCANNER
749 static FILE *lexFP;
750
751 static int input()
752 {
753     int ret;
754     
755     if (StringToLex != NULL) {
756         ret = *StringToLex;
757         if (ret == NULLCHAR)
758           ret = EOF;
759         else
760           StringToLex++;
761     } else if (unputCount > 0) {
762         ret = unputBuffer[--unputCount];
763     } else {
764         ret = fgetc(lexFP);
765     }    
766
767     if (ret == EOF) 
768       return 0;
769     else
770       return ret;
771 }
772
773 /*
774  * Return offset of next pattern within current file
775  */
776 int yyoffset()
777 {
778     int offset = ftell(lexFP) - unputCount;
779
780     if (offset < 0) {
781         offset = 0;
782     }
783     return(offset);
784 }
785  
786 static void output(ch)
787      int ch;
788 {
789     fprintf(stderr, "PARSER BUG: unmatched character '%c' (0%o)\n",
790             ch, ch);
791 }
792
793 static void unput(ch)
794      int ch;
795 {
796     if (ch == 0) return;
797     if (StringToLex != NULL) {
798         StringToLex--;
799     } else {
800         if (unputCount >= UNPUT_BUF_SIZE)
801           fprintf(stderr, "PARSER BUG: unput buffer overflow '%c' (0%o)\n",
802                   ch, ch);
803         unputBuffer[unputCount++] = ch;
804     }
805 }
806
807 /* Get ready to lex from a new file.  Kludge below sticks
808    an artificial newline at the front of the file, which the
809    above grammar ignores, but which makes ^ at start of pattern
810    match at the real start of the file.
811 */
812 void yynewfile(f)
813      FILE *f;
814 {
815     lexFP = f;
816     StringToLex = NULL;
817     unputCount = 0;
818     unput('\n'); /* kludge */
819 }
820
821 /* Get ready to lex from a string.  ^ at start of pattern WON'T
822    match at the start of the string!
823 */
824 void yynewstr(s)
825      char *s;
826 {
827     lexFP = NULL;
828     StringToLex = s;
829     unputCount = 0;
830 }
831 #endif /*!FLEX_SCANNER*/
832
833 #ifdef FLEX_SCANNER
834 void my_yy_input(buf, result, max_size)
835      char *buf;
836      int *result;
837      int max_size;
838 {
839     int count;
840
841     if (StringToLex != NULL) {
842         count = 0;
843         while (*StringToLex != NULLCHAR) {
844             *buf++ = *StringToLex++;
845             count++;
846         }
847         *result = count;
848         return;
849     } else {
850         count = fread(buf, 1, max_size, yyin);
851         if (count == 0) {
852             *result = YY_NULL;
853         } else {
854             *result = count;
855         }
856         return;
857     }    
858 }
859
860 static YY_BUFFER_STATE my_file_buffer = NULL;
861
862 /*
863     Return offset of next pattern in the current file.
864 */
865 int yyoffset()
866 {
867     int pos = yy_c_buf_p - YY_CURRENT_BUFFER->yy_ch_buf;
868
869     return(ftell(YY_CURRENT_BUFFER->yy_input_file) -
870          yy_n_chars + pos);
871 }
872
873
874 void yynewstr(s)
875      char *s;
876 {
877     if (my_file_buffer != NULL)
878       yy_delete_buffer(my_file_buffer);
879     StringToLex = s;
880     my_file_buffer = yy_create_buffer(stdin, YY_BUF_SIZE);
881     yy_switch_to_buffer(my_file_buffer);
882 }
883
884 void yynewfile(f)
885      FILE *f;
886 {
887     if (my_file_buffer != NULL)
888       yy_delete_buffer(my_file_buffer);
889     StringToLex = NULL;
890     my_file_buffer = yy_create_buffer(f, YY_BUF_SIZE);
891     yy_switch_to_buffer(my_file_buffer);
892 }
893 #endif /*FLEX_SCANNER*/
894
895 int yywrap()
896 {
897     return TRUE;
898 }
899
900 /* Parse a move from the given string s */
901 /* ^ at start of pattern WON'T work here unless using flex */
902 ChessMove yylexstr(boardIndex, s)
903      int boardIndex;
904      char *s;
905 {
906     ChessMove ret;
907     char *oldStringToLex;
908 #ifdef FLEX_SCANNER
909     YY_BUFFER_STATE buffer, oldBuffer;
910 #endif
911     
912     yyboardindex = boardIndex;
913     oldStringToLex = StringToLex;
914     StringToLex = s;
915 #ifdef FLEX_SCANNER
916     buffer = yy_create_buffer(stdin, YY_BUF_SIZE);
917     oldBuffer = YY_CURRENT_BUFFER;
918     yy_switch_to_buffer(buffer);
919 #endif /*FLEX_SCANNER*/
920
921     ret = (ChessMove) yylex();
922
923 #ifdef FLEX_SCANNER
924     if (oldBuffer != NULL) 
925       yy_switch_to_buffer(oldBuffer);
926     yy_delete_buffer(buffer);
927 #endif /*FLEX_SCANNER*/
928     StringToLex = oldStringToLex;
929
930     return ret;
931 }