| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394 |
- # pgn.py rel. 1.1 17-sep-2004
- #
- # Demonstration of the parsing module, implementing a pgn parser.
- #
- # The aim of this parser is not to support database application,
- # but to create automagically a pgn annotated reading the log console file
- # of a lecture of ICC (Internet Chess Club), saved by Blitzin.
- # Of course you can modify the Abstract Syntax Tree to your purpose.
- #
- # Copyright 2004, by Alberto Santini http://www.albertosantini.it/chess/
- #
- from pyparsing import alphanums, nums, quotedString
- from pyparsing import Combine, Forward, Group, Literal, oneOf, OneOrMore, Optional, Suppress, ZeroOrMore, Word
- from pyparsing import ParseException
- #
- # define pgn grammar
- #
- tag = Suppress("[") + Word(alphanums) + Combine(quotedString) + Suppress("]")
- comment = Suppress("{") + Word(alphanums + " ") + Suppress("}")
- dot = Literal(".")
- piece = oneOf("K Q B N R")
- file_coord = oneOf("a b c d e f g h")
- rank_coord = oneOf("1 2 3 4 5 6 7 8")
- capture = oneOf("x :")
- promote = Literal("=")
- castle_queenside = oneOf("O-O-O 0-0-0 o-o-o")
- castle_kingside = oneOf("O-O 0-0 o-o")
- move_number = Optional(comment) + Word(nums) + dot
- m1 = file_coord + rank_coord # pawn move e.g. d4
- m2 = file_coord + capture + file_coord + rank_coord # pawn capture move e.g. dxe5
- m3 = file_coord + "8" + promote + piece # pawn promotion e.g. e8=Q
- m4 = piece + file_coord + rank_coord # piece move e.g. Be6
- m5 = piece + file_coord + file_coord + rank_coord # piece move e.g. Nbd2
- m6 = piece + rank_coord + file_coord + rank_coord # piece move e.g. R4a7
- m7 = piece + capture + file_coord + rank_coord # piece capture move e.g. Bxh7
- m8 = castle_queenside | castle_kingside # castling e.g. o-o
- check = oneOf("+ ++")
- mate = Literal("#")
- annotation = Word("!?", max=2)
- nag = " $" + Word(nums)
- decoration = check | mate | annotation | nag
- variant = Forward()
- half_move = Combine((m3 | m1 | m2 | m4 | m5 | m6 | m7 | m8) + Optional(decoration)) \
- + Optional(comment) +Optional(variant)
- move = Suppress(move_number) + half_move + Optional(half_move)
- variant << "(" + OneOrMore(move) + ")"
- # grouping the plies (half-moves) for each move: useful to group annotations, variants...
- # suggested by Paul McGuire :)
- move = Group(Suppress(move_number) + half_move + Optional(half_move))
- variant << Group("(" + OneOrMore(move) + ")")
- game_terminator = oneOf("1-0 0-1 1/2-1/2 *")
- pgnGrammar = Suppress(ZeroOrMore(tag)) + ZeroOrMore(move) + Optional(Suppress(game_terminator))
- def parsePGN( pgn, bnf=pgnGrammar, fn=None ):
- try:
- return bnf.parseString( pgn )
- except ParseException as err:
- print(err.line)
- print(" "*(err.column-1) + "^")
- print(err)
- if __name__ == "__main__":
- # input string
- pgn = """
- [Event "ICC 5 0 u"]
- [Site "Internet Chess Club"]
- [Date "2004.01.25"]
- [Round "-"]
- [White "guest920"]
- [Black "IceBox"]
- [Result "0-1"]
- [ICCResult "White checkmated"]
- [BlackElo "1498"]
- [Opening "French defense"]
- [ECO "C00"]
- [NIC "FR.01"]
- [Time "04:44:56"]
- [TimeControl "300+0"]
- 1. e4 e6 2. Nf3 d5 $2 3. exd5 (3. e5 g6 4. h4) exd5 4. Qe2+ Qe7 5. Qxe7+ Bxe7 6. d3 Nf6 7. Be3
- Bg4 8. Nbd2 c5 9. h3 Be6 10. O-O-O Nc6 11. g4 Bd6 12. g5 Nd7 13. Rg1 d4 14.
- g6 fxg6 15. Bg5 Rf8 16. a3 Bd5 17. Re1+ Nde5 18. Nxe5 Nxe5 19. Bf4 Rf5 20.
- Bxe5 Rxe5 21. Rg5 Rxe1# {Black wins} 0-1
- """
- # parse input string
- tokens = parsePGN(pgn, pgnGrammar)
- print(tokens.dump())
|