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