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