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