/** * A grammar file for chess notations in PGN format. * * Author: Martin Rademacher mano@radebatz.net * Version: 0.7.0 * * 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 * * Changes from v0.6.5: * - full support * - the grammar accepts now e8=Q and e8Q * - I came across move related comments in '{' and '}' that will be tolerated now. * (see MoveAnnotation()) * - simple '(' and ')' handler are implemented (I think it's up to the application * to do something useful with them...) * - the move number can have more that one now. * - and will be trated as token now (thanks to the enhanced promotion rules) * * * * I would be happy about response including praise, donations or even * bug reports ;) * * * TODO: * - Add more syntax 'flavours' * - Some white space before EOF bug??? * - Workaround for non quoted '"' in token??? * - Support for long notations e.g. Be4-d5 * */ /* 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 : { "\r" | "\n" } /* PGN token and literals */ TOKEN : { < LBRACKET: "[" > : IN_PGN_TAG | < LPAREN: "(" > | < RPAREN: ")" > | < LBRACE: "{" > : IN_MOVE_COMMENT | < RBRACE: "}" > | < 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: "+" > | < END_OF_TOKEN: (" " | ) > | < EOL: ("\n" | "\r\n") > | < 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"]) | ("\\" ( ["n","t","b","r","f","\\","'","\""] | ["0"-"7"] ( ["0"-"7"] )? | ["0"-"3"] ["0"-"7"] ["0"-"7"] ) ) )* "\"" > } /* 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 } MORE : { < ~["}"] > } TOKEN : { < MOVE_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)); } } /** * Tag comment. */ void TagComment() : { Token cc = null; } { cc = { System.out.println("COMMENT: " + imageOf(cc)); } } /** * Move comment. */ void MoveComment() : { Token cc = null; } { ( cc = ) { System.out.println("MOVE_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; } | TagComment() { return 1; } | MoveComment() { return 1; } | GameTerminator() { return 1; } | RecAnnotation() { return 1; } | NAG() { return 1; } | ( | ) { return 0; } | { return -1; } }