Remove promotion-piece encoding from ChessMove type
[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  *
11  * Copyright 1991 by Digital Equipment Corporation, Maynard,
12  * Massachusetts.
13  *
14  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005,
15  * 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
16  *
17  * The following terms apply to Digital Equipment Corporation's copyright
18  * interest in XBoard:
19  * ------------------------------------------------------------------------
20  * All Rights Reserved
21  *
22  * Permission to use, copy, modify, and distribute this software and its
23  * documentation for any purpose and without fee is hereby granted,
24  * provided that the above copyright notice appear in all copies and that
25  * both that copyright notice and this permission notice appear in
26  * supporting documentation, and that the name of Digital not be
27  * used in advertising or publicity pertaining to distribution of the
28  * software without specific, written prior permission.
29  *
30  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
31  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
32  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
33  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
34  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
35  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
36  * SOFTWARE.
37  * ------------------------------------------------------------------------
38  *
39  * The following terms apply to the enhanced version of XBoard
40  * distributed by the Free Software Foundation:
41  * ------------------------------------------------------------------------
42  *
43  * GNU XBoard is free software: you can redistribute it and/or modify
44  * it under the terms of the GNU General Public License as published by
45  * the Free Software Foundation, either version 3 of the License, or (at
46  * your option) any later version.
47  *
48  * GNU XBoard is distributed in the hope that it will be useful, but
49  * WITHOUT ANY WARRANTY; without even the implied warranty of
50  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
51  * General Public License for more details.
52  *
53  * You should have received a copy of the GNU General Public License
54  * along with this program. If not, see http://www.gnu.org/licenses/.  
55  *
56  *------------------------------------------------------------------------
57  ** See the file ChangeLog for a revision history.  */
58
59 /* This parser handles all forms of promotion.
60  * The parser resolves ambiguous moves by searching and check-testing.
61  * It also parses comments of the form [anything] or (anything).
62  *
63  * [HGM] Parser extensively modified for bigger boards, Shogi-like syntax,
64  * and unknow pieces. All pieces are now mandatory upper case, but can be
65  * any letter A-Z. Files must be lower case (as before), but can run upto 'l'.
66  * Ranks can be 0-9. The parser returns 0 for off-board files and ranks.
67  * For an unknown piece (as mover or promotion piece) it returns
68  * IllegalMove, like it does when the piece doesn't match.
69  * Promotions can now also be appended Shogi-style, a bare '=' or '+',
70  * and this is then returned as promotion character. The piece indicator
71  * can be prefixed by a '+' to indicate it is a promoted piece.
72  */
73
74 #include "config.h"
75
76 #define NO_CONSTRAINT   -1
77 #undef YYLMAX
78 #define YYLMAX                  4096
79 #define UNPUT_BUF_SIZE          YYLMAX
80
81 #ifdef FLEX_SCANNER
82 /* yytext is probably a char*, but could be a char[].  yy_text is set
83    in YY_DECL below, because if yytext is a char*, its value is not
84    constant. */
85 char *yy_text;
86 #else /*!FLEX_SCANNER*/
87 /* yytext is definitely a char[], so yy_text can be set here, statically. */
88 char *yy_text = (char *) yytext;
89 #endif
90
91 #ifdef FLEX_SCANNER
92 /* This is flex */
93 /* [AP] use prototypes in function declarations */
94 #define YY_USE_PROTOS
95
96 #ifdef YY_USE_PROTOS
97 #define YY_PROTO(proto) proto
98 #else
99 #define YY_PROTO(proto) ()
100 #endif
101 /* end of [AP] fix */
102
103 #undef YY_INPUT
104 #define YY_INPUT(buf, result, max_size) my_yy_input(buf, &result, max_size)
105 #undef YY_DECL
106 #define YY_DECL                     \
107     int _yylex YY_PROTO((void));    \
108     int yylex YY_PROTO((void))      \
109     {                               \
110         int result = _yylex();      \
111         yy_text = (char *) yytext;  \
112         return(result);             \
113     }                               \
114     int _yylex YY_PROTO((void))
115 #else
116 /* This is lex */
117 #undef input
118 #undef output
119 #undef unput
120 #endif
121
122 /* The includes must be here, below the #undef input */
123
124 #include <ctype.h>
125
126 #if STDC_HEADERS
127 # include <stdlib.h>
128 # include <string.h>
129 #else /* not STDC_HEADERS */
130 # if HAVE_STRING_H
131 #  include <string.h>
132 # else /* not HAVE_STRING_H */
133 #  include <strings.h>
134 # endif /* not HAVE_STRING_H */
135 #endif /* not STDC_HEADERS */
136
137 #if HAVE_UNISTD_H
138 # include <unistd.h>
139 #endif
140
141 #if defined(_amigados)
142 # include <errno.h>
143 # if HAVE_FCNTL_H
144 #  include <fcntl.h>    /*  isatty() prototype  */
145 # endif /*  HAVE_FCNTL_H        */
146 #endif  /*  defined(_amigados)  */
147
148 #include "common.h"
149 #include "backend.h"
150 #include "frontend.h"
151 #include "parser.h"
152 #include "moves.h"
153
154 extern int PosFlags P((int));
155
156 extern Board    boards[MAX_MOVES];
157 int             yyboardindex;
158 int             yyskipmoves = FALSE;
159 char            currentMoveString[YYLMAX];
160 #ifndef FLEX_SCANNER
161 char            unputBuffer[UNPUT_BUF_SIZE];
162 int             unputCount = 0;
163 #endif
164
165 #ifdef FLEX_SCANNER
166 void my_yy_input P((char *buf, int *result, int max_size));
167 #else /*!FLEX_SCANNER*/
168 static int input P((void));
169 static void output P((int ch));
170 static void unput P((int ch));
171 int yylook P((void));
172 int yyback P((int *, int));
173 #endif
174 #undef yywrap
175 int yywrap P((void));
176 extern void CopyBoard P((Board to, Board from));
177
178 %}
179 %%
180
181 "+"?[A-Z][/]?[a-l][0-9][xX:-]?[a-l][0-9]((=?\(?[A-Z]\)?)|=)? {
182     /*
183      * Fully-qualified algebraic move, possibly with promotion
184      */
185     int skip1 = 0, skip2 = 0, skip3 = 0, promoted = 0;
186     ChessSquare piece;
187     ChessMove result;
188     char c;
189     
190     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
191
192     if (yytext[0] == '+') skip1 = skip3 = promoted = 1; /* [HGM] Shogi promoted */
193
194     /* remove the / */
195     if (yytext[1+skip1] == '/')  skip1++; 
196     
197     /* remove the [xX:-] */
198     if ((yytext[3+skip1] == 'x') || (yytext[3+skip1] == 'X') ||
199         (yytext[3+skip1] == '-') || (yytext[3+skip1] == ':')) skip2 = 1;
200     
201     currentMoveString[0] = yytext[1+skip1];
202     currentMoveString[1] = yytext[2+skip1];
203     currentMoveString[2] = yytext[3+skip1+skip2];
204     currentMoveString[3] = yytext[4+skip1+skip2];
205     currentMoveString[4] = NULLCHAR;
206     
207     if (appData.debugMode) {
208         fprintf(debugFP, "Parser Qa1b2: yyleng=%d\n",
209         yyleng);
210     }
211
212     if (yyleng-skip1-skip2 > 5) { char c;
213         if (yytext[yyleng-1] == ')') {
214             c = currentMoveString[4] = ToLower(yytext[yyleng-2]);
215         } else {
216             c = currentMoveString[4] = ToLower(yytext[yyleng-1]);
217         }
218         currentMoveString[5] = NULLCHAR;
219         if(c != '=' && c != '+' && CharToPiece(c) == EmptySquare)
220             return IllegalMove; /* [HGM] promotion to invalid piece */
221     }
222
223     if (appData.debugMode) {
224         fprintf(debugFP, "parser: %s\n", currentMoveString);
225     }
226     /* [HGM] do not allow values beyond board size */
227     if(currentMoveString[1] - ONE >= BOARD_HEIGHT ||
228        currentMoveString[1] - ONE <  0            ||
229        currentMoveString[0] - AAA >= BOARD_RGHT   ||
230        currentMoveString[3] - ONE >= BOARD_HEIGHT ||
231        currentMoveString[3] - ONE <  0            ||
232        currentMoveString[2] - AAA >= BOARD_RGHT   ||
233        currentMoveString[0] - AAA <  BOARD_LEFT   ||
234        currentMoveString[2] - AAA <  BOARD_LEFT     )
235       return ImpossibleMove;
236
237     piece = boards[yyboardindex]
238       [currentMoveString[1] - ONE][currentMoveString[0] - AAA];
239     if(promoted) piece = (ChessSquare) (DEMOTED piece);
240     c = PieceToChar(piece);
241     if(c == '~') c = PieceToChar((ChessSquare) (DEMOTED piece));
242     if (ToLower(yytext[skip3]) != ToLower(c))
243       return (int) IllegalMove;
244
245     result = LegalityTest(boards[yyboardindex],
246                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
247                           currentMoveString[1] - ONE,
248                           currentMoveString[0] - AAA,
249                           currentMoveString[3] - ONE,
250                           currentMoveString[2] - AAA,
251                           currentMoveString[4]);
252
253     if (currentMoveString[4] == NULLCHAR &&
254         (result == WhitePromotion  || result == BlackPromotion)) {
255         if(gameInfo.variant == VariantCourier || gameInfo.variant == VariantShatranj)
256             currentMoveString[4] = PieceToChar(BlackFerz);
257         else if(gameInfo.variant == VariantGreat)
258             currentMoveString[4] = PieceToChar(BlackMan);
259         else if(gameInfo.variant == VariantShogi)
260             currentMoveString[4] = '+';
261         else
262             currentMoveString[4] = PieceToChar(BlackQueen);
263         currentMoveString[5] = NULLCHAR;
264     }
265
266     return (int) result;
267 }
268
269 [a-l][0-9][xX:-]?[a-l][0-9]((=?\(?[A-Za-z]\)?)|=)?      {
270     /*
271      * Simple algebraic move, possibly with promotion
272      * [HGM] Engine moves are received in this format, with lower-case promoChar!
273      */
274     int skip = 0;
275     ChessMove result;
276
277     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
278
279     /* remove the [xX:-] */
280     if ((yytext[2] == 'x') || (yytext[2] == 'X') ||
281         (yytext[2] == '-') || (yytext[2] == ':')) skip = 1;
282
283     currentMoveString[0] = yytext[0];
284     currentMoveString[1] = yytext[1];
285     currentMoveString[2] = yytext[2+skip];
286     currentMoveString[3] = yytext[3+skip];
287     currentMoveString[4] = NULLCHAR;
288
289     if (yyleng-skip > 4) { char c;
290         if (yytext[yyleng-1] == ')') {
291             c = currentMoveString[4] = ToLower(yytext[yyleng-2]);
292         } else {
293             c = currentMoveString[4] = ToLower(yytext[yyleng-1]);
294         }
295         currentMoveString[5] = NULLCHAR;
296         if(c != '=' && c != '+' && CharToPiece(c) == EmptySquare)
297             return IllegalMove;
298     }
299
300     /* [HGM] do not allow values beyond board size */
301     if(currentMoveString[1] - ONE >= BOARD_HEIGHT ||
302        currentMoveString[1] - ONE <  0            ||
303        currentMoveString[0] - AAA >= BOARD_RGHT   ||
304        currentMoveString[3] - ONE >= BOARD_HEIGHT ||
305        currentMoveString[3] - ONE <  0            ||
306        currentMoveString[2] - AAA >= BOARD_RGHT   ||
307        currentMoveString[0] - AAA <  BOARD_LEFT   ||
308        currentMoveString[2] - AAA <  BOARD_LEFT     )
309       return ImpossibleMove;
310
311     result = LegalityTest(boards[yyboardindex],
312                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
313                           currentMoveString[1] - ONE,
314                           currentMoveString[0] - AAA,
315                           currentMoveString[3] - ONE,
316                           currentMoveString[2] - AAA,
317                           currentMoveString[4]);
318
319     if (currentMoveString[4] == NULLCHAR) {
320       if(result == WhitePromotion  || result == BlackPromotion) {
321         if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
322             currentMoveString[4] = PieceToChar(BlackFerz);
323         else if(gameInfo.variant == VariantGreat)
324             currentMoveString[4] = PieceToChar(BlackMan);
325         else if(gameInfo.variant == VariantShogi)
326             currentMoveString[4] = '+'; // Queen might not be defined in mini variants!
327         else
328             currentMoveString[4] = PieceToChar(BlackQueen);
329         currentMoveString[5] = NULLCHAR;
330       }
331     } else if(appData.testLegality && // strip off unnecessary and false promo characters
332        !(result == WhitePromotion  || result == BlackPromotion ||
333          result == WhiteNonPromotion || result == BlackNonPromotion)) currentMoveString[4] = NULLCHAR;
334
335     return (int) result;
336 }
337
338 [A-L][0-9][xX:-]?[A-L][0-9]      {
339     /*
340      * Simple algebraic move, in capitals
341      * [HGM] Engine moves are received in this format, with lower-case promoChar!
342      */
343     int skip = 0;
344     ChessMove result;
345
346     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
347
348     /* remove the [xX:-] */
349     if ((yytext[2] == 'x') || (yytext[2] == 'X') ||
350         (yytext[2] == '-') || (yytext[2] == ':')) skip = 1;
351
352     currentMoveString[0] = yytext[0]+32;
353     currentMoveString[1] = yytext[1];
354     currentMoveString[2] = yytext[2+skip]+32;
355     currentMoveString[3] = yytext[3+skip];
356     currentMoveString[4] = NULLCHAR;
357
358     /* [HGM] do not allow values beyond board size */
359     if(currentMoveString[1] - ONE >= BOARD_HEIGHT ||
360        currentMoveString[1] - ONE <  0            ||
361        currentMoveString[0] - AAA >= BOARD_RGHT   ||
362        currentMoveString[3] - ONE >= BOARD_HEIGHT ||
363        currentMoveString[3] - ONE <  0            ||
364        currentMoveString[2] - AAA >= BOARD_RGHT   ||
365        currentMoveString[0] - AAA <  BOARD_LEFT   ||
366        currentMoveString[2] - AAA <  BOARD_LEFT     )
367       return ImpossibleMove;
368
369     result = LegalityTest(boards[yyboardindex],
370                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
371                           currentMoveString[1] - ONE,
372                           currentMoveString[0] - AAA,
373                           currentMoveString[3] - ONE,
374                           currentMoveString[2] - AAA,
375                           currentMoveString[4]);
376
377     if (currentMoveString[4] == NULLCHAR &&
378         (result == WhitePromotion  || result == BlackPromotion)) {
379         if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
380             currentMoveString[4] = PieceToChar(BlackFerz);
381         else if(gameInfo.variant == VariantGreat)
382             currentMoveString[4] = PieceToChar(BlackMan);
383         else
384             currentMoveString[4] = PieceToChar(BlackQueen);
385         currentMoveString[5] = NULLCHAR;
386     }
387
388     return (int) result;
389 }
390
391 [a-l][0-9]((=?\(?[A-Za-z]\)?)|=)?       {
392     /*
393      * Pawn move, possibly with promotion
394      */
395     DisambiguateClosure cl;
396     int skip = 0; char c;
397
398     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
399
400     /* remove the =() */
401     if (yytext[2] == '=' && yytext[3] != NULLCHAR) skip++;
402     if (yytext[2+skip] == '(') skip++;
403
404     cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn;
405     cl.rfIn = -1;
406     cl.ffIn = yytext[0] - AAA;
407     cl.rtIn = yytext[1] - ONE;
408     cl.ftIn = yytext[0] - AAA;
409     c = cl.promoCharIn = ToLower(yytext[2+skip]);
410
411     /* [HGM] do not allow values beyond board size */
412     if(cl.rtIn >= BOARD_HEIGHT ||
413        cl.rtIn <  0            ||
414        cl.ffIn >= BOARD_RGHT   ||
415        cl.ftIn <  BOARD_LEFT     )
416       return ImpossibleMove;
417
418     if(c != '=' && c != '+' && c != NULLCHAR && CharToPiece(c) == EmptySquare)
419       return IllegalMove;
420
421
422     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
423
424     currentMoveString[0] = cl.ff + AAA;
425     currentMoveString[1] = cl.rf + ONE;
426     currentMoveString[2] = cl.ft + AAA;
427     currentMoveString[3] = cl.rt + ONE;
428     currentMoveString[4] = cl.promoChar;
429     currentMoveString[5] = NULLCHAR;
430
431     return (int) cl.kind;
432 }
433
434
435 (ab|bc|cd|de|ef|fg|gh|hi|ij|jk|kl|lk|kj|ji|ih|hg|gf|fe|ed|dc|cb|ba|aa|bb|cc|dd|ee|ff|gg|hh|ii|jj|kk|ll|([a-l][xX:-][a-l]))((=?\(?[A-Z]\)?)|ep|"e.p."|=)? {
436     /*
437      * Pawn capture, possibly with promotion, possibly ambiguous
438      */
439     DisambiguateClosure cl;
440     int skip1 = 0, skip2 = 0; char c;
441
442     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
443
444     /* remove trailing ep or e.p. (nonstandard PGN) */
445     if (yytext[yyleng-1] == 'p') {
446       yyleng -= 2;
447       yytext[yyleng] = NULLCHAR;
448     } else if (yytext[yyleng-1] == '.') {
449       yyleng -= 4;
450       yytext[yyleng] = NULLCHAR;
451     }
452
453     /* remove the [xX:-] and =() */
454     if ((yytext[1] == 'x') || (yytext[1] == 'X')
455         || (yytext[1] == ':') || (yytext[1] == '-')) skip1 = 1;
456     if (yytext[2+skip1] == '=' && yytext[3+skip1] != NULLCHAR) skip2++;
457     if (yytext[2+skip1+skip2] == '(') skip2++;
458
459     cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn;
460     cl.rfIn = -1;
461     cl.ffIn = yytext[0] - AAA;
462     cl.rtIn = -1;
463     cl.ftIn = yytext[1+skip1] - AAA;
464     c = cl.promoCharIn = yytext[2+skip1+skip2];
465
466     /* [HGM] do not allow values beyond board size */
467     if(cl.ffIn >= BOARD_RGHT  ||
468        cl.ffIn <  BOARD_LEFT  ||
469        cl.ftIn >= BOARD_RGHT  ||
470        cl.ftIn <  BOARD_LEFT     )
471       return ImpossibleMove;
472
473     if(c != '=' && c != '+' && c != NULLCHAR && CharToPiece(c) == EmptySquare)
474       return IllegalMove;
475
476     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
477
478     currentMoveString[0] = cl.ff + AAA;
479     currentMoveString[1] = cl.rf + ONE;
480     currentMoveString[2] = cl.ft + AAA;
481     currentMoveString[3] = cl.rt + ONE;
482     currentMoveString[4] = cl.promoChar;
483     currentMoveString[5] = NULLCHAR;
484
485     return (int) cl.kind;
486 }
487
488 [a-l][xX:]?[a-l][0-9]((=?\(?[A-Z]\)?)|ep|"e.p."|=)? {
489     /*
490      * unambiguously abbreviated Pawn capture, possibly with promotion
491      */
492     int skip = 0;
493     ChessMove result; char c;
494
495     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
496
497     /* remove trailing ep or e.p. (nonstandard PGN) */
498     if (yytext[yyleng-1] == 'p') {
499       yyleng -= 2;
500       yytext[yyleng] = NULLCHAR;
501     } else if (yytext[yyleng-1] == '.') {
502       yyleng -= 4;
503       yytext[yyleng] = NULLCHAR;
504     }
505
506     /* remove the [xX:-] */
507     if ((yytext[1] == 'x') || (yytext[1] == 'X')
508         || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1;
509
510     currentMoveString[0] = yytext[0];
511     currentMoveString[2] = yytext[1+skip];
512     currentMoveString[3] = yytext[2+skip];
513
514     /* [HGM] do not allow values beyond board size */
515     if(currentMoveString[0] - AAA >= BOARD_RGHT   ||
516        currentMoveString[3] - ONE >= BOARD_HEIGHT ||
517        currentMoveString[3] - ONE <  0            ||
518        currentMoveString[2] - AAA >= BOARD_RGHT   ||
519        currentMoveString[0] - AAA <  BOARD_LEFT   ||
520        currentMoveString[2] - AAA <  BOARD_LEFT     )
521       return ImpossibleMove;
522
523     if (gameInfo.variant == VariantXiangqi && /* [HGM] In Xiangqi rank stays same */
524          currentMoveString[0] != currentMoveString[2] ) {
525         currentMoveString[1] = yytext[2+skip];
526     } else 
527     if (WhiteOnMove(yyboardindex)) {
528         if (yytext[2+skip] == ONE) return (int) ImpossibleMove;
529         currentMoveString[1] = yytext[2+skip] - 1;
530         if(boards[yyboardindex][currentMoveString[1]-ONE][currentMoveString[0]-AAA] != WhitePawn) 
531                 return ImpossibleMove;
532     } else {
533         currentMoveString[1] = currentMoveString[3] + 1;
534         if (currentMoveString[3] == ONE+BOARD_HEIGHT-1) return (int) ImpossibleMove;
535         if(boards[yyboardindex][currentMoveString[1]-ONE][currentMoveString[0]-AAA] != BlackPawn) 
536                 return ImpossibleMove;
537     }
538     if (yyleng-skip > 3) {
539         if (yytext[yyleng-1] == ')')
540           c = currentMoveString[4] = ToLower(yytext[yyleng-2]);
541         else
542           c = currentMoveString[4] = ToLower(yytext[yyleng-1]);
543         currentMoveString[5] = NULLCHAR;
544         if(c != '=' && c != '+' && CharToPiece(c) == EmptySquare)
545             return IllegalMove;
546     } else {
547         currentMoveString[4] = NULLCHAR;
548     }
549
550     result = LegalityTest(boards[yyboardindex],
551                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
552                           currentMoveString[1] - ONE,
553                           currentMoveString[0] - AAA,
554                           currentMoveString[3] - ONE,
555                           currentMoveString[2] - AAA,
556                           currentMoveString[4]);
557
558     if (currentMoveString[4] == NULLCHAR &&
559         (result == WhitePromotion  || result == BlackPromotion)) {
560         currentMoveString[4] = PieceToChar(BlackQueen);
561         // [HGM] shatranj: take care of variants without Queen
562         if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
563             currentMoveString[4] = PieceToChar(BlackFerz);
564         if(gameInfo.variant == VariantGreat)
565             currentMoveString[4] = PieceToChar(BlackMan);
566         currentMoveString[5] = NULLCHAR;
567     }
568
569     if (result != IllegalMove) return (int) result;
570
571     /* Special case: improperly written en passant capture */
572     if (WhiteOnMove(yyboardindex)) {
573         if (currentMoveString[3] == '5') {
574             currentMoveString[1] = '5';
575             currentMoveString[3] = '6';
576         } else {
577             return (int) IllegalMove;
578         }
579     } else {
580         if (currentMoveString[3] == '4') {
581             currentMoveString[1] = '4';
582             currentMoveString[3] = '3';
583         } else {
584             return (int) IllegalMove;
585         }
586     }
587
588     result = LegalityTest(boards[yyboardindex],
589                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
590                           currentMoveString[1] - ONE,
591                           currentMoveString[0] - AAA,
592                           currentMoveString[3] - ONE,
593                           currentMoveString[2] - AAA,
594                           currentMoveString[4]);
595
596     if (result == WhiteCapturesEnPassant || result == BlackCapturesEnPassant)
597       return (int) result;
598     else
599       return (int) IllegalMove;
600 }
601
602 "+"?[A-Z][xX:-]?[a-l][0-9]=?  {
603     /*
604      * piece move, possibly ambiguous
605      */
606     DisambiguateClosure cl;
607     int skip = 0, skip2 = 0, promoted = 0;
608
609     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
610
611     if(yytext[0] == '+') promoted = skip = skip2 = 1;
612
613     /* remove the [xX:-] */
614     if ((yytext[1+skip] == 'x') || (yytext[1+skip] == 'X')
615         || (yytext[1+skip] == ':') || (yytext[1+skip] == '-')) skip++;
616
617     if (WhiteOnMove(yyboardindex)) {
618         cl.pieceIn = CharToPiece(ToUpper(yytext[skip2]));
619     } else {
620         cl.pieceIn = CharToPiece(ToLower(yytext[skip2]));
621     }
622     if(promoted) cl.pieceIn = (ChessSquare) (PROMOTED cl.pieceIn);
623
624     cl.rfIn = -1;
625     cl.ffIn = -1;
626     cl.rtIn = yytext[2+skip] - ONE;
627     cl.ftIn = yytext[1+skip] - AAA;
628     cl.promoCharIn = NULLCHAR;
629
630     if(yyleng-skip > 3) /* [HGM] can have Shogi-style promotion */
631         cl.promoCharIn = yytext[yyleng-1];
632
633     if (appData.debugMode) {
634         fprintf(debugFP, "Parser Qa1: yyleng=%d,  %d(%d,%d)-(%d,%d) = %d (%c)\n",
635         yyleng,
636         cl.pieceIn,cl.ffIn,cl.rfIn,cl.ftIn,cl.rtIn,cl.promoCharIn,cl.promoCharIn?cl.promoCharIn:' ');
637     }
638
639     /* [HGM] but do not allow values beyond board size */
640     if(cl.rtIn >= BOARD_HEIGHT ||
641        cl.rtIn <  0            ||
642        cl.ftIn >= BOARD_RGHT   ||
643        cl.ftIn <  BOARD_LEFT     )
644       return ImpossibleMove;
645
646     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
647
648     currentMoveString[0] = cl.ff + AAA;
649     currentMoveString[1] = cl.rf + ONE;
650     currentMoveString[2] = cl.ft + AAA;
651     currentMoveString[3] = cl.rt + ONE;
652     currentMoveString[4] = cl.promoChar;
653     currentMoveString[5] = NULLCHAR;
654
655     return (int) cl.kind;
656 }
657
658 "+"?[A-Z][a-l0-9][xX:-]?[a-l][0-9]=?   {
659     /*
660      * piece move with rank or file disambiguator
661      */
662     DisambiguateClosure cl;
663     int skip = 0, skip2 = 0; int promoted=0;
664
665     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
666
667     if(yytext[0]=='+') promoted = skip = skip2 = 1;
668
669     /* remove the [xX:-] */
670     if ((yytext[2+skip] == 'x') || (yytext[2+skip] == 'X')
671         || (yytext[2+skip] == ':') || (yytext[2+skip] == '-')) skip++;
672
673     if (WhiteOnMove(yyboardindex)) {
674         cl.pieceIn = CharToPiece(ToUpper(yytext[skip2]));
675     } else {
676         cl.pieceIn = CharToPiece(ToLower(yytext[skip2]));
677     }
678     if(promoted) cl.pieceIn = (ChessSquare) (PROMOTED cl.pieceIn);
679
680     if (isalpha(yytext[1+skip2])) {
681         cl.rfIn = -1;
682         cl.ffIn = yytext[1+skip2] - AAA;
683        
684         if(cl.ffIn >= BOARD_RGHT ||
685            cl.ffIn <  BOARD_LEFT   ) return 0;
686     } else {
687         cl.rfIn = yytext[1+skip2] - ONE;
688         cl.ffIn = -1;
689         if(cl.rfIn >= BOARD_HEIGHT ||
690            cl.rfIn <  0) return 0;
691     }
692     cl.rtIn = yytext[3+skip] - ONE;
693     cl.ftIn = yytext[2+skip] - AAA;
694     cl.promoCharIn = NULLCHAR;
695
696     if(yyleng-skip > 4) /* [HGM] can have Shogi-style promotion */
697         cl.promoCharIn = yytext[yyleng-1];
698
699     /* [HGM] do not allow values beyond board size */
700     if(cl.rtIn >= BOARD_HEIGHT ||
701        cl.rtIn <  0            ||
702        cl.ftIn >= BOARD_RGHT   ||
703        cl.ftIn <  BOARD_LEFT     )
704       return ImpossibleMove;
705
706     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
707
708     currentMoveString[0] = cl.ff + AAA;
709     currentMoveString[1] = cl.rf + ONE;
710     currentMoveString[2] = cl.ft + AAA;
711     currentMoveString[3] = cl.rt + ONE;
712     currentMoveString[4] = cl.promoChar;
713     currentMoveString[5] = NULLCHAR;
714
715     return (int) cl.kind;
716 }
717
718 000|0-0-0|ooo|OOO|o-o-o|O-O-O   {
719     int rf, ff, rt, ft;
720
721     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
722
723     /* [HGM] all squares referenced to board edges in stead of absolute */
724     if (WhiteOnMove(yyboardindex)) {
725         if (boards[yyboardindex][0][(BOARD_WIDTH-1)>>1] == WhiteKing) {
726             /* ICS wild castling */
727             rf = 0;
728             ff = (BOARD_WIDTH-1)>>1;
729             rt = 0;
730             ft = BOARD_RGHT-3;
731         } else {
732             rf = 0;
733             ff = BOARD_WIDTH>>1;
734             rt = 0;
735             ft = BOARD_LEFT+2;
736         }
737     } else{ 
738         if (boards[yyboardindex][BOARD_HEIGHT-1][(BOARD_WIDTH-1)>>1] == BlackKing) {
739             /* ICS wild castling */
740             rf = BOARD_HEIGHT-1;
741             ff = (BOARD_WIDTH-1)>>1;
742             rt = BOARD_HEIGHT-1;
743             ft = BOARD_RGHT-3;
744         } else {
745             rf = BOARD_HEIGHT-1;
746             ff = BOARD_WIDTH>>1;
747             rt = BOARD_HEIGHT-1;
748             ft = BOARD_LEFT+2;
749         }
750     }
751     if(PosFlags(0) & F_FRC_TYPE_CASTLING) {
752
753         if (WhiteOnMove(yyboardindex)) {
754             ff = initialRights[2];
755             ft = initialRights[1];
756         } else {
757             ff = initialRights[5];
758             ft = initialRights[4];
759         }
760         if (appData.debugMode) 
761         {
762           fprintf(debugFP, "Parser FRC long %d %d\n", ff, ft);
763         };
764         if(ff < 0 || ft < 0) return 0;
765     }
766     sprintf(currentMoveString, "%c%c%c%c",ff+AAA,rf+ONE,ft+AAA,rt+ONE);
767     if (appData.debugMode) {
768         fprintf(debugFP, "long castling %d %d\n", ff, ft);
769     }
770     return (int) LegalityTest(boards[yyboardindex],
771                               PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: e.p.!
772                               rf, ff, rt, ft, NULLCHAR);
773 }
774
775 00|0-0|oo|OO|o-o|O-O    {
776     int rf, ff, rt, ft;
777
778     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
779
780     if (WhiteOnMove(yyboardindex)) {
781         if (boards[yyboardindex][0][(BOARD_WIDTH-1)>>1] == WhiteKing) {
782             /* ICS wild castling */
783             rf = 0;
784             ff = (BOARD_WIDTH-1)>>1;
785             rt = 0;
786             ft = BOARD_LEFT+1;
787         } else {
788             rf = 0;
789             ff = BOARD_WIDTH>>1;
790             rt = 0;
791             ft = BOARD_RGHT-2;
792         }
793     } else {
794         if (boards[yyboardindex][BOARD_HEIGHT-1][(BOARD_WIDTH-1)>>1] == BlackKing) {
795             /* ICS wild castling */
796             rf = BOARD_HEIGHT-1;
797             ff = (BOARD_WIDTH-1)>>1;
798             rt = BOARD_HEIGHT-1;
799             ft = BOARD_LEFT+1;
800         } else {
801             rf = BOARD_HEIGHT-1;
802             ff = BOARD_WIDTH>>1;
803             rt = BOARD_HEIGHT-1;
804             ft = BOARD_RGHT-2;
805         }
806     }
807     if(PosFlags(0) & F_FRC_TYPE_CASTLING) {
808         if (WhiteOnMove(yyboardindex)) {
809             ff = initialRights[2];
810             ft = initialRights[0];
811         } else {
812             ff = initialRights[5];
813             ft = initialRights[3];
814         }
815     if (appData.debugMode) {
816         fprintf(debugFP, "Parser FRC short %d %d\n", ff, ft);
817     }
818         if(ff < 0 || ft < 0) return 0;
819     }
820     sprintf(currentMoveString, "%c%c%c%c",ff+AAA,rf+ONE,ft+AAA,rt+ONE);
821     if (appData.debugMode) {
822         fprintf(debugFP, "short castling %d %d\n", ff, ft);
823     }
824
825     return (int) LegalityTest(boards[yyboardindex],
826                               PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: e.p.!
827                               rf, ff, rt, ft, NULLCHAR);
828 }
829
830 [A-Z][@*][a-l][0-9] {
831     /* Bughouse piece drop. */
832     currentMoveString[1] = '@';
833     currentMoveString[2] = yytext[2];
834     currentMoveString[3] = yytext[3];
835     currentMoveString[4] = NULLCHAR;
836
837     if (appData.debugMode) {
838         fprintf(debugFP, "Drop: %s\n", currentMoveString);
839     }
840     /* [HGM] do not allow values beyond board size */
841     if(currentMoveString[3] - ONE >= BOARD_HEIGHT ||
842        currentMoveString[2] - AAA >= BOARD_WIDTH     )
843       return ImpossibleMove;
844
845     if (WhiteOnMove(yyboardindex)) {
846         currentMoveString[0] = ToUpper(yytext[0]);
847     } else {
848         currentMoveString[0] = ToLower(yytext[0]);
849     }
850     return LegalityTest(boards[yyboardindex], PosFlags(yyboardindex), DROP_RANK, // [HGM] does drops now too
851                         CharToPiece(currentMoveString[0]), currentMoveString[3] - ONE, currentMoveString[2] - AAA, NULLCHAR);
852 }
853
854 [Rr]esign(s|ed)?  {
855     if (WhiteOnMove(yyboardindex))
856       return (int) BlackWins;
857     else
858       return (int) WhiteWins;
859 }
860
861 (([Ww](hite)?)|([Bb](lack)?))" "(([Rr]esign)|([Ff]orfeit))(s|ed)?  {
862     return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins);
863 }
864
865 (([Ww](hite)?)|([Bb](lack)?))" "[Dd]isconnect(s|ed)  {
866     return (int) GameUnfinished;
867 }
868
869 [Ss]talemate  {
870     return (int) GameIsDrawn;
871 }
872
873 "+-+"  {
874     return (int) GameIsDrawn;
875 }
876
877 ([Cc]heck)?[Mm]ate {
878     if (WhiteOnMove(yyboardindex))
879       return (int) BlackWins;
880     else
881       return (int) WhiteWins;
882 }
883
884 "++"  {
885     if (WhiteOnMove(yyboardindex))
886       return (int) BlackWins;
887     else
888       return (int) WhiteWins;
889 }
890
891 [Dd]raw(n)?(" "by)?(" "[Rr]epetition)|(" "[Aa]gree(d|ment))  {
892     return (int) GameIsDrawn;
893 }
894
895 [Dd]raw(n)?(" (".*")")?  {
896     return (int) GameIsDrawn;
897 }
898
899 (([Ww](hite)?)|([Bb](lack)?))" "(([Mm]ates)|([Ww][io]n(s)?)) { 
900     return (int) (ToUpper(yytext[0]) == 'W' ? WhiteWins : BlackWins);
901 }
902
903 (([Ww](hite)?)|([Bb](lack)?))" "(([Mm]ated)|([Ll]os[tes]+)) { 
904     return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins);
905 }
906
907 ("{"[^\}\n]*"} ")?(1-0|"1 - 0"|"1/0"|"1 / 0"|"1:0"|"1 : 0")(" (".*")"|" {".*"}")? { 
908     return (int) WhiteWins;
909 }
910
911 ("{"[^\}\n]*"} ")?(0-1|"0 - 1"|"0/1"|"0 / 1"|"0:1"|"0 : 1")(" (".*")"|" {".*"}")? { 
912     return (int) BlackWins;
913 }
914
915 ("{"[^\}\n]*"} ")?("1/2"|"1 / 2")(" "?[-:]" "?("1/2"|"1 / 2"))?(" (".*")"|" {".*"}")? {
916     return (int) GameIsDrawn;
917 }
918
919 ("{"[^\}\n]*"} ")?"*"(" (".*")"|" {".*"}")? {
920     return (int) GameUnfinished;
921 }
922
923 [1-9][0-9]*/"."?[ \t\n]*[a-lNnPpRrBQqKACFEWDGHOo]    {
924     /* move numbers */
925     if ((yyleng == 1) && (yytext[0] == '1'))
926       return (int) MoveNumberOne;
927 }
928
929 \([0-9]+:[0-9][0-9](\.[0-9]+)?\)|\{[0-9]+:[0-9][0-9](\.[0-9]+)?\} {
930     /* elapsed time indication, e.g. (0:12) or {10:21.071} */ 
931     return (int) ElapsedTime;
932 }
933
934 "[--"[^\]]*"--]" {
935     /* position diagram enclosed in [-- --] */
936     return (int) PositionDiagram;
937 }
938
939 ^"{--------------"\n[^\}]*\n"--------------}"$ {
940     /* position diagram enclosed in {-- --} */
941     return (int) PositionDiagram;
942 }
943
944 \[[ \t\n]*[A-Za-z0-9][A-Za-z0-9_+#=-]*[ \t\n]*\"[^"]*\"[ \t\n]*\] {
945     return (int) PGNTag;
946 }    
947
948 [Gg](nu|NU)" "?[Cc](hess|HESS).*[Gg](ame|AME) {
949     return (int) GNUChessGame;
950 }
951
952 ^[#;%]" "[^ ]*(" game file"|" position file").*$ {
953     return (int) XBoardGame;
954 }
955
956 \$[0-9]+        {                               /* numeric annotation glyph */
957     return (int) NAG;
958 }
959
960 \{[^\}]*\}      {                               /* anything in {} */
961     return (int) Comment; 
962 }
963
964 ;.*$ {                                          /* ; to end of line */
965     return (int) Comment;
966 }
967
968 \[[^\]]*\]      {                               /* anything in [] */
969     return (int) Comment; 
970 }
971
972 \([^()]*(\([^()]*(\([^()]*(\([^()]*\)[^()]*)*\)[^()]*)*\)[^()]*)+[^()]*\)  { /* very nested () */
973     return (int) Comment; 
974 }
975
976 \([^)][^)]+\)   {                               /* >=2 chars in () */
977     return (int) Comment; 
978 }       
979
980 ^[-a-zA-Z0-9]+:" ".*(\n[ \t]+.*)*  {
981         /* Skip mail headers */
982 }
983
984 [a-zA-Z0-9'-]+                  {
985         /* Skip random words */
986 }
987
988 .|\n                            {
989         /* Skip everything else */
990 }
991
992 %%
993
994
995 static char *StringToLex;
996
997 #ifndef FLEX_SCANNER
998 static FILE *lexFP;
999
1000 static int input()
1001 {
1002     int ret;
1003     
1004     if (StringToLex != NULL) {
1005         ret = *StringToLex;
1006         if (ret == NULLCHAR)
1007           ret = EOF;
1008         else
1009           StringToLex++;
1010     } else if (unputCount > 0) {
1011         ret = unputBuffer[--unputCount];
1012     } else {
1013         ret = fgetc(lexFP);
1014     }    
1015
1016     if (ret == EOF) 
1017       return 0;
1018     else
1019       return ret;
1020 }
1021
1022 /*
1023  * Return offset of next pattern within current file
1024  */
1025 int yyoffset()
1026 {
1027     int offset = ftell(lexFP) - unputCount;
1028
1029     if (offset < 0) {
1030         offset = 0;
1031     }
1032     return(offset);
1033 }
1034  
1035 static void output(ch)
1036      int ch;
1037 {
1038     if(appData.debugMode) fprintf(debugFP, "PARSER BUG: unmatched character '%c' (0%o)\n",
1039             ch, ch);
1040 }
1041
1042 static void unput(ch)
1043      int ch;
1044 {
1045     if (ch == 0) return;
1046     if (StringToLex != NULL) {
1047         StringToLex--;
1048     } else {
1049         if (unputCount >= UNPUT_BUF_SIZE)
1050           if(appData.debugMode) fprintf(debugFP, "PARSER BUG: unput buffer overflow '%c' (0%o)\n",
1051                   ch, ch);
1052         unputBuffer[unputCount++] = ch;
1053     }
1054 }
1055
1056 /* Get ready to lex from a new file.  Kludge below sticks
1057    an artificial newline at the front of the file, which the
1058    above grammar ignores, but which makes ^ at start of pattern
1059    match at the real start of the file.
1060 */
1061 void yynewfile(f)
1062      FILE *f;
1063 {
1064     lexFP = f;
1065     StringToLex = NULL;
1066     unputCount = 0;
1067     unput('\n'); /* kludge */
1068 }
1069
1070 /* Get ready to lex from a string.  ^ at start of pattern WON'T
1071    match at the start of the string!
1072 */
1073 void yynewstr(s)
1074      char *s;
1075 {
1076     lexFP = NULL;
1077     StringToLex = s;
1078     unputCount = 0;
1079 }
1080 #endif /*!FLEX_SCANNER*/
1081
1082 #ifdef FLEX_SCANNER
1083 void my_yy_input(buf, result, max_size)
1084      char *buf;
1085      int *result;
1086      int max_size;
1087 {
1088     int count;
1089
1090     if (StringToLex != NULL) {
1091         count = 0;
1092         while (*StringToLex != NULLCHAR) {
1093             *buf++ = *StringToLex++;
1094             count++;
1095         }
1096         *result = count;
1097         return;
1098     } else {
1099         count = fread(buf, 1, max_size, yyin);
1100         if (count == 0) {
1101             *result = YY_NULL;
1102         } else {
1103             *result = count;
1104         }
1105         return;
1106     }    
1107 }
1108
1109 static YY_BUFFER_STATE my_file_buffer = NULL;
1110
1111 /*
1112     Return offset of next pattern in the current file.
1113 */
1114 int yyoffset()
1115 {
1116     int pos = yy_c_buf_p - YY_CURRENT_BUFFER->yy_ch_buf;
1117
1118     return(ftell(YY_CURRENT_BUFFER->yy_input_file) -
1119          yy_n_chars + pos);
1120 }
1121
1122
1123 void yynewstr(s)
1124      char *s;
1125 {
1126     if (my_file_buffer != NULL)
1127       yy_delete_buffer(my_file_buffer);
1128     StringToLex = s;
1129     my_file_buffer = yy_create_buffer(stdin, YY_BUF_SIZE);
1130     yy_switch_to_buffer(my_file_buffer);
1131 }
1132
1133 void yynewfile(f)
1134      FILE *f;
1135 {
1136     if (my_file_buffer != NULL)
1137       yy_delete_buffer(my_file_buffer);
1138     StringToLex = NULL;
1139     my_file_buffer = yy_create_buffer(f, YY_BUF_SIZE);
1140     yy_switch_to_buffer(my_file_buffer);
1141 }
1142 #endif /*FLEX_SCANNER*/
1143
1144 int yywrap()
1145 {
1146     return TRUE;
1147 }
1148
1149 /* Parse a move from the given string s */
1150 /* ^ at start of pattern WON'T work here unless using flex */
1151 ChessMove yylexstr(boardIndex, s, text, len)
1152      int boardIndex, len;
1153      char *s, *text;
1154 {
1155     ChessMove ret;
1156     char *oldStringToLex;
1157 #ifdef FLEX_SCANNER
1158     YY_BUFFER_STATE buffer, oldBuffer;
1159 #endif
1160     
1161     yyboardindex = boardIndex;
1162     oldStringToLex = StringToLex;
1163     StringToLex = s;
1164 #ifdef FLEX_SCANNER
1165     buffer = yy_create_buffer(stdin, YY_BUF_SIZE);
1166     oldBuffer = YY_CURRENT_BUFFER;
1167     yy_switch_to_buffer(buffer);
1168 #endif /*FLEX_SCANNER*/
1169
1170     ret = (ChessMove) yylex();
1171      strncpy(text, yy_text, len-1); // [HGM] vari: yy_text is not available to caller after buffer switch ?!?
1172      text[len-1] = NULLCHAR;
1173
1174 #ifdef FLEX_SCANNER
1175     if (oldBuffer != NULL) 
1176       yy_switch_to_buffer(oldBuffer);
1177     yy_delete_buffer(buffer);
1178 #endif /*FLEX_SCANNER*/
1179     StringToLex = oldStringToLex;
1180
1181     return ret;
1182 }