/** * A grammar file for chess notations in PGN format. * * Author: Martin Rademacher mano@radebatz.net * Version: 0.6.5 * * This version is designed to understand only SAN notation. * * PGN file formats are divided into import and export formats. While import * formats have a somewhat weaker grammar definition, export files are supposed * to follow certain rules. * At the moment this grammar supports a mixture of both. There are also different * 'flavours' regarding moves; see the castle pattern or pawn promotion. * So far I've seen "e8=Q" (PGN standard), "e8Q" or even e8(Q). This will be * incorporated in one of the next versions. * * This grammar is based on the PGN standard description found * on the PGN standard Revision 1994.03.12 as found at: * http://www.very-best.de/pgn-spec.htm * or * http://www.saremba.de/chessgml/standards/pgn/pgn-complete.htm * * * I would be happy about response including praise, donations or even * bug reports ;) * * TODO: * - add more syntax 'flavours' * - implement handling of parenthesis "(" and ")" * */ /* options { DEBUG_TOKEN_MANAGER = true; } */ PARSER_BEGIN(PGNParser) public class PGNParser { /** * make it a full Singleton (well, at least for streams * and only if we always use the getInstance method) */ private static PGNParser aInstance; /** * Get the only instance... */ public static final PGNParser getInstance(java.io.InputStream is) { if(null == aInstance) { aInstance = new PGNParser(is); } else { aInstance.ReInit(is); } return aInstance; } public static void main(String args[]) throws ParseException, Exception { try { PGNParser parser = new PGNParser(new java.io.FileInputStream(args[0])); while(-1 != parser.parse()); } catch (ParseException pe) { System.out.println("Exiting..."); throw pe; } catch (Exception e) { throw e; } } /** * A little helper. */ private static String imageOf(Token t) { return (null != t ? t.image : ""); //return (null != t ? t.image : null); } } PARSER_END(PGNParser) /** * skip some stuff */ SKIP : { " " | < EOL: ("\n" | "\r\n") > } /* PGN token and literals */ TOKEN : { < LBRACKET: "[" > : IN_PGN_TAG | < LPAREN: "(" > | < RPAREN: ")" > | < DOT: "." > | < MOVE_NUMBER: ( ) > | < BLACK_MOVE: ( ) > | < FILE_NAME: ["a" - "h"] > | < RANK_NAME: ["1" - "8"] > | < SQUARE: ( ) > | < CASTLE_KINGSIDE: ("O-O" | "0-0" | "o-o") > | < CASTLE_QUEENSIDE: ("O-O-O" | "0-0-0" | "o-o-o") > | < PIECE_IDENT: ["N", "B", "R", "Q", "K"] > | < PROMOTE: "=" > | < CAPTURE: "x" > | < DASH: "-" > | < CHECK: "+" > | < CHECKMATE: ("#" | "++") > | < SUFFIX_ANNOTATION: ("!!" | "!" | "!?" | "?!" | "?" | "??") > | < NAG: ("$" ()? ()?) > | < GAME_TERMINATOR: ("1-0" | "0-1" | "1/2-1/2" | "*") > | < #INTEGER: ()+ > | < #DIGIT: ["0" - "9"] > | < #LETTER: ["a" - "z", "A" - "Z"] > } /* Handle PGN tags */ TOKEN : { < RBRACKET: "]" > : DEFAULT | < SPACE: " "> | < SYMBOL: (( | ) ( | | "_" | "+" | "#" | "=" | ":" | "-")*) > | < STRING: "\"" ((~["\"","\\","\n","\r"]) | ("\\" (["\\", "'", "\""])))* "\"" > } /* Comments; * Per definition the escape char '%' is only defined as a special token, * when in the first column of the line - I suppose it'll do no damage though * since it it not defined anywhere else... */ MORE : { < START_OF_COMMENT: [";", "%"] > : IN_COMMENT } MORE : { < ~[] > } TOKEN : { < COMMENT: > : DEFAULT } /* ******************************************************************************* * Productions start here! ******************************************************************************* */ /** * PGN tag. * Doesn't allow white space around the brackets. * Tags omitting the symbol tag are regarded as global comments. */ void PGNTag() : { Token t = null; Token v = null; } { ( ((t = ()+) | ()+)? v = ()? ) { System.out.println("Tag: '" + imageOf(t) + "' = '" + imageOf(v) + "'"); } } /** * Move number. */ void MoveNumber() : { Token t; } { ( t = ) { System.out.println("#" + t.image); } } /** * A move. */ void Move() : { Token ss = null; Token pp = null; Token ii = null; Token cc = null; Token aa = null; Token ff = null; Token rr = null; } { // pawns ((ff = aa = )? ss = (pp = ii = )?) { System.out.println("pawn: " + imageOf(ff) + imageOf(aa) + imageOf(ss) + imageOf(pp) + imageOf(ii)); } // pieces | (ii = (ff = )? (rr = )? (aa = )? ss = ) { System.out.println("piece: " + imageOf(ii) + imageOf(ff) + imageOf(aa) + imageOf(rr) + imageOf(ss)); } // castle | ((cc = | cc = )) { System.out.println("casteling: " + imageOf(cc)); } // ... | (cc = ) { System.out.println("black move: " + imageOf(cc)); } } /** * Move text is a complete move notation. */ void MoveText() : { Token cc = null; Token ss = null; } { (Move() (cc = | cc = )? (ss = )?) { System.out.println("movetext: " + imageOf(cc) + imageOf(ss)); } } /** * Move text is a complete move notation. */ void GameTerminator() : { Token gg = null; } { (gg = ) { System.out.println("game terminator: " + imageOf(gg)); } } /** * NAG. * A numeric annotation glyph; Range: 0 - 255. */ void NAG() : { Token gg = null; } { gg = { System.out.println("NAG: " + imageOf(gg)); } } /** * Comment. */ void Comment() : { Token cc = null; } { cc = { System.out.println("COMMENT: " + imageOf(cc)); } } /** * Recursive annotation variation. */ void RecAnnotation() : { Token pp = null; } { (pp = ) { System.out.println("Start of recursive annotation: " + imageOf(pp)); } | (pp = ) { System.out.println("End of recursive annotation: " + imageOf(pp)); } } /** * Main production. */ int parse() : { } { PGNTag() { return 1; } | MoveNumber() { return 1; } | MoveText() { return 1; } | Comment() { return 1; } | GameTerminator() { return 1; } | RecAnnotation() { return 1; } | NAG() { return 1; } | { return 0; } | { return -1; } }