accd5700bb5a9d2cc40d16b853b6a60ed8c40e74
[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 == WhitePromotionKnight || result == BlackPromotionKnight ||
255          result == WhitePromotionQueen  || result == BlackPromotionQueen)) {
256         currentMoveString[4] = PieceToChar(BlackQueen);
257         currentMoveString[5] = NULLCHAR;
258     }
259
260     return (int) result;
261 }
262
263 [a-l][0-9][xX:-]?[a-l][0-9]((=?\(?[A-Za-z]\)?)|=)?      {
264     /*
265      * Simple algebraic move, possibly with promotion
266      * [HGM] Engine moves are received in this format, with lower-case promoChar!
267      */
268     int skip = 0;
269     ChessMove result;
270
271     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
272
273     /* remove the [xX:-] */
274     if ((yytext[2] == 'x') || (yytext[2] == 'X') ||
275         (yytext[2] == '-') || (yytext[2] == ':')) skip = 1;
276
277     currentMoveString[0] = yytext[0];
278     currentMoveString[1] = yytext[1];
279     currentMoveString[2] = yytext[2+skip];
280     currentMoveString[3] = yytext[3+skip];
281     currentMoveString[4] = NULLCHAR;
282
283     if (yyleng-skip > 4) { char c;
284         if (yytext[yyleng-1] == ')') {
285             c = currentMoveString[4] = ToLower(yytext[yyleng-2]);
286         } else {
287             c = currentMoveString[4] = ToLower(yytext[yyleng-1]);
288         }
289         currentMoveString[5] = NULLCHAR;
290         if(c != '=' && c != '+' && CharToPiece(c) == EmptySquare)
291             return IllegalMove;
292     }
293
294     /* [HGM] do not allow values beyond board size */
295     if(currentMoveString[1] - ONE >= BOARD_HEIGHT ||
296        currentMoveString[1] - ONE <  0            ||
297        currentMoveString[0] - AAA >= BOARD_RGHT   ||
298        currentMoveString[3] - ONE >= BOARD_HEIGHT ||
299        currentMoveString[3] - ONE <  0            ||
300        currentMoveString[2] - AAA >= BOARD_RGHT   ||
301        currentMoveString[0] - AAA <  BOARD_LEFT   ||
302        currentMoveString[2] - AAA <  BOARD_LEFT     )
303       return ImpossibleMove;
304
305     result = LegalityTest(boards[yyboardindex],
306                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
307                           currentMoveString[1] - ONE,
308                           currentMoveString[0] - AAA,
309                           currentMoveString[3] - ONE,
310                           currentMoveString[2] - AAA,
311                           currentMoveString[4]);
312
313     if (currentMoveString[4] == NULLCHAR) {
314       if(result == WhitePromotionKnight || result == BlackPromotionKnight ||
315          result == WhitePromotionQueen  || result == BlackPromotionQueen) {
316         if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
317             currentMoveString[4] = PieceToChar(BlackFerz);
318         else if(gameInfo.variant == VariantGreat)
319             currentMoveString[4] = PieceToChar(BlackMan);
320         else
321             currentMoveString[4] = PieceToChar(BlackQueen);
322         currentMoveString[5] = NULLCHAR;
323       }
324     } else if(appData.testLegality && // strip off unnecessary and false promo characters
325        !(result == WhitePromotionQueen  || result == BlackPromotionQueen ||
326          result == WhiteNonPromotion    || result == BlackNonPromotion)) currentMoveString[4] = NULLCHAR;
327
328     return (int) result;
329 }
330
331 [A-L][0-9][xX:-]?[A-L][0-9]      {
332     /*
333      * Simple algebraic move, in capitals
334      * [HGM] Engine moves are received in this format, with lower-case promoChar!
335      */
336     int skip = 0;
337     ChessMove result;
338
339     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
340
341     /* remove the [xX:-] */
342     if ((yytext[2] == 'x') || (yytext[2] == 'X') ||
343         (yytext[2] == '-') || (yytext[2] == ':')) skip = 1;
344
345     currentMoveString[0] = yytext[0]+32;
346     currentMoveString[1] = yytext[1];
347     currentMoveString[2] = yytext[2+skip]+32;
348     currentMoveString[3] = yytext[3+skip];
349     currentMoveString[4] = NULLCHAR;
350
351     /* [HGM] do not allow values beyond board size */
352     if(currentMoveString[1] - ONE >= BOARD_HEIGHT ||
353        currentMoveString[1] - ONE <  0            ||
354        currentMoveString[0] - AAA >= BOARD_RGHT   ||
355        currentMoveString[3] - ONE >= BOARD_HEIGHT ||
356        currentMoveString[3] - ONE <  0            ||
357        currentMoveString[2] - AAA >= BOARD_RGHT   ||
358        currentMoveString[0] - AAA <  BOARD_LEFT   ||
359        currentMoveString[2] - AAA <  BOARD_LEFT     )
360       return ImpossibleMove;
361
362     result = LegalityTest(boards[yyboardindex],
363                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
364                           currentMoveString[1] - ONE,
365                           currentMoveString[0] - AAA,
366                           currentMoveString[3] - ONE,
367                           currentMoveString[2] - AAA,
368                           currentMoveString[4]);
369
370     if (currentMoveString[4] == NULLCHAR &&
371         (result == WhitePromotionKnight || result == BlackPromotionKnight ||
372          result == WhitePromotionQueen  || result == BlackPromotionQueen)) {
373         if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
374             currentMoveString[4] = PieceToChar(BlackFerz);
375         else if(gameInfo.variant == VariantGreat)
376             currentMoveString[4] = PieceToChar(BlackMan);
377         else
378             currentMoveString[4] = PieceToChar(BlackQueen);
379         currentMoveString[5] = NULLCHAR;
380     } else if(appData.testLegality && // strip off unnecessary and false promo characters
381        !(result == WhitePromotionQueen  || result == BlackPromotionQueen ||
382          result == WhiteNonPromotion    || result == BlackNonPromotion)) currentMoveString[4] = NULLCHAR;
383
384     return (int) result;
385 }
386
387 [a-l][0-9]((=?\(?[A-Za-z]\)?)|=)?       {
388     /*
389      * Pawn move, possibly with promotion
390      */
391     DisambiguateClosure cl;
392     int skip = 0; char c;
393
394     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
395
396     /* remove the =() */
397     if (yytext[2] == '=' && yytext[3] != NULLCHAR) skip++;
398     if (yytext[2+skip] == '(') skip++;
399
400     cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn;
401     cl.rfIn = -1;
402     cl.ffIn = yytext[0] - AAA;
403     cl.rtIn = yytext[1] - ONE;
404     cl.ftIn = yytext[0] - AAA;
405     c = cl.promoCharIn = ToLower(yytext[2+skip]);
406
407     /* [HGM] do not allow values beyond board size */
408     if(cl.rtIn >= BOARD_HEIGHT ||
409        cl.rtIn <  0            ||
410        cl.ffIn >= BOARD_RGHT   ||
411        cl.ftIn <  BOARD_LEFT     )
412       return ImpossibleMove;
413
414     if(c != '=' && c != '+' && c != NULLCHAR && CharToPiece(c) == EmptySquare)
415       return IllegalMove;
416
417
418     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
419
420     currentMoveString[0] = cl.ff + AAA;
421     currentMoveString[1] = cl.rf + ONE;
422     currentMoveString[2] = cl.ft + AAA;
423     currentMoveString[3] = cl.rt + ONE;
424     currentMoveString[4] = cl.promoChar;
425     currentMoveString[5] = NULLCHAR;
426
427     return (int) cl.kind;
428 }
429
430
431 (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."|=)? {
432     /*
433      * Pawn capture, possibly with promotion, possibly ambiguous
434      */
435     DisambiguateClosure cl;
436     int skip1 = 0, skip2 = 0; char c;
437
438     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
439
440     /* remove trailing ep or e.p. (nonstandard PGN) */
441     if (yytext[yyleng-1] == 'p') {
442       yyleng -= 2;
443       yytext[yyleng] = NULLCHAR;
444     } else if (yytext[yyleng-1] == '.') {
445       yyleng -= 4;
446       yytext[yyleng] = NULLCHAR;
447     }
448
449     /* remove the [xX:-] and =() */
450     if ((yytext[1] == 'x') || (yytext[1] == 'X')
451         || (yytext[1] == ':') || (yytext[1] == '-')) skip1 = 1;
452     if (yytext[2+skip1] == '=' && yytext[3+skip1] != NULLCHAR) skip2++;
453     if (yytext[2+skip1+skip2] == '(') skip2++;
454
455     cl.pieceIn = WhiteOnMove(yyboardindex) ? WhitePawn : BlackPawn;
456     cl.rfIn = -1;
457     cl.ffIn = yytext[0] - AAA;
458     cl.rtIn = -1;
459     cl.ftIn = yytext[1+skip1] - AAA;
460     c = cl.promoCharIn = yytext[2+skip1+skip2];
461
462     /* [HGM] do not allow values beyond board size */
463     if(cl.ffIn >= BOARD_RGHT  ||
464        cl.ffIn <  BOARD_LEFT  ||
465        cl.ftIn >= BOARD_RGHT  ||
466        cl.ftIn <  BOARD_LEFT     )
467       return ImpossibleMove;
468
469     if(c != '=' && c != '+' && c != NULLCHAR && CharToPiece(c) == EmptySquare)
470       return IllegalMove;
471
472     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
473
474     currentMoveString[0] = cl.ff + AAA;
475     currentMoveString[1] = cl.rf + ONE;
476     currentMoveString[2] = cl.ft + AAA;
477     currentMoveString[3] = cl.rt + ONE;
478     currentMoveString[4] = cl.promoChar;
479     currentMoveString[5] = NULLCHAR;
480
481     return (int) cl.kind;
482 }
483
484 [a-l][xX:]?[a-l][0-9]((=?\(?[A-Z]\)?)|ep|"e.p."|=)? {
485     /*
486      * unambiguously abbreviated Pawn capture, possibly with promotion
487      */
488     int skip = 0;
489     ChessMove result; char c;
490
491     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
492
493     /* remove trailing ep or e.p. (nonstandard PGN) */
494     if (yytext[yyleng-1] == 'p') {
495       yyleng -= 2;
496       yytext[yyleng] = NULLCHAR;
497     } else if (yytext[yyleng-1] == '.') {
498       yyleng -= 4;
499       yytext[yyleng] = NULLCHAR;
500     }
501
502     /* remove the [xX:-] */
503     if ((yytext[1] == 'x') || (yytext[1] == 'X')
504         || (yytext[1] == ':') || (yytext[1] == '-')) skip = 1;
505
506     currentMoveString[0] = yytext[0];
507     currentMoveString[2] = yytext[1+skip];
508     currentMoveString[3] = yytext[2+skip];
509
510     /* [HGM] do not allow values beyond board size */
511     if(currentMoveString[0] - AAA >= BOARD_RGHT   ||
512        currentMoveString[3] - ONE >= BOARD_HEIGHT ||
513        currentMoveString[3] - ONE <  0            ||
514        currentMoveString[2] - AAA >= BOARD_RGHT   ||
515        currentMoveString[0] - AAA <  BOARD_LEFT   ||
516        currentMoveString[2] - AAA <  BOARD_LEFT     )
517       return ImpossibleMove;
518
519     if (gameInfo.variant == VariantXiangqi && /* [HGM] In Xiangqi rank stays same */
520          currentMoveString[0] != currentMoveString[2] ) {
521         currentMoveString[1] = yytext[2+skip];
522     } else 
523     if (WhiteOnMove(yyboardindex)) {
524         if (yytext[2+skip] == ONE) return (int) ImpossibleMove;
525         currentMoveString[1] = yytext[2+skip] - 1;
526         if(boards[yyboardindex][currentMoveString[1]-ONE][currentMoveString[0]-AAA] != WhitePawn) 
527                 return ImpossibleMove;
528     } else {
529         currentMoveString[1] = currentMoveString[3] + 1;
530         if (currentMoveString[3] == ONE+BOARD_HEIGHT-1) return (int) ImpossibleMove;
531         if(boards[yyboardindex][currentMoveString[1]-ONE][currentMoveString[0]-AAA] != BlackPawn) 
532                 return ImpossibleMove;
533     }
534     if (yyleng-skip > 3) {
535         if (yytext[yyleng-1] == ')')
536           c = currentMoveString[4] = ToLower(yytext[yyleng-2]);
537         else
538           c = currentMoveString[4] = ToLower(yytext[yyleng-1]);
539         currentMoveString[5] = NULLCHAR;
540         if(c != '=' && c != '+' && CharToPiece(c) == EmptySquare)
541             return IllegalMove;
542     } else {
543         currentMoveString[4] = NULLCHAR;
544     }
545
546     result = LegalityTest(boards[yyboardindex],
547                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
548                           currentMoveString[1] - ONE,
549                           currentMoveString[0] - AAA,
550                           currentMoveString[3] - ONE,
551                           currentMoveString[2] - AAA,
552                           currentMoveString[4]);
553
554     if (currentMoveString[4] == NULLCHAR &&
555         (result == WhitePromotionQueen  || result == BlackPromotionQueen ||
556          result == WhitePromotionKnight || result == BlackPromotionKnight)) {
557         currentMoveString[4] = PieceToChar(BlackQueen);
558         // [HGM] shatranj: take care of variants without Queen
559         if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
560             currentMoveString[4] = PieceToChar(BlackFerz);
561         if(gameInfo.variant == VariantGreat)
562             currentMoveString[4] = PieceToChar(BlackMan);
563         currentMoveString[5] = NULLCHAR;
564     }
565
566     if (result != IllegalMove) return (int) result;
567
568     /* Special case: improperly written en passant capture */
569     if (WhiteOnMove(yyboardindex)) {
570         if (currentMoveString[3] == '5') {
571             currentMoveString[1] = '5';
572             currentMoveString[3] = '6';
573         } else {
574             return (int) IllegalMove;
575         }
576     } else {
577         if (currentMoveString[3] == '4') {
578             currentMoveString[1] = '4';
579             currentMoveString[3] = '3';
580         } else {
581             return (int) IllegalMove;
582         }
583     }
584
585     result = LegalityTest(boards[yyboardindex],
586                           PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: might think we can e.p.!
587                           currentMoveString[1] - ONE,
588                           currentMoveString[0] - AAA,
589                           currentMoveString[3] - ONE,
590                           currentMoveString[2] - AAA,
591                           currentMoveString[4]);
592
593     if (result == WhiteCapturesEnPassant || result == BlackCapturesEnPassant)
594       return (int) result;
595     else
596       return (int) IllegalMove;
597 }
598
599 "+"?[A-Z][xX:-]?[a-l][0-9]=?  {
600     /*
601      * piece move, possibly ambiguous
602      */
603     DisambiguateClosure cl;
604     int skip = 0, skip2 = 0, promoted = 0;
605
606     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
607
608     if(yytext[0] == '+') promoted = skip = skip2 = 1;
609
610     /* remove the [xX:-] */
611     if ((yytext[1+skip] == 'x') || (yytext[1+skip] == 'X')
612         || (yytext[1+skip] == ':') || (yytext[1+skip] == '-')) skip++;
613
614     if (WhiteOnMove(yyboardindex)) {
615         cl.pieceIn = CharToPiece(ToUpper(yytext[skip2]));
616     } else {
617         cl.pieceIn = CharToPiece(ToLower(yytext[skip2]));
618     }
619     if(promoted) cl.pieceIn = (ChessSquare) (PROMOTED cl.pieceIn);
620
621     cl.rfIn = -1;
622     cl.ffIn = -1;
623     cl.rtIn = yytext[2+skip] - ONE;
624     cl.ftIn = yytext[1+skip] - AAA;
625     cl.promoCharIn = NULLCHAR;
626
627     if(yyleng-skip > 3) /* [HGM] can have Shogi-style promotion */
628         cl.promoCharIn = yytext[yyleng-1];
629
630     if (appData.debugMode) {
631         fprintf(debugFP, "Parser Qa1: yyleng=%d,  %d(%d,%d)-(%d,%d) = %d (%c)\n",
632         yyleng,
633         cl.pieceIn,cl.ffIn,cl.rfIn,cl.ftIn,cl.rtIn,cl.promoCharIn,cl.promoCharIn?cl.promoCharIn:' ');
634     }
635
636     /* [HGM] but do not allow values beyond board size */
637     if(cl.rtIn >= BOARD_HEIGHT ||
638        cl.rtIn <  0            ||
639        cl.ftIn >= BOARD_RGHT   ||
640        cl.ftIn <  BOARD_LEFT     )
641       return ImpossibleMove;
642
643     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
644
645     currentMoveString[0] = cl.ff + AAA;
646     currentMoveString[1] = cl.rf + ONE;
647     currentMoveString[2] = cl.ft + AAA;
648     currentMoveString[3] = cl.rt + ONE;
649     currentMoveString[4] = cl.promoChar;
650     currentMoveString[5] = NULLCHAR;
651
652     return (int) cl.kind;
653 }
654
655 "+"?[A-Z][a-l0-9][xX:-]?[a-l][0-9]=?   {
656     /*
657      * piece move with rank or file disambiguator
658      */
659     DisambiguateClosure cl;
660     int skip = 0, skip2 = 0; int promoted=0;
661
662     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
663
664     if(yytext[0]=='+') promoted = skip = skip2 = 1;
665
666     /* remove the [xX:-] */
667     if ((yytext[2+skip] == 'x') || (yytext[2+skip] == 'X')
668         || (yytext[2+skip] == ':') || (yytext[2+skip] == '-')) skip++;
669
670     if (WhiteOnMove(yyboardindex)) {
671         cl.pieceIn = CharToPiece(ToUpper(yytext[skip2]));
672     } else {
673         cl.pieceIn = CharToPiece(ToLower(yytext[skip2]));
674     }
675     if(promoted) cl.pieceIn = (ChessSquare) (PROMOTED cl.pieceIn);
676
677     if (isalpha(yytext[1+skip2])) {
678         cl.rfIn = -1;
679         cl.ffIn = yytext[1+skip2] - AAA;
680        
681         if(cl.ffIn >= BOARD_RGHT ||
682            cl.ffIn <  BOARD_LEFT   ) return 0;
683     } else {
684         cl.rfIn = yytext[1+skip2] - ONE;
685         cl.ffIn = -1;
686         if(cl.rfIn >= BOARD_HEIGHT ||
687            cl.rfIn <  0) return 0;
688     }
689     cl.rtIn = yytext[3+skip] - ONE;
690     cl.ftIn = yytext[2+skip] - AAA;
691     cl.promoCharIn = NULLCHAR;
692
693     if(yyleng-skip > 4) /* [HGM] can have Shogi-style promotion */
694         cl.promoCharIn = yytext[yyleng-1];
695
696     /* [HGM] do not allow values beyond board size */
697     if(cl.rtIn >= BOARD_HEIGHT ||
698        cl.rtIn <  0            ||
699        cl.ftIn >= BOARD_RGHT   ||
700        cl.ftIn <  BOARD_LEFT     )
701       return ImpossibleMove;
702
703     Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl);
704
705     currentMoveString[0] = cl.ff + AAA;
706     currentMoveString[1] = cl.rf + ONE;
707     currentMoveString[2] = cl.ft + AAA;
708     currentMoveString[3] = cl.rt + ONE;
709     currentMoveString[4] = cl.promoChar;
710     currentMoveString[5] = NULLCHAR;
711
712     return (int) cl.kind;
713 }
714
715 000|0-0-0|ooo|OOO|o-o-o|O-O-O   {
716     int rf, ff, rt, ft;
717
718     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
719
720     /* [HGM] all squares referenced to board edges in stead of absolute */
721     if (WhiteOnMove(yyboardindex)) {
722         if (boards[yyboardindex][0][(BOARD_WIDTH-1)>>1] == WhiteKing) {
723             /* ICS wild castling */
724             rf = 0;
725             ff = (BOARD_WIDTH-1)>>1;
726             rt = 0;
727             ft = BOARD_RGHT-3;
728         } else {
729             rf = 0;
730             ff = BOARD_WIDTH>>1;
731             rt = 0;
732             ft = BOARD_LEFT+2;
733         }
734     } else{ 
735         if (boards[yyboardindex][BOARD_HEIGHT-1][(BOARD_WIDTH-1)>>1] == BlackKing) {
736             /* ICS wild castling */
737             rf = BOARD_HEIGHT-1;
738             ff = (BOARD_WIDTH-1)>>1;
739             rt = BOARD_HEIGHT-1;
740             ft = BOARD_RGHT-3;
741         } else {
742             rf = BOARD_HEIGHT-1;
743             ff = BOARD_WIDTH>>1;
744             rt = BOARD_HEIGHT-1;
745             ft = BOARD_LEFT+2;
746         }
747     }
748     if(PosFlags(0) & F_FRC_TYPE_CASTLING) {
749         if (WhiteOnMove(yyboardindex)) {
750             ff = initialRights[2];
751             ft = initialRights[1];
752         } else {
753             ff = initialRights[5];
754             ft = initialRights[4];
755         }
756         if (appData.debugMode) 
757         {
758           fprintf(debugFP, "Parser FRC long %d %d\n", ff, ft);
759         };
760         if(ff < 0 || ft < 0) return 0;
761     }
762     sprintf(currentMoveString, "%c%c%c%c",ff+AAA,rf+ONE,ft+AAA,rt+ONE);
763     if (appData.debugMode) {
764         fprintf(debugFP, "long castling %d %d\n", ff, ft);
765     }
766     return (int) LegalityTest(boards[yyboardindex],
767                               PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: e.p.!
768                               rf, ff, rt, ft, NULLCHAR);
769 }
770
771 00|0-0|oo|OO|o-o|O-O    {
772     int rf, ff, rt, ft;
773
774     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
775
776     if (WhiteOnMove(yyboardindex)) {
777         if (boards[yyboardindex][0][(BOARD_WIDTH-1)>>1] == WhiteKing) {
778             /* ICS wild castling */
779             rf = 0;
780             ff = (BOARD_WIDTH-1)>>1;
781             rt = 0;
782             ft = BOARD_LEFT+1;
783         } else {
784             rf = 0;
785             ff = BOARD_WIDTH>>1;
786             rt = 0;
787             ft = BOARD_RGHT-2;
788         }
789     } else {
790         if (boards[yyboardindex][BOARD_HEIGHT-1][(BOARD_WIDTH-1)>>1] == BlackKing) {
791             /* ICS wild castling */
792             rf = BOARD_HEIGHT-1;
793             ff = (BOARD_WIDTH-1)>>1;
794             rt = BOARD_HEIGHT-1;
795             ft = BOARD_LEFT+1;
796         } else {
797             rf = BOARD_HEIGHT-1;
798             ff = BOARD_WIDTH>>1;
799             rt = BOARD_HEIGHT-1;
800             ft = BOARD_RGHT-2;
801         }
802     }
803     if(PosFlags(0) & F_FRC_TYPE_CASTLING) {
804         if (WhiteOnMove(yyboardindex)) {
805             ff = initialRights[2];
806             ft = initialRights[0];
807         } else {
808             ff = initialRights[5];
809             ft = initialRights[3];
810         }
811     if (appData.debugMode) {
812         fprintf(debugFP, "Parser FRC short %d %d\n", ff, ft);
813     }
814         if(ff < 0 || ft < 0) return 0;
815     }
816     sprintf(currentMoveString, "%c%c%c%c",ff+AAA,rf+ONE,ft+AAA,rt+ONE);
817     if (appData.debugMode) {
818         fprintf(debugFP, "short castling %d %d\n", ff, ft);
819     }
820
821     return (int) LegalityTest(boards[yyboardindex],
822                               PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: e.p.!
823                               rf, ff, rt, ft, NULLCHAR);
824 }
825
826 [A-Z][@*][a-l][0-9] {
827     /* Bughouse piece drop. */
828     currentMoveString[1] = '@';
829     currentMoveString[2] = yytext[2];
830     currentMoveString[3] = yytext[3];
831     currentMoveString[4] = NULLCHAR;
832
833     if (appData.debugMode) {
834         fprintf(debugFP, "Drop: %s\n", currentMoveString);
835     }
836     /* [HGM] do not allow values beyond board size */
837     if(currentMoveString[3] - ONE >= BOARD_HEIGHT ||
838        currentMoveString[2] - AAA >= BOARD_WIDTH     )
839       return ImpossibleMove;
840
841     if (WhiteOnMove(yyboardindex)) {
842         currentMoveString[0] = ToUpper(yytext[0]);
843     } else {
844         currentMoveString[0] = ToLower(yytext[0]);
845     }
846     return LegalityTest(boards[yyboardindex], PosFlags(yyboardindex), DROP_RANK, // [HGM] does drops now too
847                         CharToPiece(currentMoveString[0]), currentMoveString[3] - ONE, currentMoveString[2] - AAA, NULLCHAR);
848 }
849
850 [Rr]esign(s|ed)?  {
851     if (WhiteOnMove(yyboardindex))
852       return (int) BlackWins;
853     else
854       return (int) WhiteWins;
855 }
856
857 (([Ww](hite)?)|([Bb](lack)?))" "(([Rr]esign)|([Ff]orfeit))(s|ed)?  {
858     return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins);
859 }
860
861 (([Ww](hite)?)|([Bb](lack)?))" "[Dd]isconnect(s|ed)  {
862     return (int) GameUnfinished;
863 }
864
865 [Ss]talemate  {
866     return (int) GameIsDrawn;
867 }
868
869 "+-+"  {
870     return (int) GameIsDrawn;
871 }
872
873 ([Cc]heck)?[Mm]ate {
874     if (WhiteOnMove(yyboardindex))
875       return (int) BlackWins;
876     else
877       return (int) WhiteWins;
878 }
879
880 "++"  {
881     if (WhiteOnMove(yyboardindex))
882       return (int) BlackWins;
883     else
884       return (int) WhiteWins;
885 }
886
887 [Dd]raw(n)?(" "by)?(" "[Rr]epetition)|(" "[Aa]gree(d|ment))  {
888     return (int) GameIsDrawn;
889 }
890
891 [Dd]raw(n)?(" (".*")")?  {
892     return (int) GameIsDrawn;
893 }
894
895 (([Ww](hite)?)|([Bb](lack)?))" "(([Mm]ates)|([Ww][io]n(s)?)) { 
896     return (int) (ToUpper(yytext[0]) == 'W' ? WhiteWins : BlackWins);
897 }
898
899 (([Ww](hite)?)|([Bb](lack)?))" "(([Mm]ated)|([Ll]os[tes]+)) { 
900     return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins);
901 }
902
903 ("{"[^\}\n]*"} ")?(1-0|"1 - 0"|"1/0"|"1 / 0"|"1:0"|"1 : 0")(" (".*")"|" {".*"}")? { 
904     return (int) WhiteWins;
905 }
906
907 ("{"[^\}\n]*"} ")?(0-1|"0 - 1"|"0/1"|"0 / 1"|"0:1"|"0 : 1")(" (".*")"|" {".*"}")? { 
908     return (int) BlackWins;
909 }
910
911 ("{"[^\}\n]*"} ")?("1/2"|"1 / 2")(" "?[-:]" "?("1/2"|"1 / 2"))?(" (".*")"|" {".*"}")? {
912     return (int) GameIsDrawn;
913 }
914
915 ("{"[^\}\n]*"} ")?"*"(" (".*")"|" {".*"}")? {
916     return (int) GameUnfinished;
917 }
918
919 [1-9][0-9]*/"."?[ \t\n]*[a-lNnPpRrBQqKACFEWDGHOo]    {
920     /* move numbers */
921     if ((yyleng == 1) && (yytext[0] == '1'))
922       return (int) MoveNumberOne;
923 }
924
925 \([0-9]+:[0-9][0-9](\.[0-9]+)?\)|\{[0-9]+:[0-9][0-9](\.[0-9]+)?\} {
926     /* elapsed time indication, e.g. (0:12) or {10:21.071} */ 
927     return (int) ElapsedTime;
928 }
929
930 "[--"[^\]]*"--]" {
931     /* position diagram enclosed in [-- --] */
932     return (int) PositionDiagram;
933 }
934
935 ^"{--------------"\n[^\}]*\n"--------------}"$ {
936     /* position diagram enclosed in {-- --} */
937     return (int) PositionDiagram;
938 }
939
940 \[[ \t\n]*[A-Za-z0-9][A-Za-z0-9_+#=-]*[ \t\n]*\"[^"]*\"[ \t\n]*\] {
941     return (int) PGNTag;
942 }    
943
944 [Gg](nu|NU)" "?[Cc](hess|HESS).*[Gg](ame|AME) {
945     return (int) GNUChessGame;
946 }
947
948 ^[#;%]" "[^ ]*(" game file"|" position file").*$ {
949     return (int) XBoardGame;
950 }
951
952 \$[0-9]+        {                               /* numeric annotation glyph */
953     return (int) NAG;
954 }
955
956 \{[^\}]*\}      {                               /* anything in {} */
957     return (int) Comment; 
958 }
959
960 ;.*$ {                                          /* ; to end of line */
961     return (int) Comment;
962 }
963
964 \[[^\]]*\]      {                               /* anything in [] */
965     return (int) Comment; 
966 }
967
968 \([^()]*(\([^()]*(\([^()]*(\([^()]*\)[^()]*)*\)[^()]*)*\)[^()]*)+[^()]*\)  { /* very nested () */
969     return (int) Comment; 
970 }
971
972 \([^)][^)]+\)   {                               /* >=2 chars in () */
973     return (int) Comment; 
974 }       
975
976 ^[-a-zA-Z0-9]+:" ".*(\n[ \t]+.*)*  {
977         /* Skip mail headers */
978 }
979
980 [a-zA-Z0-9'-]+                  {
981         /* Skip random words */
982 }
983
984 .|\n                            {
985         /* Skip everything else */
986 }
987
988 %%
989
990
991 static char *StringToLex;
992
993 #ifndef FLEX_SCANNER
994 static FILE *lexFP;
995
996 static int input()
997 {
998     int ret;
999     
1000     if (StringToLex != NULL) {
1001         ret = *StringToLex;
1002         if (ret == NULLCHAR)
1003           ret = EOF;
1004         else
1005           StringToLex++;
1006     } else if (unputCount > 0) {
1007         ret = unputBuffer[--unputCount];
1008     } else {
1009         ret = fgetc(lexFP);
1010     }    
1011
1012     if (ret == EOF) 
1013       return 0;
1014     else
1015       return ret;
1016 }
1017
1018 /*
1019  * Return offset of next pattern within current file
1020  */
1021 int yyoffset()
1022 {
1023     int offset = ftell(lexFP) - unputCount;
1024
1025     if (offset < 0) {
1026         offset = 0;
1027     }
1028     return(offset);
1029 }
1030  
1031 static void output(ch)
1032      int ch;
1033 {
1034     if(appData.debugMode) fprintf(debugFP, "PARSER BUG: unmatched character '%c' (0%o)\n",
1035             ch, ch);
1036 }
1037
1038 static void unput(ch)
1039      int ch;
1040 {
1041     if (ch == 0) return;
1042     if (StringToLex != NULL) {
1043         StringToLex--;
1044     } else {
1045         if (unputCount >= UNPUT_BUF_SIZE)
1046           if(appData.debugMode) fprintf(debugFP, "PARSER BUG: unput buffer overflow '%c' (0%o)\n",
1047                   ch, ch);
1048         unputBuffer[unputCount++] = ch;
1049     }
1050 }
1051
1052 /* Get ready to lex from a new file.  Kludge below sticks
1053    an artificial newline at the front of the file, which the
1054    above grammar ignores, but which makes ^ at start of pattern
1055    match at the real start of the file.
1056 */
1057 void yynewfile(f)
1058      FILE *f;
1059 {
1060     lexFP = f;
1061     StringToLex = NULL;
1062     unputCount = 0;
1063     unput('\n'); /* kludge */
1064 }
1065
1066 /* Get ready to lex from a string.  ^ at start of pattern WON'T
1067    match at the start of the string!
1068 */
1069 void yynewstr(s)
1070      char *s;
1071 {
1072     lexFP = NULL;
1073     StringToLex = s;
1074     unputCount = 0;
1075 }
1076 #endif /*!FLEX_SCANNER*/
1077
1078 #ifdef FLEX_SCANNER
1079 void my_yy_input(buf, result, max_size)
1080      char *buf;
1081      int *result;
1082      int max_size;
1083 {
1084     int count;
1085
1086     if (StringToLex != NULL) {
1087         count = 0;
1088         while (*StringToLex != NULLCHAR) {
1089             *buf++ = *StringToLex++;
1090             count++;
1091         }
1092         *result = count;
1093         return;
1094     } else {
1095         count = fread(buf, 1, max_size, yyin);
1096         if (count == 0) {
1097             *result = YY_NULL;
1098         } else {
1099             *result = count;
1100         }
1101         return;
1102     }    
1103 }
1104
1105 static YY_BUFFER_STATE my_file_buffer = NULL;
1106
1107 /*
1108     Return offset of next pattern in the current file.
1109 */
1110 int yyoffset()
1111 {
1112     int pos = yy_c_buf_p - YY_CURRENT_BUFFER->yy_ch_buf;
1113
1114     return(ftell(YY_CURRENT_BUFFER->yy_input_file) -
1115          yy_n_chars + pos);
1116 }
1117
1118
1119 void yynewstr(s)
1120      char *s;
1121 {
1122     if (my_file_buffer != NULL)
1123       yy_delete_buffer(my_file_buffer);
1124     StringToLex = s;
1125     my_file_buffer = yy_create_buffer(stdin, YY_BUF_SIZE);
1126     yy_switch_to_buffer(my_file_buffer);
1127 }
1128
1129 void yynewfile(f)
1130      FILE *f;
1131 {
1132     if (my_file_buffer != NULL)
1133       yy_delete_buffer(my_file_buffer);
1134     StringToLex = NULL;
1135     my_file_buffer = yy_create_buffer(f, YY_BUF_SIZE);
1136     yy_switch_to_buffer(my_file_buffer);
1137 }
1138 #endif /*FLEX_SCANNER*/
1139
1140 int yywrap()
1141 {
1142     return TRUE;
1143 }
1144
1145 /* Parse a move from the given string s */
1146 /* ^ at start of pattern WON'T work here unless using flex */
1147 ChessMove yylexstr(boardIndex, s, text, len)
1148      int boardIndex, len;
1149      char *s, *text;
1150 {
1151     ChessMove ret;
1152     char *oldStringToLex;
1153 #ifdef FLEX_SCANNER
1154     YY_BUFFER_STATE buffer, oldBuffer;
1155 #endif
1156     
1157     yyboardindex = boardIndex;
1158     oldStringToLex = StringToLex;
1159     StringToLex = s;
1160 #ifdef FLEX_SCANNER
1161     buffer = yy_create_buffer(stdin, YY_BUF_SIZE);
1162     oldBuffer = YY_CURRENT_BUFFER;
1163     yy_switch_to_buffer(buffer);
1164 #endif /*FLEX_SCANNER*/
1165
1166     ret = (ChessMove) yylex();
1167      strncpy(text, yy_text, len-1); // [HGM] vari: yy_text is not available to caller after buffer switch ?!?
1168      text[len-1] = NULLCHAR;
1169
1170 #ifdef FLEX_SCANNER
1171     if (oldBuffer != NULL) 
1172       yy_switch_to_buffer(oldBuffer);
1173     yy_delete_buffer(buffer);
1174 #endif /*FLEX_SCANNER*/
1175     StringToLex = oldStringToLex;
1176
1177     return ret;
1178 }