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