New parser, written in C
[xboard.git] / parser.l
index 6dcd13b..8ceaa55 100644 (file)
--- a/parser.l
+++ b/parser.l
@@ -12,7 +12,7 @@
  * Massachusetts.
  *
  * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005,
- * 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ * 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
  *
  * The following terms apply to Digital Equipment Corporation's copyright
  * interest in XBoard:
@@ -178,7 +178,7 @@ extern void CopyBoard P((Board to, Board from));
 %}
 %%
 
-"+"?[A-Z][/]?[a-l][0-9][xX:-]?[a-l][0-9]((=?\(?[A-Z]\)?)|[=+])? {
+"+"?[A-Z][/]?[a-l][0-9][xX:-]?[a-l][0-9](([=/]?\(?[A-Z]\)?)|[=+])? {
     /*
      * Fully-qualified algebraic move, possibly with promotion
      */
@@ -268,7 +268,7 @@ extern void CopyBoard P((Board to, Board from));
     return (int) result;
 }
 
-[a-l][0-9][xX:-]?[a-l][0-9]((=?\(?[A-Za-z]\)?)|[=+])?      {
+[a-l][0-9][xX:-]?[a-l][0-9](([=/]?\(?[A-Za-z]\)?)|[=+])?      {
     /*
      * Simple algebraic move, possibly with promotion
      * [HGM] Engine moves are received in this format, with lower-case promoChar!
@@ -294,7 +294,7 @@ extern void CopyBoard P((Board to, Board from));
        } else {
             c = currentMoveString[4] = ToLower(yytext[yyleng-1]);
        }
-        if(c == '+' && gameInfo.variant != VariantShogi) c = currentMoveString[4] = NULLCHAR; // + means check outside Shogi
+        if(c == '+' && gameInfo.variant != VariantShogi) currentMoveString[4] = NULLCHAR; // + means check outside Shogi
        currentMoveString[5] = NULLCHAR;
     }
 
@@ -330,7 +330,7 @@ extern void CopyBoard P((Board to, Board from));
       } else if(result == WhiteNonPromotion  || result == BlackNonPromotion)
             currentMoveString[4] = '=';
       currentMoveString[5] = NULLCHAR;
-    } else if(appData.testLegality && // strip off unnecessary and false promo characters
+    } else if(appData.testLegality && gameInfo.variant != VariantSChess && // strip off unnecessary and false promo characters
        !(result == WhitePromotion  || result == BlackPromotion ||
          result == WhiteNonPromotion || result == BlackNonPromotion)) currentMoveString[4] = NULLCHAR;
 
@@ -384,7 +384,7 @@ extern void CopyBoard P((Board to, Board from));
      * Pawn move, possibly with promotion
      */
     DisambiguateClosure cl;
-    int skip = 0; char c;
+    int skip = 0;
 
     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
 
@@ -425,7 +425,7 @@ extern void CopyBoard P((Board to, Board from));
      * Pawn capture, possibly with promotion, possibly ambiguous
      */
     DisambiguateClosure cl;
-    int skip1 = 0, skip2 = 0; char c;
+    int skip1 = 0, skip2 = 0;
 
     if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
 
@@ -584,11 +584,15 @@ extern void CopyBoard P((Board to, Board from));
 
     if (result == WhiteCapturesEnPassant || result == BlackCapturesEnPassant)
       return (int) result;
-    else
+    else { // [HGM] all very nice, but this messed up the input move that we might want to accept with legality testing off...
+      if (WhiteOnMove(yyboardindex)) // undo the damage
+            currentMoveString[1]--,  currentMoveString[3]--;
+       else currentMoveString[1]++,  currentMoveString[3]++;
       return (int) IllegalMove;
+    }
 }
 
-"+"?[A-Z][xX:-]?[a-l][0-9]((=?\(?[A-Z]\)?)|[=+])?  {
+"+"?[A-Z][xX:-]?[a-l][0-9](([=/]?\(?[A-Z]\)?)|[=+])?  {
     /*
      * piece move, possibly ambiguous
      */
@@ -616,7 +620,7 @@ extern void CopyBoard P((Board to, Board from));
     cl.ftIn = yytext[1+skip] - AAA;
     cl.promoCharIn = NULLCHAR;
 
-    if(yyleng-skip > 3 && gameInfo.variant == VariantShogi) /* [HGM] can have Shogi-style promotion */
+    if(yyleng-skip > 3) /* [HGM] in some variants pieces promote */
         cl.promoCharIn = yytext[yyleng-1-(yytext[yyleng-1]==')')];
     if(cl.promoCharIn == '+' && gameInfo.variant != VariantShogi) cl.promoCharIn = NULLCHAR; // + means check outside Shogi
 
@@ -645,7 +649,7 @@ extern void CopyBoard P((Board to, Board from));
     return (int) cl.kind;
 }
 
-"+"?[A-Z][a-l0-9][xX:-]?[a-l][0-9]((=?\(?[A-Z]\)?)|[=+])?   {
+"+"?[A-Z][a-l0-9][xX:-]?[a-l][0-9](([=/]?\(?[A-Z]\)?)|[=+])?   {
     /*
      * piece move with rank or file disambiguator
      */
@@ -683,7 +687,7 @@ extern void CopyBoard P((Board to, Board from));
     cl.ftIn = yytext[2+skip] - AAA;
     cl.promoCharIn = NULLCHAR;
 
-    if(yyleng-skip > 4) /* [HGM] can have Shogi-style promotion */
+    if(yyleng-skip > 4) /* [HGM] in some variants pieces promote */
         cl.promoCharIn = yytext[yyleng-1-(yytext[yyleng-1]==')')];
     if(cl.promoCharIn == '+' && gameInfo.variant != VariantShogi) cl.promoCharIn = NULLCHAR; // + means check outside Shogi
 
@@ -752,7 +756,7 @@ extern void CopyBoard P((Board to, Board from));
         {
           fprintf(debugFP, "Parser FRC long %d %d\n", ff, ft);
         };
-        if(ff < 0 || ft < 0) return 0;
+        if(ff == NoRights || ft == NoRights) return ImpossibleMove;
     }
     sprintf(currentMoveString, "%c%c%c%c",ff+AAA,rf+ONE,ft+AAA,rt+ONE);
     if (appData.debugMode) {
@@ -806,7 +810,7 @@ extern void CopyBoard P((Board to, Board from));
     if (appData.debugMode) {
         fprintf(debugFP, "Parser FRC short %d %d\n", ff, ft);
     }
-        if(ff < 0 || ft < 0) return 0;
+        if(ff == NoRights || ft == NoRights) return ImpossibleMove;
     }
     sprintf(currentMoveString, "%c%c%c%c",ff+AAA,rf+ONE,ft+AAA,rt+ONE);
     if (appData.debugMode) {
@@ -819,6 +823,9 @@ extern void CopyBoard P((Board to, Board from));
 }
 
 [A-Za-z][@*][a-l][0-9] {
+
+    if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */
+
     /* Bughouse piece drop. */
     currentMoveString[1] = '@';
     currentMoveString[2] = yytext[2];
@@ -838,6 +845,8 @@ extern void CopyBoard P((Board to, Board from));
     } else {
        currentMoveString[0] = ToLower(yytext[0]);
     }
+    if(CharToPiece(currentMoveString[0]) == EmptySquare) return ImpossibleMove; // Unknown piece;
+
     return LegalityTest(boards[yyboardindex], PosFlags(yyboardindex), DROP_RANK, // [HGM] does drops now too
                         CharToPiece(currentMoveString[0]), currentMoveString[3] - ONE, currentMoveString[2] - AAA, NULLCHAR);
 }
@@ -895,26 +904,27 @@ extern void CopyBoard P((Board to, Board from));
     return (int) (ToUpper(yytext[0]) == 'W' ? BlackWins : WhiteWins);
 }
 
-("{"[^\}\n]*"} ")?(1-0|"1 - 0"|"1/0"|"1 / 0"|"1:0"|"1 : 0")(" (".*")"|" {".*"}")? { 
+("{"[^\}]*"}"[ \n])?(1-0|"1 - 0"|"1/0"|"1 / 0"|"1:0"|"1 : 0")(" (".*")"|" {".*"}")? { 
     return (int) WhiteWins;
 }
 
-("{"[^\}\n]*"} ")?(0-1|"0 - 1"|"0/1"|"0 / 1"|"0:1"|"0 : 1")(" (".*")"|" {".*"}")? { 
+("{"[^\}]*"}"[ \n])?(0-1|"0 - 1"|"0/1"|"0 / 1"|"0:1"|"0 : 1")(" (".*")"|" {".*"}")? { 
     return (int) BlackWins;
 }
 
-("{"[^\}\n]*"} ")?("1/2"|"1 / 2")(" "?[-:]" "?("1/2"|"1 / 2"))?(" (".*")"|" {".*"}")? {
+("{"[^\}]*"}"[ \n])?("1/2"|"1 / 2")(" "?[-:]" "?("1/2"|"1 / 2"))?(" (".*")"|" {".*"}")? {
     return (int) GameIsDrawn;
 }
 
-("{"[^\}\n]*"} ")?"*"(" (".*")"|" {".*"}")? {
+("{"[^\}]*"}"[ \n])?"*"(" (".*")"|" {".*"}")? {
     return (int) GameUnfinished;
 }
 
-[1-9][0-9]*/"."?[ \t\n]*[a-lNnPpRrBQqKACFEWDGHOo]    {
+[1-9][0-9]*/"."?[ \t\n]*[a-lnprqoA-Z+]    {
     /* move numbers */
     if ((yyleng == 1) && (yytext[0] == '1'))
       return (int) MoveNumberOne;
+    else return (int) Nothing; // [HGM] make sure something is returned, for gathering parsed text
 }
 
 \([0-9]+:[0-9][0-9](\.[0-9]+)?\)|\{[0-9]+:[0-9][0-9](\.[0-9]+)?\} {
@@ -960,24 +970,24 @@ extern void CopyBoard P((Board to, Board from));
     return (int) Comment; 
 }
 
-\([^()]*(\([^()]*(\([^()]*(\([^()]*\)[^()]*)*\)[^()]*)*\)[^()]*)+[^()]*\)  { /* very nested () */
-    return (int) Comment; 
+\(  {                               /* Opening parentheses */
+    return (int) Open; 
 }
 
-\([^)][^)]+\)   {                              /* >=2 chars in () */
-    return (int) Comment; 
+\)   {                                       /* closing parentheses */
+    return (int) Close; 
 }       
 
 ^[-a-zA-Z0-9]+:" ".*(\n[ \t]+.*)*  {
-        /* Skip mail headers */
+    return (int) Nothing;                 /* Skip mail headers */
 }
 
 [a-zA-Z0-9'-]+                 {
-        /* Skip random words */
+    return (int) Nothing;                 /* Skip random words */
 }
 
 .|\n                           {
-        /* Skip everything else */
+    return (int) Nothing;                 /* Skip everything else */
 }
 
 %%
@@ -1158,7 +1168,7 @@ ChessMove yylexstr(boardIndex, s, text, len)
     yy_switch_to_buffer(buffer);
 #endif /*FLEX_SCANNER*/
 
-    ret = (ChessMove) yylex();
+    ret = (ChessMove) Myylex();
      strncpy(text, yy_text, len-1); // [HGM] vari: yy_text is not available to caller after buffer switch ?!?
      text[len-1] = NULLCHAR;
 
@@ -1171,3 +1181,23 @@ ChessMove yylexstr(boardIndex, s, text, len)
 
     return ret;
 }
+
+int Myylex()
+{   // [HGM] wrapper for yylex, which treats nesting of parentheses
+    int symbol, nestingLevel = 0, i=0;
+    char *p;
+    static char buf[256*MSG_SIZ];
+    buf[0] = NULLCHAR;
+    do { // eat away anything not at level 0
+        symbol = yylex();
+        if(symbol == Open) nestingLevel++;
+        if(nestingLevel) { // save all parsed text between (and including) the ()
+            for(p=yytext; *p && i<256*MSG_SIZ-2;) buf[i++] = *p++;
+            buf[i] = NULLCHAR;
+        }
+        if(symbol == 0) break; // ran into EOF
+        if(symbol == Close) symbol = Comment, nestingLevel--;
+    } while(nestingLevel || symbol == Nothing);
+    yy_text = buf[0] ? buf : (char*)yytext;
+    return symbol;
+}